The core protocol of WoopChain
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
woop/wiki/blockchain.go

430 lines
14 KiB

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