30e5aa8f48
* [database] - Add Prefix method to interface - Convert leveldb error to `database error` - Be explicit with prefixedKey in `Table` as slices can be pointers * [protocol] - Add stringer method to protocol * [Chaindb] - Added saveBlock() which will allow us to save a block into the database. The block is broken up into transactions and Headers. The headers are saved as is. The transactions are saved as is, then the utxos in the transactions are collected to make the utxo db. - Verification for blocks and transactions will reside in the same package. Note that the save methods are all unexported, while the Get methods are exported. Making it so that any can call a get method, but only code in this package may save to the database. The other code which will reside in this package will be code verification logic. * [chaindb] - Added saveHeader function which saveHeaders uses - Update the latest header, each time we save a header instead of after a batch. This is so that we can call saveHeader without saveHeaders. This functionality can be rolled back if the performance of updating the header after a batch is significant - small refactor in test code
201 lines
4.7 KiB
Go
201 lines
4.7 KiB
Go
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
|
|
}
|