package wiki import ( "context" "fmt" "math/big" v3 "github.com/woop-chain/woop/block/v3" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/event" "github.com/woop-chain/woop/block" "github.com/woop-chain/woop/core" "github.com/woop-chain/woop/core/rawdb" "github.com/woop-chain/woop/core/state" "github.com/woop-chain/woop/core/types" "github.com/woop-chain/woop/crypto/bls" internal_bls "github.com/woop-chain/woop/crypto/bls" "github.com/woop-chain/woop/eth/rpc" internal_common "github.com/woop-chain/woop/internal/common" "github.com/woop-chain/woop/internal/params" "github.com/woop-chain/woop/internal/utils" "github.com/woop-chain/woop/shard" "github.com/woop-chain/woop/staking/availability" stakingReward "github.com/woop-chain/woop/staking/reward" "github.com/pkg/errors" ) // ChainConfig ... func (wiki *Woop) ChainConfig() *params.ChainConfig { return wiki.BlockChain.Config() } // GetShardState ... func (wiki *Woop) GetShardState() (*shard.State, error) { return wiki.BlockChain.ReadShardState(wiki.BlockChain.CurrentHeader().Epoch()) } // GetBlockSigners .. func (wiki *Woop) GetBlockSigners( ctx context.Context, blockNum rpc.BlockNumber, ) (shard.SlotList, *internal_bls.Mask, error) { blk, err := wiki.BlockByNumber(ctx, blockNum) if err != nil { return nil, nil, err } blockWithSigners, err := wiki.BlockByNumber(ctx, blockNum+1) if err != nil { return nil, nil, err } if blockWithSigners == nil { return nil, nil, fmt.Errorf("block number %v not found", blockNum+1) } committee, err := wiki.GetValidators(blk.Epoch()) if err != nil { return nil, nil, err } pubKeys := make([]internal_bls.PublicKeyWrapper, len(committee.Slots)) for i, validator := range committee.Slots { key, err := bls.BytesToBLSPublicKey(validator.BLSPublicKey[:]) if err != nil { return nil, nil, err } pubKeys[i] = internal_bls.PublicKeyWrapper{ Bytes: validator.BLSPublicKey, Object: key, } } mask := internal_bls.NewMask(pubKeys) err = mask.SetMask(blockWithSigners.Header().LastCommitBitmap()) if err != nil { return nil, nil, err } return committee.Slots, mask, nil } // DetailedBlockSignerInfo contains all of the block singing information type DetailedBlockSignerInfo struct { // Signers are all the signers for the block Signers shard.SlotList // Committee when the block was signed. Committee shard.SlotList BlockHash common.Hash } // GetDetailedBlockSignerInfo fetches the block signer information for any non-genesis block func (wiki *Woop) GetDetailedBlockSignerInfo( ctx context.Context, blk *types.Block, ) (*DetailedBlockSignerInfo, error) { parentBlk, err := wiki.BlockByNumber(ctx, rpc.BlockNumber(blk.NumberU64()-1)) if err != nil { return nil, err } parentShardState, err := wiki.BlockChain.ReadShardState(parentBlk.Epoch()) if err != nil { return nil, err } committee, signers, _, err := availability.BallotResult( parentBlk.Header(), blk.Header(), parentShardState, blk.ShardID(), ) return &DetailedBlockSignerInfo{ Signers: signers, Committee: committee, BlockHash: blk.Hash(), }, nil } // PreStakingBlockRewards are the rewards for a block in the pre-staking era (epoch < staking epoch). type PreStakingBlockRewards map[common.Address]*big.Int // GetPreStakingBlockRewards for the given block number. // Calculated rewards are done exactly like chain.AccumulateRewardsAndCountSigs. func (wiki *Woop) GetPreStakingBlockRewards( ctx context.Context, blk *types.Block, ) (PreStakingBlockRewards, error) { if wiki.IsStakingEpoch(blk.Epoch()) { return nil, fmt.Errorf("block %v is in staking era", blk.Number()) } if cachedReward, ok := wiki.preStakingBlockRewardsCache.Get(blk.Hash()); ok { return cachedReward.(PreStakingBlockRewards), nil } rewards := PreStakingBlockRewards{} sigInfo, err := wiki.GetDetailedBlockSignerInfo(ctx, blk) if err != nil { return nil, err } last := big.NewInt(0) count := big.NewInt(int64(len(sigInfo.Signers))) for i, slot := range sigInfo.Signers { rewardsForThisAddr, ok := rewards[slot.EcdsaAddress] if !ok { rewardsForThisAddr = big.NewInt(0) } cur := big.NewInt(0) cur.Mul(stakingReward.PreStakedBlocks, big.NewInt(int64(i+1))).Div(cur, count) reward := big.NewInt(0).Sub(cur, last) rewards[slot.EcdsaAddress] = new(big.Int).Add(reward, rewardsForThisAddr) last = cur } // Report tx fees of the coinbase (== leader) receipts, err := wiki.GetReceipts(ctx, blk.Hash()) if err != nil { return nil, err } txFees := big.NewInt(0) for _, tx := range blk.Transactions() { txnHash := tx.HashByType() dbTx, _, _, receiptIndex := rawdb.ReadTransaction(wiki.ChainDb(), txnHash) if dbTx == nil { return nil, fmt.Errorf("could not find receipt for tx: %v", txnHash.String()) } if len(receipts) <= int(receiptIndex) { return nil, fmt.Errorf("invalid receipt indext %v (>= num receipts: %v) for tx: %v", receiptIndex, len(receipts), txnHash.String()) } txFee := new(big.Int).Mul(tx.GasPrice(), big.NewInt(int64(receipts[receiptIndex].GasUsed))) txFees = new(big.Int).Add(txFee, txFees) } if amt, ok := rewards[blk.Header().Coinbase()]; ok { rewards[blk.Header().Coinbase()] = new(big.Int).Add(amt, txFees) } else { rewards[blk.Header().Coinbase()] = txFees } wiki.preStakingBlockRewardsCache.Add(blk.Hash(), rewards) return rewards, nil } // GetLatestChainHeaders .. func (wiki *Woop) GetLatestChainHeaders() *block.HeaderPair { pair := &block.HeaderPair{ BeaconHeader: &block.Header{Header: v3.NewHeader()}, ShardHeader: &block.Header{Header: v3.NewHeader()}, } if wiki.BeaconChain != nil { pair.BeaconHeader = wiki.BeaconChain.CurrentHeader() } if wiki.BlockChain != nil { pair.ShardHeader = wiki.BlockChain.CurrentHeader() } return pair } // GetLastCrossLinks .. func (wiki *Woop) GetLastCrossLinks() ([]*types.CrossLink, error) { crossLinks := []*types.CrossLink{} for i := uint32(1); i < shard.Schedule.InstanceForEpoch(wiki.CurrentBlock().Epoch()).NumShards(); i++ { link, err := wiki.BlockChain.ReadShardLastCrossLink(i) if err != nil { return nil, err } crossLinks = append(crossLinks, link) } return crossLinks, nil } // CurrentBlock ... func (wiki *Woop) CurrentBlock() *types.Block { return types.NewBlockWithHeader(wiki.BlockChain.CurrentHeader()) } // CurrentHeader returns the current header from the local chain. func (wiki *Woop) CurrentHeader() *block.Header { return wiki.BlockChain.CurrentHeader() } // GetBlock returns block by hash. func (wiki *Woop) GetBlock(ctx context.Context, hash common.Hash) (*types.Block, error) { return wiki.BlockChain.GetBlockByHash(hash), nil } // GetHeader returns header by hash. func (wiki *Woop) GetHeader(ctx context.Context, hash common.Hash) (*block.Header, error) { return wiki.BlockChain.GetHeaderByHash(hash), nil } // GetCurrentBadBlocks .. func (wiki *Woop) GetCurrentBadBlocks() []core.BadBlock { return wiki.BlockChain.BadBlocks() } func (wiki *Woop) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) { if blockNr, ok := blockNrOrHash.Number(); ok { return wiki.BlockByNumber(ctx, blockNr) } if hash, ok := blockNrOrHash.Hash(); ok { header := wiki.BlockChain.GetHeaderByHash(hash) if header == nil { return nil, errors.New("header for hash not found") } if blockNrOrHash.RequireCanonical && wiki.BlockChain.GetCanonicalHash(header.Number().Uint64()) != hash { return nil, errors.New("hash is not currently canonical") } block := wiki.BlockChain.GetBlock(hash, header.Number().Uint64()) if block == nil { return nil, errors.New("header found, but block body is missing") } return block, nil } return nil, errors.New("invalid arguments; neither block nor hash specified") } // GetBalance returns balance of an given address. func (wiki *Woop) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*big.Int, error) { s, _, err := wiki.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if s == nil || err != nil { return nil, err } return s.GetBalance(address), s.Error() } // BlockByNumber ... func (wiki *Woop) BlockByNumber(ctx context.Context, blockNum rpc.BlockNumber) (*types.Block, error) { // Pending block is only known by the miner if blockNum == rpc.PendingBlockNumber { return nil, errors.New("not implemented") } // Otherwise resolve and return the block if blockNum == rpc.LatestBlockNumber { return wiki.BlockChain.CurrentBlock(), nil } return wiki.BlockChain.GetBlockByNumber(uint64(blockNum)), nil } // HeaderByNumber ... func (wiki *Woop) HeaderByNumber(ctx context.Context, blockNum rpc.BlockNumber) (*block.Header, error) { // Pending block is only known by the miner if blockNum == rpc.PendingBlockNumber { return nil, errors.New("not implemented") } // Otherwise resolve and return the block if blockNum == rpc.LatestBlockNumber { return wiki.BlockChain.CurrentBlock().Header(), nil } return wiki.BlockChain.GetHeaderByNumber(uint64(blockNum)), nil } // HeaderByHash ... func (wiki *Woop) HeaderByHash(ctx context.Context, blockHash common.Hash) (*block.Header, error) { header := wiki.BlockChain.GetHeaderByHash(blockHash) if header == nil { return nil, errors.New("Header is not found") } return header, nil } // StateAndHeaderByNumber ... func (wiki *Woop) StateAndHeaderByNumber(ctx context.Context, blockNum rpc.BlockNumber) (*state.DB, *block.Header, error) { // Pending state is only known by the miner if blockNum == rpc.PendingBlockNumber { return nil, nil, errors.New("not implemented") } // Otherwise resolve the block number and return its state header, err := wiki.HeaderByNumber(ctx, blockNum) if header == nil || err != nil { return nil, nil, err } stateDb, err := wiki.BlockChain.StateAt(header.Root()) return stateDb, header, err } func (wiki *Woop) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.DB, *block.Header, error) { if blockNr, ok := blockNrOrHash.Number(); ok { return wiki.StateAndHeaderByNumber(ctx, blockNr) } if hash, ok := blockNrOrHash.Hash(); ok { header, err := wiki.HeaderByHash(ctx, hash) if err != nil { return nil, nil, err } if header == nil { return nil, nil, errors.New("header for hash not found") } if blockNrOrHash.RequireCanonical && wiki.BlockChain.GetCanonicalHash(header.Number().Uint64()) != hash { return nil, nil, errors.New("hash is not currently canonical") } stateDb, err := wiki.BlockChain.StateAt(header.Root()) return stateDb, header, err } return nil, nil, errors.New("invalid arguments; neither block nor hash specified") } // GetLeaderAddress returns the one address of the leader, given the coinbaseAddr. // Note that the coinbaseAddr is overloaded with the BLS pub key hash in staking era. func (wiki *Woop) GetLeaderAddress(coinbaseAddr common.Address, epoch *big.Int) string { if wiki.IsStakingEpoch(epoch) { if leader, exists := wiki.leaderCache.Get(coinbaseAddr); exists { bech32, _ := internal_common.AddressToBech32(leader.(common.Address)) return bech32 } committee, err := wiki.GetValidators(epoch) if err != nil { return "" } for _, val := range committee.Slots { addr := utils.GetAddressFromBLSPubKeyBytes(val.BLSPublicKey[:]) wiki.leaderCache.Add(addr, val.EcdsaAddress) if addr == coinbaseAddr { bech32, _ := internal_common.AddressToBech32(val.EcdsaAddress) return bech32 } } return "" // Did not find matching address } bech32, _ := internal_common.AddressToBech32(coinbaseAddr) return bech32 } // Filter related APIs // GetLogs ... func (wiki *Woop) GetLogs(ctx context.Context, blockHash common.Hash, isEth bool) ([][]*types.Log, error) { receipts := wiki.BlockChain.GetReceiptsByHash(blockHash) if receipts == nil { return nil, errors.New("Missing receipts") } if isEth { block := wiki.BlockChain.GetBlockByHash(blockHash) if block == nil { return nil, errors.New("Missing block data") } txns := block.Transactions() for i := range receipts { if i < len(txns) { ethHash := txns[i].ConvertToEth().Hash() receipts[i].TxHash = ethHash for j := range receipts[i].Logs { // Override log txHash with receipt's receipts[i].Logs[j].TxHash = ethHash } } } } logs := make([][]*types.Log, len(receipts)) for i, receipt := range receipts { logs[i] = receipt.Logs } return logs, nil } // ServiceFilter ... func (wiki *Woop) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) { // TODO(dm): implement } // SubscribeNewTxsEvent subscribes new tx event. // TODO: this is not implemented or verified yet for woop. func (wiki *Woop) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { return wiki.TxPool.SubscribeNewTxsEvent(ch) } // SubscribeChainEvent subscribes chain event. // TODO: this is not implemented or verified yet for woop. func (wiki *Woop) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { return wiki.BlockChain.SubscribeChainEvent(ch) } // SubscribeChainHeadEvent subcribes chain head event. // TODO: this is not implemented or verified yet for woop. func (wiki *Woop) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { return wiki.BlockChain.SubscribeChainHeadEvent(ch) } // SubscribeChainSideEvent subcribes chain side event. // TODO: this is not implemented or verified yet for woop. func (wiki *Woop) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription { return wiki.BlockChain.SubscribeChainSideEvent(ch) } // SubscribeRemovedLogsEvent subcribes removed logs event. // TODO: this is not implemented or verified yet for woop. func (wiki *Woop) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { return wiki.BlockChain.SubscribeRemovedLogsEvent(ch) } // SubscribeLogsEvent subcribes log event. // TODO: this is not implemented or verified yet for woop. func (wiki *Woop) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { return wiki.BlockChain.SubscribeLogsEvent(ch) }