Merge branch 'master' of https://github.com/CityOfZion/neo-go
This commit is contained in:
commit
4050dbeeb8
28 changed files with 653 additions and 126 deletions
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal 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
23
Makefile
Normal 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
109
README.md
|
@ -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
1
VERSION
Normal file
|
@ -0,0 +1 @@
|
|||
0.2.0
|
86
circle.yml
Normal file
86
circle.yml
Normal 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
|
|
@ -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
4
glide.lock
generated
Normal file
|
@ -0,0 +1,4 @@
|
|||
hash: b1152abdd9a1fa1e70773cddcf54247d3fe3332602604f9f2233165ced02eeaf
|
||||
updated: 2018-02-01T18:34:22.684905Z
|
||||
imports: []
|
||||
testImports: []
|
2
glide.yaml
Normal file
2
glide.yaml
Normal file
|
@ -0,0 +1,2 @@
|
|||
package: github.com/CityOfZion/neo-go
|
||||
import: []
|
|
@ -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
6
pkg/core/block_test.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package core
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestBlockEncodeDecode(t *testing.T) {
|
||||
}
|
26
pkg/core/blockchain.go
Normal file
26
pkg/core/blockchain.go
Normal 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,
|
||||
}
|
||||
}
|
44
pkg/core/blockchain_storer.go
Normal file
44
pkg/core/blockchain_storer.go
Normal 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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
28
pkg/core/transaction_type.go
Normal file
28
pkg/core/transaction_type.go
Normal 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 ""
|
||||
}
|
||||
}
|
|
@ -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 (
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/anthdm/neo-go/pkg/util"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// AddrWithTime payload
|
||||
|
|
|
@ -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) {
|
||||
|
|
54
pkg/network/payload/getblocks.go
Normal file
54
pkg/network/payload/getblocks.go
Normal 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
|
||||
}
|
37
pkg/network/payload/getblocks_test.go
Normal file
37
pkg/network/payload/getblocks_test.go
Normal 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)
|
||||
}
|
||||
}
|
17
pkg/network/payload/getheaders.go
Normal file
17
pkg/network/payload/getheaders.go
Normal 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
|
||||
}
|
37
pkg/network/payload/getheaders_test.go
Normal file
37
pkg/network/payload/getheaders_test.go
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue