4 "github.com/vapor/crypto/ed25519"
5 "github.com/vapor/errors"
6 "github.com/vapor/protocol/bc"
7 "github.com/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 // lock exchangeAmount of requestedAsset with sellerProgram
172 // lock valueAmount-actualAmount of valueAsset with standardProgram
173 // unlock actualAmount of valueAsset
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
181 // clause cancel(sellerSig: Signature) {
182 // verify checkTxSig(sellerKey, sellerSig)
183 // unlock valueAmount of valueAsset
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)
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())
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)
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)
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)
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()