Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clean up ethtypes: rationalize ethtypes.EthAddressFromFilecoinAddress and conversion methods #9992

Merged
merged 5 commits into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified build/openrpc/full.json.gz
Binary file not shown.
123 changes: 41 additions & 82 deletions chain/types/ethtypes/eth_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/binary"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
mathbig "math/big"
"strconv"
Expand All @@ -30,6 +31,8 @@ var (
EthTopic4 = "topic4"
)

var ErrInvalidAddress = errors.New("invalid Filecoin Eth address")

type EthUint64 uint64

func (e EthUint64) MarshalJSON() ([]byte, error) {
Expand Down Expand Up @@ -249,21 +252,32 @@ func EthAddressFromPubKey(pubk []byte) ([]byte, error) {
}

func EthAddressFromFilecoinAddress(addr address.Address) (EthAddress, error) {
ethAddr, ok, err := TryEthAddressFromFilecoinAddress(addr, true)
if err != nil {
return EthAddress{}, xerrors.Errorf("failed to try converting filecoin to eth addr: %w", err)
}

if !ok {
return EthAddress{}, xerrors.Errorf("failed to convert filecoin address %s to an equivalent eth address", addr)
switch addr.Protocol() {
case address.ID:
id, err := address.IDFromAddress(addr)
if err != nil {
return EthAddress{}, err
}
var ethaddr EthAddress
ethaddr[0] = 0xff
binary.BigEndian.PutUint64(ethaddr[12:], id)
return ethaddr, nil
case address.Delegated:
payload := addr.Payload()
namespace, n, err := varint.FromUvarint(payload)
if err != nil {
return EthAddress{}, xerrors.Errorf("invalid delegated address namespace in: %s", addr)
}
payload = payload[n:]
if namespace == builtintypes.EthereumAddressManagerActorID {
return CastEthAddress(payload)
}
}

return ethAddr, nil
return EthAddress{}, ErrInvalidAddress
}

// ParseEthAddress parses an Ethereum address from a hex string.
func ParseEthAddress(s string) (EthAddress, error) {
handlePrefix(&s)
b, err := decodeHexString(s, EthAddressLength)
if err != nil {
return EthAddress{}, err
Expand Down Expand Up @@ -304,9 +318,13 @@ func (ea *EthAddress) UnmarshalJSON(b []byte) error {
return nil
}

func (ea EthAddress) ToFilecoinAddress() (address.Address, error) {
func (ea EthAddress) IsMaskedID() bool {
idmask := [12]byte{0xff}
if bytes.Equal(ea[:12], idmask[:]) {
return bytes.Equal(ea[:12], idmask[:])
}

func (ea EthAddress) ToFilecoinAddress() (address.Address, error) {
if ea.IsMaskedID() {
// This is a masked ID address.
id := binary.BigEndian.Uint64(ea[12:])
return address.NewIDAddress(id)
Expand All @@ -322,37 +340,6 @@ func (ea EthAddress) ToFilecoinAddress() (address.Address, error) {
return addr, nil
}

// This API assumes that if an ID address is passed in, it doesn't have an equivalent
// delegated address
func TryEthAddressFromFilecoinAddress(addr address.Address, allowId bool) (EthAddress, bool, error) {
switch addr.Protocol() {
case address.ID:
if !allowId {
return EthAddress{}, false, nil
}
id, err := address.IDFromAddress(addr)
if err != nil {
return EthAddress{}, false, err
}
var ethaddr EthAddress
ethaddr[0] = 0xff
binary.BigEndian.PutUint64(ethaddr[12:], id)
return ethaddr, true, nil
case address.Delegated:
payload := addr.Payload()
namespace, n, err := varint.FromUvarint(payload)
if err != nil {
return EthAddress{}, false, xerrors.Errorf("invalid delegated address namespace in: %s", addr)
}
payload = payload[n:]
if namespace == builtintypes.EthereumAddressManagerActorID {
addr, err := CastEthAddress(payload)
return addr, err == nil, err
}
}
return EthAddress{}, false, nil
}

type EthHash [EthHashLength]byte

func (h EthHash) MarshalJSON() ([]byte, error) {
Expand All @@ -372,25 +359,22 @@ func (h *EthHash) UnmarshalJSON(b []byte) error {
return nil
}

func handlePrefix(s *string) {
if strings.HasPrefix(*s, "0x") || strings.HasPrefix(*s, "0X") {
*s = (*s)[2:]
func decodeHexString(s string, expectedLen int) ([]byte, error) {
// Strip the leading 0x or 0X prefix since hex.DecodeString does not support it.
if strings.HasPrefix(s, "0x") || strings.HasPrefix(s, "0X") {
s = s[2:]
}
if len(*s)%2 == 1 {
*s = "0" + *s
// Sometimes clients will omit a leading zero in a byte; pad so we can decode correctly.
if len(s)%2 == 1 {
s = "0" + s
}
if len(s) != expectedLen*2 {
return []byte{}, xerrors.Errorf("expected length %d, got %d", expectedLen, len(s))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is returning nil not more canonical here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, can do. I'm working on another fix that affects this code, so will do in the next PR.

}
}

func decodeHexString(s string, length int) ([]byte, error) {
b, err := hex.DecodeString(s)
if err != nil {
return []byte{}, xerrors.Errorf("cannot parse hash: %w", err)
}

if len(b) > length {
return []byte{}, xerrors.Errorf("length of decoded bytes is longer than %d", length)
return []byte{}, xerrors.Errorf("cannot parse hex value: %w", err)
}

return b, nil
}

Expand All @@ -399,7 +383,6 @@ func EthHashFromCid(c cid.Cid) (EthHash, error) {
}

func ParseEthHash(s string) (EthHash, error) {
handlePrefix(&s)
b, err := decodeHexString(s, EthHashLength)
if err != nil {
return EthHash{}, err
Expand Down Expand Up @@ -427,30 +410,6 @@ type EthFeeHistory struct {
Reward *[][]EthBigInt `json:"reward,omitempty"`
}

type BlkNumType int64

const (
BlkNumLatest BlkNumType = iota
BlkNumPending
BlkNumVal
)

func ParseBlkNumOption(str string) (typ BlkNumType, blkNum EthUint64, err error) {
switch str {
case "pending":
return BlkNumPending, 0, nil
case "latest":
return BlkNumLatest, 0, nil
default:
var num EthUint64
err := num.UnmarshalJSON([]byte(`"` + str + `"`))
if err != nil {
return BlkNumVal, 0, err
}
return BlkNumVal, num, nil
}
}

type EthFilterID EthHash

// An opaque identifier generated by the Lotus node to refer to an active subscription.
Expand Down
Loading