Merge pull request #636 from jpmorganchase/txn-size-limit-genesis

Adding support for transaction size limit via genesis
This commit is contained in:
Samer Falah 2019-03-07 11:28:37 -05:00 committed by GitHub
commit af38cd133d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 66 additions and 12 deletions

View File

@ -164,6 +164,18 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig
} else {
log.Info("Writing custom genesis block")
}
// Set default transaction size limit if not set in genesis
if genesis.Config.TransactionSizeLimit == 0 {
genesis.Config.TransactionSizeLimit = DefaultTxPoolConfig.TransactionSizeLimit
}
// Check transaction size limit
err := genesis.Config.IsValid()
if err != nil {
return genesis.Config, common.Hash{}, err
}
block, err := genesis.Commit(db)
return genesis.Config, block.Hash(), err
}

View File

@ -20,6 +20,7 @@ import (
"math/big"
"reflect"
"testing"
"errors"
"github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/common"
@ -85,6 +86,15 @@ func TestSetupGenesis(t *testing.T) {
wantHash: params.MainnetGenesisHash,
wantConfig: params.MainnetChainConfig,
},
{
name: "genesis with incorrect SizeLimit",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
customg.Config.TransactionSizeLimit = 100000
return SetupGenesisBlock(db, &customg)
},
wantErr: errors.New("Genesis transaction size limit must be between 32 and 128"),
wantConfig: customg.Config,
},
// {
// name: "custom block in DB, genesis == nil",
// fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {

View File

@ -133,6 +133,8 @@ type TxPoolConfig struct {
Journal string // Journal of local transactions to survive node restarts
Rejournal time.Duration // Time interval to regenerate the local transaction journal
TransactionSizeLimit uint64 // Maximum size allowed for valid transaction (in KB)
PriceLimit uint64 // Minimum gas price to enforce for acceptance into the pool
PriceBump uint64 // Minimum price bump percentage to replace an already existing transaction (nonce)
@ -150,6 +152,8 @@ var DefaultTxPoolConfig = TxPoolConfig{
Journal: "transactions.rlp",
Rejournal: time.Hour,
TransactionSizeLimit: 64,
PriceLimit: 1,
PriceBump: 10,
@ -560,13 +564,13 @@ func (pool *TxPool) local() map[common.Address]types.Transactions {
// rules and adheres to some heuristic limits of the local node (price and size).
func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
isQuorum := pool.chainconfig.IsQuorum
sizeLimit := pool.chainconfig.TransactionSizeLimit
if isQuorum && tx.GasPrice().Cmp(common.Big0) != 0 {
return ErrInvalidGasPrice
}
// Heuristic limit, reject transactions over 32KB to prevent DOS attacks
// UPDATED to 64KB to support the deployment of bigger contract due to the pressing need for sophisticated/complex contract in financial/capital markets - Nathan Aw
if tx.Size() > 64*1024 {
// Reject transactions over 32KB (or manually set limit) to prevent DOS attacks
if float64(tx.Size()) > float64(sizeLimit * 1024) {
return ErrOversizedData
}
// Transactions can't be negative. This may never happen using RLP decoded

View File

@ -284,14 +284,32 @@ func TestInvalidTransactions(t *testing.T) {
t.Error("expected", ErrOversizedData, "; got", err)
}
tx3, _ := types.SignTx(types.NewTransaction(1, common.Address{}, big.NewInt(100), 0, big.NewInt(0), nil), types.HomesteadSigner{}, key)
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
blockchain := &testBlockChain{statedb, statedb, 1000000, new(event.Feed)}
params.TestChainConfig.TransactionSizeLimit = 128
pool2 := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
balance = new(big.Int).Add(tx3.Value(), new(big.Int).Mul(new(big.Int).SetUint64(tx3.Gas()), tx3.GasPrice()))
pool2.currentState.AddBalance(from, big.NewInt(0xffffffffffffff))
data2 := make([]byte, (127 * 1024))
from, _ = deriveSender(tx3)
tx3, _ := types.SignTx(types.NewTransaction(2, common.Address{}, big.NewInt(100), 100000, big.NewInt(1), data2), types.HomesteadSigner{}, key)
if err := pool2.AddRemote(tx3); err != ErrIntrinsicGas {
t.Error("expected", ErrIntrinsicGas, "; got", err)
}
data3 := make([]byte, (128*1024)+1)
tx4, _ := types.SignTx(types.NewTransaction(2, common.Address{}, big.NewInt(100), 100000, big.NewInt(1), data3), types.HomesteadSigner{}, key)
if err := pool2.AddRemote(tx4); err != ErrOversizedData {
t.Error("expected", ErrOversizedData, "; got", err)
}
tx5, _ := types.SignTx(types.NewTransaction(1, common.Address{}, big.NewInt(100), 0, big.NewInt(0), nil), types.HomesteadSigner{}, key)
balance = new(big.Int).Add(tx5.Value(), new(big.Int).Mul(new(big.Int).SetUint64(tx5.Gas()), tx5.GasPrice()))
from, _ = deriveSender(tx5)
pool.currentState.AddBalance(from, balance)
tx3.SetPrivate()
if err := pool.AddRemote(tx3); err != ErrEtherValueUnsupported {
tx5.SetPrivate()
if err := pool.AddRemote(tx5); err != ErrEtherValueUnsupported {
t.Error("expected", ErrEtherValueUnsupported, "; got", err)
}
}

View File

@ -20,6 +20,7 @@ import (
"fmt"
"math"
"math/big"
"errors"
"github.com/ethereum/go-ethereum/common"
)
@ -103,19 +104,19 @@ var (
//
// This configuration is intentionally not using keyed fields to force anyone
// adding flags to the config to also have to set these fields.
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), new(EthashConfig), nil, nil, false}
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), new(EthashConfig), nil, nil, false, 32}
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
// and accepted by the Ethereum core developers into the Clique consensus.
//
// This configuration is intentionally not using keyed fields to force anyone
// adding flags to the config to also have to set these fields.
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil, false}
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil, false, 32}
TestChainConfig = &ChainConfig{big.NewInt(10), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), new(EthashConfig), nil, nil, false}
TestChainConfig = &ChainConfig{big.NewInt(10), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), new(EthashConfig), nil, nil, false, 32}
TestRules = TestChainConfig.Rules(new(big.Int))
QuorumTestChainConfig = &ChainConfig{big.NewInt(10), big.NewInt(0), nil, false, nil, common.Hash{}, nil, nil, nil, nil, new(EthashConfig), nil, nil, true}
QuorumTestChainConfig = &ChainConfig{big.NewInt(10), big.NewInt(0), nil, false, nil, common.Hash{}, nil, nil, nil, nil, new(EthashConfig), nil, nil, true, 64}
)
// ChainConfig is the core config which determines the blockchain settings.
@ -147,6 +148,7 @@ type ChainConfig struct {
Istanbul *IstanbulConfig `json:"istanbul,omitempty"`
IsQuorum bool `json:"isQuorum"`
TransactionSizeLimit uint64 `json:"txnSizeLimit"`
}
// EthashConfig is the consensus engine configs for proof-of-work based sealing.
@ -207,6 +209,14 @@ func (c *ChainConfig) String() string {
)
}
func (c *ChainConfig) IsValid() error {
if c.TransactionSizeLimit < 32 || c.TransactionSizeLimit > 128 {
return errors.New("Genesis transaction size limit must be between 32 and 128")
}
return nil
}
// IsHomestead returns whether num is either equal to the homestead block or greater.
func (c *ChainConfig) IsHomestead(num *big.Int) bool {
return isForked(c.HomesteadBlock, num)