config: add MaxBlockSize setting
This commit is contained in:
parent
38103dcc7a
commit
8f06bf21d7
6 changed files with 121 additions and 0 deletions
|
@ -18,6 +18,8 @@ type (
|
||||||
KeepOnlyLatestState bool `yaml:"KeepOnlyLatestState"`
|
KeepOnlyLatestState bool `yaml:"KeepOnlyLatestState"`
|
||||||
// RemoveUntraceableBlocks specifies if old blocks should be removed.
|
// RemoveUntraceableBlocks specifies if old blocks should be removed.
|
||||||
RemoveUntraceableBlocks bool `yaml:"RemoveUntraceableBlocks"`
|
RemoveUntraceableBlocks bool `yaml:"RemoveUntraceableBlocks"`
|
||||||
|
// MaxBlockSize is the maximum block size in bytes.
|
||||||
|
MaxBlockSize uint32 `yaml:"MaxBlockSize"`
|
||||||
// MaxTraceableBlocks is the length of the chain accessible to smart contracts.
|
// MaxTraceableBlocks is the length of the chain accessible to smart contracts.
|
||||||
MaxTraceableBlocks uint32 `yaml:"MaxTraceableBlocks"`
|
MaxTraceableBlocks uint32 `yaml:"MaxTraceableBlocks"`
|
||||||
// MaxTransactionsPerBlock is the maximum amount of transactions per block.
|
// MaxTransactionsPerBlock is the maximum amount of transactions per block.
|
||||||
|
|
|
@ -437,6 +437,14 @@ func (s *service) verifyBlock(b block.Block) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size := coreb.GetExpectedBlockSize()
|
||||||
|
if size > int(s.ProtocolConfiguration.MaxBlockSize) {
|
||||||
|
s.log.Warn("proposed block size exceeds config MaxBlockSize",
|
||||||
|
zap.Uint32("max size allowed", s.ProtocolConfiguration.MaxBlockSize),
|
||||||
|
zap.Int("block size", size))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
var pool = mempool.New(len(coreb.Transactions), 0, false)
|
var pool = mempool.New(len(coreb.Transactions), 0, false)
|
||||||
var mainPool = s.Chain.GetMemPool()
|
var mainPool = s.Chain.GetMemPool()
|
||||||
for _, tx := range coreb.Transactions {
|
for _, tx := range coreb.Transactions {
|
||||||
|
|
|
@ -409,6 +409,16 @@ func TestVerifyBlock(t *testing.T) {
|
||||||
b.Index = srv.Chain.BlockHeight()
|
b.Index = srv.Chain.BlockHeight()
|
||||||
require.False(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
require.False(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
||||||
})
|
})
|
||||||
|
t.Run("bad big size", func(t *testing.T) {
|
||||||
|
script := make([]byte, int(srv.ProtocolConfiguration.MaxBlockSize))
|
||||||
|
script[0] = byte(opcode.RET)
|
||||||
|
tx := transaction.New(netmode.UnitTestNet, script, 100000)
|
||||||
|
tx.ValidUntilBlock = 1
|
||||||
|
addSender(t, tx)
|
||||||
|
signTx(t, srv.Chain, tx)
|
||||||
|
b := testchain.NewBlock(t, srv.Chain, 1, 0, tx)
|
||||||
|
require.False(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
||||||
|
})
|
||||||
t.Run("bad timestamp", func(t *testing.T) {
|
t.Run("bad timestamp", func(t *testing.T) {
|
||||||
b := testchain.NewBlock(t, srv.Chain, 1, 0)
|
b := testchain.NewBlock(t, srv.Chain, 1, 0)
|
||||||
b.Timestamp = srv.lastTimestamp - 1
|
b.Timestamp = srv.lastTimestamp - 1
|
||||||
|
|
|
@ -21,6 +21,12 @@ const (
|
||||||
// ErrMaxContentsPerBlock is returned when the maximum number of contents per block is reached.
|
// ErrMaxContentsPerBlock is returned when the maximum number of contents per block is reached.
|
||||||
var ErrMaxContentsPerBlock = errors.New("the number of contents exceeds the maximum number of contents per block")
|
var ErrMaxContentsPerBlock = errors.New("the number of contents exceeds the maximum number of contents per block")
|
||||||
|
|
||||||
|
var expectedHeaderSizeWithEmptyWitness int
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
expectedHeaderSizeWithEmptyWitness = io.GetVarSize(new(Header))
|
||||||
|
}
|
||||||
|
|
||||||
// Block represents one block in the chain.
|
// Block represents one block in the chain.
|
||||||
type Block struct {
|
type Block struct {
|
||||||
// The base of the block.
|
// The base of the block.
|
||||||
|
@ -211,3 +217,23 @@ func (b *Block) UnmarshalJSON(data []byte) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExpectedBlockSize returns expected block size which should be equal to io.GetVarSize(b)
|
||||||
|
func (b *Block) GetExpectedBlockSize() int {
|
||||||
|
var transactionsSize int
|
||||||
|
for _, tx := range b.Transactions {
|
||||||
|
transactionsSize += tx.Size()
|
||||||
|
}
|
||||||
|
return b.GetExpectedBlockSizeWithoutTransactions(len(b.Transactions)) + transactionsSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExpectedBlockSizeWithoutTransactions returns expected block size without transactions size.
|
||||||
|
func (b *Block) GetExpectedBlockSizeWithoutTransactions(txCount int) int {
|
||||||
|
size := expectedHeaderSizeWithEmptyWitness - 1 - 1 + // 1 is for the zero-length (new(Header)).Script.Invocation/Verification
|
||||||
|
io.GetVarSize(&b.Script) +
|
||||||
|
io.GetVarSize(txCount)
|
||||||
|
if b.StateRootEnabled {
|
||||||
|
size += util.Uint256Size
|
||||||
|
}
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -234,3 +235,46 @@ func TestBlockEncodeDecode(t *testing.T) {
|
||||||
require.True(t, errors.Is(testserdes.DecodeBinary(data, new(Block)), ErrMaxContentsPerBlock))
|
require.True(t, errors.Is(testserdes.DecodeBinary(data, new(Block)), ErrMaxContentsPerBlock))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetExpectedBlockSize(t *testing.T) {
|
||||||
|
check := func(t *testing.T, stateRootEnabled bool) {
|
||||||
|
|
||||||
|
t.Run("without transactions", func(t *testing.T) {
|
||||||
|
b := newDumbBlock()
|
||||||
|
b.StateRootEnabled = stateRootEnabled
|
||||||
|
b.Transactions = []*transaction.Transaction{}
|
||||||
|
require.Equal(t, io.GetVarSize(b), b.GetExpectedBlockSize())
|
||||||
|
require.Equal(t, io.GetVarSize(b), b.GetExpectedBlockSizeWithoutTransactions(0))
|
||||||
|
})
|
||||||
|
t.Run("with one transaction", func(t *testing.T) {
|
||||||
|
b := newDumbBlock()
|
||||||
|
b.StateRootEnabled = stateRootEnabled
|
||||||
|
expected := io.GetVarSize(b)
|
||||||
|
require.Equal(t, expected, b.GetExpectedBlockSize())
|
||||||
|
require.Equal(t, expected-b.Transactions[0].Size(), b.GetExpectedBlockSizeWithoutTransactions(len(b.Transactions)))
|
||||||
|
})
|
||||||
|
t.Run("with multiple transactions", func(t *testing.T) {
|
||||||
|
b := newDumbBlock()
|
||||||
|
b.StateRootEnabled = stateRootEnabled
|
||||||
|
b.Transactions = make([]*transaction.Transaction, 123)
|
||||||
|
for i := range b.Transactions {
|
||||||
|
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.RET)}, int64(i))
|
||||||
|
tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
|
||||||
|
tx.Scripts = []transaction.Witness{{}}
|
||||||
|
b.Transactions[i] = tx
|
||||||
|
}
|
||||||
|
expected := io.GetVarSize(b)
|
||||||
|
require.Equal(t, expected, b.GetExpectedBlockSize())
|
||||||
|
for _, tx := range b.Transactions {
|
||||||
|
expected -= tx.Size()
|
||||||
|
}
|
||||||
|
require.Equal(t, expected, b.GetExpectedBlockSizeWithoutTransactions(len(b.Transactions)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
t.Run("StateRoot enabled", func(t *testing.T) {
|
||||||
|
check(t, true)
|
||||||
|
})
|
||||||
|
t.Run("StateRoot disabled", func(t *testing.T) {
|
||||||
|
check(t, false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ const (
|
||||||
|
|
||||||
defaultMemPoolSize = 50000
|
defaultMemPoolSize = 50000
|
||||||
defaultP2PNotaryRequestPayloadPoolSize = 1000
|
defaultP2PNotaryRequestPayloadPoolSize = 1000
|
||||||
|
defaultMaxBlockSize = 262144
|
||||||
defaultMaxTraceableBlocks = 2102400 // 1 year of 15s blocks
|
defaultMaxTraceableBlocks = 2102400 // 1 year of 15s blocks
|
||||||
defaultMaxTransactionsPerBlock = 512
|
defaultMaxTransactionsPerBlock = 512
|
||||||
verificationGasLimit = 100000000 // 1 GAS
|
verificationGasLimit = 100000000 // 1 GAS
|
||||||
|
@ -134,6 +135,10 @@ type Blockchain struct {
|
||||||
|
|
||||||
extensible atomic.Value
|
extensible atomic.Value
|
||||||
|
|
||||||
|
// defaultBlockWitness stores transaction.Witness with m out of n multisig,
|
||||||
|
// where n = ValidatorsCount.
|
||||||
|
defaultBlockWitness atomic.Value
|
||||||
|
|
||||||
stateRoot *stateroot.Module
|
stateRoot *stateroot.Module
|
||||||
|
|
||||||
// Notification subsystem.
|
// Notification subsystem.
|
||||||
|
@ -167,6 +172,10 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L
|
||||||
cfg.P2PNotaryRequestPayloadPoolSize = defaultP2PNotaryRequestPayloadPoolSize
|
cfg.P2PNotaryRequestPayloadPoolSize = defaultP2PNotaryRequestPayloadPoolSize
|
||||||
log.Info("P2PNotaryRequestPayloadPool size is not set or wrong, setting default value", zap.Int("P2PNotaryRequestPayloadPoolSize", cfg.P2PNotaryRequestPayloadPoolSize))
|
log.Info("P2PNotaryRequestPayloadPool size is not set or wrong, setting default value", zap.Int("P2PNotaryRequestPayloadPoolSize", cfg.P2PNotaryRequestPayloadPoolSize))
|
||||||
}
|
}
|
||||||
|
if cfg.MaxBlockSize == 0 {
|
||||||
|
cfg.MaxBlockSize = defaultMaxBlockSize
|
||||||
|
log.Info("MaxBlockSize is not set or wrong, setting default value", zap.Uint32("MaxBlockSize", cfg.MaxBlockSize))
|
||||||
|
}
|
||||||
if cfg.MaxTraceableBlocks == 0 {
|
if cfg.MaxTraceableBlocks == 0 {
|
||||||
cfg.MaxTraceableBlocks = defaultMaxTraceableBlocks
|
cfg.MaxTraceableBlocks = defaultMaxTraceableBlocks
|
||||||
log.Info("MaxTraceableBlocks is not set or wrong, using default value", zap.Uint32("MaxTraceableBlocks", cfg.MaxTraceableBlocks))
|
log.Info("MaxTraceableBlocks is not set or wrong, using default value", zap.Uint32("MaxTraceableBlocks", cfg.MaxTraceableBlocks))
|
||||||
|
@ -1335,6 +1344,28 @@ func (bc *Blockchain) ApplyPolicyToTxSet(txes []*transaction.Transaction) []*tra
|
||||||
if maxTx != 0 && len(txes) > int(maxTx) {
|
if maxTx != 0 && len(txes) > int(maxTx) {
|
||||||
txes = txes[:maxTx]
|
txes = txes[:maxTx]
|
||||||
}
|
}
|
||||||
|
maxBlockSize := bc.GetConfig().MaxBlockSize
|
||||||
|
defaultWitness := bc.defaultBlockWitness.Load()
|
||||||
|
if defaultWitness == nil {
|
||||||
|
m := smartcontract.GetDefaultHonestNodeCount(bc.config.ValidatorsCount)
|
||||||
|
verification, _ := smartcontract.CreateDefaultMultiSigRedeemScript(bc.contracts.NEO.GetNextBlockValidatorsInternal())
|
||||||
|
defaultWitness = transaction.Witness{
|
||||||
|
InvocationScript: make([]byte, 66*m),
|
||||||
|
VerificationScript: verification,
|
||||||
|
}
|
||||||
|
bc.defaultBlockWitness.Store(defaultWitness)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
b = &block.Block{Header: block.Header{Script: defaultWitness.(transaction.Witness)}}
|
||||||
|
blockSize = uint32(b.GetExpectedBlockSizeWithoutTransactions(len(txes)))
|
||||||
|
)
|
||||||
|
for i, tx := range txes {
|
||||||
|
blockSize += uint32(tx.Size())
|
||||||
|
if blockSize > maxBlockSize {
|
||||||
|
txes = txes[:i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
return txes
|
return txes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue