187 lines
4.7 KiB
Go
187 lines
4.7 KiB
Go
package blockchain
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/CityOfZion/neo-go/pkg/chainparams"
|
|
|
|
"github.com/CityOfZion/neo-go/pkg/database"
|
|
"github.com/CityOfZion/neo-go/pkg/wire/payload"
|
|
"github.com/CityOfZion/neo-go/pkg/wire/protocol"
|
|
)
|
|
|
|
var (
|
|
ErrBlockValidation = errors.New("Block failed sanity check")
|
|
ErrBlockVerification = errors.New("Block failed to be consistent with the current blockchain")
|
|
)
|
|
|
|
// Blockchain holds the state of the chain
|
|
type Chain struct {
|
|
db *database.LDB
|
|
net protocol.Magic
|
|
}
|
|
|
|
func New(db *database.LDB, net protocol.Magic) *Chain {
|
|
|
|
marker := []byte("HasBeenInitialisedAlready")
|
|
|
|
_, err := db.Get(marker)
|
|
|
|
if err != nil {
|
|
// This is a new db
|
|
fmt.Println("New Database initialisation")
|
|
db.Put(marker, []byte{})
|
|
|
|
// We add the genesis block into the db
|
|
// along with the indexes for it
|
|
if net == protocol.MainNet {
|
|
|
|
genesisBlock, err := hex.DecodeString(chainparams.GenesisBlock)
|
|
if err != nil {
|
|
fmt.Println("Could not add genesis header into db")
|
|
db.Delete(marker)
|
|
return nil
|
|
}
|
|
r := bytes.NewReader(genesisBlock)
|
|
b := payload.Block{}
|
|
err = b.Decode(r)
|
|
|
|
if err != nil {
|
|
fmt.Println("could not Decode genesis block")
|
|
db.Delete(marker)
|
|
return nil
|
|
}
|
|
err = db.AddHeader(&b.BlockBase)
|
|
if err != nil {
|
|
fmt.Println("Could not add genesis header")
|
|
db.Delete(marker)
|
|
return nil
|
|
}
|
|
err = db.AddTransactions(b.Hash, b.Txs)
|
|
if err != nil {
|
|
fmt.Println("Could not add Genesis Transactions")
|
|
db.Delete(marker)
|
|
return nil
|
|
}
|
|
}
|
|
if net == protocol.TestNet {
|
|
fmt.Println("TODO: Setup the genesisBlock for TestNet")
|
|
return nil
|
|
}
|
|
|
|
}
|
|
return &Chain{
|
|
db,
|
|
net,
|
|
}
|
|
}
|
|
func (c *Chain) AddBlock(msg *payload.BlockMessage) error {
|
|
if !validateBlock(msg) {
|
|
return ErrBlockValidation
|
|
}
|
|
|
|
if !c.verifyBlock(msg) {
|
|
return ErrBlockVerification
|
|
}
|
|
|
|
fmt.Println("Block Hash is ", msg.Hash.String())
|
|
|
|
buf := new(bytes.Buffer)
|
|
err := msg.Encode(buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return c.db.Put(msg.Hash.Bytes(), buf.Bytes())
|
|
|
|
}
|
|
|
|
// validateBlock will check the transactions,
|
|
// merkleroot is good, signature is good,every that does not require state
|
|
// This may be moved to the syncmanager. This function should not be done in a seperate go-routine
|
|
// We are intentionally blocking here because if the block is invalid, we will
|
|
// disconnect from the peer.
|
|
// We could have this return an error instead; where the error could even
|
|
// say where the validation failed, for the logs.
|
|
func validateBlock(msg *payload.BlockMessage) bool {
|
|
return true
|
|
}
|
|
|
|
func (c *Chain) verifyBlock(msg *payload.BlockMessage) bool {
|
|
return true
|
|
}
|
|
|
|
// This will add a header into the db,
|
|
// indexing it also, this method will not
|
|
// run any checks, like if it links with a header
|
|
// previously in the db
|
|
// func (c *Chain) addHeaderNoCheck(header *payload.BlockBase) error {
|
|
|
|
// }
|
|
|
|
//addHeaders is not safe for concurrent access
|
|
func (c *Chain) ValidateHeaders(msg *payload.HeadersMessage) error {
|
|
|
|
table := database.NewTable(c.db, database.HEADER)
|
|
|
|
latestHash, err := table.Get(database.LATESTHEADER)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
key := latestHash
|
|
val, err := table.Get(key)
|
|
|
|
lastHeader := &payload.BlockBase{}
|
|
err = lastHeader.Decode(bytes.NewReader(val))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// TODO?:Maybe we should sort these headers using the Index
|
|
// We should not get them in mixed order, but doing it would not be expensive
|
|
// If they are already in order
|
|
|
|
// Do checks on headers
|
|
for _, currentHeader := range msg.Headers {
|
|
|
|
if lastHeader == nil {
|
|
// This should not happen as genesis header is added if new
|
|
// database, however we check nonetheless
|
|
return errors.New("Previous Header is nil")
|
|
}
|
|
|
|
// Check current hash links with previous
|
|
if currentHeader.PrevHash != lastHeader.Hash {
|
|
return errors.New("Last Header hash != current header Prev hash")
|
|
}
|
|
|
|
// Check current Index is one more than the previous Index
|
|
if currentHeader.Index != lastHeader.Index+1 {
|
|
return errors.New("Last Header Index != current header Index")
|
|
}
|
|
|
|
// Check current timestamp is more than the previous header's timestamp
|
|
if lastHeader.Timestamp > currentHeader.Timestamp {
|
|
return errors.New("Timestamp of Previous Header is more than Timestamp of current Header")
|
|
}
|
|
|
|
// NONONO:Do not check if current is more than 15 secs in future
|
|
// some blocks had delay from forks in past.
|
|
|
|
// NOTE: These are the only non-contextual checks we can do without the blockchain state
|
|
lastHeader = currentHeader
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Chain) AddHeaders(msg *payload.HeadersMessage) error {
|
|
for _, header := range msg.Headers {
|
|
if err := c.db.AddHeader(header); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|