OSDN Git Service

add dpos consensus
[bytom/vapor.git] / blockchain / txbuilder / signature_witness.go
1 package txbuilder
2
3 import (
4         "context"
5         "encoding/json"
6
7         log "github.com/sirupsen/logrus"
8
9         "github.com/vapor/crypto/ed25519/chainkd"
10         "github.com/vapor/crypto/sha3pool"
11         chainjson "github.com/vapor/encoding/json"
12         "github.com/vapor/errors"
13         "github.com/vapor/protocol/vm"
14 )
15
16 type (
17         // SignatureWitness is a sign struct
18         SignatureWitness struct {
19                 // Quorum is the number of signatures required.
20                 Quorum int `json:"quorum"`
21
22                 // Keys are the identities of the keys to sign with.
23                 Keys []keyID `json:"keys"`
24
25                 // Program is the predicate part of the signature program, whose hash is what gets
26                 // signed. If empty, it is computed during Sign from the outputs
27                 // and the current input of the transaction.
28                 Program chainjson.HexBytes `json:"program"`
29
30                 // Sigs are signatures of Program made from each of the Keys
31                 // during Sign.
32                 Sigs []chainjson.HexBytes `json:"signatures"`
33         }
34
35         keyID struct {
36                 XPub           chainkd.XPub         `json:"xpub"`
37                 DerivationPath []chainjson.HexBytes `json:"derivation_path"`
38         }
39 )
40
41 // ErrEmptyProgram is a type of error
42 var ErrEmptyProgram = errors.New("empty signature program")
43
44 // Sign populates sw.Sigs with as many signatures of the predicate in
45 // sw.Program as it can from the overlapping set of keys in sw.Keys.
46 //
47 // If sw.Program is empty, it is populated with an _inferred_ predicate:
48 // a program committing to aspects of the current
49 // transaction. Specifically, the program commits to:
50 //  - the mintime and maxtime of the transaction (if non-zero)
51 //  - the outputID of the current input
52 //  - the assetID, amount, control program of each output.
53 func (sw *SignatureWitness) sign(ctx context.Context, tpl *Template, index uint32, auth string, signFn SignFunc) error {
54         // Compute the predicate to sign. This is either a
55         // txsighash program if tpl.AllowAdditional is false (i.e., the tx is complete
56         // and no further changes are allowed) or a program enforcing
57         // constraints derived from the existing outputs and current input.
58         if len(sw.Program) == 0 {
59                 var err error
60                 sw.Program, err = buildSigProgram(tpl, tpl.SigningInstructions[index].Position)
61                 if err != nil {
62                         return err
63                 }
64                 if len(sw.Program) == 0 {
65                         return ErrEmptyProgram
66                 }
67         }
68         if len(sw.Sigs) < len(sw.Keys) {
69                 // Each key in sw.Keys may produce a signature in sw.Sigs. Make
70                 // sure there are enough slots in sw.Sigs and that we preserve any
71                 // sigs already present.
72                 newSigs := make([]chainjson.HexBytes, len(sw.Keys))
73                 copy(newSigs, sw.Sigs)
74                 sw.Sigs = newSigs
75         }
76         var h [32]byte
77         sha3pool.Sum256(h[:], sw.Program)
78         for i, keyID := range sw.Keys {
79                 if len(sw.Sigs[i]) > 0 {
80                         // Already have a signature for this key
81                         continue
82                 }
83                 path := make([][]byte, len(keyID.DerivationPath))
84                 for i, p := range keyID.DerivationPath {
85                         path[i] = p
86                 }
87                 sigBytes, err := signFn(ctx, keyID.XPub, path, h, auth)
88                 if err != nil {
89                         log.WithField("err", err).Warningf("computing signature %d", i)
90                         continue
91                 }
92
93                 // This break is ordered to avoid signing transaction successfully only once for a multiple-sign account
94                 // that consist of different keys by the same password. Exit immediately when the signature is success,
95                 // it means that only one signature will be successful in the loop for this multiple-sign account.
96                 sw.Sigs[i] = sigBytes
97                 break
98         }
99         return nil
100 }
101
102 func (sw *SignatureWitness) Sign(tpl *Template, index uint32, xprv chainkd.XPrv) error {
103         // Compute the predicate to sign. This is either a
104         // txsighash program if tpl.AllowAdditional is false (i.e., the tx is complete
105         // and no further changes are allowed) or a program enforcing
106         // constraints derived from the existing outputs and current input.
107         if len(sw.Program) == 0 {
108                 var err error
109                 sw.Program, err = buildSigProgram(tpl, tpl.SigningInstructions[index].Position)
110                 if err != nil {
111                         return err
112                 }
113                 if len(sw.Program) == 0 {
114                         return ErrEmptyProgram
115                 }
116         }
117         if len(sw.Sigs) < len(sw.Keys) {
118                 // Each key in sw.Keys may produce a signature in sw.Sigs. Make
119                 // sure there are enough slots in sw.Sigs and that we preserve any
120                 // sigs already present.
121                 newSigs := make([]chainjson.HexBytes, len(sw.Keys))
122                 copy(newSigs, sw.Sigs)
123                 sw.Sigs = newSigs
124         }
125         var h [32]byte
126         sha3pool.Sum256(h[:], sw.Program)
127         for i, keyID := range sw.Keys {
128                 if len(sw.Sigs[i]) > 0 {
129                         // Already have a signature for this key
130                         continue
131                 }
132                 path := make([][]byte, len(keyID.DerivationPath))
133                 for i, p := range keyID.DerivationPath {
134                         path[i] = p
135                 }
136                 if keyID.XPub.String() != xprv.XPub().String() {
137                         continue
138                 }
139
140                 sigBytes := xprv.Sign(h[:])
141
142                 // This break is ordered to avoid signing transaction successfully only once for a multiple-sign account
143                 // that consist of different keys by the same password. Exit immediately when the signature is success,
144                 // it means that only one signature will be successful in the loop for this multiple-sign account.
145                 sw.Sigs[i] = sigBytes
146                 break
147         }
148         return nil
149 }
150
151 func (sw SignatureWitness) Materialize(args *[][]byte) error {
152         // This is the value of N for the CHECKPREDICATE call. The code
153         // assumes that everything already in the arg list before this call
154         // to Materialize is input to the signature program, so N is
155         // len(*args).
156         *args = append(*args, vm.Int64Bytes(int64(len(*args))))
157
158         var nsigs int
159         for i := 0; i < len(sw.Sigs) && nsigs < sw.Quorum; i++ {
160                 if len(sw.Sigs[i]) > 0 {
161                         *args = append(*args, sw.Sigs[i])
162                         nsigs++
163                 }
164         }
165         *args = append(*args, sw.Program)
166         return nil
167 }
168
169 // MarshalJSON convert struct to json
170 func (sw SignatureWitness) MarshalJSON() ([]byte, error) {
171         obj := struct {
172                 Type   string               `json:"type"`
173                 Quorum int                  `json:"quorum"`
174                 Keys   []keyID              `json:"keys"`
175                 Sigs   []chainjson.HexBytes `json:"signatures"`
176         }{
177                 Type:   "signature",
178                 Quorum: sw.Quorum,
179                 Keys:   sw.Keys,
180                 Sigs:   sw.Sigs,
181         }
182         return json.Marshal(obj)
183 }