github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/blockchain/txbuilder/signature_program.go (about) 1 package txbuilder 2 3 import ( 4 "github.com/bytom/bytom/protocol/vm" 5 "github.com/bytom/bytom/protocol/vm/vmutil" 6 ) 7 8 // Signature programs constrain how the signed inputs of a transaction 9 // in a template may be used, especially if the transaction is not yet 10 // complete. 11 // 12 // For example, suppose Alice wants to send Bob 80 EUR but only if Bob 13 // pays her 100 USD, and only if payment is made before next 14 // Tuesday. Alice constructs a partial transaction that includes her 15 // 80 EUR as one input, a payment to Bob as one output, _and_ a 16 // payment to Alice (of 100 USD) as one more output. She then 17 // constructs a program testing that the transaction includes all 18 // those components (and that the maxtime of the transaction is before 19 // next Tuesday) and signs a hash of that in order to unlock her 80 20 // EUR. She then passes the partial transaction template to Bob, who 21 // supplies his 100 USD input. Because of the signature program, Bob 22 // (or an eavesdropper) cannot use the signed 80-EUR input in any 23 // transaction other than one that pays 100 USD to Alice before 24 // Tuesday. 25 // 26 // This works because of Chain's convention for formatting of account 27 // control programs. The 80 EUR prevout being spent by Alice was paid 28 // to the program: 29 // DUP TOALTSTACK SHA3 <pubkey1> <pubkey2> ... <pubkeyN> <quorum> <N> CHECKMULTISIG VERIFY FROMALTSTACK 0 CHECKPREDICATE 30 // which means that any attempt to spend it must be accompanied by a 31 // signed program that evaluates to true. The default program (for a 32 // complete transaction to which no other entries may be added) is 33 // <txsighash> TXSIGHASH EQUAL 34 // which commits to the transaction as-is. 35 36 func buildSigProgram(tpl *Template, index uint32) ([]byte, error) { 37 if !tpl.AllowAdditional { 38 h := tpl.Hash(index) 39 builder := vmutil.NewBuilder() 40 builder.AddData(h.Bytes()) 41 builder.AddOp(vm.OP_TXSIGHASH).AddOp(vm.OP_EQUAL) 42 43 return builder.Build() 44 } 45 constraints := make([]constraint, 0, 3+len(tpl.Transaction.Outputs)) 46 id := tpl.Transaction.Tx.InputIDs[index] 47 if sp, err := tpl.Transaction.Tx.Spend(id); err == nil { 48 constraints = append(constraints, outputIDConstraint(*sp.SpentOutputId)) 49 } 50 51 for i, out := range tpl.Transaction.Outputs { 52 c := &payConstraint{ 53 Index: i, 54 AssetAmount: out.AssetAmount, 55 Program: out.ControlProgram, 56 } 57 constraints = append(constraints, c) 58 } 59 var program []byte 60 for i, c := range constraints { 61 program = append(program, c.code()...) 62 if i < len(constraints)-1 { // leave the final bool on top of the stack 63 program = append(program, byte(vm.OP_VERIFY)) 64 } 65 } 66 return program, nil 67 }