parent
dd94086a22
commit
66c8fc8012
7 changed files with 211 additions and 21 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
0.1.0
|
0.2.0
|
|
@ -1,20 +1,24 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
. "github.com/CityOfZion/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,8 +5,10 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/core"
|
||||||
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
@ -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,6 +3,7 @@ package network
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
||||||
|
@ -64,6 +65,9 @@ func handleConnection(s *Server, conn net.Conn) {
|
||||||
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
|
||||||
|
@ -83,7 +87,7 @@ func handleConnection(s *Server, conn net.Conn) {
|
||||||
func handleMessage(s *Server, p *TCPPeer) {
|
func handleMessage(s *Server, p *TCPPeer) {
|
||||||
// Disconnect the peer when we break out of the loop.
|
// Disconnect the peer when we break out of the loop.
|
||||||
defer func() {
|
defer func() {
|
||||||
p.disconnect()
|
s.unregister <- p
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -97,14 +101,6 @@ func handleMessage(s *Server, p *TCPPeer) {
|
||||||
resp := s.handleVersionCmd(msg, p)
|
resp := s.handleVersionCmd(msg, p)
|
||||||
p.nonce = msg.Payload.(*payload.Version).Nonce
|
p.nonce = msg.Payload.(*payload.Version).Nonce
|
||||||
p.send <- resp
|
p.send <- resp
|
||||||
|
|
||||||
// after sending our version we want a "verack" and nothing else.
|
|
||||||
msg := <-p.receive
|
|
||||||
if msg.commandType() != cmdVerack {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// we can start the protocol now.
|
|
||||||
go s.sendLoop(p)
|
|
||||||
case cmdAddr:
|
case cmdAddr:
|
||||||
s.handleAddrCmd(msg, p)
|
s.handleAddrCmd(msg, p)
|
||||||
case cmdGetAddr:
|
case cmdGetAddr:
|
||||||
|
@ -113,11 +109,11 @@ func handleMessage(s *Server, p *TCPPeer) {
|
||||||
resp := s.handleInvCmd(msg, p)
|
resp := s.handleInvCmd(msg, p)
|
||||||
p.send <- resp
|
p.send <- resp
|
||||||
case cmdBlock:
|
case cmdBlock:
|
||||||
|
s.handleBlockCmd(msg, p)
|
||||||
case cmdConsensus:
|
case cmdConsensus:
|
||||||
case cmdTX:
|
case cmdTX:
|
||||||
case cmdVerack:
|
case cmdVerack:
|
||||||
// disconnect the peer, verack should already be handled.
|
go s.sendLoop(p)
|
||||||
break
|
|
||||||
case cmdGetHeaders:
|
case cmdGetHeaders:
|
||||||
case cmdGetBlocks:
|
case cmdGetBlocks:
|
||||||
case cmdGetData:
|
case cmdGetData:
|
||||||
|
@ -174,9 +170,11 @@ func (p *TCPPeer) callGetaddr(msg *Message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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