neoneo-go/pkg/chain/chain.go
decentralisedkev 493d8f3d95
[chain] Refactor, add chaincfg and database initialisation (#243)
* [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
2019-03-28 20:23:50 +00:00

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(&params.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
}