OSDN Git Service

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