OSDN Git Service

maker taker v2
authorshenao78 <shenao.78@163.com>
Thu, 30 Jul 2020 08:26:07 +0000 (16:26 +0800)
committershenao78 <shenao.78@163.com>
Thu, 30 Jul 2020 08:26:07 +0000 (16:26 +0800)
application/mov/contract/contract.go
application/mov/match/engine.go
application/mov/match/fee_strategy.go
application/mov/mov_core.go
consensus/segwit/magnetic.go
protocol/vm/vmutil/script.go

index 75cd00d..7d1860e 100644 (file)
@@ -3,17 +3,20 @@ package contract
 import (
        "encoding/hex"
 
+       "github.com/bytom/vapor/errors"
        "github.com/bytom/vapor/protocol/bc/types"
        "github.com/bytom/vapor/protocol/vm"
 )
 
 const (
-       sizeOfCancelClauseArgs       = 3
-       sizeOfPartialTradeClauseArgs = 3
-       sizeOfFullTradeClauseArgs    = 2
+       sizeOfCancelClauseArgs         = 3
+       sizeOfPartialTradeClauseArgsV1 = 3
+       sizeOfFullTradeClauseArgsV1    = 2
+       sizeOfPartialTradeClauseArgsV2 = 4
+       sizeOfFullTradeClauseArgsV2    = 3
 )
 
-// smart contract clause select for differnet unlock method
+// smart contract clause select for different unlock method
 const (
        PartialTradeClauseSelector int64 = iota
        FullTradeClauseSelector
@@ -32,10 +35,33 @@ func IsTradeClauseSelector(input *types.TxInput) bool {
 
 // IsPartialTradeClauseSelector check if input select partial trade clause
 func IsPartialTradeClauseSelector(input *types.TxInput) bool {
-       return len(input.Arguments()) == sizeOfPartialTradeClauseArgs && hex.EncodeToString(input.Arguments()[len(input.Arguments())-1]) == hex.EncodeToString(vm.Int64Bytes(PartialTradeClauseSelector))
+       if len(input.Arguments()) == sizeOfPartialTradeClauseArgsV1 || len(input.Arguments()) == sizeOfPartialTradeClauseArgsV2 {
+               return hex.EncodeToString(input.Arguments()[len(input.Arguments())-1]) == hex.EncodeToString(vm.Int64Bytes(PartialTradeClauseSelector))
+       }
+       return false
 }
 
 // IsFullTradeClauseSelector check if input select full trade clause
 func IsFullTradeClauseSelector(input *types.TxInput) bool {
-       return len(input.Arguments()) == sizeOfFullTradeClauseArgs && hex.EncodeToString(input.Arguments()[len(input.Arguments())-1]) == hex.EncodeToString(vm.Int64Bytes(FullTradeClauseSelector))
+       if len(input.Arguments()) == sizeOfFullTradeClauseArgsV1 || len(input.Arguments()) == sizeOfFullTradeClauseArgsV2 {
+               return hex.EncodeToString(input.Arguments()[len(input.Arguments())-1]) == hex.EncodeToString(vm.Int64Bytes(FullTradeClauseSelector))
+       }
+       return false
+}
+
+// FeeRate return the rate of fee from input witness
+func FeeRate(input *types.TxInput) (int64, error) {
+       if IsFullTradeClauseSelector(input) {
+               if len(input.Arguments()) == sizeOfFullTradeClauseArgsV1 {
+                       return 10, nil
+               }
+               return vm.AsInt64(input.Arguments()[0])
+       }
+       if IsPartialTradeClauseSelector(input) {
+               if len(input.Arguments()) == sizeOfPartialTradeClauseArgsV1 {
+                       return 10, nil
+               }
+               return vm.AsInt64(input.Arguments()[1])
+       }
+       return 0, errors.New("invalid trade input")
 }
index 4fea5c2..f7d3477 100644 (file)
@@ -122,9 +122,9 @@ func addRefundOutput(txData *types.TxData, orders []*common.Order) {
        }
 }
 
-func addTakerOutput(txData *types.TxData, orders []*common.Order, priceDiffs []*bc.AssetAmount, isMakers []bool) {
+func addTakerOutput(txData *types.TxData, orders []*common.Order, priceDiffs []*bc.AssetAmount, makerFlags []*MakerFlag) {
        for i := range orders {
-               if isMakers[i] {
+               if makerFlags[i].IsMaker {
                        continue
                }
                for _, priceDiff := range priceDiffs {
@@ -167,7 +167,7 @@ func (e *Engine) buildMatchTx(orders []*common.Order) (*types.Tx, []*common.Orde
        return types.NewTx(*txData), partialOrders, nil
 }
 
-func addMatchTxOutput(txData *types.TxData, orders []*common.Order, receivedAmounts []*bc.AssetAmount, allocatedAssets *AllocatedAssets, isMakers []bool) ([]*common.Order, error) {
+func addMatchTxOutput(txData *types.TxData, orders []*common.Order, receivedAmounts []*bc.AssetAmount, allocatedAssets *AllocatedAssets, makerFlags []*MakerFlag) ([]*common.Order, error) {
        var partialOrders []*common.Order
        for i, order := range orders {
                contractArgs := order.ContractArgs
@@ -178,7 +178,7 @@ func addMatchTxOutput(txData *types.TxData, orders []*common.Order, receivedAmou
                exchangeAmount := order.Utxo.Amount - shouldPayAmount
                isPartialTrade := requestAmount > receivedAmount && CalcRequestAmount(exchangeAmount, contractArgs.RatioNumerator, contractArgs.RatioDenominator) >= 1
 
-               setMatchTxArguments(txData.Inputs[i], isPartialTrade, len(txData.Outputs), receivedAmount, isMakers[i])
+               setMatchTxArguments(txData.Inputs[i], isPartialTrade, len(txData.Outputs), receivedAmount, makerFlags[i].IsMaker, contractArgs.Version)
 
                txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.ToAssetID, allocatedAssets.Receives[i].Amount, contractArgs.SellerProgram))
                if isPartialTrade {
@@ -249,13 +249,20 @@ func IsMatched(orders []*common.Order) bool {
        return product.Cmp(one) <= 0
 }
 
+// MakerFlag represent whether the order is isMaker and include the contract version of order
+type MakerFlag struct {
+       IsMaker         bool
+       ContractVersion int
+}
+
 // MakerFlags return a slice of array indicate whether orders[i] is maker
-func MakerFlags(orders []*common.Order) []bool {
-       isMakers := make([]bool, len(orders))
+func MakerFlags(orders []*common.Order) []*MakerFlag {
+       makerFlags := make([]*MakerFlag, len(orders))
        for i, order := range orders {
-               isMakers[i] = isMaker(order, orders[calcOppositeIndex(len(orders), i)])
+               makerFlags[i].IsMaker = isMaker(order, orders[calcOppositeIndex(len(orders), i)])
+               makerFlags[i].ContractVersion = order.ContractArgs.Version
        }
-       return isMakers
+       return makerFlags
 }
 
 func isMaker(order, oppositeOrder *common.Order) bool {
@@ -275,17 +282,24 @@ func isMaker(order, oppositeOrder *common.Order) bool {
        return order.BlockHeight < oppositeOrder.BlockHeight
 }
 
-func setMatchTxArguments(txInput *types.TxInput, isPartialTrade bool, position int, receiveAmounts uint64, isMaker bool) {
-       feeRate := takerFeeRate
+func setMatchTxArguments(txInput *types.TxInput, isPartialTrade bool, position int, receiveAmounts uint64, isMaker bool, contractVersion int) {
+       feeRate := TakerFeeRate
        if isMaker {
-               feeRate = makerFeeRate
+               feeRate = MakerFeeRate
        }
 
        var arguments [][]byte
        if isPartialTrade {
-               arguments = [][]byte{vm.Int64Bytes(int64(receiveAmounts)), vm.Int64Bytes(int64(position)), vm.Int64Bytes(contract.PartialTradeClauseSelector), vm.Int64Bytes(feeRate)}
+               arguments = [][]byte{vm.Int64Bytes(int64(receiveAmounts))}
+               if contractVersion == segwit.MagneticV2 {
+                       arguments = append(arguments, vm.Int64Bytes(feeRate))
+               }
+               arguments = append(arguments, vm.Int64Bytes(int64(position)), vm.Int64Bytes(contract.PartialTradeClauseSelector))
        } else {
-               arguments = [][]byte{vm.Int64Bytes(int64(position)), vm.Int64Bytes(contract.FullTradeClauseSelector), vm.Int64Bytes(feeRate)}
+               if contractVersion == segwit.MagneticV2 {
+                       arguments = append(arguments, vm.Int64Bytes(feeRate))
+               }
+               arguments = [][]byte{vm.Int64Bytes(int64(position)), vm.Int64Bytes(contract.FullTradeClauseSelector)}
        }
        txInput.SetArguments(arguments)
 }
index 87e0d90..9a7049d 100644 (file)
@@ -3,6 +3,7 @@ package match
 import (
        "math"
 
+       "github.com/bytom/vapor/consensus/segwit"
        "github.com/bytom/vapor/errors"
        "github.com/bytom/vapor/protocol/bc"
 )
@@ -13,9 +14,10 @@ var (
 )
 
 const (
-       // rate of fee in units of 10000
-       makerFeeRate int64 = 0
-       takerFeeRate int64 = 3
+       // MakerFeeRate represent the fee rate of maker, which in units of 10000
+       MakerFeeRate int64 = 0
+       // TakerFeeRate represent the fee rate of taker
+       TakerFeeRate int64 = 5
 )
 
 // AllocatedAssets represent reallocated assets after calculating fees
@@ -30,10 +32,10 @@ type FeeStrategy interface {
        // @param receiveAmounts the amount of assets that the participants in the matching transaction can received when no fee is considered
        // @param priceDiffs price differential of matching transaction, it will be refunded to the taker
        // @return reallocated assets after calculating fees
-       Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount, isMakers []bool) *AllocatedAssets
+       Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount, makerFlags []*MakerFlag) *AllocatedAssets
 
        // Validate verify that the fee charged for a matching transaction is correct
-       Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error
+       Validate(receiveAmounts, priceDiffs []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64, makerFlags []*MakerFlag) error
 }
 
 // DefaultFeeStrategy represent the default fee charge strategy
@@ -45,19 +47,24 @@ func NewDefaultFeeStrategy() *DefaultFeeStrategy {
 }
 
 // Allocate will allocate the price differential in matching transaction to the participants and the fee
-func (d *DefaultFeeStrategy) Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount, isMakers []bool) *AllocatedAssets {
+func (d *DefaultFeeStrategy) Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount, makerFlags []*MakerFlag) *AllocatedAssets {
        receives := make([]*bc.AssetAmount, len(receiveAmounts))
        fees := make([]*bc.AssetAmount, len(receiveAmounts))
 
        for i, receiveAmount := range receiveAmounts {
-               fee := d.calcFeeAmount(receiveAmount.Amount, isMakers[i])
+               makerFlag := makerFlags[i]
+               fee := calcFeeAmount(receiveAmount.Amount, makerFlag.IsMaker)
+               if makerFlag.ContractVersion == segwit.MagneticV1 {
+                       fee = legendCalcMinFeeAmount(receiveAmount.Amount)
+               }
+
                receives[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: receiveAmount.Amount - fee}
                fees[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: fee}
 
-               if !isMakers[i] {
+               if makerFlag.ContractVersion == segwit.MagneticV2 && !makerFlag.IsMaker {
                        for _, priceDiff := range priceDiffs {
                                if *priceDiff.AssetId == *receiveAmount.AssetId {
-                                       fee = d.calcFeeAmount(priceDiff.Amount, false)
+                                       fee = calcFeeAmount(priceDiff.Amount, makerFlag.IsMaker)
                                        priceDiff.Amount -= fee
                                        fees[i].Amount += fee
                                }
@@ -68,21 +75,52 @@ func (d *DefaultFeeStrategy) Allocate(receiveAmounts, priceDiffs []*bc.AssetAmou
 }
 
 // Validate verify that the fee charged for a matching transaction is correct
-func (d *DefaultFeeStrategy) Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error {
-       for _, receiveAmount := range receiveAmounts {
-               realFeeAmount := feeAmounts[*receiveAmount.AssetId]
-               feeAmount := d.calcFeeAmount(receiveAmount.Amount, false)
-               if realFeeAmount < feeAmount {
+func (d *DefaultFeeStrategy) Validate(receiveAmounts, priceDiffs []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64, makerFlags []*MakerFlag) error {
+       for i, receiveAmount := range receiveAmounts {
+               receiveAssetID := receiveAmount.AssetId
+               feeAmount := feeAmounts[*receiveAssetID]
+
+               if makerFlags[i].ContractVersion == segwit.MagneticV1 {
+                       return legendValidate(receiveAmount, feeAmount)
+               }
+
+               expectFee := calcFeeAmount(receiveAmount.Amount, makerFlags[i].IsMaker)
+               if !makerFlags[i].IsMaker {
+                       for _, priceDiff := range priceDiffs {
+                               if *priceDiff.AssetId == *receiveAssetID {
+                                       expectFee += calcFeeAmount(priceDiff.Amount, false)
+                               }
+                       }
+               }
+
+               if feeAmount != expectFee {
                        return ErrInvalidAmountOfFee
                }
        }
        return nil
 }
 
-func (d *DefaultFeeStrategy) calcFeeAmount(amount uint64, isMaker bool) uint64 {
-       feeRate := takerFeeRate
+func calcFeeAmount(amount uint64, isMaker bool) uint64 {
+       feeRate := TakerFeeRate
        if isMaker {
-               feeRate = makerFeeRate
+               feeRate = MakerFeeRate
        }
        return uint64(math.Ceil(float64(amount) * float64(feeRate) / 1E4))
 }
+
+func legendValidate(receiveAmount *bc.AssetAmount, feeAmount uint64) error {
+       maxFeeAmount := legendCalcMaxFeeAmount(receiveAmount.Amount)
+       minFeeAmount := legendCalcMinFeeAmount(receiveAmount.Amount)
+       if feeAmount < minFeeAmount || feeAmount > maxFeeAmount {
+               return ErrInvalidAmountOfFee
+       }
+       return nil
+}
+
+func legendCalcMinFeeAmount(amount uint64) uint64 {
+       return uint64(math.Ceil(float64(amount) / 1000))
+}
+
+func legendCalcMaxFeeAmount(amount uint64) uint64 {
+       return uint64(math.Ceil(float64(amount) * 0.05))
+}
index 3ada693..235bb44 100644 (file)
@@ -30,6 +30,7 @@ var (
        errNotMatchedOrder              = errors.New("order in matched tx is not matched")
        errNotConfiguredRewardProgram   = errors.New("reward program is not configured properly")
        errRewardProgramIsWrong         = errors.New("the reward program is not correct")
+       errInvalidFeeRate               = errors.New("fee rate from input witness is invalid")
 )
 
 // Core represent the core logic of the match module, which include generate match transactions before packing the block,
@@ -331,14 +332,42 @@ func validateMatchedTxFee(tx *types.Tx, blockHeight uint64) error {
                return err
        }
 
-       receivedAmount, _ := match.CalcReceivedAmount(orders)
+       receivedAmount, priceDiffs := match.CalcReceivedAmount(orders)
        feeAmounts := make(map[bc.AssetID]uint64)
        for assetID, fee := range matchedTxFees {
                feeAmounts[assetID] = fee.amount
        }
 
+       makerFlags := make([]*match.MakerFlag, len(orders))
+       for i, order := range orders {
+               isMaker, err := isMakerByWitness(tx.Inputs[i], order.ContractArgs.Version)
+               if err != nil {
+                       return err
+               }
+
+               makerFlags[i].IsMaker = isMaker
+               makerFlags[i].ContractVersion = order.ContractArgs.Version
+       }
+
        feeStrategy := match.NewDefaultFeeStrategy()
-       return feeStrategy.Validate(receivedAmount, feeAmounts)
+       return feeStrategy.Validate(receivedAmount, priceDiffs, feeAmounts, makerFlags)
+}
+
+func isMakerByWitness(input *types.TxInput, contractVersion int) (bool, error) {
+       if contractVersion == segwit.MagneticV1 {
+               return true, nil
+       }
+
+       feeRate, err := contract.FeeRate(input)
+       if err != nil {
+               return false, err
+       }
+
+       if feeRate != match.MakerFeeRate && feeRate != match.TakerFeeRate {
+               return false, errInvalidFeeRate
+       }
+
+       return feeRate == match.MakerFeeRate, nil
 }
 
 func (m *Core) validateMatchedTxSequence(txs []*Tx) error {
index e10c696..af38bf6 100644 (file)
@@ -9,8 +9,10 @@ import (
 )
 
 const (
-       magneticV1 = iota + 1
-       magneticV2
+       // MagneticV1 represent the V1 version of magnetic
+       MagneticV1 = iota + 1
+       // MagneticV2 represent the v2 version of magnetic
+       MagneticV2
 )
 
 // isMagneticScript is used to determine whether it is a Magnetic script with specific version or not
@@ -25,11 +27,11 @@ func isMagneticScript(prog []byte, version int) bool {
        }
 
        switch version {
-       case magneticV1:
+       case MagneticV1:
                if insts[0].Op != vm.OP_0 {
                        return false
                }
-       case magneticV2:
+       case MagneticV2:
                if insts[0].Op != vm.OP_1 {
                        return false
                }
@@ -62,12 +64,12 @@ func isMagneticScript(prog []byte, version int) bool {
 
 // IsP2WMCScript is used to determine whether it is the v1 P2WMC script or not
 func IsP2WMCScript(prog []byte) bool {
-       return isMagneticScript(prog, magneticV1)
+       return isMagneticScript(prog, MagneticV1)
 }
 
 // IsP2WMCScriptV2 is used to determine whether it is the v2 P2WMC script or not
 func IsP2WMCScriptV2(prog []byte) bool {
-       return isMagneticScript(prog, magneticV2)
+       return isMagneticScript(prog, MagneticV2)
 }
 
 // DecodeP2WMCProgram parse standard P2WMC arguments to magneticContractArgs
@@ -81,6 +83,12 @@ func DecodeP2WMCProgram(prog []byte) (*vmutil.MagneticContractArgs, error) {
                SellerProgram: insts[4].Data,
                SellerKey:     insts[5].Data,
        }
+
+       magneticContractArgs.Version = MagneticV2
+       if insts[0].Op == vm.OP_0 {
+               magneticContractArgs.Version = MagneticV1
+       }
+
        requestedAsset := [32]byte{}
        copy(requestedAsset[:], insts[1].Data)
        magneticContractArgs.RequestedAsset = bc.NewAssetID(requestedAsset)
index 8446142..48c20fa 100644 (file)
@@ -20,6 +20,7 @@ type MagneticContractArgs struct {
        RatioDenominator int64
        SellerProgram    []byte
        SellerKey        []byte
+       Version          int
 }
 
 // IsUnspendable checks if a contorl program is absolute failed