Skip to content

Commit

Permalink
feat: implement ecrecover command to fetch public key
Browse files Browse the repository at this point in the history
  • Loading branch information
gatsbyz committed Jan 26, 2024
1 parent 5e20a71 commit d5fbde6
Show file tree
Hide file tree
Showing 9 changed files with 609 additions and 27 deletions.
53 changes: 53 additions & 0 deletions cmd/ecrecover/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package ecrecover

import (
_ "embed"
"fmt"

"github.com/maticnetwork/polygon-cli/util"
"github.com/spf13/cobra"
)

var (
//go:embed usage.md
usage string

rpcUrl string
blockNumber int
extraData string
)

var EcRecoverCmd = &cobra.Command{
Use: "ecrecover",
Short: "Recovers and returns the public key of the signature",
Long: usage,
Args: cobra.NoArgs,
PreRunE: func(cmd *cobra.Command, args []string) error {
return checkFlags()
},
RunE: func(cmd *cobra.Command, args []string) error {
return ecrecover(cmd.Context())
},
}

func init() {
EcRecoverCmd.PersistentFlags().StringVarP(&rpcUrl, "rpc-url", "r", "http://localhost:8545", "The RPC endpoint url")
EcRecoverCmd.PersistentFlags().IntVarP(&blockNumber, "block-number", "b", 0, "Block number to check the extra data for")
EcRecoverCmd.PersistentFlags().StringVarP(&extraData, "extra-data", "e", "", "Raw extra data")
}

func checkFlags() (err error) {
if err = util.ValidateUrl(rpcUrl); err != nil {
return
}

if blockNumber <= 0 {
return fmt.Errorf("block-number should be greater than 0")
}

// if extraData == "" {
// return fmt.Errorf("block-number should be greater than 0")
// }

return nil
}
71 changes: 71 additions & 0 deletions cmd/ecrecover/ecrecover.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package ecrecover

import (
"context"
"crypto/ecdsa"
"fmt"
"math/big"

_ "embed"

"github.com/ethereum/go-ethereum/common"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
ethrpc "github.com/ethereum/go-ethereum/rpc"
"github.com/maticnetwork/polygon-cli/util"

"github.com/rs/zerolog/log"
)

func ecrecover(ctx context.Context) (common.Address, error) {
rpc, err := ethrpc.DialContext(ctx, rpcUrl)
if err != nil {
log.Error().Err(err).Msg("Unable to dial rpc")
return common.Address{}, err
}

ec := ethclient.NewClient(rpc)
if _, err = ec.BlockNumber(ctx); err != nil {
return common.Address{}, err
}

block, err := ec.BlockByNumber(ctx, big.NewInt(int64(blockNumber)))
if err != nil {
log.Error().Err(err).Msg("Unable to retrieve block")
return common.Address{}, err
}

if len(block.Transactions()) == 0 {
return common.Address{}, fmt.Errorf("no transaction to derive public key from")
}

signerBytes, err := util.Ecrecover(block)
if err != nil {
log.Error().Err(err).Msg("Unable to recover signature")
return common.Address{}, err
}
signerAddress := ethcommon.BytesToAddress(signerBytes)

chainID, err := ec.NetworkID(ctx)
if err != nil {
log.Fatal().Err(err).Msg("Failed to get network ID")
}

var publicKey *ecdsa.PublicKey
for _, tx := range block.Transactions() {
tx.Value()
if msg, err := tx.AsMessage(types.LatestSignerForChainID(chainID), nil); err == nil {
if msg.From().Hex() == signerAddress {
publicKeyECDSA, err := crypto.SigToPub(signHash(msg), msg.Signature())
if err != nil {
log.Fatal().Err(err).Msg("Failed to recover public key")
}
publicKey = publicKeyECDSA
break
}
}
}

}
5 changes: 5 additions & 0 deletions cmd/ecrecover/usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
![GIF of `polycli monitor`](assets/monitor.gif)

If you're using the terminal UI and you'd like to be able to select text for copying, you might need to use a modifier key.

If you're experiencing missing blocks, try adjusting the `--batch-size` and `--interval` flags so that you poll for more blocks or more frequently.
21 changes: 2 additions & 19 deletions cmd/fork/fork.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import (
"time"

ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/core/types"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/maticnetwork/polygon-cli/util"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"

Expand Down Expand Up @@ -121,7 +120,7 @@ func writeBlock(folderName string, block *types.Block, isCanonical bool) error {
}
fields["transactions"] = block.Transactions()
// TODO in the future if this is used in other chains or with different types of consensus this would need to be revised
signer, err := ecrecover(block)
signer, err := util.Ecrecover(block)
if err != nil {
log.Error().Err(err).Msg("Unable to recover signature")
return err
Expand Down Expand Up @@ -156,19 +155,3 @@ func getBlockByHash(ctx context.Context, bh ethcommon.Hash, client *ethclient.Cl
log.Error().Err(errRetryLimitExceeded).Str("blockhash", bh.String()).Int("retryLimit", retryLimit).Msg("Unable to fetch block after retrying")
return nil, errRetryLimitExceeded
}

func ecrecover(block *types.Block) ([]byte, error) {
header := block.Header()
sigStart := len(header.Extra) - ethcrypto.SignatureLength
if sigStart < 0 || sigStart > len(header.Extra) {
return nil, fmt.Errorf("unable to recover signature")
}
signature := header.Extra[sigStart:]
pubkey, err := ethcrypto.Ecrecover(clique.SealHash(header).Bytes(), signature)
if err != nil {
return nil, err
}
signer := ethcrypto.Keccak256(pubkey[1:])[12:]

return signer, nil
}
2 changes: 2 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/maticnetwork/polygon-cli/cmd/abi"
"github.com/maticnetwork/polygon-cli/cmd/dbbench"
"github.com/maticnetwork/polygon-cli/cmd/dumpblocks"
"github.com/maticnetwork/polygon-cli/cmd/ecrecover"
"github.com/maticnetwork/polygon-cli/cmd/enr"
"github.com/maticnetwork/polygon-cli/cmd/fund"
"github.com/maticnetwork/polygon-cli/cmd/hash"
Expand Down Expand Up @@ -107,6 +108,7 @@ func NewPolycliCommand() *cobra.Command {
cmd.AddCommand(
abi.ABICmd,
dumpblocks.DumpblocksCmd,
ecrecover.EcRecoverCmd,
fork.ForkCmd,
fund.FundCmd,
hash.HashCmd,
Expand Down
23 changes: 15 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.21

require (
cloud.google.com/go/datastore v1.15.0
github.com/btcsuite/btcutil v1.0.2
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce
github.com/cenkalti/backoff/v4 v4.2.1
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593
Expand All @@ -24,7 +24,7 @@ require (
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.17.0
github.com/stretchr/testify v1.8.4
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
github.com/tyler-smith/go-bip32 v1.0.0
github.com/tyler-smith/go-bip39 v1.1.0
github.com/xeipuuv/gojsonschema v1.2.0
Expand All @@ -38,7 +38,13 @@ require (

require github.com/alecthomas/participle/v2 v2.1.1

require github.com/go-errors/errors v1.5.1 // indirect
require (
github.com/go-errors/errors v1.5.1 // indirect
github.com/sethvargo/go-retry v0.2.4 // indirect
github.com/umbracle/ethgo v0.1.4-0.20231006072852-6b068360fc97 // indirect
github.com/umbracle/fastrlp v0.1.1-0.20230504065717-58a1b8a9929d // indirect
github.com/valyala/fastjson v1.6.3 // indirect
)

require (
cloud.google.com/go v0.110.8 // indirect
Expand Down Expand Up @@ -78,21 +84,21 @@ require (
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/hcl v1.0.1-vault // indirect
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
github.com/holiman/uint256 v1.2.4 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/ipfs/go-cid v0.4.1 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/klauspost/compress v1.17.0 // indirect
github.com/klauspost/compress v1.17.1 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
Expand All @@ -113,8 +119,8 @@ require (
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.15.1 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/prometheus/client_golang v1.17.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/sagikazarmark/locafero v0.3.0 // indirect
Expand Down Expand Up @@ -158,6 +164,7 @@ require (

require (
cloud.google.com/go/iam v1.1.3 // indirect
github.com/0xPolygon/polygon-edge v1.3.2
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect
Expand Down
Loading

0 comments on commit d5fbde6

Please sign in to comment.