neoneo-go/pkg/core/dao/dao_test.go
Roman Khimov 7dc8fc443f dao: simplify buffer management for private DAO
Private DAO is only used in a single thread which means we can safely reuse
key/data buffers most of the time and handle it all in DAO.

Doesn't affect any benchmarks.
2022-02-17 22:27:39 +03:00

324 lines
9.1 KiB
Go

package dao
import (
"encoding/binary"
"errors"
"testing"
"github.com/nspcc-dev/neo-go/internal/random"
"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/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
func TestPutGetAndDecode(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), false, false)
serializable := &TestSerializable{field: random.String(4)}
hash := []byte{1}
require.NoError(t, dao.putWithBuffer(serializable, hash, io.NewBufBinWriter()))
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 TestPutGetStorageItem(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), false, false)
id := int32(random.Int(0, 1024))
key := []byte{0}
storageItem := state.StorageItem{}
dao.PutStorageItem(id, key, storageItem)
gotStorageItem := dao.GetStorageItem(id, key)
require.Equal(t, storageItem, gotStorageItem)
}
func TestDeleteStorageItem(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), false, false)
id := int32(random.Int(0, 1024))
key := []byte{0}
storageItem := state.StorageItem{}
dao.PutStorageItem(id, key, storageItem)
dao.DeleteStorageItem(id, key)
gotStorageItem := dao.GetStorageItem(id, key)
require.Nil(t, gotStorageItem)
}
func TestGetBlock_NotExists(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), false, false)
hash := random.Uint256()
block, err := dao.GetBlock(hash)
require.Error(t, err)
require.Nil(t, block)
}
func TestPutGetBlock(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), false, false)
b := &block.Block{
Header: block.Header{
Script: transaction.Witness{
VerificationScript: []byte{byte(opcode.PUSH1)},
InvocationScript: []byte{byte(opcode.NOP)},
},
},
}
hash := b.Hash()
appExecResult1 := &state.AppExecResult{
Container: hash,
Execution: state.Execution{
Trigger: trigger.OnPersist,
Events: []state.NotificationEvent{},
Stack: []stackitem.Item{},
},
}
appExecResult2 := &state.AppExecResult{
Container: hash,
Execution: state.Execution{
Trigger: trigger.PostPersist,
Events: []state.NotificationEvent{},
Stack: []stackitem.Item{},
},
}
err := dao.StoreAsBlock(b, appExecResult1, appExecResult2)
require.NoError(t, err)
gotBlock, err := dao.GetBlock(hash)
require.NoError(t, err)
require.NotNil(t, gotBlock)
gotAppExecResult, err := dao.GetAppExecResults(hash, trigger.All)
require.NoError(t, err)
require.Equal(t, 2, len(gotAppExecResult))
require.Equal(t, *appExecResult1, gotAppExecResult[0])
require.Equal(t, *appExecResult2, gotAppExecResult[1])
}
func TestGetVersion_NoVersion(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), false, false)
version, err := dao.GetVersion()
require.Error(t, err)
require.Equal(t, "", version.Value)
}
func TestGetVersion(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), false, false)
expected := Version{
StoragePrefix: 0x42,
P2PSigExtensions: true,
StateRootInHeader: true,
Value: "testVersion",
}
dao.PutVersion(expected)
actual, err := dao.GetVersion()
require.NoError(t, err)
require.Equal(t, expected, actual)
t.Run("invalid", func(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), false, false)
dao.Store.Put(storage.SYSVersion.Bytes(), []byte("0.1.2\x00x"))
_, err := dao.GetVersion()
require.Error(t, err)
})
t.Run("old format", func(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), false, false)
dao.Store.Put(storage.SYSVersion.Bytes(), []byte("0.1.2"))
version, err := dao.GetVersion()
require.NoError(t, err)
require.Equal(t, "0.1.2", version.Value)
})
}
func TestGetCurrentHeaderHeight_NoHeader(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), false, false)
height, err := dao.GetCurrentBlockHeight()
require.Error(t, err)
require.Equal(t, uint32(0), height)
}
func TestGetCurrentHeaderHeight_Store(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), false, false)
b := &block.Block{
Header: block.Header{
Script: transaction.Witness{
VerificationScript: []byte{byte(opcode.PUSH1)},
InvocationScript: []byte{byte(opcode.NOP)},
},
},
}
dao.StoreAsCurrentBlock(b)
height, err := dao.GetCurrentBlockHeight()
require.NoError(t, err)
require.Equal(t, uint32(0), height)
}
func TestStoreAsTransaction(t *testing.T) {
t.Run("P2PSigExtensions off", func(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), false, false)
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 1)
tx.Signers = append(tx.Signers, transaction.Signer{})
tx.Scripts = append(tx.Scripts, transaction.Witness{})
hash := tx.Hash()
aer := &state.AppExecResult{
Container: hash,
Execution: state.Execution{
Trigger: trigger.Application,
Events: []state.NotificationEvent{},
Stack: []stackitem.Item{},
},
}
err := dao.StoreAsTransaction(tx, 0, aer)
require.NoError(t, err)
err = dao.HasTransaction(hash)
require.NotNil(t, err)
gotAppExecResult, err := dao.GetAppExecResults(hash, trigger.All)
require.NoError(t, err)
require.Equal(t, 1, len(gotAppExecResult))
require.Equal(t, *aer, gotAppExecResult[0])
})
t.Run("P2PSigExtensions on", func(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), false, true)
conflictsH := util.Uint256{1, 2, 3}
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 1)
tx.Signers = append(tx.Signers, transaction.Signer{})
tx.Scripts = append(tx.Scripts, transaction.Witness{})
tx.Attributes = []transaction.Attribute{
{
Type: transaction.ConflictsT,
Value: &transaction.Conflicts{Hash: conflictsH},
},
}
hash := tx.Hash()
aer := &state.AppExecResult{
Container: hash,
Execution: state.Execution{
Trigger: trigger.Application,
Events: []state.NotificationEvent{},
Stack: []stackitem.Item{},
},
}
err := dao.StoreAsTransaction(tx, 0, aer)
require.NoError(t, err)
err = dao.HasTransaction(hash)
require.True(t, errors.Is(err, ErrAlreadyExists))
err = dao.HasTransaction(conflictsH)
require.True(t, errors.Is(err, ErrHasConflicts))
gotAppExecResult, err := dao.GetAppExecResults(hash, trigger.All)
require.NoError(t, err)
require.Equal(t, 1, len(gotAppExecResult))
require.Equal(t, *aer, gotAppExecResult[0])
})
}
func BenchmarkStoreAsTransaction(b *testing.B) {
dao := NewSimple(storage.NewMemoryStore(), false, true)
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 1)
tx.Attributes = []transaction.Attribute{
{
Type: transaction.ConflictsT,
Value: &transaction.Conflicts{
Hash: util.Uint256{1, 2, 3},
},
},
{
Type: transaction.ConflictsT,
Value: &transaction.Conflicts{
Hash: util.Uint256{4, 5, 6},
},
},
{
Type: transaction.ConflictsT,
Value: &transaction.Conflicts{
Hash: util.Uint256{7, 8, 9},
},
},
}
_ = tx.Hash()
aer := &state.AppExecResult{
Container: tx.Hash(),
Execution: state.Execution{
Trigger: trigger.Application,
Events: []state.NotificationEvent{},
Stack: []stackitem.Item{},
},
}
b.ResetTimer()
b.ReportAllocs()
for n := 0; n < b.N; n++ {
err := dao.StoreAsTransaction(tx, 1, aer)
if err != nil {
b.FailNow()
}
}
}
func TestMakeStorageItemKey(t *testing.T) {
var id int32 = 5
dao := NewSimple(storage.NewMemoryStore(), true, false)
expected := []byte{byte(storage.STStorage), 0, 0, 0, 0, 1, 2, 3}
binary.LittleEndian.PutUint32(expected[1:5], uint32(id))
actual := dao.makeStorageItemKey(id, []byte{1, 2, 3})
require.Equal(t, expected, actual)
expected = expected[0:5]
actual = dao.makeStorageItemKey(id, nil)
require.Equal(t, expected, actual)
expected = []byte{byte(storage.STTempStorage), 0, 0, 0, 0, 1, 2, 3}
binary.LittleEndian.PutUint32(expected[1:5], uint32(id))
dao.Version.StoragePrefix = storage.STTempStorage
actual = dao.makeStorageItemKey(id, []byte{1, 2, 3})
require.Equal(t, expected, actual)
}
func TestPutGetStateSyncPoint(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), true, false)
// empty store
_, err := dao.GetStateSyncPoint()
require.Error(t, err)
// non-empty store
var expected uint32 = 5
dao.PutStateSyncPoint(expected)
actual, err := dao.GetStateSyncPoint()
require.NoError(t, err)
require.Equal(t, expected, actual)
}
func TestPutGetStateSyncCurrentBlockHeight(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), true, false)
// empty store
_, err := dao.GetStateSyncCurrentBlockHeight()
require.Error(t, err)
// non-empty store
var expected uint32 = 5
dao.PutStateSyncCurrentBlockHeight(expected)
actual, err := dao.GetStateSyncCurrentBlockHeight()
require.NoError(t, err)
require.Equal(t, expected, actual)
}