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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
. "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,8 +5,10 @@ import (
|
|||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/core"
|
||||
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
@ -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,6 +3,7 @@ package network
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
||||
|
@ -64,6 +65,9 @@ func handleConnection(s *Server, conn net.Conn) {
|
|||
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
|
||||
|
@ -83,7 +87,7 @@ func handleConnection(s *Server, conn net.Conn) {
|
|||
func handleMessage(s *Server, p *TCPPeer) {
|
||||
// Disconnect the peer when we break out of the loop.
|
||||
defer func() {
|
||||
p.disconnect()
|
||||
s.unregister <- p
|
||||
}()
|
||||
|
||||
for {
|
||||
|
@ -97,14 +101,6 @@ func handleMessage(s *Server, p *TCPPeer) {
|
|||
resp := s.handleVersionCmd(msg, p)
|
||||
p.nonce = msg.Payload.(*payload.Version).Nonce
|
||||
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:
|
||||
s.handleAddrCmd(msg, p)
|
||||
case cmdGetAddr:
|
||||
|
@ -113,11 +109,11 @@ func handleMessage(s *Server, p *TCPPeer) {
|
|||
resp := s.handleInvCmd(msg, p)
|
||||
p.send <- resp
|
||||
case cmdBlock:
|
||||
s.handleBlockCmd(msg, p)
|
||||
case cmdConsensus:
|
||||
case cmdTX:
|
||||
case cmdVerack:
|
||||
// disconnect the peer, verack should already be handled.
|
||||
break
|
||||
go s.sendLoop(p)
|
||||
case cmdGetHeaders:
|
||||
case cmdGetBlocks:
|
||||
case cmdGetData:
|
||||
|
@ -174,9 +170,11 @@ func (p *TCPPeer) callGetaddr(msg *Message) {
|
|||
}
|
||||
|
||||
// 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