eeeb0f6f0e
Blockchain's notificationDispatcher sends events to channels and these channels must be read from. Unfortunately, regular service shutdown procedure does unsubscription first (outside of the read loop) and only then drains the channel. While it waits for unsubscription request to be accepted notificationDispatcher can try pushing more data into the same channel which will lead to a deadlock. Reading in the same method solves this, any number of events can be pushed until unsub channel accepts the data.
471 lines
15 KiB
Go
471 lines
15 KiB
Go
package fakechain
|
|
|
|
import (
|
|
"errors"
|
|
"math"
|
|
"math/big"
|
|
"sync/atomic"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/mpt"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
uatomic "go.uber.org/atomic"
|
|
)
|
|
|
|
// FakeChain implements the Blockchainer interface, but does not provide real functionality.
|
|
type FakeChain struct {
|
|
config.ProtocolConfiguration
|
|
*mempool.Pool
|
|
blocksCh []chan *block.Block
|
|
Blockheight uint32
|
|
PoolTxF func(*transaction.Transaction) error
|
|
poolTxWithData func(*transaction.Transaction, interface{}, *mempool.Pool) error
|
|
blocks map[util.Uint256]*block.Block
|
|
hdrHashes map[uint32]util.Uint256
|
|
txs map[util.Uint256]*transaction.Transaction
|
|
VerifyWitnessF func() (int64, error)
|
|
MaxVerificationGAS int64
|
|
NotaryContractScriptHash util.Uint160
|
|
NotaryDepositExpiration uint32
|
|
PostBlock []func(func(*transaction.Transaction, *mempool.Pool, bool) bool, *mempool.Pool, *block.Block)
|
|
UtilityTokenBalance *big.Int
|
|
}
|
|
|
|
// FakeStateSync implements the StateSync interface.
|
|
type FakeStateSync struct {
|
|
IsActiveFlag uatomic.Bool
|
|
IsInitializedFlag uatomic.Bool
|
|
RequestHeaders uatomic.Bool
|
|
InitFunc func(h uint32) error
|
|
TraverseFunc func(root util.Uint256, process func(node mpt.Node, nodeBytes []byte) bool) error
|
|
AddMPTNodesFunc func(nodes [][]byte) error
|
|
}
|
|
|
|
// NewFakeChain returns a new FakeChain structure.
|
|
func NewFakeChain() *FakeChain {
|
|
return NewFakeChainWithCustomCfg(nil)
|
|
}
|
|
|
|
// NewFakeChainWithCustomCfg returns a new FakeChain structure with the specified protocol configuration.
|
|
func NewFakeChainWithCustomCfg(protocolCfg func(c *config.ProtocolConfiguration)) *FakeChain {
|
|
cfg := config.ProtocolConfiguration{Magic: netmode.UnitTestNet, P2PNotaryRequestPayloadPoolSize: 10}
|
|
if protocolCfg != nil {
|
|
protocolCfg(&cfg)
|
|
}
|
|
return &FakeChain{
|
|
Pool: mempool.New(10, 0, false),
|
|
PoolTxF: func(*transaction.Transaction) error { return nil },
|
|
poolTxWithData: func(*transaction.Transaction, interface{}, *mempool.Pool) error { return nil },
|
|
blocks: make(map[util.Uint256]*block.Block),
|
|
hdrHashes: make(map[uint32]util.Uint256),
|
|
txs: make(map[util.Uint256]*transaction.Transaction),
|
|
ProtocolConfiguration: cfg,
|
|
}
|
|
}
|
|
|
|
// PutBlock implements the Blockchainer interface.
|
|
func (chain *FakeChain) PutBlock(b *block.Block) {
|
|
chain.blocks[b.Hash()] = b
|
|
chain.hdrHashes[b.Index] = b.Hash()
|
|
atomic.StoreUint32(&chain.Blockheight, b.Index)
|
|
}
|
|
|
|
// PutHeader implements the Blockchainer interface.
|
|
func (chain *FakeChain) PutHeader(b *block.Block) {
|
|
chain.hdrHashes[b.Index] = b.Hash()
|
|
}
|
|
|
|
// PutTx implements the Blockchainer interface.
|
|
func (chain *FakeChain) PutTx(tx *transaction.Transaction) {
|
|
chain.txs[tx.Hash()] = tx
|
|
}
|
|
|
|
// InitVerificationContext initializes context for witness check.
|
|
func (chain *FakeChain) InitVerificationContext(ic *interop.Context, hash util.Uint160, witness *transaction.Witness) error {
|
|
panic("TODO")
|
|
}
|
|
|
|
// IsExtensibleAllowed implements the Blockchainer interface.
|
|
func (*FakeChain) IsExtensibleAllowed(uint160 util.Uint160) bool {
|
|
return true
|
|
}
|
|
|
|
// GetNatives implements the blockchainer.Blockchainer interface.
|
|
func (*FakeChain) GetNatives() []state.NativeContract {
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetNotaryDepositExpiration implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetNotaryDepositExpiration(acc util.Uint160) uint32 {
|
|
if chain.NotaryDepositExpiration != 0 {
|
|
return chain.NotaryDepositExpiration
|
|
}
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetNotaryContractScriptHash implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetNotaryContractScriptHash() util.Uint160 {
|
|
if !chain.NotaryContractScriptHash.Equals(util.Uint160{}) {
|
|
return chain.NotaryContractScriptHash
|
|
}
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetNotaryBalance implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetNotaryBalance(acc util.Uint160) *big.Int {
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetNotaryServiceFeePerKey implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetNotaryServiceFeePerKey() int64 {
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetBaseExecFee implements the Policer interface.
|
|
func (chain *FakeChain) GetBaseExecFee() int64 {
|
|
return interop.DefaultBaseExecFee
|
|
}
|
|
|
|
// GetStoragePrice implements the Policer interface.
|
|
func (chain *FakeChain) GetStoragePrice() int64 {
|
|
return native.DefaultStoragePrice
|
|
}
|
|
|
|
// GetMaxVerificationGAS implements the Policer interface.
|
|
func (chain *FakeChain) GetMaxVerificationGAS() int64 {
|
|
if chain.MaxVerificationGAS != 0 {
|
|
return chain.MaxVerificationGAS
|
|
}
|
|
panic("TODO")
|
|
}
|
|
|
|
// PoolTxWithData implements the Blockchainer interface.
|
|
func (chain *FakeChain) PoolTxWithData(t *transaction.Transaction, data interface{}, mp *mempool.Pool, feer mempool.Feer, verificationFunction func(t *transaction.Transaction, data interface{}) error) error {
|
|
return chain.poolTxWithData(t, data, mp)
|
|
}
|
|
|
|
// RegisterPostBlock implements the Blockchainer interface.
|
|
func (chain *FakeChain) RegisterPostBlock(f func(func(*transaction.Transaction, *mempool.Pool, bool) bool, *mempool.Pool, *block.Block)) {
|
|
chain.PostBlock = append(chain.PostBlock, f)
|
|
}
|
|
|
|
// GetConfig implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetConfig() config.ProtocolConfiguration {
|
|
return chain.ProtocolConfiguration
|
|
}
|
|
|
|
// CalculateClaimable implements the Blockchainer interface.
|
|
func (chain *FakeChain) CalculateClaimable(util.Uint160, uint32) (*big.Int, error) {
|
|
panic("TODO")
|
|
}
|
|
|
|
// FeePerByte implements Feer interface.
|
|
func (chain *FakeChain) FeePerByte() int64 {
|
|
panic("TODO")
|
|
}
|
|
|
|
// P2PSigExtensionsEnabled implements Feer interface.
|
|
func (chain *FakeChain) P2PSigExtensionsEnabled() bool {
|
|
return true
|
|
}
|
|
|
|
// AddHeaders implements the Blockchainer interface.
|
|
func (chain *FakeChain) AddHeaders(...*block.Header) error {
|
|
panic("TODO")
|
|
}
|
|
|
|
// AddBlock implements the Blockchainer interface.
|
|
func (chain *FakeChain) AddBlock(block *block.Block) error {
|
|
if block.Index == atomic.LoadUint32(&chain.Blockheight)+1 {
|
|
chain.PutBlock(block)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// BlockHeight implements the Feer interface.
|
|
func (chain *FakeChain) BlockHeight() uint32 {
|
|
return atomic.LoadUint32(&chain.Blockheight)
|
|
}
|
|
|
|
// HeaderHeight implements the Blockchainer interface.
|
|
func (chain *FakeChain) HeaderHeight() uint32 {
|
|
return atomic.LoadUint32(&chain.Blockheight)
|
|
}
|
|
|
|
// GetAppExecResults implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetAppExecResults(hash util.Uint256, trig trigger.Type) ([]state.AppExecResult, error) {
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetBlock implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetBlock(hash util.Uint256) (*block.Block, error) {
|
|
if b, ok := chain.blocks[hash]; ok {
|
|
return b, nil
|
|
}
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
// GetCommittee implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetCommittee() (keys.PublicKeys, error) {
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetContractState implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetContractState(hash util.Uint160) *state.Contract {
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetContractScriptHash implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetContractScriptHash(id int32) (util.Uint160, error) {
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetNativeContractScriptHash implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetNativeContractScriptHash(name string) (util.Uint160, error) {
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetHeaderHash implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetHeaderHash(n int) util.Uint256 {
|
|
if n < 0 || n > math.MaxUint32 {
|
|
return util.Uint256{}
|
|
}
|
|
return chain.hdrHashes[uint32(n)]
|
|
}
|
|
|
|
// GetHeader implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetHeader(hash util.Uint256) (*block.Header, error) {
|
|
b, err := chain.GetBlock(hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &b.Header, nil
|
|
}
|
|
|
|
// GetNextBlockValidators implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetNextBlockValidators() ([]*keys.PublicKey, error) {
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetNEP17Contracts implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetNEP11Contracts() []util.Uint160 {
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetNEP17Contracts implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetNEP17Contracts() []util.Uint160 {
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetNEP17LastUpdated implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetTokenLastUpdated(acc util.Uint160) (map[int32]uint32, error) {
|
|
panic("TODO")
|
|
}
|
|
|
|
// ForEachNEP17Transfer implements the Blockchainer interface.
|
|
func (chain *FakeChain) ForEachNEP11Transfer(util.Uint160, uint64, func(*state.NEP11Transfer) (bool, error)) error {
|
|
panic("TODO")
|
|
}
|
|
|
|
// ForEachNEP17Transfer implements the Blockchainer interface.
|
|
func (chain *FakeChain) ForEachNEP17Transfer(util.Uint160, uint64, func(*state.NEP17Transfer) (bool, error)) error {
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetValidators implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetValidators() ([]*keys.PublicKey, error) {
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetEnrollments implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetEnrollments() ([]state.Validator, error) {
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetStorageItem implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetStorageItem(id int32, key []byte) state.StorageItem {
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetTestVM implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) *interop.Context {
|
|
panic("TODO")
|
|
}
|
|
|
|
// CurrentBlockHash implements the Blockchainer interface.
|
|
func (chain *FakeChain) CurrentBlockHash() util.Uint256 {
|
|
return util.Uint256{}
|
|
}
|
|
|
|
// HasBlock implements the Blockchainer interface.
|
|
func (chain *FakeChain) HasBlock(h util.Uint256) bool {
|
|
_, ok := chain.blocks[h]
|
|
return ok
|
|
}
|
|
|
|
// HasTransaction implements the Blockchainer interface.
|
|
func (chain *FakeChain) HasTransaction(h util.Uint256) bool {
|
|
_, ok := chain.txs[h]
|
|
return ok
|
|
}
|
|
|
|
// GetTransaction implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetTransaction(h util.Uint256) (*transaction.Transaction, uint32, error) {
|
|
if tx, ok := chain.txs[h]; ok {
|
|
return tx, 1, nil
|
|
}
|
|
return nil, 0, errors.New("not found")
|
|
}
|
|
|
|
// GetMemPool implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetMemPool() *mempool.Pool {
|
|
return chain.Pool
|
|
}
|
|
|
|
// GetGoverningTokenBalance implements the Blockchainer interface.
|
|
func (chain *FakeChain) GetGoverningTokenBalance(acc util.Uint160) (*big.Int, uint32) {
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetUtilityTokenBalance implements Feer interface.
|
|
func (chain *FakeChain) GetUtilityTokenBalance(uint160 util.Uint160) *big.Int {
|
|
if chain.UtilityTokenBalance != nil {
|
|
return chain.UtilityTokenBalance
|
|
}
|
|
panic("TODO")
|
|
}
|
|
|
|
// PoolTx implements the Blockchainer interface.
|
|
func (chain *FakeChain) PoolTx(tx *transaction.Transaction, _ ...*mempool.Pool) error {
|
|
return chain.PoolTxF(tx)
|
|
}
|
|
|
|
// SubscribeForBlocks implements the Blockchainer interface.
|
|
func (chain *FakeChain) SubscribeForBlocks(ch chan *block.Block) {
|
|
chain.blocksCh = append(chain.blocksCh, ch)
|
|
}
|
|
|
|
// SubscribeForExecutions implements the Blockchainer interface.
|
|
func (chain *FakeChain) SubscribeForExecutions(ch chan *state.AppExecResult) {
|
|
panic("TODO")
|
|
}
|
|
|
|
// SubscribeForNotifications implements the Blockchainer interface.
|
|
func (chain *FakeChain) SubscribeForNotifications(ch chan *state.ContainedNotificationEvent) {
|
|
panic("TODO")
|
|
}
|
|
|
|
// SubscribeForTransactions implements the Blockchainer interface.
|
|
func (chain *FakeChain) SubscribeForTransactions(ch chan *transaction.Transaction) {
|
|
panic("TODO")
|
|
}
|
|
|
|
// VerifyTx implements the Blockchainer interface.
|
|
func (chain *FakeChain) VerifyTx(*transaction.Transaction) error {
|
|
panic("TODO")
|
|
}
|
|
|
|
// VerifyWitness implements the Blockchainer interface.
|
|
func (chain *FakeChain) VerifyWitness(util.Uint160, hash.Hashable, *transaction.Witness, int64) (int64, error) {
|
|
if chain.VerifyWitnessF != nil {
|
|
return chain.VerifyWitnessF()
|
|
}
|
|
panic("TODO")
|
|
}
|
|
|
|
// UnsubscribeFromBlocks implements the Blockchainer interface.
|
|
func (chain *FakeChain) UnsubscribeFromBlocks(ch chan *block.Block) {
|
|
for i, c := range chain.blocksCh {
|
|
if c == ch {
|
|
if i < len(chain.blocksCh) {
|
|
copy(chain.blocksCh[i:], chain.blocksCh[i+1:])
|
|
}
|
|
chain.blocksCh = chain.blocksCh[:len(chain.blocksCh)]
|
|
}
|
|
}
|
|
}
|
|
|
|
// UnsubscribeFromExecutions implements the Blockchainer interface.
|
|
func (chain *FakeChain) UnsubscribeFromExecutions(ch chan *state.AppExecResult) {
|
|
panic("TODO")
|
|
}
|
|
|
|
// UnsubscribeFromNotifications implements the Blockchainer interface.
|
|
func (chain *FakeChain) UnsubscribeFromNotifications(ch chan *state.ContainedNotificationEvent) {
|
|
panic("TODO")
|
|
}
|
|
|
|
// UnsubscribeFromTransactions implements the Blockchainer interface.
|
|
func (chain *FakeChain) UnsubscribeFromTransactions(ch chan *transaction.Transaction) {
|
|
panic("TODO")
|
|
}
|
|
|
|
// AddBlock implements the StateSync interface.
|
|
func (s *FakeStateSync) AddBlock(block *block.Block) error {
|
|
panic("TODO")
|
|
}
|
|
|
|
// AddHeaders implements the StateSync interface.
|
|
func (s *FakeStateSync) AddHeaders(...*block.Header) error {
|
|
panic("TODO")
|
|
}
|
|
|
|
// AddMPTNodes implements the StateSync interface.
|
|
func (s *FakeStateSync) AddMPTNodes(nodes [][]byte) error {
|
|
if s.AddMPTNodesFunc != nil {
|
|
return s.AddMPTNodesFunc(nodes)
|
|
}
|
|
panic("TODO")
|
|
}
|
|
|
|
// BlockHeight implements the StateSync interface.
|
|
func (s *FakeStateSync) BlockHeight() uint32 {
|
|
return 0
|
|
}
|
|
|
|
// IsActive implements the StateSync interface.
|
|
func (s *FakeStateSync) IsActive() bool { return s.IsActiveFlag.Load() }
|
|
|
|
// IsInitialized implements the StateSync interface.
|
|
func (s *FakeStateSync) IsInitialized() bool {
|
|
return s.IsInitializedFlag.Load()
|
|
}
|
|
|
|
// Init implements the StateSync interface.
|
|
func (s *FakeStateSync) Init(currChainHeight uint32) error {
|
|
if s.InitFunc != nil {
|
|
return s.InitFunc(currChainHeight)
|
|
}
|
|
panic("TODO")
|
|
}
|
|
|
|
// NeedHeaders implements the StateSync interface.
|
|
func (s *FakeStateSync) NeedHeaders() bool { return s.RequestHeaders.Load() }
|
|
|
|
// NeedMPTNodes implements the StateSync interface.
|
|
func (s *FakeStateSync) NeedMPTNodes() bool {
|
|
panic("TODO")
|
|
}
|
|
|
|
// Traverse implements the StateSync interface.
|
|
func (s *FakeStateSync) Traverse(root util.Uint256, process func(node mpt.Node, nodeBytes []byte) bool) error {
|
|
if s.TraverseFunc != nil {
|
|
return s.TraverseFunc(root, process)
|
|
}
|
|
panic("TODO")
|
|
}
|
|
|
|
// GetUnknownMPTNodesBatch implements the StateSync interface.
|
|
func (s *FakeStateSync) GetUnknownMPTNodesBatch(limit int) []util.Uint256 {
|
|
panic("TODO")
|
|
}
|