mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-27 03:58:06 +00:00
_pkg.dev: drop chain package
Duplicated by blockchain code in core. Refs. #307.
This commit is contained in:
parent
e636537844
commit
441f1d3bf5
4 changed files with 0 additions and 729 deletions
|
@ -1,137 +0,0 @@
|
|||
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
|
||||
height uint32
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// CurrentHeight returns the index of the block
|
||||
// at the tip of the chain
|
||||
func (c Chain) CurrentHeight() uint32 {
|
||||
return c.height
|
||||
}
|
|
@ -1,372 +0,0 @@
|
|||
package chain
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/database"
|
||||
"github.com/CityOfZion/neo-go/pkg/wire/payload"
|
||||
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/wire/util"
|
||||
)
|
||||
|
||||
var (
|
||||
// TX is the prefix used when inserting a tx into the db
|
||||
TX = []byte("TX")
|
||||
// HEADER is the prefix used when inserting a header into the db
|
||||
HEADER = []byte("HE")
|
||||
// LATESTHEADER is the prefix used when inserting the latests header into the db
|
||||
LATESTHEADER = []byte("LH")
|
||||
// UTXO is the prefix used when inserting a utxo into the db
|
||||
UTXO = []byte("UT")
|
||||
// LATESTBLOCK is the prefix used when inserting the latest block into the db
|
||||
LATESTBLOCK = []byte("LB")
|
||||
// BLOCKHASHTX is the prefix used when linking a blockhash to a given tx
|
||||
BLOCKHASHTX = []byte("BT")
|
||||
// BLOCKHASHHEIGHT is the prefix used when linking a blockhash to it's height
|
||||
// This is linked both ways
|
||||
BLOCKHASHHEIGHT = []byte("BH")
|
||||
// SCRIPTHASHUTXO is the prefix used when linking a utxo to a scripthash
|
||||
// This is linked both ways
|
||||
SCRIPTHASHUTXO = []byte("SU")
|
||||
)
|
||||
|
||||
// Chaindb is a wrapper around the db interface which adds an extra block chain specific layer on top.
|
||||
type Chaindb struct {
|
||||
db database.Database
|
||||
}
|
||||
|
||||
// This should not be exported for other callers.
|
||||
// It is safe-guarded by the chain's verification logic
|
||||
func (c *Chaindb) saveBlock(blk payload.Block, genesis bool) error {
|
||||
|
||||
latestBlockTable := database.NewTable(c.db, LATESTBLOCK)
|
||||
hashHeightTable := database.NewTable(c.db, BLOCKHASHHEIGHT)
|
||||
|
||||
// Save Txs and link to block hash
|
||||
err := c.saveTXs(blk.Txs, blk.Hash.Bytes(), genesis)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// LINK block height to hash - Both ways
|
||||
// This allows us to fetch a block using it's hash or it's height
|
||||
// Given the height, we will search the table to get the hash
|
||||
// We can then fetch all transactions in the tx table, which match that block hash
|
||||
height := uint32ToBytes(blk.Index)
|
||||
err = hashHeightTable.Put(height, blk.Hash.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = hashHeightTable.Put(blk.Hash.Bytes(), height)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add block as latest block
|
||||
// This also acts a Commit() for the block.
|
||||
// If an error occured, then this will be set to the previous block
|
||||
// This is useful because if the node suddently shut down while saving and the database was not corrupted
|
||||
// Then the node will see the latestBlock as the last saved block, and re-download the faulty block
|
||||
// Note: We check for the latest block on startup
|
||||
return latestBlockTable.Put([]byte(""), blk.Hash.Bytes())
|
||||
}
|
||||
|
||||
// Saves a tx and links each tx to the block it was found in
|
||||
// This should never be exported. Only way to add a tx, is through it's block
|
||||
func (c *Chaindb) saveTXs(txs []transaction.Transactioner, blockHash []byte, genesis bool) error {
|
||||
|
||||
for txIndex, tx := range txs {
|
||||
err := c.saveTx(tx, uint32(txIndex), blockHash, genesis)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Chaindb) saveTx(tx transaction.Transactioner, txIndex uint32, blockHash []byte, genesis bool) error {
|
||||
|
||||
txTable := database.NewTable(c.db, TX)
|
||||
blockTxTable := database.NewTable(c.db, BLOCKHASHTX)
|
||||
|
||||
// Save the whole tx using it's hash a key
|
||||
// In order to find a tx in this table, we need to know it's hash
|
||||
txHash, err := tx.ID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = txTable.Put(txHash.Bytes(), tx.BaseTx().Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// LINK TXhash to block
|
||||
// This allows us to fetch a tx by just knowing what block it was in
|
||||
// This is useful for when we want to re-construct a block from it's hash
|
||||
// In order to ge the tx, we must do a prefix search on blockHash
|
||||
// This will return a set of txHashes.
|
||||
//We can then use these hashes to search the txtable for the tx's we need
|
||||
key := bytesConcat(blockHash, uint32ToBytes(txIndex))
|
||||
err = blockTxTable.Put(key, txHash.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Save all of the utxos in a transaction
|
||||
// We do this additional save so that we can form a utxo database
|
||||
// and know when a transaction is a double spend.
|
||||
utxos := tx.BaseTx().Outputs
|
||||
for utxoIndex, utxo := range utxos {
|
||||
err := c.saveUTXO(utxo, uint16(utxoIndex), txHash.Bytes(), blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Do not check for spent utxos on the genesis block
|
||||
if genesis {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove all spent utxos
|
||||
// We do this so that once an output has been spent
|
||||
// It will be removed from the utxo database and cannot be spent again
|
||||
// If the output was never in the utxo database, this function will return an error
|
||||
txos := tx.BaseTx().Inputs
|
||||
for _, txo := range txos {
|
||||
err := c.removeUTXO(txo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveUTxo will save a utxo and link it to it's transaction and block
|
||||
func (c *Chaindb) saveUTXO(utxo *transaction.Output, utxoIndex uint16, txHash, blockHash []byte) error {
|
||||
|
||||
utxoTable := database.NewTable(c.db, UTXO)
|
||||
scripthashUTXOTable := database.NewTable(c.db, SCRIPTHASHUTXO)
|
||||
|
||||
// This is quite messy, we should (if possible) find a way to pass a Writer and Reader interface
|
||||
// Encode utxo into a buffer
|
||||
buf := new(bytes.Buffer)
|
||||
bw := &util.BinWriter{W: buf}
|
||||
if utxo.Encode(bw); bw.Err != nil {
|
||||
return bw.Err
|
||||
}
|
||||
|
||||
// Save UTXO
|
||||
// In order to find a utxo in the utxoTable
|
||||
// One must know the txHash that the utxo was in
|
||||
key := bytesConcat(txHash, uint16ToBytes(utxoIndex))
|
||||
if err := utxoTable.Put(key, buf.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// LINK utxo to scripthash
|
||||
// This allows us to find a utxo with the scriptHash
|
||||
// Since the key starts with scriptHash, we can look for the scriptHash prefix
|
||||
// and find all utxos for a given scriptHash.
|
||||
// Additionally, we can search for all utxos for a certain user in a certain block with scriptHash+blockHash
|
||||
// But this may not be of use to us. However, note that we cannot have just the scriptHash with the utxoIndex
|
||||
// as this may not be unique. If Kim/Dautt agree, we can change blockHash to blockHeight, which allows us
|
||||
// To get all utxos above a certain blockHeight. Question is; Would this be useful?
|
||||
newKey := bytesConcat(utxo.ScriptHash.Bytes(), blockHash, uint16ToBytes(utxoIndex))
|
||||
if err := scripthashUTXOTable.Put(newKey, key); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := scripthashUTXOTable.Put(key, newKey); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove
|
||||
func (c *Chaindb) removeUTXO(txo *transaction.Input) error {
|
||||
|
||||
utxoTable := database.NewTable(c.db, UTXO)
|
||||
scripthashUTXOTable := database.NewTable(c.db, SCRIPTHASHUTXO)
|
||||
|
||||
// Remove spent utxos from utxo database
|
||||
key := bytesConcat(txo.PrevHash.Bytes(), uint16ToBytes(txo.PrevIndex))
|
||||
err := utxoTable.Delete(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove utxos from scripthash table
|
||||
otherKey, err := scripthashUTXOTable.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := scripthashUTXOTable.Delete(otherKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := scripthashUTXOTable.Delete(key); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveHeaders will save a set of headers into the database
|
||||
func (c *Chaindb) saveHeaders(headers []*payload.BlockBase) error {
|
||||
|
||||
for _, hdr := range headers {
|
||||
err := c.saveHeader(hdr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveHeader saves a header into the database and updates the latest header
|
||||
// The headers are saved with their `blockheights` as Key
|
||||
// If we want to search for a header, we need to know it's index
|
||||
// Alternatively, we can search the hashHeightTable with the block index to get the hash
|
||||
// If the block has been saved.
|
||||
// The reason why headers are saved with their index as Key, is so that we can
|
||||
// increment the key to find out what block we should fetch next during the initial
|
||||
// block download, when we are saving thousands of headers
|
||||
func (c *Chaindb) saveHeader(hdr *payload.BlockBase) error {
|
||||
|
||||
headerTable := database.NewTable(c.db, HEADER)
|
||||
latestHeaderTable := database.NewTable(c.db, LATESTHEADER)
|
||||
|
||||
index := uint32ToBytes(hdr.Index)
|
||||
|
||||
byt, err := hdr.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = headerTable.Put(index, byt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update latest header
|
||||
return latestHeaderTable.Put([]byte(""), index)
|
||||
}
|
||||
|
||||
// GetHeaderFromHeight will get a header given it's block height
|
||||
func (c *Chaindb) GetHeaderFromHeight(index []byte) (*payload.BlockBase, error) {
|
||||
headerTable := database.NewTable(c.db, HEADER)
|
||||
hdrBytes, err := headerTable.Get(index)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader := bytes.NewReader(hdrBytes)
|
||||
|
||||
blockBase := &payload.BlockBase{}
|
||||
err = blockBase.Decode(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return blockBase, nil
|
||||
}
|
||||
|
||||
// GetLastHeader will get the header which was saved last in the database
|
||||
func (c *Chaindb) GetLastHeader() (*payload.BlockBase, error) {
|
||||
|
||||
latestHeaderTable := database.NewTable(c.db, LATESTHEADER)
|
||||
index, err := latestHeaderTable.Get([]byte(""))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.GetHeaderFromHeight(index)
|
||||
}
|
||||
|
||||
// GetBlockFromHash will return a block given it's hash
|
||||
func (c *Chaindb) GetBlockFromHash(blockHash []byte) (*payload.Block, error) {
|
||||
|
||||
blockTxTable := database.NewTable(c.db, BLOCKHASHTX)
|
||||
|
||||
// To get a block we need to fetch:
|
||||
// The transactions (1)
|
||||
// The header (2)
|
||||
|
||||
// Reconstruct block by fetching it's txs (1)
|
||||
var txs []transaction.Transactioner
|
||||
|
||||
// Get all Txhashes for this block
|
||||
txHashes, err := blockTxTable.Prefix(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get all Tx's given their hash
|
||||
txTable := database.NewTable(c.db, TX)
|
||||
for _, txHash := range txHashes {
|
||||
|
||||
// Fetch tx by it's hash
|
||||
txBytes, err := txTable.Get(txHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader := bufio.NewReader(bytes.NewReader(txBytes))
|
||||
|
||||
tx, err := transaction.FromReader(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txs = append(txs, tx)
|
||||
}
|
||||
|
||||
// Now fetch the header (2)
|
||||
// We have the block hash, but headers are stored with their `Height` as key.
|
||||
// We first search the `BlockHashHeight` table to get the height.
|
||||
//Then we search the headers table with the height
|
||||
hashHeightTable := database.NewTable(c.db, BLOCKHASHHEIGHT)
|
||||
height, err := hashHeightTable.Get(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hdr, err := c.GetHeaderFromHeight(height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Construct block
|
||||
block := &payload.Block{
|
||||
BlockBase: *hdr,
|
||||
Txs: txs,
|
||||
}
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// GetLastBlock will return the last block that has been saved
|
||||
func (c *Chaindb) GetLastBlock() (*payload.Block, error) {
|
||||
|
||||
latestBlockTable := database.NewTable(c.db, LATESTBLOCK)
|
||||
blockHash, err := latestBlockTable.Get([]byte(""))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.GetBlockFromHash(blockHash)
|
||||
}
|
||||
|
||||
func uint16ToBytes(x uint16) []byte {
|
||||
index := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(index, x)
|
||||
return index
|
||||
}
|
||||
|
||||
func uint32ToBytes(x uint32) []byte {
|
||||
index := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(index, x)
|
||||
return index
|
||||
}
|
||||
|
||||
func bytesConcat(args ...[]byte) []byte {
|
||||
var res []byte
|
||||
for _, arg := range args {
|
||||
res = append(res, arg...)
|
||||
}
|
||||
return res
|
||||
}
|
|
@ -1,201 +0,0 @@
|
|||
package chain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/rand"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/database"
|
||||
"github.com/CityOfZion/neo-go/pkg/wire/payload"
|
||||
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/wire/util"
|
||||
)
|
||||
|
||||
var s = rand.NewSource(time.Now().UnixNano())
|
||||
var r = rand.New(s)
|
||||
|
||||
func TestLastHeader(t *testing.T) {
|
||||
_, cdb, hdrs := saveRandomHeaders(t)
|
||||
|
||||
// Select last header from list of headers
|
||||
lastHeader := hdrs[len(hdrs)-1]
|
||||
// GetLastHeader from the database
|
||||
hdr, err := cdb.GetLastHeader()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, hdr.Index, lastHeader.Index)
|
||||
|
||||
// Clean up
|
||||
os.RemoveAll(database.DbDir)
|
||||
}
|
||||
|
||||
func TestSaveHeader(t *testing.T) {
|
||||
// save headers then fetch a random element
|
||||
|
||||
db, _, hdrs := saveRandomHeaders(t)
|
||||
|
||||
headerTable := database.NewTable(db, HEADER)
|
||||
// check that each header was saved
|
||||
for _, hdr := range hdrs {
|
||||
index := uint32ToBytes(hdr.Index)
|
||||
ok, err := headerTable.Has(index)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, ok)
|
||||
}
|
||||
|
||||
// Clean up
|
||||
os.RemoveAll(database.DbDir)
|
||||
}
|
||||
|
||||
func TestSaveBlock(t *testing.T) {
|
||||
|
||||
// Init databases
|
||||
db, err := database.New("temp.test")
|
||||
assert.Nil(t, err)
|
||||
|
||||
cdb := &Chaindb{db}
|
||||
|
||||
// Construct block0 and block1
|
||||
block0, block1 := twoBlocksLinked(t)
|
||||
|
||||
// Save genesis header
|
||||
err = cdb.saveHeader(&block0.BlockBase)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Save genesis block
|
||||
err = cdb.saveBlock(block0, true)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Test genesis block saved
|
||||
testBlockWasSaved(t, cdb, block0)
|
||||
|
||||
// Save block1 header
|
||||
err = cdb.saveHeader(&block1.BlockBase)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Save block1
|
||||
err = cdb.saveBlock(block1, false)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Test block1 was saved
|
||||
testBlockWasSaved(t, cdb, block1)
|
||||
|
||||
// Clean up
|
||||
os.RemoveAll(database.DbDir)
|
||||
}
|
||||
|
||||
func testBlockWasSaved(t *testing.T, cdb *Chaindb, block payload.Block) {
|
||||
// Fetch last block from database
|
||||
lastBlock, err := cdb.GetLastBlock()
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Get byte representation of last block from database
|
||||
byts, err := lastBlock.Bytes()
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Get byte representation of block that we saved
|
||||
blockBytes, err := block.Bytes()
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Should be equal
|
||||
assert.True(t, bytes.Equal(byts, blockBytes))
|
||||
}
|
||||
|
||||
func randomHeaders(t *testing.T) []*payload.BlockBase {
|
||||
assert := assert.New(t)
|
||||
hdrsMsg, err := payload.NewHeadersMessage()
|
||||
assert.Nil(err)
|
||||
|
||||
for i := 0; i < 2000; i++ {
|
||||
err = hdrsMsg.AddHeader(randomBlockBase(t))
|
||||
assert.Nil(err)
|
||||
}
|
||||
|
||||
return hdrsMsg.Headers
|
||||
}
|
||||
|
||||
func randomBlockBase(t *testing.T) *payload.BlockBase {
|
||||
|
||||
base := &payload.BlockBase{
|
||||
Version: r.Uint32(),
|
||||
PrevHash: randUint256(t),
|
||||
MerkleRoot: randUint256(t),
|
||||
Timestamp: r.Uint32(),
|
||||
Index: r.Uint32(),
|
||||
ConsensusData: r.Uint64(),
|
||||
NextConsensus: randUint160(t),
|
||||
Witness: transaction.Witness{
|
||||
InvocationScript: []byte{0, 1, 2, 34, 56},
|
||||
VerificationScript: []byte{0, 12, 3, 45, 66},
|
||||
},
|
||||
Hash: randUint256(t),
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
func randomTxs(t *testing.T) []transaction.Transactioner {
|
||||
|
||||
var txs []transaction.Transactioner
|
||||
for i := 0; i < 10; i++ {
|
||||
tx := transaction.NewContract(0)
|
||||
tx.AddInput(transaction.NewInput(randUint256(t), uint16(r.Int())))
|
||||
tx.AddOutput(transaction.NewOutput(randUint256(t), r.Int63(), randUint160(t)))
|
||||
txs = append(txs, tx)
|
||||
}
|
||||
return txs
|
||||
}
|
||||
|
||||
func saveRandomHeaders(t *testing.T) (database.Database, *Chaindb, []*payload.BlockBase) {
|
||||
db, err := database.New("temp.test")
|
||||
assert.Nil(t, err)
|
||||
|
||||
cdb := &Chaindb{db}
|
||||
|
||||
hdrs := randomHeaders(t)
|
||||
|
||||
err = cdb.saveHeaders(hdrs)
|
||||
assert.Nil(t, err)
|
||||
return db, cdb, hdrs
|
||||
}
|
||||
|
||||
func randUint256(t *testing.T) util.Uint256 {
|
||||
slice := make([]byte, 32)
|
||||
_, err := r.Read(slice)
|
||||
u, err := util.Uint256DecodeBytes(slice)
|
||||
assert.Nil(t, err)
|
||||
return u
|
||||
}
|
||||
func randUint160(t *testing.T) util.Uint160 {
|
||||
slice := make([]byte, 20)
|
||||
_, err := r.Read(slice)
|
||||
u, err := util.Uint160DecodeBytes(slice)
|
||||
assert.Nil(t, err)
|
||||
return u
|
||||
}
|
||||
|
||||
// twoBlocksLinked will return two blocks, the second block spends from the utxos in the first
|
||||
func twoBlocksLinked(t *testing.T) (payload.Block, payload.Block) {
|
||||
genesisBase := randomBlockBase(t)
|
||||
genesisTxs := randomTxs(t)
|
||||
genesisBlock := payload.Block{BlockBase: *genesisBase, Txs: genesisTxs}
|
||||
|
||||
var txs []transaction.Transactioner
|
||||
|
||||
// Form transactions that spend from the genesis block
|
||||
for _, tx := range genesisTxs {
|
||||
txHash, err := tx.ID()
|
||||
assert.Nil(t, err)
|
||||
newTx := transaction.NewContract(0)
|
||||
newTx.AddInput(transaction.NewInput(txHash, 0))
|
||||
newTx.AddOutput(transaction.NewOutput(randUint256(t), r.Int63(), randUint160(t)))
|
||||
txs = append(txs, newTx)
|
||||
}
|
||||
|
||||
nextBase := randomBlockBase(t)
|
||||
nextBlock := payload.Block{BlockBase: *nextBase, Txs: txs}
|
||||
|
||||
return genesisBlock, nextBlock
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package chain
|
||||
|
||||
// ValidationError occurs when verificatio of the object fails
|
||||
type ValidationError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (v ValidationError) Error() string {
|
||||
return v.msg
|
||||
}
|
||||
|
||||
// DatabaseError occurs when the chain fails to save the object in the database
|
||||
type DatabaseError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (d DatabaseError) Error() string {
|
||||
return d.msg
|
||||
}
|
Loading…
Reference in a new issue