github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/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  }