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 }