/
signature_program.go
67 lines (62 loc) · 2.55 KB
/
signature_program.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package txbuilder
import (
"github.com/bytom/vapor/protocol/vm"
"github.com/bytom/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 <pubkey1> <pubkey2> ... <pubkeyN> <quorum> <N> 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> 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
}