package wiki import ( "context" "encoding/json" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" "github.com/woop-chain/woop/api/proto" "github.com/woop-chain/woop/block" "github.com/woop-chain/woop/core" "github.com/woop-chain/woop/core/state" "github.com/woop-chain/woop/core/types" "github.com/woop-chain/woop/core/vm" nodeconfig "github.com/woop-chain/woop/internal/configs/node" commonRPC "github.com/woop-chain/woop/rpc/common" "github.com/woop-chain/woop/shard" staking "github.com/woop-chain/woop/staking/types" lru "github.com/hashicorp/golang-lru" "github.com/libp2p/go-libp2p/core/peer" "github.com/pkg/errors" "golang.org/x/sync/singleflight" ) const ( // BloomBitsBlocks is the number of blocks a single bloom bit section vector // contains on the server side. BloomBitsBlocks uint64 = 4096 leaderCacheSize = 250 // Approx number of BLS keys in committee undelegationPayoutsCacheSize = 500 // max number of epochs to store in cache preStakingBlockRewardsCacheSize = 1024 // max number of block rewards to store in cache totalStakeCacheDuration = 20 // number of blocks where the returned total stake will remain the same // max number of blocks for which the map "validator address -> total delegation to validator" is stored stakeByBlockNumberCacheSize = 250 ) var ( // ErrFinalizedTransaction is returned if the transaction to be submitted is already on-chain ErrFinalizedTransaction = errors.New("transaction already finalized") ) // Woop implements the Woop full node service. type Woop struct { // Channel for shutting down the service ShutdownChan chan bool // Channel for shutting down the Woop BloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests BlockChain core.BlockChain BeaconChain core.BlockChain TxPool *core.TxPool CxPool *core.CxPool // CxPool is used to store the blockHashes of blocks containing cx receipts to be sent // DB interfaces BloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports NodeAPI NodeAPI // ChainID is used to identify which network we are using ChainID uint64 // EthCompatibleChainID is used to identify the Ethereum compatible chain ID EthChainID uint64 // RPCGasCap is the global gas cap for eth-call variants. RPCGasCap *big.Int `toml:",omitempty"` ShardID uint32 // Gas price suggestion oracle gpo *Oracle // Internals eventMux *event.TypeMux chainDb ethdb.Database // Block chain database // group for units of work which can be executed with duplicate suppression. group singleflight.Group // leaderCache to save on recomputation every epoch. leaderCache *lru.Cache // undelegationPayoutsCache to save on recomputation every epoch undelegationPayoutsCache *lru.Cache // preStakingBlockRewardsCache to save on recomputation for commonly checked blocks in epoch < staking epoch preStakingBlockRewardsCache *lru.Cache // totalStakeCache to save on recomputation for `totalStakeCacheDuration` blocks. totalStakeCache *totalStakeCache // stakeByBlockNumberCache to save on recomputation for `totalStakeCacheDuration` blocks stakeByBlockNumberCache *lru.Cache } // NodeAPI is the list of functions from node used to call rpc apis. type NodeAPI interface { AddPendingStakingTransaction(*staking.StakingTransaction) error AddPendingTransaction(newTx *types.Transaction) error Blockchain() core.BlockChain Beaconchain() core.BlockChain GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) GetStakingTransactionsHistory(address, txType, order string) ([]common.Hash, error) GetTransactionsCount(address, txType string) (uint64, error) GetStakingTransactionsCount(address, txType string) (uint64, error) GetTraceResultByHash(hash common.Hash) (json.RawMessage, error) IsCurrentlyLeader() bool IsOutOfSync(shardID uint32) bool SyncStatus(shardID uint32) (bool, uint64, uint64) SyncPeers() map[string]int ReportStakingErrorSink() types.TransactionErrorReports ReportPlainErrorSink() types.TransactionErrorReports PendingCXReceipts() []*types.CXReceiptsProof GetNodeBootTime() int64 PeerConnectivity() (int, int, int) ListPeer(topic string) []peer.ID ListTopic() []string ListBlockedPeer() []peer.ID GetConsensusInternal() commonRPC.ConsensusInternal IsBackup() bool SetNodeBackupMode(isBackup bool) bool // debug API GetConsensusMode() string GetConsensusPhase() string GetConsensusViewChangingID() uint64 GetConsensusCurViewID() uint64 GetConfig() commonRPC.Config ShutDown() GetLastSigningPower() (float64, error) GetLastSigningPower2() (float64, error) } // New creates a new Woop object (including the // initialisation of the common Woop object) func New( nodeAPI NodeAPI, txPool *core.TxPool, cxPool *core.CxPool, shardID uint32, ) *Woop { leaderCache, _ := lru.New(leaderCacheSize) undelegationPayoutsCache, _ := lru.New(undelegationPayoutsCacheSize) stakeByBlockNumberCache, _ := lru.New(stakeByBlockNumberCacheSize) preStakingBlockRewardsCache, _ := lru.New(preStakingBlockRewardsCacheSize) totalStakeCache := newTotalStakeCache(totalStakeCacheDuration) bloomIndexer := NewBloomIndexer(nodeAPI.Blockchain(), params.BloomBitsBlocks, params.BloomConfirms) bloomIndexer.Start(nodeAPI.Blockchain()) backend := &Woop{ ShutdownChan: make(chan bool), BloomRequests: make(chan chan *bloombits.Retrieval), BloomIndexer: bloomIndexer, BlockChain: nodeAPI.Blockchain(), BeaconChain: nodeAPI.Beaconchain(), TxPool: txPool, CxPool: cxPool, eventMux: new(event.TypeMux), chainDb: nodeAPI.Blockchain().ChainDb(), NodeAPI: nodeAPI, ChainID: nodeAPI.Blockchain().Config().ChainID.Uint64(), EthChainID: nodeAPI.Blockchain().Config().EthCompatibleChainID.Uint64(), ShardID: shardID, leaderCache: leaderCache, totalStakeCache: totalStakeCache, undelegationPayoutsCache: undelegationPayoutsCache, preStakingBlockRewardsCache: preStakingBlockRewardsCache, stakeByBlockNumberCache: stakeByBlockNumberCache, } // Setup gas price oracle config := nodeAPI.GetConfig().WoopConfig.GPO gpo := NewOracle(backend, &config) backend.gpo = gpo return backend } // SingleFlightRequest .. func (wiki *Woop) SingleFlightRequest( key string, fn func() (interface{}, error), ) (interface{}, error) { res, err, _ := wiki.group.Do(key, fn) return res, err } // SingleFlightForgetKey ... func (wiki *Woop) SingleFlightForgetKey(key string) { wiki.group.Forget(key) } // ProtocolVersion ... func (wiki *Woop) ProtocolVersion() int { return proto.ProtocolVersion } // IsLeader exposes if node is currently leader. func (wiki *Woop) IsLeader() bool { return wiki.NodeAPI.IsCurrentlyLeader() } // GetNodeMetadata returns the node metadata. func (wiki *Woop) GetNodeMetadata() commonRPC.NodeMetadata { var ( header = wiki.CurrentHeader() cfg = nodeconfig.GetShardConfig(header.ShardID()) blockEpoch *uint64 blsKeys []string c = commonRPC.C{} ) if header.ShardID() == shard.BeaconChainShardID { sched := shard.Schedule.InstanceForEpoch(header.Epoch()) b := sched.BlocksPerEpoch() blockEpoch = &b } if cfg.ConsensusPriKey != nil { for _, key := range cfg.ConsensusPriKey { blsKeys = append(blsKeys, key.Pub.Bytes.Hex()) } } c.TotalKnownPeers, c.Connected, c.NotConnected = wiki.NodeAPI.PeerConnectivity() syncPeers := wiki.NodeAPI.SyncPeers() consensusInternal := wiki.NodeAPI.GetConsensusInternal() return commonRPC.NodeMetadata{ BLSPublicKey: blsKeys, Version: nodeconfig.GetVersion(), NetworkType: string(cfg.GetNetworkType()), ChainConfig: *wiki.ChainConfig(), IsLeader: wiki.IsLeader(), ShardID: wiki.ShardID, CurrentBlockNum: header.Number().Uint64(), CurrentEpoch: header.Epoch().Uint64(), BlocksPerEpoch: blockEpoch, Role: cfg.Role().String(), DNSZone: cfg.DNSZone, Archival: cfg.GetArchival(), IsBackup: wiki.NodeAPI.IsBackup(), NodeBootTime: wiki.NodeAPI.GetNodeBootTime(), PeerID: nodeconfig.GetPeerID(), Consensus: consensusInternal, C: c, SyncPeers: syncPeers, } } // GetEVM returns a new EVM entity func (wiki *Woop) GetEVM(ctx context.Context, msg core.Message, state *state.DB, header *block.Header) (*vm.EVM, error) { state.SetBalance(msg.From(), math.MaxBig256) vmCtx := core.NewEVMContext(msg, header, wiki.BlockChain, nil) return vm.NewEVM(vmCtx, state, wiki.BlockChain.Config(), *wiki.BlockChain.GetVMConfig()), nil } // ChainDb .. func (wiki *Woop) ChainDb() ethdb.Database { return wiki.chainDb } // EventMux .. func (wiki *Woop) EventMux() *event.TypeMux { return wiki.eventMux } // BloomStatus ... // TODO: this is not implemented or verified yet for woop. func (wiki *Woop) BloomStatus() (uint64, uint64) { sections, _, _ := wiki.BloomIndexer.Sections() return BloomBitsBlocks, sections }