From 5a45c27c4c3cf61373bbcb4b9eda7f911e7e8db3 Mon Sep 17 00:00:00 2001 From: David Julien Date: Sun, 9 Jul 2017 19:41:03 -0700 Subject: [PATCH] ExtraData field added to block --- blockchain/block.go | 20 +++++++--- blockchain/block_test.go | 33 ++++++++++++++++ blockchain/genesis.go | 3 +- blockchain/genesis_test.go | 2 +- blockchain/validation.go | 23 ++++++++++-- blockchain/validation_test.go | 71 +++++++++++++++++++++++++++++++---- 6 files changed, 134 insertions(+), 18 deletions(-) diff --git a/blockchain/block.go b/blockchain/block.go index 6555af0..85841d3 100644 --- a/blockchain/block.go +++ b/blockchain/block.go @@ -9,10 +9,9 @@ import ( ) const ( - // BlockSize is the maximum size of a block in bytes when marshaled (about 250K). - BlockSize = 1 << 18 - // BlockHeaderLen is the length in bytes of a block header. - BlockHeaderLen = 2*(32/8) + 64/8 + 2*HashLen + // MaxBlockSize is the maximum size of a block in bytes when marshaled + // (about 250K). + MaxBlockSize = 1 << 18 ) // BlockHeader contains metadata about a block @@ -28,6 +27,9 @@ type BlockHeader struct { Time uint32 // Nonce starts at 0 and increments by 1 for every hash when mining Nonce uint64 + // ExtraData is an extra field that can be filled with arbitrary data to + // be stored in the block + ExtraData []byte } // Marshal converts a BlockHeader to a byte slice @@ -48,10 +50,18 @@ func (bh *BlockHeader) Marshal() []byte { buf = append(buf, bh.Target.Marshal()...) buf = append(buf, tempBufTime...) buf = append(buf, tempBufNonce...) + buf = append(buf, bh.ExtraData...) return buf } +// Len returns the length in bytes of the BlockHeader. +func (bh *BlockHeader) Len() int { + l := 2*(32/8) + 64/8 + 2*HashLen + l += len(bh.ExtraData) + return l +} + // Block represents a block in the blockchain. Contains transactions and header metadata. type Block struct { BlockHeader @@ -60,7 +70,7 @@ type Block struct { // Len returns the length in bytes of the Block. func (b *Block) Len() int { - l := BlockHeaderLen + l := b.BlockHeader.Len() for _, t := range b.Transactions { l += t.Len() } diff --git a/blockchain/block_test.go b/blockchain/block_test.go index 41c6db0..63bf289 100644 --- a/blockchain/block_test.go +++ b/blockchain/block_test.go @@ -3,6 +3,7 @@ package blockchain import ( "bytes" "testing" + "time" ) func TestEncodeDecodeBlock(t *testing.T) { @@ -25,3 +26,35 @@ func TestContainsTransaction(t *testing.T) { t.Fail() } } + +func TestBlockHeaderLen(t *testing.T) { + bh := &BlockHeader{ + 0, + NewHash(), + NewValidTarget(), + uint32(time.Now().Unix()), + 0, + []byte{0x00, 0x01, 0x02}, + } + + len := 2*(32/8) + 64/8 + 2*HashLen + 3 + + if bh.Len() != len { + t.Fail() + } + + bh = &BlockHeader{ + 0, + NewHash(), + NewValidTarget(), + uint32(time.Now().Unix()), + 0, + []byte{}, + } + + len = 2*(32/8) + 64/8 + 2*HashLen + + if bh.Len() != len { + t.Fail() + } +} diff --git a/blockchain/genesis.go b/blockchain/genesis.go index 243632e..d6477e5 100644 --- a/blockchain/genesis.go +++ b/blockchain/genesis.go @@ -11,7 +11,7 @@ import ( // - LastBlock = 0 // - There is only one transaction in the block, the CloudBase transaction that // awards the miner with the block reward. -func Genesis(miner Address, target Hash, blockReward uint64) *Block { +func Genesis(miner Address, target Hash, blockReward uint64, extraData []byte) *Block { cbReward := TxOutput{ Amount: blockReward, @@ -38,6 +38,7 @@ func Genesis(miner Address, target Hash, blockReward uint64) *Block { Target: target, Time: uint32(time.Now().Unix()), Nonce: 0, + ExtraData: extraData, }, Transactions: []*Transaction{cbTx}, } diff --git a/blockchain/genesis_test.go b/blockchain/genesis_test.go index e5a05ea..945721d 100644 --- a/blockchain/genesis_test.go +++ b/blockchain/genesis_test.go @@ -9,7 +9,7 @@ func TestGenesis(t *testing.T) { miner := NewWallet() currentTarget := BigIntToHash(MaxTarget) currentBlockReward := uint64(25) - gb := Genesis(miner.Public(), currentTarget, currentBlockReward) + gb := Genesis(miner.Public(), currentTarget, currentBlockReward, []byte{}) // Check if the genesis block is equal to nil if gb == nil { diff --git a/blockchain/validation.go b/blockchain/validation.go index 4eb47e3..bb8f813 100644 --- a/blockchain/validation.go +++ b/blockchain/validation.go @@ -73,8 +73,11 @@ const ( BadGenesisBlockNumber // BadGenesisTarget is returned when the genesis block's target is invalid. BadGenesisTarget - // BadGenesisTime is returned when teh gensis block's time is invalid. + // BadGenesisTime is returned when the gensis block's time is invalid. BadGenesisTime + // BadGenesisBlockSize is returned when the size of the block exceeds the + // maximum block size + BadGenesisBlockSize // NilGenesisBlock is returned when the genesis block is equal to nil. NilGenesisBlock ) @@ -104,6 +107,9 @@ const ( // BadGenesisBlock is returned if the block is a genesis block and is // invalid. BadGenesisBlock + // BadBlockSize is returned when the size of the block exceeds the maximum + // block size + BadBlockSize // NilBlock is returned when the block pointer is nil. NilBlock ) @@ -190,11 +196,17 @@ func ValidCloudBase(t *Transaction) (bool, CloudBaseTransactionCode) { // ValidGenesisBlock checks whether a block is a valid genesis block. func (bc *BlockChain) ValidGenesisBlock(gb *Block) (bool, GenesisBlockCode) { + // Check if the genesis block is equal to nil. if gb == nil { return false, NilGenesisBlock } + // Check if the genesis block is too large. + if gb.Len() > MaxBlockSize { + return false, BadGenesisBlockSize + } + // Check if the genesis block's block number is equal to 0. if gb.BlockHeader.BlockNumber != 0 || bc.Blocks[0] != gb { @@ -234,12 +246,17 @@ func (bc *BlockChain) ValidGenesisBlock(gb *Block) (bool, GenesisBlockCode) { // ValidBlock checks whether a block is valid. func (bc *BlockChain) ValidBlock(b *Block) (bool, BlockCode) { - // Check if the block is equal to nil + // Check if the block is equal to nil. if b == nil { return false, NilBlock } - // Check if the block is the genesis block + // Check if the block is too large. + if b.Len() > MaxBlockSize { + return false, BadBlockSize + } + + // Check if the block is the genesis block. if b.BlockHeader.BlockNumber == 0 || bc.Blocks[0] == b { if valid, code := bc.ValidGenesisBlock(b); !valid { log.Errorf("Invalid GenesisBlock, GenesisBlockCode: %d", code) diff --git a/blockchain/validation_test.go b/blockchain/validation_test.go index 16f33ac..659e65e 100644 --- a/blockchain/validation_test.go +++ b/blockchain/validation_test.go @@ -111,11 +111,34 @@ func TestValidBlockNilBlock(t *testing.T) { } } +func TestValidBlockBadBlockSize(t *testing.T) { + bc, b := NewValidChainAndBlock() + + for i := 0; i <= MaxBlockSize/TxOutputLen; i++ { + b.Transactions[0].Outputs = append( + b.Transactions[0].Outputs, + TxOutput{ + 0, + NewWallet().Public(), + }, + ) + } + + valid, code := bc.ValidBlock(b) + + if valid { + t.Fail() + } + if code != BadBlockSize { + t.Fail() + } +} + func TestValidBlockBadGenesisBlock(t *testing.T) { miner := NewWallet() currentTarget := BigIntToHash(MaxTarget) currentBlockReward := uint64(25) - gb := Genesis(miner.Public(), currentTarget, currentBlockReward) + gb := Genesis(miner.Public(), currentTarget, currentBlockReward, []byte{}) gb.Target = BigIntToHash(BigExp(2, 255)) bc := &BlockChain{ Blocks: []*Block{gb}, @@ -407,7 +430,7 @@ func TestValidGenesisBlock(t *testing.T) { miner := NewWallet() currentTarget := BigIntToHash(MaxTarget) currentBlockReward := uint64(25) - gb := Genesis(miner.Public(), currentTarget, currentBlockReward) + gb := Genesis(miner.Public(), currentTarget, currentBlockReward, []byte{}) bc := &BlockChain{ Blocks: []*Block{gb}, Head: HashSum(gb), @@ -442,11 +465,43 @@ func TestValidGenesisBlockNilGenesisBlock(t *testing.T) { } } +func TestValidGenesisBlockBadGenesisBlockSize(t *testing.T) { + miner := NewWallet() + currentTarget := BigIntToHash(MaxTarget) + currentBlockReward := uint64(25) + gb := Genesis(miner.Public(), currentTarget, currentBlockReward, []byte{}) + + for i := 0; i <= MaxBlockSize/TxOutputLen; i++ { + gb.Transactions[0].Outputs = append( + gb.Transactions[0].Outputs, + TxOutput{ + 0, + NewWallet().Public(), + }, + ) + } + + bc := &BlockChain{ + Blocks: []*Block{gb}, + Head: HashSum(gb), + } + + valid, code := bc.ValidGenesisBlock(gb) + + if valid { + t.Fail() + } + + if code != BadGenesisBlockSize { + t.Fail() + } +} + func TestValidGenesisBlockBadGenesisBlockNumber(t *testing.T) { miner := NewWallet() currentTarget := BigIntToHash(MaxTarget) currentBlockReward := uint64(25) - gb := Genesis(miner.Public(), currentTarget, currentBlockReward) + gb := Genesis(miner.Public(), currentTarget, currentBlockReward, []byte{}) gb.BlockHeader.BlockNumber = 1 bc := &BlockChain{ Blocks: []*Block{gb}, @@ -484,7 +539,7 @@ func TestValidGenesisBlockBadGenesisLastBlock(t *testing.T) { miner := NewWallet() currentTarget := BigIntToHash(MaxTarget) currentBlockReward := uint64(25) - gb := Genesis(miner.Public(), currentTarget, currentBlockReward) + gb := Genesis(miner.Public(), currentTarget, currentBlockReward, []byte{}) gb.LastBlock = NewHash() bc := &BlockChain{ Blocks: []*Block{gb}, @@ -506,7 +561,7 @@ func TestValidGenesisBlockBadGenesisTransactions(t *testing.T) { miner := NewWallet() currentTarget := BigIntToHash(MaxTarget) currentBlockReward := uint64(25) - gb := Genesis(miner.Public(), currentTarget, currentBlockReward) + gb := Genesis(miner.Public(), currentTarget, currentBlockReward, []byte{}) gb.Transactions = append(gb.Transactions, NewTransaction()) bc := &BlockChain{ Blocks: []*Block{gb}, @@ -528,7 +583,7 @@ func TestValidGenesisBlockBadGenesisCloudBaseTransaction(t *testing.T) { miner := NewWallet() currentTarget := BigIntToHash(MaxTarget) currentBlockReward := uint64(25) - gb := Genesis(miner.Public(), currentTarget, currentBlockReward) + gb := Genesis(miner.Public(), currentTarget, currentBlockReward, []byte{}) gb.Transactions[0] = NewTransaction() bc := &BlockChain{ Blocks: []*Block{gb}, @@ -550,7 +605,7 @@ func TestValidGenesisBlockBadGenesisTarget(t *testing.T) { miner := NewWallet() currentTarget := BigIntToHash(MaxTarget) currentBlockReward := uint64(25) - gb := Genesis(miner.Public(), currentTarget, currentBlockReward) + gb := Genesis(miner.Public(), currentTarget, currentBlockReward, []byte{}) gb.Target = BigIntToHash(BigExp(2, 255)) bc := &BlockChain{ Blocks: []*Block{gb}, @@ -572,7 +627,7 @@ func TestValidGenesisBlockBadGenesisTime(t *testing.T) { miner := NewWallet() currentTarget := BigIntToHash(MaxTarget) currentBlockReward := uint64(25) - gb := Genesis(miner.Public(), currentTarget, currentBlockReward) + gb := Genesis(miner.Public(), currentTarget, currentBlockReward, []byte{}) gb.Time = 0 bc := &BlockChain{ Blocks: []*Block{gb},