1

I've read some docs online that to get the miner/sealer/signer address of a particular block that runs with the Ethereum PoA/Clique consensus (the Rinkeby testnet for example), I need to call the clique_getSnapshot RPC interface. However when I tried it with a infura Rinkeby endpoint, it returns "The method clique_getSnapshot does not exist/is not available" error.

So is there any way where I can get the miner/sealer/signer address of a particular block on the Rinkeby testnet with an infura Rinkeby endpoint? For example on etherscan it shows that block 4753195 is mined by 0x7ffC57839B00206D1ad20c69A1981b489f772031

https://rinkeby.etherscan.io/block/4753195

How may I get that info?

======

Thanks to @Ha ĐANG , I've now come to this stage:

Web3 = require("web3");
web3 = new Web3("https://rinkeby.infura.io/v3/xxxxxxxxx"); // my infura Rinkeby endpoint
ejutils = require("ethereumjs-utils");

(async () => {
  blk = await web3.eth.getBlock(4753195);  

  blkhash = blk.hash;
  console.log(blkhash);  
  // 0xdfd0a441a76d0f54bea2b61963871a61bca5eb4adf76f15d60e2d3c7b19cf191 Correct!!

  extdat = blk.extraData;
  console.log(extdat); 
  // 0xd883010900846765746888676f312e31322e34856c696e7578000000000000009e4d991ca30e6ff6782058da4b958eb180e54f3af99f00afa9d42ea01c08b1134664c21f408d79c87d93a6ce83354a68d8f0d3fea99bc796029875563acceef801 Correct!!

  msg = ejutils.toBuffer(blkhash);
  sig = extdat.slice(-130);
  r = "0x" + sig.slice(0, 64);
  s = '0x' + sig.slice(64, 128);
  v = '0x' + sig.slice(128, 130);
  v = parseInt(v) + 27;
  pub = ejutils.ecrecover(msg, v, r, s);
  addr = ejutils.pubToAddress(pub);
  addr = ejutils.bufferToHex(addr);
  console.log(addr);
  // 0xe2877298e8cf1046ae9f3231e17f73fe85282590 Wrong :(

})();

So any idea what went wrong in the above code? Thanks.

2

Thanks a lot for helps from @Ha ĐANG I finally reached this solution

    const Web3 = require("web3");
    const web3 = new Web3("https://rinkeby.infura.io/v3/xxxxxx"); // infura Rinkeby endpoint
    const eju = require("ethereumjs-utils");

    (async () => {
      let blk = await web3.eth.getBlock(4753195);  

      let header = [
        eju.toBuffer(blk.parentHash),
        eju.toBuffer(blk.sha3Uncles),
        eju.toBuffer(blk.miner),
        eju.toBuffer(blk.stateRoot),
        eju.toBuffer(blk.transactionsRoot),
        eju.toBuffer(blk.receiptsRoot),
        eju.toBuffer(blk.logsBloom),
        parseInt(blk.difficulty),
        parseInt(blk.number),
        parseInt(blk.gasLimit),
        parseInt(blk.gasUsed),
        parseInt(blk.timestamp),
        eju.toBuffer(blk.extraData.slice(0, blk.extraData.length - 130)),
        eju.toBuffer(blk.mixHash),
        eju.toBuffer(blk.nonce)
      ];

      let msg = eju.rlp.encode(header);
      let msghash = eju.keccak256(msg)
      let sig = blk.extraData.slice(-130);
      let r = "0x" + sig.slice(0, 64);
      let s = '0x' + sig.slice(64, 128);
      let v = '0x' + sig.slice(128, 130);
      v = parseInt(v) + 27;
      let pub = eju.ecrecover(msghash, v, r, s);
      let addr = eju.pubToAddress(pub);
      addr = eju.bufferToHex(addr);
      console.log(addr);
      // 0x7ffc57839b00206d1ad20c69a1981b489f772031 Correct!!

    })();

Took a full hour of trial and error to finally get the correct result. Can't believe it's actually this hard to accomplish such a seemingly easy task. Anyone seeing this please upvote @Ha ĐANG's answer, thanks.

  • Magnificient! ^^ – Ha ĐANG Jul 18 at 12:56
2

I've read some docs online that to get the miner/sealer/signer address of a particular block that runs with the Ethereum PoA/Clique consensus (the Rinkeby testnet for example), I need to call the clique_getSnapshot RPC interface. However when I tried it with a infura Rinkeby endpoint, it returns "The method clique_getSnapshot does not exist/is not available" error.

→ We can only call clique_getSnapshot on node's console, we cannot call it via JSON RPC API.

So is there any way where I can get the miner/sealer/signer address of a particular block on the Rinkeby testnet with an infura Rinkeby endpoint? For example on etherscan it shows that block 4753195 is mined by 0x7ffC57839B00206D1ad20c69A1981b489f772031

→ You can call eth_getBlockByNumber JSON RPC API on infura endpoint to get the block info then figure out the miner in the block data by using ecrecover function to recover the miner's address from their digital signature storing in the last 130 hex charaters in the block.extraData.

Below are snippet code to use ecrecover in golang code

package main

import (
    "context"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/consensus/clique"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://rinkeby.infura.io/v3/xxxxxx")
    if err != nil {
        log.Fatal(err)
    }

    block, err := client.BlockByNumber(context.Background(), big.NewInt(4753195))
    if err != nil {
        log.Fatal(err)
    }
    pubkey, err := crypto.Ecrecover(clique.SealHash(block.Header()).Bytes(), block.Extra()[len(block.Extra())-65:])
    log.Println(pubkey)
    var signer common.Address
    copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
    log.Println(err)
    log.Println(signer.Hex())
}

and output the same miner address 0x7ffC57839B00206D1ad20c69A1981b489f772031 with mined by on etherscan.io

  • Could you elaborate on HOW may I figure out the miner in the block data? With Rinkeby, the "miner" field in the result is always "0x0000000000000000000000000000000000000000", and I don't see any field remotely resembling "recents" or "snapshot" or anything looking like the miner address shown on the etherscan page. – hellopeach Jul 18 at 6:17
  • OK, I update the answer. PTAL – Ha ĐANG Jul 18 at 6:22
  • could you be more specific? I know there's a web3.eth.personal.ecRecover method, but it needs to pass two parameters which are "dataThatWasSigned" and "signature", so what should I pass as those two parameters? thanks. – hellopeach Jul 18 at 6:33
  • miner sign for the block then data should be the hash block's header itself but honestly I dont know exactly how to make it to pass to ecRecover() function :) – Ha ĐANG Jul 18 at 6:38
  • try to use block.hash as input for dataThatWasSigned and block.extraData as input for signature – Ha ĐANG Jul 18 at 6:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.