"errors"
"github.com/vapor/consensus"
+ "github.com/vapor/protocol/bc"
"github.com/vapor/protocol/vm"
"github.com/vapor/protocol/vm/vmutil"
)
+// IsP2WScript is used to determine whether it is a P2WScript or not
func IsP2WScript(prog []byte) bool {
return IsP2WPKHScript(prog) || IsP2WSHScript(prog) || IsStraightforward(prog)
}
+// IsStraightforward is used to determine whether it is a Straightforward script or not
func IsStraightforward(prog []byte) bool {
insts, err := vm.ParseProgram(prog)
if err != nil {
return insts[0].Op == vm.OP_TRUE || insts[0].Op == vm.OP_FAIL
}
+// IsP2WPKHScript is used to determine whether it is a P2WPKH script or not
func IsP2WPKHScript(prog []byte) bool {
insts, err := vm.ParseProgram(prog)
if err != nil {
return insts[1].Op == vm.OP_DATA_20 && len(insts[1].Data) == consensus.PayToWitnessPubKeyHashDataSize
}
+// IsP2WSHScript is used to determine whether it is a P2WSH script or not
func IsP2WSHScript(prog []byte) bool {
insts, err := vm.ParseProgram(prog)
if err != nil {
return insts[1].Op == vm.OP_DATA_32 && len(insts[1].Data) == consensus.PayToWitnessScriptHashDataSize
}
+// IsP2WMCScript is used to determine whether it is a P2WMC script or not
+func IsP2WMCScript(prog []byte) bool {
+ insts, err := vm.ParseProgram(prog)
+ if err != nil {
+ return false
+ }
+
+ if len(insts) != 6 {
+ return false
+ }
+
+ if insts[0].Op > vm.OP_16 {
+ return false
+ }
+
+ if insts[1].Op != vm.OP_DATA_32 || len(insts[1].Data) != 32 {
+ return false
+ }
+
+ if !(insts[2].IsPushdata() && insts[3].IsPushdata() && insts[4].IsPushdata()) {
+ return false
+ }
+
+ if _, err = vm.AsInt64(insts[2].Data); err != nil {
+ return false
+ }
+
+ if _, err = vm.AsInt64(insts[3].Data); err != nil {
+ return false
+ }
+ return insts[5].Op == vm.OP_DATA_32 && len(insts[5].Data) == 32
+}
+
+// ConvertP2PKHSigProgram convert standard P2WPKH program into P2PKH program
func ConvertP2PKHSigProgram(prog []byte) ([]byte, error) {
insts, err := vm.ParseProgram(prog)
if err != nil {
return nil, errors.New("unknow P2PKH version number")
}
+// ConvertP2SHProgram convert standard P2WSH program into P2SH program
func ConvertP2SHProgram(prog []byte) ([]byte, error) {
insts, err := vm.ParseProgram(prog)
if err != nil {
return nil, errors.New("unknow P2SHP version number")
}
+// ConvertP2MCProgram convert standard P2WMC program into P2MC program
+func ConvertP2MCProgram(prog []byte) ([]byte, error) {
+ magneticContractArgs, err := DecodeP2WMCProgram(prog)
+ if err != nil {
+ return nil, err
+ }
+ return vmutil.P2MCProgram(*magneticContractArgs)
+}
+
+// DecodeP2WMCProgram parse standard P2WMC arguments to magneticContractArgs
+func DecodeP2WMCProgram(prog []byte) (*vmutil.MagneticContractArgs, error) {
+ if !IsP2WMCScript(prog) {
+ return nil, errors.New("invalid P2MC program")
+ }
+
+ insts, err := vm.ParseProgram(prog)
+ if err != nil {
+ return nil, err
+ }
+
+ magneticContractArgs := &vmutil.MagneticContractArgs{
+ SellerProgram: insts[4].Data,
+ SellerKey: insts[5].Data,
+ }
+ requestedAsset := [32]byte{}
+ copy(requestedAsset[:], insts[1].Data)
+ magneticContractArgs.RequestedAsset = bc.NewAssetID(requestedAsset)
+
+ if magneticContractArgs.RatioMolecule, err = vm.AsInt64(insts[2].Data); err != nil {
+ return nil, err
+ }
+
+ if magneticContractArgs.RatioDenominator, err = vm.AsInt64(insts[3].Data); err != nil {
+ return nil, err
+ }
+ return magneticContractArgs, nil
+}
+
+// GetHashFromStandardProg get hash from standard program
func GetHashFromStandardProg(prog []byte) ([]byte, error) {
insts, err := vm.ParseProgram(prog)
if err != nil {
}
}
+func TestMagneticContractTx(t *testing.T) {
+ buyerArgs := vmutil.MagneticContractArgs{
+ RequestedAsset: bc.AssetID{V0: 1},
+ RatioMolecule: 1,
+ RatioDenominator: 2,
+ SellerProgram: []byte{0x51},
+ SellerKey: testutil.MustDecodeHexString("960ecabafb88ba460a40912841afecebf0e84884178611ac97210e327c0d1173"),
+ }
+
+ sellerArgs := vmutil.MagneticContractArgs{
+ RequestedAsset: bc.AssetID{V0: 2},
+ RatioMolecule: 2,
+ RatioDenominator: 1,
+ SellerProgram: []byte{0x52},
+ SellerKey: testutil.MustDecodeHexString("ad79ec6bd3a6d6dbe4d0ee902afc99a12b9702fb63edce5f651db3081d868b75"),
+ }
+
+ programBuyer, err := vmutil.P2WMCProgram(buyerArgs)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ programSeller, err := vmutil.P2WMCProgram(sellerArgs)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cases := []struct {
+ desc string
+ block *bc.Block
+ err error
+ }{
+ {
+ desc: "contracts all full trade",
+ block: &bc.Block{
+ BlockHeader: &bc.BlockHeader{Version: 0},
+ Transactions: []*bc.Tx{
+ types.MapTx(&types.TxData{
+ SerializedSize: 1,
+ Inputs: []*types.TxInput{
+ types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, bc.Hash{V0: 10}, buyerArgs.RequestedAsset, 100000000, 1, programSeller),
+ types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, bc.Hash{V0: 20}, sellerArgs.RequestedAsset, 200000000, 0, programBuyer),
+ },
+ Outputs: []*types.TxOutput{
+ types.NewIntraChainOutput(sellerArgs.RequestedAsset, 200000000, sellerArgs.SellerProgram),
+ types.NewIntraChainOutput(buyerArgs.RequestedAsset, 100000000, buyerArgs.SellerProgram),
+ },
+ }),
+ },
+ },
+ err: nil,
+ },
+ {
+ desc: "first contract partial trade, second contract full trade",
+ block: &bc.Block{
+ BlockHeader: &bc.BlockHeader{Version: 0},
+ Transactions: []*bc.Tx{
+ types.MapTx(&types.TxData{
+ SerializedSize: 1,
+ Inputs: []*types.TxInput{
+ types.NewSpendInput([][]byte{vm.Int64Bytes(100000000), vm.Int64Bytes(0), vm.Int64Bytes(0)}, bc.Hash{V0: 10}, buyerArgs.RequestedAsset, 200000000, 1, programSeller),
+ types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, bc.Hash{V0: 20}, sellerArgs.RequestedAsset, 100000000, 0, programBuyer),
+ },
+ Outputs: []*types.TxOutput{
+ types.NewIntraChainOutput(sellerArgs.RequestedAsset, 100000000, sellerArgs.SellerProgram),
+ types.NewIntraChainOutput(buyerArgs.RequestedAsset, 150000000, programSeller),
+ types.NewIntraChainOutput(buyerArgs.RequestedAsset, 50000000, buyerArgs.SellerProgram),
+ },
+ }),
+ },
+ },
+ err: nil,
+ },
+ {
+ desc: "first contract full trade, second contract partial trade",
+ block: &bc.Block{
+ BlockHeader: &bc.BlockHeader{Version: 0},
+ Transactions: []*bc.Tx{
+ types.MapTx(&types.TxData{
+ SerializedSize: 1,
+ Inputs: []*types.TxInput{
+ types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, bc.Hash{V0: 10}, buyerArgs.RequestedAsset, 100000000, 1, programSeller),
+ types.NewSpendInput([][]byte{vm.Int64Bytes(100000000), vm.Int64Bytes(1), vm.Int64Bytes(0)}, bc.Hash{V0: 20}, sellerArgs.RequestedAsset, 300000000, 0, programBuyer),
+ },
+ Outputs: []*types.TxOutput{
+ types.NewIntraChainOutput(sellerArgs.RequestedAsset, 200000000, sellerArgs.SellerProgram),
+ types.NewIntraChainOutput(buyerArgs.RequestedAsset, 100000000, buyerArgs.SellerProgram),
+ types.NewIntraChainOutput(sellerArgs.RequestedAsset, 100000000, programBuyer),
+ },
+ }),
+ },
+ },
+ err: nil,
+ },
+ {
+ desc: "cancel magnetic contract",
+ block: &bc.Block{
+ BlockHeader: &bc.BlockHeader{Version: 0},
+ Transactions: []*bc.Tx{
+ types.MapTx(&types.TxData{
+ SerializedSize: 1,
+ Inputs: []*types.TxInput{
+ types.NewSpendInput([][]byte{testutil.MustDecodeHexString("0a72a2b2944ec9b4bcdef392e6c532effc77ea536809fa290a12e39df7651851a9939e23e492369dc8936e0ebf3ecd1de4e9077d0593bd3fcb5874fb26dfc60a"), vm.Int64Bytes(0), vm.Int64Bytes(2)}, bc.Hash{V0: 10}, buyerArgs.RequestedAsset, 100000000, 0, programSeller),
+ },
+ Outputs: []*types.TxOutput{
+ types.NewIntraChainOutput(buyerArgs.RequestedAsset, 100000000, sellerArgs.SellerProgram),
+ },
+ }),
+ },
+ },
+ err: nil,
+ },
+ {
+ desc: "wrong signature with cancel magnetic contract",
+ block: &bc.Block{
+ BlockHeader: &bc.BlockHeader{Version: 0},
+ Transactions: []*bc.Tx{
+ types.MapTx(&types.TxData{
+ SerializedSize: 1,
+ Inputs: []*types.TxInput{
+ types.NewSpendInput([][]byte{testutil.MustDecodeHexString("686b983a8de1893ef723144389fd1f07b12b048f52f389faa863243195931d5732dbfd15470b43ed63d5067900718cf94f137073f4a972d277bbd967b022545d"), vm.Int64Bytes(0), vm.Int64Bytes(2)}, bc.Hash{V0: 10}, buyerArgs.RequestedAsset, 100000000, 0, programSeller),
+ },
+ Outputs: []*types.TxOutput{
+ types.NewIntraChainOutput(buyerArgs.RequestedAsset, 100000000, sellerArgs.SellerProgram),
+ },
+ }),
+ },
+ },
+ err: vm.ErrFalseVMResult,
+ },
+ {
+ desc: "wrong output amount with contracts all full trade",
+ block: &bc.Block{
+ BlockHeader: &bc.BlockHeader{Version: 0},
+ Transactions: []*bc.Tx{
+ types.MapTx(&types.TxData{
+ SerializedSize: 1,
+ Inputs: []*types.TxInput{
+ types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, bc.Hash{V0: 10}, buyerArgs.RequestedAsset, 100000000, 1, programSeller),
+ types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, bc.Hash{V0: 20}, sellerArgs.RequestedAsset, 200000000, 0, programBuyer),
+ },
+ Outputs: []*types.TxOutput{
+ types.NewIntraChainOutput(sellerArgs.RequestedAsset, 200000000, sellerArgs.SellerProgram),
+ types.NewIntraChainOutput(buyerArgs.RequestedAsset, 50000000, buyerArgs.SellerProgram),
+ types.NewIntraChainOutput(buyerArgs.RequestedAsset, 50000000, []byte{0x55}),
+ },
+ }),
+ },
+ },
+ err: vm.ErrFalseVMResult,
+ },
+ {
+ desc: "wrong output assetID with contracts all full trade",
+ block: &bc.Block{
+ BlockHeader: &bc.BlockHeader{Version: 0},
+ Transactions: []*bc.Tx{
+ types.MapTx(&types.TxData{
+ SerializedSize: 1,
+ Inputs: []*types.TxInput{
+ types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, bc.Hash{V0: 10}, buyerArgs.RequestedAsset, 100000000, 1, programSeller),
+ types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, bc.Hash{V0: 20}, sellerArgs.RequestedAsset, 200000000, 0, programBuyer),
+ types.NewSpendInput(nil, bc.Hash{V0: 30}, bc.AssetID{V0: 1}, 200000000, 0, []byte{0x51}),
+ },
+ Outputs: []*types.TxOutput{
+ types.NewIntraChainOutput(bc.AssetID{V0: 1}, 200000000, sellerArgs.SellerProgram),
+ types.NewIntraChainOutput(buyerArgs.RequestedAsset, 100000000, buyerArgs.SellerProgram),
+ types.NewIntraChainOutput(sellerArgs.RequestedAsset, 200000000, []byte{0x55}),
+ },
+ }),
+ },
+ },
+ err: vm.ErrFalseVMResult,
+ },
+ {
+ desc: "wrong output change program with first contract partial trade and second contract full trade",
+ block: &bc.Block{
+ BlockHeader: &bc.BlockHeader{Version: 0},
+ Transactions: []*bc.Tx{
+ types.MapTx(&types.TxData{
+ SerializedSize: 1,
+ Inputs: []*types.TxInput{
+ types.NewSpendInput([][]byte{vm.Int64Bytes(100000000), vm.Int64Bytes(0), vm.Int64Bytes(0)}, bc.Hash{V0: 10}, buyerArgs.RequestedAsset, 200000000, 1, programSeller),
+ types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, bc.Hash{V0: 20}, sellerArgs.RequestedAsset, 100000000, 0, programBuyer),
+ },
+ Outputs: []*types.TxOutput{
+ types.NewIntraChainOutput(sellerArgs.RequestedAsset, 100000000, sellerArgs.SellerProgram),
+ types.NewIntraChainOutput(buyerArgs.RequestedAsset, 150000000, []byte{0x55}),
+ types.NewIntraChainOutput(buyerArgs.RequestedAsset, 50000000, buyerArgs.SellerProgram),
+ },
+ }),
+ },
+ },
+ err: vm.ErrFalseVMResult,
+ },
+ }
+
+ for i, c := range cases {
+ if _, err := ValidateTx(c.block.Transactions[0], c.block); rootErr(err) != c.err {
+ t.Errorf("case #%d (%s) got error %t, want %t", i, c.desc, rootErr(err), c.err)
+ }
+ }
+}
+
+func TestRingMagneticContractTx(t *testing.T) {
+ aliceArgs := vmutil.MagneticContractArgs{
+ RequestedAsset: bc.AssetID{V0: 1},
+ RatioMolecule: 2,
+ RatioDenominator: 1,
+ SellerProgram: []byte{0x51},
+ SellerKey: testutil.MustDecodeHexString("960ecabafb88ba460a40912841afecebf0e84884178611ac97210e327c0d1173"),
+ }
+
+ bobArgs := vmutil.MagneticContractArgs{
+ RequestedAsset: bc.AssetID{V0: 2},
+ RatioMolecule: 2,
+ RatioDenominator: 1,
+ SellerProgram: []byte{0x52},
+ SellerKey: testutil.MustDecodeHexString("ad79ec6bd3a6d6dbe4d0ee902afc99a12b9702fb63edce5f651db3081d868b75"),
+ }
+
+ jackArgs := vmutil.MagneticContractArgs{
+ RequestedAsset: bc.AssetID{V0: 3},
+ RatioMolecule: 1,
+ RatioDenominator: 4,
+ SellerProgram: []byte{0x53},
+ SellerKey: testutil.MustDecodeHexString("9c19a91988c62046c2767bd7e9999b0c142891b9ebf467bfa59210b435cb0de7"),
+ }
+
+ aliceProgram, err := vmutil.P2WMCProgram(aliceArgs)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ bobProgram, err := vmutil.P2WMCProgram(bobArgs)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ jackProgram, err := vmutil.P2WMCProgram(jackArgs)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cases := []struct {
+ desc string
+ block *bc.Block
+ err error
+ }{
+ {
+ desc: "contracts all full trade",
+ block: &bc.Block{
+ BlockHeader: &bc.BlockHeader{Version: 0},
+ Transactions: []*bc.Tx{
+ types.MapTx(&types.TxData{
+ SerializedSize: 1,
+ Inputs: []*types.TxInput{
+ types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, bc.Hash{V0: 10}, jackArgs.RequestedAsset, 100000000, 0, aliceProgram),
+ types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, bc.Hash{V0: 20}, aliceArgs.RequestedAsset, 200000000, 0, bobProgram),
+ types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, bc.Hash{V0: 30}, bobArgs.RequestedAsset, 400000000, 0, jackProgram),
+ },
+ Outputs: []*types.TxOutput{
+ types.NewIntraChainOutput(aliceArgs.RequestedAsset, 200000000, aliceArgs.SellerProgram),
+ types.NewIntraChainOutput(bobArgs.RequestedAsset, 400000000, bobArgs.SellerProgram),
+ types.NewIntraChainOutput(jackArgs.RequestedAsset, 100000000, jackArgs.SellerProgram),
+ },
+ }),
+ },
+ },
+ err: nil,
+ },
+ }
+
+ for i, c := range cases {
+ if _, err := ValidateTx(c.block.Transactions[0], c.block); rootErr(err) != c.err {
+ t.Errorf("case #%d (%s) got error %t, want %t", i, c.desc, rootErr(err), c.err)
+ }
+ }
+}
+
// A txFixture is returned by sample (below) to produce a sample
// transaction, which takes a separate, optional _input_ txFixture to
// affect the transaction that's built. The components of the
}
func witnessProgram(prog []byte) []byte {
- if segwit.IsP2WPKHScript(prog) {
- if witnessProg, err := segwit.ConvertP2PKHSigProgram([]byte(prog)); err == nil {
+ switch {
+ case segwit.IsP2WPKHScript(prog):
+ if witnessProg, err := segwit.ConvertP2PKHSigProgram(prog); err == nil {
return witnessProg
}
- } else if segwit.IsP2WSHScript(prog) {
- if witnessProg, err := segwit.ConvertP2SHProgram([]byte(prog)); err == nil {
+ case segwit.IsP2WSHScript(prog):
+ if witnessProg, err := segwit.ConvertP2SHProgram(prog); err == nil {
+ return witnessProg
+ }
+ case segwit.IsP2WMCScript(prog):
+ if witnessProg, err := segwit.ConvertP2MCProgram(prog); err == nil {
return witnessProg
}
}
import (
"github.com/vapor/crypto/ed25519"
"github.com/vapor/errors"
+ "github.com/vapor/protocol/bc"
"github.com/vapor/protocol/vm"
)
ErrMultisigFormat = errors.New("bad multisig program format")
)
+// MagneticContractArgs is a struct for magnetic contract arguments
+type MagneticContractArgs struct {
+ RequestedAsset bc.AssetID
+ RatioMolecule int64
+ RatioDenominator int64
+ SellerProgram []byte
+ SellerKey []byte
+}
+
// IsUnspendable checks if a contorl program is absolute failed
func IsUnspendable(prog []byte) bool {
return len(prog) > 0 && prog[0] == byte(vm.OP_FAIL)
}
return nil
}
+
+// P2WMCProgram return the segwit pay to magnetic contract
+func P2WMCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) {
+ builder := NewBuilder()
+ builder.AddInt64(0)
+ builder.AddData(magneticContractArgs.RequestedAsset.Bytes())
+ builder.AddInt64(magneticContractArgs.RatioMolecule)
+ builder.AddInt64(magneticContractArgs.RatioDenominator)
+ builder.AddData(magneticContractArgs.SellerProgram)
+ builder.AddData(magneticContractArgs.SellerKey)
+ return builder.Build()
+}
+
+// P2MCProgram generates the script for control with magnetic contract
+//
+// MagneticContract source code:
+// contract MagneticContract(requestedAsset: Asset,
+// ratioMolecule: Integer,
+// ratioDenominator: Integer,
+// sellerProgram: Program,
+// standardProgram: Program,
+// sellerKey: PublicKey) locks valueAmount of valueAsset {
+// clause partialTrade(exchangeAmount: Amount) {
+// define actualAmount: Integer = exchangeAmount * ratioDenominator / ratioMolecule
+// verify actualAmount > 0 && actualAmount < valueAmount
+// lock exchangeAmount of requestedAsset with sellerProgram
+// lock valueAmount-actualAmount of valueAsset with standardProgram
+// unlock actualAmount of valueAsset
+// }
+// clause fullTrade() {
+// define requestedAmount: Integer = valueAmount * ratioMolecule / ratioDenominator
+// verify requestedAmount > 0
+// lock requestedAmount of requestedAsset with sellerProgram
+// unlock valueAmount of valueAsset
+// }
+// clause cancel(sellerSig: Signature) {
+// verify checkTxSig(sellerKey, sellerSig)
+// unlock valueAmount of valueAsset
+// }
+// }
+//
+// contract stack flow:
+// 7 [... <position> <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset 7]
+// ROLL [... <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset <position>]
+// TOALTSTACK [... <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset]
+// 6 [... <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset 6]
+// ROLL [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset <clause selector>]
+// DUP [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset <clause selector> <clause selector>]
+// 2 [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset <clause selector> <clause selector> 2]
+// NUMEQUAL [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset <clause selector> (<clause selector> == 2)]
+// JUMPIF:$cancel [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset <clause selector>]
+// JUMPIF:$fullTrade [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset]
+// $partialTrade [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset]
+// 6 [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset 6]
+// PICK [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset exchangeAmount]
+// 3 [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset exchangeAmount 3]
+// ROLL [... exchangeAmount sellerKey standardProgram sellerProgram ratioMolecule requestedAsset exchangeAmount ratioDenominator]
+// MUL [... exchangeAmount sellerKey standardProgram sellerProgram ratioMolecule requestedAsset (exchangeAmount * ratioDenominator)]
+// 2 [... exchangeAmount sellerKey standardProgram sellerProgram ratioMolecule requestedAsset (exchangeAmount * ratioDenominator) 2]
+// ROLL [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset (exchangeAmount * ratioDenominator) ratioMolecule]
+// DIV [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount]
+// AMOUNT [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount]
+// OVER [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount actualAmount]
+// 0 [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount actualAmount 0]
+// GREATERTHAN [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0)]
+// 2 [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0) 2]
+// PICK [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0) actualAmount]
+// 2 [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0) actualAmount 2]
+// ROLL [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount (actualAmount > 0) actualAmount valueAmount]
+// LESSTHAN [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount (actualAmount > 0) (actualAmount < valueAmount)]
+// BOOLAND [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount ((actualAmount > 0) && (actualAmount < valueAmount))]
+// VERIFY [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount]
+// FROMALTSTACK [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position>]
+// DUP [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> <position>]
+// TOALTSTACK [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position>]
+// 6 [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> 6]
+// ROLL [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> exchangeAmount]
+// 3 [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> exchangeAmount 3]
+// ROLL [... sellerKey standardProgram sellerProgram actualAmount <position> exchangeAmount requestedAsset]
+// 1 [... sellerKey standardProgram sellerProgram actualAmount <position> exchangeAmount requestedAsset 1]
+// 5 [... sellerKey standardProgram sellerProgram actualAmount <position> exchangeAmount requestedAsset 1 5]
+// ROLL [... sellerKey standardProgram actualAmount <position> exchangeAmount requestedAsset 1 sellerProgram]
+// CHECKOUTPUT [... sellerKey standardProgram actualAmount checkOutput(exchangeAmount, requestedAsset, sellerProgram)]
+// VERIFY [... sellerKey standardProgram actualAmount]
+// FROMALTSTACK [... sellerKey standardProgram actualAmount <position>]
+// 1 [... sellerKey standardProgram actualAmount <position> 1]
+// ADD [... sellerKey standardProgram actualAmount (<position> + 1)]
+// AMOUNT [... sellerKey standardProgram actualAmount (<position> + 1) valueAmount]
+// 2 [... sellerKey standardProgram actualAmount (<position> + 1) valueAmount 2]
+// ROLL [... sellerKey standardProgram (<position> + 1) valueAmount actualAmount]
+// SUB [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount)]
+// ASSET [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount) valueAsset]
+// 1 [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount) valueAsset 1]
+// 4 [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount) valueAsset 1 4]
+// ROLL [... sellerKey (<position> + 1) (valueAmount - actualAmount) valueAsset 1 standardProgram]
+// CHECKOUTPUT [... sellerKey checkOutput((valueAmount - actualAmount), valueAsset, standardProgram)]
+// JUMP:$_end [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset]
+// $fullTrade [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset]
+// AMOUNT [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset valueAmount]
+// 2 [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset valueAmount 2]
+// ROLL [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset valueAmount ratioMolecule]
+// MUL [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset (valueAmount * ratioMolecule)]
+// 2 [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset (valueAmount * ratioMolecule) 2]
+// ROLL [... sellerKey standardProgram sellerProgram requestedAsset (valueAmount * ratioMolecule) ratioDenominator]
+// DIV [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount]
+// DUP [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount requestedAmount]
+// 0 [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount requestedAmount 0]
+// GREATERTHAN [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount (requestedAmount > 0)]
+// VERIFY [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount]
+// FROMALTSTACK [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount <position>]
+// SWAP [... sellerKey standardProgram sellerProgram requestedAsset <position> requestedAmount]
+// 2 [... sellerKey standardProgram sellerProgram requestedAsset <position> requestedAmount 2]
+// ROLL [... sellerKey standardProgram sellerProgram <position> requestedAmount requestedAsset]
+// 1 [... sellerKey standardProgram sellerProgram <position> requestedAmount requestedAsset 1]
+// 4 [... sellerKey standardProgram sellerProgram <position> requestedAmount requestedAsset 1 4]
+// ROLL [... sellerKey standardProgram <position> requestedAmount requestedAsset 1 sellerProgram]
+// CHECKOUTPUT [... sellerKey standardProgram checkOutput(requestedAmount, requestedAsset, sellerProgram)]
+// JUMP:$_end [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset]
+// $cancel [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset <clause selector>]
+// DROP [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset]
+// 6 [... sellerSig sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset 6]
+// ROLL [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset sellerSig]
+// 6 [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset sellerSig 6]
+// ROLL [... standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset sellerSig sellerKey]
+// TXSIGHASH SWAP CHECKSIG [... standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset checkTxSig(sellerKey, sellerSig)]
+// $_end [... sellerKey standardProgram sellerProgram ratioDenominator ratioMolecule requestedAsset]
+func P2MCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) {
+ standardProgram, err := P2WMCProgram(magneticContractArgs)
+ if err != nil {
+ return nil, err
+ }
+
+ builder := NewBuilder()
+ // contract arguments
+ builder.AddData(magneticContractArgs.SellerKey)
+ builder.AddData(standardProgram)
+ builder.AddData(magneticContractArgs.SellerProgram)
+ builder.AddInt64(magneticContractArgs.RatioDenominator)
+ builder.AddInt64(magneticContractArgs.RatioMolecule)
+ builder.AddData(magneticContractArgs.RequestedAsset.Bytes())
+
+ // contract instructions
+ builder.AddOp(vm.OP_7)
+ builder.AddOp(vm.OP_ROLL)
+ builder.AddOp(vm.OP_TOALTSTACK)
+ builder.AddOp(vm.OP_6)
+ builder.AddOp(vm.OP_ROLL)
+ builder.AddOp(vm.OP_DUP)
+ builder.AddOp(vm.OP_2)
+ builder.AddOp(vm.OP_NUMEQUAL)
+ builder.AddJumpIf(0)
+ builder.AddJumpIf(1)
+ builder.AddOp(vm.OP_6)
+ builder.AddOp(vm.OP_PICK)
+ builder.AddOp(vm.OP_3)
+ builder.AddOp(vm.OP_ROLL)
+ builder.AddOp(vm.OP_MUL)
+ builder.AddOp(vm.OP_ROT)
+ builder.AddOp(vm.OP_DIV)
+ builder.AddOp(vm.OP_AMOUNT)
+ builder.AddOp(vm.OP_OVER)
+ builder.AddOp(vm.OP_0)
+ builder.AddOp(vm.OP_GREATERTHAN)
+ builder.AddOp(vm.OP_2)
+ builder.AddOp(vm.OP_PICK)
+ builder.AddOp(vm.OP_ROT)
+ builder.AddOp(vm.OP_LESSTHAN)
+ builder.AddOp(vm.OP_BOOLAND)
+ builder.AddOp(vm.OP_VERIFY)
+ builder.AddOp(vm.OP_FROMALTSTACK)
+ builder.AddOp(vm.OP_DUP)
+ builder.AddOp(vm.OP_TOALTSTACK)
+ builder.AddOp(vm.OP_6)
+ builder.AddOp(vm.OP_ROLL)
+ builder.AddOp(vm.OP_3)
+ builder.AddOp(vm.OP_ROLL)
+ builder.AddOp(vm.OP_1)
+ builder.AddOp(vm.OP_5)
+ builder.AddOp(vm.OP_ROLL)
+ builder.AddOp(vm.OP_CHECKOUTPUT)
+ builder.AddOp(vm.OP_VERIFY)
+ builder.AddOp(vm.OP_FROMALTSTACK)
+ builder.AddOp(vm.OP_1)
+ builder.AddOp(vm.OP_ADD)
+ builder.AddOp(vm.OP_AMOUNT)
+ builder.AddOp(vm.OP_ROT)
+ builder.AddOp(vm.OP_SUB)
+ builder.AddOp(vm.OP_ASSET)
+ builder.AddOp(vm.OP_1)
+ builder.AddOp(vm.OP_4)
+ builder.AddOp(vm.OP_ROLL)
+ builder.AddOp(vm.OP_CHECKOUTPUT)
+ builder.AddJump(2)
+ builder.SetJumpTarget(1)
+ builder.AddOp(vm.OP_AMOUNT)
+ builder.AddOp(vm.OP_ROT)
+ builder.AddOp(vm.OP_MUL)
+ builder.AddOp(vm.OP_ROT)
+ builder.AddOp(vm.OP_DIV)
+ builder.AddOp(vm.OP_DUP)
+ builder.AddOp(vm.OP_0)
+ builder.AddOp(vm.OP_GREATERTHAN)
+ builder.AddOp(vm.OP_VERIFY)
+ builder.AddOp(vm.OP_FROMALTSTACK)
+ builder.AddOp(vm.OP_SWAP)
+ builder.AddOp(vm.OP_ROT)
+ builder.AddOp(vm.OP_1)
+ builder.AddOp(vm.OP_4)
+ builder.AddOp(vm.OP_ROLL)
+ builder.AddOp(vm.OP_CHECKOUTPUT)
+ builder.AddJump(3)
+ builder.SetJumpTarget(0)
+ builder.AddOp(vm.OP_DROP)
+ builder.AddOp(vm.OP_6)
+ builder.AddOp(vm.OP_ROLL)
+ builder.AddOp(vm.OP_6)
+ builder.AddOp(vm.OP_ROLL)
+ builder.AddOp(vm.OP_TXSIGHASH)
+ builder.AddOp(vm.OP_SWAP)
+ builder.AddOp(vm.OP_CHECKSIG)
+ builder.SetJumpTarget(2)
+ builder.SetJumpTarget(3)
+ return builder.Build()
+}