neo-go/internal/fakechain/fakechain.go
Roman Khimov eeeb0f6f0e core: accept two-side channels for sub/unsub, read on unsub
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.
2022-08-19 22:08:40 +03:00

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")
}