493d8f3d95
* [chain] - Add basic chain cfg parameters - Added logic to insert genesis block, if it is a fresh database - changed SaveBlock to ProcessBlock - changed SaveHeaders to ProcessHeaders - Changed parameter from a wire message to the payload, for header and block processing - Added check in chain for when the block is in the future, i.e. not at the tip of the chain - Added custom error returns, to distinguish between a database error and a validation error
130 lines
3.4 KiB
Go
130 lines
3.4 KiB
Go
package chain
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/CityOfZion/neo-go/pkg/chaincfg"
|
|
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction"
|
|
"github.com/CityOfZion/neo-go/pkg/wire/protocol"
|
|
|
|
"github.com/CityOfZion/neo-go/pkg/database"
|
|
"github.com/CityOfZion/neo-go/pkg/wire/payload"
|
|
)
|
|
|
|
var (
|
|
// ErrBlockAlreadyExists happens when you try to save the same block twice
|
|
ErrBlockAlreadyExists = errors.New("this block has already been saved in the database")
|
|
|
|
// ErrFutureBlock happens when you try to save a block that is not the next block sequentially
|
|
ErrFutureBlock = errors.New("this is not the next block sequentially, that should be added to the chain")
|
|
)
|
|
|
|
// Chain represents a blockchain instance
|
|
type Chain struct {
|
|
Db *Chaindb
|
|
}
|
|
|
|
// New returns a new chain instance
|
|
func New(db database.Database, magic protocol.Magic) (*Chain, error) {
|
|
|
|
chain := &Chain{
|
|
Db: &Chaindb{db},
|
|
}
|
|
|
|
// Get last header saved to see if this is a fresh database
|
|
_, err := chain.Db.GetLastHeader()
|
|
if err == nil {
|
|
return chain, nil
|
|
}
|
|
|
|
if err != database.ErrNotFound {
|
|
return nil, err
|
|
}
|
|
|
|
// We have a database.ErrNotFound. Insert the genesisBlock
|
|
fmt.Printf("Starting a fresh database for %s\n", magic.String())
|
|
|
|
params, err := chaincfg.NetParams(magic)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = chain.Db.saveHeader(¶ms.GenesisBlock.BlockBase)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = chain.Db.saveBlock(params.GenesisBlock, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return chain, nil
|
|
}
|
|
|
|
// ProcessBlock verifies and saves the block in the database
|
|
// XXX: for now we will just save without verifying the block
|
|
// This function is called by the server and if an error is returned then
|
|
// the server informs the sync manager to redownload the block
|
|
// XXX:We should also check if the header is already saved in the database
|
|
// If not, then we need to validate the header with the rest of the chain
|
|
// For now we re-save the header
|
|
func (c *Chain) ProcessBlock(block payload.Block) error {
|
|
|
|
// Check if we already have this block saved
|
|
// XXX: We can optimise by implementing a Has() method
|
|
// caching the last block in memory
|
|
lastBlock, err := c.Db.GetLastBlock()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if lastBlock.Index > block.Index {
|
|
return ErrBlockAlreadyExists
|
|
}
|
|
|
|
if block.Index > lastBlock.Index+1 {
|
|
return ErrFutureBlock
|
|
}
|
|
|
|
err = c.verifyBlock(block)
|
|
if err != nil {
|
|
return ValidationError{err.Error()}
|
|
}
|
|
err = c.Db.saveBlock(block, false)
|
|
if err != nil {
|
|
return DatabaseError{err.Error()}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// VerifyBlock verifies whether a block is valid according
|
|
// to the rules of consensus
|
|
func (c *Chain) verifyBlock(block payload.Block) error {
|
|
return nil
|
|
}
|
|
|
|
// VerifyTx verifies whether a transaction is valid according
|
|
// to the rules of consensus
|
|
func (c *Chain) VerifyTx(tx transaction.Transactioner) error {
|
|
return nil
|
|
}
|
|
|
|
// ProcessHeaders will save the set of headers without validating
|
|
func (c *Chain) ProcessHeaders(hdrs []*payload.BlockBase) error {
|
|
|
|
err := c.verifyHeaders(hdrs)
|
|
if err != nil {
|
|
return ValidationError{err.Error()}
|
|
}
|
|
err = c.Db.saveHeaders(hdrs)
|
|
if err != nil {
|
|
return DatabaseError{err.Error()}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// verifyHeaders will be used to verify a batch of headers
|
|
// should only ever be called during the initial block download
|
|
// or when the node receives a HeadersMessage
|
|
func (c *Chain) verifyHeaders(hdrs []*payload.BlockBase) error {
|
|
return nil
|
|
}
|