OSDN Git Service

equity compiler tool (#18)
[bytom/equity.git] / vendor / github.com / bytom / protocol / vm / vmutil / script.go
1 package vmutil
2
3 import (
4         "github.com/bytom/crypto/ed25519"
5         "github.com/bytom/errors"
6         "github.com/bytom/protocol/vm"
7 )
8
9 // pre-define errors
10 var (
11         ErrBadValue       = errors.New("bad value")
12         ErrMultisigFormat = errors.New("bad multisig program format")
13 )
14
15 // IsUnspendable checks if a contorl program is absolute failed
16 func IsUnspendable(prog []byte) bool {
17         return len(prog) > 0 && prog[0] == byte(vm.OP_FAIL)
18 }
19
20 func (b *Builder) addP2SPMultiSig(pubkeys []ed25519.PublicKey, nrequired int) error {
21         if err := checkMultiSigParams(int64(nrequired), int64(len(pubkeys))); err != nil {
22                 return err
23         }
24
25         b.AddOp(vm.OP_TXSIGHASH) // stack is now [... NARGS SIG SIG SIG PREDICATEHASH]
26         for _, p := range pubkeys {
27                 b.AddData(p)
28         }
29         b.AddInt64(int64(nrequired))    // stack is now [... SIG SIG SIG PREDICATEHASH PUB PUB PUB M]
30         b.AddInt64(int64(len(pubkeys))) // stack is now [... SIG SIG SIG PREDICATEHASH PUB PUB PUB M N]
31         b.AddOp(vm.OP_CHECKMULTISIG)    // stack is now [... NARGS]
32         return nil
33 }
34
35 // DefaultCoinbaseProgram generates the script for contorl coinbase output
36 func DefaultCoinbaseProgram() ([]byte, error) {
37         builder := NewBuilder()
38         builder.AddOp(vm.OP_TRUE)
39         return builder.Build()
40 }
41
42 // P2WPKHProgram return the segwit pay to public key hash
43 func P2WPKHProgram(hash []byte) ([]byte, error) {
44         builder := NewBuilder()
45         builder.AddInt64(0)
46         builder.AddData(hash)
47         return builder.Build()
48 }
49
50 // P2WSHProgram return the segwit pay to script hash
51 func P2WSHProgram(hash []byte) ([]byte, error) {
52         builder := NewBuilder()
53         builder.AddInt64(0)
54         builder.AddData(hash)
55         return builder.Build()
56 }
57
58 // RetireProgram generates the script for retire output
59 func RetireProgram(comment []byte) ([]byte, error) {
60         builder := NewBuilder()
61         builder.AddOp(vm.OP_FAIL)
62         if len(comment) != 0 {
63                 builder.AddData(comment)
64         }
65         return builder.Build()
66 }
67
68 // P2PKHSigProgram generates the script for control with pubkey hash
69 func P2PKHSigProgram(pubkeyHash []byte) ([]byte, error) {
70         builder := NewBuilder()
71         builder.AddOp(vm.OP_DUP)
72         builder.AddOp(vm.OP_HASH160)
73         builder.AddData(pubkeyHash)
74         builder.AddOp(vm.OP_EQUALVERIFY)
75         builder.AddOp(vm.OP_TXSIGHASH)
76         builder.AddOp(vm.OP_SWAP)
77         builder.AddOp(vm.OP_CHECKSIG)
78         return builder.Build()
79 }
80
81 // P2SHProgram generates the script for control with script hash
82 func P2SHProgram(scriptHash []byte) ([]byte, error) {
83         builder := NewBuilder()
84         builder.AddOp(vm.OP_DUP)
85         builder.AddOp(vm.OP_SHA3)
86         builder.AddData(scriptHash)
87         builder.AddOp(vm.OP_EQUALVERIFY)
88         builder.AddInt64(-1)
89         builder.AddOp(vm.OP_SWAP)
90         builder.AddInt64(0)
91         builder.AddOp(vm.OP_CHECKPREDICATE)
92         return builder.Build()
93 }
94
95 // P2SPMultiSigProgram generates the script for contorl transaction output
96 func P2SPMultiSigProgram(pubkeys []ed25519.PublicKey, nrequired int) ([]byte, error) {
97         builder := NewBuilder()
98         if err := builder.addP2SPMultiSig(pubkeys, nrequired); err != nil {
99                 return nil, err
100         }
101         return builder.Build()
102 }
103
104 // ParseP2SPMultiSigProgram is unknow for us yet
105 func ParseP2SPMultiSigProgram(program []byte) ([]ed25519.PublicKey, int, error) {
106         pops, err := vm.ParseProgram(program)
107         if err != nil {
108                 return nil, 0, err
109         }
110         if len(pops) < 11 {
111                 return nil, 0, vm.ErrShortProgram
112         }
113
114         // Count all instructions backwards from the end in case there are
115         // extra instructions at the beginning of the program (like a
116         // <pushdata> DROP).
117
118         npubkeys, err := vm.AsInt64(pops[len(pops)-6].Data)
119         if err != nil {
120                 return nil, 0, err
121         }
122         if int(npubkeys) > len(pops)-10 {
123                 return nil, 0, vm.ErrShortProgram
124         }
125         nrequired, err := vm.AsInt64(pops[len(pops)-7].Data)
126         if err != nil {
127                 return nil, 0, err
128         }
129         err = checkMultiSigParams(nrequired, npubkeys)
130         if err != nil {
131                 return nil, 0, err
132         }
133
134         firstPubkeyIndex := len(pops) - 7 - int(npubkeys)
135
136         pubkeys := make([]ed25519.PublicKey, 0, npubkeys)
137         for i := firstPubkeyIndex; i < firstPubkeyIndex+int(npubkeys); i++ {
138                 if len(pops[i].Data) != ed25519.PublicKeySize {
139                         return nil, 0, err
140                 }
141                 pubkeys = append(pubkeys, ed25519.PublicKey(pops[i].Data))
142         }
143         return pubkeys, int(nrequired), nil
144 }
145
146 func checkMultiSigParams(nrequired, npubkeys int64) error {
147         if nrequired < 0 {
148                 return errors.WithDetail(ErrBadValue, "negative quorum")
149         }
150         if npubkeys < 0 {
151                 return errors.WithDetail(ErrBadValue, "negative pubkey count")
152         }
153         if nrequired > npubkeys {
154                 return errors.WithDetail(ErrBadValue, "quorum too big")
155         }
156         if nrequired == 0 && npubkeys > 0 {
157                 return errors.WithDetail(ErrBadValue, "quorum empty with non-empty pubkey list")
158         }
159         return nil
160 }