9 log "github.com/sirupsen/logrus"
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"
19 // SignatureWitness is a sign struct
20 SignatureWitness struct {
21 // Quorum is the number of signatures required.
22 Quorum int `json:"quorum"`
24 // Keys are the identities of the keys to sign with.
25 Keys []keyID `json:"keys"`
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"`
32 // Sigs are signatures of Program made from each of the Keys
34 Sigs []chainjson.HexBytes `json:"signatures"`
38 XPub chainkd.XPub `json:"xpub"`
39 DerivationPath []chainjson.HexBytes `json:"derivation_path"`
43 // ErrEmptyProgram is a type of error
44 var ErrEmptyProgram = errors.New("empty signature program")
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.
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 {
62 sw.Program, err = buildSigProgram(tpl, tpl.SigningInstructions[index].Position)
66 if len(sw.Program) == 0 {
67 return ErrEmptyProgram
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)
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
85 path := make([][]byte, len(keyID.DerivationPath))
86 for i, p := range keyID.DerivationPath {
89 sigBytes, err := signFn(ctx, keyID.XPub, path, h, auth)
91 log.WithFields(log.Fields{"module": logModule, "err": err}).Warningf("computing signature %d", i)
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.
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
109 *args = append(*args, vm.Uint64Bytes(uint64(len(*args))))
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])
118 *args = append(*args, sw.Program)
122 // MarshalJSON convert struct to json
123 func (sw SignatureWitness) MarshalJSON() ([]byte, error) {
125 Type string `json:"type"`
126 Quorum int `json:"quorum"`
127 Keys []keyID `json:"keys"`
128 Sigs []chainjson.HexBytes `json:"signatures"`
135 return json.Marshal(obj)