OSDN Git Service

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