Merge branch 'release/v1.20.0' into asr/merge-release-into-master
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
@@ -16,6 +17,10 @@ var ErrModuleDisabled = errors.New("module disabled, enable with Fevm.EnableEthR
|
||||
|
||||
type EthModuleDummy struct{}
|
||||
|
||||
func (e *EthModuleDummy) EthAddressToFilecoinAddress(ctx context.Context, ethAddress ethtypes.EthAddress) (address.Address, error) {
|
||||
return address.Undef, ErrModuleDisabled
|
||||
}
|
||||
|
||||
func (e *EthModuleDummy) EthGetMessageCidByTransactionHash(ctx context.Context, txHash *ethtypes.EthHash) (*cid.Cid, error) {
|
||||
return nil, ErrModuleDisabled
|
||||
}
|
||||
|
||||
+154
-10
@@ -31,6 +31,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
builtinactors "github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||
builtinevm "github.com/filecoin-project/lotus/chain/actors/builtin/evm"
|
||||
"github.com/filecoin-project/lotus/chain/ethhashlookup"
|
||||
"github.com/filecoin-project/lotus/chain/events/filter"
|
||||
"github.com/filecoin-project/lotus/chain/messagepool"
|
||||
@@ -181,6 +182,10 @@ func (a *EthModule) EthAccounts(context.Context) ([]ethtypes.EthAddress, error)
|
||||
return []ethtypes.EthAddress{}, nil
|
||||
}
|
||||
|
||||
func (a *EthAPI) EthAddressToFilecoinAddress(ctx context.Context, ethAddress ethtypes.EthAddress) (address.Address, error) {
|
||||
return ethAddress.ToFilecoinAddress()
|
||||
}
|
||||
|
||||
func (a *EthModule) countTipsetMsgs(ctx context.Context, ts *types.TipSet) (int, error) {
|
||||
blkMsgs, err := a.Chain.BlockMsgsForTipset(ctx, ts)
|
||||
if err != nil {
|
||||
@@ -363,6 +368,26 @@ func (a *EthModule) EthGetTransactionCount(ctx context.Context, sender ethtypes.
|
||||
return ethtypes.EthUint64(0), xerrors.Errorf("cannot parse block param: %s", blkParam)
|
||||
}
|
||||
|
||||
// First, handle the case where the "sender" is an EVM actor.
|
||||
if actor, err := a.StateManager.LoadActor(ctx, addr, ts); err != nil {
|
||||
if xerrors.Is(err, types.ErrActorNotFound) {
|
||||
return 0, nil
|
||||
}
|
||||
return 0, xerrors.Errorf("failed to lookup contract %s: %w", sender, err)
|
||||
} else if builtinactors.IsEvmActor(actor.Code) {
|
||||
evmState, err := builtinevm.Load(a.Chain.ActorStore(ctx), actor)
|
||||
if err != nil {
|
||||
return 0, xerrors.Errorf("failed to load evm state: %w", err)
|
||||
}
|
||||
if alive, err := evmState.IsAlive(); err != nil {
|
||||
return 0, err
|
||||
} else if !alive {
|
||||
return 0, nil
|
||||
}
|
||||
nonce, err := evmState.Nonce()
|
||||
return ethtypes.EthUint64(nonce), err
|
||||
}
|
||||
|
||||
nonce, err := a.Mpool.GetNonce(ctx, addr, ts.Key())
|
||||
if err != nil {
|
||||
return ethtypes.EthUint64(0), nil
|
||||
@@ -506,13 +531,18 @@ func (a *EthModule) EthGetCode(ctx context.Context, ethAddr ethtypes.EthAddress,
|
||||
}
|
||||
|
||||
func (a *EthModule) EthGetStorageAt(ctx context.Context, ethAddr ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) {
|
||||
ts, err := a.parseBlkParam(ctx, blkParam)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("cannot parse block param: %s", blkParam)
|
||||
}
|
||||
|
||||
l := len(position)
|
||||
if l > 32 {
|
||||
return nil, fmt.Errorf("supplied storage key is too long")
|
||||
}
|
||||
|
||||
// pad with zero bytes if smaller than 32 bytes
|
||||
position = append(make([]byte, 32-l, 32-l), position...)
|
||||
position = append(make([]byte, 32-l, 32), position...)
|
||||
|
||||
to, err := ethAddr.ToFilecoinAddress()
|
||||
if err != nil {
|
||||
@@ -525,6 +555,18 @@ func (a *EthModule) EthGetStorageAt(ctx context.Context, ethAddr ethtypes.EthAdd
|
||||
return nil, fmt.Errorf("failed to construct system sender address: %w", err)
|
||||
}
|
||||
|
||||
actor, err := a.StateManager.LoadActor(ctx, to, ts)
|
||||
if err != nil {
|
||||
if xerrors.Is(err, types.ErrActorNotFound) {
|
||||
return ethtypes.EthBytes(make([]byte, 32)), nil
|
||||
}
|
||||
return nil, xerrors.Errorf("failed to lookup contract %s: %w", ethAddr, err)
|
||||
}
|
||||
|
||||
if !builtinactors.IsEvmActor(actor.Code) {
|
||||
return ethtypes.EthBytes(make([]byte, 32)), nil
|
||||
}
|
||||
|
||||
params, err := actors.SerializeParams(&evm.GetStorageAtParams{
|
||||
StorageKey: *(*[32]byte)(position),
|
||||
})
|
||||
@@ -543,8 +585,6 @@ func (a *EthModule) EthGetStorageAt(ctx context.Context, ethAddr ethtypes.EthAdd
|
||||
GasPremium: big.Zero(),
|
||||
}
|
||||
|
||||
ts := a.Chain.GetHeaviestTipSet()
|
||||
|
||||
// Try calling until we find a height with no migration.
|
||||
var res *api.InvocResult
|
||||
for {
|
||||
@@ -563,10 +603,22 @@ func (a *EthModule) EthGetStorageAt(ctx context.Context, ethAddr ethtypes.EthAdd
|
||||
}
|
||||
|
||||
if res.MsgRct == nil {
|
||||
return nil, fmt.Errorf("no message receipt")
|
||||
return nil, xerrors.Errorf("no message receipt")
|
||||
}
|
||||
|
||||
return res.MsgRct.Return, nil
|
||||
if res.MsgRct.ExitCode.IsError() {
|
||||
return nil, xerrors.Errorf("failed to lookup storage slot: %s", res.Error)
|
||||
}
|
||||
|
||||
var ret abi.CborBytes
|
||||
if err := ret.UnmarshalCBOR(bytes.NewReader(res.MsgRct.Return)); err != nil {
|
||||
return nil, xerrors.Errorf("failed to unmarshal storage slot: %w", err)
|
||||
}
|
||||
|
||||
// pad with zero bytes if smaller than 32 bytes
|
||||
ret = append(make([]byte, 32-len(ret), 32), ret...)
|
||||
|
||||
return ethtypes.EthBytes(ret), nil
|
||||
}
|
||||
|
||||
func (a *EthModule) EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) {
|
||||
@@ -840,7 +892,8 @@ func (a *EthModule) applyMessage(ctx context.Context, msg *types.Message, tsk ty
|
||||
return nil, xerrors.Errorf("CallWithGas failed: %w", err)
|
||||
}
|
||||
if res.MsgRct.ExitCode.IsError() {
|
||||
return nil, xerrors.Errorf("message execution failed: exit %s, msg receipt: %s, reason: %s", res.MsgRct.ExitCode, res.MsgRct.Return, res.Error)
|
||||
reason := parseEthRevert(res.MsgRct.Return)
|
||||
return nil, xerrors.Errorf("message execution failed: exit %s, revert reason: %s, vm error: %s", res.MsgRct.ExitCode, reason, res.Error)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
@@ -856,14 +909,24 @@ func (a *EthModule) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (et
|
||||
msg.GasLimit = 0
|
||||
|
||||
ts := a.Chain.GetHeaviestTipSet()
|
||||
msg, err = a.GasAPI.GasEstimateMessageGas(ctx, msg, nil, ts.Key())
|
||||
gassedMsg, err := a.GasAPI.GasEstimateMessageGas(ctx, msg, nil, ts.Key())
|
||||
if err != nil {
|
||||
// On failure, GasEstimateMessageGas doesn't actually return the invocation result,
|
||||
// it just returns an error. That means we can't get the revert reason.
|
||||
//
|
||||
// So we re-execute the message with EthCall (well, applyMessage which contains the
|
||||
// guts of EthCall). This will give us an ethereum specific error with revert
|
||||
// information.
|
||||
msg.GasLimit = build.BlockGasLimit
|
||||
if _, err2 := a.applyMessage(ctx, msg, ts.Key()); err2 != nil {
|
||||
err = err2
|
||||
}
|
||||
return ethtypes.EthUint64(0), xerrors.Errorf("failed to estimate gas: %w", err)
|
||||
}
|
||||
|
||||
expectedGas, err := ethGasSearch(ctx, a.Chain, a.Stmgr, a.Mpool, msg, ts)
|
||||
expectedGas, err := ethGasSearch(ctx, a.Chain, a.Stmgr, a.Mpool, gassedMsg, ts)
|
||||
if err != nil {
|
||||
log.Errorw("expected gas", "err", err)
|
||||
return 0, xerrors.Errorf("gas search failed: %w", err)
|
||||
}
|
||||
|
||||
return ethtypes.EthUint64(expectedGas), nil
|
||||
@@ -1001,7 +1064,7 @@ func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam s
|
||||
|
||||
invokeResult, err := a.applyMessage(ctx, msg, ts.Key())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to apply message: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if msg.To == builtintypes.EthereumAddressManagerActorAddr {
|
||||
@@ -2125,11 +2188,13 @@ func (m *EthTxHashManager) ProcessSignedMessage(ctx context.Context, msg *types.
|
||||
ethTx, err := newEthTxFromSignedMessage(ctx, msg, m.StateAPI)
|
||||
if err != nil {
|
||||
log.Errorf("error converting filecoin message to eth tx: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = m.TransactionHashLookup.UpsertHash(ethTx.Hash, msg.Cid())
|
||||
if err != nil {
|
||||
log.Errorf("error inserting tx mapping to db: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2180,6 +2245,85 @@ func parseEthTopics(topics ethtypes.EthTopicSpec) (map[string][][]byte, error) {
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
const errorFunctionSelector = "\x08\xc3\x79\xa0" // Error(string)
|
||||
const panicFunctionSelector = "\x4e\x48\x7b\x71" // Panic(uint256)
|
||||
// Eth ABI (solidity) panic codes.
|
||||
var panicErrorCodes map[uint64]string = map[uint64]string{
|
||||
0x00: "Panic()",
|
||||
0x01: "Assert()",
|
||||
0x11: "ArithmeticOverflow()",
|
||||
0x12: "DivideByZero()",
|
||||
0x21: "InvalidEnumVariant()",
|
||||
0x22: "InvalidStorageArray()",
|
||||
0x31: "PopEmptyArray()",
|
||||
0x32: "ArrayIndexOutOfBounds()",
|
||||
0x41: "OutOfMemory()",
|
||||
0x51: "CalledUninitializedFunction()",
|
||||
}
|
||||
|
||||
// Parse an ABI encoded revert reason. This reason should be encoded as if it were the parameters to
|
||||
// an `Error(string)` function call.
|
||||
//
|
||||
// See https://docs.soliditylang.org/en/latest/control-structures.html#panic-via-assert-and-error-via-require
|
||||
func parseEthRevert(ret []byte) string {
|
||||
if len(ret) == 0 {
|
||||
return "none"
|
||||
}
|
||||
var cbytes abi.CborBytes
|
||||
if err := cbytes.UnmarshalCBOR(bytes.NewReader(ret)); err != nil {
|
||||
return "ERROR: revert reason is not cbor encoded bytes"
|
||||
}
|
||||
if len(cbytes) == 0 {
|
||||
return "none"
|
||||
}
|
||||
// If it's not long enough to contain an ABI encoded response, return immediately.
|
||||
if len(cbytes) < 4+32 {
|
||||
return ethtypes.EthBytes(cbytes).String()
|
||||
}
|
||||
switch string(cbytes[:4]) {
|
||||
case panicFunctionSelector:
|
||||
cbytes := cbytes[4 : 4+32]
|
||||
// Read the and check the code.
|
||||
code, err := ethtypes.EthUint64FromBytes(cbytes)
|
||||
if err != nil {
|
||||
// If it's too big, just return the raw value.
|
||||
codeInt := big.PositiveFromUnsignedBytes(cbytes)
|
||||
return fmt.Sprintf("Panic(%s)", ethtypes.EthBigInt(codeInt).String())
|
||||
}
|
||||
if s, ok := panicErrorCodes[uint64(code)]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Panic(0x%x)", code)
|
||||
case errorFunctionSelector:
|
||||
cbytes := cbytes[4:]
|
||||
cbytesLen := ethtypes.EthUint64(len(cbytes))
|
||||
// Read the and check the offset.
|
||||
offset, err := ethtypes.EthUint64FromBytes(cbytes[:32])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if cbytesLen < offset {
|
||||
break
|
||||
}
|
||||
|
||||
// Read and check the length.
|
||||
if cbytesLen-offset < 32 {
|
||||
break
|
||||
}
|
||||
start := offset + 32
|
||||
length, err := ethtypes.EthUint64FromBytes(cbytes[offset : offset+32])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if cbytesLen-start < length {
|
||||
break
|
||||
}
|
||||
// Slice the error message.
|
||||
return fmt.Sprintf("Error(%s)", cbytes[start:start+length])
|
||||
}
|
||||
return ethtypes.EthBytes(cbytes).String()
|
||||
}
|
||||
|
||||
func calculateRewardsAndGasUsed(rewardPercentiles []float64, txGasRewards gasRewardSorter) ([]ethtypes.EthBigInt, uint64) {
|
||||
var totalGasUsed uint64
|
||||
for _, tx := range txGasRewards {
|
||||
|
||||
Reference in New Issue
Block a user