mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-29 03:41:45 +00:00
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>
|
</p>
|
||||||
|
|
||||||
<h1 align="center">NEO-GO</h1>
|
<h1 align="center">neo-go</h1>
|
||||||
|
|
||||||
<p align="center">
|
<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>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
@ -18,67 +18,112 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
# Overview
|
# Overview
|
||||||
> This project is currently in alpha and under active development.
|
|
||||||
|
|
||||||
### Long term project goals
|
> This project is currently in **alpha** and under active development.
|
||||||
Full port of the original C# [NEO project](https://github.com/neo-project). A complete toolkit for the NEO blockchain.
|
|
||||||
|
|
||||||
- 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
|
- RPC client
|
||||||
- build, compile and deploy smart contracts with the Go vm
|
- CLI tool
|
||||||
|
- Smart contract compiler
|
||||||
|
|
||||||
### Current state
|
## 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.
|
|
||||||
|
|
||||||
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)
|
The project will exist out of the following packages:
|
||||||
2. core (started)
|
|
||||||
3. api (JSON-RPC server) (started)
|
|
||||||
4. vm (open)
|
|
||||||
5. smartcontract (open)
|
|
||||||
|
|
||||||
# Getting started
|
| Package | State | Developer |
|
||||||
### Server
|
|---------------|---------|--------------------------------------|
|
||||||
|
| 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:
|
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:
|
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:
|
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
|
## VM
|
||||||
To be implemented..
|
|
||||||
|
|
||||||
### smart contracts
|
```
|
||||||
To be implemented..
|
TODO
|
||||||
|
```
|
||||||
|
|
||||||
|
## Smart Contracts
|
||||||
|
|
||||||
|
```
|
||||||
|
TODO
|
||||||
|
```
|
||||||
|
|
||||||
# Contributing
|
# 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
|
# Contact
|
||||||
|
|
||||||
- [@anthdm](https://github.com/anthdm) on Github
|
- [@anthdm](https://github.com/anthdm) on Github
|
||||||
- [@anthdm](https://twitter.com/anthdm) on Twitter
|
- [@anthdm](https://twitter.com/anthdm) on Twitter
|
||||||
- Send me an email anthony@cityofzion.io
|
- Send me an email anthony@cityofzion.io
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
- Open-source [MIT](https://github.com/anthdm/neo-go/blob/master/LICENCE.md)
|
- 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"
|
"flag"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/anthdm/neo-go/pkg/network"
|
"github.com/CityOfZion/neo-go/pkg/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
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
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
. "github.com/anthdm/neo-go/pkg/util"
|
. "github.com/CityOfZion/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Block represents one block in the chain.
|
// BlockBase holds the base info of a block
|
||||||
type Block struct {
|
type BlockBase struct {
|
||||||
Version uint32
|
Version uint32
|
||||||
// hash of the previous block.
|
// hash of the previous block.
|
||||||
PrevBlock Uint256
|
PrevBlock Uint256
|
||||||
// Root hash of a transaction list.
|
// Root hash of a transaction list.
|
||||||
MerkleRoot Uint256
|
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
|
Timestamp uint32
|
||||||
// height of the block
|
// height of the block
|
||||||
Height uint32
|
Height uint32
|
||||||
|
@ -22,14 +26,40 @@ type Block struct {
|
||||||
Nonce uint64
|
Nonce uint64
|
||||||
// contract addresss of the next miner
|
// contract addresss of the next miner
|
||||||
NextMiner Uint160
|
NextMiner Uint160
|
||||||
// seperator ? fixed to 1
|
// fixed to 1
|
||||||
_sep uint8
|
_sep uint8
|
||||||
// Script used to validate the block
|
// Script used to validate the block
|
||||||
Script *Witness
|
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
|
// transaction list
|
||||||
Transactions []*Transaction
|
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.
|
// EncodeBinary encodes the block to the given writer.
|
||||||
func (b *Block) EncodeBinary(w io.Writer) error {
|
func (b *Block) EncodeBinary(w io.Writer) error {
|
||||||
return nil
|
return nil
|
||||||
|
@ -66,5 +96,20 @@ func (b *Block) DecodeBinary(r io.Reader) error {
|
||||||
return err
|
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.
|
// Size implements the payload interface.
|
||||||
func (b *Block) Size() uint32 { return 0 }
|
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"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TransactionType is the type of a transaction.
|
// Transaction is a process recorded in the NEO system.
|
||||||
type TransactionType uint8
|
type Transaction struct {
|
||||||
|
Type TransactionType
|
||||||
// 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 ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// All processes in NEO system are recorded in transactions.
|
// All processes in NEO system are recorded in transactions.
|
||||||
|
@ -45,18 +23,13 @@ const (
|
||||||
AgencyTX = 0xb0
|
AgencyTX = 0xb0
|
||||||
)
|
)
|
||||||
|
|
||||||
// Transaction is a process recorded in the NEO system.
|
|
||||||
type Transaction struct {
|
|
||||||
Type TransactionType
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeBinary implements the payload interface.
|
// 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)
|
err := binary.Read(r, binary.LittleEndian, &t.Type)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeBinary implements the payload interface.
|
// EncodeBinary implements the payload interface.
|
||||||
func (t *Transaction) EncodeBinary(w io.Writer) error {
|
func (t Transaction) EncodeBinary(w io.Writer) error {
|
||||||
return nil
|
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"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/anthdm/neo-go/pkg/core"
|
"github.com/CityOfZion/neo-go/pkg/core"
|
||||||
"github.com/anthdm/neo-go/pkg/network/payload"
|
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/anthdm/neo-go/pkg/network/payload"
|
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMessageEncodeDecode(t *testing.T) {
|
func TestMessageEncodeDecode(t *testing.T) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/anthdm/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddrWithTime payload
|
// AddrWithTime payload
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/anthdm/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEncodeDecodeAddr(t *testing.T) {
|
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"
|
"encoding/binary"
|
||||||
"io"
|
"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.
|
// The node can broadcast the object information it owns by this message.
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/anthdm/neo-go/pkg/util"
|
. "github.com/CityOfZion/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInventoryEncodeDecode(t *testing.T) {
|
func TestInventoryEncodeDecode(t *testing.T) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package network
|
package network
|
||||||
|
|
||||||
import (
|
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
|
// Peer is the local representation of a remote node. It's an interface that may
|
||||||
|
@ -9,7 +9,6 @@ import (
|
||||||
type Peer interface {
|
type Peer interface {
|
||||||
id() uint32
|
id() uint32
|
||||||
addr() util.Endpoint
|
addr() util.Endpoint
|
||||||
verack() bool
|
|
||||||
disconnect()
|
disconnect()
|
||||||
callVersion(*Message)
|
callVersion(*Message)
|
||||||
callGetaddr(*Message)
|
callGetaddr(*Message)
|
||||||
|
@ -20,7 +19,6 @@ type Peer interface {
|
||||||
type LocalPeer struct {
|
type LocalPeer struct {
|
||||||
s *Server
|
s *Server
|
||||||
nonce uint32
|
nonce uint32
|
||||||
isVerack bool
|
|
||||||
endpoint util.Endpoint
|
endpoint util.Endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +37,5 @@ func (p *LocalPeer) callGetaddr(msg *Message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *LocalPeer) id() uint32 { return p.nonce }
|
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) addr() util.Endpoint { return p.endpoint }
|
||||||
func (p *LocalPeer) disconnect() {}
|
func (p *LocalPeer) disconnect() {}
|
||||||
|
|
|
@ -5,10 +5,12 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/anthdm/neo-go/pkg/network/payload"
|
"github.com/CityOfZion/neo-go/pkg/core"
|
||||||
"github.com/anthdm/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -54,6 +56,46 @@ type Server struct {
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
// channel for safely responding the number of current connected peers.
|
// channel for safely responding the number of current connected peers.
|
||||||
peerCountCh chan peerCount
|
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.
|
// NewServer returns a pointer to a new server.
|
||||||
|
@ -76,6 +118,8 @@ func NewServer(net NetMode) *Server {
|
||||||
net: net,
|
net: net,
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
peerCountCh: make(chan peerCount),
|
peerCountCh: make(chan peerCount),
|
||||||
|
// knownHashes: protectedHashmap{},
|
||||||
|
bc: core.NewBlockchain(core.NewMemoryStore()),
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
@ -162,11 +206,11 @@ func (s *Server) handlePeerConnected(p Peer) {
|
||||||
func (s *Server) handleVersionCmd(msg *Message, p Peer) *Message {
|
func (s *Server) handleVersionCmd(msg *Message, p Peer) *Message {
|
||||||
version := msg.Payload.(*payload.Version)
|
version := msg.Payload.(*payload.Version)
|
||||||
if s.id == version.Nonce {
|
if s.id == version.Nonce {
|
||||||
p.disconnect()
|
// s.unregister <- p
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if p.addr().Port != version.Port {
|
if p.addr().Port != version.Port {
|
||||||
p.disconnect()
|
// s.unregister <- p
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return newMessage(ModeDevNet, cmdVerack, nil)
|
return newMessage(ModeDevNet, cmdVerack, nil)
|
||||||
|
@ -176,22 +220,46 @@ func (s *Server) handleGetaddrCmd(msg *Message, p Peer) *Message {
|
||||||
return nil
|
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 {
|
func (s *Server) handleInvCmd(msg *Message, p Peer) *Message {
|
||||||
inv := msg.Payload.(*payload.Inventory)
|
inv := msg.Payload.(*payload.Inventory)
|
||||||
if !inv.Type.Valid() {
|
if !inv.Type.Valid() {
|
||||||
p.disconnect()
|
s.unregister <- p
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if len(inv.Hashes) == 0 {
|
if len(inv.Hashes) == 0 {
|
||||||
p.disconnect()
|
s.unregister <- p
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: only grab the hashes that we dont know.
|
||||||
|
|
||||||
payload := payload.NewInventory(inv.Type, inv.Hashes)
|
payload := payload.NewInventory(inv.Type, inv.Hashes)
|
||||||
resp := newMessage(s.net, cmdGetData, payload)
|
resp := newMessage(s.net, cmdGetData, payload)
|
||||||
return resp
|
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) {
|
func (s *Server) handleAddrCmd(msg *Message, p Peer) {
|
||||||
addrList := msg.Payload.(*payload.AddressList)
|
addrList := msg.Payload.(*payload.AddressList)
|
||||||
for _, addr := range addrList.Addrs {
|
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.
|
// check if the addr is already connected to the server.
|
||||||
func (s *Server) peerAlreadyConnected(addr net.Addr) bool {
|
func (s *Server) peerAlreadyConnected(addr net.Addr) bool {
|
||||||
for peer := range s.peers {
|
for peer := range s.peers {
|
||||||
|
|
|
@ -3,7 +3,7 @@ package network
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/anthdm/neo-go/pkg/network/payload"
|
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHandleVersion(t *testing.T) {
|
func TestHandleVersion(t *testing.T) {
|
||||||
|
|
|
@ -3,10 +3,11 @@ package network
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/anthdm/neo-go/pkg/network/payload"
|
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
||||||
"github.com/anthdm/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func listenTCP(s *Server, port int) error {
|
func listenTCP(s *Server, port int) error {
|
||||||
|
@ -22,6 +23,7 @@ func listenTCP(s *Server, port int) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
go handleConnection(s, conn)
|
go handleConnection(s, conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,13 +56,18 @@ func handleConnection(s *Server, conn net.Conn) {
|
||||||
s.unregister <- peer
|
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()
|
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.
|
// Read from the connection and decode it into a Message ready for processing.
|
||||||
buf := make([]byte, 1024)
|
buf := make([]byte, 1024)
|
||||||
for {
|
for {
|
||||||
_, err := conn.Read(buf)
|
_, err := conn.Read(buf)
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Printf("conn read error: %s", err)
|
s.logger.Printf("conn read error: %s", err)
|
||||||
break
|
break
|
||||||
|
@ -71,39 +78,47 @@ func handleConnection(s *Server, conn net.Conn) {
|
||||||
s.logger.Printf("decode error %s", err)
|
s.logger.Printf("decode error %s", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
handleMessage(msg, s, peer)
|
|
||||||
|
peer.receive <- msg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleMessage hands the message received from a TCP connection over to the server.
|
// handleMessage hands the message received from a TCP connection over to the server.
|
||||||
func handleMessage(msg *Message, s *Server, p *TCPPeer) {
|
func handleMessage(s *Server, p *TCPPeer) {
|
||||||
command := msg.commandType()
|
// 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 {
|
s.logger.Printf("IN :: %d :: %s :: %v", p.id(), command, msg)
|
||||||
case cmdVersion:
|
|
||||||
resp := s.handleVersionCmd(msg, p)
|
switch command {
|
||||||
p.isVerack = true
|
case cmdVersion:
|
||||||
p.nonce = msg.Payload.(*payload.Version).Nonce
|
resp := s.handleVersionCmd(msg, p)
|
||||||
p.send <- resp
|
p.nonce = msg.Payload.(*payload.Version).Nonce
|
||||||
case cmdAddr:
|
p.send <- resp
|
||||||
s.handleAddrCmd(msg, p)
|
case cmdAddr:
|
||||||
case cmdGetAddr:
|
s.handleAddrCmd(msg, p)
|
||||||
s.handleGetaddrCmd(msg, p)
|
case cmdGetAddr:
|
||||||
case cmdInv:
|
s.handleGetaddrCmd(msg, p)
|
||||||
resp := s.handleInvCmd(msg, p)
|
case cmdInv:
|
||||||
p.send <- resp
|
resp := s.handleInvCmd(msg, p)
|
||||||
case cmdBlock:
|
p.send <- resp
|
||||||
case cmdConsensus:
|
case cmdBlock:
|
||||||
case cmdTX:
|
s.handleBlockCmd(msg, p)
|
||||||
case cmdVerack:
|
case cmdConsensus:
|
||||||
go s.sendLoop(p)
|
case cmdTX:
|
||||||
case cmdGetHeaders:
|
case cmdVerack:
|
||||||
case cmdGetBlocks:
|
go s.sendLoop(p)
|
||||||
case cmdGetData:
|
case cmdGetHeaders:
|
||||||
case cmdHeaders:
|
case cmdGetBlocks:
|
||||||
default:
|
case cmdGetData:
|
||||||
|
case cmdHeaders:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,8 +133,8 @@ type TCPPeer struct {
|
||||||
endpoint util.Endpoint
|
endpoint util.Endpoint
|
||||||
// channel to coordinate messages writen back to the connection.
|
// channel to coordinate messages writen back to the connection.
|
||||||
send chan *Message
|
send chan *Message
|
||||||
// whether this peers version was acknowledged.
|
// channel to receive from underlying connection.
|
||||||
isVerack bool
|
receive chan *Message
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTCPPeer returns a pointer to a TCP Peer.
|
// NewTCPPeer returns a pointer to a TCP Peer.
|
||||||
|
@ -129,6 +144,7 @@ func NewTCPPeer(conn net.Conn, s *Server) *TCPPeer {
|
||||||
return &TCPPeer{
|
return &TCPPeer{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
send: make(chan *Message),
|
send: make(chan *Message),
|
||||||
|
receive: make(chan *Message),
|
||||||
endpoint: e,
|
endpoint: e,
|
||||||
s: s,
|
s: s,
|
||||||
}
|
}
|
||||||
|
@ -148,20 +164,17 @@ func (p *TCPPeer) addr() util.Endpoint {
|
||||||
return p.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.
|
// callGetaddr will send the "getaddr" command to the remote.
|
||||||
func (p *TCPPeer) callGetaddr(msg *Message) {
|
func (p *TCPPeer) callGetaddr(msg *Message) {
|
||||||
p.send <- msg
|
p.send <- msg
|
||||||
}
|
}
|
||||||
|
|
||||||
// disconnect closes the send channel and the underlying connection.
|
// 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() {
|
func (p *TCPPeer) disconnect() {
|
||||||
close(p.send)
|
|
||||||
p.conn.Close()
|
p.conn.Close()
|
||||||
|
close(p.send)
|
||||||
|
close(p.receive)
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeLoop writes messages to the underlying TCP connection.
|
// writeLoop writes messages to the underlying TCP connection.
|
||||||
|
|
Loading…
Reference in a new issue