OSDN Git Service

Hulk did something
[bytom/vapor.git] / blockchain / txbuilder / signature_program.go
1 package txbuilder
2
3 import (
4         "github.com/vapor/protocol/vm"
5         "github.com/vapor/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 }