OSDN Git Service

add op mul fraction to solve the mov exchange issue (#452)
authorPaladz <yzhu101@uottawa.ca>
Wed, 27 Nov 2019 07:14:41 +0000 (15:14 +0800)
committerGitHub <noreply@github.com>
Wed, 27 Nov 2019 07:14:41 +0000 (15:14 +0800)
* add op mul fraction to solve the mov exchange issue

* minor

* edit for code review

* no water

application/mov/match/match.go
application/mov/mov_core.go
application/mov/mov_core_test.go
protocol/vm/numeric.go
protocol/vm/numeric_test.go
protocol/vm/ops.go
protocol/vm/vmutil/script.go

index f3103f4..ef8b241 100644 (file)
@@ -3,6 +3,7 @@ package match
 import (
        "encoding/hex"
        "math"
+       "math/big"
 
        "github.com/vapor/application/mov/common"
        "github.com/vapor/application/mov/contract"
@@ -221,7 +222,12 @@ func addMatchTxOutput(txData *types.TxData, txInput *types.TxInput, order *commo
 }
 
 func CalcRequestAmount(fromAmount uint64, contractArg *vmutil.MagneticContractArgs) uint64 {
-       return uint64(int64(fromAmount) * contractArg.RatioNumerator / contractArg.RatioDenominator)
+       res := big.NewInt(0).SetUint64(fromAmount)
+       res.Mul(res, big.NewInt(contractArg.RatioNumerator)).Div(res, big.NewInt(contractArg.RatioDenominator))
+       if !res.IsUint64() {
+               return 0
+       }
+       return res.Uint64()
 }
 
 func calcShouldPayAmount(receiveAmount uint64, contractArg *vmutil.MagneticContractArgs) uint64 {
index 1bc9c2f..9e9923d 100644 (file)
@@ -8,7 +8,6 @@ import (
        "github.com/vapor/consensus/segwit"
        dbm "github.com/vapor/database/leveldb"
        "github.com/vapor/errors"
-       "github.com/vapor/math/checked"
        "github.com/vapor/protocol/bc"
        "github.com/vapor/protocol/bc/types"
 )
@@ -27,7 +26,7 @@ var (
        errNumeratorOfRatioIsOverflow    = errors.New("ratio numerator of contract args product input amount is overflow")
        errLengthOfInputIsIncorrect      = errors.New("length of matched tx input is not equals to actual matched tx input")
        errSpendOutputIDIsIncorrect      = errors.New("spend output id of matched tx is not equals to actual matched tx")
-       errRequestAmountLessThenOne      = errors.New("request amount of order less than one")
+       errRequestAmountMath             = errors.New("request amount of order less than one or big than max of int64")
 )
 
 // MovCore represent the core logic of the match module, which include generate match transactions before packing the block,
@@ -233,12 +232,8 @@ func validateMagneticContractArgs(fromAmount uint64, program []byte) error {
                return errRatioOfTradeLessThanZero
        }
 
-       if _, ok := checked.MulInt64(int64(fromAmount), contractArgs.RatioNumerator); !ok {
-               return errNumeratorOfRatioIsOverflow
-       }
-
        if match.CalcRequestAmount(fromAmount, contractArgs) < 1 {
-               return errRequestAmountLessThenOne
+               return errRequestAmountMath
        }
        return nil
 }
index 8fb0be3..4f23298 100644 (file)
@@ -389,17 +389,17 @@ func TestValidateBlock(t *testing.T) {
                        wantError:     errRatioOfTradeLessThanZero,
                },
                {
-                       desc: "ratio numerator product input amount is overflow",
+                       desc: "want amount is overflow",
                        block: &types.Block{
                                Transactions: []*types.Tx{
                                        types.NewTx(types.TxData{
                                                Inputs:  []*types.TxInput{types.NewSpendInput(nil, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, []byte{0x51})},
-                                               Outputs: []*types.TxOutput{types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.MustCreateP2WMCProgram(mock.ETH, testutil.MustDecodeHexString("51"), math.MaxInt64, 10))},
+                                               Outputs: []*types.TxOutput{types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.MustCreateP2WMCProgram(mock.ETH, testutil.MustDecodeHexString("51"), math.MaxInt64, 1))},
                                        }),
                                },
                        },
                        verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
-                       wantError:     errNumeratorOfRatioIsOverflow,
+                       wantError:     errRequestAmountMath,
                },
        }
 
index 9a65af6..f74290f 100644 (file)
@@ -2,6 +2,7 @@ package vm
 
 import (
        "math"
+       "math/big"
 
        "github.com/vapor/math/checked"
 )
@@ -457,3 +458,36 @@ func opWithin(vm *virtualMachine) error {
        }
        return vm.pushBool(x >= min && x < max, true)
 }
+
+func opMulFraction(vm *virtualMachine) error {
+       if err := vm.applyCost(8); err != nil {
+               return err
+       }
+
+       z, err := vm.popInt64(true)
+       if err != nil {
+               return err
+       }
+
+       if z == 0 {
+               return ErrDivZero
+       }
+
+       y, err := vm.popInt64(true)
+       if err != nil {
+               return err
+       }
+
+       x, err := vm.popInt64(true)
+       if err != nil {
+               return err
+       }
+
+       res := big.NewInt(x)
+       res.Mul(res, big.NewInt(y)).Div(res, big.NewInt(z))
+       if !res.IsInt64() {
+               return ErrRange
+       }
+
+       return vm.pushInt64(res.Int64(), true)
+}
index ed5bf03..a7670fd 100644 (file)
@@ -446,6 +446,40 @@ func TestNumericOps(t *testing.T) {
                        deferredCost: -18,
                        dataStack:    [][]byte{{1}},
                },
+       }, {
+               op: OP_MULFRACTION,
+               startVM: &virtualMachine{
+                       runLimit:  50000,
+                       dataStack: [][]byte{{15}, {2}, {3}},
+               },
+               wantVM: &virtualMachine{
+                       runLimit:     49992,
+                       deferredCost: -18,
+                       dataStack:    [][]byte{{10}},
+               },
+       }, {
+               op: OP_MULFRACTION,
+               startVM: &virtualMachine{
+                       runLimit:  50000,
+                       dataStack: [][]byte{Int64Bytes(-654), {3}, {2}},
+               },
+               wantVM: &virtualMachine{
+                       runLimit:     49992,
+                       deferredCost: -18,
+                       dataStack:    [][]byte{Int64Bytes(-981)},
+               },
+       }, {
+               op: OP_MULFRACTION,
+               startVM: &virtualMachine{
+                       runLimit:  50000,
+                       dataStack: [][]byte{Int64Bytes(-654), {3}, {}},
+               },
+               wantVM: &virtualMachine{
+                       runLimit:     49992,
+                       deferredCost: -18,
+                       dataStack:    [][]byte{},
+               },
+               wantErr: ErrDivZero,
        }}
 
        numops := []Op{
@@ -453,6 +487,7 @@ func TestNumericOps(t *testing.T) {
                OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_LSHIFT, OP_RSHIFT, OP_BOOLAND,
                OP_BOOLOR, OP_NUMEQUAL, OP_NUMEQUALVERIFY, OP_NUMNOTEQUAL, OP_LESSTHAN,
                OP_LESSTHANOREQUAL, OP_GREATERTHAN, OP_GREATERTHANOREQUAL, OP_MIN, OP_MAX, OP_WITHIN,
+               OP_MULFRACTION,
        }
 
        for _, op := range numops {
@@ -536,6 +571,14 @@ func TestRangeErrs(t *testing.T) {
                {fmt.Sprintf("%d 1 LSHIFT", int64(math.MinInt64)), true},
                {fmt.Sprintf("%d 1 LSHIFT", int64(math.MinInt64)/2), false},
                {fmt.Sprintf("%d 2 LSHIFT", int64(math.MinInt64)/2), true},
+               {fmt.Sprintf("%d %d %d MULFRACTION", int64(math.MaxInt64)/2-1, 2, 1), false},
+               {fmt.Sprintf("%d %d %d MULFRACTION", int64(math.MaxInt64)/2+1, 2, 1), true},
+               {fmt.Sprintf("%d %d %d MULFRACTION", int64(math.MinInt64)/2+1, 2, 1), false},
+               {fmt.Sprintf("%d %d %d MULFRACTION", int64(math.MinInt64)/2-1, 2, 1), true},
+               {fmt.Sprintf("%d %d %d MULFRACTION", int64(math.MaxInt64), 3, 2), true},
+               {fmt.Sprintf("%d %d %d MULFRACTION", int64(math.MaxInt64), 2, 3), false},
+               {fmt.Sprintf("%d %d %d MULFRACTION", int64(math.MinInt64), 3, 2), true},
+               {fmt.Sprintf("%d %d %d MULFRACTION", int64(math.MinInt64), 2, 3), false},
        }
 
        for i, c := range cases {
index 5052c62..f5b6e18 100644 (file)
@@ -194,6 +194,7 @@ const (
        OP_MIN                Op = 0xa3
        OP_MAX                Op = 0xa4
        OP_WITHIN             Op = 0xa5
+       OP_MULFRACTION        Op = 0xa6
 
        OP_SHA256        Op = 0xa8
        OP_SHA3          Op = 0xaa
@@ -300,6 +301,7 @@ var (
                OP_MIN:                {OP_MIN, "MIN", opMin},
                OP_MAX:                {OP_MAX, "MAX", opMax},
                OP_WITHIN:             {OP_WITHIN, "WITHIN", opWithin},
+               OP_MULFRACTION:        {OP_MULFRACTION, "MULFRACTION", opMulFraction},
 
                OP_SHA256:        {OP_SHA256, "SHA256", opSha256},
                OP_SHA3:          {OP_SHA3, "SHA3", opSha3},
index 5c6d405..f9efb26 100644 (file)
@@ -200,10 +200,9 @@ func P2WMCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) {
 // PICK                     [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset exchangeAmount]
 // 3                        [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset exchangeAmount 3]
 // ROLL                     [... exchangeAmount sellerKey standardProgram sellerProgram ratioNumerator requestedAsset exchangeAmount ratioDenominator]
-// MUL                      [... exchangeAmount sellerKey standardProgram sellerProgram ratioNumerator requestedAsset (exchangeAmount * ratioDenominator)]
-// 2                        [... exchangeAmount sellerKey standardProgram sellerProgram ratioNumerator requestedAsset (exchangeAmount * ratioDenominator) 2]
-// ROLL                     [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset (exchangeAmount * ratioDenominator) ratioNumerator]
-// DIV                      [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount]
+// 3                        [... exchangeAmount sellerKey standardProgram sellerProgram ratioNumerator requestedAsset exchangeAmount ratioDenominator 3]
+// ROLL                     [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset exchangeAmount ratioDenominator ratioNumerator]
+// MULFRACTION              [... 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]
@@ -244,10 +243,9 @@ func P2WMCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) {
 // AMOUNT                   [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset valueAmount]
 // 2                        [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset valueAmount 2]
 // ROLL                     [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset valueAmount ratioNumerator]
-// MUL                      [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset (valueAmount * ratioNumerator)]
-// 2                        [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset (valueAmount * ratioNumerator) 2]
-// ROLL                     [... sellerKey standardProgram sellerProgram requestedAsset (valueAmount * ratioNumerator) ratioDenominator]
-// DIV                      [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount]
+// 3                        [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset valueAmount ratioNumerator 3]
+// ROLL                     [... sellerKey standardProgram sellerProgram requestedAsset valueAmount ratioNumerator ratioDenominator]
+// MULFRACTION              [... 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)]
@@ -299,9 +297,9 @@ func P2MCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) {
        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_3)
+       builder.AddOp(vm.OP_ROLL)
+       builder.AddOp(vm.OP_MULFRACTION)
        builder.AddOp(vm.OP_AMOUNT)
        builder.AddOp(vm.OP_OVER)
        builder.AddOp(vm.OP_0)
@@ -339,9 +337,9 @@ func P2MCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) {
        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_3)
+       builder.AddOp(vm.OP_ROLL)
+       builder.AddOp(vm.OP_MULFRACTION)
        builder.AddOp(vm.OP_DUP)
        builder.AddOp(vm.OP_0)
        builder.AddOp(vm.OP_GREATERTHAN)