diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 39c6fb51f27..dc027c9b728 100644 Binary files a/build/openrpc/full.json.gz and b/build/openrpc/full.json.gz differ diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index e33db917ec2..a4f8fb94dd3 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "encoding/hex" "encoding/json" + "errors" "fmt" mathbig "math/big" "strconv" @@ -30,6 +31,8 @@ var ( EthTopic4 = "topic4" ) +var ErrInvalidAddress = errors.New("invalid Filecoin Eth address") + type EthUint64 uint64 func (e EthUint64) MarshalJSON() ([]byte, error) { @@ -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 @@ -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) @@ -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) { @@ -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)) } -} - -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 } @@ -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 @@ -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. diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 8d74e6e5e59..991d1b69263 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -2529,26 +2529,26 @@ Inputs: 0, 0, 0, - 92, - 190, - 236, - 1, - 35, - 69, - 103, - 63, - 37, - 227, - 9, - 204, - 38, - 79, - 36, - 11, - 176, - 102, - 64, - 49 + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 ] ] ``` @@ -2583,26 +2583,26 @@ Inputs: 0, 0, 0, - 92, - 190, - 236, - 1, - 35, - 69, - 103, - 63, - 37, - 227, - 9, - 204, - 38, - 79, - 36, - 11, - 176, - 102, - 64, - 49 + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 ] ] ``` @@ -2857,26 +2857,26 @@ Response: 0, 0, 0, - 92, - 190, - 236, - 1, - 35, - 69, - 103, - 63, - 37, - 227, - 9, - 204, - 38, - 79, - 36, - 11, - 176, - 102, - 64, - 49 + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 ] ``` @@ -2914,26 +2914,26 @@ Response: 0, 0, 0, - 92, - 190, - 236, - 1, - 35, - 69, - 103, - 63, - 37, - 227, - 9, - 204, - 38, - 79, - 36, - 11, - 176, - 102, - 64, - 49 + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 ] ``` @@ -2960,26 +2960,26 @@ Response: 0, 0, 0, - 92, - 190, - 236, - 1, - 35, - 69, - 103, - 63, - 37, - 227, - 9, - 204, - 38, - 79, - 36, - 11, - 176, - 102, - 64, - 49 + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 ] ``` @@ -3048,26 +3048,26 @@ Response: 0, 0, 0, - 92, - 190, - 236, - 249, - 157, - 63, - 219, - 48, - 18, - 52, - 86, - 124, - 38, - 79, - 36, - 11, - 176, - 102, - 64, - 49 + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 ], "result": {} } @@ -3095,26 +3095,26 @@ Inputs: 0, 0, 0, - 92, - 190, - 236, - 1, - 35, - 69, - 103, - 63, - 37, - 227, - 9, - 204, - 38, - 79, - 36, - 11, - 176, - 102, - 64, - 49 + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 ] ] ``` @@ -3143,26 +3143,26 @@ Inputs: 0, 0, 0, - 92, - 190, - 236, - 249, - 157, - 63, - 219, - 48, - 18, - 52, - 86, - 124, - 38, - 79, - 36, - 11, - 176, - 102, - 64, - 49 + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 ] ] ``` diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index e402149a650..c400dae3143 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -1431,30 +1431,25 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx // use that ID to form the masked ID address. // 4. Otherwise, we fetch the actor's ID from the state tree and form the masked ID with it. func lookupEthAddress(ctx context.Context, addr address.Address, sa StateAPI) (ethtypes.EthAddress, error) { - // Attempt to convert directly. - if ethAddr, ok, err := ethtypes.TryEthAddressFromFilecoinAddress(addr, false); err != nil { - return ethtypes.EthAddress{}, err - } else if ok { + // BLOCK A: We are trying to get an actual Ethereum address from an f410 address. + // Attempt to convert directly, if it's an f4 address. + ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(addr) + if err == nil && !ethAddr.IsMaskedID() { return ethAddr, nil } - // Lookup on the target actor. - actor, err := sa.StateGetActor(ctx, addr, types.EmptyTSK) - if err != nil { + // Lookup on the target actor and try to get an f410 address. + if actor, err := sa.StateGetActor(ctx, addr, types.EmptyTSK); err != nil { return ethtypes.EthAddress{}, err - } - if actor.Address != nil { - if ethAddr, ok, err := ethtypes.TryEthAddressFromFilecoinAddress(*actor.Address, false); err != nil { - return ethtypes.EthAddress{}, err - } else if ok { + } else if actor.Address != nil { + if ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(*actor.Address); err == nil && !ethAddr.IsMaskedID() { return ethAddr, nil } } + // BLOCK B: We gave up on getting an actual Ethereum address and are falling back to a Masked ID address. // Check if we already have an ID addr, and use it if possible. - if ethAddr, ok, err := ethtypes.TryEthAddressFromFilecoinAddress(addr, true); err != nil { - return ethtypes.EthAddress{}, err - } else if ok { + if err == nil && ethAddr.IsMaskedID() { return ethAddr, nil }