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"
15 SignatureWitness struct {
16 // Quorum is the number of signatures required.
17 Quorum int `json:"quorum"`
19 // Keys are the identities of the keys to sign with.
20 Keys []keyID `json:"keys"`
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"`
27 // Sigs are signatures of Program made from each of the Keys
29 Sigs []chainjson.HexBytes `json:"signatures"`
33 XPub chainkd.XPub `json:"xpub"`
34 DerivationPath []chainjson.HexBytes `json:"derivation_path"`
38 var ErrEmptyProgram = errors.New("empty signature program")
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
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 {
57 sw.Program, err = buildSigProgram(tpl, tpl.SigningInstructions[index].Position)
61 if len(sw.Program) == 0 {
62 return ErrEmptyProgram
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)
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
81 for _, xpub := range xpubs {
82 if keyID.XPub == xpub {
87 if xpubs != nil && !found {
90 path := make([]([]byte), len(keyID.DerivationPath))
91 for i, p := range keyID.DerivationPath {
94 sigBytes, err := signFn(ctx, keyID.XPub, path, h, auth[i])
96 return errors.WithDetailf(err, "computing signature %d", i)
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
108 *args = append(*args, vm.Int64Bytes(int64(len(*args))))
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])
117 *args = append(*args, sw.Program)
121 func (sw SignatureWitness) MarshalJSON() ([]byte, error) {
123 Type string `json:"type"`
124 Quorum int `json:"quorum"`
125 Keys []keyID `json:"keys"`
126 Sigs []chainjson.HexBytes `json:"signatures"`
133 return json.Marshal(obj)