merge original into coz repo (#10)

merged with the original repo.
This commit is contained in:
Anthony De Meulemeester 2018-02-01 21:28:45 +01:00 committed by GitHub
parent dd94086a22
commit 66c8fc8012
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 211 additions and 21 deletions

View file

@ -1 +1 @@
0.1.0
0.2.0

View file

@ -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
View file

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

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

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

View file

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

View file

@ -5,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 {

View file

@ -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.