From 4635e5a0ad60e7bd8d87243ea5702b4272212980 Mon Sep 17 00:00:00 2001 From: oysheng <33340252+oysheng@users.noreply.github.com> Date: Wed, 17 Jul 2019 11:15:46 +0800 Subject: [PATCH] add free gas (#295) * add free gas * unit test * delete checkout nobtm input * optimise --- api/errors.go | 5 ++--- blockchain/txbuilder/finalize.go | 20 ------------------- protocol/txpool.go | 16 +-------------- protocol/txpool_test.go | 30 +++++++--------------------- protocol/validation/block_test.go | 12 ++++++----- protocol/validation/test/tx_ugly_test.go | 34 ++++++++------------------------ protocol/validation/tx.go | 14 +++++++++---- protocol/validation/tx_test.go | 6 +++--- 8 files changed, 38 insertions(+), 99 deletions(-) diff --git a/api/errors.go b/api/errors.go index 5f8fe2fb..0a6c38f7 100644 --- a/api/errors.go +++ b/api/errors.go @@ -73,9 +73,8 @@ var respErrFormatter = map[error]httperror.Info{ txbuilder.ErrBadContractArgType: {400, "BTM715", "Invalid contract argument type"}, txbuilder.ErrOrphanTx: {400, "BTM716", "Transaction input UTXO not found"}, txbuilder.ErrExtTxFee: {400, "BTM717", "Transaction fee exceeded max limit"}, - txbuilder.ErrNoGasInput: {400, "BTM718", "Transaction has no gas input"}, - txbuilder.ErrRejected: {400, "BTM719", "Transaction rejected"}, - protocol.ErrDustTx: {400, "BTM720", "Dust Transaction"}, + txbuilder.ErrRejected: {400, "BTM718", "Transaction rejected"}, + protocol.ErrDustTx: {400, "BTM719", "Dust Transaction"}, // Submit transaction error namespace (73x ~ 79x) // Validation error (73x ~ 75x) diff --git a/blockchain/txbuilder/finalize.go b/blockchain/txbuilder/finalize.go index 270bb3b8..09558762 100644 --- a/blockchain/txbuilder/finalize.go +++ b/blockchain/txbuilder/finalize.go @@ -24,8 +24,6 @@ var ( ErrOrphanTx = errors.New("finalize can't find transaction input utxo") // ErrExtTxFee means transaction fee exceed max limit ErrExtTxFee = errors.New("transaction fee exceed max limit") - // ErrNoGasInput means transaction has no gas input - ErrNoGasInput = errors.New("transaction has no gas input") ) // FinalizeTx validates a transaction signature template, @@ -42,10 +40,6 @@ func FinalizeTx(ctx context.Context, c *protocol.Chain, tx *types.Tx) error { return err } - if err := checkGasInputIDs(tx); err != nil { - return err - } - // This part is use for prevent tx size is 0 data, err := tx.TxData.MarshalText() if err != nil { @@ -131,17 +125,3 @@ func checkTxSighashCommitment(tx *types.Tx) error { return lastError } - -func checkGasInputIDs(tx *types.Tx) error { - for _, inp := range tx.Inputs { - switch inp.InputType() { - case types.CrossChainInputType: - return nil - } - } - - if len(tx.GasInputIDs) == 0 { - return ErrNoGasInput - } - return nil -} diff --git a/protocol/txpool.go b/protocol/txpool.go index 912091cd..5a06c670 100644 --- a/protocol/txpool.go +++ b/protocol/txpool.go @@ -192,20 +192,6 @@ func (tp *TxPool) HaveTransaction(txHash *bc.Hash) bool { return tp.IsTransactionInPool(txHash) || tp.IsTransactionInErrCache(txHash) } -func isTransactionNoBtmInput(tx *types.Tx) bool { - for _, input := range tx.TxData.Inputs { - switch input.InputType() { - case types.CrossChainInputType: - return false - } - if input.AssetID() == *consensus.BTMAssetID { - return false - } - } - - return true -} - func isTransactionZeroOutput(tx *types.Tx) bool { for _, output := range tx.TxData.Outputs { if value := output.AssetAmount(); value.Amount == uint64(0) { @@ -216,7 +202,7 @@ func isTransactionZeroOutput(tx *types.Tx) bool { } func (tp *TxPool) IsDust(tx *types.Tx) bool { - return isTransactionNoBtmInput(tx) || isTransactionZeroOutput(tx) + return isTransactionZeroOutput(tx) } func (tp *TxPool) processTransaction(tx *types.Tx, statusFail bool, height, fee uint64) (bool, error) { diff --git a/protocol/txpool_test.go b/protocol/txpool_test.go index 7b96cb0b..9af97ef6 100644 --- a/protocol/txpool_test.go +++ b/protocol/txpool_test.go @@ -118,7 +118,7 @@ func (s *mockStore) GetStoreStatus() *BlockStoreState func (s *mockStore) GetTransactionStatus(*bc.Hash) (*bc.TransactionStatus, error) { return nil, nil } func (s *mockStore) GetTransactionsUtxo(*state.UtxoViewpoint, []*bc.Tx) error { return nil } func (s *mockStore) GetUtxo(*bc.Hash) (*storage.UtxoEntry, error) { return nil, nil } -func (s *mockStore) GetConsensusResult(uint64) (*state.ConsensusResult, error) { return nil, nil } +func (s *mockStore) GetConsensusResult(uint64) (*state.ConsensusResult, error) { return nil, nil } func (s *mockStore) GetMainChainHash(uint64) (*bc.Hash, error) { return nil, nil } func (s *mockStore) GetBlockHashesByHeight(uint64) ([]*bc.Hash, error) { return nil, nil } func (s *mockStore) SaveBlock(*types.Block, *bc.TransactionStatus) error { return nil } @@ -668,12 +668,12 @@ func (s *mockStore1) GetTransactionsUtxo(utxoView *state.UtxoViewpoint, tx []*bc } return nil } -func (s *mockStore1) GetUtxo(*bc.Hash) (*storage.UtxoEntry, error) { return nil, nil } -func (s *mockStore1) GetConsensusResult(uint64) (*state.ConsensusResult, error) { return nil, nil } -func (s *mockStore1) GetMainChainHash(uint64) (*bc.Hash, error) { return nil, nil } -func (s *mockStore1) GetBlockHashesByHeight(uint64) ([]*bc.Hash, error) { return nil, nil } -func (s *mockStore1) SaveBlock(*types.Block, *bc.TransactionStatus) error { return nil } -func (s *mockStore1) SaveBlockHeader(*types.BlockHeader) error { return nil } +func (s *mockStore1) GetUtxo(*bc.Hash) (*storage.UtxoEntry, error) { return nil, nil } +func (s *mockStore1) GetConsensusResult(uint64) (*state.ConsensusResult, error) { return nil, nil } +func (s *mockStore1) GetMainChainHash(uint64) (*bc.Hash, error) { return nil, nil } +func (s *mockStore1) GetBlockHashesByHeight(uint64) ([]*bc.Hash, error) { return nil, nil } +func (s *mockStore1) SaveBlock(*types.Block, *bc.TransactionStatus) error { return nil } +func (s *mockStore1) SaveBlockHeader(*types.BlockHeader) error { return nil } func (s *mockStore1) SaveChainStatus(*types.BlockHeader, *types.BlockHeader, []*types.BlockHeader, *state.UtxoViewpoint, []*state.ConsensusResult) error { return nil } @@ -695,22 +695,6 @@ func TestProcessTransaction(t *testing.T) { { want: &TxPool{}, addTx: &TxDesc{ - Tx: testTxs[3], - StatusFail: false, - }, - }, - //Dust tx - { - want: &TxPool{}, - addTx: &TxDesc{ - Tx: testTxs[4], - StatusFail: false, - }, - }, - //Dust tx - { - want: &TxPool{}, - addTx: &TxDesc{ Tx: testTxs[5], StatusFail: false, }, diff --git a/protocol/validation/block_test.go b/protocol/validation/block_test.go index 6749963e..f7a0f7b0 100644 --- a/protocol/validation/block_test.go +++ b/protocol/validation/block_test.go @@ -338,10 +338,12 @@ func TestValidateBlock(t *testing.T) { block: &bc.Block{ ID: bc.Hash{V0: 1}, BlockHeader: &bc.BlockHeader{ - Version: 1, - Height: 1, - Timestamp: 1523352601000, - PreviousBlockId: &parentHash, + Version: 1, + Height: 1, + Timestamp: 1523352601000, + PreviousBlockId: &parentHash, + TransactionsRoot: &bc.Hash{V0: 16229071813194843118, V1: 7413717724217377663, V2: 10255217553502780716, V3: 17975900656333257644}, + TransactionStatusHash: &txStatusHash, }, Transactions: []*bc.Tx{ types.MapTx(&types.TxData{ @@ -362,7 +364,7 @@ func TestValidateBlock(t *testing.T) { }, }, parent: parent, - err: vm.ErrRunLimitExceeded, + err: nil, }, } diff --git a/protocol/validation/test/tx_ugly_test.go b/protocol/validation/test/tx_ugly_test.go index 5d9a210b..a1897976 100644 --- a/protocol/validation/test/tx_ugly_test.go +++ b/protocol/validation/test/tx_ugly_test.go @@ -66,7 +66,7 @@ func TestValidateUglyTx(t *testing.T) { gasValid: false, }, { - category: "fee insufficient", + category: "normal with no fee", desc: "sum of btm output equals to input btm", insts: []*signingInst{singleSignInst}, txData: types.TxData{ @@ -77,11 +77,11 @@ func TestValidateUglyTx(t *testing.T) { *consensus.BTMAssetID, 10000000000, 0, nil), }, Outputs: []*types.TxOutput{ - types.NewIntraChainOutput(*consensus.BTMAssetID, 10000000001, testutil.MustDecodeHexString("00145931e1b7b65897f47845ac08fc136e0c0a4ff166")), + types.NewIntraChainOutput(*consensus.BTMAssetID, 10000000000, testutil.MustDecodeHexString("00145931e1b7b65897f47845ac08fc136e0c0a4ff166")), }, }, - err: true, - gasValid: false, + err: false, + gasValid: true, }, { category: "fee insufficient", @@ -98,11 +98,11 @@ func TestValidateUglyTx(t *testing.T) { types.NewIntraChainOutput(*consensus.BTMAssetID, 10000000000, testutil.MustDecodeHexString("00145931e1b7b65897f47845ac08fc136e0c0a4ff166")), }, }, - err: true, - gasValid: false, + err: false, + gasValid: true, }, { - category: "fee insufficient", + category: "normal with no fee", desc: "no btm input", insts: []*signingInst{singleSignInst}, txData: types.TxData{ @@ -116,7 +116,7 @@ func TestValidateUglyTx(t *testing.T) { types.NewIntraChainOutput(testutil.MustDecodeAsset("97575084e5161406a0977da729fbf51ad230e0ff0aec607a97e4336611c8707f"), 10000000000, testutil.MustDecodeHexString("00145931e1b7b65897f47845ac08fc136e0c0a4ff166")), }, }, - err: true, + err: false, gasValid: true, }, { @@ -286,24 +286,6 @@ func TestValidateUglyTx(t *testing.T) { }, { category: "input output unbalance", - desc: "input utxo is zero", - insts: []*signingInst{singleSignInst}, - txData: types.TxData{ - Version: 1, - Inputs: []*types.TxInput{ - types.NewSpendInput(nil, - bc.Hash{V0: 14760873410800997144, V1: 1698395500822741684, V2: 5965908492734661392, V3: 9445539829830863994}, - *consensus.BTMAssetID, 0, 0, nil), - }, - Outputs: []*types.TxOutput{ - types.NewIntraChainOutput(*consensus.BTMAssetID, 0, testutil.MustDecodeHexString("00145931e1b7b65897f47845ac08fc136e0c0a4ff166")), - }, - }, - err: true, - gasValid: false, - }, - { - category: "input output unbalance", desc: "no btm input", txData: types.TxData{ Version: 1, diff --git a/protocol/validation/tx.go b/protocol/validation/tx.go index 93644552..d360854e 100644 --- a/protocol/validation/tx.go +++ b/protocol/validation/tx.go @@ -58,12 +58,15 @@ func (g *GasState) setGas(BTMValue int64, txSize int64) error { } g.BTMValue = uint64(BTMValue) - var ok bool if g.GasLeft, ok = checked.DivInt64(BTMValue, consensus.ActiveNetParams.VMGasRate); !ok { return errors.Wrap(ErrGasCalculate, "setGas calc gas amount") } + if g.GasLeft, ok = checked.AddInt64(g.GasLeft, consensus.ActiveNetParams.DefaultGasCredit); !ok { + return errors.Wrap(ErrGasCalculate, "setGas calc free gas") + } + if g.GasLeft > consensus.ActiveNetParams.MaxGasAmount { g.GasLeft = consensus.ActiveNetParams.MaxGasAmount } @@ -172,16 +175,19 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { parity[*dest.Value.AssetId] = diff } + btmAmount := int64(0) for assetID, amount := range parity { if assetID == *consensus.BTMAssetID { - if err = vs.gasStatus.setGas(amount, int64(vs.tx.SerializedSize)); err != nil { - return err - } + btmAmount = amount } else if amount != 0 { return errors.WithDetailf(ErrUnbalanced, "asset %x sources - destinations = %d (should be 0)", assetID.Bytes(), amount) } } + if err = vs.gasStatus.setGas(btmAmount, int64(vs.tx.SerializedSize)); err != nil { + return err + } + for _, BTMInputID := range vs.tx.GasInputIDs { e, ok := vs.tx.Entries[BTMInputID] if !ok { diff --git a/protocol/validation/tx_test.go b/protocol/validation/tx_test.go index 17664df3..60b34abd 100644 --- a/protocol/validation/tx_test.go +++ b/protocol/validation/tx_test.go @@ -34,7 +34,7 @@ func TestGasStatus(t *testing.T) { BTMValue: 0, }, output: &GasState{ - GasLeft: 10000 / consensus.ActiveNetParams.VMGasRate, + GasLeft: 10000/consensus.ActiveNetParams.VMGasRate + consensus.ActiveNetParams.DefaultGasCredit, GasUsed: 0, BTMValue: 10000, }, @@ -500,7 +500,7 @@ func TestTxValidation(t *testing.T) { err: bc.ErrMissingEntry, }, { - desc: "no gas spend input", + desc: "normal check with no gas spend input", f: func() { spendID := mux.Sources[len(mux.Sources)-1].Ref delete(tx.Entries, *spendID) @@ -508,7 +508,7 @@ func TestTxValidation(t *testing.T) { tx.GasInputIDs = nil vs.gasStatus.GasLeft = 0 }, - err: vm.ErrRunLimitExceeded, + err: nil, }, { desc: "no gas spend input, but set gas left, so it's ok", -- 2.11.0