core: restrict the muximum number of contents per block
This commit is contained in:
parent
c79d440c21
commit
e1e586f18b
6 changed files with 140 additions and 3 deletions
|
@ -2,6 +2,7 @@ package consensus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/dbft/payload"
|
"github.com/nspcc-dev/dbft/payload"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"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/util"
|
||||||
)
|
)
|
||||||
|
@ -26,7 +27,7 @@ func (p *prepareRequest) EncodeBinary(w *io.BinWriter) {
|
||||||
func (p *prepareRequest) DecodeBinary(r *io.BinReader) {
|
func (p *prepareRequest) DecodeBinary(r *io.BinReader) {
|
||||||
p.timestamp = r.ReadU64LE()
|
p.timestamp = r.ReadU64LE()
|
||||||
p.nonce = r.ReadU64LE()
|
p.nonce = r.ReadU64LE()
|
||||||
r.ReadArray(&p.transactionHashes)
|
r.ReadArray(&p.transactionHashes, block.MaxTransactionsPerBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timestamp implements payload.PrepareRequest interface.
|
// Timestamp implements payload.PrepareRequest interface.
|
||||||
|
|
|
@ -3,7 +3,9 @@ package consensus
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/internal/random"
|
"github.com/nspcc-dev/neo-go/pkg/internal/random"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -30,3 +32,32 @@ func TestPrepareRequest_Setters(t *testing.T) {
|
||||||
p.SetTransactionHashes(hashes[:])
|
p.SetTransactionHashes(hashes[:])
|
||||||
require.Equal(t, hashes[:], p.TransactionHashes())
|
require.Equal(t, hashes[:], p.TransactionHashes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrepareRequest_EncodeDecodeBinary(t *testing.T) {
|
||||||
|
t.Run("positive", func(t *testing.T) {
|
||||||
|
expected := &prepareRequest{
|
||||||
|
timestamp: 112,
|
||||||
|
nonce: 1325,
|
||||||
|
transactionHashes: []util.Uint256{
|
||||||
|
random.Uint256(),
|
||||||
|
random.Uint256(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testserdes.EncodeDecodeBinary(t, expected, new(prepareRequest))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("bad hashes count", func(t *testing.T) {
|
||||||
|
hashes := make([]util.Uint256, block.MaxTransactionsPerBlock+1)
|
||||||
|
for i := range hashes {
|
||||||
|
hashes[i] = random.Uint256()
|
||||||
|
}
|
||||||
|
expected := &prepareRequest{
|
||||||
|
timestamp: 112,
|
||||||
|
nonce: 1325,
|
||||||
|
transactionHashes: hashes,
|
||||||
|
}
|
||||||
|
data, err := testserdes.EncodeBinary(expected)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Error(t, testserdes.DecodeBinary(data, new(prepareRequest)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package block
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"math"
|
||||||
|
|
||||||
"github.com/Workiva/go-datastructures/queue"
|
"github.com/Workiva/go-datastructures/queue"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
|
@ -12,6 +13,16 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MaxContentsPerBlock is the maximum number of contents (transactions + consensus data) per block.
|
||||||
|
MaxContentsPerBlock = math.MaxUint16
|
||||||
|
// MaxTransactionsPerBlock is the maximum number of transactions per block.
|
||||||
|
MaxTransactionsPerBlock = MaxContentsPerBlock - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -82,6 +93,9 @@ func NewBlockFromTrimmedBytes(network netmode.Magic, b []byte) (*Block, error) {
|
||||||
block.Script.DecodeBinary(br)
|
block.Script.DecodeBinary(br)
|
||||||
|
|
||||||
lenHashes := br.ReadVarUint()
|
lenHashes := br.ReadVarUint()
|
||||||
|
if lenHashes > MaxContentsPerBlock {
|
||||||
|
return nil, ErrMaxContentsPerBlock
|
||||||
|
}
|
||||||
if lenHashes > 0 {
|
if lenHashes > 0 {
|
||||||
var consensusDataHash util.Uint256
|
var consensusDataHash util.Uint256
|
||||||
consensusDataHash.DecodeBinary(br)
|
consensusDataHash.DecodeBinary(br)
|
||||||
|
@ -142,6 +156,10 @@ func (b *Block) DecodeBinary(br *io.BinReader) {
|
||||||
br.Err = errors.New("invalid block format")
|
br.Err = errors.New("invalid block format")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if contentsCount > MaxContentsPerBlock {
|
||||||
|
br.Err = ErrMaxContentsPerBlock
|
||||||
|
return
|
||||||
|
}
|
||||||
b.ConsensusData.DecodeBinary(br)
|
b.ConsensusData.DecodeBinary(br)
|
||||||
txes := make([]*transaction.Transaction, contentsCount-1)
|
txes := make([]*transaction.Transaction, contentsCount-1)
|
||||||
for i := 0; i < int(contentsCount)-1; i++ {
|
for i := 0; i < int(contentsCount)-1; i++ {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package block
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -218,3 +219,27 @@ func TestBlockCompare(t *testing.T) {
|
||||||
assert.Equal(t, 0, b2.Compare(&b2))
|
assert.Equal(t, 0, b2.Compare(&b2))
|
||||||
assert.Equal(t, -1, b2.Compare(&b3))
|
assert.Equal(t, -1, b2.Compare(&b3))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBlockEncodeDecode(t *testing.T) {
|
||||||
|
t.Run("positive", func(t *testing.T) {
|
||||||
|
b := newDumbBlock()
|
||||||
|
b.Transactions = []*transaction.Transaction{}
|
||||||
|
_ = b.Hash()
|
||||||
|
testserdes.EncodeDecodeBinary(t, b, new(Block))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("bad contents count", func(t *testing.T) {
|
||||||
|
b := newDumbBlock()
|
||||||
|
b.Transactions = make([]*transaction.Transaction, MaxContentsPerBlock)
|
||||||
|
for i := range b.Transactions {
|
||||||
|
b.Transactions[i] = &transaction.Transaction{
|
||||||
|
Script: []byte("my_pretty_script"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = b.Hash()
|
||||||
|
data, err := testserdes.EncodeBinary(b)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.True(t, errors.Is(testserdes.DecodeBinary(data, new(Block)), ErrMaxContentsPerBlock))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -19,8 +19,13 @@ func (m *MerkleBlock) DecodeBinary(br *io.BinReader) {
|
||||||
m.Base = &block.Base{}
|
m.Base = &block.Base{}
|
||||||
m.Base.DecodeBinary(br)
|
m.Base.DecodeBinary(br)
|
||||||
|
|
||||||
m.TxCount = int(br.ReadVarUint())
|
txCount := int(br.ReadVarUint())
|
||||||
br.ReadArray(&m.Hashes)
|
if txCount > block.MaxContentsPerBlock {
|
||||||
|
br.Err = block.ErrMaxContentsPerBlock
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.TxCount = txCount
|
||||||
|
br.ReadArray(&m.Hashes, m.TxCount)
|
||||||
m.Flags = br.ReadVarBytes()
|
m.Flags = br.ReadVarBytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
57
pkg/network/payload/merkleblock_test.go
Normal file
57
pkg/network/payload/merkleblock_test.go
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package payload
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newDumbBlock() *block.Base {
|
||||||
|
return &block.Base{
|
||||||
|
Version: 0,
|
||||||
|
PrevHash: hash.Sha256([]byte("a")),
|
||||||
|
MerkleRoot: hash.Sha256([]byte("b")),
|
||||||
|
Timestamp: 100500,
|
||||||
|
Index: 1,
|
||||||
|
NextConsensus: hash.Hash160([]byte("a")),
|
||||||
|
Script: transaction.Witness{
|
||||||
|
VerificationScript: []byte{0x51}, // PUSH1
|
||||||
|
InvocationScript: []byte{0x61}, // NOP
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMerkleBlock_EncodeDecodeBinary(t *testing.T) {
|
||||||
|
t.Run("positive", func(t *testing.T) {
|
||||||
|
b := newDumbBlock()
|
||||||
|
_ = b.Hash()
|
||||||
|
expected := &MerkleBlock{
|
||||||
|
Base: b,
|
||||||
|
TxCount: 0,
|
||||||
|
Hashes: []util.Uint256{},
|
||||||
|
Flags: []byte{},
|
||||||
|
}
|
||||||
|
testserdes.EncodeDecodeBinary(t, expected, new(MerkleBlock))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("bad contents count", func(t *testing.T) {
|
||||||
|
b := newDumbBlock()
|
||||||
|
_ = b.Hash()
|
||||||
|
expected := &MerkleBlock{
|
||||||
|
Base: b,
|
||||||
|
TxCount: block.MaxContentsPerBlock + 1,
|
||||||
|
Hashes: make([]util.Uint256, block.MaxContentsPerBlock),
|
||||||
|
Flags: []byte{},
|
||||||
|
}
|
||||||
|
data, err := testserdes.EncodeBinary(expected)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, errors.Is(block.ErrMaxContentsPerBlock, testserdes.DecodeBinary(data, new(MerkleBlock))))
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in a new issue