Implement 'block' and 'getdata' commands

This commit is contained in:
Ivan Kuznetsov 2017-10-01 11:50:27 +07:00
parent 9adb5fc139
commit 470adef2c3
2 changed files with 112 additions and 6 deletions

View File

@ -97,6 +97,11 @@ func NewBlockchain(nodeID string) *Blockchain {
return &bc
}
// Reset removes all blockchain data
func (bc *Blockchain) Reset() {
}
// FindTransaction finds a transaction by its ID
func (bc *Blockchain) FindTransaction(ID []byte) (Transaction, error) {
bci := bc.Iterator()
@ -169,6 +174,30 @@ func (bc *Blockchain) Iterator() *BlockchainIterator {
return bci
}
// GetBlock finds a block by its hash and returns it
func (bc *Blockchain) GetBlock(blockHash []byte) (Block, error) {
var block Block
err := bc.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blocksBucket))
blockData := b.Get(blockHash)
if blockData == nil {
return errors.New("Block is not found.")
}
block = *DeserializeBlock(blockData)
return nil
})
if err != nil {
return block, err
}
return block, nil
}
// GetBlockHashes returns a list of hashes of all the blocks in the chain
func (bc *Blockchain) GetBlockHashes() [][]byte {
var blocks [][]byte

View File

@ -22,13 +22,25 @@ type addr struct {
AddrList []string
}
type block struct {
AddrFrom string
Block []byte
}
type getblocks struct {
AddrFrom string
}
type getdata struct {
AddrFrom string
Type string
ID []byte
}
type inv struct {
Type string
Items [][]byte
AddrFrom string
Type string
Items [][]byte
}
type verack struct {
@ -81,6 +93,14 @@ func sendAddr(address string) {
sendData(address, request)
}
func sendBlock(addr string, b *Block) {
data := block{nodeAddress, b.Serialize()}
payload := gobEncode(data)
request := append(commandToBytes("block"), payload...)
sendData(addr, request)
}
func sendData(addr string, data []byte) {
conn, err := net.Dial(protocol, addr)
if err != nil {
@ -95,7 +115,7 @@ func sendData(addr string, data []byte) {
}
func sendInv(address, kind string, items [][]byte) {
inventory := inv{kind, items}
inventory := inv{nodeAddress, kind, items}
payload := gobEncode(inventory)
request := append(commandToBytes("inv"), payload...)
@ -109,6 +129,13 @@ func sendGetBlocks(address string) {
sendData(address, request)
}
func sendGetData(address, kind string, id []byte) {
payload := gobEncode(getdata{nodeAddress, kind, id})
request := append(commandToBytes("getdata"), payload...)
sendData(address, request)
}
func sendVersion(addr string) {
payload := gobEncode(verzion{nodeVersion, nodeAddress})
@ -141,7 +168,25 @@ func handleAddr(request []byte) {
requestBlocks()
}
func handleInv(request []byte) {
func handleBlock(request []byte, bc *Blockchain) {
var buff bytes.Buffer
var payload block
buff.Write(request[commandLength:])
dec := gob.NewDecoder(&buff)
err := dec.Decode(&payload)
if err != nil {
log.Panic(err)
}
blockData := payload.Block
block := DeserializeBlock(blockData)
fmt.Println("Recevied a new block!")
fmt.Println(block)
}
func handleInv(request []byte, bc *Blockchain) {
var buff bytes.Buffer
var payload inv
@ -152,7 +197,14 @@ func handleInv(request []byte) {
log.Panic(err)
}
fmt.Printf("Recevied %d %s\n", len(payload.Items), payload.Type)
fmt.Printf("Recevied inventory with %d %s\n", len(payload.Items), payload.Type)
blocks := bc.GetBlockHashes()
if len(blocks) < len(payload.Items) {
for _, blockHash := range invResponse.Items {
sendGetData(sourceNode, "block", blockHash)
}
}
}
func handleGetBlocks(request []byte, bc *Blockchain) {
@ -170,6 +222,27 @@ func handleGetBlocks(request []byte, bc *Blockchain) {
sendInv(payload.AddrFrom, "blocks", blocks)
}
func handleGetData(request []byte, bc *Blockchain) {
var buff bytes.Buffer
var payload getdata
buff.Write(request[commandLength:])
dec := gob.NewDecoder(&buff)
err := dec.Decode(&payload)
if err != nil {
log.Panic(err)
}
if payload.Type == "block" {
block, err := bc.GetBlock([]byte(payload.ID))
if err != nil {
return
}
sendBlock(payload.AddrFrom, &block)
}
}
func handleVersion(request []byte) {
var buff bytes.Buffer
var payload verzion
@ -197,10 +270,14 @@ func handleConnection(conn net.Conn, bc *Blockchain) {
switch command {
case "addr":
handleAddr(request)
case "block":
handleBlock(request, bc)
case "inv":
handleInv(request)
handleInv(request, bc)
case "getblocks":
handleGetBlocks(request, bc)
case "getdata":
handleGetData(request, bc)
case "version":
handleVersion(request)
case "verack":