From 2e97a1a277bd106f7b0a5abae025c847fe7156db Mon Sep 17 00:00:00 2001 From: Paolo Galli Date: Fri, 29 Nov 2024 11:48:45 +0100 Subject: [PATCH 01/32] feat: add the dynamic fee tx and support for typed txs --- api/debug/debug_test.go | 4 +++ api/subscriptions/subscriptions_test.go | 12 +++++++++ api/subscriptions/types_test.go | 12 +++++++++ api/transactions/transactions_test.go | 21 +++++++++++++++ tx/transaction.go | 35 +++++++++++++++++++++++++ tx/transaction_test.go | 2 ++ 6 files changed, 86 insertions(+) diff --git a/api/debug/debug_test.go b/api/debug/debug_test.go index 1d98f7bfa..a153a2513 100644 --- a/api/debug/debug_test.go +++ b/api/debug/debug_test.go @@ -559,7 +559,11 @@ func initDebugServer(t *testing.T) { Clause(cla). Clause(cla2). BlockRef(tx.NewBlockRef(0)). +<<<<<<< HEAD MustBuild() +======= + BuildLegacy() +>>>>>>> e6630db0 (feat: add the dynamic fee tx and support for typed txs) transaction = tx.MustSign(transaction, genesis.DevAccounts()[0].PrivateKey) dynFeeTx := tx.NewTxBuilder(tx.DynamicFeeTxType). diff --git a/api/subscriptions/subscriptions_test.go b/api/subscriptions/subscriptions_test.go index ced67cdff..82d5de38e 100644 --- a/api/subscriptions/subscriptions_test.go +++ b/api/subscriptions/subscriptions_test.go @@ -263,7 +263,11 @@ func initSubscriptionsServer(t *testing.T, enabledDeprecated bool) { Nonce(3). Clause(tx.NewClause(nil).WithData(common.Hex2Bytes(eventcontract.HexBytecode))). BlockRef(tx.NewBlockRef(0)). +<<<<<<< HEAD MustBuild() +======= + BuildLegacy() +>>>>>>> e6630db0 (feat: add the dynamic fee tx and support for typed txs) sigTxDeploy, err := crypto.Sign(txDeploy.SigningHash().Bytes(), genesis.DevAccounts()[1].PrivateKey) require.NoError(t, err) txDeploy = txDeploy.WithSignature(sigTxDeploy) @@ -299,7 +303,11 @@ func TestSubscriptionsBacktrace(t *testing.T) { Nonce(1). Clause(cla). BlockRef(tx.NewBlockRef(0)). +<<<<<<< HEAD MustBuild() +======= + BuildLegacy() +>>>>>>> e6630db0 (feat: add the dynamic fee tx and support for typed txs) sig, err := crypto.Sign(tr.SigningHash().Bytes(), genesis.DevAccounts()[0].PrivateKey) if err != nil { @@ -315,7 +323,11 @@ func TestSubscriptionsBacktrace(t *testing.T) { Nonce(3). Clause(tx.NewClause(nil).WithData(common.Hex2Bytes(eventcontract.HexBytecode))). BlockRef(tx.NewBlockRef(0)). +<<<<<<< HEAD MustBuild() +======= + BuildLegacy() +>>>>>>> e6630db0 (feat: add the dynamic fee tx and support for typed txs) sigTxDeploy, err := crypto.Sign(txDeploy.SigningHash().Bytes(), genesis.DevAccounts()[1].PrivateKey) require.NoError(t, err) txDeploy = txDeploy.WithSignature(sigTxDeploy) diff --git a/api/subscriptions/types_test.go b/api/subscriptions/types_test.go index c9028213b..7f667453c 100644 --- a/api/subscriptions/types_test.go +++ b/api/subscriptions/types_test.go @@ -215,6 +215,7 @@ func TestConvertEvent(t *testing.T) { repo, _ := chain.NewRepository(db, b) // New tx +<<<<<<< HEAD transaction := tx.NewTxBuilder(tx.LegacyTxType). ChainTag(repo.ChainTag()). GasPriceCoef(1). @@ -225,6 +226,17 @@ func TestConvertEvent(t *testing.T) { MustBuild() transaction = tx.MustSign( transaction, +======= + transaction := tx.MustSign( + new(tx.Builder). + ChainTag(repo.ChainTag()). + GasPriceCoef(1). + Expiration(10). + Gas(21000). + Nonce(1). + BlockRef(tx.NewBlockRef(0)). + BuildLegacy(), +>>>>>>> e6630db0 (feat: add the dynamic fee tx and support for typed txs) genesis.DevAccounts()[0].PrivateKey, ) diff --git a/api/transactions/transactions_test.go b/api/transactions/transactions_test.go index 41aa8d03e..c1ef7a83c 100644 --- a/api/transactions/transactions_test.go +++ b/api/transactions/transactions_test.go @@ -18,6 +18,7 @@ import ( "github.com/gorilla/mux" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/vechain/go-ethereum/rlp" "github.com/vechain/thor/v2/api/transactions" "github.com/vechain/thor/v2/genesis" "github.com/vechain/thor/v2/test/testchain" @@ -100,6 +101,26 @@ func getLegacyTx(t *testing.T) { assert.Equal(t, hexutil.Encode(rlpTx), rawTx["raw"], "should be equal raw") } +func getDynamicFeeTx(t *testing.T) { + res := httpGetAndCheckResponseStatus(t, "/transactions/"+dynFeeTx.ID().String(), 200) + var rtx *transactions.Transaction + if err := json.Unmarshal(res, &rtx); err != nil { + t.Fatal(err) + } + checkMatchingTx(t, dynFeeTx, rtx) + + res = httpGetAndCheckResponseStatus(t, "/transactions/"+dynFeeTx.ID().String()+"?raw=true", 200) + var rawTx map[string]interface{} + if err := json.Unmarshal(res, &rawTx); err != nil { + t.Fatal(err) + } + rlpTx, err := rlp.EncodeToBytes(dynFeeTx) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, hexutil.Encode(rlpTx), rawTx["raw"], "should be equal raw") +} + func getDynamicFeeTx(t *testing.T) { res := httpGetAndCheckResponseStatus(t, "/transactions/"+dynFeeTx.ID().String(), 200) var rtx *transactions.Transaction diff --git a/tx/transaction.go b/tx/transaction.go index 788193a2a..81cbf8eeb 100644 --- a/tx/transaction.go +++ b/tx/transaction.go @@ -174,6 +174,41 @@ func (t *Transaction) EvaluateWork(origin thor.Address) func(nonce uint64) *big. } } +func (t *Transaction) hashWithoutNonceLegacyTx(origin thor.Address) *thor.Bytes32 { + b := thor.Blake2bFn(func(w io.Writer) { + rlp.Encode(w, []interface{}{ + t.body.chainTag(), + t.body.blockRef(), + t.body.expiration(), + t.body.clauses(), + t.body.gasPriceCoef(), + t.body.dependsOn(), + t.body.nonce(), + t.body.reserved(), + origin, + }) + }) + return &b +} + +func (t *Transaction) hashWithoutNonceDynamicFeeTx(origin thor.Address) *thor.Bytes32 { + b := thor.Blake2bFn(func(w io.Writer) { + rlp.Encode(w, []interface{}{ + t.body.chainTag(), + t.body.blockRef(), + t.body.expiration(), + t.body.clauses(), + t.body.maxFeePerGas(), + t.body.maxPriorityFeePerGas(), + t.body.dependsOn(), + t.body.nonce(), + t.body.reserved(), + origin, + }) + }) + return &b +} + // SigningHash returns hash of tx excludes signature. func (t *Transaction) SigningHash() (hash thor.Bytes32) { if cached := t.cache.signingHash.Load(); cached != nil { diff --git a/tx/transaction_test.go b/tx/transaction_test.go index 5926abc1b..542be4598 100644 --- a/tx/transaction_test.go +++ b/tx/transaction_test.go @@ -7,6 +7,7 @@ package tx_test import ( "encoding/hex" + "fmt" "math/big" "testing" @@ -231,6 +232,7 @@ func TestLegacyTx(t *testing.T) { sig, _ := crypto.Sign(trx.SigningHash().Bytes(), priv) trx = trx.WithSignature(sig) + fmt.Println(trx.ID()) assert.Equal(t, "0xd989829d88b0ed1b06edf5c50174ecfa64f14a64", func() string { s, _ := trx.Origin(); return s.String() }()) assert.Equal(t, "0xda90eaea52980bc4bb8d40cb2ff84d78433b3b4a6e7d50b75736c5e3e77b71ec", trx.ID().String()) From 47ce11bbf7ed3e6e00b08e01427606d8684ebe38 Mon Sep 17 00:00:00 2001 From: Paolo Galli Date: Mon, 2 Dec 2024 17:30:53 +0100 Subject: [PATCH 02/32] test: add tests for txpool and logdb --- tx/transaction_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/tx/transaction_test.go b/tx/transaction_test.go index 542be4598..5926abc1b 100644 --- a/tx/transaction_test.go +++ b/tx/transaction_test.go @@ -7,7 +7,6 @@ package tx_test import ( "encoding/hex" - "fmt" "math/big" "testing" @@ -232,7 +231,6 @@ func TestLegacyTx(t *testing.T) { sig, _ := crypto.Sign(trx.SigningHash().Bytes(), priv) trx = trx.WithSignature(sig) - fmt.Println(trx.ID()) assert.Equal(t, "0xd989829d88b0ed1b06edf5c50174ecfa64f14a64", func() string { s, _ := trx.Origin(); return s.String() }()) assert.Equal(t, "0xda90eaea52980bc4bb8d40cb2ff84d78433b3b4a6e7d50b75736c5e3e77b71ec", trx.ID().String()) From 492ac994bffa40bfe2593bb383ea4dc9141188e8 Mon Sep 17 00:00:00 2001 From: Paolo Galli Date: Tue, 3 Dec 2024 17:35:39 +0100 Subject: [PATCH 03/32] refactor: split builder into legacy and dynFee builder --- api/debug/debug_test.go | 5 +- api/subscriptions/subscriptions_test.go | 13 +--- api/subscriptions/types_test.go | 13 +--- tx/{builder.go => builder_dynamic_fee.go} | 2 +- tx/builder_legacy.go | 82 +++++++++++++++++++++++ txpool/tx_object_test.go | 28 ++++++++ 6 files changed, 114 insertions(+), 29 deletions(-) rename tx/{builder.go => builder_dynamic_fee.go} (98%) create mode 100644 tx/builder_legacy.go diff --git a/api/debug/debug_test.go b/api/debug/debug_test.go index a153a2513..1e4cd46f0 100644 --- a/api/debug/debug_test.go +++ b/api/debug/debug_test.go @@ -559,11 +559,8 @@ func initDebugServer(t *testing.T) { Clause(cla). Clause(cla2). BlockRef(tx.NewBlockRef(0)). -<<<<<<< HEAD MustBuild() -======= - BuildLegacy() ->>>>>>> e6630db0 (feat: add the dynamic fee tx and support for typed txs) + transaction = tx.MustSign(transaction, genesis.DevAccounts()[0].PrivateKey) dynFeeTx := tx.NewTxBuilder(tx.DynamicFeeTxType). diff --git a/api/subscriptions/subscriptions_test.go b/api/subscriptions/subscriptions_test.go index 82d5de38e..7a529830f 100644 --- a/api/subscriptions/subscriptions_test.go +++ b/api/subscriptions/subscriptions_test.go @@ -263,11 +263,8 @@ func initSubscriptionsServer(t *testing.T, enabledDeprecated bool) { Nonce(3). Clause(tx.NewClause(nil).WithData(common.Hex2Bytes(eventcontract.HexBytecode))). BlockRef(tx.NewBlockRef(0)). -<<<<<<< HEAD MustBuild() -======= - BuildLegacy() ->>>>>>> e6630db0 (feat: add the dynamic fee tx and support for typed txs) + sigTxDeploy, err := crypto.Sign(txDeploy.SigningHash().Bytes(), genesis.DevAccounts()[1].PrivateKey) require.NoError(t, err) txDeploy = txDeploy.WithSignature(sigTxDeploy) @@ -303,11 +300,7 @@ func TestSubscriptionsBacktrace(t *testing.T) { Nonce(1). Clause(cla). BlockRef(tx.NewBlockRef(0)). -<<<<<<< HEAD MustBuild() -======= - BuildLegacy() ->>>>>>> e6630db0 (feat: add the dynamic fee tx and support for typed txs) sig, err := crypto.Sign(tr.SigningHash().Bytes(), genesis.DevAccounts()[0].PrivateKey) if err != nil { @@ -323,11 +316,7 @@ func TestSubscriptionsBacktrace(t *testing.T) { Nonce(3). Clause(tx.NewClause(nil).WithData(common.Hex2Bytes(eventcontract.HexBytecode))). BlockRef(tx.NewBlockRef(0)). -<<<<<<< HEAD MustBuild() -======= - BuildLegacy() ->>>>>>> e6630db0 (feat: add the dynamic fee tx and support for typed txs) sigTxDeploy, err := crypto.Sign(txDeploy.SigningHash().Bytes(), genesis.DevAccounts()[1].PrivateKey) require.NoError(t, err) txDeploy = txDeploy.WithSignature(sigTxDeploy) diff --git a/api/subscriptions/types_test.go b/api/subscriptions/types_test.go index 7f667453c..6f4d029ce 100644 --- a/api/subscriptions/types_test.go +++ b/api/subscriptions/types_test.go @@ -215,7 +215,6 @@ func TestConvertEvent(t *testing.T) { repo, _ := chain.NewRepository(db, b) // New tx -<<<<<<< HEAD transaction := tx.NewTxBuilder(tx.LegacyTxType). ChainTag(repo.ChainTag()). GasPriceCoef(1). @@ -224,19 +223,9 @@ func TestConvertEvent(t *testing.T) { Nonce(1). BlockRef(tx.NewBlockRef(0)). MustBuild() + transaction = tx.MustSign( transaction, -======= - transaction := tx.MustSign( - new(tx.Builder). - ChainTag(repo.ChainTag()). - GasPriceCoef(1). - Expiration(10). - Gas(21000). - Nonce(1). - BlockRef(tx.NewBlockRef(0)). - BuildLegacy(), ->>>>>>> e6630db0 (feat: add the dynamic fee tx and support for typed txs) genesis.DevAccounts()[0].PrivateKey, ) diff --git a/tx/builder.go b/tx/builder_dynamic_fee.go similarity index 98% rename from tx/builder.go rename to tx/builder_dynamic_fee.go index cd261d4b9..44ae9680b 100644 --- a/tx/builder.go +++ b/tx/builder_dynamic_fee.go @@ -94,7 +94,7 @@ func (b *Builder) Nonce(nonce uint64) *Builder { } // DependsOn set depended tx. -func (b *Builder) DependsOn(txID *thor.Bytes32) *Builder { +func (b *DynFeeBuilder) DependsOn(txID *thor.Bytes32) *DynFeeBuilder { if txID == nil { b.dependsOn = nil } else { diff --git a/tx/builder_legacy.go b/tx/builder_legacy.go new file mode 100644 index 000000000..0ae1fa7cc --- /dev/null +++ b/tx/builder_legacy.go @@ -0,0 +1,82 @@ +// Copyright (c) 2018 The VeChainThor developers + +// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying +// file LICENSE or + +package tx + +import ( + "encoding/binary" + + "github.com/vechain/thor/v2/thor" +) + +// LegacyBuilder to make it easy to build transaction. +type LegacyBuilder struct { + legacyTx LegacyTransaction +} + +// ChainTag set chain tag. +func (b *LegacyBuilder) ChainTag(tag byte) *LegacyBuilder { + b.legacyTx.ChainTag = tag + return b +} + +// Clause add a clause. +func (b *LegacyBuilder) Clause(c *Clause) *LegacyBuilder { + b.legacyTx.Clauses = append(b.legacyTx.Clauses, c) + return b +} + +// GasPriceCoef set gas price coef. +func (b *LegacyBuilder) GasPriceCoef(coef uint8) *LegacyBuilder { + b.legacyTx.GasPriceCoef = coef + return b +} + +// Gas set gas provision for tx. +func (b *LegacyBuilder) Gas(gas uint64) *LegacyBuilder { + b.legacyTx.Gas = gas + return b +} + +// BlockRef set block reference. +func (b *LegacyBuilder) BlockRef(br BlockRef) *LegacyBuilder { + b.legacyTx.BlockRef = binary.BigEndian.Uint64(br[:]) + return b +} + +// Expiration set expiration. +func (b *LegacyBuilder) Expiration(exp uint32) *LegacyBuilder { + b.legacyTx.Expiration = exp + return b +} + +// Nonce set nonce. +func (b *LegacyBuilder) Nonce(nonce uint64) *LegacyBuilder { + b.legacyTx.Nonce = nonce + return b +} + +// DependsOn set depended tx. +func (b *LegacyBuilder) DependsOn(txID *thor.Bytes32) *LegacyBuilder { + if txID == nil { + b.legacyTx.DependsOn = nil + } else { + cpy := *txID + b.legacyTx.DependsOn = &cpy + } + return b +} + +// Features set features. +func (b *LegacyBuilder) Features(feat Features) *LegacyBuilder { + b.legacyTx.Reserved.Features = feat + return b +} + +// BuildLegacy builds legacy tx object. +func (b *LegacyBuilder) Build() *Transaction { + tx := Transaction{body: &b.legacyTx} + return &tx +} diff --git a/txpool/tx_object_test.go b/txpool/tx_object_test.go index 1fe992214..171e6a33a 100644 --- a/txpool/tx_object_test.go +++ b/txpool/tx_object_test.go @@ -58,6 +58,34 @@ func txBuilder(txType int, chainTag byte, clauses []*tx.Clause, gas uint64, bloc Gas(gas) } +func legacyTxBuilder(chainTag byte, clauses []*tx.Clause, gas uint64, blockRef tx.BlockRef, expiration uint32, dependsOn *thor.Bytes32, features tx.Features) *tx.LegacyBuilder { + builder := new(tx.LegacyBuilder).ChainTag(chainTag) + for _, c := range clauses { + builder.Clause(c) + } + + return builder.BlockRef(blockRef). + Expiration(expiration). + Nonce(rand.Uint64()). //#nosec G404 + DependsOn(dependsOn). + Features(features). + Gas(gas) +} + +func dynFeeTxBuilder(chainTag byte, clauses []*tx.Clause, gas uint64, blockRef tx.BlockRef, expiration uint32, dependsOn *thor.Bytes32, features tx.Features) *tx.DynFeeBuilder { + builder := new(tx.DynFeeBuilder).ChainTag(chainTag) + for _, c := range clauses { + builder.Clause(c) + } + + return builder.BlockRef(blockRef). + Expiration(expiration). + Nonce(rand.Uint64()). //#nosec G404 + DependsOn(dependsOn). + Features(features). + Gas(gas) +} + func SetupTest() (genesis.DevAccount, *chain.Repository, *block.Block, *state.State) { acc := genesis.DevAccounts()[0] From 297cb4e7e539789f9ee82e20ce67d89464baea16 Mon Sep 17 00:00:00 2001 From: Paolo Galli Date: Thu, 5 Dec 2024 10:03:38 +0100 Subject: [PATCH 04/32] test: add more tests including dynamic fee txs --- tx/builder_legacy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tx/builder_legacy.go b/tx/builder_legacy.go index 0ae1fa7cc..ebcdde955 100644 --- a/tx/builder_legacy.go +++ b/tx/builder_legacy.go @@ -1,4 +1,4 @@ -// Copyright (c) 2018 The VeChainThor developers +// Copyright (c) 2024 The VeChainThor developers // Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying // file LICENSE or From 985417ca2613b0b1a801b6b16a3fa6ef56a5eea0 Mon Sep 17 00:00:00 2001 From: Paolo Galli Date: Thu, 5 Dec 2024 16:37:49 +0100 Subject: [PATCH 05/32] test: add tests for legacy and dynamic tx builder --- tx/builder_dynamic_fee_test.go | 92 ++++++++++++++++++++++++++++++++++ tx/builder_legacy_test.go | 89 ++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 tx/builder_dynamic_fee_test.go create mode 100644 tx/builder_legacy_test.go diff --git a/tx/builder_dynamic_fee_test.go b/tx/builder_dynamic_fee_test.go new file mode 100644 index 000000000..cdec978b0 --- /dev/null +++ b/tx/builder_dynamic_fee_test.go @@ -0,0 +1,92 @@ +// Copyright (c) 2024 The VeChainThor developers + +// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying +// file LICENSE or + +package tx + +import ( + "encoding/binary" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/vechain/thor/v2/thor" +) + +func TestDynFeeBuilder_ChainTag(t *testing.T) { + builder := &DynFeeBuilder{} + builder.ChainTag(0x4a) + assert.Equal(t, byte(0x4a), builder.dynamicFeeTx.ChainTag) +} + +func TestDynFeeBuilder_Clause(t *testing.T) { + builder := &DynFeeBuilder{} + addr := thor.BytesToAddress([]byte("to")) + clause := NewClause(&addr) + builder.Clause(clause) + assert.Equal(t, 1, len(builder.dynamicFeeTx.Clauses)) + assert.Equal(t, clause, builder.dynamicFeeTx.Clauses[0]) +} + +func TestDynFeeBuilder_Gas(t *testing.T) { + builder := &DynFeeBuilder{} + builder.Gas(21000) + assert.Equal(t, uint64(21000), builder.dynamicFeeTx.Gas) +} + +func TestDynFeeBuilder_MaxFeePerGas(t *testing.T) { + builder := &DynFeeBuilder{} + maxFee := big.NewInt(1000000000) + builder.MaxFeePerGas(maxFee) + assert.Equal(t, maxFee, builder.dynamicFeeTx.MaxFeePerGas) +} + +func TestDynFeeBuilder_MaxPriorityFeePerGas(t *testing.T) { + builder := &DynFeeBuilder{} + maxPriorityFee := big.NewInt(2000000000) + builder.MaxPriorityFeePerGas(maxPriorityFee) + assert.Equal(t, maxPriorityFee, builder.dynamicFeeTx.MaxPriorityFeePerGas) +} + +func TestDynFeeBuilder_BlockRef(t *testing.T) { + builder := &DynFeeBuilder{} + blockRef := BlockRef{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} + builder.BlockRef(blockRef) + assert.Equal(t, binary.BigEndian.Uint64(blockRef[:]), builder.dynamicFeeTx.BlockRef) +} + +func TestDynFeeBuilder_Expiration(t *testing.T) { + builder := &DynFeeBuilder{} + builder.Expiration(720) + assert.Equal(t, uint32(720), builder.dynamicFeeTx.Expiration) +} + +func TestDynFeeBuilder_Nonce(t *testing.T) { + builder := &DynFeeBuilder{} + builder.Nonce(12345) + assert.Equal(t, uint64(12345), builder.dynamicFeeTx.Nonce) +} + +func TestDynFeeBuilder_DependsOn(t *testing.T) { + builder := &DynFeeBuilder{} + txID := thor.Bytes32{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20} + builder.DependsOn(&txID) + assert.Equal(t, &txID, builder.dynamicFeeTx.DependsOn) + builder.DependsOn(nil) + assert.Nil(t, builder.dynamicFeeTx.DependsOn) +} + +func TestDynFeeBuilder_Features(t *testing.T) { + builder := &DynFeeBuilder{} + features := Features(0x01) + builder.Features(features) + assert.Equal(t, features, builder.dynamicFeeTx.Reserved.Features) +} + +func TestDynFeeBuilder_Build(t *testing.T) { + builder := &DynFeeBuilder{} + tx := builder.Build() + assert.NotNil(t, tx) + assert.Equal(t, &builder.dynamicFeeTx, tx.body) +} diff --git a/tx/builder_legacy_test.go b/tx/builder_legacy_test.go new file mode 100644 index 000000000..2ba47624c --- /dev/null +++ b/tx/builder_legacy_test.go @@ -0,0 +1,89 @@ +// Copyright (c) 2024 The VeChainThor developers + +// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying +// file LICENSE or + +package tx + +import ( + "encoding/binary" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/vechain/thor/v2/thor" +) + +func TestLegacyBuilder_ChainTag(t *testing.T) { + builder := &LegacyBuilder{} + tag := byte(0x4a) + builder.ChainTag(tag) + + assert.Equal(t, tag, builder.Build().ChainTag()) +} + +func TestLegacyBuilder_Clause(t *testing.T) { + builder := &LegacyBuilder{} + addr := thor.BytesToAddress([]byte("to")) + clause := NewClause(&addr) + builder.Clause(clause) + + assert.Equal(t, 1, len(builder.legacyTx.Clauses)) + assert.Equal(t, clause, builder.legacyTx.Clauses[0]) +} + +func TestLegacyBuilder_GasPriceCoef(t *testing.T) { + builder := &LegacyBuilder{} + coef := uint8(10) + builder.GasPriceCoef(coef) + + assert.Equal(t, coef, builder.Build().GasPriceCoef()) +} + +func TestLegacyBuilder_Gas(t *testing.T) { + builder := &LegacyBuilder{} + gas := uint64(21000) + builder.Gas(gas) + + assert.Equal(t, gas, builder.Build().Gas()) +} + +func TestLegacyBuilder_BlockRef(t *testing.T) { + builder := &LegacyBuilder{} + blockRef := BlockRef{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} + builder.BlockRef(blockRef) + + expected := binary.BigEndian.Uint32(blockRef[:]) + assert.Equal(t, expected, builder.Build().BlockRef().Number()) +} + +func TestLegacyBuilder_Expiration(t *testing.T) { + builder := &LegacyBuilder{} + expiration := uint32(100) + builder.Expiration(expiration) + + assert.Equal(t, expiration, builder.Build().Expiration()) +} + +func TestLegacyBuilder_Nonce(t *testing.T) { + builder := &LegacyBuilder{} + nonce := uint64(12345) + builder.Nonce(nonce) + + assert.Equal(t, nonce, builder.Build().Nonce()) +} + +func TestLegacyBuilder_DependsOn(t *testing.T) { + builder := &LegacyBuilder{} + txID := thor.Bytes32{0x01, 0x02, 0x03, 0x04} + builder.DependsOn(&txID) + + assert.Equal(t, txID, *builder.Build().DependsOn()) +} + +func TestLegacyBuilder_Features(t *testing.T) { + builder := &LegacyBuilder{} + features := Features(0x01) + builder.Features(features) + + assert.Equal(t, features, builder.Build().Features()) +} From 18f6cad422ad7f5f1ad88a6fca8475304547dbbd Mon Sep 17 00:00:00 2001 From: paologalligit Date: Wed, 18 Dec 2024 15:24:02 +0100 Subject: [PATCH 06/32] test: add more dynFee txs to tests --- api/transactions/types_test.go | 9 +++++++++ chain/repository_test.go | 4 ++++ packer/flow_test.go | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/api/transactions/types_test.go b/api/transactions/types_test.go index 413e24fa3..220157004 100644 --- a/api/transactions/types_test.go +++ b/api/transactions/types_test.go @@ -132,6 +132,15 @@ func newDynFeeTx(clause *tx.Clause) *tx.Transaction { return tx.WithSignature(sig) } +func newDynFeeTx(clause *tx.Clause) *tx.Transaction { + tx := new(tx.DynFeeBuilder). + Clause(clause). + Build() + pk, _ := crypto.GenerateKey() + sig, _ := crypto.Sign(tx.SigningHash().Bytes(), pk) + return tx.WithSignature(sig) +} + func randomBytes32() thor.Bytes32 { var b32 thor.Bytes32 diff --git a/chain/repository_test.go b/chain/repository_test.go index 22c6f03f3..62ec84c9c 100644 --- a/chain/repository_test.go +++ b/chain/repository_test.go @@ -98,7 +98,11 @@ func TestRepository(t *testing.T) { assert.Equal(t, tx.Receipts{receipt1}.RootHash(), gotReceipts.RootHash()) } +<<<<<<< HEAD tx2 := tx.NewTxBuilder(tx.DynamicFeeTxType).MustBuild() +======= + tx2 := new(tx.DynFeeBuilder).Build() +>>>>>>> 8422b551 (test: add more dynFee txs to tests) receipt2 := &tx.Receipt{} b2 := newBlock(b1, 20, tx2) diff --git a/packer/flow_test.go b/packer/flow_test.go index 2584e22e5..d996d7913 100644 --- a/packer/flow_test.go +++ b/packer/flow_test.go @@ -39,6 +39,25 @@ func createTx(txType int, chainTag byte, gasPriceCoef uint8, expiration uint32, return transaction.WithSignature(signature) } +func createDynFeeTx(chainTag byte, expiration uint32, gas uint64, maxFeePerGas, maxPriorityFeePerGas *big.Int, nonce uint64, dependsOn *thor.Bytes32, clause *tx.Clause, br tx.BlockRef) *tx.Transaction { + builder := new(tx.DynFeeBuilder). + ChainTag(chainTag). + Expiration(expiration). + Gas(gas). + MaxFeePerGas(maxFeePerGas). + MaxPriorityFeePerGas(maxPriorityFeePerGas). + Nonce(nonce). + DependsOn(dependsOn). + Clause(clause). + BlockRef(br) + + transaction := builder.Build() + + signature, _ := crypto.Sign(transaction.SigningHash().Bytes(), genesis.DevAccounts()[0].PrivateKey) + + return transaction.WithSignature(signature) +} + func TestAdopt(t *testing.T) { // Setup environment db := muxdb.NewMem() From 8be755e413ffb330ed819662cc643f7e881d1fa8 Mon Sep 17 00:00:00 2001 From: paologalligit Date: Thu, 9 Jan 2025 16:15:38 +0100 Subject: [PATCH 07/32] refactor: merge tx builder into one builder --- api/transactions/transactions_test.go | 21 ---- api/transactions/types_test.go | 9 -- chain/repository_test.go | 8 ++ packer/flow_test.go | 19 ---- tx/builder.go | 150 ++++++++++++++++++++++++++ tx/builder_dynamic_fee_test.go | 92 ---------------- tx/builder_legacy.go | 82 -------------- tx/builder_legacy_test.go | 89 --------------- txpool/tx_object_test.go | 28 ----- 9 files changed, 158 insertions(+), 340 deletions(-) create mode 100644 tx/builder.go delete mode 100644 tx/builder_dynamic_fee_test.go delete mode 100644 tx/builder_legacy.go delete mode 100644 tx/builder_legacy_test.go diff --git a/api/transactions/transactions_test.go b/api/transactions/transactions_test.go index c1ef7a83c..41aa8d03e 100644 --- a/api/transactions/transactions_test.go +++ b/api/transactions/transactions_test.go @@ -18,7 +18,6 @@ import ( "github.com/gorilla/mux" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/vechain/go-ethereum/rlp" "github.com/vechain/thor/v2/api/transactions" "github.com/vechain/thor/v2/genesis" "github.com/vechain/thor/v2/test/testchain" @@ -101,26 +100,6 @@ func getLegacyTx(t *testing.T) { assert.Equal(t, hexutil.Encode(rlpTx), rawTx["raw"], "should be equal raw") } -func getDynamicFeeTx(t *testing.T) { - res := httpGetAndCheckResponseStatus(t, "/transactions/"+dynFeeTx.ID().String(), 200) - var rtx *transactions.Transaction - if err := json.Unmarshal(res, &rtx); err != nil { - t.Fatal(err) - } - checkMatchingTx(t, dynFeeTx, rtx) - - res = httpGetAndCheckResponseStatus(t, "/transactions/"+dynFeeTx.ID().String()+"?raw=true", 200) - var rawTx map[string]interface{} - if err := json.Unmarshal(res, &rawTx); err != nil { - t.Fatal(err) - } - rlpTx, err := rlp.EncodeToBytes(dynFeeTx) - if err != nil { - t.Fatal(err) - } - assert.Equal(t, hexutil.Encode(rlpTx), rawTx["raw"], "should be equal raw") -} - func getDynamicFeeTx(t *testing.T) { res := httpGetAndCheckResponseStatus(t, "/transactions/"+dynFeeTx.ID().String(), 200) var rtx *transactions.Transaction diff --git a/api/transactions/types_test.go b/api/transactions/types_test.go index 220157004..413e24fa3 100644 --- a/api/transactions/types_test.go +++ b/api/transactions/types_test.go @@ -132,15 +132,6 @@ func newDynFeeTx(clause *tx.Clause) *tx.Transaction { return tx.WithSignature(sig) } -func newDynFeeTx(clause *tx.Clause) *tx.Transaction { - tx := new(tx.DynFeeBuilder). - Clause(clause). - Build() - pk, _ := crypto.GenerateKey() - sig, _ := crypto.Sign(tx.SigningHash().Bytes(), pk) - return tx.WithSignature(sig) -} - func randomBytes32() thor.Bytes32 { var b32 thor.Bytes32 diff --git a/chain/repository_test.go b/chain/repository_test.go index 62ec84c9c..c40c0a150 100644 --- a/chain/repository_test.go +++ b/chain/repository_test.go @@ -71,7 +71,11 @@ func TestRepository(t *testing.T) { assert.Equal(t, b0summary, repo1.BestBlockSummary()) assert.Equal(t, repo1.GenesisBlock().Header().ID()[31], repo1.ChainTag()) +<<<<<<< HEAD tx1 := tx.NewTxBuilder(tx.LegacyTxType).MustBuild() +======= + tx1, _ := tx.NewTxBuilder(tx.LegacyTxType).Build() +>>>>>>> 8a375f1e (refactor: merge tx builder into one builder) receipt1 := &tx.Receipt{} b1 := newBlock(repo1.GenesisBlock(), 10, tx1) @@ -98,11 +102,15 @@ func TestRepository(t *testing.T) { assert.Equal(t, tx.Receipts{receipt1}.RootHash(), gotReceipts.RootHash()) } +<<<<<<< HEAD <<<<<<< HEAD tx2 := tx.NewTxBuilder(tx.DynamicFeeTxType).MustBuild() ======= tx2 := new(tx.DynFeeBuilder).Build() >>>>>>> 8422b551 (test: add more dynFee txs to tests) +======= + tx2, _ := tx.NewTxBuilder(tx.DynamicFeeTxType).Build() +>>>>>>> 8a375f1e (refactor: merge tx builder into one builder) receipt2 := &tx.Receipt{} b2 := newBlock(b1, 20, tx2) diff --git a/packer/flow_test.go b/packer/flow_test.go index d996d7913..2584e22e5 100644 --- a/packer/flow_test.go +++ b/packer/flow_test.go @@ -39,25 +39,6 @@ func createTx(txType int, chainTag byte, gasPriceCoef uint8, expiration uint32, return transaction.WithSignature(signature) } -func createDynFeeTx(chainTag byte, expiration uint32, gas uint64, maxFeePerGas, maxPriorityFeePerGas *big.Int, nonce uint64, dependsOn *thor.Bytes32, clause *tx.Clause, br tx.BlockRef) *tx.Transaction { - builder := new(tx.DynFeeBuilder). - ChainTag(chainTag). - Expiration(expiration). - Gas(gas). - MaxFeePerGas(maxFeePerGas). - MaxPriorityFeePerGas(maxPriorityFeePerGas). - Nonce(nonce). - DependsOn(dependsOn). - Clause(clause). - BlockRef(br) - - transaction := builder.Build() - - signature, _ := crypto.Sign(transaction.SigningHash().Bytes(), genesis.DevAccounts()[0].PrivateKey) - - return transaction.WithSignature(signature) -} - func TestAdopt(t *testing.T) { // Setup environment db := muxdb.NewMem() diff --git a/tx/builder.go b/tx/builder.go new file mode 100644 index 000000000..a441a56f6 --- /dev/null +++ b/tx/builder.go @@ -0,0 +1,150 @@ +// Copyright (c) 2024 The VeChainThor developers + +// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying +// file LICENSE or + +package tx + +import ( + "encoding/binary" + "math/big" + + "github.com/vechain/thor/v2/thor" +) + +// Builder to make it easy to build transaction. +type Builder struct { + txType int + chainTag byte + clauses []*Clause + gasPriceCoef uint8 + maxFeePerGas *big.Int + maxPriorityFeePerGas *big.Int + gas uint64 + blockRef uint64 + expiration uint32 + nonce uint64 + dependsOn *thor.Bytes32 + reserved reserved +} + +func NewTxBuilder(txType int) *Builder { + return &Builder{txType: txType} +} + +// ChainTag set chain tag. +func (b *Builder) ChainTag(tag byte) *Builder { + b.chainTag = tag + return b +} + +// Clause add a clause. +func (b *Builder) Clause(c *Clause) *Builder { + b.clauses = append(b.clauses, c) + return b +} + +func (b *Builder) Clauses(clauses []*Clause) *Builder { + for _, c := range clauses { + b.Clause(c) + } + return b +} + +// GasPriceCoef set gas price coef. +func (b *Builder) GasPriceCoef(coef uint8) *Builder { + b.gasPriceCoef = coef + return b +} + +// MaxFeePerGas set max fee per gas. +func (b *Builder) MaxFeePerGas(maxFeePerGas *big.Int) *Builder { + b.maxFeePerGas = maxFeePerGas + return b +} + +// MaxPriorityFeePerGas set max priority fee per gas. +func (b *Builder) MaxPriorityFeePerGas(maxPriorityFeePerGas *big.Int) *Builder { + b.maxPriorityFeePerGas = maxPriorityFeePerGas + return b +} + +// Gas set gas provision for tx. +func (b *Builder) Gas(gas uint64) *Builder { + b.gas = gas + return b +} + +// BlockRef set block reference. +func (b *Builder) BlockRef(br BlockRef) *Builder { + b.blockRef = binary.BigEndian.Uint64(br[:]) + return b +} + +// Expiration set expiration. +func (b *Builder) Expiration(exp uint32) *Builder { + b.expiration = exp + return b +} + +// Nonce set nonce. +func (b *Builder) Nonce(nonce uint64) *Builder { + b.nonce = nonce + return b +} + +// DependsOn set depended tx. +func (b *Builder) DependsOn(txID *thor.Bytes32) *Builder { + if txID == nil { + b.dependsOn = nil + } else { + cpy := *txID + b.dependsOn = &cpy + } + return b +} + +// Features set features. +func (b *Builder) Features(feat Features) *Builder { + b.reserved.Features = feat + return b +} + +// BuildLegacy builds legacy tx object. +func (b *Builder) Build() (*Transaction, error) { + var tx *Transaction + switch b.txType { + case LegacyTxType: + tx = &Transaction{ + body: &LegacyTransaction{ + ChainTag: b.chainTag, + Clauses: b.clauses, + GasPriceCoef: b.gasPriceCoef, + Gas: b.gas, + BlockRef: b.blockRef, + Expiration: b.expiration, + Nonce: b.nonce, + DependsOn: b.dependsOn, + Reserved: b.reserved, + }, + } + case DynamicFeeTxType: + tx = &Transaction{ + body: &DynamicFeeTransaction{ + ChainTag: b.chainTag, + Clauses: b.clauses, + MaxFeePerGas: b.maxFeePerGas, + MaxPriorityFeePerGas: b.maxPriorityFeePerGas, + Gas: b.gas, + BlockRef: b.blockRef, + Expiration: b.expiration, + Nonce: b.nonce, + DependsOn: b.dependsOn, + Reserved: b.reserved, + }, + } + default: + return nil, ErrTxTypeNotSupported + } + return tx, nil +} diff --git a/tx/builder_dynamic_fee_test.go b/tx/builder_dynamic_fee_test.go deleted file mode 100644 index cdec978b0..000000000 --- a/tx/builder_dynamic_fee_test.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2024 The VeChainThor developers - -// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying -// file LICENSE or - -package tx - -import ( - "encoding/binary" - "math/big" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/vechain/thor/v2/thor" -) - -func TestDynFeeBuilder_ChainTag(t *testing.T) { - builder := &DynFeeBuilder{} - builder.ChainTag(0x4a) - assert.Equal(t, byte(0x4a), builder.dynamicFeeTx.ChainTag) -} - -func TestDynFeeBuilder_Clause(t *testing.T) { - builder := &DynFeeBuilder{} - addr := thor.BytesToAddress([]byte("to")) - clause := NewClause(&addr) - builder.Clause(clause) - assert.Equal(t, 1, len(builder.dynamicFeeTx.Clauses)) - assert.Equal(t, clause, builder.dynamicFeeTx.Clauses[0]) -} - -func TestDynFeeBuilder_Gas(t *testing.T) { - builder := &DynFeeBuilder{} - builder.Gas(21000) - assert.Equal(t, uint64(21000), builder.dynamicFeeTx.Gas) -} - -func TestDynFeeBuilder_MaxFeePerGas(t *testing.T) { - builder := &DynFeeBuilder{} - maxFee := big.NewInt(1000000000) - builder.MaxFeePerGas(maxFee) - assert.Equal(t, maxFee, builder.dynamicFeeTx.MaxFeePerGas) -} - -func TestDynFeeBuilder_MaxPriorityFeePerGas(t *testing.T) { - builder := &DynFeeBuilder{} - maxPriorityFee := big.NewInt(2000000000) - builder.MaxPriorityFeePerGas(maxPriorityFee) - assert.Equal(t, maxPriorityFee, builder.dynamicFeeTx.MaxPriorityFeePerGas) -} - -func TestDynFeeBuilder_BlockRef(t *testing.T) { - builder := &DynFeeBuilder{} - blockRef := BlockRef{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} - builder.BlockRef(blockRef) - assert.Equal(t, binary.BigEndian.Uint64(blockRef[:]), builder.dynamicFeeTx.BlockRef) -} - -func TestDynFeeBuilder_Expiration(t *testing.T) { - builder := &DynFeeBuilder{} - builder.Expiration(720) - assert.Equal(t, uint32(720), builder.dynamicFeeTx.Expiration) -} - -func TestDynFeeBuilder_Nonce(t *testing.T) { - builder := &DynFeeBuilder{} - builder.Nonce(12345) - assert.Equal(t, uint64(12345), builder.dynamicFeeTx.Nonce) -} - -func TestDynFeeBuilder_DependsOn(t *testing.T) { - builder := &DynFeeBuilder{} - txID := thor.Bytes32{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20} - builder.DependsOn(&txID) - assert.Equal(t, &txID, builder.dynamicFeeTx.DependsOn) - builder.DependsOn(nil) - assert.Nil(t, builder.dynamicFeeTx.DependsOn) -} - -func TestDynFeeBuilder_Features(t *testing.T) { - builder := &DynFeeBuilder{} - features := Features(0x01) - builder.Features(features) - assert.Equal(t, features, builder.dynamicFeeTx.Reserved.Features) -} - -func TestDynFeeBuilder_Build(t *testing.T) { - builder := &DynFeeBuilder{} - tx := builder.Build() - assert.NotNil(t, tx) - assert.Equal(t, &builder.dynamicFeeTx, tx.body) -} diff --git a/tx/builder_legacy.go b/tx/builder_legacy.go deleted file mode 100644 index ebcdde955..000000000 --- a/tx/builder_legacy.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2024 The VeChainThor developers - -// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying -// file LICENSE or - -package tx - -import ( - "encoding/binary" - - "github.com/vechain/thor/v2/thor" -) - -// LegacyBuilder to make it easy to build transaction. -type LegacyBuilder struct { - legacyTx LegacyTransaction -} - -// ChainTag set chain tag. -func (b *LegacyBuilder) ChainTag(tag byte) *LegacyBuilder { - b.legacyTx.ChainTag = tag - return b -} - -// Clause add a clause. -func (b *LegacyBuilder) Clause(c *Clause) *LegacyBuilder { - b.legacyTx.Clauses = append(b.legacyTx.Clauses, c) - return b -} - -// GasPriceCoef set gas price coef. -func (b *LegacyBuilder) GasPriceCoef(coef uint8) *LegacyBuilder { - b.legacyTx.GasPriceCoef = coef - return b -} - -// Gas set gas provision for tx. -func (b *LegacyBuilder) Gas(gas uint64) *LegacyBuilder { - b.legacyTx.Gas = gas - return b -} - -// BlockRef set block reference. -func (b *LegacyBuilder) BlockRef(br BlockRef) *LegacyBuilder { - b.legacyTx.BlockRef = binary.BigEndian.Uint64(br[:]) - return b -} - -// Expiration set expiration. -func (b *LegacyBuilder) Expiration(exp uint32) *LegacyBuilder { - b.legacyTx.Expiration = exp - return b -} - -// Nonce set nonce. -func (b *LegacyBuilder) Nonce(nonce uint64) *LegacyBuilder { - b.legacyTx.Nonce = nonce - return b -} - -// DependsOn set depended tx. -func (b *LegacyBuilder) DependsOn(txID *thor.Bytes32) *LegacyBuilder { - if txID == nil { - b.legacyTx.DependsOn = nil - } else { - cpy := *txID - b.legacyTx.DependsOn = &cpy - } - return b -} - -// Features set features. -func (b *LegacyBuilder) Features(feat Features) *LegacyBuilder { - b.legacyTx.Reserved.Features = feat - return b -} - -// BuildLegacy builds legacy tx object. -func (b *LegacyBuilder) Build() *Transaction { - tx := Transaction{body: &b.legacyTx} - return &tx -} diff --git a/tx/builder_legacy_test.go b/tx/builder_legacy_test.go deleted file mode 100644 index 2ba47624c..000000000 --- a/tx/builder_legacy_test.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2024 The VeChainThor developers - -// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying -// file LICENSE or - -package tx - -import ( - "encoding/binary" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/vechain/thor/v2/thor" -) - -func TestLegacyBuilder_ChainTag(t *testing.T) { - builder := &LegacyBuilder{} - tag := byte(0x4a) - builder.ChainTag(tag) - - assert.Equal(t, tag, builder.Build().ChainTag()) -} - -func TestLegacyBuilder_Clause(t *testing.T) { - builder := &LegacyBuilder{} - addr := thor.BytesToAddress([]byte("to")) - clause := NewClause(&addr) - builder.Clause(clause) - - assert.Equal(t, 1, len(builder.legacyTx.Clauses)) - assert.Equal(t, clause, builder.legacyTx.Clauses[0]) -} - -func TestLegacyBuilder_GasPriceCoef(t *testing.T) { - builder := &LegacyBuilder{} - coef := uint8(10) - builder.GasPriceCoef(coef) - - assert.Equal(t, coef, builder.Build().GasPriceCoef()) -} - -func TestLegacyBuilder_Gas(t *testing.T) { - builder := &LegacyBuilder{} - gas := uint64(21000) - builder.Gas(gas) - - assert.Equal(t, gas, builder.Build().Gas()) -} - -func TestLegacyBuilder_BlockRef(t *testing.T) { - builder := &LegacyBuilder{} - blockRef := BlockRef{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} - builder.BlockRef(blockRef) - - expected := binary.BigEndian.Uint32(blockRef[:]) - assert.Equal(t, expected, builder.Build().BlockRef().Number()) -} - -func TestLegacyBuilder_Expiration(t *testing.T) { - builder := &LegacyBuilder{} - expiration := uint32(100) - builder.Expiration(expiration) - - assert.Equal(t, expiration, builder.Build().Expiration()) -} - -func TestLegacyBuilder_Nonce(t *testing.T) { - builder := &LegacyBuilder{} - nonce := uint64(12345) - builder.Nonce(nonce) - - assert.Equal(t, nonce, builder.Build().Nonce()) -} - -func TestLegacyBuilder_DependsOn(t *testing.T) { - builder := &LegacyBuilder{} - txID := thor.Bytes32{0x01, 0x02, 0x03, 0x04} - builder.DependsOn(&txID) - - assert.Equal(t, txID, *builder.Build().DependsOn()) -} - -func TestLegacyBuilder_Features(t *testing.T) { - builder := &LegacyBuilder{} - features := Features(0x01) - builder.Features(features) - - assert.Equal(t, features, builder.Build().Features()) -} diff --git a/txpool/tx_object_test.go b/txpool/tx_object_test.go index 171e6a33a..1fe992214 100644 --- a/txpool/tx_object_test.go +++ b/txpool/tx_object_test.go @@ -58,34 +58,6 @@ func txBuilder(txType int, chainTag byte, clauses []*tx.Clause, gas uint64, bloc Gas(gas) } -func legacyTxBuilder(chainTag byte, clauses []*tx.Clause, gas uint64, blockRef tx.BlockRef, expiration uint32, dependsOn *thor.Bytes32, features tx.Features) *tx.LegacyBuilder { - builder := new(tx.LegacyBuilder).ChainTag(chainTag) - for _, c := range clauses { - builder.Clause(c) - } - - return builder.BlockRef(blockRef). - Expiration(expiration). - Nonce(rand.Uint64()). //#nosec G404 - DependsOn(dependsOn). - Features(features). - Gas(gas) -} - -func dynFeeTxBuilder(chainTag byte, clauses []*tx.Clause, gas uint64, blockRef tx.BlockRef, expiration uint32, dependsOn *thor.Bytes32, features tx.Features) *tx.DynFeeBuilder { - builder := new(tx.DynFeeBuilder).ChainTag(chainTag) - for _, c := range clauses { - builder.Clause(c) - } - - return builder.BlockRef(blockRef). - Expiration(expiration). - Nonce(rand.Uint64()). //#nosec G404 - DependsOn(dependsOn). - Features(features). - Gas(gas) -} - func SetupTest() (genesis.DevAccount, *chain.Repository, *block.Block, *state.State) { acc := genesis.DevAccounts()[0] From c1835042242f55fb4bbd412d3459739c749d30eb Mon Sep 17 00:00:00 2001 From: paologalligit Date: Thu, 23 Jan 2025 16:31:49 +0100 Subject: [PATCH 08/32] refactor: add MustBuild to tx builder and fix error --- api/debug/debug_test.go | 1 - api/subscriptions/subscriptions_test.go | 1 - api/subscriptions/types_test.go | 1 - chain/repository_test.go | 8 ++++++++ tx/builder.go | 11 ++++++++++- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/api/debug/debug_test.go b/api/debug/debug_test.go index 1e4cd46f0..1d98f7bfa 100644 --- a/api/debug/debug_test.go +++ b/api/debug/debug_test.go @@ -560,7 +560,6 @@ func initDebugServer(t *testing.T) { Clause(cla2). BlockRef(tx.NewBlockRef(0)). MustBuild() - transaction = tx.MustSign(transaction, genesis.DevAccounts()[0].PrivateKey) dynFeeTx := tx.NewTxBuilder(tx.DynamicFeeTxType). diff --git a/api/subscriptions/subscriptions_test.go b/api/subscriptions/subscriptions_test.go index 7a529830f..ced67cdff 100644 --- a/api/subscriptions/subscriptions_test.go +++ b/api/subscriptions/subscriptions_test.go @@ -264,7 +264,6 @@ func initSubscriptionsServer(t *testing.T, enabledDeprecated bool) { Clause(tx.NewClause(nil).WithData(common.Hex2Bytes(eventcontract.HexBytecode))). BlockRef(tx.NewBlockRef(0)). MustBuild() - sigTxDeploy, err := crypto.Sign(txDeploy.SigningHash().Bytes(), genesis.DevAccounts()[1].PrivateKey) require.NoError(t, err) txDeploy = txDeploy.WithSignature(sigTxDeploy) diff --git a/api/subscriptions/types_test.go b/api/subscriptions/types_test.go index 6f4d029ce..c9028213b 100644 --- a/api/subscriptions/types_test.go +++ b/api/subscriptions/types_test.go @@ -223,7 +223,6 @@ func TestConvertEvent(t *testing.T) { Nonce(1). BlockRef(tx.NewBlockRef(0)). MustBuild() - transaction = tx.MustSign( transaction, genesis.DevAccounts()[0].PrivateKey, diff --git a/chain/repository_test.go b/chain/repository_test.go index c40c0a150..88014d93f 100644 --- a/chain/repository_test.go +++ b/chain/repository_test.go @@ -71,11 +71,15 @@ func TestRepository(t *testing.T) { assert.Equal(t, b0summary, repo1.BestBlockSummary()) assert.Equal(t, repo1.GenesisBlock().Header().ID()[31], repo1.ChainTag()) +<<<<<<< HEAD <<<<<<< HEAD tx1 := tx.NewTxBuilder(tx.LegacyTxType).MustBuild() ======= tx1, _ := tx.NewTxBuilder(tx.LegacyTxType).Build() >>>>>>> 8a375f1e (refactor: merge tx builder into one builder) +======= + tx1 := tx.NewTxBuilder(tx.LegacyTxType).MustBuild() +>>>>>>> 316917ad (refactor: add MustBuild to tx builder and fix error) receipt1 := &tx.Receipt{} b1 := newBlock(repo1.GenesisBlock(), 10, tx1) @@ -102,6 +106,7 @@ func TestRepository(t *testing.T) { assert.Equal(t, tx.Receipts{receipt1}.RootHash(), gotReceipts.RootHash()) } +<<<<<<< HEAD <<<<<<< HEAD <<<<<<< HEAD tx2 := tx.NewTxBuilder(tx.DynamicFeeTxType).MustBuild() @@ -111,6 +116,9 @@ func TestRepository(t *testing.T) { ======= tx2, _ := tx.NewTxBuilder(tx.DynamicFeeTxType).Build() >>>>>>> 8a375f1e (refactor: merge tx builder into one builder) +======= + tx2 := tx.NewTxBuilder(tx.DynamicFeeTxType).MustBuild() +>>>>>>> 316917ad (refactor: add MustBuild to tx builder and fix error) receipt2 := &tx.Receipt{} b2 := newBlock(b1, 20, tx2) diff --git a/tx/builder.go b/tx/builder.go index a441a56f6..cd261d4b9 100644 --- a/tx/builder.go +++ b/tx/builder.go @@ -110,7 +110,7 @@ func (b *Builder) Features(feat Features) *Builder { return b } -// BuildLegacy builds legacy tx object. +// Build builds a tx object. func (b *Builder) Build() (*Transaction, error) { var tx *Transaction switch b.txType { @@ -148,3 +148,12 @@ func (b *Builder) Build() (*Transaction, error) { } return tx, nil } + +// MustBuild builds a tx object, it panics if an error is returned. +func (b *Builder) MustBuild() *Transaction { + tx, err := b.Build() + if err != nil { + panic(err) + } + return tx +} From edcfcd7852267760e4c41f9fe7295d8a18c1dbbf Mon Sep 17 00:00:00 2001 From: paologalligit Date: Thu, 23 Jan 2025 16:42:46 +0100 Subject: [PATCH 09/32] refactor: move hashWithoutNonce to concrete tx implementation --- tx/transaction.go | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/tx/transaction.go b/tx/transaction.go index 81cbf8eeb..788193a2a 100644 --- a/tx/transaction.go +++ b/tx/transaction.go @@ -174,41 +174,6 @@ func (t *Transaction) EvaluateWork(origin thor.Address) func(nonce uint64) *big. } } -func (t *Transaction) hashWithoutNonceLegacyTx(origin thor.Address) *thor.Bytes32 { - b := thor.Blake2bFn(func(w io.Writer) { - rlp.Encode(w, []interface{}{ - t.body.chainTag(), - t.body.blockRef(), - t.body.expiration(), - t.body.clauses(), - t.body.gasPriceCoef(), - t.body.dependsOn(), - t.body.nonce(), - t.body.reserved(), - origin, - }) - }) - return &b -} - -func (t *Transaction) hashWithoutNonceDynamicFeeTx(origin thor.Address) *thor.Bytes32 { - b := thor.Blake2bFn(func(w io.Writer) { - rlp.Encode(w, []interface{}{ - t.body.chainTag(), - t.body.blockRef(), - t.body.expiration(), - t.body.clauses(), - t.body.maxFeePerGas(), - t.body.maxPriorityFeePerGas(), - t.body.dependsOn(), - t.body.nonce(), - t.body.reserved(), - origin, - }) - }) - return &b -} - // SigningHash returns hash of tx excludes signature. func (t *Transaction) SigningHash() (hash thor.Bytes32) { if cached := t.cache.signingHash.Load(); cached != nil { From 36586769720825a29cf45ef4253751fbaccc90b9 Mon Sep 17 00:00:00 2001 From: Paolo Galli Date: Fri, 29 Nov 2024 11:48:45 +0100 Subject: [PATCH 10/32] feat: add the dynamic fee tx and support for typed txs --- tx/transaction.go | 1 + tx/transaction_test.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/tx/transaction.go b/tx/transaction.go index 788193a2a..d9377b56f 100644 --- a/tx/transaction.go +++ b/tx/transaction.go @@ -24,6 +24,7 @@ import ( var ( errIntrinsicGasOverflow = errors.New("intrinsic gas overflow") + ErrInvalidTxType = errors.New("transaction type not valid in this context") ErrTxTypeNotSupported = errors.New("transaction type not supported") errEmptyTypedTx = errors.New("empty typed transaction bytes") ) diff --git a/tx/transaction_test.go b/tx/transaction_test.go index 5926abc1b..542be4598 100644 --- a/tx/transaction_test.go +++ b/tx/transaction_test.go @@ -7,6 +7,7 @@ package tx_test import ( "encoding/hex" + "fmt" "math/big" "testing" @@ -231,6 +232,7 @@ func TestLegacyTx(t *testing.T) { sig, _ := crypto.Sign(trx.SigningHash().Bytes(), priv) trx = trx.WithSignature(sig) + fmt.Println(trx.ID()) assert.Equal(t, "0xd989829d88b0ed1b06edf5c50174ecfa64f14a64", func() string { s, _ := trx.Origin(); return s.String() }()) assert.Equal(t, "0xda90eaea52980bc4bb8d40cb2ff84d78433b3b4a6e7d50b75736c5e3e77b71ec", trx.ID().String()) From 620e4e24bce4601179a0f2f1b41e17b4daf48dde Mon Sep 17 00:00:00 2001 From: Paolo Galli Date: Fri, 6 Dec 2024 10:45:03 +0100 Subject: [PATCH 11/32] feat: add baseFee to header struct --- block/builder.go | 8 +++ block/header.go | 21 ++++++- block/header_test.go | 36 +++++++++++ consensus/fork/galactica.go | 102 +++++++++++++++++++++++++++++++ consensus/fork/galactica_test.go | 97 +++++++++++++++++++++++++++++ go.mod | 2 +- thor/params.go | 4 ++ tx/transaction.go | 2 +- vm/chain_config.go | 10 +-- 9 files changed, 272 insertions(+), 10 deletions(-) create mode 100644 consensus/fork/galactica.go create mode 100644 consensus/fork/galactica_test.go diff --git a/block/builder.go b/block/builder.go index 00a24c112..00b7bd964 100644 --- a/block/builder.go +++ b/block/builder.go @@ -6,6 +6,8 @@ package block import ( + "math/big" + "github.com/vechain/thor/v2/thor" "github.com/vechain/thor/v2/tx" ) @@ -88,6 +90,12 @@ func (b *Builder) COM() *Builder { return b } +// BaseFee sets base fee. +func (b *Builder) BaseFee(baseFee *big.Int) *Builder { + b.headerBody.BaseFee = baseFee + return b +} + // Build build a block object. func (b *Builder) Build() *Block { header := Header{body: b.headerBody} diff --git a/block/header.go b/block/header.go index b5b51b29b..f12fa6707 100644 --- a/block/header.go +++ b/block/header.go @@ -12,6 +12,7 @@ import ( "errors" "fmt" "io" + "math/big" "sync/atomic" "github.com/ethereum/go-ethereum/crypto" @@ -51,6 +52,7 @@ type headerBody struct { Signature []byte Extension extension + BaseFee *big.Int `rlp:"optional"` } // ParentID returns id of parent block. @@ -84,6 +86,14 @@ func (h *Header) GasUsed() uint64 { return h.body.GasUsed } +// BaseFee returns base fee of this block. +func (h *Header) BaseFee() *big.Int { + if h.body.BaseFee == nil { + return nil + } + return new(big.Int).Set(h.body.BaseFee) +} + // Beneficiary returns reward recipient. func (h *Header) Beneficiary() thor.Address { return h.body.Beneficiary @@ -137,7 +147,7 @@ func (h *Header) SigningHash() (hash thor.Bytes32) { defer func() { h.cache.signingHash.Store(hash) }() return thor.Blake2bFn(func(w io.Writer) { - rlp.Encode(w, []interface{}{ + hashBody := []interface{}{ &h.body.ParentID, h.body.Timestamp, h.body.GasLimit, @@ -149,7 +159,11 @@ func (h *Header) SigningHash() (hash thor.Bytes32) { &h.body.TxsRootFeatures, &h.body.StateRoot, &h.body.ReceiptsRoot, - }) + } + if h.body.BaseFee != nil { + hashBody = append(hashBody, &h.body.BaseFee) + } + rlp.Encode(w, hashBody) }) } @@ -268,6 +282,7 @@ func (h *Header) String() string { Beneficiary: %v GasLimit: %v GasUsed: %v + BaseFee: %v TotalScore: %v TxsRoot: %v TxsFeatures: %v @@ -276,7 +291,7 @@ func (h *Header) String() string { Alpha: 0x%x COM: %v Signature: 0x%x`, h.ID(), h.Number(), h.body.ParentID, h.body.Timestamp, signerStr, - h.body.Beneficiary, h.body.GasLimit, h.body.GasUsed, h.body.TotalScore, + h.body.Beneficiary, h.body.GasLimit, h.body.GasUsed, h.body.BaseFee, h.body.TotalScore, h.body.TxsRootFeatures.Root, h.body.TxsRootFeatures.Features, h.body.StateRoot, h.body.ReceiptsRoot, h.body.Extension.Alpha, h.body.Extension.COM, h.body.Signature) } diff --git a/block/header_test.go b/block/header_test.go index 0d611eac4..abd563d1b 100644 --- a/block/header_test.go +++ b/block/header_test.go @@ -7,6 +7,7 @@ package block import ( "crypto/rand" + "math/big" "sync/atomic" "testing" @@ -107,6 +108,41 @@ func TestHeaderEncoding(t *testing.T) { } } +func TestGalacticaHeaderEncoding(t *testing.T) { + var sig [ComplexSigSize]byte + var alpha [32]byte + rand.Read(sig[:]) + rand.Read(alpha[:]) + baseFee := big.NewInt(10000) + + block := new(Builder).BaseFee(baseFee).Alpha(alpha[:]).Build().WithSignature(sig[:]) + h := block.Header() + + bytes, err := rlp.EncodeToBytes(h) + if err != nil { + t.Fatal(err) + } + + var hh Header + err = rlp.DecodeBytes(bytes, &hh) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, *h, hh) + assert.Equal(t, baseFee, h.BaseFee()) + + data, _, err := rlp.SplitList(bytes) + if err != nil { + t.Fatal(err) + } + count, err := rlp.CountValues(data) + if err != nil { + t.Fatal(err) + } + assert.EqualValues(t, 12, count) +} + // type extension struct{Alpha []byte} func TestEncodingBadExtension(t *testing.T) { var sig [65]byte diff --git a/consensus/fork/galactica.go b/consensus/fork/galactica.go new file mode 100644 index 000000000..67c3b2a03 --- /dev/null +++ b/consensus/fork/galactica.go @@ -0,0 +1,102 @@ +// Copyright (c) 2024 The VeChainThor developers + +// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying +// file LICENSE or + +package fork + +import ( + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/types" + "github.com/vechain/thor/v2/thor" + "github.com/vechain/thor/v2/vm" +) + +// VerifyGalacticaHeader verifies some header attributes which were changed in Galactica fork, +// - gas limit check +// - basefee check +func VerifyGalacticaHeader(config *vm.ChainConfig, parent, header *types.Header) error { + // Verify that the gas limit remains within allowed bounds + parentGasLimit := parent.GasLimit + if !config.IsGalactica(parent.Number) { + parentGasLimit = parent.GasLimit * thor.ElasticityMultiplier + } + if err := VerifyGaslimit(parentGasLimit, header.GasLimit); err != nil { + return err + } + // Verify the header is not malformed + if header.BaseFee == nil { + return fmt.Errorf("header is missing baseFee") + } + // Verify the baseFee is correct based on the parent header. + expectedBaseFee := CalcBaseFee(config, parent) + if header.BaseFee.Cmp(expectedBaseFee) != 0 { + return fmt.Errorf("invalid baseFee: have %s, want %s, parentBaseFee %s, parentGasUsed %d", + expectedBaseFee, header.BaseFee, parent.BaseFee, parent.GasUsed) + } + return nil +} + +// VerifyGaslimit verifies the header gas limit according increase/decrease +// in relation to the parent gas limit. +func VerifyGaslimit(parentGasLimit, headerGasLimit uint64) error { + // Verify that the gas limit remains within allowed bounds + diff := int64(parentGasLimit) - int64(headerGasLimit) + if diff < 0 { + diff *= -1 + } + limit := parentGasLimit / thor.GasLimitBoundDivisor + if uint64(diff) >= limit { + return fmt.Errorf("invalid gas limit: have %d, want %d +-= %d", headerGasLimit, parentGasLimit, limit-1) + } + if headerGasLimit < thor.MinGasLimit { + return errors.New("invalid gas limit below 5000") + } + return nil +} + +// CalcBaseFee calculates the basefee of the header. +func CalcBaseFee(config *vm.ChainConfig, parent *types.Header) *big.Int { + // If the current block is the first Galactica block, return the InitialBaseFee. + if !config.IsGalactica(parent.Number) { + return new(big.Int).SetUint64(thor.InitialBaseFee) + } + + var ( + parentGasTarget = parent.GasLimit / thor.ElasticityMultiplier + parentGasTargetBig = new(big.Int).SetUint64(parentGasTarget) + baseFeeChangeDenominator = new(big.Int).SetUint64(thor.BaseFeeChangeDenominator) + ) + // If the parent gasUsed is the same as the target, the baseFee remains unchanged. + if parent.GasUsed == parentGasTarget { + return new(big.Int).Set(parent.BaseFee) + } + if parent.GasUsed > parentGasTarget { + // If the parent block used more gas than its target, the baseFee should increase. + gasUsedDelta := new(big.Int).SetUint64(parent.GasUsed - parentGasTarget) + x := new(big.Int).Mul(parent.BaseFee, gasUsedDelta) + y := x.Div(x, parentGasTargetBig) + baseFeeDelta := math.BigMax( + x.Div(y, baseFeeChangeDenominator), + common.Big1, + ) + + return x.Add(parent.BaseFee, baseFeeDelta) + } else { + // Otherwise if the parent block used less gas than its target, the baseFee should decrease. + gasUsedDelta := new(big.Int).SetUint64(parentGasTarget - parent.GasUsed) + x := new(big.Int).Mul(parent.BaseFee, gasUsedDelta) + y := x.Div(x, parentGasTargetBig) + baseFeeDelta := x.Div(y, baseFeeChangeDenominator) + + return math.BigMax( + x.Sub(parent.BaseFee, baseFeeDelta), + common.Big0, + ) + } +} diff --git a/consensus/fork/galactica_test.go b/consensus/fork/galactica_test.go new file mode 100644 index 000000000..a9f7c6d19 --- /dev/null +++ b/consensus/fork/galactica_test.go @@ -0,0 +1,97 @@ +// Copyright (c) 2024 The VeChainThor developers + +// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying +// file LICENSE or + +package fork + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/vechain/thor/v2/thor" + "github.com/vechain/thor/v2/vm" +) + +func config() *vm.ChainConfig { + return &vm.ChainConfig{ + GalacticaBlock: big.NewInt(5), + } +} + +// TestBlockGasLimits tests the gasLimit checks for blocks both across +// the Galactica boundary and post-Galactica blocks +func TestBlockGasLimits(t *testing.T) { + initial := new(big.Int).SetUint64(thor.InitialBaseFee) + + for i, tc := range []struct { + pGasLimit uint64 + pNum int64 + gasLimit uint64 + ok bool + }{ + // Transitions from non-Galactica to Galactica + {10000000, 4, 20000000, true}, // No change + {10000000, 4, 20019530, true}, // Upper limit + {10000000, 4, 20019531, false}, // Upper +1 + {10000000, 4, 19980470, true}, // Lower limit + {10000000, 4, 19980469, false}, // Lower limit -1 + // Galactica to Galactica + {20000000, 5, 20000000, true}, + {20000000, 5, 20019530, true}, // Upper limit + {20000000, 5, 20019531, false}, // Upper limit +1 + {20000000, 5, 19980470, true}, // Lower limit + {20000000, 5, 19980469, false}, // Lower limit -1 + {40000000, 5, 40039061, true}, // Upper limit + {40000000, 5, 40039062, false}, // Upper limit +1 + {40000000, 5, 39960939, true}, // lower limit + {40000000, 5, 39960938, false}, // Lower limit -1 + } { + parent := &types.Header{ + GasUsed: tc.pGasLimit / 2, + GasLimit: tc.pGasLimit, + BaseFee: initial, + Number: big.NewInt(tc.pNum), + } + header := &types.Header{ + GasUsed: tc.gasLimit / 2, + GasLimit: tc.gasLimit, + BaseFee: initial, + Number: big.NewInt(tc.pNum + 1), + } + err := VerifyGalacticaHeader(config(), parent, header) + if tc.ok && err != nil { + t.Errorf("test %d: Expected valid header: %s", i, err) + } + if !tc.ok && err == nil { + t.Errorf("test %d: Expected invalid header", i) + } + } +} + +// TestCalcBaseFee assumes all blocks are post Galactica blocks +func TestCalcBaseFee(t *testing.T) { + tests := []struct { + parentBaseFee int64 + parentGasLimit uint64 + parentGasUsed uint64 + expectedBaseFee int64 + }{ + {thor.InitialBaseFee, 20000000, 10000000, thor.InitialBaseFee}, // usage == target + {thor.InitialBaseFee, 20000000, 9000000, 987500000}, // usage below target + {thor.InitialBaseFee, 20000000, 11000000, 1012500000}, // usage above target + } + for i, test := range tests { + parent := &types.Header{ + Number: common.Big32, + GasLimit: test.parentGasLimit, + GasUsed: test.parentGasUsed, + BaseFee: big.NewInt(test.parentBaseFee), + } + if have, want := CalcBaseFee(config(), parent), big.NewInt(test.expectedBaseFee); have.Cmp(want) != 0 { + t.Errorf("test %d: have %d want %d, ", i, have, want) + } + } +} diff --git a/go.mod b/go.mod index 9e8869618..70fb9ec61 100644 --- a/go.mod +++ b/go.mod @@ -64,4 +64,4 @@ require ( replace github.com/syndtr/goleveldb => github.com/vechain/goleveldb v1.0.1-0.20220809091043-51eb019c8655 -replace github.com/ethereum/go-ethereum => github.com/vechain/go-ethereum v1.8.15-0.20241126085506-c74017ec91b2 +replace github.com/ethereum/go-ethereum => /Users/paologalli/code/vechain/go-ethereum diff --git a/thor/params.go b/thor/params.go index d65041e06..499cfbabc 100644 --- a/thor/params.go +++ b/thor/params.go @@ -38,6 +38,10 @@ const ( SeederInterval = 8640 // blocks between two seeder epochs. CheckpointInterval = 180 // blocks between two bft checkpoints. + + ElasticityMultiplier = 2 + InitialBaseFee = 1000000000 + BaseFeeChangeDenominator = 8 ) // Keys of governance params. diff --git a/tx/transaction.go b/tx/transaction.go index d9377b56f..cb95faaf0 100644 --- a/tx/transaction.go +++ b/tx/transaction.go @@ -319,7 +319,7 @@ func (t *Transaction) encodeTyped(w *bytes.Buffer) error { // MarshalBinary returns the canonical encoding of the transaction. // For legacy transactions, it returns the RLP encoding. For typed -// transactions, it returns the type the RLP encoding of the tx. +// transactions, it returns the type RLP encoding of the tx. func (t *Transaction) MarshalBinary() ([]byte, error) { if t.Type() == LegacyTxType { return rlp.EncodeToBytes(t.body) diff --git a/vm/chain_config.go b/vm/chain_config.go index 62cd17837..edc968777 100644 --- a/vm/chain_config.go +++ b/vm/chain_config.go @@ -22,8 +22,8 @@ func isForked(s, head *big.Int) bool { // ChainConfig extends eth ChainConfig. type ChainConfig struct { params.ChainConfig - IstanbulBlock *big.Int // Istanbul switch block (nil = no fork, 0 = already on istanbul) - ShanghaiBlock *big.Int // Shanghai switch block (nil = no fork, 0 = already on shanghai) + IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul) + GalacticaBlock *big.Int `json:"GalacticaBlock,omitempty"` // Galactica switch block (nil = no fork, 0 = already on galactica) } // IsIstanbul returns whether num is either equal to the Istanbul fork block or greater. @@ -31,9 +31,9 @@ func (c *ChainConfig) IsIstanbul(num *big.Int) bool { return isForked(c.IstanbulBlock, num) } -// IsShanghai returns whether num is either equal to the Shanghai fork block or greater. -func (c *ChainConfig) IsShanghai(num *big.Int) bool { - return isForked(c.ShanghaiBlock, num) +// IsGalactica returns whether num is either equal to the Istanbul fork block or greater. +func (c *ChainConfig) IsGalactica(num *big.Int) bool { + return isForked(c.GalacticaBlock, num) } // Rules wraps ChainConfig and is merely syntatic sugar or can be used for functions From e15a48ff48c78df55e6c7f320f307491423c8839 Mon Sep 17 00:00:00 2001 From: Paolo Galli Date: Mon, 9 Dec 2024 09:52:00 +0100 Subject: [PATCH 12/32] test: add more header tests --- block/builder_test.go | 118 +++++++++++++++++++++++++++++++ consensus/fork/galactica_test.go | 30 ++++++++ 2 files changed, 148 insertions(+) create mode 100644 block/builder_test.go diff --git a/block/builder_test.go b/block/builder_test.go new file mode 100644 index 000000000..b6ea94625 --- /dev/null +++ b/block/builder_test.go @@ -0,0 +1,118 @@ +// Copyright (c) 2024 The VeChainThor developers + +// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying +// file LICENSE or + +package block + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/vechain/thor/v2/thor" + "github.com/vechain/thor/v2/tx" +) + +func TestBuilder_ParentID(t *testing.T) { + builder := &Builder{} + id := thor.Bytes32{1, 2, 3} + builder.ParentID(id) + b := builder.Build() + assert.Equal(t, id, b.header.ParentID()) +} + +func TestBuilder_Timestamp(t *testing.T) { + builder := &Builder{} + ts := uint64(1234567890) + builder.Timestamp(ts) + b := builder.Build() + assert.Equal(t, ts, b.header.Timestamp()) +} + +func TestBuilder_TotalScore(t *testing.T) { + builder := &Builder{} + score := uint64(100) + builder.TotalScore(score) + b := builder.Build() + assert.Equal(t, score, b.header.TotalScore()) +} + +func TestBuilder_GasLimit(t *testing.T) { + builder := &Builder{} + limit := uint64(5000) + builder.GasLimit(limit) + b := builder.Build() + assert.Equal(t, limit, b.header.GasLimit()) +} + +func TestBuilder_GasUsed(t *testing.T) { + builder := &Builder{} + used := uint64(3000) + builder.GasUsed(used) + b := builder.Build() + assert.Equal(t, used, b.header.GasUsed()) +} + +func TestBuilder_Beneficiary(t *testing.T) { + builder := &Builder{} + addr := thor.Address{1, 2, 3} + builder.Beneficiary(addr) + b := builder.Build() + assert.Equal(t, addr, b.header.Beneficiary()) +} + +func TestBuilder_StateRoot(t *testing.T) { + builder := &Builder{} + hash := thor.Bytes32{1, 2, 3} + builder.StateRoot(hash) + b := builder.Build() + assert.Equal(t, hash, b.header.StateRoot()) +} + +func TestBuilder_ReceiptsRoot(t *testing.T) { + builder := &Builder{} + hash := thor.Bytes32{1, 2, 3} + builder.ReceiptsRoot(hash) + b := builder.Build() + assert.Equal(t, hash, b.header.ReceiptsRoot()) +} + +func TestBuilder_Transaction(t *testing.T) { + builder := &Builder{} + tx := new(tx.LegacyBuilder).Build() + builder.Transaction(tx) + b := builder.Build() + assert.Contains(t, b.Transactions(), tx) +} + +func TestBuilder_TransactionFeatures(t *testing.T) { + builder := &Builder{} + features := tx.Features(0x01) + builder.TransactionFeatures(features) + b := builder.Build() + assert.Equal(t, features, b.header.TxsFeatures()) +} + +func TestBuilder_Alpha(t *testing.T) { + builder := &Builder{} + alpha := []byte{1, 2, 3} + builder.Alpha(alpha) + b := builder.Build() + assert.Equal(t, alpha, b.header.Alpha()) +} + +func TestBuilder_COM(t *testing.T) { + builder := &Builder{} + builder.COM() + b := builder.Build() + assert.True(t, b.header.COM()) +} + +func TestBuilder_BaseFee(t *testing.T) { + builder := &Builder{} + baseFee := big.NewInt(1000) + builder.BaseFee(baseFee) + b := builder.Build() + assert.Equal(t, baseFee, b.header.BaseFee()) +} diff --git a/consensus/fork/galactica_test.go b/consensus/fork/galactica_test.go index a9f7c6d19..ef986a82c 100644 --- a/consensus/fork/galactica_test.go +++ b/consensus/fork/galactica_test.go @@ -95,3 +95,33 @@ func TestCalcBaseFee(t *testing.T) { } } } + +func TestVerifyGaslimit(t *testing.T) { + for i, tc := range []struct { + parentGasLimit uint64 + headerGasLimit uint64 + ok bool + }{ + // Valid gas limits + {20000000, 20000000, true}, + {20000000, 20019530, true}, // Upper limit + {20000000, 19980470, true}, // Lower limit + {40000000, 40039061, true}, // Upper limit + {40000000, 39960939, true}, // Lower limit + + // Invalid gas limits + {20000000, 20019531, false}, // Upper limit +1 + {20000000, 19980469, false}, // Lower limit -1 + {40000000, 40039062, false}, // Upper limit +1 + {40000000, 39960938, false}, // Lower limit -1 + {20000000, 4999, false}, // Below minimum gas limit + } { + err := VerifyGaslimit(tc.parentGasLimit, tc.headerGasLimit) + if tc.ok && err != nil { + t.Errorf("test %d: Expected valid gas limit: %s", i, err) + } + if !tc.ok && err == nil { + t.Errorf("test %d: Expected invalid gas limit", i) + } + } +} From e8b27459f930efb674741b2729b71a33b428b27e Mon Sep 17 00:00:00 2001 From: paologalligit Date: Thu, 12 Dec 2024 14:56:07 +0100 Subject: [PATCH 13/32] fix: replace header type in baseFee calc --- consensus/fork/galactica.go | 47 ++++++++++++++++++-------------- consensus/fork/galactica_test.go | 32 ++++++++-------------- 2 files changed, 38 insertions(+), 41 deletions(-) diff --git a/consensus/fork/galactica.go b/consensus/fork/galactica.go index 67c3b2a03..4179bc101 100644 --- a/consensus/fork/galactica.go +++ b/consensus/fork/galactica.go @@ -12,7 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/core/types" + "github.com/vechain/thor/v2/block" "github.com/vechain/thor/v2/thor" "github.com/vechain/thor/v2/vm" ) @@ -20,24 +20,25 @@ import ( // VerifyGalacticaHeader verifies some header attributes which were changed in Galactica fork, // - gas limit check // - basefee check -func VerifyGalacticaHeader(config *vm.ChainConfig, parent, header *types.Header) error { +func VerifyGalacticaHeader(config *vm.ChainConfig, parent, header *block.Header) error { // Verify that the gas limit remains within allowed bounds - parentGasLimit := parent.GasLimit - if !config.IsGalactica(parent.Number) { - parentGasLimit = parent.GasLimit * thor.ElasticityMultiplier + parentBlockNum := big.NewInt(int64(parent.Number())) + parentGasLimit := parent.GasLimit() + if !config.IsGalactica(parentBlockNum) { + parentGasLimit = parent.GasLimit() * thor.ElasticityMultiplier } - if err := VerifyGaslimit(parentGasLimit, header.GasLimit); err != nil { + if err := VerifyGaslimit(parentGasLimit, header.GasLimit()); err != nil { return err } // Verify the header is not malformed - if header.BaseFee == nil { + if header.BaseFee() == nil { return fmt.Errorf("header is missing baseFee") } // Verify the baseFee is correct based on the parent header. expectedBaseFee := CalcBaseFee(config, parent) - if header.BaseFee.Cmp(expectedBaseFee) != 0 { + if header.BaseFee().Cmp(expectedBaseFee) != 0 { return fmt.Errorf("invalid baseFee: have %s, want %s, parentBaseFee %s, parentGasUsed %d", - expectedBaseFee, header.BaseFee, parent.BaseFee, parent.GasUsed) + expectedBaseFee, header.BaseFee(), parent.BaseFee(), parent.GasUsed()) } return nil } @@ -61,41 +62,45 @@ func VerifyGaslimit(parentGasLimit, headerGasLimit uint64) error { } // CalcBaseFee calculates the basefee of the header. -func CalcBaseFee(config *vm.ChainConfig, parent *types.Header) *big.Int { +func CalcBaseFee(config *vm.ChainConfig, parent *block.Header) *big.Int { // If the current block is the first Galactica block, return the InitialBaseFee. - if !config.IsGalactica(parent.Number) { + parentBlockNum := big.NewInt(int64(parent.Number())) + if !config.IsGalactica(parentBlockNum) { return new(big.Int).SetUint64(thor.InitialBaseFee) } var ( - parentGasTarget = parent.GasLimit / thor.ElasticityMultiplier + parentGasTarget = parent.GasLimit() / thor.ElasticityMultiplier parentGasTargetBig = new(big.Int).SetUint64(parentGasTarget) baseFeeChangeDenominator = new(big.Int).SetUint64(thor.BaseFeeChangeDenominator) ) + parentGasUsed := parent.GasUsed() + parentBaseFee := parent.BaseFee() + // If the parent gasUsed is the same as the target, the baseFee remains unchanged. - if parent.GasUsed == parentGasTarget { - return new(big.Int).Set(parent.BaseFee) + if parentGasUsed == parentGasTarget { + return new(big.Int).Set(parentBaseFee) } - if parent.GasUsed > parentGasTarget { + if parentGasUsed > parentGasTarget { // If the parent block used more gas than its target, the baseFee should increase. - gasUsedDelta := new(big.Int).SetUint64(parent.GasUsed - parentGasTarget) - x := new(big.Int).Mul(parent.BaseFee, gasUsedDelta) + gasUsedDelta := new(big.Int).SetUint64(parentGasUsed - parentGasTarget) + x := new(big.Int).Mul(parentBaseFee, gasUsedDelta) y := x.Div(x, parentGasTargetBig) baseFeeDelta := math.BigMax( x.Div(y, baseFeeChangeDenominator), common.Big1, ) - return x.Add(parent.BaseFee, baseFeeDelta) + return x.Add(parentBaseFee, baseFeeDelta) } else { // Otherwise if the parent block used less gas than its target, the baseFee should decrease. - gasUsedDelta := new(big.Int).SetUint64(parentGasTarget - parent.GasUsed) - x := new(big.Int).Mul(parent.BaseFee, gasUsedDelta) + gasUsedDelta := new(big.Int).SetUint64(parentGasTarget - parentGasUsed) + x := new(big.Int).Mul(parentBaseFee, gasUsedDelta) y := x.Div(x, parentGasTargetBig) baseFeeDelta := x.Div(y, baseFeeChangeDenominator) return math.BigMax( - x.Sub(parent.BaseFee, baseFeeDelta), + x.Sub(parentBaseFee, baseFeeDelta), common.Big0, ) } diff --git a/consensus/fork/galactica_test.go b/consensus/fork/galactica_test.go index ef986a82c..19b7ce36e 100644 --- a/consensus/fork/galactica_test.go +++ b/consensus/fork/galactica_test.go @@ -6,11 +6,11 @@ package fork import ( + "encoding/binary" "math/big" "testing" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" + "github.com/vechain/thor/v2/block" "github.com/vechain/thor/v2/thor" "github.com/vechain/thor/v2/vm" ) @@ -28,7 +28,7 @@ func TestBlockGasLimits(t *testing.T) { for i, tc := range []struct { pGasLimit uint64 - pNum int64 + pNum uint32 gasLimit uint64 ok bool }{ @@ -49,18 +49,11 @@ func TestBlockGasLimits(t *testing.T) { {40000000, 5, 39960939, true}, // lower limit {40000000, 5, 39960938, false}, // Lower limit -1 } { - parent := &types.Header{ - GasUsed: tc.pGasLimit / 2, - GasLimit: tc.pGasLimit, - BaseFee: initial, - Number: big.NewInt(tc.pNum), - } - header := &types.Header{ - GasUsed: tc.gasLimit / 2, - GasLimit: tc.gasLimit, - BaseFee: initial, - Number: big.NewInt(tc.pNum + 1), - } + var parentID thor.Bytes32 + binary.BigEndian.PutUint32(parentID[:], tc.pNum-1) + + parent := new(block.Builder).ParentID(parentID).GasUsed(tc.pGasLimit / 2).GasLimit(tc.pGasLimit).BaseFee(initial).Build().Header() + header := new(block.Builder).ParentID(parent.ID()).GasUsed(tc.gasLimit / 2).GasLimit(tc.gasLimit).BaseFee(initial).Build().Header() err := VerifyGalacticaHeader(config(), parent, header) if tc.ok && err != nil { t.Errorf("test %d: Expected valid header: %s", i, err) @@ -84,12 +77,11 @@ func TestCalcBaseFee(t *testing.T) { {thor.InitialBaseFee, 20000000, 11000000, 1012500000}, // usage above target } for i, test := range tests { - parent := &types.Header{ - Number: common.Big32, - GasLimit: test.parentGasLimit, - GasUsed: test.parentGasUsed, - BaseFee: big.NewInt(test.parentBaseFee), + b := new(block.Builder).Build() + for j := 0; j < 3; j++ { + b = new(block.Builder).ParentID(b.Header().ID()).GasUsed(test.parentGasUsed).GasLimit(test.parentGasLimit).BaseFee(big.NewInt(test.parentBaseFee)).Build() } + parent := new(block.Builder).ParentID(b.Header().ID()).GasLimit(test.parentGasLimit).GasUsed(test.parentGasUsed).BaseFee(big.NewInt(test.parentBaseFee)).Build().Header() if have, want := CalcBaseFee(config(), parent), big.NewInt(test.expectedBaseFee); have.Cmp(want) != 0 { t.Errorf("test %d: have %d want %d, ", i, have, want) } From 7b42d92e740db00d502d43e265a18dceb8a6378f Mon Sep 17 00:00:00 2001 From: paologalligit Date: Mon, 16 Dec 2024 10:44:30 +0100 Subject: [PATCH 14/32] refactor: use the extension struct to include the baseFee instead of using the optional rlp tag --- block/builder.go | 2 +- block/extension.go | 53 ++++++++++++++---- block/header.go | 11 ++-- block/header_test.go | 95 ++++++++++++++++++++------------ consensus/fork/galactica_test.go | 9 ++- 5 files changed, 113 insertions(+), 57 deletions(-) diff --git a/block/builder.go b/block/builder.go index 00b7bd964..63acfa1a9 100644 --- a/block/builder.go +++ b/block/builder.go @@ -92,7 +92,7 @@ func (b *Builder) COM() *Builder { // BaseFee sets base fee. func (b *Builder) BaseFee(baseFee *big.Int) *Builder { - b.headerBody.BaseFee = baseFee + b.headerBody.Extension.BaseFee = baseFee return b } diff --git a/block/extension.go b/block/extension.go index 692728e91..91b779623 100644 --- a/block/extension.go +++ b/block/extension.go @@ -8,23 +8,36 @@ package block import ( "errors" "io" + "math/big" "github.com/ethereum/go-ethereum/rlp" ) +/** + * extension represents a data structure that follows a tail trim strategy, + * where the last element is not considered if it is its default null value. + */ type extension struct { - Alpha []byte - COM bool + Alpha []byte + COM bool + BaseFee *big.Int } type _extension extension // EncodeRLP implements rlp.Encoder. func (ex *extension) EncodeRLP(w io.Writer) error { - if ex.COM { + if ex.BaseFee != nil { return rlp.Encode(w, (*_extension)(ex)) } + if ex.COM { + return rlp.Encode(w, []interface{}{ + ex.Alpha, + ex.COM, + }) + } + if len(ex.Alpha) != 0 { return rlp.Encode(w, []interface{}{ ex.Alpha, @@ -44,12 +57,13 @@ func (ex *extension) DecodeRLP(s *rlp.Stream) error { *ex = extension{ nil, false, + nil, } return nil } } - if len(raws) == 0 || len(raws) > 2 { + if len(raws) == 0 || len(raws) > 3 { return errors.New("rlp: unexpected extension") } else { var alpha []byte @@ -69,21 +83,40 @@ func (ex *extension) DecodeRLP(s *rlp.Stream) error { } return nil } - var com bool if err := rlp.DecodeBytes(raws[1], &com); err != nil { return err } - // COM must be trimmed if not set - if !com { - return errors.New("rlp: extension must be trimmed") + // alpha and com, make sure baseFee is trimmed + if len(raws) == 2 { + // COM must be trimmed if not set + if !com { + return errors.New("rlp: extension must be trimmed") + } + + *ex = extension{ + Alpha: alpha, + COM: com, + } + return nil } + var baseFee *big.Int + if err := rlp.DecodeBytes(raws[2], &baseFee); err != nil { + return err + } + + // baseFee must be trimmed if not set + if baseFee == nil { + return errors.New("rlp: extension must be trimmed") + } *ex = extension{ - Alpha: alpha, - COM: com, + Alpha: alpha, + COM: com, + BaseFee: baseFee, } + return nil } } diff --git a/block/header.go b/block/header.go index f12fa6707..08f15fe63 100644 --- a/block/header.go +++ b/block/header.go @@ -52,7 +52,6 @@ type headerBody struct { Signature []byte Extension extension - BaseFee *big.Int `rlp:"optional"` } // ParentID returns id of parent block. @@ -88,10 +87,10 @@ func (h *Header) GasUsed() uint64 { // BaseFee returns base fee of this block. func (h *Header) BaseFee() *big.Int { - if h.body.BaseFee == nil { + if h.body.Extension.BaseFee == nil { return nil } - return new(big.Int).Set(h.body.BaseFee) + return new(big.Int).Set(h.body.Extension.BaseFee) } // Beneficiary returns reward recipient. @@ -160,8 +159,8 @@ func (h *Header) SigningHash() (hash thor.Bytes32) { &h.body.StateRoot, &h.body.ReceiptsRoot, } - if h.body.BaseFee != nil { - hashBody = append(hashBody, &h.body.BaseFee) + if h.body.Extension.BaseFee != nil { + hashBody = append(hashBody, &h.body.Extension.BaseFee) } rlp.Encode(w, hashBody) }) @@ -291,7 +290,7 @@ func (h *Header) String() string { Alpha: 0x%x COM: %v Signature: 0x%x`, h.ID(), h.Number(), h.body.ParentID, h.body.Timestamp, signerStr, - h.body.Beneficiary, h.body.GasLimit, h.body.GasUsed, h.body.BaseFee, h.body.TotalScore, + h.body.Beneficiary, h.body.GasLimit, h.body.GasUsed, h.body.Extension.BaseFee, h.body.TotalScore, h.body.TxsRootFeatures.Root, h.body.TxsRootFeatures.Features, h.body.StateRoot, h.body.ReceiptsRoot, h.body.Extension.Alpha, h.body.Extension.COM, h.body.Signature) } diff --git a/block/header_test.go b/block/header_test.go index abd563d1b..3430143a7 100644 --- a/block/header_test.go +++ b/block/header_test.go @@ -108,41 +108,6 @@ func TestHeaderEncoding(t *testing.T) { } } -func TestGalacticaHeaderEncoding(t *testing.T) { - var sig [ComplexSigSize]byte - var alpha [32]byte - rand.Read(sig[:]) - rand.Read(alpha[:]) - baseFee := big.NewInt(10000) - - block := new(Builder).BaseFee(baseFee).Alpha(alpha[:]).Build().WithSignature(sig[:]) - h := block.Header() - - bytes, err := rlp.EncodeToBytes(h) - if err != nil { - t.Fatal(err) - } - - var hh Header - err = rlp.DecodeBytes(bytes, &hh) - if err != nil { - t.Fatal(err) - } - - assert.Equal(t, *h, hh) - assert.Equal(t, baseFee, h.BaseFee()) - - data, _, err := rlp.SplitList(bytes) - if err != nil { - t.Fatal(err) - } - count, err := rlp.CountValues(data) - if err != nil { - t.Fatal(err) - } - assert.EqualValues(t, 12, count) -} - // type extension struct{Alpha []byte} func TestEncodingBadExtension(t *testing.T) { var sig [65]byte @@ -328,6 +293,66 @@ func TestExtensionV2(t *testing.T) { assert.False(t, dst.Extension.COM) }, }, + { + name: "alpha, com and baseFee", + test: func(t *testing.T) { + baseFee := big.NewInt(123456) + bytes, err := rlp.EncodeToBytes(&v2{ + Extension: extension{ + Alpha: thor.Bytes32{}.Bytes(), + COM: true, + BaseFee: baseFee, + }, + }) + assert.Nil(t, err) + + content, _, err := rlp.SplitList(bytes) + assert.Nil(t, err) + + cnt, err := rlp.CountValues(content) + assert.Nil(t, err) + + assert.Equal(t, 1, cnt) + + var dst v2 + err = rlp.DecodeBytes(bytes, &dst) + assert.Nil(t, err) + + assert.Equal(t, thor.Bytes32{}.Bytes(), dst.Extension.Alpha) + assert.True(t, dst.Extension.COM) + assert.Equal(t, baseFee, dst.Extension.BaseFee) + }, + }, + { + name: "alpha, com is false and baseFee", + test: func(t *testing.T) { + baseFee := big.NewInt(123456) + bytes, err := rlp.EncodeToBytes(&v2{ + Extension: extension{ + Alpha: thor.Bytes32{}.Bytes(), + COM: false, + BaseFee: baseFee, + }, + }) + assert.Nil(t, err) + + content, _, err := rlp.SplitList(bytes) + assert.Nil(t, err) + + cnt, err := rlp.CountValues(content) + assert.Nil(t, err) + + assert.Equal(t, 1, cnt) + + var dst v2 + err = rlp.DecodeBytes(bytes, &dst) + assert.Nil(t, err) + + assert.Equal(t, thor.Bytes32{}.Bytes(), dst.Extension.Alpha) + assert.False(t, dst.Extension.COM) + assert.Equal(t, baseFee, dst.Extension.BaseFee) + }, + }, } for _, tt := range tests { diff --git a/consensus/fork/galactica_test.go b/consensus/fork/galactica_test.go index 19b7ce36e..a0bca7f29 100644 --- a/consensus/fork/galactica_test.go +++ b/consensus/fork/galactica_test.go @@ -77,11 +77,10 @@ func TestCalcBaseFee(t *testing.T) { {thor.InitialBaseFee, 20000000, 11000000, 1012500000}, // usage above target } for i, test := range tests { - b := new(block.Builder).Build() - for j := 0; j < 3; j++ { - b = new(block.Builder).ParentID(b.Header().ID()).GasUsed(test.parentGasUsed).GasLimit(test.parentGasLimit).BaseFee(big.NewInt(test.parentBaseFee)).Build() - } - parent := new(block.Builder).ParentID(b.Header().ID()).GasLimit(test.parentGasLimit).GasUsed(test.parentGasUsed).BaseFee(big.NewInt(test.parentBaseFee)).Build().Header() + var parentID thor.Bytes32 + binary.BigEndian.PutUint32(parentID[:], 5) + + parent := new(block.Builder).ParentID(parentID).GasLimit(test.parentGasLimit).GasUsed(test.parentGasUsed).BaseFee(big.NewInt(test.parentBaseFee)).Build().Header() if have, want := CalcBaseFee(config(), parent), big.NewInt(test.expectedBaseFee); have.Cmp(want) != 0 { t.Errorf("test %d: have %d want %d, ", i, have, want) } From 13ca58edb35d2ab9c12a2b39a77877080c78190d Mon Sep 17 00:00:00 2001 From: paologalligit Date: Tue, 17 Dec 2024 10:10:43 +0100 Subject: [PATCH 15/32] feat: add galactica fork --- genesis/customnet_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/genesis/customnet_test.go b/genesis/customnet_test.go index 03a63d068..625953a21 100644 --- a/genesis/customnet_test.go +++ b/genesis/customnet_test.go @@ -24,6 +24,7 @@ func CustomNetWithParams(t *testing.T, executor genesis.Executor, baseGasPrice g ETH_IST: math.MaxUint32, VIP214: math.MaxUint32, FINALITY: 0, + GALACTICA: math.MaxUint32, } devAccounts := genesis.DevAccounts() @@ -136,7 +137,7 @@ func TestNewCustomGenesisMarshalUnmarshal(t *testing.T) { marshalVal, err := json.Marshal(customGenesis) assert.NoError(t, err, "Marshaling should not produce an error") - expectedMarshal := `{"launchTime":1526400000,"gaslimit":0,"extraData":"","accounts":[{"address":"0x0000000000000000000000000000000000000000","balance":"0x0","energy":"0x0","code":"0x608060405234801561001057600080fd5b50606460008190555061017f806100286000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80632f5f3b3c14610046578063a32a3ee414610064578063acfee28314610082575b600080fd5b61004e61009e565b60405161005b91906100d0565b60405180910390f35b61006c6100a4565b60405161007991906100d0565b60405180910390f35b61009c6004803603810190610097919061011c565b6100ad565b005b60005481565b60008054905090565b8060008190555050565b6000819050919050565b6100ca816100b7565b82525050565b60006020820190506100e560008301846100c1565b92915050565b600080fd5b6100f9816100b7565b811461010457600080fd5b50565b600081359050610116816100f0565b92915050565b600060208284031215610132576101316100eb565b5b600061014084828501610107565b9150509291505056fea2646970667358221220a1012465f7be855f040e95566de3bbd50542ba31a7730d7fea2ef9de563a9ac164736f6c63430008110033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000001":"0x0000000000000000000000000000000000000000000000000000000000000002"}},{"address":"0x0000000000000000000000000000000000000000","balance":null,"energy":null,"code":"","storage":null}],"authority":[{"masterAddress":"0xf077b491b355e64048ce21e3a6fc4751eeea77fa","endorsorAddress":"0xf077b491b355e64048ce21e3a6fc4751eeea77fa","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0x435933c8064b4ae76be665428e0307ef2ccfbd68","endorsorAddress":"0x435933c8064b4ae76be665428e0307ef2ccfbd68","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0x0f872421dc479f3c11edd89512731814d0598db5","endorsorAddress":"0x0f872421dc479f3c11edd89512731814d0598db5","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0xf370940abdbd2583bc80bfc19d19bc216c88ccf0","endorsorAddress":"0xf370940abdbd2583bc80bfc19d19bc216c88ccf0","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0x99602e4bbc0503b8ff4432bb1857f916c3653b85","endorsorAddress":"0x99602e4bbc0503b8ff4432bb1857f916c3653b85","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0x61e7d0c2b25706be3485980f39a3a994a8207acf","endorsorAddress":"0x61e7d0c2b25706be3485980f39a3a994a8207acf","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0x361277d1b27504f36a3b33d3a52d1f8270331b8c","endorsorAddress":"0x361277d1b27504f36a3b33d3a52d1f8270331b8c","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0xd7f75a0a1287ab2916848909c8531a0ea9412800","endorsorAddress":"0xd7f75a0a1287ab2916848909c8531a0ea9412800","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0xabef6032b9176c186f6bf984f548bda53349f70a","endorsorAddress":"0xabef6032b9176c186f6bf984f548bda53349f70a","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0x865306084235bf804c8bba8a8d56890940ca8f0b","endorsorAddress":"0x865306084235bf804c8bba8a8d56890940ca8f0b","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"}],"params":{"rewardRatio":"-0x64","baseGasPrice":"0x0","proposerEndorsement":"0x0","executorAddress":null,"maxBlockProposers":10000},"executor":{"approvers":null},"forkConfig":{"VIP191":4294967295,"ETH_CONST":4294967295,"BLOCKLIST":4294967295,"ETH_IST":4294967295,"VIP214":4294967295,"FINALITY":0,"GALACTICA":0}}` + expectedMarshal := `{"launchTime":1526400000,"gaslimit":0,"extraData":"","accounts":[{"address":"0x0000000000000000000000000000000000000000","balance":"0x0","energy":"0x0","code":"0x608060405234801561001057600080fd5b50606460008190555061017f806100286000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80632f5f3b3c14610046578063a32a3ee414610064578063acfee28314610082575b600080fd5b61004e61009e565b60405161005b91906100d0565b60405180910390f35b61006c6100a4565b60405161007991906100d0565b60405180910390f35b61009c6004803603810190610097919061011c565b6100ad565b005b60005481565b60008054905090565b8060008190555050565b6000819050919050565b6100ca816100b7565b82525050565b60006020820190506100e560008301846100c1565b92915050565b600080fd5b6100f9816100b7565b811461010457600080fd5b50565b600081359050610116816100f0565b92915050565b600060208284031215610132576101316100eb565b5b600061014084828501610107565b9150509291505056fea2646970667358221220a1012465f7be855f040e95566de3bbd50542ba31a7730d7fea2ef9de563a9ac164736f6c63430008110033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000001":"0x0000000000000000000000000000000000000000000000000000000000000002"}},{"address":"0x0000000000000000000000000000000000000000","balance":null,"energy":null,"code":"","storage":null}],"authority":[{"masterAddress":"0xf077b491b355e64048ce21e3a6fc4751eeea77fa","endorsorAddress":"0xf077b491b355e64048ce21e3a6fc4751eeea77fa","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0x435933c8064b4ae76be665428e0307ef2ccfbd68","endorsorAddress":"0x435933c8064b4ae76be665428e0307ef2ccfbd68","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0x0f872421dc479f3c11edd89512731814d0598db5","endorsorAddress":"0x0f872421dc479f3c11edd89512731814d0598db5","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0xf370940abdbd2583bc80bfc19d19bc216c88ccf0","endorsorAddress":"0xf370940abdbd2583bc80bfc19d19bc216c88ccf0","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0x99602e4bbc0503b8ff4432bb1857f916c3653b85","endorsorAddress":"0x99602e4bbc0503b8ff4432bb1857f916c3653b85","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0x61e7d0c2b25706be3485980f39a3a994a8207acf","endorsorAddress":"0x61e7d0c2b25706be3485980f39a3a994a8207acf","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0x361277d1b27504f36a3b33d3a52d1f8270331b8c","endorsorAddress":"0x361277d1b27504f36a3b33d3a52d1f8270331b8c","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0xd7f75a0a1287ab2916848909c8531a0ea9412800","endorsorAddress":"0xd7f75a0a1287ab2916848909c8531a0ea9412800","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0xabef6032b9176c186f6bf984f548bda53349f70a","endorsorAddress":"0xabef6032b9176c186f6bf984f548bda53349f70a","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"},{"masterAddress":"0x865306084235bf804c8bba8a8d56890940ca8f0b","endorsorAddress":"0x865306084235bf804c8bba8a8d56890940ca8f0b","identity":"0x00000000000000000000000000000000000000000000000000006d6173746572"}],"params":{"rewardRatio":"-0x64","baseGasPrice":"0x0","proposerEndorsement":"0x0","executorAddress":null,"maxBlockProposers":10000},"executor":{"approvers":null},"forkConfig":{"VIP191":4294967295,"ETH_CONST":4294967295,"BLOCKLIST":4294967295,"ETH_IST":4294967295,"VIP214":4294967295,"FINALITY":0,"GALACTICA":4294967295}}` assert.Equal(t, expectedMarshal, string(marshalVal)) } From 9af7b02514b1ac1e1e9d7184296db405ec5ee1cb Mon Sep 17 00:00:00 2001 From: paologalligit Date: Tue, 17 Dec 2024 17:43:19 +0100 Subject: [PATCH 16/32] feat: add header check for galactica fork --- consensus/consensus_test.go | 13 +++++++++++++ consensus/fork/galactica.go | 11 ++++------- consensus/fork/galactica_test.go | 7 +++---- consensus/validator.go | 11 +++++++++++ 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/consensus/consensus_test.go b/consensus/consensus_test.go index 41c28b21c..83d19da9b 100644 --- a/consensus/consensus_test.go +++ b/consensus/consensus_test.go @@ -88,6 +88,7 @@ func newTestConsensus() (*testConsensus, error) { forkConfig.VIP191 = 1 forkConfig.BLOCKLIST = 0 forkConfig.VIP214 = 2 + forkConfig.GALACTICA = 5 proposer := genesis.DevAccounts()[0] p := packer.New(repo, stater, proposer.Address, &proposer.Address, forkConfig) @@ -445,6 +446,18 @@ func TestValidateBlockHeader(t *testing.T) { assert.Equal(t, expected, err) }, }, + { + "Invalid BaseFee", func(t *testing.T) { + + builder := tc.builder(tc.original.Header()) + blk, err := tc.sign(builder.BaseFee(big.NewInt(10000))) + assert.NoError(t, err) + + err = tc.consent(blk) + expected := consensusError("invalid block: baseFee should not set before fork GALACTICA") + assert.Equal(t, expected, err) + }, + }, } for _, tt := range tests { diff --git a/consensus/fork/galactica.go b/consensus/fork/galactica.go index 4179bc101..eaba8dee7 100644 --- a/consensus/fork/galactica.go +++ b/consensus/fork/galactica.go @@ -14,17 +14,15 @@ import ( "github.com/ethereum/go-ethereum/common/math" "github.com/vechain/thor/v2/block" "github.com/vechain/thor/v2/thor" - "github.com/vechain/thor/v2/vm" ) // VerifyGalacticaHeader verifies some header attributes which were changed in Galactica fork, // - gas limit check // - basefee check -func VerifyGalacticaHeader(config *vm.ChainConfig, parent, header *block.Header) error { +func VerifyGalacticaHeader(config *thor.ForkConfig, parent, header *block.Header) error { // Verify that the gas limit remains within allowed bounds - parentBlockNum := big.NewInt(int64(parent.Number())) parentGasLimit := parent.GasLimit() - if !config.IsGalactica(parentBlockNum) { + if parent.Number() < config.GALACTICA { parentGasLimit = parent.GasLimit() * thor.ElasticityMultiplier } if err := VerifyGaslimit(parentGasLimit, header.GasLimit()); err != nil { @@ -62,10 +60,9 @@ func VerifyGaslimit(parentGasLimit, headerGasLimit uint64) error { } // CalcBaseFee calculates the basefee of the header. -func CalcBaseFee(config *vm.ChainConfig, parent *block.Header) *big.Int { +func CalcBaseFee(config *thor.ForkConfig, parent *block.Header) *big.Int { // If the current block is the first Galactica block, return the InitialBaseFee. - parentBlockNum := big.NewInt(int64(parent.Number())) - if !config.IsGalactica(parentBlockNum) { + if parent.Number() == config.GALACTICA { return new(big.Int).SetUint64(thor.InitialBaseFee) } diff --git a/consensus/fork/galactica_test.go b/consensus/fork/galactica_test.go index a0bca7f29..c1cee20c7 100644 --- a/consensus/fork/galactica_test.go +++ b/consensus/fork/galactica_test.go @@ -12,12 +12,11 @@ import ( "github.com/vechain/thor/v2/block" "github.com/vechain/thor/v2/thor" - "github.com/vechain/thor/v2/vm" ) -func config() *vm.ChainConfig { - return &vm.ChainConfig{ - GalacticaBlock: big.NewInt(5), +func config() *thor.ForkConfig { + return &thor.ForkConfig{ + GALACTICA: 5, } } diff --git a/consensus/validator.go b/consensus/validator.go index dc7ee85b3..d34778a96 100644 --- a/consensus/validator.go +++ b/consensus/validator.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/vechain/thor/v2/block" "github.com/vechain/thor/v2/builtin" + "github.com/vechain/thor/v2/consensus/fork" "github.com/vechain/thor/v2/poa" "github.com/vechain/thor/v2/runtime" "github.com/vechain/thor/v2/state" @@ -156,6 +157,16 @@ func (c *Consensus) validateBlockHeader(header *block.Header, parent *block.Head } } + if header.Number() < c.forkConfig.GALACTICA { + if header.BaseFee() != nil { + return consensusError("invalid block: baseFee should not set before fork GALACTICA") + } + } else { + if err := fork.VerifyGalacticaHeader(&c.forkConfig, parent, header); err != nil { + return consensusError(fmt.Sprintf("block header invalid: %v", err)) + } + } + return nil } From d539df732587d83b49b5716198a503061d45a4c7 Mon Sep 17 00:00:00 2001 From: paologalligit Date: Wed, 18 Dec 2024 17:33:18 +0100 Subject: [PATCH 17/32] feat: add checks for new galactica header --- api/accounts/accounts.go | 1 + api/debug/debug.go | 1 + consensus/consensus.go | 1 + consensus/consensus_test.go | 1 - consensus/fork/galactica.go | 23 ++------------ consensus/fork/galactica_test.go | 54 +++++++------------------------- consensus/validator.go | 9 +++--- packer/packer.go | 2 ++ runtime/runtime.go | 10 ++++-- xenv/env.go | 1 + 10 files changed, 33 insertions(+), 70 deletions(-) diff --git a/api/accounts/accounts.go b/api/accounts/accounts.go index 4e78e8711..b7ad99765 100644 --- a/api/accounts/accounts.go +++ b/api/accounts/accounts.go @@ -256,6 +256,7 @@ func (a *Accounts) batchCall( Time: header.Timestamp(), GasLimit: header.GasLimit(), TotalScore: header.TotalScore(), + BaseFee: header.BaseFee(), }, a.forkConfig) results = make(BatchCallResults, 0) diff --git a/api/debug/debug.go b/api/debug/debug.go index 497518982..2eef23e8e 100644 --- a/api/debug/debug.go +++ b/api/debug/debug.go @@ -268,6 +268,7 @@ func (d *Debug) traceCall(ctx context.Context, tracer tracers.Tracer, header *bl Time: header.Timestamp(), GasLimit: header.GasLimit(), TotalScore: header.TotalScore(), + BaseFee: header.BaseFee(), }, d.forkConfig) diff --git a/consensus/consensus.go b/consensus/consensus.go index 8d6f7a9c3..e6b7801bc 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -96,6 +96,7 @@ func (c *Consensus) NewRuntimeForReplay(header *block.Header, skipPoA bool) (*ru Time: header.Timestamp(), GasLimit: header.GasLimit(), TotalScore: header.TotalScore(), + BaseFee: header.BaseFee(), }, c.forkConfig), nil } diff --git a/consensus/consensus_test.go b/consensus/consensus_test.go index 83d19da9b..1223c27ac 100644 --- a/consensus/consensus_test.go +++ b/consensus/consensus_test.go @@ -448,7 +448,6 @@ func TestValidateBlockHeader(t *testing.T) { }, { "Invalid BaseFee", func(t *testing.T) { - builder := tc.builder(tc.original.Header()) blk, err := tc.sign(builder.BaseFee(big.NewInt(10000))) assert.NoError(t, err) diff --git a/consensus/fork/galactica.go b/consensus/fork/galactica.go index eaba8dee7..718242d71 100644 --- a/consensus/fork/galactica.go +++ b/consensus/fork/galactica.go @@ -6,7 +6,6 @@ package fork import ( - "errors" "fmt" "math/big" @@ -25,8 +24,8 @@ func VerifyGalacticaHeader(config *thor.ForkConfig, parent, header *block.Header if parent.Number() < config.GALACTICA { parentGasLimit = parent.GasLimit() * thor.ElasticityMultiplier } - if err := VerifyGaslimit(parentGasLimit, header.GasLimit()); err != nil { - return err + if err := block.GasLimit(header.GasLimit()).IsValid(parentGasLimit); !err { + return fmt.Errorf("invalid gas limit: have %d, want %d", header.GasLimit(), parentGasLimit) } // Verify the header is not malformed if header.BaseFee() == nil { @@ -41,24 +40,6 @@ func VerifyGalacticaHeader(config *thor.ForkConfig, parent, header *block.Header return nil } -// VerifyGaslimit verifies the header gas limit according increase/decrease -// in relation to the parent gas limit. -func VerifyGaslimit(parentGasLimit, headerGasLimit uint64) error { - // Verify that the gas limit remains within allowed bounds - diff := int64(parentGasLimit) - int64(headerGasLimit) - if diff < 0 { - diff *= -1 - } - limit := parentGasLimit / thor.GasLimitBoundDivisor - if uint64(diff) >= limit { - return fmt.Errorf("invalid gas limit: have %d, want %d +-= %d", headerGasLimit, parentGasLimit, limit-1) - } - if headerGasLimit < thor.MinGasLimit { - return errors.New("invalid gas limit below 5000") - } - return nil -} - // CalcBaseFee calculates the basefee of the header. func CalcBaseFee(config *thor.ForkConfig, parent *block.Header) *big.Int { // If the current block is the first Galactica block, return the InitialBaseFee. diff --git a/consensus/fork/galactica_test.go b/consensus/fork/galactica_test.go index c1cee20c7..cdfa2b73f 100644 --- a/consensus/fork/galactica_test.go +++ b/consensus/fork/galactica_test.go @@ -33,20 +33,20 @@ func TestBlockGasLimits(t *testing.T) { }{ // Transitions from non-Galactica to Galactica {10000000, 4, 20000000, true}, // No change - {10000000, 4, 20019530, true}, // Upper limit - {10000000, 4, 20019531, false}, // Upper +1 - {10000000, 4, 19980470, true}, // Lower limit - {10000000, 4, 19980469, false}, // Lower limit -1 + {10000000, 4, 20019531, true}, // Upper limit + {10000000, 4, 20019532, false}, // Upper +1 + {10000000, 4, 19980469, true}, // Lower limit + {10000000, 4, 19980468, false}, // Lower limit -1 // Galactica to Galactica {20000000, 5, 20000000, true}, - {20000000, 5, 20019530, true}, // Upper limit - {20000000, 5, 20019531, false}, // Upper limit +1 - {20000000, 5, 19980470, true}, // Lower limit - {20000000, 5, 19980469, false}, // Lower limit -1 - {40000000, 5, 40039061, true}, // Upper limit - {40000000, 5, 40039062, false}, // Upper limit +1 - {40000000, 5, 39960939, true}, // lower limit - {40000000, 5, 39960938, false}, // Lower limit -1 + {20000000, 5, 20019531, true}, // Upper limit + {20000000, 5, 20019532, false}, // Upper limit +1 + {20000000, 5, 19980469, true}, // Lower limit + {20000000, 5, 19980468, false}, // Lower limit -1 + {40000000, 5, 40039062, true}, // Upper limit + {40000000, 5, 40039063, false}, // Upper limit +1 + {40000000, 5, 39960938, true}, // lower limit + {40000000, 5, 39960937, false}, // Lower limit -1 } { var parentID thor.Bytes32 binary.BigEndian.PutUint32(parentID[:], tc.pNum-1) @@ -85,33 +85,3 @@ func TestCalcBaseFee(t *testing.T) { } } } - -func TestVerifyGaslimit(t *testing.T) { - for i, tc := range []struct { - parentGasLimit uint64 - headerGasLimit uint64 - ok bool - }{ - // Valid gas limits - {20000000, 20000000, true}, - {20000000, 20019530, true}, // Upper limit - {20000000, 19980470, true}, // Lower limit - {40000000, 40039061, true}, // Upper limit - {40000000, 39960939, true}, // Lower limit - - // Invalid gas limits - {20000000, 20019531, false}, // Upper limit +1 - {20000000, 19980469, false}, // Lower limit -1 - {40000000, 40039062, false}, // Upper limit +1 - {40000000, 39960938, false}, // Lower limit -1 - {20000000, 4999, false}, // Below minimum gas limit - } { - err := VerifyGaslimit(tc.parentGasLimit, tc.headerGasLimit) - if tc.ok && err != nil { - t.Errorf("test %d: Expected valid gas limit: %s", i, err) - } - if !tc.ok && err == nil { - t.Errorf("test %d: Expected invalid gas limit", i) - } - } -} diff --git a/consensus/validator.go b/consensus/validator.go index d34778a96..70c4ef761 100644 --- a/consensus/validator.go +++ b/consensus/validator.go @@ -104,10 +104,6 @@ func (c *Consensus) validateBlockHeader(header *block.Header, parent *block.Head return errFutureBlock } - if !block.GasLimit(header.GasLimit()).IsValid(parent.GasLimit()) { - return consensusError(fmt.Sprintf("block gas limit invalid: parent %v, current %v", parent.GasLimit(), header.GasLimit())) - } - if header.GasUsed() > header.GasLimit() { return consensusError(fmt.Sprintf("block gas used exceeds limit: limit %v, used %v", header.GasLimit(), header.GasUsed())) } @@ -161,6 +157,10 @@ func (c *Consensus) validateBlockHeader(header *block.Header, parent *block.Head if header.BaseFee() != nil { return consensusError("invalid block: baseFee should not set before fork GALACTICA") } + + if !block.GasLimit(header.GasLimit()).IsValid(parent.GasLimit()) { + return consensusError(fmt.Sprintf("block gas limit invalid: parent %v, current %v", parent.GasLimit(), header.GasLimit())) + } } else { if err := fork.VerifyGalacticaHeader(&c.forkConfig, parent, header); err != nil { return consensusError(fmt.Sprintf("block header invalid: %v", err)) @@ -283,6 +283,7 @@ func (c *Consensus) verifyBlock(blk *block.Block, state *state.State, blockConfl Time: header.Timestamp(), GasLimit: header.GasLimit(), TotalScore: header.TotalScore(), + BaseFee: header.BaseFee(), }, c.forkConfig) diff --git a/packer/packer.go b/packer/packer.go index aa85ade87..39717fa91 100644 --- a/packer/packer.go +++ b/packer/packer.go @@ -131,6 +131,7 @@ func (p *Packer) Schedule(parent *chain.BlockSummary, nowTimestamp uint64) (flow Time: newBlockTime, GasLimit: p.gasLimit(parent.Header.GasLimit()), TotalScore: parent.Header.TotalScore() + score, + BaseFee: parent.Header.BaseFee(), }, p.forkConfig) @@ -163,6 +164,7 @@ func (p *Packer) Mock(parent *chain.BlockSummary, targetTime uint64, gasLimit ui Time: targetTime, GasLimit: gl, TotalScore: parent.Header.TotalScore() + 1, + BaseFee: parent.Header.BaseFee(), }, p.forkConfig) diff --git a/runtime/runtime.go b/runtime/runtime.go index cbf6d7de0..22accbde1 100644 --- a/runtime/runtime.go +++ b/runtime/runtime.go @@ -155,7 +155,13 @@ func (rt *Runtime) SetVMConfig(config vm.Config) *Runtime { } func (rt *Runtime) newEVM(stateDB *statedb.StateDB, clauseIndex uint32, txCtx *xenv.TransactionContext) *vm.EVM { - var lastNonNativeCallGas uint64 + var ( + lastNonNativeCallGas uint64 + baseFee *big.Int + ) + if rt.ctx.BaseFee != nil { + baseFee = new(big.Int).Set(rt.ctx.BaseFee) + } return vm.NewEVM(vm.Context{ CanTransfer: func(_ vm.StateDB, addr common.Address, amount *big.Int) bool { return stateDB.GetBalance(addr).Cmp(amount) >= 0 @@ -316,7 +322,7 @@ func (rt *Runtime) newEVM(stateDB *statedb.StateDB, clauseIndex uint32, txCtx *x BlockNumber: new(big.Int).SetUint64(uint64(rt.ctx.Number)), Time: new(big.Int).SetUint64(rt.ctx.Time), Difficulty: &big.Int{}, - BaseFee: &big.Int{}, + BaseFee: baseFee, }, stateDB, &rt.chainConfig, rt.vmConfig) } diff --git a/xenv/env.go b/xenv/env.go index d801dc38a..232b8a478 100644 --- a/xenv/env.go +++ b/xenv/env.go @@ -28,6 +28,7 @@ type BlockContext struct { Time uint64 GasLimit uint64 TotalScore uint64 + BaseFee *big.Int } // TransactionContext transaction context. From 5f626ca4cc8f5d5efb0606f897a3ea83c05adb2b Mon Sep 17 00:00:00 2001 From: paologalligit Date: Fri, 3 Jan 2025 10:53:03 +0100 Subject: [PATCH 18/32] clean --- tx/transaction.go | 1 - 1 file changed, 1 deletion(-) diff --git a/tx/transaction.go b/tx/transaction.go index cb95faaf0..36a368351 100644 --- a/tx/transaction.go +++ b/tx/transaction.go @@ -24,7 +24,6 @@ import ( var ( errIntrinsicGasOverflow = errors.New("intrinsic gas overflow") - ErrInvalidTxType = errors.New("transaction type not valid in this context") ErrTxTypeNotSupported = errors.New("transaction type not supported") errEmptyTypedTx = errors.New("empty typed transaction bytes") ) From a8825558fde117dadf080d7b899ff6953df0e184 Mon Sep 17 00:00:00 2001 From: paologalligit Date: Fri, 3 Jan 2025 12:25:54 +0100 Subject: [PATCH 19/32] test: add baseFee tests with consecutive empty and full blocks --- consensus/fork/galactica_test.go | 102 +++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/consensus/fork/galactica_test.go b/consensus/fork/galactica_test.go index cdfa2b73f..efd0d3bd8 100644 --- a/consensus/fork/galactica_test.go +++ b/consensus/fork/galactica_test.go @@ -74,6 +74,7 @@ func TestCalcBaseFee(t *testing.T) { {thor.InitialBaseFee, 20000000, 10000000, thor.InitialBaseFee}, // usage == target {thor.InitialBaseFee, 20000000, 9000000, 987500000}, // usage below target {thor.InitialBaseFee, 20000000, 11000000, 1012500000}, // usage above target + {thor.InitialBaseFee, 20000000, 0, 875000000}, // empty block } for i, test := range tests { var parentID thor.Bytes32 @@ -85,3 +86,104 @@ func TestCalcBaseFee(t *testing.T) { } } } + +func TestBaseFeeLimits(t *testing.T) { + targetPercentage := new(big.Float).SetFloat64(0.125) + targetPercentage.SetPrec(24) + oneFloat := new(big.Float).SetInt64(1) + + t.Run("EmptyBlocks", func(t *testing.T) { + // Post Galactica fork + var parentID thor.Bytes32 + binary.BigEndian.PutUint32(parentID[:], 5) + parentGasLimit := uint64(20000000) + parentGasUsed := uint64(0) + tagetDelta := new(big.Float).SetFloat64(0.875) + + tests := []struct { + name string + blockRange int + }{ + { + name: "short", + blockRange: 10, + }, + { + name: "medium", + blockRange: 50, + }, + { + name: "long", + blockRange: 100, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + parentBaseFee := big.NewInt(thor.InitialBaseFee) + + for i := 0; i < tt.blockRange; i++ { + parent := new(block.Builder).ParentID(parentID).GasLimit(parentGasLimit).GasUsed(parentGasUsed).BaseFee(parentBaseFee).Build().Header() + parentID = parent.ID() + baseFee := CalcBaseFee(config(), parent) + + currentFloat, previousFloat := new(big.Float).SetInt(baseFee), new(big.Float).SetInt(parentBaseFee) + delta := new(big.Float).SetPrec(7).Quo(currentFloat, previousFloat) + + percentage := new(big.Float).SetPrec(7).Sub(oneFloat, delta) + if delta.Cmp(tagetDelta) != 0 || percentage.Cmp(targetPercentage) != 0 { + t.Errorf("delta: %f, percentage: %f", delta, percentage) + } + parentBaseFee = baseFee + } + }) + } + }) + + t.Run("FullBlocks", func(t *testing.T) { + // Post Galactica fork + var parentID thor.Bytes32 + binary.BigEndian.PutUint32(parentID[:], 5) + parentGasLimit := uint64(20000000) + parentGasUsed := uint64(20000000) + tagetDelta := new(big.Float).SetFloat64(1.125) + + tests := []struct { + name string + blockRange int + }{ + { + name: "short", + blockRange: 10, + }, + { + name: "medium", + blockRange: 50, + }, + { + name: "long", + blockRange: 100, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + parentBaseFee := big.NewInt(thor.InitialBaseFee) + for i := 0; i < tt.blockRange; i++ { + parent := new(block.Builder).ParentID(parentID).GasLimit(parentGasLimit).GasUsed(parentGasUsed).BaseFee(parentBaseFee).Build().Header() + parentID = parent.ID() + baseFee := CalcBaseFee(config(), parent) + + currentFloat, previousFloat := new(big.Float).SetInt(baseFee), new(big.Float).SetInt(parentBaseFee) + delta := new(big.Float).SetPrec(7).Quo(currentFloat, previousFloat) + percentage := new(big.Float).SetPrec(7).Sub(delta, oneFloat) + + if delta.Cmp(tagetDelta) != 0 || percentage.Cmp(targetPercentage) != 0 { + t.Errorf("delta: %s, percentage: %s", delta, percentage) + } + parentBaseFee = baseFee + } + }) + } + }) +} From 0dd158555c93e3aee067eede35d4ffe3d106c04c Mon Sep 17 00:00:00 2001 From: paologalligit Date: Tue, 7 Jan 2025 10:55:59 +0100 Subject: [PATCH 20/32] fix: revert go.mod --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 70fb9ec61..74a3f265f 100644 --- a/go.mod +++ b/go.mod @@ -64,4 +64,4 @@ require ( replace github.com/syndtr/goleveldb => github.com/vechain/goleveldb v1.0.1-0.20220809091043-51eb019c8655 -replace github.com/ethereum/go-ethereum => /Users/paologalli/code/vechain/go-ethereum +replace github.com/ethereum/go-ethereum => github.com/vechain/go-ethereum v1.8.15-0.20241126085506-c74017ec91b2 \ No newline at end of file From 92e0cd3e20a9fe8caa6ce0c9e8ad9b62128ba659 Mon Sep 17 00:00:00 2001 From: paologalligit Date: Tue, 7 Jan 2025 11:08:07 +0100 Subject: [PATCH 21/32] fix: clean code --- tx/transaction_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/tx/transaction_test.go b/tx/transaction_test.go index 542be4598..5926abc1b 100644 --- a/tx/transaction_test.go +++ b/tx/transaction_test.go @@ -7,7 +7,6 @@ package tx_test import ( "encoding/hex" - "fmt" "math/big" "testing" @@ -232,7 +231,6 @@ func TestLegacyTx(t *testing.T) { sig, _ := crypto.Sign(trx.SigningHash().Bytes(), priv) trx = trx.WithSignature(sig) - fmt.Println(trx.ID()) assert.Equal(t, "0xd989829d88b0ed1b06edf5c50174ecfa64f14a64", func() string { s, _ := trx.Origin(); return s.String() }()) assert.Equal(t, "0xda90eaea52980bc4bb8d40cb2ff84d78433b3b4a6e7d50b75736c5e3e77b71ec", trx.ID().String()) From f7cf6dc02e40b8c9284b4ce141d46e3e35fc9908 Mon Sep 17 00:00:00 2001 From: paologalligit Date: Tue, 7 Jan 2025 16:46:58 +0100 Subject: [PATCH 22/32] feat: include baseFee when Galactica fork enebled + tests --- api/blocks/types.go | 2 ++ consensus/fork/galactica.go | 2 +- packer/flow.go | 5 ++++ packer/flow_test.go | 46 +++++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/api/blocks/types.go b/api/blocks/types.go index 4a34d01da..09291d331 100644 --- a/api/blocks/types.go +++ b/api/blocks/types.go @@ -33,6 +33,7 @@ type JSONBlockSummary struct { Signer thor.Address `json:"signer"` IsTrunk bool `json:"isTrunk"` IsFinalized bool `json:"isFinalized"` + BaseFee *big.Int `json:"baseFee,omitempty"` } type JSONRawBlockSummary struct { @@ -120,6 +121,7 @@ func buildJSONBlockSummary(summary *chain.BlockSummary, isTrunk bool, isFinalize COM: header.COM(), IsTrunk: isTrunk, IsFinalized: isFinalized, + BaseFee: summary.Header.BaseFee(), } } diff --git a/consensus/fork/galactica.go b/consensus/fork/galactica.go index 718242d71..749cec616 100644 --- a/consensus/fork/galactica.go +++ b/consensus/fork/galactica.go @@ -43,7 +43,7 @@ func VerifyGalacticaHeader(config *thor.ForkConfig, parent, header *block.Header // CalcBaseFee calculates the basefee of the header. func CalcBaseFee(config *thor.ForkConfig, parent *block.Header) *big.Int { // If the current block is the first Galactica block, return the InitialBaseFee. - if parent.Number() == config.GALACTICA { + if parent.Number()+1 == config.GALACTICA { return new(big.Int).SetUint64(thor.InitialBaseFee) } diff --git a/packer/flow.go b/packer/flow.go index 47bd97ae7..db42fa2d7 100644 --- a/packer/flow.go +++ b/packer/flow.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/pkg/errors" "github.com/vechain/thor/v2/block" + "github.com/vechain/thor/v2/consensus/fork" "github.com/vechain/thor/v2/runtime" "github.com/vechain/thor/v2/state" "github.com/vechain/thor/v2/thor" @@ -181,6 +182,10 @@ func (f *Flow) Pack(privateKey *ecdsa.PrivateKey, newBlockConflicts uint32, shou builder.COM() } + if f.Number() >= f.packer.forkConfig.GALACTICA { + builder.BaseFee(fork.CalcBaseFee(&f.packer.forkConfig, f.parentHeader)) + } + if f.Number() < f.packer.forkConfig.VIP214 { newBlock := builder.Build() diff --git a/packer/flow_test.go b/packer/flow_test.go index 2584e22e5..bf09db0d3 100644 --- a/packer/flow_test.go +++ b/packer/flow_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/assert" "github.com/vechain/thor/v2/builtin" "github.com/vechain/thor/v2/chain" "github.com/vechain/thor/v2/genesis" @@ -177,6 +178,51 @@ func TestPack(t *testing.T) { } } +func TestPackAfterGalacticaFork(t *testing.T) { + db := muxdb.NewMem() + g := genesis.NewDevnet() + + stater := state.NewStater(db) + parent, _, _, _ := g.Build(stater) + + repo, _ := chain.NewRepository(db, parent) + + forkConfig := thor.NoFork + forkConfig.BLOCKLIST = 0 + forkConfig.VIP214 = 0 + forkConfig.FINALITY = 0 + forkConfig.GALACTICA = 2 + + proposer := genesis.DevAccounts()[0] + p := packer.New(repo, stater, proposer.Address, &proposer.Address, forkConfig) + parentSum, _ := repo.GetBlockSummary(parent.Header().ID()) + flow, _ := p.Schedule(parentSum, parent.Header().Timestamp()+100*thor.BlockInterval) + + // Block 1: Galactica is not enabled + block, stg, receipts, err := flow.Pack(proposer.PrivateKey, 0, false) + assert.Nil(t, err) + assert.Equal(t, uint32(1), block.Header().Number()) + assert.Nil(t, block.Header().BaseFee()) + + if _, err := stg.Commit(); err != nil { + t.Fatal("Error committing state:", err) + } + if err := repo.AddBlock(block, receipts, 0); err != nil { + t.Fatal("Error adding block:", err) + } + if err := repo.SetBestBlockID(block.Header().ID()); err != nil { + t.Fatal("Error setting best block ID:", err) + } + + // Block 2: Galactica is enabled + parentSum, _ = repo.GetBlockSummary(block.Header().ID()) + flow, _ = p.Schedule(parentSum, block.Header().Timestamp()+100*thor.BlockInterval) + block, _, _, err = flow.Pack(proposer.PrivateKey, 0, false) + assert.Nil(t, err) + assert.Equal(t, uint32(2), block.Header().Number()) + assert.Equal(t, big.NewInt(thor.InitialBaseFee), block.Header().BaseFee()) +} + func TestAdoptErr(t *testing.T) { db := muxdb.NewMem() stater := state.NewStater(db) From 458dab649a500563e62a43443b7f178e4b2ffef9 Mon Sep 17 00:00:00 2001 From: paologalligit Date: Wed, 8 Jan 2025 10:05:16 +0100 Subject: [PATCH 23/32] style: fix lowercase --- vm/chain_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/chain_config.go b/vm/chain_config.go index edc968777..669db03ef 100644 --- a/vm/chain_config.go +++ b/vm/chain_config.go @@ -23,7 +23,7 @@ func isForked(s, head *big.Int) bool { type ChainConfig struct { params.ChainConfig IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul) - GalacticaBlock *big.Int `json:"GalacticaBlock,omitempty"` // Galactica switch block (nil = no fork, 0 = already on galactica) + GalacticaBlock *big.Int `json:"galacticaBlock,omitempty"` // Galactica switch block (nil = no fork, 0 = already on galactica) } // IsIstanbul returns whether num is either equal to the Istanbul fork block or greater. From 0f6c4f7310ed2b3afd19a7fd03e414d790f31930 Mon Sep 17 00:00:00 2001 From: paologalligit Date: Fri, 10 Jan 2025 09:46:46 +0100 Subject: [PATCH 24/32] test: fix test --- block/builder_test.go | 2 +- cmd/thor/solo/solo_test.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/block/builder_test.go b/block/builder_test.go index b6ea94625..6aeaf97dd 100644 --- a/block/builder_test.go +++ b/block/builder_test.go @@ -80,7 +80,7 @@ func TestBuilder_ReceiptsRoot(t *testing.T) { func TestBuilder_Transaction(t *testing.T) { builder := &Builder{} - tx := new(tx.LegacyBuilder).Build() + tx, _ := tx.NewTxBuilder(tx.LegacyTxType).Build() builder.Transaction(tx) b := builder.Build() assert.Contains(t, b.Transactions(), tx) diff --git a/cmd/thor/solo/solo_test.go b/cmd/thor/solo/solo_test.go index a4df3f35d..b8d401ee8 100644 --- a/cmd/thor/solo/solo_test.go +++ b/cmd/thor/solo/solo_test.go @@ -7,6 +7,7 @@ package solo import ( "context" + "math" "testing" "time" @@ -30,7 +31,7 @@ func newSolo() *Solo { repo, _ := chain.NewRepository(db, b) mempool := txpool.New(repo, stater, txpool.Options{Limit: 10000, LimitPerAccount: 16, MaxLifetime: 10 * time.Minute}) - return New(repo, stater, logDb, mempool, 0, true, false, thor.BlockInterval, thor.ForkConfig{}) + return New(repo, stater, logDb, mempool, 0, true, false, thor.BlockInterval, thor.ForkConfig{GALACTICA: math.MaxUint32}) } func TestInitSolo(t *testing.T) { From 0baeeb0bc5cb75f3b54e323cf3df095e85332f3a Mon Sep 17 00:00:00 2001 From: paologalligit Date: Tue, 14 Jan 2025 16:36:21 +0100 Subject: [PATCH 25/32] test: add validate block tests --- consensus/consensus_test.go | 2 +- consensus/validator.go | 20 ++++---- consensus/validator_test.go | 92 +++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 10 deletions(-) create mode 100644 consensus/validator_test.go diff --git a/consensus/consensus_test.go b/consensus/consensus_test.go index 1223c27ac..bf1f9fd44 100644 --- a/consensus/consensus_test.go +++ b/consensus/consensus_test.go @@ -578,7 +578,7 @@ func TestVerifyBlock(t *testing.T) { } } -func TestValidateBlockBody(t *testing.T) { +func TestConsent(t *testing.T) { tc, err := newTestConsensus() if err != nil { t.Fatal(err) diff --git a/consensus/validator.go b/consensus/validator.go index 70c4ef761..062dd1700 100644 --- a/consensus/validator.go +++ b/consensus/validator.go @@ -237,8 +237,8 @@ func (c *Consensus) validateBlockBody(blk *block.Block) error { return consensusError(fmt.Sprintf("block txs root mismatch: want %v, have %v", header.TxsRoot(), txs.RootHash())) } - for _, tx := range txs { - origin, err := tx.Origin() + for _, tr := range txs { + origin, err := tr.Origin() if err != nil { return consensusError(fmt.Sprintf("tx signer unavailable: %v", err)) } @@ -248,15 +248,17 @@ func (c *Consensus) validateBlockBody(blk *block.Block) error { } switch { - case tx.ChainTag() != c.repo.ChainTag(): - return consensusError(fmt.Sprintf("tx chain tag mismatch: want %v, have %v", c.repo.ChainTag(), tx.ChainTag())) - case header.Number() < tx.BlockRef().Number(): - return consensusError(fmt.Sprintf("tx ref future block: ref %v, current %v", tx.BlockRef().Number(), header.Number())) - case tx.IsExpired(header.Number()): - return consensusError(fmt.Sprintf("tx expired: ref %v, current %v, expiration %v", tx.BlockRef().Number(), header.Number(), tx.Expiration())) + case tr.ChainTag() != c.repo.ChainTag(): + return consensusError(fmt.Sprintf("tx chain tag mismatch: want %v, have %v", c.repo.ChainTag(), tr.ChainTag())) + case header.Number() < tr.BlockRef().Number(): + return consensusError(fmt.Sprintf("tx ref future block: ref %v, current %v", tr.BlockRef().Number(), header.Number())) + case tr.IsExpired(header.Number()): + return consensusError(fmt.Sprintf("tx expired: ref %v, current %v, expiration %v", tr.BlockRef().Number(), header.Number(), tr.Expiration())) + case header.Number() < c.forkConfig.GALACTICA && tr.Type() != tx.LegacyTxType: + return consensusError(tx.ErrTxTypeNotSupported.Error()) } - if err := tx.TestFeatures(header.TxsFeatures()); err != nil { + if err := tr.TestFeatures(header.TxsFeatures()); err != nil { return consensusError("invalid tx: " + err.Error()) } } diff --git a/consensus/validator_test.go b/consensus/validator_test.go new file mode 100644 index 000000000..1d743eab8 --- /dev/null +++ b/consensus/validator_test.go @@ -0,0 +1,92 @@ +// Copyright (c) 2024 The VeChainThor developers + +// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying +// file LICENSE or + +package consensus + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/vechain/thor/v2/block" + "github.com/vechain/thor/v2/chain" + "github.com/vechain/thor/v2/genesis" + "github.com/vechain/thor/v2/muxdb" + "github.com/vechain/thor/v2/state" + "github.com/vechain/thor/v2/thor" + "github.com/vechain/thor/v2/tx" +) + +func TestValidateBlockBody(t *testing.T) { + db := muxdb.NewMem() + gen := genesis.NewDevnet() + stater := state.NewStater(db) + + parent, _, _, err := gen.Build(stater) + assert.NoError(t, err) + + repo, err := chain.NewRepository(db, parent) + assert.NoError(t, err) + + tests := []struct { + name string + getBlock func() *block.Block + forkConfig *thor.ForkConfig + expectedError error + }{ + { + name: "supported legacy tx type before galactica fork", + getBlock: func() *block.Block { + tr, err := tx.NewTxBuilder(tx.LegacyTxType).ChainTag(repo.ChainTag()).Expiration(10).Build() + assert.NoError(t, err) + tr = tx.MustSign(tr, genesis.DevAccounts()[0].PrivateKey) + return new(block.Builder).Transaction(tr).Build() + }, + forkConfig: &thor.ForkConfig{GALACTICA: 10}, + expectedError: nil, + }, + { + name: "unsupported tx type before galactica fork", + getBlock: func() *block.Block { + tr, err := tx.NewTxBuilder(tx.DynamicFeeTxType).ChainTag(repo.ChainTag()).Expiration(10).Build() + assert.NoError(t, err) + tr = tx.MustSign(tr, genesis.DevAccounts()[0].PrivateKey) + return new(block.Builder).Transaction(tr).Build() + }, + forkConfig: &thor.ForkConfig{GALACTICA: 10}, + expectedError: consensusError(tx.ErrTxTypeNotSupported.Error()), + }, + { + name: "supported legacy tx type after galactica fork", + getBlock: func() *block.Block { + tr, err := tx.NewTxBuilder(tx.LegacyTxType).ChainTag(repo.ChainTag()).Expiration(10).Build() + assert.NoError(t, err) + tr = tx.MustSign(tr, genesis.DevAccounts()[0].PrivateKey) + return new(block.Builder).Transaction(tr).Build() + }, + forkConfig: &thor.ForkConfig{GALACTICA: 0}, + expectedError: nil, + }, + { + name: "supported tx type after galactica fork", + getBlock: func() *block.Block { + tr, err := tx.NewTxBuilder(tx.DynamicFeeTxType).ChainTag(repo.ChainTag()).Expiration(10).Build() + assert.NoError(t, err) + tr = tx.MustSign(tr, genesis.DevAccounts()[0].PrivateKey) + return new(block.Builder).Transaction(tr).Build() + }, + forkConfig: &thor.ForkConfig{GALACTICA: 0}, + expectedError: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := New(repo, stater, *tt.forkConfig) + + err := c.validateBlockBody(tt.getBlock()) + assert.Equal(t, tt.expectedError, err) + }) + } +} From 12d81aefa9cd4306e48a1d74c4b188623e105ca8 Mon Sep 17 00:00:00 2001 From: paologalligit Date: Tue, 14 Jan 2025 17:20:31 +0100 Subject: [PATCH 26/32] fix: go.mod file --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 74a3f265f..9e8869618 100644 --- a/go.mod +++ b/go.mod @@ -64,4 +64,4 @@ require ( replace github.com/syndtr/goleveldb => github.com/vechain/goleveldb v1.0.1-0.20220809091043-51eb019c8655 -replace github.com/ethereum/go-ethereum => github.com/vechain/go-ethereum v1.8.15-0.20241126085506-c74017ec91b2 \ No newline at end of file +replace github.com/ethereum/go-ethereum => github.com/vechain/go-ethereum v1.8.15-0.20241126085506-c74017ec91b2 From 57860c90af2fb758cb5b70802d7cff17e94117d8 Mon Sep 17 00:00:00 2001 From: paologalligit Date: Wed, 15 Jan 2025 14:10:11 +0100 Subject: [PATCH 27/32] fix: base fee tests --- runtime/runtime.go | 8 ++++---- runtime/runtime_test.go | 13 ++++++------- vm/chain_config.go | 4 ++-- vm/contracts.go | 14 +++++++------- vm/evm.go | 6 +++--- vm/interpreter.go | 2 +- 6 files changed, 23 insertions(+), 24 deletions(-) diff --git a/runtime/runtime.go b/runtime/runtime.go index 22accbde1..4229145c3 100644 --- a/runtime/runtime.go +++ b/runtime/runtime.go @@ -59,8 +59,8 @@ var baseChainConfig = vm.ChainConfig{ Ethash: nil, Clique: nil, }, - IstanbulBlock: nil, - ShanghaiBlock: nil, + IstanbulBlock: nil, + GalacticaBlock: nil, } // Output output of clause execution. @@ -99,7 +99,7 @@ func New( currentChainConfig := baseChainConfig currentChainConfig.ConstantinopleBlock = big.NewInt(int64(forkConfig.ETH_CONST)) currentChainConfig.IstanbulBlock = big.NewInt(int64(forkConfig.ETH_IST)) - currentChainConfig.ShanghaiBlock = big.NewInt(int64(forkConfig.GALACTICA)) + currentChainConfig.GalacticaBlock = big.NewInt(int64(forkConfig.GALACTICA)) if chain != nil { // use genesis id as chain id currentChainConfig.ChainID = new(big.Int).SetBytes(chain.GenesisID().Bytes()) @@ -107,7 +107,7 @@ func New( // alloc precompiled contracts if forkConfig.GALACTICA == ctx.Number { - for addr := range vm.PrecompiledContractsShanghai { + for addr := range vm.PrecompiledContractsGalactica { if err := state.SetCode(thor.Address(addr), EmptyRuntimeBytecode); err != nil { panic(err) } diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index 36ad9f97c..817d775f5 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -312,13 +312,12 @@ func TestEVMFunction(t *testing.T) { abi: "", methodName: "", testFunc: func(ctx *context, t *testing.T) { - exec, _ := runtime.New(ctx.chain, ctx.state, &xenv.BlockContext{}, thor.ForkConfig{}). + exec, _ := runtime.New(ctx.chain, ctx.state, &xenv.BlockContext{BaseFee: common.Big0}, thor.ForkConfig{GALACTICA: 0}). PrepareClause(tx.NewClause(&target), 0, math.MaxUint64, &xenv.TransactionContext{}) out, _, err := exec() assert.Nil(t, err) assert.Nil(t, out.VMErr) - assert.True(t, new(big.Int).SetBytes(out.Data).Cmp(big.NewInt(0)) == 0) assert.Equal(t, uint64(2), math.MaxUint64-out.LeftOverGas) }, }, @@ -328,13 +327,13 @@ func TestEVMFunction(t *testing.T) { abi: "", methodName: "", testFunc: func(ctx *context, t *testing.T) { - exec, _ := runtime.New(ctx.chain, ctx.state, &xenv.BlockContext{}, thor.ForkConfig{}). + expectedBaseFee := big.NewInt(100_000) + exec, _ := runtime.New(ctx.chain, ctx.state, &xenv.BlockContext{BaseFee: expectedBaseFee}, thor.ForkConfig{GALACTICA: 0}). PrepareClause(tx.NewClause(&target), 0, math.MaxUint64, &xenv.TransactionContext{}) out, _, err := exec() assert.Nil(t, err) assert.Nil(t, out.VMErr) - - assert.True(t, new(big.Int).SetBytes(out.Data).Cmp(big.NewInt(0)) == 0) + assert.True(t, new(big.Int).SetBytes(out.Data).Cmp(expectedBaseFee) == 0) }, }, { @@ -589,7 +588,7 @@ func TestPreForkOpCode(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - exec, _ := runtime.New(chain, state, &xenv.BlockContext{}, thor.NoFork). + exec, _ := runtime.New(chain, state, &xenv.BlockContext{BaseFee: common.Big0}, thor.NoFork). PrepareClause(tx.NewClause(nil).WithData(tt.code), 0, math.MaxUint64, &xenv.TransactionContext{}) out, _, err := exec() assert.Nil(t, err) @@ -597,7 +596,7 @@ func TestPreForkOpCode(t *testing.T) { assert.Equal(t, fmt.Sprintf("invalid opcode 0x%x", int(tt.op)), out.VMErr.Error()) // this one applies a fork config that forks from the start - exec, _ = runtime.New(chain, state, &xenv.BlockContext{}, thor.ForkConfig{}). + exec, _ = runtime.New(chain, state, &xenv.BlockContext{BaseFee: common.Big0}, thor.ForkConfig{}). PrepareClause(tx.NewClause(nil).WithData(tt.code), 0, math.MaxUint64, &xenv.TransactionContext{}) out, _, err = exec() assert.Nil(t, err) diff --git a/vm/chain_config.go b/vm/chain_config.go index 669db03ef..50f0a86ae 100644 --- a/vm/chain_config.go +++ b/vm/chain_config.go @@ -46,7 +46,7 @@ type Rules struct { IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsByzantium bool IsIstanbul bool - IsShanghai bool + IsGalactica bool } // Rules ensures c's ChainID is not nil. @@ -63,6 +63,6 @@ func (c *ChainConfig) Rules(num *big.Int) Rules { IsEIP158: c.IsEIP158(num), IsByzantium: c.IsByzantium(num), IsIstanbul: c.IsIstanbul((num)), - IsShanghai: c.IsShanghai((num)), + IsGalactica: c.IsGalactica((num)), } } diff --git a/vm/contracts.go b/vm/contracts.go index 46c3a6381..ad211f070 100644 --- a/vm/contracts.go +++ b/vm/contracts.go @@ -77,11 +77,11 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{9}): &blake2F{}, } -// PrecompiledContractsShanghai contains the default set of pre-compiled Ethereum +// PrecompiledContractsGalactica contains the default set of pre-compiled Ethereum // contracts used in the Shanghai release. // NOTE: Shanghai release does not introduce any changes in precompiled contracts. // We are catching up from Istanbul, so Shanghai in thor includes eip1108 and eip2565. -var PrecompiledContractsShanghai = map[common.Address]PrecompiledContract{ +var PrecompiledContractsGalactica = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{1}): &safeEcrecover{}, common.BytesToAddress([]byte{2}): &sha256hash{}, common.BytesToAddress([]byte{3}): &ripemd160hash{}, @@ -94,7 +94,7 @@ var PrecompiledContractsShanghai = map[common.Address]PrecompiledContract{ } var ( - PrecompiledAddressesShanghai []common.Address + PrecompiledAddressesGalactica []common.Address PrecompiledAddressesIstanbul []common.Address PrecompiledAddressesByzantium []common.Address PrecompiledAddressesHomestead []common.Address @@ -110,16 +110,16 @@ func init() { for k := range PrecompiledContractsIstanbul { PrecompiledAddressesIstanbul = append(PrecompiledAddressesIstanbul, k) } - for k := range PrecompiledContractsShanghai { - PrecompiledAddressesShanghai = append(PrecompiledAddressesShanghai, k) + for k := range PrecompiledContractsGalactica { + PrecompiledAddressesGalactica = append(PrecompiledAddressesGalactica, k) } } // ActivePrecompiles returns the precompiles enabled with the current configuration. func ActivePrecompiles(rules Rules) []common.Address { switch { - case rules.IsShanghai: - return PrecompiledAddressesShanghai + case rules.IsGalactica: + return PrecompiledAddressesGalactica case rules.IsIstanbul: return PrecompiledAddressesIstanbul case rules.IsByzantium: diff --git a/vm/evm.go b/vm/evm.go index fcba8ed32..79fbebcdf 100644 --- a/vm/evm.go +++ b/vm/evm.go @@ -52,8 +52,8 @@ type ( func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { var precompiles map[common.Address]PrecompiledContract switch { - case evm.chainRules.IsShanghai: - precompiles = PrecompiledContractsShanghai + case evm.chainRules.IsGalactica: + precompiles = PrecompiledContractsGalactica case evm.chainRules.IsIstanbul: precompiles = PrecompiledContractsIstanbul case evm.chainRules.IsByzantium: @@ -492,7 +492,7 @@ func (evm *EVM) create(caller ContractRef, code []byte, gas uint64, value *big.I } // Reject code starting with 0xEF if EIP-3541 is enabled. - if err == nil && len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsShanghai { + if err == nil && len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsGalactica { err = ErrInvalidCode } diff --git a/vm/interpreter.go b/vm/interpreter.go index f4ff5cf7e..25c827b9a 100644 --- a/vm/interpreter.go +++ b/vm/interpreter.go @@ -59,7 +59,7 @@ func NewInterpreter(evm *EVM, cfg Config) *Interpreter { // we'll set the default jump table. if cfg.JumpTable == nil { switch { - case evm.ChainConfig().IsShanghai(evm.BlockNumber): + case evm.ChainConfig().IsGalactica(evm.BlockNumber): cfg.JumpTable = shanghaiInstructionSet case evm.ChainConfig().IsIstanbul(evm.BlockNumber): cfg.JumpTable = istanbulInstructionSet From 422defb6de3196a000dcb33b5c8d65e18f681081 Mon Sep 17 00:00:00 2001 From: paologalligit Date: Fri, 24 Jan 2025 14:47:56 +0100 Subject: [PATCH 28/32] tests: add more dyn fee tx tests --- .../transactions_benchmark_test.go | 120 ++++++++++++------ runtime/runtime_test.go | 10 +- 2 files changed, 87 insertions(+), 43 deletions(-) diff --git a/api/transactions/transactions_benchmark_test.go b/api/transactions/transactions_benchmark_test.go index ec366de31..30ed1bcfe 100644 --- a/api/transactions/transactions_benchmark_test.go +++ b/api/transactions/transactions_benchmark_test.go @@ -38,9 +38,13 @@ import ( ) var ( - cachedAccounts []genesis.DevAccount - once sync.Once - blockCount = 1_000 + cachedAccounts []genesis.DevAccount + once sync.Once + blockCount = 1_000 + createManyTxFuncs = []func(signerPK *ecdsa.PrivateKey, thorChain *testchain.Chain) (tx.Transactions, error){ + createManyClausesPerTxLegacy, + createManyClausesPerTxDynFee, + } ) func getCachedAccounts(b *testing.B) []genesis.DevAccount { @@ -56,28 +60,29 @@ func BenchmarkFetchTx_RealDB_RandomSigners_ManyClausesPerTx(b *testing.B) { // create state accounts accounts := getCachedAccounts(b) - // randomly pick a signer for signing the transactions - randomSignerFunc := randomPickSignerFunc(accounts, createManyClausesPerTx) + for _, createManyClausesPerTx := range createManyTxFuncs { // randomly pick a signer for signing the transactions + randomSignerFunc := randomPickSignerFunc(accounts, createManyClausesPerTx) - // create test db - will be automagically removed when the benchmark ends - db, err := openTempMainDB(b.TempDir()) - require.NoError(b, err) + // create test db - will be automagically removed when the benchmark ends + db, err := openTempMainDB(b.TempDir()) + require.NoError(b, err) - // create blocks - newChain, transactions := createPackedChain(b, db, blockCount, accounts, randomSignerFunc) + // create blocks + newChain, transactions := createPackedChain(b, db, blockCount, accounts, randomSignerFunc) - // shuffle the transaction into a randomized order - randomizedTransactions := shuffleSlice(transactions) - b.Logf("About to process %d txs", len(randomizedTransactions)) + // shuffle the transaction into a randomized order + randomizedTransactions := shuffleSlice(transactions) + b.Logf("About to process %d txs", len(randomizedTransactions)) - // run the benchmarks - b.Run("getTransaction", func(b *testing.B) { - benchmarkGetTransaction(b, newChain, randomizedTransactions) - }) + // run the benchmarks + b.Run("getTransaction", func(b *testing.B) { + benchmarkGetTransaction(b, newChain, randomizedTransactions) + }) - b.Run("getReceipt", func(b *testing.B) { - benchmarkGetReceipt(b, newChain, randomizedTransactions) - }) + b.Run("getReceipt", func(b *testing.B) { + benchmarkGetReceipt(b, newChain, randomizedTransactions) + }) + } } func BenchmarkFetchTx_RealDB_RandomSigners_OneClausePerTx(b *testing.B) { @@ -109,27 +114,27 @@ func BenchmarkFetchTx_RealDB_RandomSigners_OneClausePerTx(b *testing.B) { } func BenchmarkFetchTx_RandomSigners_ManyClausesPerTx(b *testing.B) { - // create state accounts - accounts := getCachedAccounts(b) + for _, createManyClausesPerTx := range createManyTxFuncs { // create state accounts + accounts := getCachedAccounts(b) + // randomly pick a signer for signing the transactions + randomSignerFunc := randomPickSignerFunc(accounts, createManyClausesPerTx) - // randomly pick a signer for signing the transactions - randomSignerFunc := randomPickSignerFunc(accounts, createManyClausesPerTx) + // create blocks + newChain, transactions := createPackedChain(b, muxdb.NewMem(), blockCount, accounts, randomSignerFunc) - // create blocks - newChain, transactions := createPackedChain(b, muxdb.NewMem(), blockCount, accounts, randomSignerFunc) + // shuffle the transaction into a randomized order + randomizedTransactions := shuffleSlice(transactions) + b.Logf("About to process %d txs", len(randomizedTransactions)) - // shuffle the transaction into a randomized order - randomizedTransactions := shuffleSlice(transactions) - b.Logf("About to process %d txs", len(randomizedTransactions)) - - // run the benchmarks - b.Run("getTransaction", func(b *testing.B) { - benchmarkGetTransaction(b, newChain, randomizedTransactions) - }) + // run the benchmarks + b.Run("getTransaction", func(b *testing.B) { + benchmarkGetTransaction(b, newChain, randomizedTransactions) + }) - b.Run("getReceipt", func(b *testing.B) { - benchmarkGetReceipt(b, newChain, randomizedTransactions) - }) + b.Run("getReceipt", func(b *testing.B) { + benchmarkGetReceipt(b, newChain, randomizedTransactions) + }) + } } func BenchmarkFetchTx_RandomSigners_OneClausePerTx(b *testing.B) { @@ -233,9 +238,15 @@ func createOneClausePerTx(signerPK *ecdsa.PrivateKey, thorChain *testchain.Chain for gasUsed < 9_500_000 { toAddr := datagen.RandAddress() cla := tx.NewClause(&toAddr).WithValue(big.NewInt(10000)) - transaction := tx.NewTxBuilder(tx.LegacyTxType). + b := tx.NewTxBuilder(tx.LegacyTxType) + if gasUsed%2 == 0 { + b = tx.NewTxBuilder(tx.DynamicFeeTxType) + } + transaction := b. ChainTag(thorChain.Repo().ChainTag()). GasPriceCoef(1). + MaxFeePerGas(big.NewInt(1000000)). + MaxPriorityFeePerGas(big.NewInt(100)). Expiration(math.MaxUint32 - 1). Gas(21_000). Nonce(uint64(datagen.RandInt())). @@ -255,7 +266,7 @@ func createOneClausePerTx(signerPK *ecdsa.PrivateKey, thorChain *testchain.Chain return transactions, nil } -func createManyClausesPerTx(signerPK *ecdsa.PrivateKey, thorChain *testchain.Chain) (tx.Transactions, error) { +func createManyClausesPerTxLegacy(signerPK *ecdsa.PrivateKey, thorChain *testchain.Chain) (tx.Transactions, error) { var transactions tx.Transactions gasUsed := uint64(0) txGas := uint64(42_000) @@ -285,6 +296,37 @@ func createManyClausesPerTx(signerPK *ecdsa.PrivateKey, thorChain *testchain.Cha return transactions, nil } +func createManyClausesPerTxDynFee(signerPK *ecdsa.PrivateKey, thorChain *testchain.Chain) (tx.Transactions, error) { + var transactions tx.Transactions + gasUsed := uint64(0) + txGas := uint64(42_000) + + transactionBuilder := tx.NewTxBuilder(tx.DynamicFeeTxType). + ChainTag(thorChain.Repo().ChainTag()). + MaxFeePerGas(big.NewInt(1000000)). + MaxPriorityFeePerGas(big.NewInt(100)). + Expiration(math.MaxUint32 - 1). + Nonce(uint64(datagen.RandInt())). + BlockRef(tx.NewBlockRef(0)) + + for ; gasUsed < 9_500_000; gasUsed += txGas { + toAddr := datagen.RandAddress() + transactionBuilder.Clause(tx.NewClause(&toAddr).WithValue(big.NewInt(10000))) + } + + transaction := transactionBuilder.Gas(gasUsed).MustBuild() + + sig, err := crypto.Sign(transaction.SigningHash().Bytes(), signerPK) + if err != nil { + return nil, err + } + transaction = transaction.WithSignature(sig) + + transactions = append(transactions, transaction) + + return transactions, nil +} + func packTxsIntoBlock(thorChain *testchain.Chain, proposerAccount *genesis.DevAccount, parentBlk *block.Block, transactions tx.Transactions) (*block.Block, error) { p := packer.New(thorChain.Repo(), thorChain.Stater(), proposerAccount.Address, &proposerAccount.Address, thorChain.GetForkConfig()) diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index 817d775f5..d609e5c71 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -763,11 +763,13 @@ func TestExecuteTransactionFailure(t *testing.T) { originEnergy.SetString("9000000000000000000000000000000000000", 10) state.SetEnergy(origin.Address, originEnergy, 0) - tx := GetMockFailedTx(tx.LegacyTxType) + for _, txType := range []int{tx.LegacyTxType, tx.DynamicFeeTxType} { + tx := GetMockFailedTx(txType) - rt := runtime.New(repo.NewChain(b0.Header().ID()), state, &xenv.BlockContext{}, thor.NoFork) + rt := runtime.New(repo.NewChain(b0.Header().ID()), state, &xenv.BlockContext{}, thor.NoFork) - _, err = rt.ExecuteTransaction(tx) + _, err := rt.ExecuteTransaction(tx) - assert.NotNil(t, err) + assert.NotNil(t, err) + } } From 6790e11b85b5ee215285a930e11c679c88156d3d Mon Sep 17 00:00:00 2001 From: paologalligit Date: Fri, 24 Jan 2025 16:02:48 +0100 Subject: [PATCH 29/32] fix: rebase conflicts --- chain/repository_test.go | 20 ----- tx/builder_dynamic_fee.go | 159 -------------------------------------- 2 files changed, 179 deletions(-) delete mode 100644 tx/builder_dynamic_fee.go diff --git a/chain/repository_test.go b/chain/repository_test.go index 88014d93f..22c6f03f3 100644 --- a/chain/repository_test.go +++ b/chain/repository_test.go @@ -71,15 +71,7 @@ func TestRepository(t *testing.T) { assert.Equal(t, b0summary, repo1.BestBlockSummary()) assert.Equal(t, repo1.GenesisBlock().Header().ID()[31], repo1.ChainTag()) -<<<<<<< HEAD -<<<<<<< HEAD tx1 := tx.NewTxBuilder(tx.LegacyTxType).MustBuild() -======= - tx1, _ := tx.NewTxBuilder(tx.LegacyTxType).Build() ->>>>>>> 8a375f1e (refactor: merge tx builder into one builder) -======= - tx1 := tx.NewTxBuilder(tx.LegacyTxType).MustBuild() ->>>>>>> 316917ad (refactor: add MustBuild to tx builder and fix error) receipt1 := &tx.Receipt{} b1 := newBlock(repo1.GenesisBlock(), 10, tx1) @@ -106,19 +98,7 @@ func TestRepository(t *testing.T) { assert.Equal(t, tx.Receipts{receipt1}.RootHash(), gotReceipts.RootHash()) } -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD - tx2 := tx.NewTxBuilder(tx.DynamicFeeTxType).MustBuild() -======= - tx2 := new(tx.DynFeeBuilder).Build() ->>>>>>> 8422b551 (test: add more dynFee txs to tests) -======= - tx2, _ := tx.NewTxBuilder(tx.DynamicFeeTxType).Build() ->>>>>>> 8a375f1e (refactor: merge tx builder into one builder) -======= tx2 := tx.NewTxBuilder(tx.DynamicFeeTxType).MustBuild() ->>>>>>> 316917ad (refactor: add MustBuild to tx builder and fix error) receipt2 := &tx.Receipt{} b2 := newBlock(b1, 20, tx2) diff --git a/tx/builder_dynamic_fee.go b/tx/builder_dynamic_fee.go deleted file mode 100644 index 44ae9680b..000000000 --- a/tx/builder_dynamic_fee.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) 2024 The VeChainThor developers - -// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying -// file LICENSE or - -package tx - -import ( - "encoding/binary" - "math/big" - - "github.com/vechain/thor/v2/thor" -) - -// Builder to make it easy to build transaction. -type Builder struct { - txType int - chainTag byte - clauses []*Clause - gasPriceCoef uint8 - maxFeePerGas *big.Int - maxPriorityFeePerGas *big.Int - gas uint64 - blockRef uint64 - expiration uint32 - nonce uint64 - dependsOn *thor.Bytes32 - reserved reserved -} - -func NewTxBuilder(txType int) *Builder { - return &Builder{txType: txType} -} - -// ChainTag set chain tag. -func (b *Builder) ChainTag(tag byte) *Builder { - b.chainTag = tag - return b -} - -// Clause add a clause. -func (b *Builder) Clause(c *Clause) *Builder { - b.clauses = append(b.clauses, c) - return b -} - -func (b *Builder) Clauses(clauses []*Clause) *Builder { - for _, c := range clauses { - b.Clause(c) - } - return b -} - -// GasPriceCoef set gas price coef. -func (b *Builder) GasPriceCoef(coef uint8) *Builder { - b.gasPriceCoef = coef - return b -} - -// MaxFeePerGas set max fee per gas. -func (b *Builder) MaxFeePerGas(maxFeePerGas *big.Int) *Builder { - b.maxFeePerGas = maxFeePerGas - return b -} - -// MaxPriorityFeePerGas set max priority fee per gas. -func (b *Builder) MaxPriorityFeePerGas(maxPriorityFeePerGas *big.Int) *Builder { - b.maxPriorityFeePerGas = maxPriorityFeePerGas - return b -} - -// Gas set gas provision for tx. -func (b *Builder) Gas(gas uint64) *Builder { - b.gas = gas - return b -} - -// BlockRef set block reference. -func (b *Builder) BlockRef(br BlockRef) *Builder { - b.blockRef = binary.BigEndian.Uint64(br[:]) - return b -} - -// Expiration set expiration. -func (b *Builder) Expiration(exp uint32) *Builder { - b.expiration = exp - return b -} - -// Nonce set nonce. -func (b *Builder) Nonce(nonce uint64) *Builder { - b.nonce = nonce - return b -} - -// DependsOn set depended tx. -func (b *DynFeeBuilder) DependsOn(txID *thor.Bytes32) *DynFeeBuilder { - if txID == nil { - b.dependsOn = nil - } else { - cpy := *txID - b.dependsOn = &cpy - } - return b -} - -// Features set features. -func (b *Builder) Features(feat Features) *Builder { - b.reserved.Features = feat - return b -} - -// Build builds a tx object. -func (b *Builder) Build() (*Transaction, error) { - var tx *Transaction - switch b.txType { - case LegacyTxType: - tx = &Transaction{ - body: &LegacyTransaction{ - ChainTag: b.chainTag, - Clauses: b.clauses, - GasPriceCoef: b.gasPriceCoef, - Gas: b.gas, - BlockRef: b.blockRef, - Expiration: b.expiration, - Nonce: b.nonce, - DependsOn: b.dependsOn, - Reserved: b.reserved, - }, - } - case DynamicFeeTxType: - tx = &Transaction{ - body: &DynamicFeeTransaction{ - ChainTag: b.chainTag, - Clauses: b.clauses, - MaxFeePerGas: b.maxFeePerGas, - MaxPriorityFeePerGas: b.maxPriorityFeePerGas, - Gas: b.gas, - BlockRef: b.blockRef, - Expiration: b.expiration, - Nonce: b.nonce, - DependsOn: b.dependsOn, - Reserved: b.reserved, - }, - } - default: - return nil, ErrTxTypeNotSupported - } - return tx, nil -} - -// MustBuild builds a tx object, it panics if an error is returned. -func (b *Builder) MustBuild() *Transaction { - tx, err := b.Build() - if err != nil { - panic(err) - } - return tx -} From 47454ff4a3839d8d159e2eea887235348cdcf2fa Mon Sep 17 00:00:00 2001 From: paologalligit Date: Fri, 24 Jan 2025 16:33:49 +0100 Subject: [PATCH 30/32] fix: remove check on gas limit block header before galactica --- consensus/fork/galactica.go | 3 --- consensus/fork/galactica_test.go | 26 +++++++++++++------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/consensus/fork/galactica.go b/consensus/fork/galactica.go index 749cec616..29176e25f 100644 --- a/consensus/fork/galactica.go +++ b/consensus/fork/galactica.go @@ -21,9 +21,6 @@ import ( func VerifyGalacticaHeader(config *thor.ForkConfig, parent, header *block.Header) error { // Verify that the gas limit remains within allowed bounds parentGasLimit := parent.GasLimit() - if parent.Number() < config.GALACTICA { - parentGasLimit = parent.GasLimit() * thor.ElasticityMultiplier - } if err := block.GasLimit(header.GasLimit()).IsValid(parentGasLimit); !err { return fmt.Errorf("invalid gas limit: have %d, want %d", header.GasLimit(), parentGasLimit) } diff --git a/consensus/fork/galactica_test.go b/consensus/fork/galactica_test.go index efd0d3bd8..316660257 100644 --- a/consensus/fork/galactica_test.go +++ b/consensus/fork/galactica_test.go @@ -32,24 +32,24 @@ func TestBlockGasLimits(t *testing.T) { ok bool }{ // Transitions from non-Galactica to Galactica - {10000000, 4, 20000000, true}, // No change - {10000000, 4, 20019531, true}, // Upper limit - {10000000, 4, 20019532, false}, // Upper +1 - {10000000, 4, 19980469, true}, // Lower limit - {10000000, 4, 19980468, false}, // Lower limit -1 - // Galactica to Galactica - {20000000, 5, 20000000, true}, + {20000000, 5, 20000000, true}, // No change {20000000, 5, 20019531, true}, // Upper limit - {20000000, 5, 20019532, false}, // Upper limit +1 + {20000000, 5, 20019532, false}, // Upper +1 {20000000, 5, 19980469, true}, // Lower limit {20000000, 5, 19980468, false}, // Lower limit -1 - {40000000, 5, 40039062, true}, // Upper limit - {40000000, 5, 40039063, false}, // Upper limit +1 - {40000000, 5, 39960938, true}, // lower limit - {40000000, 5, 39960937, false}, // Lower limit -1 + // Galactica to Galactica + {20000000, 6, 20000000, true}, + {20000000, 6, 20019531, true}, // Upper limit + {20000000, 6, 20019532, false}, // Upper limit +1 + {20000000, 6, 19980469, true}, // Lower limit + {20000000, 6, 19980468, false}, // Lower limit -1 + {40000000, 6, 40039062, true}, // Upper limit + {40000000, 6, 40039063, false}, // Upper limit +1 + {40000000, 6, 39960938, true}, // lower limit + {40000000, 6, 39960937, false}, // Lower limit -1 } { var parentID thor.Bytes32 - binary.BigEndian.PutUint32(parentID[:], tc.pNum-1) + binary.BigEndian.PutUint32(parentID[:], tc.pNum-2) parent := new(block.Builder).ParentID(parentID).GasUsed(tc.pGasLimit / 2).GasLimit(tc.pGasLimit).BaseFee(initial).Build().Header() header := new(block.Builder).ParentID(parent.ID()).GasUsed(tc.gasLimit / 2).GasLimit(tc.gasLimit).BaseFee(initial).Build().Header() From 4617f3b109a555af2a85307ddefe5cb27b7b653f Mon Sep 17 00:00:00 2001 From: paologalligit Date: Wed, 29 Jan 2025 10:22:18 +0100 Subject: [PATCH 31/32] fixes as per review --- block/builder_test.go | 242 +++++++++++++++++++++--------------- consensus/fork/galactica.go | 14 ++- 2 files changed, 150 insertions(+), 106 deletions(-) diff --git a/block/builder_test.go b/block/builder_test.go index 6aeaf97dd..fe077dc0d 100644 --- a/block/builder_test.go +++ b/block/builder_test.go @@ -14,105 +14,145 @@ import ( "github.com/vechain/thor/v2/tx" ) -func TestBuilder_ParentID(t *testing.T) { - builder := &Builder{} - id := thor.Bytes32{1, 2, 3} - builder.ParentID(id) - b := builder.Build() - assert.Equal(t, id, b.header.ParentID()) -} - -func TestBuilder_Timestamp(t *testing.T) { - builder := &Builder{} - ts := uint64(1234567890) - builder.Timestamp(ts) - b := builder.Build() - assert.Equal(t, ts, b.header.Timestamp()) -} - -func TestBuilder_TotalScore(t *testing.T) { - builder := &Builder{} - score := uint64(100) - builder.TotalScore(score) - b := builder.Build() - assert.Equal(t, score, b.header.TotalScore()) -} - -func TestBuilder_GasLimit(t *testing.T) { - builder := &Builder{} - limit := uint64(5000) - builder.GasLimit(limit) - b := builder.Build() - assert.Equal(t, limit, b.header.GasLimit()) -} - -func TestBuilder_GasUsed(t *testing.T) { - builder := &Builder{} - used := uint64(3000) - builder.GasUsed(used) - b := builder.Build() - assert.Equal(t, used, b.header.GasUsed()) -} - -func TestBuilder_Beneficiary(t *testing.T) { - builder := &Builder{} - addr := thor.Address{1, 2, 3} - builder.Beneficiary(addr) - b := builder.Build() - assert.Equal(t, addr, b.header.Beneficiary()) -} - -func TestBuilder_StateRoot(t *testing.T) { - builder := &Builder{} - hash := thor.Bytes32{1, 2, 3} - builder.StateRoot(hash) - b := builder.Build() - assert.Equal(t, hash, b.header.StateRoot()) -} - -func TestBuilder_ReceiptsRoot(t *testing.T) { - builder := &Builder{} - hash := thor.Bytes32{1, 2, 3} - builder.ReceiptsRoot(hash) - b := builder.Build() - assert.Equal(t, hash, b.header.ReceiptsRoot()) -} - -func TestBuilder_Transaction(t *testing.T) { - builder := &Builder{} - tx, _ := tx.NewTxBuilder(tx.LegacyTxType).Build() - builder.Transaction(tx) - b := builder.Build() - assert.Contains(t, b.Transactions(), tx) -} - -func TestBuilder_TransactionFeatures(t *testing.T) { - builder := &Builder{} - features := tx.Features(0x01) - builder.TransactionFeatures(features) - b := builder.Build() - assert.Equal(t, features, b.header.TxsFeatures()) -} - -func TestBuilder_Alpha(t *testing.T) { - builder := &Builder{} - alpha := []byte{1, 2, 3} - builder.Alpha(alpha) - b := builder.Build() - assert.Equal(t, alpha, b.header.Alpha()) -} - -func TestBuilder_COM(t *testing.T) { - builder := &Builder{} - builder.COM() - b := builder.Build() - assert.True(t, b.header.COM()) -} - -func TestBuilder_BaseFee(t *testing.T) { - builder := &Builder{} - baseFee := big.NewInt(1000) - builder.BaseFee(baseFee) - b := builder.Build() - assert.Equal(t, baseFee, b.header.BaseFee()) +func TestBuilder(t *testing.T) { + tests := []struct { + name string + fn func(*testing.T) + }{ + { + name: "ParentID", + fn: func(t *testing.T) { + builder := &Builder{} + id := thor.Bytes32{1, 2, 3} + builder.ParentID(id) + b := builder.Build() + assert.Equal(t, id, b.header.ParentID()) + }, + }, + { + name: "Timestamp", + fn: func(t *testing.T) { + builder := &Builder{} + ts := uint64(1234567890) + builder.Timestamp(ts) + b := builder.Build() + assert.Equal(t, ts, b.header.Timestamp()) + }, + }, + { + name: "TotalScore", + fn: func(t *testing.T) { + builder := &Builder{} + score := uint64(100) + builder.TotalScore(score) + b := builder.Build() + assert.Equal(t, score, b.header.TotalScore()) + }, + }, + { + name: "GasLimit", + fn: func(t *testing.T) { + builder := &Builder{} + limit := uint64(5000) + builder.GasLimit(limit) + b := builder.Build() + assert.Equal(t, limit, b.header.GasLimit()) + }, + }, + { + name: "GasUsed", + fn: func(t *testing.T) { + builder := &Builder{} + used := uint64(3000) + builder.GasUsed(used) + b := builder.Build() + assert.Equal(t, used, b.header.GasUsed()) + }, + }, + { + name: "Beneficiary", + fn: func(t *testing.T) { + builder := &Builder{} + addr := thor.Address{1, 2, 3} + builder.Beneficiary(addr) + b := builder.Build() + assert.Equal(t, addr, b.header.Beneficiary()) + }, + }, + { + name: "StateRoot", + fn: func(t *testing.T) { + builder := &Builder{} + hash := thor.Bytes32{1, 2, 3} + builder.StateRoot(hash) + b := builder.Build() + assert.Equal(t, hash, b.header.StateRoot()) + }, + }, + { + name: "ReceiptsRoot", + fn: func(t *testing.T) { + builder := &Builder{} + hash := thor.Bytes32{1, 2, 3} + builder.ReceiptsRoot(hash) + b := builder.Build() + assert.Equal(t, hash, b.header.ReceiptsRoot()) + }, + }, + { + name: "Transaction", + fn: func(t *testing.T) { + builder := &Builder{} + tx, _ := tx.NewTxBuilder(tx.LegacyTxType).Build() + builder.Transaction(tx) + b := builder.Build() + assert.Contains(t, b.Transactions(), tx) + }, + }, + { + name: "TransactionFeatures", + fn: func(t *testing.T) { + builder := &Builder{} + features := tx.Features(0x01) + builder.TransactionFeatures(features) + b := builder.Build() + assert.Equal(t, features, b.header.TxsFeatures()) + }, + }, + { + name: "Alpha", + fn: func(t *testing.T) { + builder := &Builder{} + alpha := []byte{1, 2, 3} + builder.Alpha(alpha) + b := builder.Build() + assert.Equal(t, alpha, b.header.Alpha()) + }, + }, + { + name: "COM", + fn: func(t *testing.T) { + builder := &Builder{} + builder.COM() + b := builder.Build() + assert.True(t, b.header.COM()) + }, + }, + { + name: "BaseFee", + fn: func(t *testing.T) { + builder := &Builder{} + baseFee := big.NewInt(1000) + builder.BaseFee(baseFee) + b := builder.Build() + assert.Equal(t, baseFee, b.header.BaseFee()) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.fn(t) + }) + } } diff --git a/consensus/fork/galactica.go b/consensus/fork/galactica.go index 29176e25f..577fb80db 100644 --- a/consensus/fork/galactica.go +++ b/consensus/fork/galactica.go @@ -19,15 +19,17 @@ import ( // - gas limit check // - basefee check func VerifyGalacticaHeader(config *thor.ForkConfig, parent, header *block.Header) error { + // Verify the header is not malformed + if header.BaseFee() == nil { + return fmt.Errorf("header is missing baseFee") + } + // Verify that the gas limit remains within allowed bounds parentGasLimit := parent.GasLimit() if err := block.GasLimit(header.GasLimit()).IsValid(parentGasLimit); !err { return fmt.Errorf("invalid gas limit: have %d, want %d", header.GasLimit(), parentGasLimit) } - // Verify the header is not malformed - if header.BaseFee() == nil { - return fmt.Errorf("header is missing baseFee") - } + // Verify the baseFee is correct based on the parent header. expectedBaseFee := CalcBaseFee(config, parent) if header.BaseFee().Cmp(expectedBaseFee) != 0 { @@ -58,6 +60,7 @@ func CalcBaseFee(config *thor.ForkConfig, parent *block.Header) *big.Int { } if parentGasUsed > parentGasTarget { // If the parent block used more gas than its target, the baseFee should increase. + // newBaseFee := parentBaseFee + max(1, parentBaseFee * (parentGasUsed - parentGasTarget) / parentGasTarget / baseFeeChangeDenominator) gasUsedDelta := new(big.Int).SetUint64(parentGasUsed - parentGasTarget) x := new(big.Int).Mul(parentBaseFee, gasUsedDelta) y := x.Div(x, parentGasTargetBig) @@ -68,7 +71,8 @@ func CalcBaseFee(config *thor.ForkConfig, parent *block.Header) *big.Int { return x.Add(parentBaseFee, baseFeeDelta) } else { - // Otherwise if the parent block used less gas than its target, the baseFee should decrease. + // Otherwise if the parent block used less or equal gas than its target, the baseFee should decrease. + // newBaseFee := max(0, parentBaseFee - parentBaseFee * (parentGasTarget - parentGasUsed) / parentGasTarget / baseFeeChangeDenominator) gasUsedDelta := new(big.Int).SetUint64(parentGasTarget - parentGasUsed) x := new(big.Int).Mul(parentBaseFee, gasUsedDelta) y := x.Div(x, parentGasTargetBig) From 83d5be908cc88b63afa5e254bb2a1ed03d734c81 Mon Sep 17 00:00:00 2001 From: paologalligit Date: Wed, 29 Jan 2025 17:40:26 +0100 Subject: [PATCH 32/32] test: refactor block builder test --- block/builder_test.go | 195 ++++++++++++------------------------------ 1 file changed, 54 insertions(+), 141 deletions(-) diff --git a/block/builder_test.go b/block/builder_test.go index fe077dc0d..c27e22a72 100644 --- a/block/builder_test.go +++ b/block/builder_test.go @@ -14,145 +14,58 @@ import ( "github.com/vechain/thor/v2/tx" ) -func TestBuilder(t *testing.T) { - tests := []struct { - name string - fn func(*testing.T) - }{ - { - name: "ParentID", - fn: func(t *testing.T) { - builder := &Builder{} - id := thor.Bytes32{1, 2, 3} - builder.ParentID(id) - b := builder.Build() - assert.Equal(t, id, b.header.ParentID()) - }, - }, - { - name: "Timestamp", - fn: func(t *testing.T) { - builder := &Builder{} - ts := uint64(1234567890) - builder.Timestamp(ts) - b := builder.Build() - assert.Equal(t, ts, b.header.Timestamp()) - }, - }, - { - name: "TotalScore", - fn: func(t *testing.T) { - builder := &Builder{} - score := uint64(100) - builder.TotalScore(score) - b := builder.Build() - assert.Equal(t, score, b.header.TotalScore()) - }, - }, - { - name: "GasLimit", - fn: func(t *testing.T) { - builder := &Builder{} - limit := uint64(5000) - builder.GasLimit(limit) - b := builder.Build() - assert.Equal(t, limit, b.header.GasLimit()) - }, - }, - { - name: "GasUsed", - fn: func(t *testing.T) { - builder := &Builder{} - used := uint64(3000) - builder.GasUsed(used) - b := builder.Build() - assert.Equal(t, used, b.header.GasUsed()) - }, - }, - { - name: "Beneficiary", - fn: func(t *testing.T) { - builder := &Builder{} - addr := thor.Address{1, 2, 3} - builder.Beneficiary(addr) - b := builder.Build() - assert.Equal(t, addr, b.header.Beneficiary()) - }, - }, - { - name: "StateRoot", - fn: func(t *testing.T) { - builder := &Builder{} - hash := thor.Bytes32{1, 2, 3} - builder.StateRoot(hash) - b := builder.Build() - assert.Equal(t, hash, b.header.StateRoot()) - }, - }, - { - name: "ReceiptsRoot", - fn: func(t *testing.T) { - builder := &Builder{} - hash := thor.Bytes32{1, 2, 3} - builder.ReceiptsRoot(hash) - b := builder.Build() - assert.Equal(t, hash, b.header.ReceiptsRoot()) - }, - }, - { - name: "Transaction", - fn: func(t *testing.T) { - builder := &Builder{} - tx, _ := tx.NewTxBuilder(tx.LegacyTxType).Build() - builder.Transaction(tx) - b := builder.Build() - assert.Contains(t, b.Transactions(), tx) - }, - }, - { - name: "TransactionFeatures", - fn: func(t *testing.T) { - builder := &Builder{} - features := tx.Features(0x01) - builder.TransactionFeatures(features) - b := builder.Build() - assert.Equal(t, features, b.header.TxsFeatures()) - }, - }, - { - name: "Alpha", - fn: func(t *testing.T) { - builder := &Builder{} - alpha := []byte{1, 2, 3} - builder.Alpha(alpha) - b := builder.Build() - assert.Equal(t, alpha, b.header.Alpha()) - }, - }, - { - name: "COM", - fn: func(t *testing.T) { - builder := &Builder{} - builder.COM() - b := builder.Build() - assert.True(t, b.header.COM()) - }, - }, - { - name: "BaseFee", - fn: func(t *testing.T) { - builder := &Builder{} - baseFee := big.NewInt(1000) - builder.BaseFee(baseFee) - b := builder.Build() - assert.Equal(t, baseFee, b.header.BaseFee()) - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.fn(t) - }) - } +func TestBuilder_Build(t *testing.T) { + builder := &Builder{} + + id := thor.Bytes32{1, 2, 3} + builder.ParentID(id) + + ts := uint64(1234567890) + builder.Timestamp(ts) + + score := uint64(100) + builder.TotalScore(score) + + limit := uint64(5000) + builder.GasLimit(limit) + + used := uint64(3000) + builder.GasUsed(used) + + addr := thor.Address{1, 2, 3} + builder.Beneficiary(addr) + + hash := thor.Bytes32{1, 2, 3} + builder.StateRoot(hash) + + builder.ReceiptsRoot(hash) + tr := tx.NewTxBuilder(tx.LegacyTxType).MustBuild() + + builder.Transaction(tr) + features := tx.Features(0x01) + + builder.TransactionFeatures(features) + alpha := []byte{1, 2, 3} + + builder.Alpha(alpha) + builder.COM() + + baseFee := big.NewInt(1000) + builder.BaseFee(baseFee) + + b := builder.Build() + + assert.Equal(t, id, b.header.ParentID()) + assert.Equal(t, ts, b.header.Timestamp()) + assert.Equal(t, score, b.header.TotalScore()) + assert.Equal(t, limit, b.header.GasLimit()) + assert.Equal(t, used, b.header.GasUsed()) + assert.Equal(t, addr, b.header.Beneficiary()) + assert.Equal(t, hash, b.header.StateRoot()) + assert.Equal(t, hash, b.header.ReceiptsRoot()) + assert.Contains(t, b.Transactions(), tr) + assert.Equal(t, features, b.header.TxsFeatures()) + assert.Equal(t, alpha, b.header.Alpha()) + assert.True(t, b.header.COM()) + assert.Equal(t, baseFee, b.header.BaseFee()) }