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
26 // IsUnspendable checks if a contorl program is absolute failed
27 func IsUnspendable(prog []byte) bool {
28 return len(prog) > 0 && prog[0] == byte(vm.OP_FAIL)
31 func (b *Builder) addP2SPMultiSig(pubkeys []ed25519.PublicKey, nrequired int) error {
32 if err := checkMultiSigParams(int64(nrequired), int64(len(pubkeys))); err != nil {
36 b.AddOp(vm.OP_TXSIGHASH) // stack is now [... NARGS SIG SIG SIG PREDICATEHASH]
37 for _, p := range pubkeys {
40 b.AddInt64(int64(nrequired)) // stack is now [... SIG SIG SIG PREDICATEHASH PUB PUB PUB M]
41 b.AddInt64(int64(len(pubkeys))) // stack is now [... SIG SIG SIG PREDICATEHASH PUB PUB PUB M N]
42 b.AddOp(vm.OP_CHECKMULTISIG) // stack is now [... NARGS]
46 // DefaultCoinbaseProgram generates the script for contorl coinbase output
47 func DefaultCoinbaseProgram() ([]byte, error) {
48 builder := NewBuilder()
49 builder.AddOp(vm.OP_TRUE)
50 return builder.Build()
53 // P2WPKHProgram return the segwit pay to public key hash
54 func P2WPKHProgram(hash []byte) ([]byte, error) {
55 builder := NewBuilder()
58 return builder.Build()
61 // P2WSHProgram return the segwit pay to script hash
62 func P2WSHProgram(hash []byte) ([]byte, error) {
63 builder := NewBuilder()
66 return builder.Build()
69 // RetireProgram generates the script for retire output
70 func RetireProgram(comment []byte) ([]byte, error) {
71 builder := NewBuilder()
72 builder.AddOp(vm.OP_FAIL)
73 if len(comment) != 0 {
74 builder.AddData(comment)
76 return builder.Build()
79 // P2PKHSigProgram generates the script for control with pubkey hash
80 func P2PKHSigProgram(pubkeyHash []byte) ([]byte, error) {
81 builder := NewBuilder()
82 builder.AddOp(vm.OP_DUP)
83 builder.AddOp(vm.OP_HASH160)
84 builder.AddData(pubkeyHash)
85 builder.AddOp(vm.OP_EQUALVERIFY)
86 builder.AddOp(vm.OP_TXSIGHASH)
87 builder.AddOp(vm.OP_SWAP)
88 builder.AddOp(vm.OP_CHECKSIG)
89 return builder.Build()
92 // P2SHProgram generates the script for control with script hash
93 func P2SHProgram(scriptHash []byte) ([]byte, error) {
94 builder := NewBuilder()
95 builder.AddOp(vm.OP_DUP)
96 builder.AddOp(vm.OP_SHA3)
97 builder.AddData(scriptHash)
98 builder.AddOp(vm.OP_EQUALVERIFY)
100 builder.AddOp(vm.OP_SWAP)
102 builder.AddOp(vm.OP_CHECKPREDICATE)
103 return builder.Build()
106 // P2SPMultiSigProgram generates the script for control transaction output
107 func P2SPMultiSigProgram(pubkeys []ed25519.PublicKey, nrequired int) ([]byte, error) {
108 builder := NewBuilder()
109 if err := builder.addP2SPMultiSig(pubkeys, nrequired); err != nil {
112 return builder.Build()
115 // P2SPMultiSigProgramWithHeight generates the script with block height for control transaction output
116 func P2SPMultiSigProgramWithHeight(pubkeys []ed25519.PublicKey, nrequired int, blockHeight int64) ([]byte, error) {
117 builder := NewBuilder()
119 builder.AddInt64(blockHeight)
120 builder.AddOp(vm.OP_BLOCKHEIGHT)
121 builder.AddOp(vm.OP_GREATERTHAN)
122 builder.AddOp(vm.OP_VERIFY)
123 } else if blockHeight < 0 {
124 return nil, errors.WithDetail(ErrBadValue, "negative blockHeight")
126 if err := builder.addP2SPMultiSig(pubkeys, nrequired); err != nil {
129 return builder.Build()
132 func checkMultiSigParams(nrequired, npubkeys int64) error {
134 return errors.WithDetail(ErrBadValue, "negative quorum")
137 return errors.WithDetail(ErrBadValue, "negative pubkey count")
139 if nrequired > npubkeys {
140 return errors.WithDetail(ErrBadValue, "quorum too big")
142 if nrequired == 0 && npubkeys > 0 {
143 return errors.WithDetail(ErrBadValue, "quorum empty with non-empty pubkey list")
148 // P2WMCProgram return the segwit pay to magnetic contract
149 func P2WMCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) {
150 builder := NewBuilder()
152 builder.AddData(magneticContractArgs.RequestedAsset.Bytes())
153 builder.AddInt64(magneticContractArgs.RatioNumerator)
154 builder.AddInt64(magneticContractArgs.RatioDenominator)
155 builder.AddData(magneticContractArgs.SellerProgram)
156 builder.AddData(magneticContractArgs.SellerKey)
157 return builder.Build()
160 // P2MCProgram generates the script for control with magnetic contract
162 // MagneticContract source code:
163 // contract MagneticContract(requestedAsset: Asset,
164 // ratioNumerator: Integer,
165 // ratioDenominator: Integer,
166 // sellerProgram: Program,
167 // standardProgram: Program,
168 // sellerKey: PublicKey) locks valueAmount of valueAsset {
169 // clause partialTrade(exchangeAmount: Amount) {
170 // define actualAmount: Integer = exchangeAmount * ratioDenominator / ratioNumerator
171 // verify actualAmount >= 0 && actualAmount < valueAmount
172 // define receiveAmount: Integer = exchangeAmount * 999 / 1000
173 // lock receiveAmount of requestedAsset with sellerProgram
174 // lock valueAmount-actualAmount of valueAsset with standardProgram
175 // unlock actualAmount of valueAsset
177 // clause fullTrade() {
178 // define requestedAmount: Integer = valueAmount * ratioNumerator / ratioDenominator
179 // define requestedAmount: Integer = requestedAmount * 999 / 1000
180 // verify requestedAmount >= 0
181 // lock requestedAmount of requestedAsset with sellerProgram
182 // unlock valueAmount of valueAsset
184 // clause cancel(sellerSig: Signature) {
185 // verify checkTxSig(sellerKey, sellerSig)
186 // unlock valueAmount of valueAsset
190 // contract stack flow:
191 // 7 [... <position> <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset 7]
192 // ROLL [... <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <position>]
193 // TOALTSTACK [... <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
194 // 6 [... <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset 6]
195 // ROLL [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector>]
196 // DUP [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector> <clause selector>]
197 // 2 [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector> <clause selector> 2]
198 // NUMEQUAL [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector> (<clause selector> == 2)]
199 // JUMPIF:$cancel [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector>]
200 // JUMPIF:$fullTrade [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
201 // $partialTrade [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
202 // 6 [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset 6]
203 // PICK [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset exchangeAmount]
204 // 3 [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset exchangeAmount 3]
205 // ROLL [... exchangeAmount sellerKey standardProgram sellerProgram ratioNumerator requestedAsset exchangeAmount ratioDenominator]
206 // 3 [... exchangeAmount sellerKey standardProgram sellerProgram ratioNumerator requestedAsset exchangeAmount ratioDenominator 3]
207 // ROLL [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset exchangeAmount ratioDenominator ratioNumerator]
208 // MULFRACTION [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount]
209 // AMOUNT [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount]
210 // OVER [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount actualAmount]
211 // 0 [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount actualAmount 0]
212 // GREATERTHANOREQUAL [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0)]
213 // 2 [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0) 2]
214 // PICK [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0) actualAmount]
215 // 2 [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0) actualAmount 2]
216 // ROLL [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount (actualAmount > 0) actualAmount valueAmount]
217 // LESSTHAN [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount (actualAmount > 0) (actualAmount < valueAmount)]
218 // BOOLAND [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount ((actualAmount > 0) && (actualAmount < valueAmount))]
219 // VERIFY [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount]
220 // FROMALTSTACK [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position>]
221 // DUP [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> <position>]
222 // TOALTSTACK [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position>]
223 // 6 [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> 6]
224 // ROLL [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> exchangeAmount]
225 // 999 [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> exchangeAmount 999]
226 // 1000 [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> exchangeAmount 1000]
227 // MULFRACTION [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> receiveAmount]
228 // 3 [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> receiveAmount 3]
229 // ROLL [... sellerKey standardProgram sellerProgram actualAmount <position> receiveAmount requestedAsset]
230 // 1 [... sellerKey standardProgram sellerProgram actualAmount <position> receiveAmount requestedAsset 1]
231 // 5 [... sellerKey standardProgram sellerProgram actualAmount <position> receiveAmount requestedAsset 1 5]
232 // ROLL [... sellerKey standardProgram actualAmount <position> receiveAmount requestedAsset 1 sellerProgram]
233 // CHECKOUTPUT [... sellerKey standardProgram actualAmount checkOutput(receiveAmount, requestedAsset, sellerProgram)]
234 // VERIFY [... sellerKey standardProgram actualAmount]
235 // FROMALTSTACK [... sellerKey standardProgram actualAmount <position>]
236 // 1 [... sellerKey standardProgram actualAmount <position> 1]
237 // ADD [... sellerKey standardProgram actualAmount (<position> + 1)]
238 // AMOUNT [... sellerKey standardProgram actualAmount (<position> + 1) valueAmount]
239 // 2 [... sellerKey standardProgram actualAmount (<position> + 1) valueAmount 2]
240 // ROLL [... sellerKey standardProgram (<position> + 1) valueAmount actualAmount]
241 // SUB [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount)]
242 // ASSET [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount) valueAsset]
243 // 1 [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount) valueAsset 1]
244 // 4 [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount) valueAsset 1 4]
245 // ROLL [... sellerKey (<position> + 1) (valueAmount - actualAmount) valueAsset 1 standardProgram]
246 // CHECKOUTPUT [... sellerKey checkOutput((valueAmount - actualAmount), valueAsset, standardProgram)]
247 // JUMP:$_end [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
248 // $fullTrade [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
249 // AMOUNT [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset valueAmount]
250 // 2 [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset valueAmount 2]
251 // ROLL [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset valueAmount ratioNumerator]
252 // 3 [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset valueAmount ratioNumerator 3]
253 // ROLL [... sellerKey standardProgram sellerProgram requestedAsset valueAmount ratioNumerator ratioDenominator]
254 // MULFRACTION [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount]
255 // 999 [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount 999]
256 // 1000 [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount 999 1000]
257 // MULFRACTION [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount]
258 // DUP [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount requestedAmount]
259 // 0 [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount requestedAmount 0]
260 // GREATERTHANOREQUAL [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount (requestedAmount > 0)]
261 // VERIFY [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount]
262 // FROMALTSTACK [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount <position>]
263 // SWAP [... sellerKey standardProgram sellerProgram requestedAsset <position> requestedAmount]
264 // 2 [... sellerKey standardProgram sellerProgram requestedAsset <position> requestedAmount 2]
265 // ROLL [... sellerKey standardProgram sellerProgram <position> requestedAmount requestedAsset]
266 // 1 [... sellerKey standardProgram sellerProgram <position> requestedAmount requestedAsset 1]
267 // 4 [... sellerKey standardProgram sellerProgram <position> requestedAmount requestedAsset 1 4]
268 // ROLL [... sellerKey standardProgram <position> requestedAmount requestedAsset 1 sellerProgram]
269 // CHECKOUTPUT [... sellerKey standardProgram checkOutput(requestedAmount, requestedAsset, sellerProgram)]
270 // JUMP:$_end [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
271 // $cancel [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector>]
272 // DROP [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
273 // 6 [... sellerSig sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset 6]
274 // ROLL [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset sellerSig]
275 // 6 [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset sellerSig 6]
276 // ROLL [... standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset sellerSig sellerKey]
277 // TXSIGHASH SWAP CHECKSIG [... standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset checkTxSig(sellerKey, sellerSig)]
278 // $_end [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
279 func P2MCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) {
280 standardProgram, err := P2WMCProgram(magneticContractArgs)
285 builder := NewBuilder()
286 // contract arguments
287 builder.AddData(magneticContractArgs.SellerKey)
288 builder.AddData(standardProgram)
289 builder.AddData(magneticContractArgs.SellerProgram)
290 builder.AddInt64(magneticContractArgs.RatioDenominator)
291 builder.AddInt64(magneticContractArgs.RatioNumerator)
292 builder.AddData(magneticContractArgs.RequestedAsset.Bytes())
294 // contract instructions
295 builder.AddOp(vm.OP_7)
296 builder.AddOp(vm.OP_ROLL)
297 builder.AddOp(vm.OP_TOALTSTACK)
298 builder.AddOp(vm.OP_6)
299 builder.AddOp(vm.OP_ROLL)
300 builder.AddOp(vm.OP_DUP)
301 builder.AddOp(vm.OP_2)
302 builder.AddOp(vm.OP_NUMEQUAL)
305 builder.AddOp(vm.OP_6)
306 builder.AddOp(vm.OP_PICK)
307 builder.AddOp(vm.OP_3)
308 builder.AddOp(vm.OP_ROLL)
309 builder.AddOp(vm.OP_3)
310 builder.AddOp(vm.OP_ROLL)
311 builder.AddOp(vm.OP_MULFRACTION)
312 builder.AddOp(vm.OP_AMOUNT)
313 builder.AddOp(vm.OP_OVER)
314 builder.AddOp(vm.OP_0)
315 builder.AddOp(vm.OP_GREATERTHANOREQUAL)
316 builder.AddOp(vm.OP_2)
317 builder.AddOp(vm.OP_PICK)
318 builder.AddOp(vm.OP_ROT)
319 builder.AddOp(vm.OP_LESSTHAN)
320 builder.AddOp(vm.OP_BOOLAND)
321 builder.AddOp(vm.OP_VERIFY)
322 builder.AddOp(vm.OP_FROMALTSTACK)
323 builder.AddOp(vm.OP_DUP)
324 builder.AddOp(vm.OP_TOALTSTACK)
325 builder.AddOp(vm.OP_6)
326 builder.AddOp(vm.OP_ROLL)
327 builder.AddInt64(999)
328 builder.AddInt64(1000)
329 builder.AddOp(vm.OP_MULFRACTION)
330 builder.AddOp(vm.OP_3)
331 builder.AddOp(vm.OP_ROLL)
332 builder.AddOp(vm.OP_1)
333 builder.AddOp(vm.OP_5)
334 builder.AddOp(vm.OP_ROLL)
335 builder.AddOp(vm.OP_CHECKOUTPUT)
336 builder.AddOp(vm.OP_VERIFY)
337 builder.AddOp(vm.OP_FROMALTSTACK)
338 builder.AddOp(vm.OP_1)
339 builder.AddOp(vm.OP_ADD)
340 builder.AddOp(vm.OP_AMOUNT)
341 builder.AddOp(vm.OP_ROT)
342 builder.AddOp(vm.OP_SUB)
343 builder.AddOp(vm.OP_ASSET)
344 builder.AddOp(vm.OP_1)
345 builder.AddOp(vm.OP_4)
346 builder.AddOp(vm.OP_ROLL)
347 builder.AddOp(vm.OP_CHECKOUTPUT)
349 builder.SetJumpTarget(1)
350 builder.AddOp(vm.OP_AMOUNT)
351 builder.AddOp(vm.OP_ROT)
352 builder.AddOp(vm.OP_3)
353 builder.AddOp(vm.OP_ROLL)
354 builder.AddOp(vm.OP_MULFRACTION)
355 builder.AddInt64(999)
356 builder.AddInt64(1000)
357 builder.AddOp(vm.OP_MULFRACTION)
358 builder.AddOp(vm.OP_DUP)
359 builder.AddOp(vm.OP_0)
360 builder.AddOp(vm.OP_GREATERTHANOREQUAL)
361 builder.AddOp(vm.OP_VERIFY)
362 builder.AddOp(vm.OP_FROMALTSTACK)
363 builder.AddOp(vm.OP_SWAP)
364 builder.AddOp(vm.OP_ROT)
365 builder.AddOp(vm.OP_1)
366 builder.AddOp(vm.OP_4)
367 builder.AddOp(vm.OP_ROLL)
368 builder.AddOp(vm.OP_CHECKOUTPUT)
370 builder.SetJumpTarget(0)
371 builder.AddOp(vm.OP_DROP)
372 builder.AddOp(vm.OP_6)
373 builder.AddOp(vm.OP_ROLL)
374 builder.AddOp(vm.OP_6)
375 builder.AddOp(vm.OP_ROLL)
376 builder.AddOp(vm.OP_TXSIGHASH)
377 builder.AddOp(vm.OP_SWAP)
378 builder.AddOp(vm.OP_CHECKSIG)
379 builder.SetJumpTarget(2)
380 builder.SetJumpTarget(3)
381 return builder.Build()