Skip to content

Commit

Permalink
Merge pull request #94 from ubclaunchpad/92-incoming-block-handling-l…
Browse files Browse the repository at this point in the history
…ogic

Incoming Block Handling Logic
  • Loading branch information
chadlagore committed Jul 15, 2017
2 parents bb21886 + 18d6d2b commit 0c6862b
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 48 deletions.
15 changes: 11 additions & 4 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,27 +125,34 @@ 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,
"Invalid request parameter, blockNumber must be uint32.")
notFoundErr := msg.NewProtocolError(msg.ResourceNotFound,
"Resource not found.")

switch req.ResourceType {
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 {
res.Error = paramErr
// Bad index parameter.
res.Error = notFoundErr
} else {
res.Resource = blk
}
} else {
res.Error = paramErr
// No index parameter.
res.Error = notFoundErr
}
default:
// Return err by default.
res.Error = typeErr
}

Expand Down
34 changes: 34 additions & 0 deletions app/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package app

import "github.com/ubclaunchpad/cumulus/blockchain"

// User holds basic user information.
type User struct {
// Wallet holds the users wallets (currently just support 1).
Wallet blockchain.Wallet
// UserBlockSize is the maximum size of a block in bytes when marshaled
// as specified by the user.
BlockSize uint32
}

var currentUser *User

const defaultBlockSize = 1 << 18

func init() {
// Temporary to create a new user for testing.
currentUser = NewUser()
}

// NewUser creates a new user
func NewUser() *User {
return &User{
Wallet: blockchain.NewWallet(),
BlockSize: defaultBlockSize,
}
}

// GetCurrentUser returns the current user.
func GetCurrentUser() *User {
return currentUser
}
21 changes: 14 additions & 7 deletions app/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,30 +62,37 @@ func (w AppWorker) Start() {

// HandleTransaction handles new instance of TransactionWork.
func (w *AppWorker) HandleTransaction(work TransactionWork) {
ok := tpool.Set(work.Transaction, chain)
validTransaction := tpool.Set(work.Transaction, chain)

// Respond to the request if a response method was provided.
if work.Responder != nil {
work.Responder.Lock()
defer work.Responder.Unlock()
work.Responder.Send(ok)
work.Responder.Send(validTransaction)
}
}

// HandleBlock handles TransactionWork.
// HandleBlock handles new instance of BlockWork.
func (w *AppWorker) HandleBlock(work BlockWork) {
ok, _ := chain.ValidBlock(work.Block)
if ok {
validBlock := tpool.Update(work.Block, chain)

if validBlock {
user := GetCurrentUser()
// Append to the chain before requesting
// the next block so that the block
// numbers make sense.
chain.AppendBlock(work.Block)
address := user.Wallet.Public()
blk := tpool.NextBlock(chain, address, user.BlockSize)
if miner.IsMining() {
miner.RestartMiner(chain, work.Block)
miner.RestartMiner(chain, blk)
}
}

// Respond to the request if a response method was provided.
if work.Responder != nil {
work.Responder.Lock()
defer work.Responder.Unlock()
work.Responder.Send(ok)
work.Responder.Send(validBlock)
}
}
2 changes: 2 additions & 0 deletions msg/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const (
// InvalidResourceType occurs when a request is received with an unknown
// ResourceType value.
InvalidResourceType = 401
// ResourceNotFound occurs when a node reports the requested resource missing.
ResourceNotFound = 404
// NotImplemented occurs when a message or request is received whos response
// requires functionality that does not yet exist.
NotImplemented = 501
Expand Down
64 changes: 51 additions & 13 deletions pool/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"time"

"github.com/ubclaunchpad/cumulus/blockchain"
"github.com/ubclaunchpad/cumulus/common/util"
"github.com/ubclaunchpad/cumulus/miner"
)

// PooledTransaction is a Transaction with a timetamp.
Expand Down Expand Up @@ -107,20 +109,56 @@ 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,
address blockchain.Address, size uint32) *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: util.UnixNow(),
Nonce: 0,
}, Transactions: txns,
}

// Prepend the cloudbase transaction for this miner.
miner.CloudBase(b, chain, address)

// 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 {
nextSize := p.Peek().Len()
if b.Len()+nextSize < int(size) {
b.Transactions = append(b.Transactions, p.Pop())
} else {
break
}
}
return txns

return b
}
58 changes: 34 additions & 24 deletions pool/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package pool
import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/ubclaunchpad/cumulus/blockchain"
)

Expand Down Expand Up @@ -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) {
Expand All @@ -111,3 +90,34 @@ 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, blockchain.NewWallet().Public(), 1<<18)
assert.NotNil(t, b)
assert.True(t, b.Len() < 1<<18)
assert.True(t, b.Len() > 0)

// The difference is off by one thanks to cloud transaction.
assert.Equal(t, len(b.Transactions), numTxns-p.Len()+1)
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())
}

0 comments on commit 0c6862b

Please sign in to comment.