neoneo-go/pkg/core/dao_test.go
Roman Khimov 23464401bc core/state: merge spent and unspent coins state, use it to store more things
This change reduces pressure on DB by doing the following things:
 * not storing additional KV pair for SpentCoin
 * storing Output right in the UnspentCoin, thus eliminating the need to get a
   full transaction from DB

At the same time it makes UnspentCoin more fat and hot, but it should probably
worth it.

Also drop `GetUnspentCoinStateOrNew` as it shouldn't ever existed, UTXOs
can't come out of nowhere.

1.5M block import time (VerifyBlocks disabled) on AMD Ryzen 5 1600/16GB/HDD,
before:
real    302m9.895s
user    96m17.200s
sys     13m37.084s

after:
real    159m16.551s
user    69m58.279s
sys     7m34.334s

So it's almost two-fold which is a great improvement.
2020-03-11 12:40:02 +03:00

281 lines
8.2 KiB
Go

package core
import (
"testing"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/internal/random"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/stretchr/testify/require"
)
func TestPutGetAndDecode(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
serializable := &TestSerializable{field: random.String(4)}
hash := []byte{1}
err := dao.Put(serializable, hash)
require.NoError(t, err)
gotAndDecoded := &TestSerializable{}
err = dao.GetAndDecode(gotAndDecoded, hash)
require.NoError(t, err)
}
// TestSerializable structure used in testing.
type TestSerializable struct {
field string
}
func (t *TestSerializable) EncodeBinary(writer *io.BinWriter) {
writer.WriteString(t.field)
}
func (t *TestSerializable) DecodeBinary(reader *io.BinReader) {
t.field = reader.ReadString()
}
func TestGetAccountStateOrNew_New(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
hash := random.Uint160()
createdAccount, err := dao.GetAccountStateOrNew(hash)
require.NoError(t, err)
require.NotNil(t, createdAccount)
}
func TestPutAndGetAccountStateOrNew(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
hash := random.Uint160()
accountState := &state.Account{ScriptHash: hash}
err := dao.PutAccountState(accountState)
require.NoError(t, err)
gotAccount, err := dao.GetAccountStateOrNew(hash)
require.NoError(t, err)
require.Equal(t, accountState.ScriptHash, gotAccount.ScriptHash)
}
func TestPutAndGetAssetState(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
id := random.Uint256()
assetState := &state.Asset{ID: id, Owner: keys.PublicKey{}}
err := dao.PutAssetState(assetState)
require.NoError(t, err)
gotAssetState, err := dao.GetAssetState(id)
require.NoError(t, err)
require.Equal(t, assetState, gotAssetState)
}
func TestPutAndGetContractState(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
contractState := &state.Contract{Script: []byte{}, ParamList: []smartcontract.ParamType{}}
hash := contractState.ScriptHash()
err := dao.PutContractState(contractState)
require.NoError(t, err)
gotContractState, err := dao.GetContractState(hash)
require.NoError(t, err)
require.Equal(t, contractState, gotContractState)
}
func TestDeleteContractState(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
contractState := &state.Contract{Script: []byte{}, ParamList: []smartcontract.ParamType{}}
hash := contractState.ScriptHash()
err := dao.PutContractState(contractState)
require.NoError(t, err)
err = dao.DeleteContractState(hash)
require.NoError(t, err)
gotContractState, err := dao.GetContractState(hash)
require.Error(t, err)
require.Nil(t, gotContractState)
}
func TestGetUnspentCoinState_Err(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
hash := random.Uint256()
gotUnspentCoinState, err := dao.GetUnspentCoinState(hash)
require.Error(t, err)
require.Nil(t, gotUnspentCoinState)
}
func TestPutGetUnspentCoinState(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
hash := random.Uint256()
unspentCoinState := &state.UnspentCoin{Height: 42, States: []state.OutputState{}}
err := dao.PutUnspentCoinState(hash, unspentCoinState)
require.NoError(t, err)
gotUnspentCoinState, err := dao.GetUnspentCoinState(hash)
require.NoError(t, err)
require.Equal(t, unspentCoinState, gotUnspentCoinState)
}
func TestGetValidatorStateOrNew_New(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
publicKey := &keys.PublicKey{}
validatorState, err := dao.GetValidatorStateOrNew(publicKey)
require.NoError(t, err)
require.NotNil(t, validatorState)
}
func TestPutGetValidatorState(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
publicKey := &keys.PublicKey{}
validatorState := &state.Validator{
PublicKey: publicKey,
Registered: false,
Votes: 0,
}
err := dao.PutValidatorState(validatorState)
require.NoError(t, err)
gotValidatorState, err := dao.GetValidatorState(publicKey)
require.NoError(t, err)
require.Equal(t, validatorState, gotValidatorState)
}
func TestDeleteValidatorState(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
publicKey := &keys.PublicKey{}
validatorState := &state.Validator{
PublicKey: publicKey,
Registered: false,
Votes: 0,
}
err := dao.PutValidatorState(validatorState)
require.NoError(t, err)
err = dao.DeleteValidatorState(validatorState)
require.NoError(t, err)
gotValidatorState, err := dao.GetValidatorState(publicKey)
require.Error(t, err)
require.Nil(t, gotValidatorState)
}
func TestGetValidators(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
publicKey := &keys.PublicKey{}
validatorState := &state.Validator{
PublicKey: publicKey,
Registered: false,
Votes: 0,
}
err := dao.PutValidatorState(validatorState)
require.NoError(t, err)
validators := dao.GetValidators()
require.Equal(t, validatorState, validators[0])
require.Len(t, validators, 1)
}
func TestPutGetAppExecResult(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
hash := random.Uint256()
appExecResult := &state.AppExecResult{TxHash: hash, Events: []state.NotificationEvent{}}
err := dao.PutAppExecResult(appExecResult)
require.NoError(t, err)
gotAppExecResult, err := dao.GetAppExecResult(hash)
require.NoError(t, err)
require.Equal(t, appExecResult, gotAppExecResult)
}
func TestPutGetStorageItem(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
hash := random.Uint160()
key := []byte{0}
storageItem := &state.StorageItem{Value: []uint8{}}
err := dao.PutStorageItem(hash, key, storageItem)
require.NoError(t, err)
gotStorageItem := dao.GetStorageItem(hash, key)
require.Equal(t, storageItem, gotStorageItem)
}
func TestDeleteStorageItem(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
hash := random.Uint160()
key := []byte{0}
storageItem := &state.StorageItem{Value: []uint8{}}
err := dao.PutStorageItem(hash, key, storageItem)
require.NoError(t, err)
err = dao.DeleteStorageItem(hash, key)
require.NoError(t, err)
gotStorageItem := dao.GetStorageItem(hash, key)
require.Nil(t, gotStorageItem)
}
func TestGetBlock_NotExists(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
hash := random.Uint256()
block, _, err := dao.GetBlock(hash)
require.Error(t, err)
require.Nil(t, block)
}
func TestPutGetBlock(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
b := &block.Block{
Base: block.Base{
Script: transaction.Witness{
VerificationScript: []byte{byte(opcode.PUSH1)},
InvocationScript: []byte{byte(opcode.NOP)},
},
},
}
hash := b.Hash()
err := dao.StoreAsBlock(b, 42)
require.NoError(t, err)
gotBlock, sysfee, err := dao.GetBlock(hash)
require.NoError(t, err)
require.NotNil(t, gotBlock)
require.EqualValues(t, 42, sysfee)
}
func TestGetVersion_NoVersion(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
version, err := dao.GetVersion()
require.Error(t, err)
require.Equal(t, "", version)
}
func TestGetVersion(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
err := dao.PutVersion("testVersion")
require.NoError(t, err)
version, err := dao.GetVersion()
require.NoError(t, err)
require.NotNil(t, version)
}
func TestGetCurrentHeaderHeight_NoHeader(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
height, err := dao.GetCurrentBlockHeight()
require.Error(t, err)
require.Equal(t, uint32(0), height)
}
func TestGetCurrentHeaderHeight_Store(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
b := &block.Block{
Base: block.Base{
Script: transaction.Witness{
VerificationScript: []byte{byte(opcode.PUSH1)},
InvocationScript: []byte{byte(opcode.NOP)},
},
},
}
err := dao.StoreAsCurrentBlock(b)
require.NoError(t, err)
height, err := dao.GetCurrentBlockHeight()
require.NoError(t, err)
require.Equal(t, uint32(0), height)
}
func TestStoreAsTransaction(t *testing.T) {
dao := newDao(storage.NewMemoryStore())
tx := &transaction.Transaction{Type: transaction.IssueType, Data: &transaction.IssueTX{}}
hash := tx.Hash()
err := dao.StoreAsTransaction(tx, 0)
require.NoError(t, err)
hasTransaction := dao.HasTransaction(hash)
require.True(t, hasTransaction)
}