package mainchain import ( "github.com/vapor/protocol/vm" "github.com/vapor/protocol/vm/vmutil" ) // Signature programs constrain how the signed inputs of a transaction // in a template may be used, especially if the transaction is not yet // complete. // // For example, suppose Alice wants to send Bob 80 EUR but only if Bob // pays her 100 USD, and only if payment is made before next // Tuesday. Alice constructs a partial transaction that includes her // 80 EUR as one input, a payment to Bob as one output, _and_ a // payment to Alice (of 100 USD) as one more output. She then // constructs a program testing that the transaction includes all // those components (and that the maxtime of the transaction is before // next Tuesday) and signs a hash of that in order to unlock her 80 // EUR. She then passes the partial transaction template to Bob, who // supplies his 100 USD input. Because of the signature program, Bob // (or an eavesdropper) cannot use the signed 80-EUR input in any // transaction other than one that pays 100 USD to Alice before // Tuesday. // // This works because of Chain's convention for formatting of account // control programs. The 80 EUR prevout being spent by Alice was paid // to the program: // DUP TOALTSTACK SHA3 ... CHECKMULTISIG VERIFY FROMALTSTACK 0 CHECKPREDICATE // which means that any attempt to spend it must be accompanied by a // signed program that evaluates to true. The default program (for a // complete transaction to which no other entries may be added) is // TXSIGHASH EQUAL // which commits to the transaction as-is. func buildSigProgram(tpl *Template, index uint32) ([]byte, error) { if !tpl.AllowAdditional { h := tpl.Hash(index) builder := vmutil.NewBuilder() builder.AddData(h.Bytes()) builder.AddOp(vm.OP_TXSIGHASH).AddOp(vm.OP_EQUAL) return builder.Build() } constraints := make([]constraint, 0, 3+len(tpl.Transaction.Outputs)) id := tpl.Transaction.Tx.InputIDs[index] if sp, err := tpl.Transaction.Tx.Spend(id); err == nil { constraints = append(constraints, outputIDConstraint(*sp.SpentOutputId)) } for i, out := range tpl.Transaction.Outputs { c := &payConstraint{ Index: i, AssetAmount: out.AssetAmount, Program: out.ControlProgram, } constraints = append(constraints, c) } var program []byte for i, c := range constraints { program = append(program, c.code()...) if i < len(constraints)-1 { // leave the final bool on top of the stack program = append(program, byte(vm.OP_VERIFY)) } } return program, nil }