github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/blockchain/txbuilder/signature_witness.go (about) 1 package txbuilder 2 3 import ( 4 "context" 5 "encoding/json" 6 7 log "github.com/sirupsen/logrus" 8 9 "github.com/bytom/bytom/crypto/ed25519/chainkd" 10 "github.com/bytom/bytom/crypto/sha3pool" 11 chainjson "github.com/bytom/bytom/encoding/json" 12 "github.com/bytom/bytom/errors" 13 "github.com/bytom/bytom/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.WithFields(log.Fields{"module": logModule, "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) materialize(args *[][]byte) error { 103 // This is the value of N for the CHECKPREDICATE call. The code 104 // assumes that everything already in the arg list before this call 105 // to Materialize is input to the signature program, so N is 106 // len(*args). 107 *args = append(*args, vm.Uint64Bytes(uint64(len(*args)))) 108 109 var nsigs int 110 for i := 0; i < len(sw.Sigs) && nsigs < sw.Quorum; i++ { 111 if len(sw.Sigs[i]) > 0 { 112 *args = append(*args, sw.Sigs[i]) 113 nsigs++ 114 } 115 } 116 *args = append(*args, sw.Program) 117 return nil 118 } 119 120 // MarshalJSON convert struct to json 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 }