github.com/klaytn/klaytn@v1.12.1/consensus/istanbul/backend/randao.go (about)

     1  package backend
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"math/big"
     7  
     8  	lru "github.com/hashicorp/golang-lru"
     9  	"github.com/klaytn/klaytn/accounts/abi/bind/backends"
    10  	"github.com/klaytn/klaytn/blockchain/system"
    11  	"github.com/klaytn/klaytn/blockchain/types"
    12  	"github.com/klaytn/klaytn/common"
    13  	"github.com/klaytn/klaytn/common/hexutil"
    14  	"github.com/klaytn/klaytn/consensus"
    15  	"github.com/klaytn/klaytn/crypto"
    16  	"github.com/klaytn/klaytn/crypto/bls"
    17  	"github.com/klaytn/klaytn/params"
    18  )
    19  
    20  // For testing without KIP-113 contract setup
    21  type BlsPubkeyProvider interface {
    22  	// num should be the header number of the block to be verified.
    23  	// Thus, since the state of num does not exist, the state of num-1 must be used.
    24  	GetBlsPubkey(chain consensus.ChainReader, proposer common.Address, num *big.Int) (bls.PublicKey, error)
    25  	ResetBlsCache()
    26  }
    27  
    28  type ChainBlsPubkeyProvider struct {
    29  	cache *lru.ARCCache // Cached BlsPublicKeyInfos
    30  }
    31  
    32  func newChainBlsPubkeyProvider() *ChainBlsPubkeyProvider {
    33  	cache, _ := lru.NewARC(128)
    34  	return &ChainBlsPubkeyProvider{
    35  		cache: cache,
    36  	}
    37  }
    38  
    39  // The default implementation for BlsPubkeyFunc.
    40  // Queries KIP-113 contract and verifies the PoP.
    41  func (p *ChainBlsPubkeyProvider) GetBlsPubkey(chain consensus.ChainReader, proposer common.Address, num *big.Int) (bls.PublicKey, error) {
    42  	infos, err := p.getAllCached(chain, num)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	info, ok := infos[proposer]
    48  	if !ok {
    49  		return nil, errNoBlsPub
    50  	}
    51  	if info.VerifyErr != nil {
    52  		return nil, info.VerifyErr
    53  	}
    54  	return bls.PublicKeyFromBytes(info.PublicKey)
    55  }
    56  
    57  func (p *ChainBlsPubkeyProvider) getAllCached(chain consensus.ChainReader, num *big.Int) (system.BlsPublicKeyInfos, error) {
    58  	if item, ok := p.cache.Get(num.Uint64()); ok {
    59  		logger.Trace("BlsPublicKeyInfos cache hit", "number", num.Uint64())
    60  		return item.(system.BlsPublicKeyInfos), nil
    61  	}
    62  
    63  	backend := backends.NewBlockchainContractBackend(chain, nil, nil)
    64  	if common.Big0.Cmp(num) == 0 {
    65  		return nil, errors.New("num cannot be zero")
    66  	}
    67  	parentNum := new(big.Int).Sub(num, common.Big1)
    68  
    69  	var kip113Addr common.Address
    70  	// Because the system contract Registry is installed at Finalize() of RandaoForkBlock,
    71  	// it is not possible to read KIP113 address from the Registry at RandaoForkBlock.
    72  	// Hence the ChainConfig fallback.
    73  	if chain.Config().IsRandaoForkBlock(num) {
    74  		var ok bool
    75  		kip113Addr, ok = chain.Config().RandaoRegistry.Records[system.Kip113Name]
    76  		if !ok {
    77  			return nil, errors.New("KIP113 address not set in ChainConfig")
    78  		}
    79  	} else if chain.Config().IsRandaoForkEnabled(num) {
    80  		// If no state exist at block number `parentNum`,
    81  		// return the error `consensus.ErrPrunedAncestor`
    82  		pHeader := chain.GetHeaderByNumber(parentNum.Uint64())
    83  		if pHeader == nil {
    84  			return nil, consensus.ErrUnknownAncestor
    85  		}
    86  		_, err := chain.StateAt(pHeader.Root)
    87  		if err != nil {
    88  			return nil, consensus.ErrPrunedAncestor
    89  		}
    90  		kip113Addr, err = system.ReadActiveAddressFromRegistry(backend, system.Kip113Name, parentNum)
    91  		if err != nil {
    92  			return nil, err
    93  		}
    94  	} else {
    95  		return nil, errors.New("Cannot read KIP113 address from registry before Randao fork")
    96  	}
    97  
    98  	infos, err := system.ReadKip113All(backend, kip113Addr, parentNum)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	logger.Trace("BlsPublicKeyInfos cache miss", "number", num.Uint64())
   103  	p.cache.Add(num.Uint64(), infos)
   104  
   105  	return infos, nil
   106  }
   107  
   108  func (p *ChainBlsPubkeyProvider) ResetBlsCache() {
   109  	p.cache.Purge()
   110  }
   111  
   112  // Calculate KIP-114 Randao header fields
   113  // https://github.com/klaytn/kips/blob/kip114/KIPs/kip-114.md
   114  func (sb *backend) CalcRandao(number *big.Int, prevMixHash []byte) ([]byte, []byte, error) {
   115  	if sb.blsSecretKey == nil {
   116  		return nil, nil, errNoBlsKey
   117  	}
   118  	if len(prevMixHash) != 32 {
   119  		logger.Error("invalid prevMixHash", "number", number.Uint64(), "prevMixHash", hexutil.Encode(prevMixHash))
   120  		return nil, nil, errInvalidRandaoFields
   121  	}
   122  
   123  	// block_num_to_bytes() = num.to_bytes(32, byteorder="big")
   124  	msg := calcRandaoMsg(number)
   125  
   126  	// calc_random_reveal() = sign(privateKey, headerNumber)
   127  	randomReveal := bls.Sign(sb.blsSecretKey, msg[:]).Marshal()
   128  
   129  	// calc_mix_hash() = xor(prevMixHash, keccak256(randomReveal))
   130  	mixHash := calcMixHash(randomReveal, prevMixHash)
   131  
   132  	return randomReveal, mixHash, nil
   133  }
   134  
   135  func (sb *backend) VerifyRandao(chain consensus.ChainReader, header *types.Header, prevMixHash []byte) error {
   136  	if header.Number.Sign() == 0 {
   137  		return nil // Do not verify genesis block
   138  	}
   139  
   140  	proposer, err := sb.Author(header)
   141  	if err != nil {
   142  		return err
   143  	}
   144  
   145  	// [proposerPubkey, proposerPop] = get_proposer_pubkey_pop()
   146  	// if not pop_verify(proposerPubkey, proposerPop): return False
   147  	proposerPub, err := sb.blsPubkeyProvider.GetBlsPubkey(chain, proposer, header.Number)
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	// if not verify(proposerPubkey, newHeader.number, newHeader.randomReveal): return False
   153  	sig := header.RandomReveal
   154  	msg := calcRandaoMsg(header.Number)
   155  	ok, err := bls.VerifySignature(sig, msg, proposerPub)
   156  	if err != nil {
   157  		return err
   158  	} else if !ok {
   159  		return errInvalidRandaoFields
   160  	}
   161  
   162  	// if not newHeader.mixHash == calc_mix_hash(prevMixHash, newHeader.randomReveal): return False
   163  	mixHash := calcMixHash(header.RandomReveal, prevMixHash)
   164  	if !bytes.Equal(header.MixHash, mixHash) {
   165  		return errInvalidRandaoFields
   166  	}
   167  
   168  	return nil
   169  }
   170  
   171  // block_num_to_bytes() = num.to_bytes(32, byteorder="big")
   172  func calcRandaoMsg(number *big.Int) common.Hash {
   173  	return common.BytesToHash(number.Bytes())
   174  }
   175  
   176  // calc_mix_hash() = xor(prevMixHash, keccak256(randomReveal))
   177  func calcMixHash(randomReveal, prevMixHash []byte) []byte {
   178  	mixHash := make([]byte, 32)
   179  	revealHash := crypto.Keccak256(randomReveal)
   180  	for i := 0; i < 32; i++ {
   181  		mixHash[i] = prevMixHash[i] ^ revealHash[i]
   182  	}
   183  	return mixHash
   184  }
   185  
   186  // At the fork block's parent, pretend that prevMixHash is ZeroMixHash.
   187  func headerMixHash(chain consensus.ChainReader, header *types.Header) []byte {
   188  	if chain.Config().IsRandaoForkBlockParent(header.Number) {
   189  		return params.ZeroMixHash
   190  	} else {
   191  		return header.MixHash
   192  	}
   193  }