This commit is contained in:
anthdm 2018-02-01 21:30:06 +01:00
commit 4050dbeeb8
28 changed files with 653 additions and 126 deletions

18
.gitignore vendored Normal file
View file

@ -0,0 +1,18 @@
# Binaries for programs and plugins
*.exe
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
# Added by CoZ developers
vendor/
bin/

23
Makefile Normal file
View file

@ -0,0 +1,23 @@
BRANCH = "master"
VERSION = $(shell cat ./VERSION)
build:
@go build -o ./bin/neo-go ./cli/main.go
check-version:
git fetch && (! git rev-list ${VERSION})
deps:
@glide install
push-tag:
git checkout ${BRANCH}
git pull origin ${BRANCH}
git tag ${VERSION}
git push origin ${BRANCH} --tags
test:
@go test $(shell glide nv) -cover
vet:
@go vet $(shell glide nv)

109
README.md
View file

@ -5,10 +5,10 @@
>
</p>
<h1 align="center">NEO-GO</h1>
<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.
<b>Go</b> Node and SDK for the <a href="https://neo.org">NEO</a> blockchain.
</p>
<p align="center">
@ -18,67 +18,112 @@
</p>
# Overview
> This project is currently in alpha and under active development.
### Long term project goals
Full port of the original C# [NEO project](https://github.com/neo-project). A complete toolkit for the NEO blockchain.
> This project is currently in **alpha** and under active development.
- Full server (consensus and RPC) nodes.
## Project Goals
Full port of the original C# [NEO project](https://github.com/neo-project).
A complete toolkit for the NEO blockchain, including:
- Full consensus node
- Full RPC node
- RPC client
- build, compile and deploy smart contracts with the Go vm
- CLI tool
- Smart contract compiler
### Current state
This project is still under heavy development. Still working on internal API's and project layout. This should not take longer than 2 weeks.
## Current State
The project will exist out of the following topics/packages:
This project is still under heavy development. Still working on internal API's and project layout. T
his should not take longer than 2 weeks.
1. network (started)
2. core (started)
3. api (JSON-RPC server) (started)
4. vm (open)
5. smartcontract (open)
The project will exist out of the following packages:
# Getting started
### Server
| Package | State | Developer |
|---------------|---------|--------------------------------------|
| api | started | [@anthdm](https://github.com/anthdm) |
| core | started | [@anthdm](https://github.com/anthdm) |
| network | started | [@anthdm](https://github.com/anthdm) |
| smartcontract | started | [@revett](https://github.com/revett) |
| vm | started | [@revett](https://github.com/revett) |
Install the neoserver cli `go install ./cmd/neoserver`
# Getting Started
Currently, there is a minimal subset of the NEO protocol implemented. To start experimenting make sure you a have a private net running on your machine. If you dont, take a look at [docker-privnet-with-gas](https://hub.docker.com/r/metachris/neo-privnet-with-gas/).
## Server
Install dependencies, this requires [Glide](https://github.com/Masterminds/glide#install):
```
make deps
```
Build the **neo-go** CLI:
```
make build
```
Currently, there is a minimal subset of the NEO protocol implemented.
To start experimenting make sure you a have a private net running on your machine.
If you dont, take a look at [docker-privnet-with-gas](https://hub.docker.com/r/metachris/neo-privnet-with-gas/).
Start the server:
`neoserver -seed 127.0.0.1:20333`
```
./bin/neo-go -seed 127.0.0.1:20333
```
You can add multiple seeds if you want:
`neoserver -seed 127.0.0.1:20333,127.0.01:20334`
```
./bin/neo-go -seed 127.0.0.1:20333,127.0.01:20334
```
By default the server will currently run on port 3000, for testing purposes. You can change that by setting the tcp flag:
By default the server will currently run on port 3000, for testing purposes.
You can change that by setting the tcp flag:
`neoserver -seed 127.0.0.1:20333 -tcp 1337`
```
./bin/neo-go -seed 127.0.0.1:20333 -tcp 1337
```
## RPC
### RPC
If you want your node to also serve JSON-RPC, you can do that by setting the following flag:
`neoserver -rpc 4000`
```
./bin/neo-go -rpc 4000
```
In this case server will accept and respond JSON-RPC on port 4000. Keep in mind that currently there is only a small subset of the JSON-RPC implemented. Feel free to make a PR with more functionality.
In this case server will accept and respond JSON-RPC on port 4000.
Keep in mind that currently there is only a small subset of the JSON-RPC implemented.
Feel free to make a PR with more functionality.
### vm
To be implemented..
## VM
### smart contracts
To be implemented..
```
TODO
```
## Smart Contracts
```
TODO
```
# Contributing
Feel free to contribute to this project after reading the [contributing guidelines](https://github.com/anthdm/neo-go/blob/master/CONTRIBUTING.md).
Before starting to work on a certain topic, create an new issue first, describing the feauture/topic you are going to implement.
Feel free to contribute to this project after reading the
[contributing guidelines](https://github.com/anthdm/neo-go/blob/master/CONTRIBUTING.md).
Before starting to work on a certain topic, create an new issue first,
describing the feauture/topic you are going to implement.
# Contact
- [@anthdm](https://github.com/anthdm) on Github
- [@anthdm](https://twitter.com/anthdm) on Twitter
- Send me an email anthony@cityofzion.io
# License
- Open-source [MIT](https://github.com/anthdm/neo-go/blob/master/LICENCE.md)

1
VERSION Normal file
View file

@ -0,0 +1 @@
0.2.0

86
circle.yml Normal file
View file

@ -0,0 +1,86 @@
version: 2
jobs:
install_deps:
working_directory: /go/src/github.com/CityOfZion/neo-go
docker:
- image: vidsyhq/go-builder:latest
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ .Revision }}
- run: BUILD=false /scripts/build.sh
- save_cache:
key: dependency-cache-{{ .Revision }}
paths:
- vendor
test:
working_directory: /go/src/github.com/CityOfZion/neo-go
docker:
- image: vidsyhq/go-builder:latest
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ .Revision }}
- run: make test
vet:
working_directory: /go/src/github.com/CityOfZion/neo-go
docker:
- image: vidsyhq/go-builder:latest
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ .Revision }}
- run: make vet
check_version:
working_directory: /go/src/github.com/CityOfZion/neo-go
docker:
- image: vidsyhq/go-builder:latest
steps:
- checkout
- run: make check-version
build_cli:
working_directory: /go/src/github.com/CityOfZion/neo-go
docker:
- image: vidsyhq/go-builder:latest
steps:
- checkout
- run: make build
workflows:
version: 2
workflow:
jobs:
- install_deps:
filters:
tags:
only: /[0-9]+\.[0-9]+\.[0-9]+/
branches:
ignore: master
- test:
requires:
- install_deps
filters:
tags:
only: /[0-9]+\.[0-9]+\.[0-9]+/
branches:
ignore: master
- vet:
requires:
- install_deps
filters:
tags:
only: /[0-9]+\.[0-9]+\.[0-9]+/
branches:
ignore: master
- check_version:
filters:
branches:
ignore: master
- build_cli:
requires:
- install_deps
filters:
tags:
only: /[0-9]+\.[0-9]+\.[0-9]+/
branches:
ignore: master

View file

@ -4,7 +4,7 @@ import (
"flag"
"strings"
"github.com/anthdm/neo-go/pkg/network"
"github.com/CityOfZion/neo-go/pkg/network"
)
var (

4
glide.lock generated Normal file
View file

@ -0,0 +1,4 @@
hash: b1152abdd9a1fa1e70773cddcf54247d3fe3332602604f9f2233165ced02eeaf
updated: 2018-02-01T18:34:22.684905Z
imports: []
testImports: []

2
glide.yaml Normal file
View file

@ -0,0 +1,2 @@
package: github.com/CityOfZion/neo-go
import: []

View file

@ -1,20 +1,24 @@
package core
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"io"
. "github.com/anthdm/neo-go/pkg/util"
. "github.com/CityOfZion/neo-go/pkg/util"
)
// Block represents one block in the chain.
type Block struct {
// BlockBase holds the base info of a block
type BlockBase struct {
Version uint32
// hash of the previous block.
PrevBlock Uint256
// Root hash of a transaction list.
MerkleRoot Uint256
// timestamp
// 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
@ -22,14 +26,40 @@ type Block struct {
Nonce uint64
// contract addresss of the next miner
NextMiner Uint160
// seperator ? fixed to 1
// fixed to 1
_sep uint8
// Script used to validate the block
Script *Witness
}
// BlockHead holds the head info of a block
type BlockHead struct {
BlockBase
// fixed to 0
_sep1 uint8
}
// Block represents one block in the chain.
type Block struct {
BlockBase
// transaction list
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)
return err
}
// EncodeBinary encodes the block to the given writer.
func (b *Block) EncodeBinary(w io.Writer) error {
return nil
@ -66,5 +96,20 @@ func (b *Block) DecodeBinary(r io.Reader) error {
return err
}
// 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 }

6
pkg/core/block_test.go Normal file
View file

@ -0,0 +1,6 @@
package core
import "testing"
func TestBlockEncodeDecode(t *testing.T) {
}

26
pkg/core/blockchain.go Normal file
View file

@ -0,0 +1,26 @@
package core
// tuning parameters
const (
secondsPerBlock = 15
)
var (
genAmount = []int{8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
)
// Blockchain holds the chain.
type Blockchain struct {
// Any object that satisfies the BlockchainStorer interface.
BlockchainStorer
// index of the latest block.
currentHeight uint32
}
// NewBlockchain returns a pointer to a Blockchain.
func NewBlockchain(store BlockchainStorer) *Blockchain {
return &Blockchain{
BlockchainStorer: store,
}
}

View file

@ -0,0 +1,44 @@
package core
import (
"sync"
"github.com/CityOfZion/neo-go/pkg/util"
)
// BlockchainStorer is anything that can persist and retrieve the blockchain.
type BlockchainStorer interface {
HasBlock(util.Uint256) bool
GetBlockByHeight(uint32) (*Block, error)
GetBlockByHash(util.Uint256) (*Block, error)
}
// MemoryStore is an in memory implementation of a BlockChainStorer.
type MemoryStore struct {
mtx *sync.RWMutex
blocks map[util.Uint256]*Block
}
// NewMemoryStore returns a pointer to a MemoryStore object.
func NewMemoryStore() *MemoryStore {
return &MemoryStore{
mtx: &sync.RWMutex{},
blocks: map[util.Uint256]*Block{},
}
}
// HasBlock implements the BlockchainStorer interface.
func (s *MemoryStore) HasBlock(hash util.Uint256) bool {
_, ok := s.blocks[hash]
return ok
}
// GetBlockByHash returns a block by its hash.
func (s *MemoryStore) GetBlockByHash(hash util.Uint256) (*Block, error) {
return nil, nil
}
// GetBlockByHeight returns a block by its height.
func (s *MemoryStore) GetBlockByHeight(i uint32) (*Block, error) {
return nil, nil
}

View file

@ -5,31 +5,9 @@ import (
"io"
)
// TransactionType is the type of a transaction.
type TransactionType uint8
// String implements the stringer interface.
func (t TransactionType) String() string {
switch t {
case MinerTX:
return "miner transaction"
case IssueTX:
return "issue transaction"
case ClaimTX:
return "claim transaction"
case EnrollmentTX:
return "enrollment transaction"
case VotingTX:
return "voting transaction"
case RegisterTX:
return "register transaction"
case ContractTX:
return "contract transaction"
case AgencyTX:
return "agency transaction"
default:
return ""
}
// Transaction is a process recorded in the NEO system.
type Transaction struct {
Type TransactionType
}
// All processes in NEO system are recorded in transactions.
@ -45,18 +23,13 @@ const (
AgencyTX = 0xb0
)
// Transaction is a process recorded in the NEO system.
type Transaction struct {
Type TransactionType
}
// DecodeBinary implements the payload interface.
func (t *Transaction) DecodeBinary(r io.Reader) error {
func (t Transaction) DecodeBinary(r io.Reader) error {
err := binary.Read(r, binary.LittleEndian, &t.Type)
return err
}
// EncodeBinary implements the payload interface.
func (t *Transaction) EncodeBinary(w io.Writer) error {
func (t Transaction) EncodeBinary(w io.Writer) error {
return nil
}

View file

@ -0,0 +1,28 @@
package core
// TransactionType is the type of a transaction.
type TransactionType uint8
// String implements the stringer interface.
func (t TransactionType) String() string {
switch t {
case MinerTX:
return "miner transaction"
case IssueTX:
return "issue transaction"
case ClaimTX:
return "claim transaction"
case EnrollmentTX:
return "enrollment transaction"
case VotingTX:
return "voting transaction"
case RegisterTX:
return "register transaction"
case ContractTX:
return "contract transaction"
case AgencyTX:
return "agency transaction"
default:
return ""
}
}

View file

@ -8,8 +8,8 @@ import (
"fmt"
"io"
"github.com/anthdm/neo-go/pkg/core"
"github.com/anthdm/neo-go/pkg/network/payload"
"github.com/CityOfZion/neo-go/pkg/core"
"github.com/CityOfZion/neo-go/pkg/network/payload"
)
const (

View file

@ -5,7 +5,7 @@ import (
"reflect"
"testing"
"github.com/anthdm/neo-go/pkg/network/payload"
"github.com/CityOfZion/neo-go/pkg/network/payload"
)
func TestMessageEncodeDecode(t *testing.T) {

View file

@ -4,7 +4,7 @@ import (
"encoding/binary"
"io"
"github.com/anthdm/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/util"
)
// AddrWithTime payload

View file

@ -6,7 +6,7 @@ import (
"reflect"
"testing"
"github.com/anthdm/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/util"
)
func TestEncodeDecodeAddr(t *testing.T) {

View file

@ -0,0 +1,54 @@
package payload
import (
"encoding/binary"
"io"
. "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
type GetBlocks struct {
HashStartStop
}
// NewGetBlocks return a pointer to a GetBlocks object.
func NewGetBlocks(start []Uint256, stop Uint256) *GetBlocks {
p := &GetBlocks{}
p.HashStart = start
p.HashStop = stop
return p
}

View file

@ -0,0 +1,37 @@
package payload
import (
"bytes"
"crypto/sha256"
"reflect"
"testing"
. "github.com/CityOfZion/neo-go/pkg/util"
)
func TestGetBlocksEncodeDecode(t *testing.T) {
start := []Uint256{
sha256.Sum256([]byte("a")),
sha256.Sum256([]byte("b")),
}
stop := sha256.Sum256([]byte("c"))
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)
}
if !reflect.DeepEqual(p, pDecode) {
t.Fatalf("expecting both getblocks payloads to be equal %v and %v", p, pDecode)
}
}

View file

@ -0,0 +1,17 @@
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
}

View file

@ -0,0 +1,37 @@
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)
}
}

View file

@ -4,7 +4,7 @@ import (
"encoding/binary"
"io"
. "github.com/anthdm/neo-go/pkg/util"
. "github.com/CityOfZion/neo-go/pkg/util"
)
// The node can broadcast the object information it owns by this message.

View file

@ -6,7 +6,7 @@ import (
"reflect"
"testing"
. "github.com/anthdm/neo-go/pkg/util"
. "github.com/CityOfZion/neo-go/pkg/util"
)
func TestInventoryEncodeDecode(t *testing.T) {

View file

@ -1,7 +1,7 @@
package network
import (
"github.com/anthdm/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/util"
)
// Peer is the local representation of a remote node. It's an interface that may
@ -9,7 +9,6 @@ import (
type Peer interface {
id() uint32
addr() util.Endpoint
verack() bool
disconnect()
callVersion(*Message)
callGetaddr(*Message)
@ -20,7 +19,6 @@ type Peer interface {
type LocalPeer struct {
s *Server
nonce uint32
isVerack bool
endpoint util.Endpoint
}
@ -39,6 +37,5 @@ func (p *LocalPeer) callGetaddr(msg *Message) {
}
func (p *LocalPeer) id() uint32 { return p.nonce }
func (p *LocalPeer) verack() bool { return p.isVerack }
func (p *LocalPeer) addr() util.Endpoint { return p.endpoint }
func (p *LocalPeer) disconnect() {}

View file

@ -5,10 +5,12 @@ import (
"log"
"net"
"os"
"sync"
"time"
"github.com/anthdm/neo-go/pkg/network/payload"
"github.com/anthdm/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/core"
"github.com/CityOfZion/neo-go/pkg/network/payload"
"github.com/CityOfZion/neo-go/pkg/util"
)
const (
@ -54,6 +56,46 @@ type Server struct {
listener net.Listener
// channel for safely responding the number of current connected peers.
peerCountCh chan peerCount
// a list of hashes that
knownHashes protectedHashmap
// The blockchain.
bc *core.Blockchain
}
type protectedHashmap struct {
*sync.RWMutex
hashes map[util.Uint256]bool
}
func (m protectedHashmap) add(h util.Uint256) bool {
m.Lock()
defer m.Unlock()
if _, ok := m.hashes[h]; !ok {
m.hashes[h] = true
return true
}
return false
}
func (m protectedHashmap) remove(h util.Uint256) bool {
m.Lock()
defer m.Unlock()
if _, ok := m.hashes[h]; ok {
delete(m.hashes, h)
return true
}
return false
}
func (m protectedHashmap) has(h util.Uint256) bool {
m.RLock()
defer m.RUnlock()
_, ok := m.hashes[h]
return ok
}
// NewServer returns a pointer to a new server.
@ -76,6 +118,8 @@ func NewServer(net NetMode) *Server {
net: net,
quit: make(chan struct{}),
peerCountCh: make(chan peerCount),
// knownHashes: protectedHashmap{},
bc: core.NewBlockchain(core.NewMemoryStore()),
}
return s
@ -162,11 +206,11 @@ func (s *Server) handlePeerConnected(p Peer) {
func (s *Server) handleVersionCmd(msg *Message, p Peer) *Message {
version := msg.Payload.(*payload.Version)
if s.id == version.Nonce {
p.disconnect()
// s.unregister <- p
return nil
}
if p.addr().Port != version.Port {
p.disconnect()
// s.unregister <- p
return nil
}
return newMessage(ModeDevNet, cmdVerack, nil)
@ -176,22 +220,46 @@ func (s *Server) handleGetaddrCmd(msg *Message, p Peer) *Message {
return nil
}
// The node can broadcast the object information it owns by this message.
// The message can be sent automatically or can be used to answer getbloks messages.
func (s *Server) handleInvCmd(msg *Message, p Peer) *Message {
inv := msg.Payload.(*payload.Inventory)
if !inv.Type.Valid() {
p.disconnect()
s.unregister <- p
return nil
}
if len(inv.Hashes) == 0 {
p.disconnect()
s.unregister <- p
return nil
}
// todo: only grab the hashes that we dont know.
payload := payload.NewInventory(inv.Type, inv.Hashes)
resp := newMessage(s.net, cmdGetData, payload)
return resp
}
// handleBlockCmd processes the received block.
func (s *Server) handleBlockCmd(msg *Message, p Peer) {
block := msg.Payload.(*core.Block)
hash, err := block.Hash()
if err != nil {
// not quite sure what to do here.
// should we disconnect the client or just silently log and move on?
s.logger.Printf("failed to generate block hash: %s", err)
return
}
fmt.Println(hash)
if s.bc.HasBlock(hash) {
return
}
}
// After receiving the getaddr message, the node returns an addr message as response
// and provides information about the known nodes on the network.
func (s *Server) handleAddrCmd(msg *Message, p Peer) {
addrList := msg.Payload.(*payload.AddressList)
for _, addr := range addrList.Addrs {
@ -202,6 +270,9 @@ func (s *Server) handleAddrCmd(msg *Message, p Peer) {
}
}
func (s *Server) relayInventory(inv *payload.Inventory) {
}
// check if the addr is already connected to the server.
func (s *Server) peerAlreadyConnected(addr net.Addr) bool {
for peer := range s.peers {

View file

@ -3,7 +3,7 @@ package network
import (
"testing"
"github.com/anthdm/neo-go/pkg/network/payload"
"github.com/CityOfZion/neo-go/pkg/network/payload"
)
func TestHandleVersion(t *testing.T) {

View file

@ -3,10 +3,11 @@ package network
import (
"bytes"
"fmt"
"io"
"net"
"github.com/anthdm/neo-go/pkg/network/payload"
"github.com/anthdm/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/network/payload"
"github.com/CityOfZion/neo-go/pkg/util"
)
func listenTCP(s *Server, port int) error {
@ -22,6 +23,7 @@ func listenTCP(s *Server, port int) error {
if err != nil {
return err
}
go handleConnection(s, conn)
}
}
@ -54,13 +56,18 @@ func handleConnection(s *Server, conn net.Conn) {
s.unregister <- peer
}()
// Start a goroutine that will handle all writes to the registered peer.
// Start a goroutine that will handle all outgoing messages.
go peer.writeLoop()
// Start a goroutine that will handle all incomming messages.
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 {
break
}
if err != nil {
s.logger.Printf("conn read error: %s", err)
break
@ -71,39 +78,47 @@ func handleConnection(s *Server, conn net.Conn) {
s.logger.Printf("decode error %s", err)
break
}
handleMessage(msg, s, peer)
peer.receive <- msg
}
}
// handleMessage hands the message received from a TCP connection over to the server.
func handleMessage(msg *Message, s *Server, p *TCPPeer) {
command := msg.commandType()
func handleMessage(s *Server, p *TCPPeer) {
// Disconnect the peer when we break out of the loop.
defer func() {
s.unregister <- p
}()
s.logger.Printf("IN :: %d :: %s :: %v", p.id(), command, msg)
for {
msg := <-p.receive
command := msg.commandType()
switch command {
case cmdVersion:
resp := s.handleVersionCmd(msg, p)
p.isVerack = true
p.nonce = msg.Payload.(*payload.Version).Nonce
p.send <- resp
case cmdAddr:
s.handleAddrCmd(msg, p)
case cmdGetAddr:
s.handleGetaddrCmd(msg, p)
case cmdInv:
resp := s.handleInvCmd(msg, p)
p.send <- resp
case cmdBlock:
case cmdConsensus:
case cmdTX:
case cmdVerack:
go s.sendLoop(p)
case cmdGetHeaders:
case cmdGetBlocks:
case cmdGetData:
case cmdHeaders:
default:
s.logger.Printf("IN :: %d :: %s :: %v", p.id(), command, msg)
switch command {
case cmdVersion:
resp := s.handleVersionCmd(msg, p)
p.nonce = msg.Payload.(*payload.Version).Nonce
p.send <- resp
case cmdAddr:
s.handleAddrCmd(msg, p)
case cmdGetAddr:
s.handleGetaddrCmd(msg, p)
case cmdInv:
resp := s.handleInvCmd(msg, p)
p.send <- resp
case cmdBlock:
s.handleBlockCmd(msg, p)
case cmdConsensus:
case cmdTX:
case cmdVerack:
go s.sendLoop(p)
case cmdGetHeaders:
case cmdGetBlocks:
case cmdGetData:
case cmdHeaders:
}
}
}
@ -118,8 +133,8 @@ type TCPPeer struct {
endpoint util.Endpoint
// channel to coordinate messages writen back to the connection.
send chan *Message
// whether this peers version was acknowledged.
isVerack bool
// channel to receive from underlying connection.
receive chan *Message
}
// NewTCPPeer returns a pointer to a TCP Peer.
@ -129,6 +144,7 @@ func NewTCPPeer(conn net.Conn, s *Server) *TCPPeer {
return &TCPPeer{
conn: conn,
send: make(chan *Message),
receive: make(chan *Message),
endpoint: e,
s: s,
}
@ -148,20 +164,17 @@ func (p *TCPPeer) addr() util.Endpoint {
return p.endpoint
}
// verack implements the peer interface
func (p *TCPPeer) verack() bool {
return p.isVerack
}
// callGetaddr will send the "getaddr" command to the remote.
func (p *TCPPeer) callGetaddr(msg *Message) {
p.send <- msg
}
// disconnect closes the send channel and the underlying connection.
// TODO: this needs some love. We will get send on closed channel.
func (p *TCPPeer) disconnect() {
close(p.send)
p.conn.Close()
close(p.send)
close(p.receive)
}
// writeLoop writes messages to the underlying TCP connection.