2020-04-07 09:41:12 +00:00
package dao
2019-11-25 17:39:11 +00:00
import (
2024-03-04 18:09:36 +00:00
"bytes"
2021-10-06 12:54:44 +00:00
"context"
2019-11-25 17:39:11 +00:00
"encoding/binary"
2020-10-15 11:45:29 +00:00
"errors"
2022-02-25 10:44:14 +00:00
"fmt"
2020-11-11 15:43:28 +00:00
iocore "io"
2022-05-31 20:10:56 +00:00
"math/big"
2022-04-20 14:47:48 +00:00
"sync"
2019-11-25 17:39:11 +00:00
2022-07-08 16:51:59 +00:00
"github.com/nspcc-dev/neo-go/pkg/config/limits"
2020-03-03 14:21:42 +00:00
"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"
2023-09-19 14:35:51 +00:00
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
2022-05-31 20:10:56 +00:00
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/io"
2020-11-11 15:43:28 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/util"
2022-05-31 17:10:20 +00:00
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
2019-11-25 17:39:11 +00:00
)
2021-05-12 20:17:03 +00:00
// HasTransaction errors.
2020-10-15 11:45:29 +00:00
var (
2022-04-20 18:30:09 +00:00
// ErrAlreadyExists is returned when the transaction exists in dao.
2020-10-15 11:45:29 +00:00
ErrAlreadyExists = errors . New ( "transaction already exists" )
2022-04-20 18:30:09 +00:00
// ErrHasConflicts is returned when the transaction is in the list of conflicting
2020-10-15 11:45:29 +00:00
// transactions which are already in dao.
ErrHasConflicts = errors . New ( "transaction has conflicts" )
2022-04-20 18:30:09 +00:00
// ErrInternalDBInconsistency is returned when the format of the retrieved DAO
2022-04-04 16:07:32 +00:00
// record is unexpected.
ErrInternalDBInconsistency = errors . New ( "internal DB inconsistency" )
2020-10-15 11:45:29 +00:00
)
2020-04-07 09:41:12 +00:00
// Simple is memCached wrapper around DB, simple DAO implementation.
type Simple struct {
2021-10-22 07:58:53 +00:00
Version Version
Store * storage . MemCachedStore
2022-04-20 14:47:48 +00:00
nativeCacheLock sync . RWMutex
nativeCache map [ int32 ] NativeContractCache
// nativeCachePS is the backend store that provides functionality to store
// and retrieve multi-tier native contract cache. The lowest Simple has its
// nativeCachePS set to nil.
nativeCachePS * Simple
2022-02-18 11:24:45 +00:00
private bool
2022-05-31 17:10:20 +00:00
serCtx * stackitem . SerializationContext
2022-02-16 20:33:53 +00:00
keyBuf [ ] byte
dataBuf * io . BufBinWriter
2019-11-25 17:39:11 +00:00
}
2022-04-20 14:47:48 +00:00
// NativeContractCache is an interface representing cache for a native contract.
// Cache can be copied to create a wrapper around current DAO layer. Wrapped cache
// can be persisted to the underlying DAO native cache.
type NativeContractCache interface {
// Copy returns a copy of native cache item that can safely be changed within
// the subsequent DAO operations.
Copy ( ) NativeContractCache
}
2022-04-20 18:30:09 +00:00
// NewSimple creates a new simple dao using the provided backend store.
2023-09-04 13:48:16 +00:00
func NewSimple ( backend storage . Store , stateRootInHeader bool ) * Simple {
2020-05-29 14:20:00 +00:00
st := storage . NewMemCachedStore ( backend )
2023-09-04 13:48:16 +00:00
return newSimple ( st , stateRootInHeader )
2022-02-16 16:13:06 +00:00
}
2023-09-04 13:48:16 +00:00
func newSimple ( st * storage . MemCachedStore , stateRootInHeader bool ) * Simple {
2021-09-27 13:35:25 +00:00
return & Simple {
2021-10-22 07:58:53 +00:00
Version : Version {
StoragePrefix : storage . STStorage ,
StateRootInHeader : stateRootInHeader ,
} ,
2022-04-20 14:47:48 +00:00
Store : st ,
nativeCache : make ( map [ int32 ] NativeContractCache ) ,
2021-09-27 13:35:25 +00:00
}
2020-04-03 06:49:01 +00:00
}
2022-04-20 18:30:09 +00:00
// GetBatch returns the currently accumulated DB changeset.
2020-04-07 09:41:12 +00:00
func ( dao * Simple ) GetBatch ( ) * storage . MemBatch {
return dao . Store . GetBatch ( )
2020-04-03 06:49:01 +00:00
}
2022-04-20 18:30:09 +00:00
// GetWrapped returns a new DAO instance with another layer of wrapped
2020-04-07 09:41:12 +00:00
// MemCachedStore around the current DAO Store.
2022-02-16 15:04:47 +00:00
func ( dao * Simple ) GetWrapped ( ) * Simple {
2023-09-04 13:48:16 +00:00
d := NewSimple ( dao . Store , dao . Version . StateRootInHeader )
2021-10-22 07:58:53 +00:00
d . Version = dao . Version
2022-04-20 14:47:48 +00:00
d . nativeCachePS = dao
2020-05-29 14:20:00 +00:00
return d
2019-12-12 18:04:55 +00:00
}
2022-04-20 18:30:09 +00:00
// GetPrivate returns a new DAO instance with another layer of private
2022-02-16 16:13:06 +00:00
// MemCachedStore around the current DAO Store.
func ( dao * Simple ) GetPrivate ( ) * Simple {
2022-04-20 15:42:49 +00:00
d := & Simple {
Version : dao . Version ,
keyBuf : dao . keyBuf ,
dataBuf : dao . dataBuf ,
2022-05-31 17:10:20 +00:00
serCtx : dao . serCtx ,
2022-04-20 15:42:49 +00:00
} // Inherit everything...
2022-02-18 11:18:56 +00:00
d . Store = storage . NewPrivateMemCachedStore ( dao . Store ) // except storage, wrap another layer.
2022-02-18 11:24:45 +00:00
d . private = true
2022-04-20 14:47:48 +00:00
d . nativeCachePS = dao
// Do not inherit cache from nativeCachePS; instead should create clear map:
// GetRWCache and GetROCache will retrieve cache from the underlying
// nativeCache if requested. The lowest underlying DAO MUST have its native
// cache initialized before access it, otherwise GetROCache and GetRWCache
// won't work properly.
d . nativeCache = make ( map [ int32 ] NativeContractCache )
2022-02-16 16:13:06 +00:00
return d
}
2019-11-25 17:39:11 +00:00
// GetAndDecode performs get operation and decoding with serializable structures.
2020-04-07 09:41:12 +00:00
func ( dao * Simple ) GetAndDecode ( entity io . Serializable , key [ ] byte ) error {
entityBytes , err := dao . Store . Get ( key )
2019-11-25 17:39:11 +00:00
if err != nil {
return err
}
reader := io . NewBinReaderFromBuf ( entityBytes )
entity . DecodeBinary ( reader )
return reader . Err
}
2020-03-17 09:06:46 +00:00
// putWithBuffer performs put operation using buf as a pre-allocated buffer for serialization.
2020-04-07 09:41:12 +00:00
func ( dao * Simple ) putWithBuffer ( entity io . Serializable , key [ ] byte , buf * io . BufBinWriter ) error {
2019-11-25 17:39:11 +00:00
entity . EncodeBinary ( buf . BinWriter )
if buf . Err != nil {
return buf . Err
}
2022-02-16 14:48:15 +00:00
dao . Store . Put ( key , buf . Bytes ( ) )
return nil
2019-11-25 17:39:11 +00:00
}
2021-11-18 13:37:42 +00:00
// -- start NEP-17 transfer info.
2020-03-11 15:22:46 +00:00
2022-02-16 20:33:53 +00:00
func ( dao * Simple ) makeTTIKey ( acc util . Uint160 ) [ ] byte {
key := dao . getKeyBuf ( 1 + util . Uint160Size )
key [ 0 ] = byte ( storage . STTokenTransferInfo )
copy ( key [ 1 : ] , acc . BytesBE ( ) )
return key
}
2021-11-18 13:37:42 +00:00
// GetTokenTransferInfo retrieves NEP-17 transfer info from the cache.
2021-11-16 16:18:06 +00:00
func ( dao * Simple ) GetTokenTransferInfo ( acc util . Uint160 ) ( * state . TokenTransferInfo , error ) {
2022-02-16 20:33:53 +00:00
key := dao . makeTTIKey ( acc )
2021-11-16 16:18:06 +00:00
bs := state . NewTokenTransferInfo ( )
2020-03-11 15:22:46 +00:00
err := dao . GetAndDecode ( bs , key )
2022-09-02 11:29:47 +00:00
if err != nil && ! errors . Is ( err , storage . ErrKeyNotFound ) {
2020-03-11 15:22:46 +00:00
return nil , err
}
return bs , nil
}
2021-11-18 13:37:42 +00:00
// PutTokenTransferInfo saves NEP-17 transfer info in the cache.
2021-11-16 16:18:06 +00:00
func ( dao * Simple ) PutTokenTransferInfo ( acc util . Uint160 , bs * state . TokenTransferInfo ) error {
2022-02-16 20:33:53 +00:00
return dao . putTokenTransferInfo ( acc , bs , dao . getDataBuf ( ) )
2020-03-17 09:06:46 +00:00
}
2021-11-16 16:18:06 +00:00
func ( dao * Simple ) putTokenTransferInfo ( acc util . Uint160 , bs * state . TokenTransferInfo , buf * io . BufBinWriter ) error {
2022-02-16 20:33:53 +00:00
return dao . putWithBuffer ( bs , dao . makeTTIKey ( acc ) , buf )
2020-03-11 15:22:46 +00:00
}
2021-11-18 13:37:42 +00:00
// -- end NEP-17 transfer info.
2020-03-11 15:22:46 +00:00
2020-03-05 14:11:58 +00:00
// -- start transfer log.
2022-02-16 20:33:53 +00:00
func ( dao * Simple ) getTokenTransferLogKey ( acc util . Uint160 , newestTimestamp uint64 , index uint32 , isNEP11 bool ) [ ] byte {
key := dao . getKeyBuf ( 1 + util . Uint160Size + 8 + 4 )
2021-11-16 20:09:04 +00:00
if isNEP11 {
key [ 0 ] = byte ( storage . STNEP11Transfers )
} else {
key [ 0 ] = byte ( storage . STNEP17Transfers )
}
2020-03-12 09:43:21 +00:00
copy ( key [ 1 : ] , acc . BytesBE ( ) )
2022-01-18 15:28:24 +00:00
binary . BigEndian . PutUint64 ( key [ 1 + util . Uint160Size : ] , newestTimestamp )
binary . BigEndian . PutUint32 ( key [ 1 + util . Uint160Size + 8 : ] , index )
2020-03-12 09:43:21 +00:00
return key
}
2022-01-18 15:28:24 +00:00
// SeekNEP17TransferLog executes f for each NEP-17 transfer in log starting from
// the transfer with the newest timestamp up to the oldest transfer. It continues
// iteration until false is returned from f. The last non-nil error is returned.
func ( dao * Simple ) SeekNEP17TransferLog ( acc util . Uint160 , newestTimestamp uint64 , f func ( * state . NEP17Transfer ) ( bool , error ) ) error {
2022-02-16 20:33:53 +00:00
key := dao . getTokenTransferLogKey ( acc , newestTimestamp , 0 , false )
2022-01-18 15:28:24 +00:00
prefixLen := 1 + util . Uint160Size
var seekErr error
dao . Store . Seek ( storage . SeekRange {
Prefix : key [ : prefixLen ] ,
Start : key [ prefixLen : prefixLen + 8 ] ,
Backwards : true ,
} , func ( k , v [ ] byte ) bool {
lg := & state . TokenTransferLog { Raw : v }
cont , err := lg . ForEachNEP17 ( f )
if err != nil {
seekErr = err
}
return cont
} )
return seekErr
}
// SeekNEP11TransferLog executes f for each NEP-11 transfer in log starting from
// the transfer with the newest timestamp up to the oldest transfer. It continues
// iteration until false is returned from f. The last non-nil error is returned.
func ( dao * Simple ) SeekNEP11TransferLog ( acc util . Uint160 , newestTimestamp uint64 , f func ( * state . NEP11Transfer ) ( bool , error ) ) error {
2022-02-16 20:33:53 +00:00
key := dao . getTokenTransferLogKey ( acc , newestTimestamp , 0 , true )
2022-01-18 15:28:24 +00:00
prefixLen := 1 + util . Uint160Size
var seekErr error
dao . Store . Seek ( storage . SeekRange {
Prefix : key [ : prefixLen ] ,
Start : key [ prefixLen : prefixLen + 8 ] ,
Backwards : true ,
} , func ( k , v [ ] byte ) bool {
lg := & state . TokenTransferLog { Raw : v }
cont , err := lg . ForEachNEP11 ( f )
if err != nil {
seekErr = err
}
return cont
} )
return seekErr
}
2021-11-16 16:18:06 +00:00
// GetTokenTransferLog retrieves transfer log from the cache.
2022-01-18 15:28:24 +00:00
func ( dao * Simple ) GetTokenTransferLog ( acc util . Uint160 , newestTimestamp uint64 , index uint32 , isNEP11 bool ) ( * state . TokenTransferLog , error ) {
2022-02-16 20:33:53 +00:00
key := dao . getTokenTransferLogKey ( acc , newestTimestamp , index , isNEP11 )
2020-04-07 09:41:12 +00:00
value , err := dao . Store . Get ( key )
2020-03-05 14:11:58 +00:00
if err != nil {
2022-09-02 11:29:47 +00:00
if errors . Is ( err , storage . ErrKeyNotFound ) {
2021-11-16 16:18:06 +00:00
return new ( state . TokenTransferLog ) , nil
2020-03-05 12:16:03 +00:00
}
2020-03-05 14:11:58 +00:00
return nil , err
}
2021-11-16 16:18:06 +00:00
return & state . TokenTransferLog { Raw : value } , nil
2020-03-05 14:11:58 +00:00
}
2022-04-20 18:30:09 +00:00
// PutTokenTransferLog saves the given transfer log in the cache.
2022-02-16 14:48:15 +00:00
func ( dao * Simple ) PutTokenTransferLog ( acc util . Uint160 , start uint64 , index uint32 , isNEP11 bool , lg * state . TokenTransferLog ) {
2022-02-16 20:33:53 +00:00
key := dao . getTokenTransferLogKey ( acc , start , index , isNEP11 )
2022-02-16 14:48:15 +00:00
dao . Store . Put ( key , lg . Raw )
2020-03-05 14:11:58 +00:00
}
// -- end transfer log.
2019-11-25 17:39:11 +00:00
// -- start notification event.
2022-02-16 20:33:53 +00:00
func ( dao * Simple ) makeExecutableKey ( hash util . Uint256 ) [ ] byte {
key := dao . getKeyBuf ( 1 + util . Uint256Size )
key [ 0 ] = byte ( storage . DataExecutable )
copy ( key [ 1 : ] , hash . BytesBE ( ) )
return key
}
2020-11-11 15:43:28 +00:00
// GetAppExecResults gets application execution results with the specified trigger from the
2019-11-25 17:39:11 +00:00
// given store.
2020-11-11 15:43:28 +00:00
func ( dao * Simple ) GetAppExecResults ( hash util . Uint256 , trig trigger . Type ) ( [ ] state . AppExecResult , error ) {
2022-02-16 20:33:53 +00:00
key := dao . makeExecutableKey ( hash )
2021-12-07 20:05:28 +00:00
bs , err := dao . Store . Get ( key )
2019-11-25 17:39:11 +00:00
if err != nil {
return nil , err
}
2022-04-04 16:28:36 +00:00
if len ( bs ) == 0 {
return nil , fmt . Errorf ( "%w: empty execution log" , ErrInternalDBInconsistency )
}
switch bs [ 0 ] {
2021-12-07 20:05:28 +00:00
case storage . ExecBlock :
2022-04-04 16:28:36 +00:00
r := io . NewBinReaderFromBuf ( bs )
_ = r . ReadB ( )
2021-12-07 20:05:28 +00:00
_ , err = block . NewTrimmedFromReader ( dao . Version . StateRootInHeader , r )
if err != nil {
return nil , err
}
2022-04-04 16:28:36 +00:00
result := make ( [ ] state . AppExecResult , 0 , 2 )
for {
aer := new ( state . AppExecResult )
aer . DecodeBinary ( r )
if r . Err != nil {
2022-09-02 11:29:47 +00:00
if errors . Is ( r . Err , iocore . EOF ) {
2022-04-04 16:28:36 +00:00
break
}
return nil , r . Err
}
if aer . Trigger & trig != 0 {
result = append ( result , * aer )
}
}
return result , nil
2021-12-07 20:05:28 +00:00
case storage . ExecTransaction :
2022-04-04 16:28:36 +00:00
_ , _ , aer , err := decodeTxAndExecResult ( bs )
if err != nil {
return nil , err
2022-04-04 09:47:29 +00:00
}
2022-04-04 16:28:36 +00:00
if aer . Trigger & trig != 0 {
return [ ] state . AppExecResult { * aer } , nil
}
return nil , nil
2022-04-04 16:07:32 +00:00
default :
2022-04-04 16:28:36 +00:00
return nil , fmt . Errorf ( "%w: unexpected executable prefix %d" , ErrInternalDBInconsistency , bs [ 0 ] )
2021-12-07 20:05:28 +00:00
}
2022-04-04 16:28:36 +00:00
}
// GetTxExecResult gets application execution result of the specified transaction
// and returns the transaction itself, its height and its AppExecResult.
func ( dao * Simple ) GetTxExecResult ( hash util . Uint256 ) ( uint32 , * transaction . Transaction , * state . AppExecResult , error ) {
key := dao . makeExecutableKey ( hash )
bs , err := dao . Store . Get ( key )
if err != nil {
return 0 , nil , nil , err
}
if len ( bs ) == 0 {
return 0 , nil , nil , fmt . Errorf ( "%w: empty execution log" , ErrInternalDBInconsistency )
}
if bs [ 0 ] != storage . ExecTransaction {
return 0 , nil , nil , storage . ErrKeyNotFound
}
return decodeTxAndExecResult ( bs )
}
// decodeTxAndExecResult decodes transaction, its height and execution result from
// the given executable bytes. It performs no executable prefix check.
func decodeTxAndExecResult ( buf [ ] byte ) ( uint32 , * transaction . Transaction , * state . AppExecResult , error ) {
2024-05-15 11:29:27 +00:00
if len ( buf ) == 1 + 4 { // conflict record stub.
2022-04-04 16:28:36 +00:00
return 0 , nil , nil , storage . ErrKeyNotFound
}
r := io . NewBinReaderFromBuf ( buf )
_ = r . ReadB ( )
h := r . ReadU32LE ( )
tx := & transaction . Transaction { }
tx . DecodeBinary ( r )
2021-12-07 20:05:28 +00:00
if r . Err != nil {
2022-04-04 16:28:36 +00:00
return 0 , nil , nil , r . Err
2021-12-07 20:05:28 +00:00
}
2022-04-04 16:28:36 +00:00
aer := new ( state . AppExecResult )
aer . DecodeBinary ( r )
if r . Err != nil {
return 0 , nil , nil , r . Err
2020-11-11 15:43:28 +00:00
}
2022-04-04 16:28:36 +00:00
return h , tx , aer , nil
2020-11-11 15:43:28 +00:00
}
2019-11-25 17:39:11 +00:00
// -- end notification event.
// -- start storage item.
2020-04-07 09:41:12 +00:00
// GetStorageItem returns StorageItem if it exists in the given store.
2021-03-05 14:06:54 +00:00
func ( dao * Simple ) GetStorageItem ( id int32 , key [ ] byte ) state . StorageItem {
2022-02-16 20:33:53 +00:00
b , err := dao . Store . Get ( dao . makeStorageItemKey ( id , key ) )
2019-11-25 17:39:11 +00:00
if err != nil {
return nil
}
2021-03-09 09:09:44 +00:00
return b
2019-11-25 17:39:11 +00:00
}
2022-04-20 18:30:09 +00:00
// PutStorageItem puts the given StorageItem for the given id with the given
2020-04-07 09:41:12 +00:00
// key into the given store.
2022-02-16 14:48:15 +00:00
func ( dao * Simple ) PutStorageItem ( id int32 , key [ ] byte , si state . StorageItem ) {
2022-02-16 20:33:53 +00:00
stKey := dao . makeStorageItemKey ( id , key )
2022-02-16 14:48:15 +00:00
dao . Store . Put ( stKey , si )
2019-11-25 17:39:11 +00:00
}
2022-05-31 20:10:56 +00:00
// PutBigInt serializaed and puts the given integer for the given id with the given
// key into the given store.
func ( dao * Simple ) PutBigInt ( id int32 , key [ ] byte , n * big . Int ) {
var buf [ bigint . MaxBytesLen ] byte
stData := bigint . ToPreallocatedBytes ( n , buf [ : ] )
dao . PutStorageItem ( id , key , stData )
}
2022-04-20 18:30:09 +00:00
// DeleteStorageItem drops a storage item for the given id with the
2020-04-07 09:41:12 +00:00
// given key from the store.
2022-02-16 14:48:15 +00:00
func ( dao * Simple ) DeleteStorageItem ( id int32 , key [ ] byte ) {
2022-02-16 20:33:53 +00:00
stKey := dao . makeStorageItemKey ( id , key )
2022-02-16 14:48:15 +00:00
dao . Store . Delete ( stKey )
2019-11-25 17:39:11 +00:00
}
2022-04-20 18:30:09 +00:00
// Seek executes f for all storage items matching the given `rng` (matching the given prefix and
// starting from the point specified). If the key or the value is to be used outside of f, they
2023-11-23 15:44:19 +00:00
// may not be copied. Seek continues iterating until false is returned from f. A requested prefix
// (if any non-empty) is trimmed before passing to f.
2022-01-17 17:41:51 +00:00
func ( dao * Simple ) Seek ( id int32 , rng storage . SeekRange , f func ( k , v [ ] byte ) bool ) {
2024-03-04 18:09:36 +00:00
rng . Prefix = bytes . Clone ( dao . makeStorageItemKey ( id , rng . Prefix ) ) // f() can use dao too.
2022-01-17 17:41:51 +00:00
dao . Store . Seek ( rng , func ( k , v [ ] byte ) bool {
return f ( k [ len ( rng . Prefix ) : ] , v )
2021-10-19 15:03:47 +00:00
} )
2021-10-04 14:01:42 +00:00
}
2022-04-20 18:30:09 +00:00
// SeekAsync sends all storage items matching the given `rng` (matching the given prefix and
2021-12-16 13:55:50 +00:00
// starting from the point specified) to a channel and returns the channel.
// Resulting keys and values may not be copied.
func ( dao * Simple ) SeekAsync ( ctx context . Context , id int32 , rng storage . SeekRange ) chan storage . KeyValue {
2024-03-04 18:09:36 +00:00
rng . Prefix = bytes . Clone ( dao . makeStorageItemKey ( id , rng . Prefix ) )
2021-12-16 13:55:50 +00:00
return dao . Store . SeekAsync ( ctx , rng , true )
2020-11-03 15:08:58 +00:00
}
2022-04-20 18:30:09 +00:00
// makeStorageItemKey returns the key used to store the StorageItem in the DB.
2022-02-16 20:33:53 +00:00
func ( dao * Simple ) makeStorageItemKey ( id int32 , key [ ] byte ) [ ] byte {
2020-06-18 10:50:30 +00:00
// 1 for prefix + 4 for Uint32 + len(key) for key
2022-02-16 20:33:53 +00:00
buf := dao . getKeyBuf ( 5 + len ( key ) )
buf [ 0 ] = byte ( dao . Version . StoragePrefix )
2020-06-18 10:50:30 +00:00
binary . LittleEndian . PutUint32 ( buf [ 1 : ] , uint32 ( id ) )
copy ( buf [ 5 : ] , key )
return buf
2019-11-25 17:39:11 +00:00
}
// -- end storage item.
// -- other.
// GetBlock returns Block by the given hash if it exists in the store.
2020-06-04 19:59:34 +00:00
func ( dao * Simple ) GetBlock ( hash util . Uint256 ) ( * block . Block , error ) {
2022-02-18 08:41:27 +00:00
return dao . getBlock ( dao . makeExecutableKey ( hash ) )
}
func ( dao * Simple ) getBlock ( key [ ] byte ) ( * block . Block , error ) {
2020-04-07 09:41:12 +00:00
b , err := dao . Store . Get ( key )
2019-11-25 17:39:11 +00:00
if err != nil {
2020-06-04 19:59:34 +00:00
return nil , err
2019-11-25 17:39:11 +00:00
}
2020-02-27 13:31:28 +00:00
2021-12-07 20:05:28 +00:00
r := io . NewBinReaderFromBuf ( b )
if r . ReadB ( ) != storage . ExecBlock {
2022-04-04 16:07:32 +00:00
// It may be a transaction.
return nil , storage . ErrKeyNotFound
2021-12-07 20:05:28 +00:00
}
block , err := block . NewTrimmedFromReader ( dao . Version . StateRootInHeader , r )
2019-11-25 17:39:11 +00:00
if err != nil {
2020-06-04 19:59:34 +00:00
return nil , err
2019-11-25 17:39:11 +00:00
}
2020-06-04 19:59:34 +00:00
return block , nil
2019-11-25 17:39:11 +00:00
}
2022-04-20 18:30:09 +00:00
// Version represents the current dao version.
2021-10-20 14:19:16 +00:00
type Version struct {
2021-11-03 09:55:33 +00:00
StoragePrefix storage . KeyPrefix
StateRootInHeader bool
P2PSigExtensions bool
P2PStateExchangeExtensions bool
KeepOnlyLatestState bool
2023-01-11 09:05:05 +00:00
Magic uint32
2021-11-03 09:55:33 +00:00
Value string
2021-10-20 14:19:16 +00:00
}
2021-10-22 07:58:53 +00:00
const (
stateRootInHeaderBit = 1 << iota
p2pSigExtensionsBit
2021-11-03 09:55:33 +00:00
p2pStateExchangeExtensionsBit
2021-10-22 08:09:47 +00:00
keepOnlyLatestStateBit
2021-10-22 07:58:53 +00:00
)
2021-10-20 14:19:16 +00:00
// FromBytes decodes v from a byte-slice.
func ( v * Version ) FromBytes ( data [ ] byte ) error {
if len ( data ) == 0 {
return errors . New ( "missing version" )
}
i := 0
2023-03-29 03:19:23 +00:00
for i < len ( data ) && data [ i ] != '\x00' {
i ++
2021-10-20 14:19:16 +00:00
}
if i == len ( data ) {
v . Value = string ( data )
return nil
}
2023-01-11 09:05:05 +00:00
if len ( data ) < i + 3 {
2021-10-22 07:58:53 +00:00
return errors . New ( "version is invalid" )
}
2021-10-20 14:19:16 +00:00
v . Value = string ( data [ : i ] )
2021-10-22 07:58:53 +00:00
v . StoragePrefix = storage . KeyPrefix ( data [ i + 1 ] )
v . StateRootInHeader = data [ i + 2 ] & stateRootInHeaderBit != 0
v . P2PSigExtensions = data [ i + 2 ] & p2pSigExtensionsBit != 0
2021-11-03 09:55:33 +00:00
v . P2PStateExchangeExtensions = data [ i + 2 ] & p2pStateExchangeExtensionsBit != 0
2021-10-22 08:09:47 +00:00
v . KeepOnlyLatestState = data [ i + 2 ] & keepOnlyLatestStateBit != 0
2023-01-11 09:05:05 +00:00
m := i + 3
if len ( data ) == m + 4 {
v . Magic = binary . LittleEndian . Uint32 ( data [ m : ] )
}
2021-10-20 14:19:16 +00:00
return nil
}
// Bytes encodes v to a byte-slice.
func ( v * Version ) Bytes ( ) [ ] byte {
2021-10-22 07:58:53 +00:00
var mask byte
if v . StateRootInHeader {
mask |= stateRootInHeaderBit
}
if v . P2PSigExtensions {
mask |= p2pSigExtensionsBit
}
2021-11-03 09:55:33 +00:00
if v . P2PStateExchangeExtensions {
mask |= p2pStateExchangeExtensionsBit
}
2021-10-22 08:09:47 +00:00
if v . KeepOnlyLatestState {
mask |= keepOnlyLatestStateBit
}
2023-10-11 12:41:04 +00:00
res := append ( [ ] byte ( v . Value ) , '\x00' , byte ( v . StoragePrefix ) , mask )
res = binary . LittleEndian . AppendUint32 ( res , v . Magic )
2023-01-11 09:05:05 +00:00
return res
2021-10-20 14:19:16 +00:00
}
2022-02-18 12:19:57 +00:00
func ( dao * Simple ) mkKeyPrefix ( k storage . KeyPrefix ) [ ] byte {
b := dao . getKeyBuf ( 1 )
b [ 0 ] = byte ( k )
return b
}
2019-11-25 17:39:11 +00:00
// GetVersion attempts to get the current version stored in the
2020-04-07 09:41:12 +00:00
// underlying store.
2021-10-20 14:19:16 +00:00
func ( dao * Simple ) GetVersion ( ) ( Version , error ) {
var version Version
2022-02-18 12:19:57 +00:00
data , err := dao . Store . Get ( dao . mkKeyPrefix ( storage . SYSVersion ) )
2021-10-20 14:19:16 +00:00
if err == nil {
err = version . FromBytes ( data )
}
return version , err
2019-11-25 17:39:11 +00:00
}
// GetCurrentBlockHeight returns the current block height found in the
2020-04-07 09:41:12 +00:00
// underlying store.
func ( dao * Simple ) GetCurrentBlockHeight ( ) ( uint32 , error ) {
2022-02-18 12:19:57 +00:00
b , err := dao . Store . Get ( dao . mkKeyPrefix ( storage . SYSCurrentBlock ) )
2019-11-25 17:39:11 +00:00
if err != nil {
return 0 , err
}
return binary . LittleEndian . Uint32 ( b [ 32 : 36 ] ) , nil
}
// GetCurrentHeaderHeight returns the current header height and hash from
2020-04-07 09:41:12 +00:00
// the underlying store.
func ( dao * Simple ) GetCurrentHeaderHeight ( ) ( i uint32 , h util . Uint256 , err error ) {
2019-11-25 17:39:11 +00:00
var b [ ] byte
2022-02-18 12:19:57 +00:00
b , err = dao . Store . Get ( dao . mkKeyPrefix ( storage . SYSCurrentHeader ) )
2019-11-25 17:39:11 +00:00
if err != nil {
return
}
i = binary . LittleEndian . Uint32 ( b [ 32 : 36 ] )
h , err = util . Uint256DecodeBytesLE ( b [ : 32 ] )
return
}
2022-04-20 18:30:09 +00:00
// GetStateSyncPoint returns current state synchronization point P.
2021-08-03 06:56:39 +00:00
func ( dao * Simple ) GetStateSyncPoint ( ) ( uint32 , error ) {
2022-02-18 12:19:57 +00:00
b , err := dao . Store . Get ( dao . mkKeyPrefix ( storage . SYSStateSyncPoint ) )
2021-08-03 06:56:39 +00:00
if err != nil {
return 0 , err
}
return binary . LittleEndian . Uint32 ( b ) , nil
}
2022-04-20 18:30:09 +00:00
// GetStateSyncCurrentBlockHeight returns the current block height stored during state
// synchronization process.
2021-08-03 06:56:39 +00:00
func ( dao * Simple ) GetStateSyncCurrentBlockHeight ( ) ( uint32 , error ) {
2022-02-18 12:19:57 +00:00
b , err := dao . Store . Get ( dao . mkKeyPrefix ( storage . SYSStateSyncCurrentBlockHeight ) )
2021-08-03 06:56:39 +00:00
if err != nil {
return 0 , err
}
return binary . LittleEndian . Uint32 ( b ) , nil
}
2022-11-18 20:06:39 +00:00
// GetHeaderHashes returns a page of header hashes retrieved from
2020-04-07 09:41:12 +00:00
// the given underlying store.
2022-11-18 20:06:39 +00:00
func ( dao * Simple ) GetHeaderHashes ( height uint32 ) ( [ ] util . Uint256 , error ) {
var hashes [ ] util . Uint256
2022-02-18 11:35:17 +00:00
2022-11-18 20:06:39 +00:00
key := dao . mkHeaderHashKey ( height )
b , err := dao . Store . Get ( key )
if err != nil {
return nil , err
}
2019-11-25 17:39:11 +00:00
2022-11-18 20:06:39 +00:00
br := io . NewBinReaderFromBuf ( b )
br . ReadArray ( & hashes )
if br . Err != nil {
return nil , br . Err
}
return hashes , nil
2019-11-25 17:39:11 +00:00
}
2022-10-20 10:59:19 +00:00
// DeleteHeaderHashes removes batches of header hashes starting from the one that
// contains header with index `since` up to the most recent batch. It assumes that
// all stored batches contain `batchSize` hashes.
func ( dao * Simple ) DeleteHeaderHashes ( since uint32 , batchSize int ) {
dao . Store . Seek ( storage . SeekRange {
Prefix : dao . mkKeyPrefix ( storage . IXHeaderHashList ) ,
Backwards : true ,
} , func ( k , _ [ ] byte ) bool {
first := binary . BigEndian . Uint32 ( k [ 1 : ] )
if first >= since {
dao . Store . Delete ( k )
return first != since
}
if first + uint32 ( batchSize ) - 1 >= since {
dao . Store . Delete ( k )
}
return false
} )
}
2019-11-25 17:39:11 +00:00
// GetTransaction returns Transaction and its height by the given hash
2024-05-15 11:29:27 +00:00
// if it exists in the store. It does not return conflict record stubs.
2020-04-07 09:41:12 +00:00
func ( dao * Simple ) GetTransaction ( hash util . Uint256 ) ( * transaction . Transaction , uint32 , error ) {
2022-02-16 20:33:53 +00:00
key := dao . makeExecutableKey ( hash )
2020-04-07 09:41:12 +00:00
b , err := dao . Store . Get ( key )
2019-11-25 17:39:11 +00:00
if err != nil {
return nil , 0 , err
}
2023-09-19 14:35:51 +00:00
if len ( b ) < 1 {
2020-10-15 11:45:29 +00:00
return nil , 0 , errors . New ( "bad transaction bytes" )
}
2021-12-07 20:05:28 +00:00
if b [ 0 ] != storage . ExecTransaction {
2022-04-04 16:07:32 +00:00
// It may be a block.
return nil , 0 , storage . ErrKeyNotFound
2021-12-07 20:05:28 +00:00
}
2023-09-19 14:35:51 +00:00
if len ( b ) == 1 + 4 { // storage.ExecTransaction + index
// It's a conflict record stub.
2020-10-15 11:45:29 +00:00
return nil , 0 , storage . ErrKeyNotFound
}
2019-11-25 17:39:11 +00:00
r := io . NewBinReaderFromBuf ( b )
2021-12-07 20:05:28 +00:00
_ = r . ReadB ( )
2019-11-25 17:39:11 +00:00
2019-12-12 15:52:23 +00:00
var height = r . ReadU32LE ( )
2019-11-25 17:39:11 +00:00
2021-03-25 16:18:01 +00:00
tx := & transaction . Transaction { }
2019-11-25 17:39:11 +00:00
tx . DecodeBinary ( r )
if r . Err != nil {
return nil , 0 , r . Err
}
return tx , height , nil
}
2020-04-07 09:41:12 +00:00
// PutVersion stores the given version in the underlying store.
2022-02-16 14:48:15 +00:00
func ( dao * Simple ) PutVersion ( v Version ) {
2021-10-22 07:58:53 +00:00
dao . Version = v
2022-02-18 12:19:57 +00:00
dao . Store . Put ( dao . mkKeyPrefix ( storage . SYSVersion ) , v . Bytes ( ) )
2019-11-25 17:39:11 +00:00
}
2022-04-20 18:30:09 +00:00
// PutCurrentHeader stores the current header.
2022-02-18 12:04:57 +00:00
func ( dao * Simple ) PutCurrentHeader ( h util . Uint256 , index uint32 ) {
buf := dao . getDataBuf ( )
buf . WriteBytes ( h . BytesLE ( ) )
buf . WriteU32LE ( index )
2022-02-18 12:19:57 +00:00
dao . Store . Put ( dao . mkKeyPrefix ( storage . SYSCurrentHeader ) , buf . Bytes ( ) )
2019-11-25 17:39:11 +00:00
}
2022-04-20 18:30:09 +00:00
// PutStateSyncPoint stores the current state synchronization point P.
2022-02-16 14:48:15 +00:00
func ( dao * Simple ) PutStateSyncPoint ( p uint32 ) {
2022-02-18 12:19:57 +00:00
buf := dao . getDataBuf ( )
buf . WriteU32LE ( p )
dao . Store . Put ( dao . mkKeyPrefix ( storage . SYSStateSyncPoint ) , buf . Bytes ( ) )
2021-08-03 06:56:39 +00:00
}
2022-04-20 18:30:09 +00:00
// PutStateSyncCurrentBlockHeight stores the current block height during state synchronization process.
2022-02-16 14:48:15 +00:00
func ( dao * Simple ) PutStateSyncCurrentBlockHeight ( h uint32 ) {
2022-02-18 12:19:57 +00:00
buf := dao . getDataBuf ( )
buf . WriteU32LE ( h )
dao . Store . Put ( dao . mkKeyPrefix ( storage . SYSStateSyncCurrentBlockHeight ) , buf . Bytes ( ) )
2021-08-03 06:56:39 +00:00
}
2022-02-18 11:54:05 +00:00
func ( dao * Simple ) mkHeaderHashKey ( h uint32 ) [ ] byte {
b := dao . getKeyBuf ( 1 + 4 )
b [ 0 ] = byte ( storage . IXHeaderHashList )
binary . BigEndian . PutUint32 ( b [ 1 : ] , h )
return b
}
// StoreHeaderHashes pushes a batch of header hashes into the store.
func ( dao * Simple ) StoreHeaderHashes ( hashes [ ] util . Uint256 , height uint32 ) error {
key := dao . mkHeaderHashKey ( height )
buf := dao . getDataBuf ( )
buf . WriteArray ( hashes )
if buf . Err != nil {
return buf . Err
}
dao . Store . Put ( key , buf . Bytes ( ) )
return nil
}
2020-10-15 11:45:29 +00:00
// HasTransaction returns nil if the given store does not contain the given
2022-04-20 18:30:09 +00:00
// Transaction hash. It returns an error in case the transaction is in chain
2023-07-14 08:57:18 +00:00
// or in the list of conflicting transactions. If non-zero signers are specified,
// then additional check against the conflicting transaction signers intersection
// is held. Do not omit signers in case if it's important to check the validity
2023-09-19 14:35:51 +00:00
// of a supposedly conflicting on-chain transaction. The retrieved conflict isn't
// checked against the maxTraceableBlocks setting if signers are omitted.
// HasTransaction does not consider the case of block executable.
func ( dao * Simple ) HasTransaction ( hash util . Uint256 , signers [ ] transaction . Signer , currentIndex uint32 , maxTraceableBlocks uint32 ) error {
2022-02-16 20:33:53 +00:00
key := dao . makeExecutableKey ( hash )
2020-10-15 11:45:29 +00:00
bytes , err := dao . Store . Get ( key )
if err != nil {
return nil
2019-11-25 17:39:11 +00:00
}
2020-10-15 11:45:29 +00:00
2023-09-19 14:35:51 +00:00
if len ( bytes ) < 5 { // (storage.ExecTransaction + index) for conflict record
2020-10-15 11:45:29 +00:00
return nil
}
core: allow transaction to conflict with block
Transaction
0x289c235dcdab8be7426d05f0fbb5e86c619f81481ea136493fa95deee5dbb7cc is
already on mainnet at block 5272006 and we can't do anything with it.
This transaction has genesis block hash in Conflicts attribute. It leads
to the following consequences:
1. Genesis block executable record is overwritten by conflict record
stub. Genesis block can't be retrieved anymore. This bug is described
in #3427.
2. Somehow this transaction has passed verification on NeoGo CN without
any warnings:
```
Apr 24 16:12:30 kangra neo-go[2453907]: 2024-04-24T16:12:30.865+0300 INFO initializing dbft {"height": 5272006, "view": 0, "index": 6, "role": "Backup"}
Apr 24 16:12:31 kangra neo-go[2453907]: 2024-04-24T16:12:31.245+0300 INFO persisted to disk {"blocks": 1, "keys": 37, "headerHeight": 5272005, "blockHeight": 5272005, "took": "14.548903ms"}
Apr 24 16:12:34 kangra neo-go[2453907]: 2024-04-24T16:12:34.977+0300 ERROR can't add SV-signed state root {"error": "stateroot mismatch at block 5272005: 9d5f95784f26c862d6f889f213aad1e3330611880c02330e88db8802c750aa46 vs d25304d518645df725014897d13bbf023919928e79074abcea48f31cf9f32a25"}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.820+0300 INFO received PrepareRequest {"validator": 5, "tx": 1}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.821+0300 INFO sending PrepareResponse {"height": 5272006, "view": 0}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.827+0300 INFO received PrepareResponse {"validator": 4}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.830+0300 INFO received PrepareResponse {"validator": 3}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.875+0300 INFO received PrepareResponse {"validator": 2}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.878+0300 INFO sending Commit {"height": 5272006, "view": 0}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.879+0300 INFO received Commit {"validator": 4}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.881+0300 INFO received PrepareResponse {"validator": 0}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.881+0300 INFO received Commit {"validator": 3}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.906+0300 INFO received Commit {"validator": 0}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.907+0300 INFO received PrepareResponse {"validator": 1}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.915+0300 INFO received Commit {"validator": 1}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.915+0300 INFO approving block {"height": 5272006, "hash": "6b111519537343ce579d04ccad71c43318b12c680d0f374dfcd466aa22643fb6", "tx_count": 1, "merkle": "ccb7dbe5ee5da93f4936a11e48819f616ce8b5fbf0056d42e78babcd5d239c28", "prev": "12ad6cc5d0cd357b9fc9fb0c1a016ba8014d3cdd5a96818598e6a40a1a4a2a21"}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.917+0300 WARN contract invocation failed {"tx": "289c235dcdab8be7426d05f0fbb5e86c619f81481ea136493fa95deee5dbb7cc", "block": 5272006, "error": "at instruction 86 (ASSERT): ASSERT failed"}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.950+0300 INFO initializing dbft {"height": 5272007, "view": 0, "index": 6, "role": "Primary"}
Apr 24 16:12:46 kangra neo-go[2453907]: 2024-04-24T16:12:46.256+0300 INFO persisted to disk {"blocks": 1, "keys": 67, "headerHeight": 5272006, "blockHeight": 5272006, "took": "16.576594ms"}
```
And thus, we must treat this transaction as valid for this behaviour
to be reproducable.
This commit contains two fixes:
1. Do not overwrite block executable records by conflict record stubs.
If some transaction conflicts with block, then just skip the conflict
record stub for this attribute since it's impossible to create
transaction with the same hash.
2. Do not fail verification for those transactions that have Conflicts
attribute with block hash inside. This one is controversial, but we
have to adjust this code to treat already accepted transaction as
valid.
Close #3427.
The transaction itself:
```
{
"id" : 1,
"jsonrpc" : "2.0",
"result" : {
"attributes" : [
{
"height" : 0,
"type" : "NotValidBefore"
},
{
"hash" : "0x1f4d1defa46faa5e7b9b8d3f79a06bec777d7c26c4aa5f6f5899a291daa87c15",
"type" : "Conflicts"
}
],
"blockhash" : "0xb63f6422aa66d4fc4d370f0d682cb11833c471adcc049d57ce4373531915116b",
"blocktime" : 1713964365700,
"confirmations" : 108335,
"hash" : "0x289c235dcdab8be7426d05f0fbb5e86c619f81481ea136493fa95deee5dbb7cc",
"netfee" : "237904",
"nonce" : 0,
"script" : "CxAMFIPvkoyXujYCRmgq9qEfMJQ4wNveDBSD75KMl7o2AkZoKvahHzCUOMDb3hTAHwwIdHJhbnNmZXIMFPVj6kC8KD1NDgXEjqMFs/Kgc0DvQWJ9W1I5",
"sender" : "NbcGB1tBEGM5MfhNbDAimvpJKzvVjLQ3jW",
"signers" : [
{
"account" : "0x649ca095e38a790d6c15ff78e0c6175099b428ac",
"scopes" : "None"
},
{
"account" : "0xdedbc03894301fa1f62a68460236ba978c92ef83",
"scopes" : "None"
}
],
"size" : 412,
"sysfee" : "997778",
"validuntilblock" : 5277629,
"version" : 0,
"vmstate" : "FAULT",
"witnesses" : [
{
"invocation" : "DECw8XNuyRg5vPeHxisQXlZ7VYNDxxK4xEm8zwpPyWJSSu+JaRKQxdrlPkXxXj34wc4ZSrZvKICGgPFE0ZHXhLPo",
"verification" : "DCEC+PI2tRSlp0wGwnjRuQdWdI0tBXNS7SlzSBBHFsaKUsdBVuezJw=="
},
{
"invocation" : "DEAxwi97t+rg9RsccOUzdJTJK7idbR7uUqQp0/0/ob9FbuW/tFius3/FOi82PDZtwdhk7s7KiNM/pU7vZLsgIbM0",
"verification" : "DCEDbInkzF5llzmgljE4HSMvtrNgPaz73XO5wgVJXLHNLXRBVuezJw=="
}
]
}
}
```
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-05-15 11:06:38 +00:00
if bytes [ 0 ] != storage . ExecTransaction {
// It's a block, thus no conflict. This path is needed since there's a transaction accepted on mainnet
// that conflicts with block. This transaction was declined by Go nodes, but accepted by C# nodes, and hence
// we need to adjust Go behaviour post-factum. Ref. #3427 and 0x289c235dcdab8be7426d05f0fbb5e86c619f81481ea136493fa95deee5dbb7cc.
return nil
}
2023-09-19 14:35:51 +00:00
if len ( bytes ) != 5 {
return ErrAlreadyExists // fully-qualified transaction
2023-07-14 08:57:18 +00:00
}
if len ( signers ) == 0 {
2020-10-15 11:45:29 +00:00
return ErrHasConflicts
}
2023-07-14 08:57:18 +00:00
2023-09-19 14:35:51 +00:00
if ! isTraceableBlock ( bytes [ 1 : ] , currentIndex , maxTraceableBlocks ) {
// The most fresh conflict record is already outdated.
return nil
}
2023-07-14 08:57:18 +00:00
for _ , s := range signers {
2023-09-19 14:35:51 +00:00
v , err := dao . Store . Get ( append ( key , s . Account . BytesBE ( ) ... ) )
if err == nil {
if isTraceableBlock ( v [ 1 : ] , currentIndex , maxTraceableBlocks ) {
return ErrHasConflicts
2023-07-14 08:57:18 +00:00
}
}
}
return nil
2019-11-25 17:39:11 +00:00
}
2023-09-19 14:35:51 +00:00
func isTraceableBlock ( indexBytes [ ] byte , height , maxTraceableBlocks uint32 ) bool {
index := binary . LittleEndian . Uint32 ( indexBytes )
return index <= height && index + maxTraceableBlocks > height
}
2020-08-31 19:11:49 +00:00
// StoreAsBlock stores given block as DataBlock. It can reuse given buffer for
// the purpose of value serialization.
2022-02-16 20:33:53 +00:00
func ( dao * Simple ) StoreAsBlock ( block * block . Block , aer1 * state . AppExecResult , aer2 * state . AppExecResult ) error {
2019-11-25 17:39:11 +00:00
var (
2022-02-16 20:33:53 +00:00
key = dao . makeExecutableKey ( block . Hash ( ) )
buf = dao . getDataBuf ( )
2019-11-25 17:39:11 +00:00
)
2021-12-07 20:05:28 +00:00
buf . WriteB ( storage . ExecBlock )
2022-03-18 07:49:25 +00:00
block . EncodeTrimmed ( buf . BinWriter )
2021-12-07 20:05:28 +00:00
if aer1 != nil {
2022-05-31 17:10:20 +00:00
aer1 . EncodeBinaryWithContext ( buf . BinWriter , dao . GetItemCtx ( ) )
2021-12-07 20:05:28 +00:00
}
if aer2 != nil {
2022-05-31 17:10:20 +00:00
aer2 . EncodeBinaryWithContext ( buf . BinWriter , dao . GetItemCtx ( ) )
2021-12-07 20:05:28 +00:00
}
2019-11-25 17:39:11 +00:00
if buf . Err != nil {
return buf . Err
}
2022-02-16 14:48:15 +00:00
dao . Store . Put ( key , buf . Bytes ( ) )
return nil
2019-11-25 17:39:11 +00:00
}
2022-04-20 18:30:09 +00:00
// DeleteBlock removes the block from dao. It's not atomic, so make sure you're
2022-02-16 13:13:12 +00:00
// using private MemCached instance here.
2022-02-16 20:33:53 +00:00
func ( dao * Simple ) DeleteBlock ( h util . Uint256 ) error {
key := dao . makeExecutableKey ( h )
2020-11-24 09:07:58 +00:00
2022-02-18 08:41:27 +00:00
b , err := dao . getBlock ( key )
2020-11-24 09:07:58 +00:00
if err != nil {
return err
}
2022-11-20 17:55:45 +00:00
err = dao . storeHeader ( key , & b . Header )
if err != nil {
return err
2020-11-24 09:07:58 +00:00
}
for _ , tx := range b . Transactions {
copy ( key [ 1 : ] , tx . Hash ( ) . BytesBE ( ) )
2022-02-16 14:48:15 +00:00
dao . Store . Delete ( key )
2023-04-07 08:26:58 +00:00
for _ , attr := range tx . GetAttributes ( transaction . ConflictsT ) {
hash := attr . Value . ( * transaction . Conflicts ) . Hash
copy ( key [ 1 : ] , hash . BytesBE ( ) )
2023-09-19 14:35:51 +00:00
v , err := dao . Store . Get ( key )
if err != nil {
return fmt . Errorf ( "failed to retrieve conflict record stub for %s (height %d, conflict %s): %w" , tx . Hash ( ) . StringLE ( ) , b . Index , hash . StringLE ( ) , err )
}
index := binary . LittleEndian . Uint32 ( v [ 1 : ] )
// We can check for `<=` here, but use equality comparison to be more precise
// and do not touch earlier conflict records (if any). Their removal must be triggered
// by the caller code.
if index == b . Index {
dao . Store . Delete ( key )
}
for _ , s := range tx . Signers {
sKey := append ( key , s . Account . BytesBE ( ) ... )
v , err := dao . Store . Get ( sKey )
if err != nil {
return fmt . Errorf ( "failed to retrieve conflict record for %s (height %d, conflict %s, signer %s): %w" , tx . Hash ( ) . StringLE ( ) , b . Index , hash . StringLE ( ) , address . Uint160ToString ( s . Account ) , err )
}
index = binary . LittleEndian . Uint32 ( v [ 1 : ] )
if index == b . Index {
dao . Store . Delete ( sKey )
}
}
2021-08-17 15:40:11 +00:00
}
2020-11-24 09:07:58 +00:00
}
2022-02-16 13:13:12 +00:00
return nil
2020-11-24 09:07:58 +00:00
}
2022-11-20 17:55:45 +00:00
// PurgeHeader completely removes specified header from dao. It differs from
// DeleteBlock in that it removes header anyway and does nothing except removing
// header. It does no checks for header existence.
func ( dao * Simple ) PurgeHeader ( h util . Uint256 ) {
key := dao . makeExecutableKey ( h )
dao . Store . Delete ( key )
}
2022-04-20 18:30:09 +00:00
// StoreHeader saves the block header into the store.
2022-02-18 08:55:06 +00:00
func ( dao * Simple ) StoreHeader ( h * block . Header ) error {
return dao . storeHeader ( dao . makeExecutableKey ( h . Hash ( ) ) , h )
}
func ( dao * Simple ) storeHeader ( key [ ] byte , h * block . Header ) error {
buf := dao . getDataBuf ( )
buf . WriteB ( storage . ExecBlock )
h . EncodeBinary ( buf . BinWriter )
buf . BinWriter . WriteB ( 0 )
if buf . Err != nil {
return buf . Err
}
dao . Store . Put ( key , buf . Bytes ( ) )
return nil
}
2022-04-20 18:30:09 +00:00
// StoreAsCurrentBlock stores the hash of the given block with prefix
// SYSCurrentBlock.
2022-02-16 20:33:53 +00:00
func ( dao * Simple ) StoreAsCurrentBlock ( block * block . Block ) {
buf := dao . getDataBuf ( )
2019-12-12 15:52:23 +00:00
h := block . Hash ( )
h . EncodeBinary ( buf . BinWriter )
buf . WriteU32LE ( block . Index )
2022-02-18 12:19:57 +00:00
dao . Store . Put ( dao . mkKeyPrefix ( storage . SYSCurrentBlock ) , buf . Bytes ( ) )
2019-11-25 17:39:11 +00:00
}
2024-05-15 11:29:27 +00:00
// StoreAsTransaction stores the given TX as DataTransaction. It also stores conflict records
// (hashes of transactions the given tx has conflicts with) as DataTransaction with value containing
// only five bytes: 1-byte [storage.ExecTransaction] executable prefix + 4-bytes-LE block index. It can reuse the given
2020-08-31 19:11:49 +00:00
// buffer for the purpose of value serialization.
2022-02-16 20:33:53 +00:00
func ( dao * Simple ) StoreAsTransaction ( tx * transaction . Transaction , index uint32 , aer * state . AppExecResult ) error {
key := dao . makeExecutableKey ( tx . Hash ( ) )
buf := dao . getDataBuf ( )
2021-12-07 20:05:28 +00:00
buf . WriteB ( storage . ExecTransaction )
2019-12-12 15:52:23 +00:00
buf . WriteU32LE ( index )
2021-08-17 15:33:09 +00:00
tx . EncodeBinary ( buf . BinWriter )
2021-12-07 20:05:28 +00:00
if aer != nil {
2022-05-31 17:10:20 +00:00
aer . EncodeBinaryWithContext ( buf . BinWriter , dao . GetItemCtx ( ) )
2021-12-07 20:05:28 +00:00
}
2019-11-25 17:39:11 +00:00
if buf . Err != nil {
return buf . Err
}
2023-09-19 14:35:51 +00:00
val := buf . Bytes ( )
dao . Store . Put ( key , val )
2023-07-14 08:57:18 +00:00
2023-09-19 14:35:51 +00:00
val = val [ : 5 ] // storage.ExecTransaction (1 byte) + index (4 bytes)
2023-04-07 08:26:58 +00:00
attrs := tx . GetAttributes ( transaction . ConflictsT )
for _ , attr := range attrs {
2023-09-19 14:35:51 +00:00
// Conflict record stub.
2023-04-07 08:26:58 +00:00
hash := attr . Value . ( * transaction . Conflicts ) . Hash
copy ( key [ 1 : ] , hash . BytesBE ( ) )
core: allow transaction to conflict with block
Transaction
0x289c235dcdab8be7426d05f0fbb5e86c619f81481ea136493fa95deee5dbb7cc is
already on mainnet at block 5272006 and we can't do anything with it.
This transaction has genesis block hash in Conflicts attribute. It leads
to the following consequences:
1. Genesis block executable record is overwritten by conflict record
stub. Genesis block can't be retrieved anymore. This bug is described
in #3427.
2. Somehow this transaction has passed verification on NeoGo CN without
any warnings:
```
Apr 24 16:12:30 kangra neo-go[2453907]: 2024-04-24T16:12:30.865+0300 INFO initializing dbft {"height": 5272006, "view": 0, "index": 6, "role": "Backup"}
Apr 24 16:12:31 kangra neo-go[2453907]: 2024-04-24T16:12:31.245+0300 INFO persisted to disk {"blocks": 1, "keys": 37, "headerHeight": 5272005, "blockHeight": 5272005, "took": "14.548903ms"}
Apr 24 16:12:34 kangra neo-go[2453907]: 2024-04-24T16:12:34.977+0300 ERROR can't add SV-signed state root {"error": "stateroot mismatch at block 5272005: 9d5f95784f26c862d6f889f213aad1e3330611880c02330e88db8802c750aa46 vs d25304d518645df725014897d13bbf023919928e79074abcea48f31cf9f32a25"}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.820+0300 INFO received PrepareRequest {"validator": 5, "tx": 1}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.821+0300 INFO sending PrepareResponse {"height": 5272006, "view": 0}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.827+0300 INFO received PrepareResponse {"validator": 4}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.830+0300 INFO received PrepareResponse {"validator": 3}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.875+0300 INFO received PrepareResponse {"validator": 2}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.878+0300 INFO sending Commit {"height": 5272006, "view": 0}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.879+0300 INFO received Commit {"validator": 4}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.881+0300 INFO received PrepareResponse {"validator": 0}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.881+0300 INFO received Commit {"validator": 3}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.906+0300 INFO received Commit {"validator": 0}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.907+0300 INFO received PrepareResponse {"validator": 1}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.915+0300 INFO received Commit {"validator": 1}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.915+0300 INFO approving block {"height": 5272006, "hash": "6b111519537343ce579d04ccad71c43318b12c680d0f374dfcd466aa22643fb6", "tx_count": 1, "merkle": "ccb7dbe5ee5da93f4936a11e48819f616ce8b5fbf0056d42e78babcd5d239c28", "prev": "12ad6cc5d0cd357b9fc9fb0c1a016ba8014d3cdd5a96818598e6a40a1a4a2a21"}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.917+0300 WARN contract invocation failed {"tx": "289c235dcdab8be7426d05f0fbb5e86c619f81481ea136493fa95deee5dbb7cc", "block": 5272006, "error": "at instruction 86 (ASSERT): ASSERT failed"}
Apr 24 16:12:45 kangra neo-go[2453907]: 2024-04-24T16:12:45.950+0300 INFO initializing dbft {"height": 5272007, "view": 0, "index": 6, "role": "Primary"}
Apr 24 16:12:46 kangra neo-go[2453907]: 2024-04-24T16:12:46.256+0300 INFO persisted to disk {"blocks": 1, "keys": 67, "headerHeight": 5272006, "blockHeight": 5272006, "took": "16.576594ms"}
```
And thus, we must treat this transaction as valid for this behaviour
to be reproducable.
This commit contains two fixes:
1. Do not overwrite block executable records by conflict record stubs.
If some transaction conflicts with block, then just skip the conflict
record stub for this attribute since it's impossible to create
transaction with the same hash.
2. Do not fail verification for those transactions that have Conflicts
attribute with block hash inside. This one is controversial, but we
have to adjust this code to treat already accepted transaction as
valid.
Close #3427.
The transaction itself:
```
{
"id" : 1,
"jsonrpc" : "2.0",
"result" : {
"attributes" : [
{
"height" : 0,
"type" : "NotValidBefore"
},
{
"hash" : "0x1f4d1defa46faa5e7b9b8d3f79a06bec777d7c26c4aa5f6f5899a291daa87c15",
"type" : "Conflicts"
}
],
"blockhash" : "0xb63f6422aa66d4fc4d370f0d682cb11833c471adcc049d57ce4373531915116b",
"blocktime" : 1713964365700,
"confirmations" : 108335,
"hash" : "0x289c235dcdab8be7426d05f0fbb5e86c619f81481ea136493fa95deee5dbb7cc",
"netfee" : "237904",
"nonce" : 0,
"script" : "CxAMFIPvkoyXujYCRmgq9qEfMJQ4wNveDBSD75KMl7o2AkZoKvahHzCUOMDb3hTAHwwIdHJhbnNmZXIMFPVj6kC8KD1NDgXEjqMFs/Kgc0DvQWJ9W1I5",
"sender" : "NbcGB1tBEGM5MfhNbDAimvpJKzvVjLQ3jW",
"signers" : [
{
"account" : "0x649ca095e38a790d6c15ff78e0c6175099b428ac",
"scopes" : "None"
},
{
"account" : "0xdedbc03894301fa1f62a68460236ba978c92ef83",
"scopes" : "None"
}
],
"size" : 412,
"sysfee" : "997778",
"validuntilblock" : 5277629,
"version" : 0,
"vmstate" : "FAULT",
"witnesses" : [
{
"invocation" : "DECw8XNuyRg5vPeHxisQXlZ7VYNDxxK4xEm8zwpPyWJSSu+JaRKQxdrlPkXxXj34wc4ZSrZvKICGgPFE0ZHXhLPo",
"verification" : "DCEC+PI2tRSlp0wGwnjRuQdWdI0tBXNS7SlzSBBHFsaKUsdBVuezJw=="
},
{
"invocation" : "DEAxwi97t+rg9RsccOUzdJTJK7idbR7uUqQp0/0/ob9FbuW/tFius3/FOi82PDZtwdhk7s7KiNM/pU7vZLsgIbM0",
"verification" : "DCEDbInkzF5llzmgljE4HSMvtrNgPaz73XO5wgVJXLHNLXRBVuezJw=="
}
]
}
}
```
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-05-15 11:06:38 +00:00
// A short path if there's a block with the matching hash. If it's there, then
// don't store the conflict record stub and conflict signers since it's a
// useless record, no transaction with the same hash is possible.
exec , err := dao . Store . Get ( key )
if err == nil {
if len ( exec ) > 0 && exec [ 0 ] != storage . ExecTransaction {
continue
}
}
2023-04-07 08:26:58 +00:00
dao . Store . Put ( key , val )
2023-09-19 14:35:51 +00:00
// Conflicting signers.
sKey := make ( [ ] byte , len ( key ) + util . Uint160Size )
copy ( sKey , key )
for _ , s := range tx . Signers {
copy ( sKey [ len ( key ) : ] , s . Account . BytesBE ( ) )
dao . Store . Put ( sKey , val )
2021-08-02 11:20:33 +00:00
}
}
return nil
}
2022-09-02 15:22:35 +00:00
func ( dao * Simple ) getKeyBuf ( l int ) [ ] byte {
2022-02-18 11:24:45 +00:00
if dao . private {
if dao . keyBuf == nil {
2022-07-08 16:51:59 +00:00
dao . keyBuf = make ( [ ] byte , 0 , 1 + 4 + limits . MaxStorageKeyLen ) // Prefix, uint32, key.
2022-02-18 11:24:45 +00:00
}
2022-09-02 15:22:35 +00:00
return dao . keyBuf [ : l ] // Should have enough capacity.
2022-02-16 20:33:53 +00:00
}
2022-09-02 15:22:35 +00:00
return make ( [ ] byte , l )
2022-02-16 20:33:53 +00:00
}
func ( dao * Simple ) getDataBuf ( ) * io . BufBinWriter {
2022-02-18 11:24:45 +00:00
if dao . private {
if dao . dataBuf == nil {
dao . dataBuf = io . NewBufBinWriter ( )
}
2022-02-16 20:33:53 +00:00
dao . dataBuf . Reset ( )
return dao . dataBuf
}
return io . NewBufBinWriter ( )
}
2022-05-31 17:10:20 +00:00
func ( dao * Simple ) GetItemCtx ( ) * stackitem . SerializationContext {
if dao . private {
if dao . serCtx == nil {
dao . serCtx = stackitem . NewSerializationContext ( )
}
return dao . serCtx
}
return stackitem . NewSerializationContext ( )
}
2019-12-12 18:17:13 +00:00
// Persist flushes all the changes made into the (supposedly) persistent
2021-11-22 07:41:40 +00:00
// underlying store. It doesn't block accesses to DAO from other threads.
2020-04-07 09:41:12 +00:00
func ( dao * Simple ) Persist ( ) ( int , error ) {
2022-04-20 14:47:48 +00:00
if dao . nativeCachePS != nil {
2022-05-06 07:27:48 +00:00
dao . nativeCacheLock . Lock ( )
dao . nativeCachePS . nativeCacheLock . Lock ( )
defer func ( ) {
dao . nativeCachePS . nativeCacheLock . Unlock ( )
dao . nativeCacheLock . Unlock ( )
} ( )
2022-04-20 14:47:48 +00:00
dao . persistNativeCache ( )
}
2020-04-07 09:41:12 +00:00
return dao . Store . Persist ( )
2019-12-12 18:17:13 +00:00
}
2020-12-24 16:32:27 +00:00
2021-11-22 07:41:40 +00:00
// PersistSync flushes all the changes made into the (supposedly) persistent
// underlying store. It's a synchronous version of Persist that doesn't allow
// other threads to work with DAO while flushing the Store.
func ( dao * Simple ) PersistSync ( ) ( int , error ) {
2022-04-20 14:47:48 +00:00
if dao . nativeCachePS != nil {
dao . nativeCacheLock . Lock ( )
dao . nativeCachePS . nativeCacheLock . Lock ( )
defer func ( ) {
dao . nativeCachePS . nativeCacheLock . Unlock ( )
dao . nativeCacheLock . Unlock ( )
} ( )
dao . persistNativeCache ( )
}
2021-11-22 07:41:40 +00:00
return dao . Store . PersistSync ( )
}
2022-04-20 14:47:48 +00:00
// persistNativeCache is internal unprotected method for native cache persisting.
// It does NO checks for nativeCachePS is not nil.
func ( dao * Simple ) persistNativeCache ( ) {
lower := dao . nativeCachePS
for id , nativeCache := range dao . nativeCache {
lower . nativeCache [ id ] = nativeCache
}
dao . nativeCache = nil
}
// GetROCache returns native contact cache. The cache CAN NOT be modified by
// the caller. It's the caller's duty to keep it unmodified.
func ( dao * Simple ) GetROCache ( id int32 ) NativeContractCache {
2022-05-06 07:27:48 +00:00
dao . nativeCacheLock . RLock ( )
defer dao . nativeCacheLock . RUnlock ( )
2022-04-20 14:47:48 +00:00
return dao . getCache ( id , true )
}
// GetRWCache returns native contact cache. The cache CAN BE safely modified
// by the caller.
func ( dao * Simple ) GetRWCache ( id int32 ) NativeContractCache {
2022-05-06 07:27:48 +00:00
dao . nativeCacheLock . Lock ( )
defer dao . nativeCacheLock . Unlock ( )
2022-04-20 14:47:48 +00:00
return dao . getCache ( id , false )
}
// getCache is an internal unlocked representation of GetROCache and GetRWCache.
func ( dao * Simple ) getCache ( k int32 , ro bool ) NativeContractCache {
if itm , ok := dao . nativeCache [ k ] ; ok {
// Don't need to create itm copy, because its value was already copied
2023-11-02 14:15:34 +00:00
// the first time it was retrieved from lower ps.
2022-04-20 14:47:48 +00:00
return itm
}
if dao . nativeCachePS != nil {
if ro {
return dao . nativeCachePS . GetROCache ( k )
}
v := dao . nativeCachePS . GetRWCache ( k )
if v != nil {
// Create a copy here in order not to modify the existing cache.
cp := v . Copy ( )
dao . nativeCache [ k ] = cp
return cp
}
}
return nil
}
// SetCache adds native contract cache to the cache map.
func ( dao * Simple ) SetCache ( id int32 , v NativeContractCache ) {
dao . nativeCacheLock . Lock ( )
defer dao . nativeCacheLock . Unlock ( )
dao . nativeCache [ id ] = v
}