OSDN Git Service

modify parameter name (#411)
[bytom/vapor.git] / protocol / vm / vmutil / script.go
1 package vmutil
2
3 import (
4         "github.com/vapor/crypto/ed25519"
5         "github.com/vapor/errors"
6         "github.com/vapor/protocol/bc"
7         "github.com/vapor/protocol/vm"
8 )
9
10 // pre-define errors
11 var (
12         ErrBadValue       = errors.New("bad value")
13         ErrMultisigFormat = errors.New("bad multisig program format")
14 )
15
16 // MagneticContractArgs is a struct for magnetic contract arguments
17 type MagneticContractArgs struct {
18         RequestedAsset   bc.AssetID
19         RatioNumerator   int64
20         RatioDenominator int64
21         SellerProgram    []byte
22         SellerKey        []byte
23 }
24
25 // IsUnspendable checks if a contorl program is absolute failed
26 func IsUnspendable(prog []byte) bool {
27         return len(prog) > 0 && prog[0] == byte(vm.OP_FAIL)
28 }
29
30 func (b *Builder) addP2SPMultiSig(pubkeys []ed25519.PublicKey, nrequired int) error {
31         if err := checkMultiSigParams(int64(nrequired), int64(len(pubkeys))); err != nil {
32                 return err
33         }
34
35         b.AddOp(vm.OP_TXSIGHASH) // stack is now [... NARGS SIG SIG SIG PREDICATEHASH]
36         for _, p := range pubkeys {
37                 b.AddData(p)
38         }
39         b.AddInt64(int64(nrequired))    // stack is now [... SIG SIG SIG PREDICATEHASH PUB PUB PUB M]
40         b.AddInt64(int64(len(pubkeys))) // stack is now [... SIG SIG SIG PREDICATEHASH PUB PUB PUB M N]
41         b.AddOp(vm.OP_CHECKMULTISIG)    // stack is now [... NARGS]
42         return nil
43 }
44
45 // DefaultCoinbaseProgram generates the script for contorl coinbase output
46 func DefaultCoinbaseProgram() ([]byte, error) {
47         builder := NewBuilder()
48         builder.AddOp(vm.OP_TRUE)
49         return builder.Build()
50 }
51
52 // P2WPKHProgram return the segwit pay to public key hash
53 func P2WPKHProgram(hash []byte) ([]byte, error) {
54         builder := NewBuilder()
55         builder.AddInt64(0)
56         builder.AddData(hash)
57         return builder.Build()
58 }
59
60 // P2WSHProgram return the segwit pay to script hash
61 func P2WSHProgram(hash []byte) ([]byte, error) {
62         builder := NewBuilder()
63         builder.AddInt64(0)
64         builder.AddData(hash)
65         return builder.Build()
66 }
67
68 // RetireProgram generates the script for retire output
69 func RetireProgram(comment []byte) ([]byte, error) {
70         builder := NewBuilder()
71         builder.AddOp(vm.OP_FAIL)
72         if len(comment) != 0 {
73                 builder.AddData(comment)
74         }
75         return builder.Build()
76 }
77
78 // P2PKHSigProgram generates the script for control with pubkey hash
79 func P2PKHSigProgram(pubkeyHash []byte) ([]byte, error) {
80         builder := NewBuilder()
81         builder.AddOp(vm.OP_DUP)
82         builder.AddOp(vm.OP_HASH160)
83         builder.AddData(pubkeyHash)
84         builder.AddOp(vm.OP_EQUALVERIFY)
85         builder.AddOp(vm.OP_TXSIGHASH)
86         builder.AddOp(vm.OP_SWAP)
87         builder.AddOp(vm.OP_CHECKSIG)
88         return builder.Build()
89 }
90
91 // P2SHProgram generates the script for control with script hash
92 func P2SHProgram(scriptHash []byte) ([]byte, error) {
93         builder := NewBuilder()
94         builder.AddOp(vm.OP_DUP)
95         builder.AddOp(vm.OP_SHA3)
96         builder.AddData(scriptHash)
97         builder.AddOp(vm.OP_EQUALVERIFY)
98         builder.AddInt64(-1)
99         builder.AddOp(vm.OP_SWAP)
100         builder.AddInt64(0)
101         builder.AddOp(vm.OP_CHECKPREDICATE)
102         return builder.Build()
103 }
104
105 // P2SPMultiSigProgram generates the script for control transaction output
106 func P2SPMultiSigProgram(pubkeys []ed25519.PublicKey, nrequired int) ([]byte, error) {
107         builder := NewBuilder()
108         if err := builder.addP2SPMultiSig(pubkeys, nrequired); err != nil {
109                 return nil, err
110         }
111         return builder.Build()
112 }
113
114 // P2SPMultiSigProgramWithHeight generates the script with block height for control transaction output
115 func P2SPMultiSigProgramWithHeight(pubkeys []ed25519.PublicKey, nrequired int, blockHeight int64) ([]byte, error) {
116         builder := NewBuilder()
117         if blockHeight > 0 {
118                 builder.AddInt64(blockHeight)
119                 builder.AddOp(vm.OP_BLOCKHEIGHT)
120                 builder.AddOp(vm.OP_GREATERTHAN)
121                 builder.AddOp(vm.OP_VERIFY)
122         } else if blockHeight < 0 {
123                 return nil, errors.WithDetail(ErrBadValue, "negative blockHeight")
124         }
125         if err := builder.addP2SPMultiSig(pubkeys, nrequired); err != nil {
126                 return nil, err
127         }
128         return builder.Build()
129 }
130
131 func checkMultiSigParams(nrequired, npubkeys int64) error {
132         if nrequired < 0 {
133                 return errors.WithDetail(ErrBadValue, "negative quorum")
134         }
135         if npubkeys < 0 {
136                 return errors.WithDetail(ErrBadValue, "negative pubkey count")
137         }
138         if nrequired > npubkeys {
139                 return errors.WithDetail(ErrBadValue, "quorum too big")
140         }
141         if nrequired == 0 && npubkeys > 0 {
142                 return errors.WithDetail(ErrBadValue, "quorum empty with non-empty pubkey list")
143         }
144         return nil
145 }
146
147 // P2WMCProgram return the segwit pay to magnetic contract
148 func P2WMCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) {
149         builder := NewBuilder()
150         builder.AddInt64(0)
151         builder.AddData(magneticContractArgs.RequestedAsset.Bytes())
152         builder.AddInt64(magneticContractArgs.RatioNumerator)
153         builder.AddInt64(magneticContractArgs.RatioDenominator)
154         builder.AddData(magneticContractArgs.SellerProgram)
155         builder.AddData(magneticContractArgs.SellerKey)
156         return builder.Build()
157 }
158
159 // P2MCProgram generates the script for control with magnetic contract
160 //
161 // MagneticContract source code:
162 // contract MagneticContract(requestedAsset: Asset,
163 //                           ratioNumerator: Integer,
164 //                           ratioDenominator: Integer,
165 //                           sellerProgram: Program,
166 //                           standardProgram: Program,
167 //                           sellerKey: PublicKey) locks valueAmount of valueAsset {
168 //  clause partialTrade(exchangeAmount: Amount) {
169 //       define actualAmount: Integer = exchangeAmount * ratioDenominator / ratioNumerator
170 //       verify actualAmount > 0 && actualAmount < valueAmount
171 //   lock exchangeAmount of requestedAsset with sellerProgram
172 //   lock valueAmount-actualAmount of valueAsset with standardProgram
173 //   unlock actualAmount of valueAsset
174 //  }
175 //  clause fullTrade() {
176 //   define requestedAmount: Integer = valueAmount * ratioNumerator / ratioDenominator
177 //   verify requestedAmount > 0
178 //   lock requestedAmount of requestedAsset with sellerProgram
179 //   unlock valueAmount of valueAsset
180 //  }
181 //  clause cancel(sellerSig: Signature) {
182 //   verify checkTxSig(sellerKey, sellerSig)
183 //   unlock valueAmount of valueAsset
184 //  }
185 // }
186 //
187 // contract stack flow:
188 // 7                        [... <position> <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset 7]
189 // ROLL                     [... <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <position>]
190 // TOALTSTACK               [... <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
191 // 6                        [... <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset 6]
192 // ROLL                     [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector>]
193 // DUP                      [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector> <clause selector>]
194 // 2                        [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector> <clause selector> 2]
195 // NUMEQUAL                 [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector> (<clause selector> == 2)]
196 // JUMPIF:$cancel           [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector>]
197 // JUMPIF:$fullTrade        [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
198 // $partialTrade            [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
199 // 6                        [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset 6]
200 // PICK                     [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset exchangeAmount]
201 // 3                        [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset exchangeAmount 3]
202 // ROLL                     [... exchangeAmount sellerKey standardProgram sellerProgram ratioNumerator requestedAsset exchangeAmount ratioDenominator]
203 // MUL                      [... exchangeAmount sellerKey standardProgram sellerProgram ratioNumerator requestedAsset (exchangeAmount * ratioDenominator)]
204 // 2                        [... exchangeAmount sellerKey standardProgram sellerProgram ratioNumerator requestedAsset (exchangeAmount * ratioDenominator) 2]
205 // ROLL                     [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset (exchangeAmount * ratioDenominator) ratioNumerator]
206 // DIV                      [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount]
207 // AMOUNT                   [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount]
208 // OVER                     [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount actualAmount]
209 // 0                        [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount actualAmount 0]
210 // GREATERTHAN              [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0)]
211 // 2                        [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0) 2]
212 // PICK                     [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0) actualAmount]
213 // 2                        [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0) actualAmount 2]
214 // ROLL                     [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount (actualAmount > 0) actualAmount valueAmount]
215 // LESSTHAN                 [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount (actualAmount > 0) (actualAmount < valueAmount)]
216 // BOOLAND                  [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount ((actualAmount > 0) && (actualAmount < valueAmount))]
217 // VERIFY                   [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount]
218 // FROMALTSTACK             [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position>]
219 // DUP                      [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> <position>]
220 // TOALTSTACK               [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position>]
221 // 6                        [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> 6]
222 // ROLL                     [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> exchangeAmount]
223 // 3                        [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> exchangeAmount 3]
224 // ROLL                     [... sellerKey standardProgram sellerProgram actualAmount <position> exchangeAmount requestedAsset]
225 // 1                        [... sellerKey standardProgram sellerProgram actualAmount <position> exchangeAmount requestedAsset 1]
226 // 5                        [... sellerKey standardProgram sellerProgram actualAmount <position> exchangeAmount requestedAsset 1 5]
227 // ROLL                     [... sellerKey standardProgram actualAmount <position> exchangeAmount requestedAsset 1 sellerProgram]
228 // CHECKOUTPUT              [... sellerKey standardProgram actualAmount checkOutput(exchangeAmount, requestedAsset, sellerProgram)]
229 // VERIFY                   [... sellerKey standardProgram actualAmount]
230 // FROMALTSTACK             [... sellerKey standardProgram actualAmount <position>]
231 // 1                        [... sellerKey standardProgram actualAmount <position> 1]
232 // ADD                      [... sellerKey standardProgram actualAmount (<position> + 1)]
233 // AMOUNT                   [... sellerKey standardProgram actualAmount (<position> + 1) valueAmount]
234 // 2                        [... sellerKey standardProgram actualAmount (<position> + 1) valueAmount 2]
235 // ROLL                     [... sellerKey standardProgram (<position> + 1) valueAmount actualAmount]
236 // SUB                      [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount)]
237 // ASSET                    [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount) valueAsset]
238 // 1                        [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount) valueAsset 1]
239 // 4                        [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount) valueAsset 1 4]
240 // ROLL                     [... sellerKey (<position> + 1) (valueAmount - actualAmount) valueAsset 1 standardProgram]
241 // CHECKOUTPUT              [... sellerKey checkOutput((valueAmount - actualAmount), valueAsset, standardProgram)]
242 // JUMP:$_end               [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
243 // $fullTrade               [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
244 // AMOUNT                   [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset valueAmount]
245 // 2                        [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset valueAmount 2]
246 // ROLL                     [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset valueAmount ratioNumerator]
247 // MUL                      [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset (valueAmount * ratioNumerator)]
248 // 2                        [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset (valueAmount * ratioNumerator) 2]
249 // ROLL                     [... sellerKey standardProgram sellerProgram requestedAsset (valueAmount * ratioNumerator) ratioDenominator]
250 // DIV                      [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount]
251 // DUP                      [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount requestedAmount]
252 // 0                        [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount requestedAmount 0]
253 // GREATERTHAN              [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount (requestedAmount > 0)]
254 // VERIFY                   [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount]
255 // FROMALTSTACK             [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount <position>]
256 // SWAP                     [... sellerKey standardProgram sellerProgram requestedAsset <position> requestedAmount]
257 // 2                        [... sellerKey standardProgram sellerProgram requestedAsset <position> requestedAmount 2]
258 // ROLL                     [... sellerKey standardProgram sellerProgram <position> requestedAmount requestedAsset]
259 // 1                        [... sellerKey standardProgram sellerProgram <position> requestedAmount requestedAsset 1]
260 // 4                        [... sellerKey standardProgram sellerProgram <position> requestedAmount requestedAsset 1 4]
261 // ROLL                     [... sellerKey standardProgram <position> requestedAmount requestedAsset 1 sellerProgram]
262 // CHECKOUTPUT              [... sellerKey standardProgram checkOutput(requestedAmount, requestedAsset, sellerProgram)]
263 // JUMP:$_end               [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
264 // $cancel                  [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector>]
265 // DROP                     [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
266 // 6                        [... sellerSig sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset 6]
267 // ROLL                     [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset sellerSig]
268 // 6                        [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset sellerSig 6]
269 // ROLL                     [... standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset sellerSig sellerKey]
270 // TXSIGHASH SWAP CHECKSIG  [... standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset checkTxSig(sellerKey, sellerSig)]
271 // $_end                    [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
272 func P2MCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) {
273         standardProgram, err := P2WMCProgram(magneticContractArgs)
274         if err != nil {
275                 return nil, err
276         }
277
278         builder := NewBuilder()
279         // contract arguments
280         builder.AddData(magneticContractArgs.SellerKey)
281         builder.AddData(standardProgram)
282         builder.AddData(magneticContractArgs.SellerProgram)
283         builder.AddInt64(magneticContractArgs.RatioDenominator)
284         builder.AddInt64(magneticContractArgs.RatioNumerator)
285         builder.AddData(magneticContractArgs.RequestedAsset.Bytes())
286
287         // contract instructions
288         builder.AddOp(vm.OP_7)
289         builder.AddOp(vm.OP_ROLL)
290         builder.AddOp(vm.OP_TOALTSTACK)
291         builder.AddOp(vm.OP_6)
292         builder.AddOp(vm.OP_ROLL)
293         builder.AddOp(vm.OP_DUP)
294         builder.AddOp(vm.OP_2)
295         builder.AddOp(vm.OP_NUMEQUAL)
296         builder.AddJumpIf(0)
297         builder.AddJumpIf(1)
298         builder.AddOp(vm.OP_6)
299         builder.AddOp(vm.OP_PICK)
300         builder.AddOp(vm.OP_3)
301         builder.AddOp(vm.OP_ROLL)
302         builder.AddOp(vm.OP_MUL)
303         builder.AddOp(vm.OP_ROT)
304         builder.AddOp(vm.OP_DIV)
305         builder.AddOp(vm.OP_AMOUNT)
306         builder.AddOp(vm.OP_OVER)
307         builder.AddOp(vm.OP_0)
308         builder.AddOp(vm.OP_GREATERTHAN)
309         builder.AddOp(vm.OP_2)
310         builder.AddOp(vm.OP_PICK)
311         builder.AddOp(vm.OP_ROT)
312         builder.AddOp(vm.OP_LESSTHAN)
313         builder.AddOp(vm.OP_BOOLAND)
314         builder.AddOp(vm.OP_VERIFY)
315         builder.AddOp(vm.OP_FROMALTSTACK)
316         builder.AddOp(vm.OP_DUP)
317         builder.AddOp(vm.OP_TOALTSTACK)
318         builder.AddOp(vm.OP_6)
319         builder.AddOp(vm.OP_ROLL)
320         builder.AddOp(vm.OP_3)
321         builder.AddOp(vm.OP_ROLL)
322         builder.AddOp(vm.OP_1)
323         builder.AddOp(vm.OP_5)
324         builder.AddOp(vm.OP_ROLL)
325         builder.AddOp(vm.OP_CHECKOUTPUT)
326         builder.AddOp(vm.OP_VERIFY)
327         builder.AddOp(vm.OP_FROMALTSTACK)
328         builder.AddOp(vm.OP_1)
329         builder.AddOp(vm.OP_ADD)
330         builder.AddOp(vm.OP_AMOUNT)
331         builder.AddOp(vm.OP_ROT)
332         builder.AddOp(vm.OP_SUB)
333         builder.AddOp(vm.OP_ASSET)
334         builder.AddOp(vm.OP_1)
335         builder.AddOp(vm.OP_4)
336         builder.AddOp(vm.OP_ROLL)
337         builder.AddOp(vm.OP_CHECKOUTPUT)
338         builder.AddJump(2)
339         builder.SetJumpTarget(1)
340         builder.AddOp(vm.OP_AMOUNT)
341         builder.AddOp(vm.OP_ROT)
342         builder.AddOp(vm.OP_MUL)
343         builder.AddOp(vm.OP_ROT)
344         builder.AddOp(vm.OP_DIV)
345         builder.AddOp(vm.OP_DUP)
346         builder.AddOp(vm.OP_0)
347         builder.AddOp(vm.OP_GREATERTHAN)
348         builder.AddOp(vm.OP_VERIFY)
349         builder.AddOp(vm.OP_FROMALTSTACK)
350         builder.AddOp(vm.OP_SWAP)
351         builder.AddOp(vm.OP_ROT)
352         builder.AddOp(vm.OP_1)
353         builder.AddOp(vm.OP_4)
354         builder.AddOp(vm.OP_ROLL)
355         builder.AddOp(vm.OP_CHECKOUTPUT)
356         builder.AddJump(3)
357         builder.SetJumpTarget(0)
358         builder.AddOp(vm.OP_DROP)
359         builder.AddOp(vm.OP_6)
360         builder.AddOp(vm.OP_ROLL)
361         builder.AddOp(vm.OP_6)
362         builder.AddOp(vm.OP_ROLL)
363         builder.AddOp(vm.OP_TXSIGHASH)
364         builder.AddOp(vm.OP_SWAP)
365         builder.AddOp(vm.OP_CHECKSIG)
366         builder.SetJumpTarget(2)
367         builder.SetJumpTarget(3)
368         return builder.Build()
369 }