4 "github.com/bytom/vapor/crypto/ed25519"
5 "github.com/bytom/vapor/errors"
6 "github.com/bytom/vapor/protocol/bc"
7 "github.com/bytom/vapor/protocol/vm"
12 ErrBadValue = errors.New("bad value")
13 ErrMultisigFormat = errors.New("bad multisig program format")
16 // MagneticContractArgs is a struct for magnetic contract arguments
17 type MagneticContractArgs struct {
18 RequestedAsset bc.AssetID
20 RatioDenominator int64
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)
30 func (b *Builder) addP2SPMultiSig(pubkeys []ed25519.PublicKey, nrequired int) error {
31 if err := checkMultiSigParams(int64(nrequired), int64(len(pubkeys))); err != nil {
35 b.AddOp(vm.OP_TXSIGHASH) // stack is now [... NARGS SIG SIG SIG PREDICATEHASH]
36 for _, p := range pubkeys {
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]
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()
52 // P2WPKHProgram return the segwit pay to public key hash
53 func P2WPKHProgram(hash []byte) ([]byte, error) {
54 builder := NewBuilder()
57 return builder.Build()
60 // P2WSHProgram return the segwit pay to script hash
61 func P2WSHProgram(hash []byte) ([]byte, error) {
62 builder := NewBuilder()
65 return builder.Build()
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)
75 return builder.Build()
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()
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)
99 builder.AddOp(vm.OP_SWAP)
101 builder.AddOp(vm.OP_CHECKPREDICATE)
102 return builder.Build()
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 {
111 return builder.Build()
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()
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")
125 if err := builder.addP2SPMultiSig(pubkeys, nrequired); err != nil {
128 return builder.Build()
131 func checkMultiSigParams(nrequired, npubkeys int64) error {
133 return errors.WithDetail(ErrBadValue, "negative quorum")
136 return errors.WithDetail(ErrBadValue, "negative pubkey count")
138 if nrequired > npubkeys {
139 return errors.WithDetail(ErrBadValue, "quorum too big")
141 if nrequired == 0 && npubkeys > 0 {
142 return errors.WithDetail(ErrBadValue, "quorum empty with non-empty pubkey list")
147 // P2WMCProgram return the segwit pay to magnetic contract
148 func P2WMCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) {
149 builder := NewBuilder()
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()
159 // P2MCProgram generates the script for control with magnetic contract
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 // define receiveAmount: Integer = exchangeAmount * 999 / 1000
172 // lock receiveAmount of requestedAsset with sellerProgram
173 // lock valueAmount-actualAmount of valueAsset with standardProgram
174 // unlock actualAmount of valueAsset
176 // clause fullTrade() {
177 // define requestedAmount: Integer = valueAmount * ratioNumerator / ratioDenominator
178 // define requestedAmount: Integer = requestedAmount * 999 / 1000
179 // verify requestedAmount > 0
180 // lock requestedAmount of requestedAsset with sellerProgram
181 // unlock valueAmount of valueAsset
183 // clause cancel(sellerSig: Signature) {
184 // verify checkTxSig(sellerKey, sellerSig)
185 // unlock valueAmount of valueAsset
189 // contract stack flow:
190 // 7 [... <position> <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset 7]
191 // ROLL [... <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <position>]
192 // TOALTSTACK [... <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
193 // 6 [... <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset 6]
194 // ROLL [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector>]
195 // DUP [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector> <clause selector>]
196 // 2 [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector> <clause selector> 2]
197 // NUMEQUAL [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector> (<clause selector> == 2)]
198 // JUMPIF:$cancel [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector>]
199 // JUMPIF:$fullTrade [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
200 // $partialTrade [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
201 // 6 [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset 6]
202 // PICK [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset exchangeAmount]
203 // 3 [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset exchangeAmount 3]
204 // ROLL [... exchangeAmount sellerKey standardProgram sellerProgram ratioNumerator requestedAsset exchangeAmount ratioDenominator]
205 // 3 [... exchangeAmount sellerKey standardProgram sellerProgram ratioNumerator requestedAsset exchangeAmount ratioDenominator 3]
206 // ROLL [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset exchangeAmount ratioDenominator ratioNumerator]
207 // MULFRACTION [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount]
208 // AMOUNT [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount]
209 // OVER [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount actualAmount]
210 // 0 [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount actualAmount 0]
211 // GREATERTHAN [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0)]
212 // 2 [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0) 2]
213 // PICK [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0) actualAmount]
214 // 2 [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0) actualAmount 2]
215 // ROLL [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount (actualAmount > 0) actualAmount valueAmount]
216 // LESSTHAN [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount (actualAmount > 0) (actualAmount < valueAmount)]
217 // BOOLAND [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount ((actualAmount > 0) && (actualAmount < valueAmount))]
218 // VERIFY [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount]
219 // FROMALTSTACK [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position>]
220 // DUP [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> <position>]
221 // TOALTSTACK [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position>]
222 // 6 [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> 6]
223 // ROLL [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> exchangeAmount]
224 // 999 [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> exchangeAmount 999]
225 // 1000 [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> exchangeAmount 1000]
226 // MULFRACTION [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> receiveAmount]
227 // 3 [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> receiveAmount 3]
228 // ROLL [... sellerKey standardProgram sellerProgram actualAmount <position> receiveAmount requestedAsset]
229 // 1 [... sellerKey standardProgram sellerProgram actualAmount <position> receiveAmount requestedAsset 1]
230 // 5 [... sellerKey standardProgram sellerProgram actualAmount <position> receiveAmount requestedAsset 1 5]
231 // ROLL [... sellerKey standardProgram actualAmount <position> receiveAmount requestedAsset 1 sellerProgram]
232 // CHECKOUTPUT [... sellerKey standardProgram actualAmount checkOutput(receiveAmount, requestedAsset, sellerProgram)]
233 // VERIFY [... sellerKey standardProgram actualAmount]
234 // FROMALTSTACK [... sellerKey standardProgram actualAmount <position>]
235 // 1 [... sellerKey standardProgram actualAmount <position> 1]
236 // ADD [... sellerKey standardProgram actualAmount (<position> + 1)]
237 // AMOUNT [... sellerKey standardProgram actualAmount (<position> + 1) valueAmount]
238 // 2 [... sellerKey standardProgram actualAmount (<position> + 1) valueAmount 2]
239 // ROLL [... sellerKey standardProgram (<position> + 1) valueAmount actualAmount]
240 // SUB [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount)]
241 // ASSET [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount) valueAsset]
242 // 1 [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount) valueAsset 1]
243 // 4 [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount) valueAsset 1 4]
244 // ROLL [... sellerKey (<position> + 1) (valueAmount - actualAmount) valueAsset 1 standardProgram]
245 // CHECKOUTPUT [... sellerKey checkOutput((valueAmount - actualAmount), valueAsset, standardProgram)]
246 // JUMP:$_end [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
247 // $fullTrade [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
248 // AMOUNT [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset valueAmount]
249 // 2 [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset valueAmount 2]
250 // ROLL [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset valueAmount ratioNumerator]
251 // 3 [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset valueAmount ratioNumerator 3]
252 // ROLL [... sellerKey standardProgram sellerProgram requestedAsset valueAmount ratioNumerator ratioDenominator]
253 // MULFRACTION [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount]
254 // 999 [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount 999]
255 // 1000 [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount 999 1000]
256 // MULFRACTION [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount]
257 // DUP [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount requestedAmount]
258 // 0 [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount requestedAmount 0]
259 // GREATERTHAN [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount (requestedAmount > 0)]
260 // VERIFY [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount]
261 // FROMALTSTACK [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount <position>]
262 // SWAP [... sellerKey standardProgram sellerProgram requestedAsset <position> requestedAmount]
263 // 2 [... sellerKey standardProgram sellerProgram requestedAsset <position> requestedAmount 2]
264 // ROLL [... sellerKey standardProgram sellerProgram <position> requestedAmount requestedAsset]
265 // 1 [... sellerKey standardProgram sellerProgram <position> requestedAmount requestedAsset 1]
266 // 4 [... sellerKey standardProgram sellerProgram <position> requestedAmount requestedAsset 1 4]
267 // ROLL [... sellerKey standardProgram <position> requestedAmount requestedAsset 1 sellerProgram]
268 // CHECKOUTPUT [... sellerKey standardProgram checkOutput(requestedAmount, requestedAsset, sellerProgram)]
269 // JUMP:$_end [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
270 // $cancel [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector>]
271 // DROP [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
272 // 6 [... sellerSig sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset 6]
273 // ROLL [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset sellerSig]
274 // 6 [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset sellerSig 6]
275 // ROLL [... standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset sellerSig sellerKey]
276 // TXSIGHASH SWAP CHECKSIG [... standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset checkTxSig(sellerKey, sellerSig)]
277 // $_end [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
278 func P2MCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) {
279 standardProgram, err := P2WMCProgram(magneticContractArgs)
284 builder := NewBuilder()
285 // contract arguments
286 builder.AddData(magneticContractArgs.SellerKey)
287 builder.AddData(standardProgram)
288 builder.AddData(magneticContractArgs.SellerProgram)
289 builder.AddInt64(magneticContractArgs.RatioDenominator)
290 builder.AddInt64(magneticContractArgs.RatioNumerator)
291 builder.AddData(magneticContractArgs.RequestedAsset.Bytes())
293 // contract instructions
294 builder.AddOp(vm.OP_7)
295 builder.AddOp(vm.OP_ROLL)
296 builder.AddOp(vm.OP_TOALTSTACK)
297 builder.AddOp(vm.OP_6)
298 builder.AddOp(vm.OP_ROLL)
299 builder.AddOp(vm.OP_DUP)
300 builder.AddOp(vm.OP_2)
301 builder.AddOp(vm.OP_NUMEQUAL)
304 builder.AddOp(vm.OP_6)
305 builder.AddOp(vm.OP_PICK)
306 builder.AddOp(vm.OP_3)
307 builder.AddOp(vm.OP_ROLL)
308 builder.AddOp(vm.OP_3)
309 builder.AddOp(vm.OP_ROLL)
310 builder.AddOp(vm.OP_MULFRACTION)
311 builder.AddOp(vm.OP_AMOUNT)
312 builder.AddOp(vm.OP_OVER)
313 builder.AddOp(vm.OP_0)
314 builder.AddOp(vm.OP_GREATERTHAN)
315 builder.AddOp(vm.OP_2)
316 builder.AddOp(vm.OP_PICK)
317 builder.AddOp(vm.OP_ROT)
318 builder.AddOp(vm.OP_LESSTHAN)
319 builder.AddOp(vm.OP_BOOLAND)
320 builder.AddOp(vm.OP_VERIFY)
321 builder.AddOp(vm.OP_FROMALTSTACK)
322 builder.AddOp(vm.OP_DUP)
323 builder.AddOp(vm.OP_TOALTSTACK)
324 builder.AddOp(vm.OP_6)
325 builder.AddOp(vm.OP_ROLL)
326 builder.AddInt64(999)
327 builder.AddInt64(1000)
328 builder.AddOp(vm.OP_MULFRACTION)
329 builder.AddOp(vm.OP_3)
330 builder.AddOp(vm.OP_ROLL)
331 builder.AddOp(vm.OP_1)
332 builder.AddOp(vm.OP_5)
333 builder.AddOp(vm.OP_ROLL)
334 builder.AddOp(vm.OP_CHECKOUTPUT)
335 builder.AddOp(vm.OP_VERIFY)
336 builder.AddOp(vm.OP_FROMALTSTACK)
337 builder.AddOp(vm.OP_1)
338 builder.AddOp(vm.OP_ADD)
339 builder.AddOp(vm.OP_AMOUNT)
340 builder.AddOp(vm.OP_ROT)
341 builder.AddOp(vm.OP_SUB)
342 builder.AddOp(vm.OP_ASSET)
343 builder.AddOp(vm.OP_1)
344 builder.AddOp(vm.OP_4)
345 builder.AddOp(vm.OP_ROLL)
346 builder.AddOp(vm.OP_CHECKOUTPUT)
348 builder.SetJumpTarget(1)
349 builder.AddOp(vm.OP_AMOUNT)
350 builder.AddOp(vm.OP_ROT)
351 builder.AddOp(vm.OP_3)
352 builder.AddOp(vm.OP_ROLL)
353 builder.AddOp(vm.OP_MULFRACTION)
354 builder.AddInt64(999)
355 builder.AddInt64(1000)
356 builder.AddOp(vm.OP_MULFRACTION)
357 builder.AddOp(vm.OP_DUP)
358 builder.AddOp(vm.OP_0)
359 builder.AddOp(vm.OP_GREATERTHAN)
360 builder.AddOp(vm.OP_VERIFY)
361 builder.AddOp(vm.OP_FROMALTSTACK)
362 builder.AddOp(vm.OP_SWAP)
363 builder.AddOp(vm.OP_ROT)
364 builder.AddOp(vm.OP_1)
365 builder.AddOp(vm.OP_4)
366 builder.AddOp(vm.OP_ROLL)
367 builder.AddOp(vm.OP_CHECKOUTPUT)
369 builder.SetJumpTarget(0)
370 builder.AddOp(vm.OP_DROP)
371 builder.AddOp(vm.OP_6)
372 builder.AddOp(vm.OP_ROLL)
373 builder.AddOp(vm.OP_6)
374 builder.AddOp(vm.OP_ROLL)
375 builder.AddOp(vm.OP_TXSIGHASH)
376 builder.AddOp(vm.OP_SWAP)
377 builder.AddOp(vm.OP_CHECKSIG)
378 builder.SetJumpTarget(2)
379 builder.SetJumpTarget(3)
380 return builder.Build()