From e6c5e52a91039dc9e4db8fd23c5ba340c4a59eab Mon Sep 17 00:00:00 2001 From: Paladz Date: Wed, 9 Jun 2021 13:56:16 +0800 Subject: [PATCH] open_gas_stand_tx_limit (#1953) * open_gas_stand_tx_limit * edit the error name Co-authored-by: paladz --- blockchain/txbuilder/finalize.go | 4 -- protocol/bc/tx.go | 1 - protocol/bc/types/map.go | 99 ++++++++++++++-------------------------- protocol/bc/types/map_test.go | 3 -- protocol/validation/tx.go | 65 +++++--------------------- protocol/validation/tx_test.go | 76 ++---------------------------- 6 files changed, 50 insertions(+), 198 deletions(-) diff --git a/blockchain/txbuilder/finalize.go b/blockchain/txbuilder/finalize.go index 3639b48f..1d96618d 100644 --- a/blockchain/txbuilder/finalize.go +++ b/blockchain/txbuilder/finalize.go @@ -39,10 +39,6 @@ func FinalizeTx(ctx context.Context, c *protocol.Chain, tx *types.Tx) error { return err } - if len(tx.GasInputIDs) == 0 { - return ErrNoGasInput - } - // This part is use for prevent tx size is 0 data, err := tx.TxData.MarshalText() if err != nil { diff --git a/protocol/bc/tx.go b/protocol/bc/tx.go index 20191cec..44552395 100644 --- a/protocol/bc/tx.go +++ b/protocol/bc/tx.go @@ -13,7 +13,6 @@ type Tx struct { InputIDs []Hash // 1:1 correspondence with TxData.Inputs SpentOutputIDs []Hash - GasInputIDs []Hash } // SigHash ... diff --git a/protocol/bc/types/map.go b/protocol/bc/types/map.go index 7067c755..7cc20470 100644 --- a/protocol/bc/types/map.go +++ b/protocol/bc/types/map.go @@ -1,64 +1,11 @@ package types import ( - "github.com/bytom/bytom/consensus" "github.com/bytom/bytom/protocol/bc" "github.com/bytom/bytom/protocol/vm" "github.com/bytom/bytom/protocol/vm/vmutil" ) -// MapTx converts a types TxData object into its entries-based -// representation. -func MapTx(oldTx *TxData) *bc.Tx { - txID, txHeader, entries := mapTx(oldTx) - tx := &bc.Tx{ - TxHeader: txHeader, - ID: txID, - Entries: entries, - InputIDs: make([]bc.Hash, len(oldTx.Inputs)), - } - - spentOutputIDs := make(map[bc.Hash]bool) - for id, e := range entries { - var ord uint64 - switch e := e.(type) { - case *bc.Issuance: - ord = e.Ordinal - - case *bc.Spend: - ord = e.Ordinal - spentOutputIDs[*e.SpentOutputId] = true - if *e.WitnessDestination.Value.AssetId == *consensus.BTMAssetID { - tx.GasInputIDs = append(tx.GasInputIDs, id) - } - - case *bc.VetoInput: - ord = e.Ordinal - spentOutputIDs[*e.SpentOutputId] = true - if *e.WitnessDestination.Value.AssetId == *consensus.BTMAssetID { - tx.GasInputIDs = append(tx.GasInputIDs, id) - } - - case *bc.Coinbase: - ord = 0 - tx.GasInputIDs = append(tx.GasInputIDs, id) - - default: - continue - } - - if ord >= uint64(len(oldTx.Inputs)) { - continue - } - tx.InputIDs[ord] = id - } - - for id := range spentOutputIDs { - tx.SpentOutputIDs = append(tx.SpentOutputIDs, id) - } - return tx -} - type mapHelper struct { txData *TxData entryMap map[bc.Hash]bc.Entry @@ -72,18 +19,22 @@ type mapHelper struct { muxSources []*bc.ValueSource mux *bc.Mux - resultIDs []*bc.Hash + inputIDs []bc.Hash + spentOutputIDs []bc.Hash + resultIDs []*bc.Hash } func newMapHelper(txData *TxData) *mapHelper { return &mapHelper{ - txData: txData, - entryMap: make(map[bc.Hash]bc.Entry), - spends: []*bc.Spend{}, - issuances: []*bc.Issuance{}, - vetos: []*bc.VetoInput{}, - muxSources: make([]*bc.ValueSource, len(txData.Inputs)), - resultIDs: []*bc.Hash{}, + txData: txData, + entryMap: make(map[bc.Hash]bc.Entry), + spends: []*bc.Spend{}, + issuances: []*bc.Issuance{}, + vetos: []*bc.VetoInput{}, + muxSources: make([]*bc.ValueSource, len(txData.Inputs)), + inputIDs: make([]bc.Hash, len(txData.Inputs)), + spentOutputIDs: []bc.Hash{}, + resultIDs: []*bc.Hash{}, } } @@ -93,9 +44,22 @@ func (mh *mapHelper) addEntry(e bc.Entry) bc.Hash { return id } +func (mh *mapHelper) generateTx() *bc.Tx { + header := bc.NewTxHeader(mh.txData.Version, mh.txData.SerializedSize, mh.txData.TimeRange, mh.resultIDs) + return &bc.Tx{ + TxHeader: header, + ID: mh.addEntry(header), + Entries: mh.entryMap, + InputIDs: mh.inputIDs, + SpentOutputIDs: mh.spentOutputIDs, + } + +} + func (mh *mapHelper) mapCoinbaseInput(i int, input *CoinbaseInput) { mh.coinbase = bc.NewCoinbase(input.Arbitrary) id := mh.addEntry(mh.coinbase) + mh.inputIDs[i] = id mh.muxSources[i] = &bc.ValueSource{ Ref: &id, Value: &mh.txData.Outputs[0].AssetAmount, @@ -123,6 +87,7 @@ func (mh *mapHelper) mapIssuanceInput(i int, input *IssuanceInput) { issuance.WitnessArguments = input.Arguments mh.issuances = append(mh.issuances, issuance) id := mh.addEntry(issuance) + mh.inputIDs[i] = id mh.muxSources[i] = &bc.ValueSource{ Ref: &id, Value: &value, @@ -147,6 +112,8 @@ func (mh *mapHelper) mapSpendInput(i int, input *SpendInput) { spend.WitnessArguments = input.Arguments mh.spends = append(mh.spends, spend) id := mh.addEntry(spend) + mh.inputIDs[i] = id + mh.spentOutputIDs = append(mh.spentOutputIDs, prevoutID) mh.muxSources[i] = &bc.ValueSource{ Ref: &id, Value: &input.AssetAmount, @@ -169,6 +136,8 @@ func (mh *mapHelper) mapVetoInput(i int, input *VetoInput) { vetoInput.WitnessArguments = input.Arguments mh.vetos = append(mh.vetos, vetoInput) id := mh.addEntry(vetoInput) + mh.inputIDs[i] = id + mh.spentOutputIDs = append(mh.spentOutputIDs, prevoutID) mh.muxSources[i] = &bc.ValueSource{ Ref: &id, Value: &input.AssetAmount, @@ -251,14 +220,14 @@ func (mh *mapHelper) mapOutputs() { } } -func mapTx(txData *TxData) (headerID bc.Hash, hdr *bc.TxHeader, entryMap map[bc.Hash]bc.Entry) { +// MapTx converts a types TxData object into its entries-based +// representation. +func MapTx(txData *TxData) *bc.Tx { mh := newMapHelper(txData) mh.mapInputs() mh.initMux() mh.mapOutputs() - - h := bc.NewTxHeader(txData.Version, txData.SerializedSize, txData.TimeRange, mh.resultIDs) - return mh.addEntry(h), h, mh.entryMap + return mh.generateTx() } func mapBlockHeader(old *BlockHeader) (bc.Hash, *bc.BlockHeader) { diff --git a/protocol/bc/types/map_test.go b/protocol/bc/types/map_test.go index 6f0b2b5b..3acdceaa 100644 --- a/protocol/bc/types/map_test.go +++ b/protocol/bc/types/map_test.go @@ -118,9 +118,6 @@ func TestMapCoinbaseTx(t *testing.T) { if len(tx.SpentOutputIDs) != 0 { t.Errorf("coinbase tx doesn't spend any utxo") } - if len(tx.GasInputIDs) != 1 { - t.Errorf("coinbase tx should have 1 gas input") - } if len(tx.ResultIds) != 1 { t.Errorf("expect to only have one output") } diff --git a/protocol/validation/tx.go b/protocol/validation/tx.go index aa0414c9..4c14be36 100644 --- a/protocol/validation/tx.go +++ b/protocol/validation/tx.go @@ -7,21 +7,18 @@ import ( "sync" "github.com/bytom/bytom/consensus" - "github.com/bytom/bytom/consensus/segwit" "github.com/bytom/bytom/errors" "github.com/bytom/bytom/math/checked" "github.com/bytom/bytom/protocol/bc" "github.com/bytom/bytom/protocol/vm" ) -const ruleAA = 142500 - // validate transaction error var ( ErrTxVersion = errors.New("invalid transaction version") ErrWrongTransactionSize = errors.New("invalid transaction size") ErrBadTimeRange = errors.New("invalid transaction time range") - ErrEmptyInputIDs = errors.New("got the empty InputIDs") + ErrInputDoubleSend = errors.New("got the double spend input") ErrNotStandardTx = errors.New("not standard transaction") ErrWrongCoinbaseTransaction = errors.New("wrong coinbase transaction") ErrWrongCoinbaseAsset = errors.New("wrong coinbase assetID") @@ -181,19 +178,6 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { } } - for _, BTMInputID := range vs.tx.GasInputIDs { - e, ok := vs.tx.Entries[BTMInputID] - if !ok { - return errors.Wrapf(bc.ErrMissingEntry, "entry for bytom input %x not found", BTMInputID) - } - - vs2 := *vs - vs2.entryID = BTMInputID - if err := checkValid(&vs2, e); err != nil { - return errors.Wrap(err, "checking gas input") - } - } - for i, dest := range e.WitnessDestinations { vs2 := *vs vs2.destPos = uint64(i) @@ -202,10 +186,6 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { } } - if err := vs.gasStatus.setGasValid(); err != nil { - return err - } - for i, src := range e.Sources { vs2 := *vs vs2.sourcePos = uint64(i) @@ -214,6 +194,10 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { } } + if err := vs.gasStatus.setGasValid(); err != nil { + return err + } + case *bc.Output: vs2 := *vs vs2.sourcePos = 0 @@ -443,43 +427,16 @@ func checkValidDest(vs *validationState, vd *bc.ValueDestination) error { return nil } -func checkStandardTx(tx *bc.Tx, blockHeight uint64) error { +func checkDoubleSpend(tx *bc.Tx) error { + usedInputMap := make(map[bc.Hash]bool) for _, id := range tx.InputIDs { - if blockHeight >= ruleAA && id.IsZero() { - return ErrEmptyInputIDs - } - } - - for _, id := range tx.GasInputIDs { - spend, err := tx.Spend(id) - if err != nil { - continue - } - spentOutput, err := tx.Output(*spend.SpentOutputId) - if err != nil { - return err + if _, ok := usedInputMap[id]; ok { + return ErrInputDoubleSend } - if !segwit.IsP2WScript(spentOutput.ControlProgram.Code) { - return ErrNotStandardTx - } + usedInputMap[id] = true } - for _, id := range tx.ResultIds { - e, ok := tx.Entries[*id] - if !ok { - return errors.Wrapf(bc.ErrMissingEntry, "id %x", id.Bytes()) - } - - output, ok := e.(*bc.Output) - if !ok || *output.Source.Value.AssetId != *consensus.BTMAssetID { - continue - } - - if !segwit.IsP2WScript(output.ControlProgram.Code) { - return ErrNotStandardTx - } - } return nil } @@ -508,7 +465,7 @@ func ValidateTx(tx *bc.Tx, block *bc.Block, converter ProgramConverterFunc) (*Ga return nil, err } - if err := checkStandardTx(tx, block.Height); err != nil { + if err := checkDoubleSpend(tx); err != nil { return nil, err } diff --git a/protocol/validation/tx_test.go b/protocol/validation/tx_test.go index 8719c175..74ef533a 100644 --- a/protocol/validation/tx_test.go +++ b/protocol/validation/tx_test.go @@ -224,8 +224,8 @@ func TestOverflow(t *testing.T) { txInputs := make([]*types.TxInput, 0, len(inputs)) txOutputs := make([]*types.TxOutput, 0, len(outputs)) - for _, amount := range inputs { - txInput := types.NewSpendInput(nil, *sourceID, *consensus.BTMAssetID, amount, 0, ctrlProgram, nil) + for i, amount := range inputs { + txInput := types.NewSpendInput(nil, *sourceID, *consensus.BTMAssetID, amount, uint64(i), ctrlProgram, nil) txInputs = append(txInputs, txInput) } @@ -524,21 +524,11 @@ func TestTxValidation(t *testing.T) { err: ErrOverGasCredit, }, { - desc: "can't find gas spend input in entries", - f: func() { - spendID := mux.Sources[len(mux.Sources)-1].Ref - delete(tx.Entries, *spendID) - mux.Sources = mux.Sources[:len(mux.Sources)-1] - }, - err: bc.ErrMissingEntry, - }, - { desc: "no gas spend input", f: func() { spendID := mux.Sources[len(mux.Sources)-1].Ref delete(tx.Entries, *spendID) mux.Sources = mux.Sources[:len(mux.Sources)-1] - tx.GasInputIDs = nil vs.gasStatus.GasLeft = 0 }, err: vm.ErrRunLimitExceeded, @@ -549,7 +539,6 @@ func TestTxValidation(t *testing.T) { spendID := mux.Sources[len(mux.Sources)-1].Ref delete(tx.Entries, *spendID) mux.Sources = mux.Sources[:len(mux.Sources)-1] - tx.GasInputIDs = nil }, err: nil, }, @@ -823,7 +812,7 @@ func TestCoinbase(t *testing.T) { } } -func TestRuleAA(t *testing.T) { +func TestDoubleSpend(t *testing.T) { testData := "07010004016201609bc47dda88eee18c7433340c16e054cabee4318a8d638e873be19e979df81dc7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0e3f9f5c80e0101160014f233267911e94dc74df706fe3b697273e212d5450063024088b5e730136407312980d3b1446004a8c552111721a4ba48044365cf7f7785542f2d7799f73d7cba1be2301fdfb91ad6ea99559b1857a25336eaefd90675870f207642ba797fd89d1f98a8559b4ca74123697dd4dee882955acd0da9010a80d64e0161015fe334d4fe18398f0232d2aca7050388ce4ee5ae82c8148d7f0cea748438b65135ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80ace684200101160014f233267911e94dc74df706fe3b697273e212d545006302404a17a5995b8163ee448719b462a5694b22a35522dd9883333fd462cc3d0aabf049445c5cbb911a40e1906a5bea99b23b1a79e215eeb1a818d8b1dd27e06f3004207642ba797fd89d1f98a8559b4ca74123697dd4dee882955acd0da9010a80d64e016201609bc47dda88eee18c7433340c16e054cabee4318a8d638e873be19e979df81dc7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0e3f9f5c80e0101160014f233267911e94dc74df706fe3b697273e212d5450063024088b5e730136407312980d3b1446004a8c552111721a4ba48044365cf7f7785542f2d7799f73d7cba1be2301fdfb91ad6ea99559b1857a25336eaefd90675870f207642ba797fd89d1f98a8559b4ca74123697dd4dee882955acd0da9010a80d64e0161015fe334d4fe18398f0232d2aca7050388ce4ee5ae82c8148d7f0cea748438b65135ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80ace684200101160014f233267911e94dc74df706fe3b697273e212d545006302409278702c74eb3ae7666f9da4841443a4b001d6c7d7de631faf9f26eb464f6cdd741dcd4c2f3a1eb47cbc345f56a16902380b8f74b7a559f9bec854bd0e955b0c207642ba797fd89d1f98a8559b4ca74123697dd4dee882955acd0da9010a80d64e0201003fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08ba3fae80e01160014aac0345165045e612b3d7363f39a372bead80ce7000001003fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08fe0fae80e01160014aac0345165045e612b3d7363f39a372bead80ce70000" /* 07 // serflags @@ -931,18 +920,10 @@ func TestRuleAA(t *testing.T) { { block: &bc.Block{ BlockHeader: &bc.BlockHeader{ - Height: ruleAA - 1, - }, - }, - err: ErrMismatchedPosition, - }, - { - block: &bc.Block{ - BlockHeader: &bc.BlockHeader{ - Height: ruleAA, + Height: 5000, }, }, - err: ErrEmptyInputIDs, + err: ErrInputDoubleSend, }, } @@ -1005,53 +986,6 @@ func TestTimeRange(t *testing.T) { } } -func TestStandardTx(t *testing.T) { - fixture := sample(t, nil) - tx := types.NewTx(*fixture.tx).Tx - - cases := []struct { - desc string - f func() - err error - }{ - { - desc: "normal standard tx", - err: nil, - }, - { - desc: "not standard tx in spend input", - f: func() { - inputID := tx.GasInputIDs[0] - spend := tx.Entries[inputID].(*bc.Spend) - spentOutput, err := tx.Output(*spend.SpentOutputId) - if err != nil { - t.Fatal(err) - } - spentOutput.ControlProgram = &bc.Program{Code: []byte{0}} - }, - err: ErrNotStandardTx, - }, - { - desc: "not standard tx in output", - f: func() { - outputID := tx.ResultIds[0] - output := tx.Entries[*outputID].(*bc.Output) - output.ControlProgram = &bc.Program{Code: []byte{0}} - }, - err: ErrNotStandardTx, - }, - } - - for i, c := range cases { - if c.f != nil { - c.f() - } - if err := checkStandardTx(tx, 0); err != c.err { - t.Errorf("case #%d (%s) got error %t, want %t", i, c.desc, err, c.err) - } - } -} - func TestValidateTxVersion(t *testing.T) { converter := func(prog []byte) ([]byte, error) { return nil, nil } cases := []struct { -- 2.11.0