From e9f9354b86e5ee26b6b389d536595817c05b26fb Mon Sep 17 00:00:00 2001
From: anthdm <anthony@academiclabs.co>
Date: Tue, 30 Jan 2018 11:56:36 +0100
Subject: [PATCH] added core block type

---
 README.md                                   |  4 +--
 pkg/core/block.go                           | 28 ++++++++++++++++
 pkg/core/transaction.go                     |  4 +++
 pkg/network/message.go                      |  7 ++++
 pkg/network/payload/inventory.go            | 36 ++++++++++++++++++---
 pkg/network/payload/inventory_test.go       | 32 ++++++++++++++++++
 pkg/network/server.go                       | 18 +++++++++++
 pkg/util/{uint256.go => types.go}           |  6 +++-
 pkg/util/{uint256_test.go => types_test.go} |  0
 9 files changed, 128 insertions(+), 7 deletions(-)
 create mode 100644 pkg/core/block.go
 create mode 100644 pkg/core/transaction.go
 create mode 100644 pkg/network/payload/inventory_test.go
 rename pkg/util/{uint256.go => types.go} (84%)
 rename pkg/util/{uint256_test.go => types_test.go} (100%)

diff --git a/README.md b/README.md
index 1449ce4be..e248c70ab 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
 <h1 align="center">NEO-GO</h1>
 
 <p align="center">
-    Node and SDK for the <b>NEO</b> blockchain written in the <b>Go</b> language.
+  Node and SDK for the <b>NEO</b> blockchain written in the <b>Go</b> language.
 </p>
 
 <p align="center">
@@ -20,7 +20,7 @@
 ### Long term project goals
 Full port of the original C# [NEO project](https://github.com/neo-project). A complete toolkit for the NEO blockchain.
 
-- Full server (consensus, RPC and bookkeeping) nodes.
+- Full server (consensus and RPC) nodes.
 - RPC client
 - build, compile and deploy smart contracts with the Go vm
 
diff --git a/pkg/core/block.go b/pkg/core/block.go
new file mode 100644
index 000000000..67057569d
--- /dev/null
+++ b/pkg/core/block.go
@@ -0,0 +1,28 @@
+package core
+
+import (
+	. "github.com/anthdm/neo-go/pkg/util"
+)
+
+// Block represents one block in the chain.
+type Block struct {
+	Version uint32
+	// hash of the previous block.
+	PrevBlock Uint256
+	// Root hash of a transaction list.
+	MerkleRoot Uint256
+	// timestamp
+	Timestamp uint32
+	// height of the block
+	Height uint32
+	// Random number
+	Nonce uint64
+	// contract addresss of the next miner
+	NextMiner Uint160
+	// seperator ? fixed to 1
+	_sep uint8
+	// Script used to validate the block
+	Script []byte
+	// transaction list
+	Transactions []*Transaction
+}
diff --git a/pkg/core/transaction.go b/pkg/core/transaction.go
new file mode 100644
index 000000000..905a7b158
--- /dev/null
+++ b/pkg/core/transaction.go
@@ -0,0 +1,4 @@
+package core
+
+// Transaction is a process recorded in the NEO system.
+type Transaction struct{}
diff --git a/pkg/network/message.go b/pkg/network/message.go
index eeec5a59c..3df2e0305 100644
--- a/pkg/network/message.go
+++ b/pkg/network/message.go
@@ -83,6 +83,7 @@ const (
 	cmdGetData                = "getdata"
 	cmdBlock                  = "block"
 	cmdTX                     = "tx"
+	cmdConsensus              = "consensus"
 )
 
 func newMessage(magic NetMode, cmd commandType, p payload.Payload) *Message {
@@ -137,6 +138,8 @@ func (m *Message) commandType() commandType {
 		return cmdBlock
 	case "tx":
 		return cmdTX
+	case "consensus":
+		return cmdConsensus
 	default:
 		return ""
 	}
@@ -149,6 +152,8 @@ func (m *Message) decode(r io.Reader) error {
 	binary.Read(r, binary.LittleEndian, &m.Length)
 	binary.Read(r, binary.LittleEndian, &m.Checksum)
 
+	fmt.Println(cmdByteArrayToString(m.Command))
+
 	// return if their is no payload.
 	if m.Length == 0 {
 		return nil
@@ -164,6 +169,8 @@ func (m *Message) decodePayload(r io.Reader) error {
 		return err
 	}
 
+	fmt.Printf("The length of the payload is %d\n", n)
+
 	if uint32(n) != m.Length {
 		return fmt.Errorf("expected to have read exactly %d bytes got %d", m.Length, n)
 	}
diff --git a/pkg/network/payload/inventory.go b/pkg/network/payload/inventory.go
index 980433865..306995016 100644
--- a/pkg/network/payload/inventory.go
+++ b/pkg/network/payload/inventory.go
@@ -27,6 +27,11 @@ func (i InventoryType) String() string {
 	}
 }
 
+// Valid returns true if the inventory (type) is known.
+func (i InventoryType) Valid() bool {
+	return i == BlockType || i == TXType || i == ConsensusType
+}
+
 // List of valid InventoryTypes.
 const (
 	BlockType     InventoryType = 0x01 // 1
@@ -39,7 +44,15 @@ type Inventory struct {
 	// Type if the object hash.
 	Type InventoryType
 	// The hash of the object (uint256).
-	Hash Uint256
+	Hashes []Uint256
+}
+
+// NewInventory return a pointer to an Inventory.
+func NewInventory(typ InventoryType, hashes []Uint256) *Inventory {
+	return &Inventory{
+		Type:   typ,
+		Hashes: hashes,
+	}
 }
 
 // DecodeBinary implements the Payload interface.
@@ -50,15 +63,30 @@ func (p *Inventory) DecodeBinary(r io.Reader) error {
 	var listLen uint8
 	err := binary.Read(r, binary.LittleEndian, &p.Type)
 	err = binary.Read(r, binary.LittleEndian, &listLen)
-	err = binary.Read(r, binary.LittleEndian, &p.Hash)
+
+	p.Hashes = make([]Uint256, listLen)
+	for i := 0; i < int(listLen); i++ {
+		if err := binary.Read(r, binary.LittleEndian, &p.Hashes[i]); err != nil {
+			return err
+		}
+	}
 
 	return err
 }
 
 // EncodeBinary implements the Payload interface.
 func (p *Inventory) EncodeBinary(w io.Writer) error {
-	// TODO
-	return nil
+	listLen := uint8(len(p.Hashes))
+	err := binary.Write(w, binary.LittleEndian, p.Type)
+	err = binary.Write(w, binary.LittleEndian, listLen)
+
+	for i := 0; i < len(p.Hashes); i++ {
+		if err := binary.Write(w, binary.LittleEndian, p.Hashes[i]); err != nil {
+			return err
+		}
+	}
+
+	return err
 }
 
 // Size implements the Payloader interface.
diff --git a/pkg/network/payload/inventory_test.go b/pkg/network/payload/inventory_test.go
new file mode 100644
index 000000000..774e33017
--- /dev/null
+++ b/pkg/network/payload/inventory_test.go
@@ -0,0 +1,32 @@
+package payload
+
+import (
+	"bytes"
+	"crypto/sha256"
+	"reflect"
+	"testing"
+
+	. "github.com/anthdm/neo-go/pkg/util"
+)
+
+func TestInventoryEncodeDecode(t *testing.T) {
+	hashes := []Uint256{
+		sha256.Sum256([]byte("a")),
+		sha256.Sum256([]byte("b")),
+	}
+	inv := NewInventory(BlockType, hashes)
+
+	buf := new(bytes.Buffer)
+	if err := inv.EncodeBinary(buf); err != nil {
+		t.Fatal(err)
+	}
+
+	invDecode := &Inventory{}
+	if err := invDecode.DecodeBinary(buf); err != nil {
+		t.Fatal(err)
+	}
+
+	if !reflect.DeepEqual(inv, invDecode) {
+		t.Fatalf("expected both inventories to be equal %v and %v", inv, invDecode)
+	}
+}
diff --git a/pkg/network/server.go b/pkg/network/server.go
index 4b2680431..ceba713b2 100644
--- a/pkg/network/server.go
+++ b/pkg/network/server.go
@@ -189,9 +189,11 @@ func (s *Server) processMessage(msg *Message, peer *Peer) error {
 	case cmdHeaders:
 	case cmdGetBlocks:
 	case cmdInv:
+		return s.handleInvCmd(msg.Payload.(*payload.Inventory), peer)
 	case cmdGetData:
 	case cmdBlock:
 	case cmdTX:
+	case cmdConsensus:
 	default:
 		return fmt.Errorf("invalid RPC command received: %s", command)
 	}
@@ -241,6 +243,22 @@ func (s *Server) handleAddrCmd(addrList *payload.AddressList, peer *Peer) error
 	return nil
 }
 
+func (s *Server) handleInvCmd(inv *payload.Inventory, peer *Peer) error {
+	if !inv.Type.Valid() {
+		return fmt.Errorf("invalid inventory type: %s", inv.Type)
+	}
+	if len(inv.Hashes) == 0 {
+		return nil
+	}
+
+	payload := payload.NewInventory(inv.Type, inv.Hashes)
+	msg := newMessage(s.net, cmdGetData, payload)
+
+	peer.send <- msg
+
+	return nil
+}
+
 func (s *Server) peerAlreadyConnected(addr net.Addr) bool {
 	// TODO: check for race conditions
 	//s.mtx.RLock()
diff --git a/pkg/util/uint256.go b/pkg/util/types.go
similarity index 84%
rename from pkg/util/uint256.go
rename to pkg/util/types.go
index d82e19877..51588988e 100644
--- a/pkg/util/uint256.go
+++ b/pkg/util/types.go
@@ -7,7 +7,8 @@ import (
 	"fmt"
 )
 
-// Uint256 ...
+// Uint256 is a 32 byte long unsigned integer.
+// Commonly used to store hashes.
 type Uint256 [32]uint8
 
 // Uint256FromBytes return an Uint256 from a byte slice.
@@ -44,3 +45,6 @@ func (u Uint256) ToSlice() []byte {
 func (u Uint256) String() string {
 	return hex.EncodeToString(u.ToSlice())
 }
+
+// Uint160 is a 20 byte long unsigned integer
+type Uint160 [20]uint8
diff --git a/pkg/util/uint256_test.go b/pkg/util/types_test.go
similarity index 100%
rename from pkg/util/uint256_test.go
rename to pkg/util/types_test.go