From eee77fb3abc4f14629848226e6826069731d9b65 Mon Sep 17 00:00:00 2001 From: chadlagore Date: Wed, 12 Jul 2017 22:03:55 -0700 Subject: [PATCH] new pool functions; miner reset with new block; popTxns method :gun:; comments --- app/app.go | 7 +++++ app/worker.go | 11 +++++--- blockchain/block.go | 9 +++++++ blockchain/transaction.go | 9 +++++++ pool/pool.go | 57 ++++++++++++++++++++++++++++++--------- pool/pool_test.go | 56 +++++++++++++++++++++----------------- 6 files changed, 109 insertions(+), 40 deletions(-) diff --git a/app/app.go b/app/app.go index f06d97f..da8b366 100644 --- a/app/app.go +++ b/app/app.go @@ -125,6 +125,8 @@ func ConnectAndDiscover(target string) { // on peers whos RequestHandlers have been overridden. func RequestHandler(req *msg.Request) msg.Response { res := msg.Response{ID: req.ID} + + // Build some error types. typeErr := msg.NewProtocolError(msg.InvalidResourceType, "Invalid resource type") paramErr := msg.NewProtocolError(msg.InvalidResourceType, @@ -134,18 +136,23 @@ func RequestHandler(req *msg.Request) msg.Response { case msg.ResourcePeerInfo: res.Resource = peer.PStore.Addrs() case msg.ResourceBlock: + // Block is requested by number. blockNumber, ok := req.Params["blockNumber"].(uint32) if ok { + // If its ok, we make try to a copy of it. blk, err := chain.CopyBlockByIndex(blockNumber) if err != nil { + // Bad index parameter. res.Error = paramErr } else { res.Resource = blk } } else { + // No index parameter. res.Error = paramErr } default: + // Return err by default. res.Error = typeErr } diff --git a/app/worker.go b/app/worker.go index 8da3d2d..ef6614e 100644 --- a/app/worker.go +++ b/app/worker.go @@ -72,13 +72,18 @@ func (w *AppWorker) HandleTransaction(work TransactionWork) { } } -// HandleBlock handles TransactionWork. +// HandleBlock handles new instance of BlockWork. func (w *AppWorker) HandleBlock(work BlockWork) { - ok, _ := chain.ValidBlock(work.Block) + ok := tpool.Update(work.Block, chain) + if ok { + // Append to the chain before requesting + // the next block so that the block + // numbers make sense. chain.AppendBlock(work.Block) + blk := tpool.NextBlock(chain) if miner.IsMining() { - miner.RestartMiner(chain, work.Block) + miner.RestartMiner(chain, blk) } } diff --git a/blockchain/block.go b/blockchain/block.go index 4d67a7f..ed57b51 100644 --- a/blockchain/block.go +++ b/blockchain/block.go @@ -8,6 +8,15 @@ import ( "io" ) +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 + // MaxTransactionsPerBlock is the maximum number of transactions in a block. + MaxTransactionsPerBlock = BlockSize / MinTxnLen +) + // BlockHeader contains metadata about a block type BlockHeader struct { // BlockNumber is the position of the block within the blockchain diff --git a/blockchain/transaction.go b/blockchain/transaction.go index efaf165..e51bb94 100644 --- a/blockchain/transaction.go +++ b/blockchain/transaction.go @@ -5,6 +5,15 @@ import ( "io" ) +const ( + // TxHashPointerLen is the length in bytes of a hash pointer. + TxHashPointerLen = 32/8 + HashLen + // TxOutputLen is the length in bytes of a transaction output. + TxOutputLen = 64/8 + AddrLen + // MinTxnLen is the minimum length in bytes of a transaction. + MinTxnLen = TxHashPointerLen + TxOutputLen + SigLen + AddrLen +) + // TxHashPointer is a reference to a transaction on the blockchain. type TxHashPointer struct { BlockNumber uint32 diff --git a/pool/pool.go b/pool/pool.go index 279a627..64593a9 100644 --- a/pool/pool.go +++ b/pool/pool.go @@ -107,20 +107,51 @@ func (p *Pool) Update(b *blockchain.Block, bc *blockchain.BlockChain) bool { return true } -// PopTxns returns the the largest of l or size of pool transactions. -// It selects the highest priority transactions, and removes them from the pool. -func (p *Pool) PopTxns(l int) []*blockchain.Transaction { - if p.Len() == 0 { - return make([]*blockchain.Transaction, 0) +// Pop returns the next transaction and removes it from the pool. +func (p *Pool) Pop() *blockchain.Transaction { + if p.Len() > 0 { + next := p.GetN(0) + p.Delete(next) + return next } - if p.Len() < l { - l = p.Len() + return nil +} + +// Peek returns the next transaction and does not remove it from the pool. +func (p *Pool) Peek() *blockchain.Transaction { + if p.Len() > 0 { + return p.GetN(0) } - txns := make([]*blockchain.Transaction, l) - for i := 0; i < l; i++ { - t := p.GetN(i) - txns[i] = t - p.Delete(t) + return nil +} + +// NextBlock produces a new block from the pool for mining. +func (p *Pool) NextBlock(chain *blockchain.BlockChain) *blockchain.Block { + var txns []*blockchain.Transaction + + // Hash the last block in the chain. + ix := len(chain.Blocks) - 1 + lastHash := blockchain.HashSum(chain.Blocks[ix]) + + // Build a new block for mining. + b := &blockchain.Block{ + BlockHeader: blockchain.BlockHeader{ + BlockNumber: uint32(len(chain.Blocks)), + LastBlock: lastHash, + Time: uint32(time.Now().Second()), + Nonce: 0, + }, Transactions: txns, + } + + // Try to grab as many transactions as the block will allow. + // Test each transaction to see if we break size before adding. + for p.Len() > 0 { + if b.Len()+p.Peek().Len() < blockchain.BlockSize { + b.Transactions = append(b.Transactions, p.Pop()) + } else { + break + } } - return txns + + return b } diff --git a/pool/pool_test.go b/pool/pool_test.go index 24f6603..ef7fc19 100644 --- a/pool/pool_test.go +++ b/pool/pool_test.go @@ -3,6 +3,7 @@ package pool import ( "testing" + "github.com/stretchr/testify/assert" "github.com/ubclaunchpad/cumulus/blockchain" ) @@ -66,32 +67,10 @@ func TestUpdatePool(t *testing.T) { } } -func TestGetTxns(t *testing.T) { - p := New() - bc, b := blockchain.NewValidTestChainAndBlock() - for _, tr := range b.Transactions[1:] { - if !p.Set(tr, bc) { - t.FailNow() - } - } - nTxns := len(b.Transactions[1:]) + 12 // arbitrary. - txns := p.PopTxns(nTxns) - for _, tr := range txns { - if ok, _ := b.ContainsTransaction(tr); !ok { - t.FailNow() - } - } - if p.Len() != 0 { - t.FailNow() - } -} - func TestGetNewBlockEmpty(t *testing.T) { p := New() - txns := p.PopTxns(305) - if len(txns) != 0 { - t.FailNow() - } + txn := p.Pop() + assert.Nil(t, txn) } func TestGetIndex(t *testing.T) { @@ -111,3 +90,32 @@ func TestGetIndex(t *testing.T) { } } } + +func TestNextBlock(t *testing.T) { + p := New() + chain, _ := blockchain.NewValidTestChainAndBlock() + nBlks := len(chain.Blocks) + lastBlk := chain.Blocks[nBlks-1] + numTxns := 1000 + for i := 0; i < numTxns; i++ { + p.SetUnsafe(blockchain.NewTestTransaction()) + } + b := p.NextBlock(chain) + assert.NotNil(t, b) + assert.True(t, b.Len() < blockchain.BlockSize) + assert.True(t, b.Len() > 0) + assert.Equal(t, len(b.Transactions), numTxns-p.Len()) + assert.Equal(t, blockchain.HashSum(lastBlk), b.LastBlock) + assert.Equal(t, uint64(0), b.Nonce) + assert.Equal(t, uint32(nBlks), b.BlockNumber) +} + +func TestPeek(t *testing.T) { + p := New() + assert.Nil(t, p.Peek()) +} + +func TestPop(t *testing.T) { + p := New() + assert.Nil(t, p.Pop()) +}