diff --git a/pkg/config/protocol_config.go b/pkg/config/protocol_config.go index 39bb96cf0..2edd1f936 100644 --- a/pkg/config/protocol_config.go +++ b/pkg/config/protocol_config.go @@ -20,6 +20,8 @@ type ( RemoveUntraceableBlocks bool `yaml:"RemoveUntraceableBlocks"` // MaxBlockSize is the maximum block size in bytes. MaxBlockSize uint32 `yaml:"MaxBlockSize"` + // MaxBlockSystemFee is the maximum overall system fee per block. + MaxBlockSystemFee int64 `yaml:"MaxBlockSystemFee"` // MaxTraceableBlocks is the length of the chain accessible to smart contracts. MaxTraceableBlocks uint32 `yaml:"MaxTraceableBlocks"` // MaxTransactionsPerBlock is the maximum amount of transactions per block. diff --git a/pkg/consensus/consensus.go b/pkg/consensus/consensus.go index a5c90787c..a87b1fe4a 100644 --- a/pkg/consensus/consensus.go +++ b/pkg/consensus/consensus.go @@ -445,11 +445,13 @@ func (s *service) verifyBlock(b block.Block) bool { return false } + var fee int64 var pool = mempool.New(len(coreb.Transactions), 0, false) var mainPool = s.Chain.GetMemPool() for _, tx := range coreb.Transactions { var err error + fee += tx.SystemFee if mainPool.ContainsKey(tx.Hash()) { err = pool.Add(tx, s.Chain) if err == nil { @@ -470,6 +472,14 @@ func (s *service) verifyBlock(b block.Block) bool { } } + maxBlockSysFee := s.ProtocolConfiguration.MaxBlockSystemFee + if fee > maxBlockSysFee { + s.log.Warn("proposed block system fee exceeds config MaxBlockSystemFee", + zap.Int("max system fee allowed", int(maxBlockSysFee)), + zap.Int("block system fee", int(fee))) + return false + } + return true } diff --git a/pkg/consensus/consensus_test.go b/pkg/consensus/consensus_test.go index 2efabd301..b448e4f56 100644 --- a/pkg/consensus/consensus_test.go +++ b/pkg/consensus/consensus_test.go @@ -433,6 +433,17 @@ func TestVerifyBlock(t *testing.T) { b := testchain.NewBlock(t, srv.Chain, 1, 0, tx) require.False(t, srv.verifyBlock(&neoBlock{Block: *b})) }) + t.Run("bad big sys fee", func(t *testing.T) { + txes := make([]*transaction.Transaction, 2) + for i := range txes { + txes[i] = transaction.New(netmode.UnitTestNet, []byte{byte(opcode.RET)}, srv.ProtocolConfiguration.MaxBlockSystemFee/2+1) + txes[i].ValidUntilBlock = 1 + addSender(t, txes[i]) + signTx(t, srv.Chain, txes[i]) + } + b := testchain.NewBlock(t, srv.Chain, 1, 0, txes...) + require.False(t, srv.verifyBlock(&neoBlock{Block: *b})) + }) } func shouldReceive(t *testing.T, ch chan Payload) { diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 706cd4fde..f179a433a 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -47,6 +47,7 @@ const ( defaultMemPoolSize = 50000 defaultP2PNotaryRequestPayloadPoolSize = 1000 defaultMaxBlockSize = 262144 + defaultMaxBlockSystemFee = 900000000000 defaultMaxTraceableBlocks = 2102400 // 1 year of 15s blocks defaultMaxTransactionsPerBlock = 512 verificationGasLimit = 100000000 // 1 GAS @@ -176,6 +177,10 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L cfg.MaxBlockSize = defaultMaxBlockSize log.Info("MaxBlockSize is not set or wrong, setting default value", zap.Uint32("MaxBlockSize", cfg.MaxBlockSize)) } + if cfg.MaxBlockSystemFee <= 0 { + cfg.MaxBlockSystemFee = defaultMaxBlockSystemFee + log.Info("MaxBlockSystemFee is not set or wrong, setting default value", zap.Int64("MaxBlockSystemFee", cfg.MaxBlockSystemFee)) + } if cfg.MaxTraceableBlocks == 0 { cfg.MaxTraceableBlocks = defaultMaxTraceableBlocks log.Info("MaxTraceableBlocks is not set or wrong, using default value", zap.Uint32("MaxTraceableBlocks", cfg.MaxTraceableBlocks)) @@ -1345,6 +1350,7 @@ func (bc *Blockchain) ApplyPolicyToTxSet(txes []*transaction.Transaction) []*tra txes = txes[:maxTx] } maxBlockSize := bc.GetConfig().MaxBlockSize + maxBlockSysFee := bc.GetConfig().MaxBlockSystemFee defaultWitness := bc.defaultBlockWitness.Load() if defaultWitness == nil { m := smartcontract.GetDefaultHonestNodeCount(bc.config.ValidatorsCount) @@ -1356,12 +1362,14 @@ func (bc *Blockchain) ApplyPolicyToTxSet(txes []*transaction.Transaction) []*tra bc.defaultBlockWitness.Store(defaultWitness) } var ( - b = &block.Block{Header: block.Header{Script: defaultWitness.(transaction.Witness)}} - blockSize = uint32(b.GetExpectedBlockSizeWithoutTransactions(len(txes))) + b = &block.Block{Header: block.Header{Script: defaultWitness.(transaction.Witness)}} + blockSize = uint32(b.GetExpectedBlockSizeWithoutTransactions(len(txes))) + blockSysFee int64 ) for i, tx := range txes { blockSize += uint32(tx.Size()) - if blockSize > maxBlockSize { + blockSysFee += tx.SystemFee + if blockSize > maxBlockSize || blockSysFee > maxBlockSysFee { txes = txes[:i] break }