Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

Commit

Permalink
more efficient ParseChainID implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
yihuang committed Oct 28, 2022
1 parent 9b71c37 commit 784e5d4
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 36 deletions.
60 changes: 55 additions & 5 deletions types/benchmark_test.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,66 @@
package types

import (
"fmt"
fmt "fmt"
"math/big"
"regexp"
"strings"
"testing"

sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

var (
regexChainID = `[a-z]{1,}`
regexEIP155Separator = `_{1}`
regexEIP155 = `[1-9][0-9]*`
regexEpochSeparator = `-{1}`
regexEpoch = `[1-9][0-9]*`
ethermintChainID = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)%s(%s)$`,
regexChainID,
regexEIP155Separator,
regexEIP155,
regexEpochSeparator,
regexEpoch))
)

// ParseChainID parses a string chain identifier's epoch to an Ethereum-compatible
// chain-id in *big.Int format. The function returns an error if the chain-id has an invalid format
func ParseChainIDLegacy(chainID string) (*big.Int, error) {
chainID = strings.TrimSpace(chainID)
if len(chainID) > 48 {
return nil, sdkerrors.Wrapf(ErrInvalidChainID, "chain-id '%s' cannot exceed 48 chars", chainID)
}

matches := ethermintChainID.FindStringSubmatch(chainID)
if matches == nil || len(matches) != 4 || matches[1] == "" {
return nil, sdkerrors.Wrapf(ErrInvalidChainID, "%s: %v", chainID, matches)
}

// verify that the chain-id entered is a base 10 integer
chainIDInt, ok := new(big.Int).SetString(matches[2], 10)
if !ok {
return nil, sdkerrors.Wrapf(ErrInvalidChainID, "epoch %s must be base-10 integer format", matches[2])
}

return chainIDInt, nil
}

func BenchmarkParseChainID(b *testing.B) {
b.ReportAllocs()
// Start at 1, for valid EIP155, see regexEIP155 variable.
for i := 1; i < b.N; i++ {
chainID := fmt.Sprintf("ethermint_1-%d", i)
if _, err := ParseChainID(chainID); err != nil {
// Parsing a typical ethermint chain id
for i := 0; i < b.N; i++ {
if _, err := ParseChainID("ethermint_9000-1"); err != nil {
b.Fatal(err)
}
}
}

func BenchmarkParseChainIDLegacy(b *testing.B) {
b.ReportAllocs()
// Parsing a typical ethermint chain id
for i := 0; i < b.N; i++ {
if _, err := ParseChainIDLegacy("ethermint_9000-1"); err != nil {
b.Fatal(err)
}
}
Expand Down
47 changes: 16 additions & 31 deletions types/chain_id.go
Original file line number Diff line number Diff line change
@@ -1,55 +1,40 @@
package types

import (
"fmt"
"math/big"
"regexp"
"strconv"
"strings"

sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

var (
regexChainID = `[a-z]{1,}`
regexEIP155Separator = `_{1}`
regexEIP155 = `[1-9][0-9]*`
regexEpochSeparator = `-{1}`
regexEpoch = `[1-9][0-9]*`
ethermintChainID = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)%s(%s)$`,
regexChainID,
regexEIP155Separator,
regexEIP155,
regexEpochSeparator,
regexEpoch))
)

// IsValidChainID returns false if the given chain identifier is incorrectly formatted.
func IsValidChainID(chainID string) bool {
if len(chainID) > 48 {
return false
}

return ethermintChainID.MatchString(chainID)
_, err := ParseChainID(chainID)
return err != nil
}

// ParseChainID parses a string chain identifier's epoch to an Ethereum-compatible
// chain-id in *big.Int format. The function returns an error if the chain-id has an invalid format
func ParseChainID(chainID string) (*big.Int, error) {
chainID = strings.TrimSpace(chainID)
if len(chainID) > 48 {
return nil, sdkerrors.Wrapf(ErrInvalidChainID, "chain-id '%s' cannot exceed 48 chars", chainID)
if len(chainID) == 0 {
return nil, sdkerrors.Wrapf(ErrInvalidChainID, "empty chain id")
}

matches := ethermintChainID.FindStringSubmatch(chainID)
if matches == nil || len(matches) != 4 || matches[1] == "" {
return nil, sdkerrors.Wrapf(ErrInvalidChainID, "%s: %v", chainID, matches)
idx := strings.LastIndexByte(chainID, '-')
if idx < 0 {
return nil, sdkerrors.Wrapf(ErrInvalidChainID, "expect at least 1 epoch seperator, got 0")
}
idx2 := strings.LastIndexByte(chainID[:idx], '_')
if idx2 < 0 {
return nil, sdkerrors.Wrapf(ErrInvalidChainID, "expect at least 1 eip155 chain-id seperator, got 0")
}

// verify that the chain-id entered is a base 10 integer
chainIDInt, ok := new(big.Int).SetString(matches[2], 10)
if !ok {
return nil, sdkerrors.Wrapf(ErrInvalidChainID, "epoch %s must be base-10 integer format", matches[2])
n, err := strconv.ParseInt(chainID[idx2+1:idx], 10, 64)
if err != nil {
return nil, sdkerrors.Wrapf(ErrInvalidChainID, "eip155 chain-id %s must be base-10 64bit integer format", chainID[idx2+1:idx])
}

return chainIDInt, nil
return big.NewInt(n), nil
}

0 comments on commit 784e5d4

Please sign in to comment.