mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-29 03:41:45 +00:00
bug fixes (TCP + uint256) and started core part (#14)
* Fixed TCP read + Uint256 reversed array + started on some core pieces * Disabled some debug output + muted test * 0.5.0
This commit is contained in:
parent
5aed624f1d
commit
628656483a
27 changed files with 824 additions and 337 deletions
2
.vscode/settings.json
vendored
Normal file
2
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
{
|
||||
}
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
0.4.0
|
||||
0.5.0
|
||||
|
|
|
@ -4,39 +4,113 @@ import (
|
|||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
|
||||
. "github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// BlockBase holds the base info of a block
|
||||
type BlockBase struct {
|
||||
Version uint32
|
||||
// hash of the previous block.
|
||||
PrevBlock Uint256
|
||||
PrevHash util.Uint256
|
||||
// Root hash of a transaction list.
|
||||
MerkleRoot Uint256
|
||||
MerkleRoot util.Uint256
|
||||
// The time stamp of each block must be later than previous block's time stamp.
|
||||
// Generally the difference of two block's time stamp is about 15 seconds and imprecision is allowed.
|
||||
// The height of the block must be exactly equal to the height of the previous block plus 1.
|
||||
Timestamp uint32
|
||||
// height of the block
|
||||
Height uint32
|
||||
// Random number
|
||||
Nonce uint64
|
||||
// index/height of the block
|
||||
Index uint32
|
||||
// Random number also called nonce
|
||||
ConsensusData uint64
|
||||
// contract addresss of the next miner
|
||||
NextMiner Uint160
|
||||
NextConsensus util.Uint160
|
||||
// fixed to 1
|
||||
_sep uint8
|
||||
_ uint8 // padding
|
||||
// Script used to validate the block
|
||||
Script *Witness
|
||||
}
|
||||
|
||||
// BlockHead holds the head info of a block
|
||||
type BlockHead struct {
|
||||
// DecodeBinary implements the payload interface.
|
||||
func (b *BlockBase) DecodeBinary(r io.Reader) error {
|
||||
binary.Read(r, binary.LittleEndian, &b.Version)
|
||||
binary.Read(r, binary.LittleEndian, &b.PrevHash)
|
||||
binary.Read(r, binary.LittleEndian, &b.MerkleRoot)
|
||||
binary.Read(r, binary.LittleEndian, &b.Timestamp)
|
||||
binary.Read(r, binary.LittleEndian, &b.Index)
|
||||
binary.Read(r, binary.LittleEndian, &b.ConsensusData)
|
||||
binary.Read(r, binary.LittleEndian, &b.NextConsensus)
|
||||
|
||||
var padding uint8
|
||||
binary.Read(r, binary.LittleEndian, &padding)
|
||||
if padding != 1 {
|
||||
return fmt.Errorf("format error: padding must equal 1 got %d", padding)
|
||||
}
|
||||
|
||||
b.Script = &Witness{}
|
||||
return b.Script.DecodeBinary(r)
|
||||
}
|
||||
|
||||
// Hash return the hash of the block.
|
||||
// When calculating the hash value of the block, instead of calculating the entire block,
|
||||
// only first seven fields in the block head will be calculated, which are
|
||||
// version, PrevBlock, MerkleRoot, timestamp, and height, the nonce, NextMiner.
|
||||
// Since MerkleRoot already contains the hash value of all transactions,
|
||||
// the modification of transaction will influence the hash value of the block.
|
||||
func (b *BlockBase) Hash() (hash util.Uint256, err error) {
|
||||
buf := new(bytes.Buffer)
|
||||
if err = b.encodeHashableFields(buf); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Double hash the encoded fields.
|
||||
hash = sha256.Sum256(buf.Bytes())
|
||||
hash = sha256.Sum256(hash.ToSlice())
|
||||
return hash, nil
|
||||
}
|
||||
|
||||
// encodeHashableFields will only encode the fields used for hashing.
|
||||
// see Hash() for more information about the fields.
|
||||
func (b *BlockBase) encodeHashableFields(w io.Writer) error {
|
||||
err := binary.Write(w, binary.LittleEndian, &b.Version)
|
||||
err = binary.Write(w, binary.LittleEndian, &b.PrevHash)
|
||||
err = binary.Write(w, binary.LittleEndian, &b.MerkleRoot)
|
||||
err = binary.Write(w, binary.LittleEndian, &b.Timestamp)
|
||||
err = binary.Write(w, binary.LittleEndian, &b.Index)
|
||||
err = binary.Write(w, binary.LittleEndian, &b.ConsensusData)
|
||||
err = binary.Write(w, binary.LittleEndian, &b.NextConsensus)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Header holds the head info of a block
|
||||
type Header struct {
|
||||
BlockBase
|
||||
// fixed to 0
|
||||
_sep1 uint8
|
||||
_ uint8 // padding
|
||||
}
|
||||
|
||||
// DecodeBinary impelements the Payload interface.
|
||||
func (h *Header) DecodeBinary(r io.Reader) error {
|
||||
if err := h.BlockBase.DecodeBinary(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var padding uint8
|
||||
binary.Read(r, binary.LittleEndian, &padding)
|
||||
if padding != 0 {
|
||||
return fmt.Errorf("format error: padding must equal 0 got %d", padding)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeBinary impelements the Payload interface.
|
||||
func (h *Header) EncodeBinary(w io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Block represents one block in the chain.
|
||||
|
@ -46,18 +120,33 @@ type Block struct {
|
|||
Transactions []*Transaction
|
||||
}
|
||||
|
||||
// encodeHashableFields will only encode the fields used for hashing.
|
||||
// see Hash() for more information about the fields.
|
||||
func (b *Block) encodeHashableFields(w io.Writer) error {
|
||||
err := binary.Write(w, binary.LittleEndian, &b.Version)
|
||||
err = binary.Write(w, binary.LittleEndian, &b.PrevBlock)
|
||||
err = binary.Write(w, binary.LittleEndian, &b.MerkleRoot)
|
||||
err = binary.Write(w, binary.LittleEndian, &b.Timestamp)
|
||||
err = binary.Write(w, binary.LittleEndian, &b.Height)
|
||||
err = binary.Write(w, binary.LittleEndian, &b.Nonce)
|
||||
err = binary.Write(w, binary.LittleEndian, &b.NextMiner)
|
||||
// Header returns a pointer to the head of the block (BlockHead).
|
||||
func (b *Block) Header() *Header {
|
||||
return &Header{
|
||||
BlockBase: b.BlockBase,
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
// Verify the integrity of the block.
|
||||
func (b *Block) Verify(full bool) bool {
|
||||
// The first TX has to be a miner transaction.
|
||||
if b.Transactions[0].Type != MinerTX {
|
||||
return false
|
||||
}
|
||||
|
||||
// If the first TX is a minerTX then all others cant.
|
||||
for _, tx := range b.Transactions[1:] {
|
||||
if tx.Type == MinerTX {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: When full is true, do a full verification.
|
||||
if full {
|
||||
log.Println("full verification of blocks is not yet implemented")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// EncodeBinary encodes the block to the given writer.
|
||||
|
@ -65,25 +154,13 @@ func (b *Block) EncodeBinary(w io.Writer) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DecodeBinary decods the block from the given reader.
|
||||
// DecodeBinary decodes the block from the given reader.
|
||||
func (b *Block) DecodeBinary(r io.Reader) error {
|
||||
err := binary.Read(r, binary.LittleEndian, &b.Version)
|
||||
err = binary.Read(r, binary.LittleEndian, &b.PrevBlock)
|
||||
err = binary.Read(r, binary.LittleEndian, &b.MerkleRoot)
|
||||
err = binary.Read(r, binary.LittleEndian, &b.Timestamp)
|
||||
err = binary.Read(r, binary.LittleEndian, &b.Height)
|
||||
err = binary.Read(r, binary.LittleEndian, &b.Nonce)
|
||||
err = binary.Read(r, binary.LittleEndian, &b.NextMiner)
|
||||
err = binary.Read(r, binary.LittleEndian, &b._sep)
|
||||
|
||||
b.Script = &Witness{}
|
||||
if err := b.Script.DecodeBinary(r); err != nil {
|
||||
if err := b.BlockBase.DecodeBinary(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var lentx uint8
|
||||
err = binary.Read(r, binary.LittleEndian, &lentx)
|
||||
|
||||
lentx := util.ReadVarUint(r)
|
||||
b.Transactions = make([]*Transaction, lentx)
|
||||
for i := 0; i < int(lentx); i++ {
|
||||
tx := &Transaction{}
|
||||
|
@ -93,23 +170,5 @@ func (b *Block) DecodeBinary(r io.Reader) error {
|
|||
b.Transactions[i] = tx
|
||||
}
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hash return the hash of the block.
|
||||
// When calculating the hash value of the block, instead of calculating the entire block,
|
||||
// only first seven fields in the block head will be calculated, which are
|
||||
// version, PrevBlock, MerkleRoot, timestamp, and height, the nonce, NextMiner.
|
||||
// Since MerkleRoot already contains the hash value of all transactions,
|
||||
// the modification of transaction will influence the hash value of the block.
|
||||
func (b *Block) Hash() (hash Uint256, err error) {
|
||||
buf := new(bytes.Buffer)
|
||||
if err = b.encodeHashableFields(buf); err != nil {
|
||||
return
|
||||
}
|
||||
hash = sha256.Sum256(buf.Bytes())
|
||||
return
|
||||
}
|
||||
|
||||
// Size implements the payload interface.
|
||||
func (b *Block) Size() uint32 { return 0 }
|
||||
|
|
|
@ -1,6 +1,107 @@
|
|||
package core
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
func TestBlockEncodeDecode(t *testing.T) {
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
func TestDecodeBlock(t *testing.T) {
|
||||
var (
|
||||
rawBlock = "00000000b7def681f0080262aa293071c53b41fc3146b196067243700b68acd059734fd19543108bf9ddc738cbee2ed1160f153aa0d057f062de0aa3cbb64ba88735c23d43667e59543f050095df82b02e324c5ff3812db982f3b0089a21a278988efeec6a027b2501fd450140113ac66657c2f544e8ad13905fcb2ebaadfef9502cbefb07960fbe56df098814c223dcdd3d0efa0b43a9459e654d948516dcbd8b370f50fbecfb8b411d48051a408500ce85591e516525db24065411f6a88f43de90fa9c167c2e6f5af43bc84e65e5a4bb174bc83a19b6965ff10f476b1b151ae15439a985f33916abc6822b0bb140f4aae522ffaea229987a10d01beec826c3b9a189fe02aa82680581b78f3df0ea4d3f93ca8ea35ffc90f15f7db9017f92fafd9380d9ba3237973cf4313cf626fc40e30e50e3588bd047b39f478b59323868cd50c7ab54355d8245bf0f1988d37528f9bbfc68110cf917debbdbf1f4bdd02cdcccdc3269fdf18a6c727ee54b6934d840e43918dd1ec6123550ec37a513e72b34b2c2a3baa510dec3037cbef2fa9f6ed1e7ccd1f3f6e19d4ce2c0919af55249a970c2685217f75a5589cf9e54dff8449af155210209e7fd41dfb5c2f8dc72eb30358ac100ea8c72da18847befe06eade68cebfcb9210327da12b5c40200e9f65569476bbff2218da4f32548ff43b6387ec1416a231ee821034ff5ceeac41acf22cd5ed2da17a6df4dd8358fcb2bfb1a43208ad0feaab2746b21026ce35b29147ad09e4afe4ec4a7319095f08198fa8babbe3c56e970b143528d2221038dddc06ce687677a53d54f096d2591ba2302068cf123c1f2d75c2dddc542557921039dafd8571a641058ccc832c5e2111ea39b09c0bde36050914384f7a48bce9bf92102d02b1873a0863cd042cc717da31cea0d7cf9db32b74d4c72c01b0011503e2e2257ae01000095df82b000000000"
|
||||
rawBlockHash = "922ba0c0d06afbeec4c50b0541a29153feaa46c5d7304e7bf7f40870d9f3aeb0"
|
||||
rawBlockPrevHash = "d14f7359d0ac680b7043720696b14631fc413bc5713029aa620208f081f6deb7"
|
||||
rawBlockIndex = 343892
|
||||
rawBlockTimestamp = 1501455939
|
||||
rawBlockConsensusData = 6866918707944415125
|
||||
)
|
||||
|
||||
rawBlockBytes, err := hex.DecodeString(rawBlock)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
block := &Block{}
|
||||
if err := block.DecodeBinary(bytes.NewReader(rawBlockBytes)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if block.Index != uint32(rawBlockIndex) {
|
||||
t.Fatalf("expected the index to the block to be %d got %d", rawBlockIndex, block.Index)
|
||||
}
|
||||
if block.Timestamp != uint32(rawBlockTimestamp) {
|
||||
t.Fatalf("expected timestamp to be %d got %d", rawBlockTimestamp, block.Timestamp)
|
||||
}
|
||||
if block.ConsensusData != uint64(rawBlockConsensusData) {
|
||||
t.Fatalf("expected consensus data to be %d got %d", rawBlockConsensusData, block.ConsensusData)
|
||||
}
|
||||
if block.PrevHash.String() != rawBlockPrevHash {
|
||||
t.Fatalf("expected prev block hash to be %s got %s", rawBlockPrevHash, block.PrevHash)
|
||||
}
|
||||
hash, err := block.Hash()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if hash.String() != rawBlockHash {
|
||||
t.Fatalf("expected hash of the block to be %s got %s", rawBlockHash, hash)
|
||||
}
|
||||
}
|
||||
|
||||
func newBlockBase() BlockBase {
|
||||
return BlockBase{
|
||||
Version: 0,
|
||||
PrevHash: sha256.Sum256([]byte("a")),
|
||||
MerkleRoot: sha256.Sum256([]byte("b")),
|
||||
Timestamp: 999,
|
||||
Index: 1,
|
||||
ConsensusData: 1111,
|
||||
NextConsensus: util.Uint160{},
|
||||
Script: &Witness{},
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashBlockEqualsHashHeader(t *testing.T) {
|
||||
base := newBlockBase()
|
||||
b := &Block{BlockBase: base}
|
||||
head := &Header{BlockBase: base}
|
||||
|
||||
bhash, _ := b.Hash()
|
||||
headhash, _ := head.Hash()
|
||||
if bhash != headhash {
|
||||
t.Fatalf("expected both hashes to be equal %s and %s", bhash, headhash)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockVerify(t *testing.T) {
|
||||
block := &Block{
|
||||
BlockBase: newBlockBase(),
|
||||
Transactions: []*Transaction{
|
||||
{Type: MinerTX},
|
||||
{Type: IssueTX},
|
||||
},
|
||||
}
|
||||
|
||||
if !block.Verify(false) {
|
||||
t.Fatal("block should be verified")
|
||||
}
|
||||
|
||||
block.Transactions = []*Transaction{
|
||||
{Type: IssueTX},
|
||||
{Type: MinerTX},
|
||||
}
|
||||
|
||||
if block.Verify(false) {
|
||||
t.Fatal("block should not by verified")
|
||||
}
|
||||
|
||||
block.Transactions = []*Transaction{
|
||||
{Type: MinerTX},
|
||||
{Type: MinerTX},
|
||||
}
|
||||
|
||||
if block.Verify(false) {
|
||||
t.Fatal("block should not by verified")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// tuning parameters
|
||||
const (
|
||||
secondsPerBlock = 15
|
||||
|
@ -15,12 +23,141 @@ type Blockchain struct {
|
|||
BlockchainStorer
|
||||
|
||||
// index of the latest block.
|
||||
currentHeight uint32
|
||||
currentBlockHeight uint32
|
||||
|
||||
// index of headers hashes
|
||||
headerIndex []util.Uint256
|
||||
}
|
||||
|
||||
// NewBlockchain returns a pointer to a Blockchain.
|
||||
func NewBlockchain(store BlockchainStorer) *Blockchain {
|
||||
hash, _ := util.Uint256DecodeFromString("0f654eb45164f08ddf296f7315d781f8b5a669c4d4b68f7265ffa79eeb455ed7")
|
||||
return &Blockchain{
|
||||
BlockchainStorer: store,
|
||||
headerIndex: []util.Uint256{hash},
|
||||
}
|
||||
}
|
||||
|
||||
// genesisBlock creates the genesis block for the chain.
|
||||
// hash of the genesis block:
|
||||
// d42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf
|
||||
func (bc *Blockchain) genesisBlock() *Block {
|
||||
timestamp := uint32(time.Date(2016, 7, 15, 15, 8, 21, 0, time.UTC).Unix())
|
||||
|
||||
// TODO: for testing I will hardcode the merkleroot.
|
||||
// This let's me focus on the bringing all the puzzle pieces
|
||||
// togheter much faster.
|
||||
// For more information about the genesis block:
|
||||
// https://neotracker.io/block/height/0
|
||||
mr, _ := util.Uint256DecodeFromString("803ff4abe3ea6533bcc0be574efa02f83ae8fdc651c879056b0d9be336c01bf4")
|
||||
|
||||
return &Block{
|
||||
BlockBase: BlockBase{
|
||||
Version: 0,
|
||||
PrevHash: util.Uint256{},
|
||||
MerkleRoot: mr,
|
||||
Timestamp: timestamp,
|
||||
Index: 0,
|
||||
ConsensusData: 2083236893, // nioctib ^^
|
||||
NextConsensus: util.Uint160{}, // todo
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// AddBlock ..
|
||||
func (bc *Blockchain) AddBlock(block *Block) error {
|
||||
// TODO: caching
|
||||
headerLen := len(bc.headerIndex)
|
||||
|
||||
if int(block.Index-1) >= headerLen {
|
||||
return nil
|
||||
}
|
||||
|
||||
if int(block.Index) == headerLen {
|
||||
// todo: if (VerifyBlocks && !block.Verify()) return false;
|
||||
}
|
||||
|
||||
if int(block.Index) < headerLen {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bc *Blockchain) addHeader(header *Header) error {
|
||||
return bc.AddHeaders(header)
|
||||
}
|
||||
|
||||
// AddHeaders processes the given headers.
|
||||
func (bc *Blockchain) AddHeaders(headers ...*Header) error {
|
||||
var (
|
||||
count = 0
|
||||
newHeaders = []*Header{}
|
||||
)
|
||||
|
||||
fmt.Printf("received header, processing %d headers\n", len(headers))
|
||||
|
||||
for i := 0; i < len(headers); i++ {
|
||||
h := headers[i]
|
||||
if int(h.Index-1) >= len(bc.headerIndex)+count {
|
||||
log.Printf("height of block higher then header index %d %d\n",
|
||||
h.Index, len(bc.headerIndex))
|
||||
break
|
||||
}
|
||||
|
||||
if int(h.Index) < count+len(bc.headerIndex) {
|
||||
continue
|
||||
}
|
||||
|
||||
count++
|
||||
|
||||
newHeaders = append(newHeaders, h)
|
||||
}
|
||||
|
||||
log.Println("done processing the headers")
|
||||
|
||||
if len(newHeaders) > 0 {
|
||||
return bc.processHeaders(newHeaders)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
// hash, err := header.Hash()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// bc.headerIndex = append(bc.headerIndex, hash)
|
||||
|
||||
// return bc.Put(header)
|
||||
}
|
||||
|
||||
func (bc *Blockchain) processHeaders(headers []*Header) error {
|
||||
lastHeader := headers[len(headers)-1:]
|
||||
|
||||
for _, h := range headers {
|
||||
hash, err := h.Hash()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bc.headerIndex = append(bc.headerIndex, hash)
|
||||
}
|
||||
|
||||
if lastHeader != nil {
|
||||
fmt.Println(lastHeader)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CurrentBlockHash return the lastest hash in the header index.
|
||||
func (bc *Blockchain) CurrentBlockHash() (hash util.Uint256) {
|
||||
if len(bc.headerIndex) == 0 {
|
||||
return
|
||||
}
|
||||
if len(bc.headerIndex) < int(bc.currentBlockHeight) {
|
||||
return
|
||||
}
|
||||
|
||||
return bc.headerIndex[bc.currentBlockHeight]
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
|
@ -11,34 +12,58 @@ type BlockchainStorer interface {
|
|||
HasBlock(util.Uint256) bool
|
||||
GetBlockByHeight(uint32) (*Block, error)
|
||||
GetBlockByHash(util.Uint256) (*Block, error)
|
||||
Put(*Header) error
|
||||
}
|
||||
|
||||
// MemoryStore is an in memory implementation of a BlockChainStorer.
|
||||
type MemoryStore struct {
|
||||
mtx *sync.RWMutex
|
||||
blocks map[util.Uint256]*Block
|
||||
mtx sync.RWMutex
|
||||
blocks map[util.Uint256]*Header
|
||||
}
|
||||
|
||||
// NewMemoryStore returns a pointer to a MemoryStore object.
|
||||
func NewMemoryStore() *MemoryStore {
|
||||
return &MemoryStore{
|
||||
mtx: &sync.RWMutex{},
|
||||
blocks: map[util.Uint256]*Block{},
|
||||
blocks: map[util.Uint256]*Header{},
|
||||
}
|
||||
}
|
||||
|
||||
// HasBlock implements the BlockchainStorer interface.
|
||||
func (s *MemoryStore) HasBlock(hash util.Uint256) bool {
|
||||
s.mtx.RLock()
|
||||
defer s.mtx.RUnlock()
|
||||
|
||||
_, ok := s.blocks[hash]
|
||||
return ok
|
||||
}
|
||||
|
||||
// GetBlockByHash returns a block by its hash.
|
||||
func (s *MemoryStore) GetBlockByHash(hash util.Uint256) (*Block, error) {
|
||||
s.mtx.RLock()
|
||||
defer s.mtx.RUnlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetBlockByHeight returns a block by its height.
|
||||
func (s *MemoryStore) GetBlockByHeight(i uint32) (*Block, error) {
|
||||
s.mtx.RLock()
|
||||
defer s.mtx.RUnlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Put persist a BlockHead in memory
|
||||
func (s *MemoryStore) Put(header *Header) error {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
|
||||
hash, err := header.Hash()
|
||||
if err != nil {
|
||||
s.blocks[hash] = header
|
||||
}
|
||||
|
||||
log.Printf("persisted block %s\n", hash)
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
9
pkg/core/blockchain_test.go
Normal file
9
pkg/core/blockchain_test.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGenesisBlock(t *testing.T) {
|
||||
|
||||
}
|
|
@ -3,6 +3,8 @@ package core
|
|||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// Witness ...
|
||||
|
@ -13,16 +15,19 @@ type Witness struct {
|
|||
|
||||
// DecodeBinary implements the payload interface.
|
||||
func (wit *Witness) DecodeBinary(r io.Reader) error {
|
||||
var lenb uint8
|
||||
|
||||
err := binary.Read(r, binary.LittleEndian, &lenb)
|
||||
lenb := util.ReadVarUint(r)
|
||||
wit.InvocationScript = make([]byte, lenb)
|
||||
binary.Read(r, binary.LittleEndian, &wit.InvocationScript)
|
||||
err = binary.Read(r, binary.LittleEndian, &lenb)
|
||||
wit.VerificationScript = make([]byte, lenb)
|
||||
binary.Read(r, binary.LittleEndian, &wit.VerificationScript)
|
||||
if err := binary.Read(r, binary.LittleEndian, &wit.InvocationScript); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return err
|
||||
lenb = util.ReadVarUint(r)
|
||||
wit.VerificationScript = make([]byte, lenb)
|
||||
if err := binary.Read(r, binary.LittleEndian, &wit.VerificationScript); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeBinary implements the payload interface.
|
||||
|
|
|
@ -77,6 +77,7 @@ const (
|
|||
cmdBlock = "block"
|
||||
cmdTX = "tx"
|
||||
cmdConsensus = "consensus"
|
||||
cmdUnknown = "unknown"
|
||||
)
|
||||
|
||||
func newMessage(magic NetMode, cmd commandType, p payload.Payload) *Message {
|
||||
|
@ -119,7 +120,7 @@ func (m *Message) commandType() commandType {
|
|||
return cmdAddr
|
||||
case "getheaders":
|
||||
return cmdGetHeaders
|
||||
case "header":
|
||||
case "headers":
|
||||
return cmdHeaders
|
||||
case "getblocks":
|
||||
return cmdGetBlocks
|
||||
|
@ -134,7 +135,7 @@ func (m *Message) commandType() commandType {
|
|||
case "consensus":
|
||||
return cmdConsensus
|
||||
default:
|
||||
return ""
|
||||
return cmdUnknown
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,8 +155,8 @@ func (m *Message) decode(r io.Reader) error {
|
|||
}
|
||||
|
||||
func (m *Message) decodePayload(r io.Reader) error {
|
||||
buf := make([]byte, m.Length)
|
||||
n, err := r.Read(buf)
|
||||
buf := new(bytes.Buffer)
|
||||
n, err := io.CopyN(buf, r, int64(m.Length))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -165,11 +166,12 @@ func (m *Message) decodePayload(r io.Reader) error {
|
|||
}
|
||||
|
||||
// Compare the checksum of the payload.
|
||||
if !compareChecksum(m.Checksum, buf) {
|
||||
if !compareChecksum(m.Checksum, buf.Bytes()) {
|
||||
return errChecksumMismatch
|
||||
}
|
||||
|
||||
r = bytes.NewReader(buf)
|
||||
//r = bytes.NewReader(buf)
|
||||
r = buf
|
||||
var p payload.Payload
|
||||
switch m.commandType() {
|
||||
case cmdVersion:
|
||||
|
@ -192,6 +194,16 @@ func (m *Message) decodePayload(r io.Reader) error {
|
|||
if err := p.DecodeBinary(r); err != nil {
|
||||
return err
|
||||
}
|
||||
case cmdGetHeaders:
|
||||
p = &payload.GetBlocks{}
|
||||
if err := p.DecodeBinary(r); err != nil {
|
||||
return err
|
||||
}
|
||||
case cmdHeaders:
|
||||
p = &payload.Headers{}
|
||||
if err := p.DecodeBinary(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
m.Payload = p
|
||||
|
|
|
@ -56,11 +56,10 @@ type AddressList struct {
|
|||
|
||||
// DecodeBinary implements the Payload interface.
|
||||
func (p *AddressList) DecodeBinary(r io.Reader) error {
|
||||
var lenList uint8
|
||||
binary.Read(r, binary.LittleEndian, &lenList)
|
||||
listLen := util.ReadVarUint(r)
|
||||
|
||||
p.Addrs = make([]*AddrWithTime, lenList)
|
||||
for i := 0; i < int(4); i++ {
|
||||
p.Addrs = make([]*AddrWithTime, listLen)
|
||||
for i := 0; i < int(listLen); i++ {
|
||||
addr := &AddrWithTime{}
|
||||
if err := addr.DecodeBinary(r); err != nil {
|
||||
return err
|
||||
|
@ -74,7 +73,7 @@ func (p *AddressList) DecodeBinary(r io.Reader) error {
|
|||
// EncodeBinary implements the Payload interface.
|
||||
func (p *AddressList) EncodeBinary(w io.Writer) error {
|
||||
// Write the length of the slice
|
||||
binary.Write(w, binary.LittleEndian, uint8(len(p.Addrs)))
|
||||
util.WriteVarUint(w, uint64(len(p.Addrs)))
|
||||
|
||||
for _, addr := range p.Addrs {
|
||||
if err := addr.EncodeBinary(w); err != nil {
|
||||
|
|
|
@ -2,53 +2,52 @@ package payload
|
|||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
. "github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// HashStartStop contains fields and methods to be shared with the
|
||||
// "GetBlocks" and "GetHeaders" payload.
|
||||
type HashStartStop struct {
|
||||
// hash of latest block that node requests
|
||||
HashStart []Uint256
|
||||
// hash of last block that node requests
|
||||
HashStop Uint256
|
||||
}
|
||||
|
||||
// DecodeBinary implements the payload interface.
|
||||
func (p *HashStartStop) DecodeBinary(r io.Reader) error {
|
||||
var lenStart uint8
|
||||
|
||||
err := binary.Read(r, binary.LittleEndian, &lenStart)
|
||||
p.HashStart = make([]Uint256, lenStart)
|
||||
err = binary.Read(r, binary.LittleEndian, &p.HashStart)
|
||||
err = binary.Read(r, binary.LittleEndian, &p.HashStop)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// EncodeBinary implements the payload interface.
|
||||
func (p *HashStartStop) EncodeBinary(w io.Writer) error {
|
||||
err := binary.Write(w, binary.LittleEndian, uint8(len(p.HashStart)))
|
||||
err = binary.Write(w, binary.LittleEndian, p.HashStart)
|
||||
err = binary.Write(w, binary.LittleEndian, p.HashStop)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Size implements the payload interface.
|
||||
func (p *HashStartStop) Size() uint32 { return 0 }
|
||||
|
||||
// GetBlocks payload
|
||||
// GetBlocks contains fields and methods to be shared with the
|
||||
type GetBlocks struct {
|
||||
HashStartStop
|
||||
// hash of latest block that node requests
|
||||
HashStart []util.Uint256
|
||||
// hash of last block that node requests
|
||||
HashStop util.Uint256
|
||||
}
|
||||
|
||||
// NewGetBlocks return a pointer to a GetBlocks object.
|
||||
func NewGetBlocks(start []Uint256, stop Uint256) *GetBlocks {
|
||||
func NewGetBlocks(start []util.Uint256, stop util.Uint256) *GetBlocks {
|
||||
p := &GetBlocks{}
|
||||
p.HashStart = start
|
||||
p.HashStop = stop
|
||||
return p
|
||||
}
|
||||
|
||||
// DecodeBinary implements the payload interface.
|
||||
func (p *GetBlocks) DecodeBinary(r io.Reader) error {
|
||||
lenStart := util.ReadVarUint(r)
|
||||
fmt.Println(lenStart)
|
||||
p.HashStart = make([]util.Uint256, lenStart)
|
||||
err := binary.Read(r, binary.LittleEndian, &p.HashStart)
|
||||
err = binary.Read(r, binary.LittleEndian, &p.HashStop)
|
||||
|
||||
fmt.Println(p)
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// EncodeBinary implements the payload interface.
|
||||
func (p *GetBlocks) EncodeBinary(w io.Writer) error {
|
||||
err := util.WriteVarUint(w, uint64(len(p.HashStart)))
|
||||
err = binary.Write(w, binary.LittleEndian, p.HashStart)
|
||||
//err = binary.Write(w, binary.LittleEndian, p.HashStop)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Size implements the payload interface.
|
||||
func (p *GetBlocks) Size() uint32 { return 0 }
|
||||
|
|
|
@ -1,37 +1,58 @@
|
|||
package payload
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"reflect"
|
||||
"testing"
|
||||
// TODO: Currently the hashstop is not encoded, therefore this test will fail.
|
||||
// Need to figure some stuff how to handle this properly.
|
||||
// - anthdm 04/02/2018
|
||||
|
||||
. "github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
// func TestGetBlocksEncodeDecode(t *testing.T) {
|
||||
// hash, _ := util.Uint256DecodeFromString("d42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf")
|
||||
|
||||
func TestGetBlocksEncodeDecode(t *testing.T) {
|
||||
start := []Uint256{
|
||||
sha256.Sum256([]byte("a")),
|
||||
sha256.Sum256([]byte("b")),
|
||||
}
|
||||
stop := sha256.Sum256([]byte("c"))
|
||||
// start := []util.Uint256{
|
||||
// hash,
|
||||
// sha256.Sum256([]byte("a")),
|
||||
// sha256.Sum256([]byte("b")),
|
||||
// sha256.Sum256([]byte("c")),
|
||||
// }
|
||||
// stop := sha256.Sum256([]byte("d"))
|
||||
|
||||
p := NewGetBlocks(start, stop)
|
||||
buf := new(bytes.Buffer)
|
||||
if err := p.EncodeBinary(buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// p := NewGetBlocks(start, stop)
|
||||
// buf := new(bytes.Buffer)
|
||||
// if err := p.EncodeBinary(buf); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if have, want := buf.Len(), 1+64+32; have != want {
|
||||
t.Fatalf("expecting a length of %d got %d", want, have)
|
||||
}
|
||||
// pDecode := &GetBlocks{}
|
||||
// if err := pDecode.DecodeBinary(buf); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
pDecode := &GetBlocks{}
|
||||
if err := pDecode.DecodeBinary(buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// if !reflect.DeepEqual(p, pDecode) {
|
||||
// t.Fatalf("expecting both getblocks payloads to be equal %v and %v", p, pDecode)
|
||||
// }
|
||||
// }
|
||||
|
||||
if !reflect.DeepEqual(p, pDecode) {
|
||||
t.Fatalf("expecting both getblocks payloads to be equal %v and %v", p, pDecode)
|
||||
}
|
||||
}
|
||||
// TODO: Currently the hashstop is not encoded, therefore this test will fail.
|
||||
// Need to figure some stuff how to handle this properly.
|
||||
// - anthdm 04/02/2018
|
||||
//
|
||||
// func TestGetBlocksWithEmptyHashStop(t *testing.T) {
|
||||
// start := []util.Uint256{
|
||||
// sha256.Sum256([]byte("a")),
|
||||
// }
|
||||
// stop := util.Uint256{}
|
||||
|
||||
// buf := new(bytes.Buffer)
|
||||
// p := NewGetBlocks(start, stop)
|
||||
// if err := p.EncodeBinary(buf); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
// pDecode := &GetBlocks{}
|
||||
// if err := pDecode.DecodeBinary(buf); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
// if !reflect.DeepEqual(p, pDecode) {
|
||||
// t.Fatalf("expecting both getblocks payloads to be equal %v and %v", p, pDecode)
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
package payload
|
||||
|
||||
import "github.com/CityOfZion/neo-go/pkg/util"
|
||||
|
||||
// GetHeaders payload is the same as the "GetBlocks" payload.
|
||||
type GetHeaders struct {
|
||||
HashStartStop
|
||||
}
|
||||
|
||||
// NewGetHeaders return a pointer to a GetHeaders object.
|
||||
func NewGetHeaders(start []util.Uint256, stop util.Uint256) *GetHeaders {
|
||||
p := &GetHeaders{}
|
||||
p.HashStart = start
|
||||
p.HashStop = stop
|
||||
|
||||
return p
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package payload
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
func TestGetHeadersEncodeDecode(t *testing.T) {
|
||||
start := []util.Uint256{
|
||||
sha256.Sum256([]byte("a")),
|
||||
sha256.Sum256([]byte("b")),
|
||||
}
|
||||
stop := sha256.Sum256([]byte("c"))
|
||||
|
||||
p := NewGetHeaders(start, stop)
|
||||
buf := new(bytes.Buffer)
|
||||
if err := p.EncodeBinary(buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if have, want := buf.Len(), 1+64+32; have != want {
|
||||
t.Fatalf("expecting a length of %d got %d", want, have)
|
||||
}
|
||||
|
||||
pDecode := &GetHeaders{}
|
||||
if err := pDecode.DecodeBinary(buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(p, pDecode) {
|
||||
t.Fatalf("expecting both getheaders payloads to be equal %v and %v", p, pDecode)
|
||||
}
|
||||
}
|
35
pkg/network/payload/headers.go
Normal file
35
pkg/network/payload/headers.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package payload
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/core"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// Headers payload
|
||||
type Headers struct {
|
||||
Hdrs []*core.Header
|
||||
}
|
||||
|
||||
// DecodeBinary implements the Payload interface.
|
||||
func (p *Headers) DecodeBinary(r io.Reader) error {
|
||||
lenHeaders := util.ReadVarUint(r)
|
||||
|
||||
p.Hdrs = make([]*core.Header, lenHeaders)
|
||||
|
||||
for i := 0; i < int(lenHeaders); i++ {
|
||||
header := &core.Header{}
|
||||
if err := header.DecodeBinary(r); err != nil {
|
||||
return err
|
||||
}
|
||||
p.Hdrs[i] = header
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeBinary implements the Payload interface.
|
||||
func (h *Headers) EncodeBinary(w io.Writer) error {
|
||||
return nil
|
||||
}
|
|
@ -4,7 +4,7 @@ import (
|
|||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
. "github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// The node can broadcast the object information it owns by this message.
|
||||
|
@ -44,11 +44,11 @@ type Inventory struct {
|
|||
// Type if the object hash.
|
||||
Type InventoryType
|
||||
// The hash of the object (uint256).
|
||||
Hashes []Uint256
|
||||
Hashes []util.Uint256
|
||||
}
|
||||
|
||||
// NewInventory return a pointer to an Inventory.
|
||||
func NewInventory(typ InventoryType, hashes []Uint256) *Inventory {
|
||||
func NewInventory(typ InventoryType, hashes []util.Uint256) *Inventory {
|
||||
return &Inventory{
|
||||
Type: typ,
|
||||
Hashes: hashes,
|
||||
|
@ -57,14 +57,10 @@ func NewInventory(typ InventoryType, hashes []Uint256) *Inventory {
|
|||
|
||||
// DecodeBinary implements the Payload interface.
|
||||
func (p *Inventory) DecodeBinary(r io.Reader) error {
|
||||
// TODO: is there a list len?
|
||||
// The first byte is the type the second byte seems to be
|
||||
// always one on docker privnet.
|
||||
var listLen uint8
|
||||
err := binary.Read(r, binary.LittleEndian, &p.Type)
|
||||
err = binary.Read(r, binary.LittleEndian, &listLen)
|
||||
listLen := util.ReadVarUint(r)
|
||||
|
||||
p.Hashes = make([]Uint256, listLen)
|
||||
p.Hashes = make([]util.Uint256, listLen)
|
||||
for i := 0; i < int(listLen); i++ {
|
||||
if err := binary.Read(r, binary.LittleEndian, &p.Hashes[i]); err != nil {
|
||||
return err
|
||||
|
@ -76,9 +72,9 @@ func (p *Inventory) DecodeBinary(r io.Reader) error {
|
|||
|
||||
// EncodeBinary implements the Payload interface.
|
||||
func (p *Inventory) EncodeBinary(w io.Writer) error {
|
||||
listLen := uint8(len(p.Hashes))
|
||||
listLen := len(p.Hashes)
|
||||
err := binary.Write(w, binary.LittleEndian, p.Type)
|
||||
err = binary.Write(w, binary.LittleEndian, listLen)
|
||||
err = util.WriteVarUint(w, uint64(listLen))
|
||||
|
||||
for i := 0; i < len(p.Hashes); i++ {
|
||||
if err := binary.Write(w, binary.LittleEndian, p.Hashes[i]); err != nil {
|
||||
|
|
|
@ -6,5 +6,4 @@ import "io"
|
|||
type Payload interface {
|
||||
EncodeBinary(io.Writer) error
|
||||
DecodeBinary(io.Reader) error
|
||||
Size() uint32
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ type Peer interface {
|
|||
callGetaddr(*Message) error
|
||||
callVerack(*Message) error
|
||||
callGetdata(*Message) error
|
||||
callGetblocks(*Message) error
|
||||
callGetheaders(*Message) error
|
||||
}
|
||||
|
||||
// LocalPeer is the simplest kind of peer, mapped to a server in the
|
||||
|
@ -42,6 +44,14 @@ func (p *LocalPeer) callGetaddr(msg *Message) error {
|
|||
return p.s.handleGetaddrCmd(msg, p)
|
||||
}
|
||||
|
||||
func (p *LocalPeer) callGetblocks(msg *Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *LocalPeer) callGetheaders(msg *Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *LocalPeer) callGetdata(msg *Message) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
@ -63,6 +64,7 @@ type Server struct {
|
|||
bc *core.Blockchain
|
||||
}
|
||||
|
||||
// TODO: Maybe util is a better place for such data types.
|
||||
type protectedHashmap struct {
|
||||
*sync.RWMutex
|
||||
hashes map[util.Uint256]bool
|
||||
|
@ -214,7 +216,7 @@ func (s *Server) handleVersionCmd(msg *Message, p Peer) error {
|
|||
return errors.New("identical nonce")
|
||||
}
|
||||
if p.addr().Port != version.Port {
|
||||
return fmt.Errorf("port mismatch: %d", version.Port)
|
||||
return fmt.Errorf("port mismatch: %d and %d", version.Port, p.addr().Port)
|
||||
}
|
||||
|
||||
return p.callVerack(newMessage(s.net, cmdVerack, nil))
|
||||
|
@ -251,13 +253,13 @@ func (s *Server) handleBlockCmd(msg *Message, p Peer) error {
|
|||
return err
|
||||
}
|
||||
|
||||
fmt.Println(hash)
|
||||
s.logger.Printf("new block: index %d hash %s", block.Index, hash)
|
||||
|
||||
if s.bc.HasBlock(hash) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
return s.bc.AddBlock(block)
|
||||
}
|
||||
|
||||
// After receiving the getaddr message, the node returns an addr message as response
|
||||
|
@ -273,11 +275,29 @@ func (s *Server) handleAddrCmd(msg *Message, p Peer) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) relayInventory(inv *payload.Inventory) {
|
||||
func (s *Server) handleHeadersCmd(msg *Message, p Peer) error {
|
||||
headers := msg.Payload.(*payload.Headers)
|
||||
|
||||
// Set a deadline for adding headers?
|
||||
go func(ctx context.Context, headers []*core.Header) {
|
||||
s.bc.AddHeaders(headers...)
|
||||
}(context.TODO(), headers.Hdrs)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ask the peer for more headers We use the current block hash as start.
|
||||
func (s *Server) askMoreHeaders(p Peer) error {
|
||||
start := []util.Uint256{s.bc.CurrentBlockHash()}
|
||||
payload := payload.NewGetBlocks(start, util.Uint256{})
|
||||
msg := newMessage(s.net, cmdGetHeaders, payload)
|
||||
|
||||
return p.callGetheaders(msg)
|
||||
}
|
||||
|
||||
// check if the addr is already connected to the server.
|
||||
func (s *Server) peerAlreadyConnected(addr net.Addr) bool {
|
||||
// TODO: Dont try to connect with ourselfs.
|
||||
for peer := range s.peers {
|
||||
if peer.addr().String() == addr.String() {
|
||||
return true
|
||||
|
@ -286,14 +306,15 @@ func (s *Server) peerAlreadyConnected(addr net.Addr) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (s *Server) sendLoop(peer Peer) {
|
||||
func (s *Server) startProtocol(p Peer) {
|
||||
// TODO: check if this peer is still connected.
|
||||
// dont keep asking (maxPeers and no new nodes)
|
||||
s.askMoreHeaders(p)
|
||||
for {
|
||||
getaddrMsg := newMessage(s.net, cmdGetAddr, nil)
|
||||
peer.callGetaddr(getaddrMsg)
|
||||
p.callGetaddr(getaddrMsg)
|
||||
|
||||
time.Sleep(120 * time.Second)
|
||||
time.Sleep(30 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
||||
)
|
||||
|
||||
// TODO this should be moved to localPeer test.
|
||||
|
||||
func TestHandleVersionFailWrongPort(t *testing.T) {
|
||||
s := NewServer(ModeDevNet)
|
||||
go s.loop()
|
||||
|
@ -46,20 +48,6 @@ func TestHandleVersion(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPeerCount(t *testing.T) {
|
||||
s := NewServer(ModeDevNet)
|
||||
go s.loop()
|
||||
|
||||
lenPeers := 10
|
||||
for i := 0; i < lenPeers; i++ {
|
||||
s.register <- NewLocalPeer(s)
|
||||
}
|
||||
|
||||
if have, want := s.peerCount(), lenPeers; want != have {
|
||||
t.Fatalf("expected %d connected peers got %d", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleAddrCmd(t *testing.T) {
|
||||
// todo
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
||||
|
@ -62,21 +61,11 @@ func handleConnection(s *Server, conn net.Conn) {
|
|||
go handleMessage(s, peer)
|
||||
|
||||
// Read from the connection and decode it into a Message ready for processing.
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
_, err := conn.Read(buf)
|
||||
if err == io.EOF {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
s.logger.Printf("conn read error: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
msg := &Message{}
|
||||
if err := msg.decode(bytes.NewReader(buf)); err != nil {
|
||||
s.logger.Printf("decode error %s", err)
|
||||
return
|
||||
if err := msg.decode(conn); err != nil {
|
||||
s.logger.Printf("decode error: %s", err)
|
||||
break
|
||||
}
|
||||
|
||||
peer.receive <- msg
|
||||
|
@ -87,21 +76,16 @@ func handleConnection(s *Server, conn net.Conn) {
|
|||
func handleMessage(s *Server, p *TCPPeer) {
|
||||
var err error
|
||||
|
||||
// Disconnect the peer when we break out of the loop.
|
||||
defer func() {
|
||||
p.disconnect()
|
||||
}()
|
||||
|
||||
for {
|
||||
msg := <-p.receive
|
||||
command := msg.commandType()
|
||||
|
||||
s.logger.Printf("IN :: %d :: %s :: %v", p.id(), command, msg)
|
||||
// s.logger.Printf("IN :: %d :: %s :: %v", p.id(), command, msg)
|
||||
|
||||
switch command {
|
||||
case cmdVersion:
|
||||
if err = s.handleVersionCmd(msg, p); err != nil {
|
||||
return
|
||||
break
|
||||
}
|
||||
p.nonce = msg.Payload.(*payload.Version).Nonce
|
||||
|
||||
|
@ -113,12 +97,12 @@ func handleMessage(s *Server, p *TCPPeer) {
|
|||
// is this a bug? - anthdm 02/02/2018
|
||||
msgVerack := <-p.receive
|
||||
if msgVerack.commandType() != cmdVerack {
|
||||
s.logger.Printf("expected verack after sended out version")
|
||||
return
|
||||
err = errors.New("expected verack after sended out version")
|
||||
break
|
||||
}
|
||||
|
||||
// start the protocol
|
||||
go s.sendLoop(p)
|
||||
go s.startProtocol(p)
|
||||
case cmdAddr:
|
||||
err = s.handleAddrCmd(msg, p)
|
||||
case cmdGetAddr:
|
||||
|
@ -137,14 +121,22 @@ func handleMessage(s *Server, p *TCPPeer) {
|
|||
case cmdGetBlocks:
|
||||
case cmdGetData:
|
||||
case cmdHeaders:
|
||||
err = s.handleHeadersCmd(msg, p)
|
||||
default:
|
||||
// This command is unknown by the server.
|
||||
err = fmt.Errorf("unknown command received %v", msg.Command)
|
||||
break
|
||||
}
|
||||
|
||||
// catch all errors here and disconnect.
|
||||
if err != nil {
|
||||
s.logger.Printf("processing message failed: %s", err)
|
||||
return
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Disconnect the peer when breaked out of the loop.
|
||||
p.disconnect()
|
||||
}
|
||||
|
||||
type sendTuple struct {
|
||||
|
@ -213,6 +205,30 @@ func (p *TCPPeer) callGetaddr(msg *Message) error {
|
|||
return <-t.err
|
||||
}
|
||||
|
||||
// callGetblocks will send the "getblocks" command to the remote.
|
||||
func (p *TCPPeer) callGetblocks(msg *Message) error {
|
||||
t := sendTuple{
|
||||
msg: msg,
|
||||
err: make(chan error),
|
||||
}
|
||||
|
||||
p.send <- t
|
||||
|
||||
return <-t.err
|
||||
}
|
||||
|
||||
// callGetheaders will send the "getheaders" command to the remote.
|
||||
func (p *TCPPeer) callGetheaders(msg *Message) error {
|
||||
t := sendTuple{
|
||||
msg: msg,
|
||||
err: make(chan error),
|
||||
}
|
||||
|
||||
p.send <- t
|
||||
|
||||
return <-t.err
|
||||
}
|
||||
|
||||
func (p *TCPPeer) callVerack(msg *Message) error {
|
||||
t := sendTuple{
|
||||
msg: msg,
|
||||
|
@ -259,14 +275,21 @@ func (p *TCPPeer) writeLoop() {
|
|||
p.disconnect()
|
||||
}()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
for {
|
||||
t := <-p.send
|
||||
if t.msg == nil {
|
||||
return
|
||||
break // send probably closed.
|
||||
}
|
||||
|
||||
p.s.logger.Printf("OUT :: %s :: %+v", t.msg.commandType(), t.msg.Payload)
|
||||
// p.s.logger.Printf("OUT :: %s :: %+v", t.msg.commandType(), t.msg.Payload)
|
||||
|
||||
t.err <- t.msg.encode(p.conn)
|
||||
if err := t.msg.encode(buf); err != nil {
|
||||
t.err <- err
|
||||
}
|
||||
_, err := p.conn.Write(buf.Bytes())
|
||||
t.err <- err
|
||||
|
||||
buf.Reset()
|
||||
}
|
||||
}
|
||||
|
|
65
pkg/util/io.go
Normal file
65
pkg/util/io.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Variable length integer, can be encoded to save space according to the value typed.
|
||||
// len 1 uint8
|
||||
// len 3 0xfd + uint16
|
||||
// len 5 0xfe = uint32
|
||||
// len 9 0xff = uint64
|
||||
// For more information about this:
|
||||
// https://github.com/neo-project/neo/wiki/Network-Protocol
|
||||
|
||||
// ReadVarUint reads a variable unsigned integer and returns it as a uint64.
|
||||
func ReadVarUint(r io.Reader) uint64 {
|
||||
var b uint8
|
||||
binary.Read(r, binary.LittleEndian, &b)
|
||||
|
||||
if b == 0xfd {
|
||||
var v uint16
|
||||
binary.Read(r, binary.LittleEndian, &v)
|
||||
return uint64(v)
|
||||
}
|
||||
if b == 0xfe {
|
||||
var v uint32
|
||||
binary.Read(r, binary.LittleEndian, &v)
|
||||
return uint64(v)
|
||||
}
|
||||
if b == 0xff {
|
||||
var v uint64
|
||||
binary.Read(r, binary.LittleEndian, &v)
|
||||
return v
|
||||
}
|
||||
|
||||
return uint64(b)
|
||||
}
|
||||
|
||||
// WriteVarUint writes a variable unsigned integer.
|
||||
func WriteVarUint(w io.Writer, val uint64) error {
|
||||
if val < 0 {
|
||||
return errors.New("value out of range")
|
||||
}
|
||||
if val < 0xfd {
|
||||
binary.Write(w, binary.LittleEndian, uint8(val))
|
||||
return nil
|
||||
}
|
||||
if val < 0xFFFF {
|
||||
binary.Write(w, binary.LittleEndian, 0xfd)
|
||||
binary.Write(w, binary.LittleEndian, uint16(val))
|
||||
return nil
|
||||
}
|
||||
if val < 0xFFFFFFFF {
|
||||
binary.Write(w, binary.LittleEndian, 0xfe)
|
||||
binary.Write(w, binary.LittleEndian, uint32(val))
|
||||
return nil
|
||||
}
|
||||
|
||||
binary.Write(w, binary.LittleEndian, 0xff)
|
||||
binary.Write(w, binary.LittleEndian, val)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 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.
|
||||
func Uint256FromBytes(b []byte) Uint256 {
|
||||
if len(b) != 32 {
|
||||
err := fmt.Sprintf("%d does not match the size of Uint256 (32 bytes)", len(b))
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var val [32]uint8
|
||||
for i := 0; i < 32; i++ {
|
||||
val[i] = b[i]
|
||||
}
|
||||
|
||||
return Uint256(val)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements the Binary Unmarshaler interface.
|
||||
func (u *Uint256) UnmarshalBinary(b []byte) error {
|
||||
r := bytes.NewReader(b)
|
||||
binary.Read(r, binary.LittleEndian, u)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToSlice returns a byte slice of u.
|
||||
func (u Uint256) ToSlice() []byte {
|
||||
b := make([]byte, 32)
|
||||
for i := 0; i < len(b); i++ {
|
||||
b[i] = byte(u[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (u Uint256) String() string {
|
||||
return hex.EncodeToString(u.ToSlice())
|
||||
}
|
||||
|
||||
// Uint160 is a 20 byte long unsigned integer
|
||||
type Uint160 [20]uint8
|
||||
|
||||
// ToSlice returns a byte slice of u.
|
||||
func (u Uint160) ToSlice() []byte {
|
||||
b := make([]byte, 20)
|
||||
for i := 0; i < len(b); i++ {
|
||||
b[i] = byte(u[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// String implements the stringer interface.
|
||||
func (u Uint160) String() string {
|
||||
return hex.EncodeToString(u.ToSlice())
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUint256(t *testing.T) {
|
||||
}
|
24
pkg/util/uint160.go
Normal file
24
pkg/util/uint160.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
const uint160Size = 20
|
||||
|
||||
// Uint160 is a 20 byte long unsigned integer.
|
||||
type Uint160 [uint160Size]uint8
|
||||
|
||||
// ToSlice returns a byte slice of u.
|
||||
func (u Uint160) ToSlice() []byte {
|
||||
b := make([]byte, uint160Size)
|
||||
for i := 0; i < uint160Size; i++ {
|
||||
b[i] = byte(u[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// String implements the stringer interface.
|
||||
func (u Uint160) String() string {
|
||||
return hex.EncodeToString(u.ToSlice())
|
||||
}
|
82
pkg/util/uint256.go
Normal file
82
pkg/util/uint256.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const uint256Size = 32
|
||||
|
||||
// Uint256 is a 32 byte long unsigned integer.
|
||||
type Uint256 [uint256Size]uint8
|
||||
|
||||
// Uint256DecodeFromString returns an Uint256 from a (hex) string.
|
||||
func Uint256DecodeFromString(s string) (Uint256, error) {
|
||||
var val Uint256
|
||||
|
||||
if len(s) != uint256Size*2 {
|
||||
return val, fmt.Errorf("expected string size of %d got %d", uint256Size*2, len(s))
|
||||
}
|
||||
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
|
||||
b = ReverseByteSlice(b)
|
||||
|
||||
return Uint256DecodeFromBytes(b)
|
||||
}
|
||||
|
||||
// Uint256DecodeFromBytes return an Uint256 from a byte slice.
|
||||
func Uint256DecodeFromBytes(b []byte) (Uint256, error) {
|
||||
var val Uint256
|
||||
|
||||
if len(b) != uint256Size {
|
||||
return val, fmt.Errorf("expected []byte of size %d got %d", uint256Size, len(b))
|
||||
}
|
||||
|
||||
for i := 0; i < uint256Size; i++ {
|
||||
val[i] = b[i]
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func ReverseByteSlice(b []byte) []byte {
|
||||
dest := make([]byte, len(b))
|
||||
|
||||
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
|
||||
dest[i], dest[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
return dest
|
||||
}
|
||||
|
||||
// ToSlice returns a byte slice of u.
|
||||
func (u Uint256) ToSlice() []byte {
|
||||
b := make([]byte, uint256Size)
|
||||
for i := 0; i < uint256Size; i++ {
|
||||
b[i] = byte(u[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// ToSliceReverse returns a reversed byte slice of u.
|
||||
func (u Uint256) ToSliceReverse() []byte {
|
||||
b := make([]byte, uint256Size)
|
||||
for i, j := 0, uint256Size-1; i < j; i, j = i+1, j-1 {
|
||||
b[i], b[j] = byte(u[j]), byte(u[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Equals returns true if both Uint256 values are the same.
|
||||
func (u Uint256) Equals(other Uint256) bool {
|
||||
return u.String() == other.String()
|
||||
}
|
||||
|
||||
// String implements the stringer interface.
|
||||
func (u Uint256) String() string {
|
||||
return hex.EncodeToString(u.ToSliceReverse())
|
||||
}
|
1
pkg/util/uint256_test.go
Normal file
1
pkg/util/uint256_test.go
Normal file
|
@ -0,0 +1 @@
|
|||
package util
|
Loading…
Reference in a new issue