The signer can be recovered by taking a block, constructoring the
headers and then using the Ecrecover
of the go-ethereum crypto
library.
First, we’ll download a block, extract the result using jq
and then
save the response in block.json
.
curl -s -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0", "id": 1, "method": "eth_getBlockByNumber", "params": ["0x31b7feb", true]}' https://polygon-rpc.com | jq '.result' | tee block.json
Now that we have the block saved locally in json format, we can parse it and output the signer with go
package main
import (
"encoding/hex"
"fmt"
"log/slog"
"os"
"github.com/ethereum/go-ethereum/consensus/clique"
ethtypes "github.com/ethereum/go-ethereum/core/types"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
)
func main() {
blockData, err := os.ReadFile("block.json")
if err != nil {
slog.Error("Could not open file", "error", err.Error())
os.Exit(1)
}
_, signer, err := ecrecover(blockData)
if err != nil {
slog.Error("Unable to recover signer", "error", err.Error())
os.Exit(1)
}
fmt.Println(hex.EncodeToString(signer))
}
func ecrecover(data []byte) (*ethtypes.Header, []byte, error) {
header := new(ethtypes.Header)
err := header.UnmarshalJSON(data)
if err != nil {
return header, nil, err
}
sigStart := len(header.Extra) - ethcrypto.SignatureLength
if sigStart < 0 || sigStart > len(header.Extra) {
return header, nil, fmt.Errorf("Unable to recover signature")
}
signature := header.Extra[sigStart:]
pubkey, err := ethcrypto.Ecrecover(clique.SealHash(header).Bytes(), signature)
if err != nil {
return header, nil, err
}
signer := ethcrypto.Keccak256(pubkey[1:])[12:]
return header, signer, nil
}