Merge branch 'release/v1.20.0' into iand/issue-9849-filters

This commit is contained in:
Raúl Kripalani 2023-01-20 01:11:31 +00:00
commit 21c93f1dc1
28 changed files with 553 additions and 384 deletions

View File

@ -730,6 +730,11 @@ workflows:
suite: itest-eth_balance
target: "./itests/eth_balance_test.go"
- test:
name: test-itest-eth_block_hash
suite: itest-eth_block_hash
target: "./itests/eth_block_hash_test.go"
- test:
name: test-itest-eth_deploy
suite: itest-eth_deploy

View File

@ -275,13 +275,13 @@ func (te *TipSetEvents) messages(ctx context.Context) ([]executedMessage, error)
}
type executedMessage struct {
msg *types.Message
msg types.ChainMsg
rct *types.MessageReceipt
// events extracted from receipt
evs []*types.Event
}
func (e *executedMessage) Message() *types.Message {
func (e *executedMessage) Message() types.ChainMsg {
return e.msg
}
@ -437,7 +437,7 @@ func (m *EventFilterManager) loadExecutedMessages(ctx context.Context, msgTs, rc
ems := make([]executedMessage, len(msgs))
for i := 0; i < len(msgs); i++ {
ems[i].msg = msgs[i].VMMessage()
ems[i].msg = msgs[i]
var rct types.MessageReceipt
found, err := arr.Get(uint64(i), &rct)

View File

@ -13,10 +13,13 @@ import (
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/messagepool"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
"github.com/filecoin-project/lotus/chain/wallet/key"
"github.com/filecoin-project/lotus/node/modules/dtypes"
)
@ -66,15 +69,24 @@ func (ms *MessageSigner) SignMessage(ctx context.Context, msg *types.Message, sp
// Sign the message with the nonce
msg.Nonce = nonce
keyInfo, err := ms.wallet.WalletExport(ctx, msg.From)
if err != nil {
return nil, err
}
sb, err := SigningBytes(msg, key.ActSigType(keyInfo.Type))
if err != nil {
return nil, err
}
mb, err := msg.ToStorageBlock()
if err != nil {
return nil, xerrors.Errorf("serializing message: %w", err)
}
sig, err := ms.wallet.WalletSign(ctx, msg.From, mb.Cid().Bytes(), api.MsgMeta{
sig, err := ms.wallet.WalletSign(ctx, msg.From, sb, api.MsgMeta{
Type: api.MTChainMsg,
Extra: mb.RawData(),
})
if err != nil {
return nil, xerrors.Errorf("failed to sign message: %w, addr=%s", err, msg.From)
}
@ -187,3 +199,19 @@ func (ms *MessageSigner) SaveNonce(ctx context.Context, addr address.Address, no
func (ms *MessageSigner) dstoreKey(addr address.Address) datastore.Key {
return datastore.KeyWithNamespaces([]string{dsKeyActorNonce, addr.String()})
}
func SigningBytes(msg *types.Message, sigType crypto.SigType) ([]byte, error) {
if sigType == crypto.SigTypeDelegated {
txArgs, err := ethtypes.EthTxArgsFromMessage(msg)
if err != nil {
return nil, xerrors.Errorf("failed to reconstruct eth transaction: %w", err)
}
rlpEncodedMsg, err := txArgs.ToRlpUnsignedMsg()
if err != nil {
return nil, xerrors.Errorf("failed to repack eth rlp message: %w", err)
}
return rlpEncodedMsg, nil
}
return msg.Cid().Bytes(), nil
}

View File

@ -384,7 +384,19 @@ func (cs *ChainStore) PutTipSet(ctx context.Context, ts *types.TipSet) error {
if err != nil {
return xerrors.Errorf("errored while expanding tipset: %w", err)
}
log.Debugf("expanded %s into %s\n", ts.Cids(), expanded.Cids())
if expanded.Key() != ts.Key() {
log.Debugf("expanded %s into %s\n", ts.Cids(), expanded.Cids())
tsBlk, err := expanded.Key().ToStorageBlock()
if err != nil {
return xerrors.Errorf("failed to get tipset key block: %w", err)
}
if err = cs.chainLocalBlockstore.Put(ctx, tsBlk); err != nil {
return xerrors.Errorf("failed to put tipset key block: %w", err)
}
}
if err := cs.MaybeTakeHeavierTipSet(ctx, expanded); err != nil {
return xerrors.Errorf("MaybeTakeHeavierTipSet failed in PutTipSet: %w", err)

View File

@ -345,8 +345,8 @@ var EvmInvokeCmd = &cli.Command{
defer closer()
ctx := ReqContext(cctx)
if argc := cctx.Args().Len(); argc < 2 || argc > 3 {
return xerrors.Errorf("must pass the address, entry point and (optionally) input data")
if argc := cctx.Args().Len(); argc != 2 {
return xerrors.Errorf("must pass the address and calldata")
}
addr, err := address.NewFromString(cctx.Args().Get(0))
@ -355,7 +355,7 @@ var EvmInvokeCmd = &cli.Command{
}
var calldata []byte
calldata, err = hex.DecodeString(cctx.Args().Get(2))
calldata, err = hex.DecodeString(cctx.Args().Get(1))
if err != nil {
return xerrors.Errorf("decoding hex input data: %w", err)
}
@ -388,7 +388,7 @@ var EvmInvokeCmd = &cli.Command{
To: addr,
From: fromAddr,
Value: val,
Method: abi.MethodNum(2),
Method: builtintypes.MethodsEVM.InvokeContract,
Params: calldata,
}

View File

@ -13,6 +13,7 @@ import (
"github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
)
var sendCmd = &cli.Command{
@ -24,6 +25,10 @@ var sendCmd = &cli.Command{
Name: "from",
Usage: "optionally specify the account to send funds from",
},
&cli.StringFlag{
Name: "from-eth-addr",
Usage: "optionally specify the eth addr to send funds from",
},
&cli.StringFlag{
Name: "gas-premium",
Usage: "specify gas price to use in AttoFIL",
@ -98,6 +103,18 @@ var sendCmd = &cli.Command{
}
params.From = addr
} else if from := cctx.String("from-eth-addr"); from != "" {
eaddr, err := ethtypes.ParseEthAddress(from)
if err != nil {
return err
}
faddr, err := eaddr.ToFilecoinAddress()
if err != nil {
fmt.Println("error on conversion to faddr")
return err
}
fmt.Println("f4 addr: ", faddr)
params.From = faddr
}
if cctx.IsSet("gas-premium") {

View File

@ -181,15 +181,16 @@ CATEGORY:
BASIC
OPTIONS:
--force Deprecated: use global 'force-send' (default: false)
--from value optionally specify the account to send funds from
--gas-feecap value specify gas fee cap to use in AttoFIL (default: "0")
--gas-limit value specify gas limit (default: 0)
--gas-premium value specify gas price to use in AttoFIL (default: "0")
--method value specify method to invoke (default: 0)
--nonce value specify the nonce to use (default: 0)
--params-hex value specify invocation parameters in hex
--params-json value specify invocation parameters in json
--force Deprecated: use global 'force-send' (default: false)
--from value optionally specify the account to send funds from
--from-eth-addr value optionally specify the eth addr to send funds from
--gas-feecap value specify gas fee cap to use in AttoFIL (default: "0")
--gas-limit value specify gas limit (default: 0)
--gas-premium value specify gas price to use in AttoFIL (default: "0")
--method value specify method to invoke (default: 0)
--nonce value specify the nonce to use (default: 0)
--params-hex value specify invocation parameters in hex
--params-json value specify invocation parameters in json
```

View File

@ -293,63 +293,13 @@
#Tracing = false
[ActorEvent]
# EnableRealTimeFilterAPI enables APIs that can create and query filters for actor events as they are emitted.
#
# type: bool
# env var: LOTUS_ACTOREVENT_ENABLEREALTIMEFILTERAPI
#EnableRealTimeFilterAPI = false
# EnableHistoricFilterAPI enables APIs that can create and query filters for actor events that occurred in the past.
# A queryable index of events will be maintained.
#
# type: bool
# env var: LOTUS_ACTOREVENT_ENABLEHISTORICFILTERAPI
#EnableHistoricFilterAPI = false
# FilterTTL specifies the time to live for actor event filters. Filters that haven't been accessed longer than
# this time become eligible for automatic deletion.
#
# type: Duration
# env var: LOTUS_ACTOREVENT_FILTERTTL
#FilterTTL = "24h0m0s"
# MaxFilters specifies the maximum number of filters that may exist at any one time.
#
# type: int
# env var: LOTUS_ACTOREVENT_MAXFILTERS
#MaxFilters = 100
# MaxFilterResults specifies the maximum number of results that can be accumulated by an actor event filter.
#
# type: int
# env var: LOTUS_ACTOREVENT_MAXFILTERRESULTS
#MaxFilterResults = 10000
# MaxFilterHeightRange specifies the maximum range of heights that can be used in a filter (to avoid querying
# the entire chain)
#
# type: uint64
# env var: LOTUS_ACTOREVENT_MAXFILTERHEIGHTRANGE
#MaxFilterHeightRange = 2880
# ActorEventDatabasePath is the full path to a sqlite database that will be used to index actor events to
# support the historic filter APIs. If the database does not exist it will be created. The directory containing
# the database must already exist and be writeable. If a relative path is provided here, sqlite treats it as
# relative to the CWD (current working directory).
#
# type: string
# env var: LOTUS_ACTOREVENT_ACTOREVENTDATABASEPATH
#ActorEventDatabasePath = ""
[Fevm]
# EnableEthHashToFilecoinCidMapping enables storing a mapping of eth transaction hashes to filecoin message Cids
# You will not be able to look up ethereum transactions by their hash if this is disabled.
# EnableEthRPC enables eth_ rpc, and enables storing a mapping of eth transaction hashes to filecoin message Cids.
# This will also enable the RealTimeFilterAPI and HistoricFilterAPI by default, but they can be disabled by config options above.
#
# type: bool
# env var: LOTUS_FEVM_ENABLEETHHASHTOFILECOINCIDMAPPING
#EnableEthHashToFilecoinCidMapping = false
# env var: LOTUS_FEVM_ENABLEETHRPC
#EnableEthRPC = false
# EthTxHashMappingLifetimeDays the transaction hash lookup database will delete mappings that have been stored for more than x days
# Set to 0 to keep all mappings
@ -358,4 +308,56 @@
# env var: LOTUS_FEVM_ETHTXHASHMAPPINGLIFETIMEDAYS
#EthTxHashMappingLifetimeDays = 0
[Fevm.Events]
# EnableEthRPC enables APIs that
# DisableRealTimeFilterAPI will disable the RealTimeFilterAPI that can create and query filters for actor events as they are emitted.
# The API is enabled when EnableEthRPC is true, but can be disabled selectively with this flag.
#
# type: bool
# env var: LOTUS_FEVM_EVENTS_DISABLEREALTIMEFILTERAPI
#DisableRealTimeFilterAPI = false
# DisableHistoricFilterAPI will disable the HistoricFilterAPI that can create and query filters for actor events
# that occurred in the past. HistoricFilterAPI maintains a queryable index of events.
# The API is enabled when EnableEthRPC is true, but can be disabled selectively with this flag.
#
# type: bool
# env var: LOTUS_FEVM_EVENTS_DISABLEHISTORICFILTERAPI
#DisableHistoricFilterAPI = false
# FilterTTL specifies the time to live for actor event filters. Filters that haven't been accessed longer than
# this time become eligible for automatic deletion.
#
# type: Duration
# env var: LOTUS_FEVM_EVENTS_FILTERTTL
#FilterTTL = "24h0m0s"
# MaxFilters specifies the maximum number of filters that may exist at any one time.
#
# type: int
# env var: LOTUS_FEVM_EVENTS_MAXFILTERS
#MaxFilters = 100
# MaxFilterResults specifies the maximum number of results that can be accumulated by an actor event filter.
#
# type: int
# env var: LOTUS_FEVM_EVENTS_MAXFILTERRESULTS
#MaxFilterResults = 10000
# MaxFilterHeightRange specifies the maximum range of heights that can be used in a filter (to avoid querying
# the entire chain)
#
# type: uint64
# env var: LOTUS_FEVM_EVENTS_MAXFILTERHEIGHTRANGE
#MaxFilterHeightRange = 2880
# DatabasePath is the full path to a sqlite database that will be used to index actor events to
# support the historic filter APIs. If the database does not exist it will be created. The directory containing
# the database must already exist and be writeable. If a relative path is provided here, sqlite treats it as
# relative to the CWD (current working directory).
#
# type: string
# env var: LOTUS_FEVM_EVENTS_DATABASEPATH
#DatabasePath = ""

View File

@ -33,7 +33,7 @@ func TestDealPadding(t *testing.T) {
dh := kit.NewDealHarness(t, client, miner, miner)
ctx := context.Background()
client.WaitTillChain(ctx, kit.BlockMinedBy(miner.ActorAddr))
client.WaitTillChain(ctx, kit.BlocksMinedByAll(miner.ActorAddr))
// Create a random file, would originally be a 256-byte sector
res, inFile := client.CreateImportFile(ctx, 1, 200)

View File

@ -52,7 +52,7 @@ func TestFirstDealEnablesMining(t *testing.T) {
providerMined := make(chan struct{})
go func() {
_ = client.WaitTillChain(ctx, kit.BlockMinedBy(provider.ActorAddr))
_ = client.WaitTillChain(ctx, kit.BlocksMinedByAll(provider.ActorAddr))
close(providerMined)
}()

View File

@ -0,0 +1,65 @@
package itests
import (
"context"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/itests/kit"
)
// TestEthBlockHashesCorrect_MultiBlockTipset validates that blocks retrieved through
// EthGetBlockByNumber are identical to blocks retrieved through
// EthGetBlockByHash, when using the block hash returned by the former.
//
// Specifically, it checks the system behaves correctly with multiblock tipsets.
//
// Catches regressions around https://github.com/filecoin-project/lotus/issues/10061.
func TestEthBlockHashesCorrect_MultiBlockTipset(t *testing.T) {
// miner is connected to the first node, and we want to observe the chain
// from the second node.
blocktime := 100 * time.Millisecond
n1, m1, m2, ens := kit.EnsembleOneTwo(t,
kit.MockProofs(),
kit.ThroughRPC(),
)
ens.InterconnectAll().BeginMining(blocktime)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
n1.WaitTillChain(ctx, kit.HeightAtLeast(abi.ChainEpoch(25)))
defer cancel()
var n2 kit.TestFullNode
ens.FullNode(&n2, kit.ThroughRPC()).Start().Connect(n2, n1)
// find the first tipset where all miners mined a block.
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Minute)
n2.WaitTillChain(ctx, kit.BlocksMinedByAll(m1.ActorAddr, m2.ActorAddr))
defer cancel()
head, err := n2.ChainHead(context.Background())
require.NoError(t, err)
// let the chain run a little bit longer to minimise the chance of reorgs
n2.WaitTillChain(ctx, kit.HeightAtLeast(head.Height()+50))
head, err = n2.ChainHead(context.Background())
require.NoError(t, err)
for i := 1; i <= int(head.Height()); i++ {
hex := fmt.Sprintf("0x%x", i)
ethBlockA, err := n2.EthGetBlockByNumber(ctx, hex, true)
require.NoError(t, err)
ethBlockB, err := n2.EthGetBlockByHash(ctx, ethBlockA.Hash, true)
require.NoError(t, err)
require.Equal(t, ethBlockA, ethBlockB)
}
}

View File

@ -20,7 +20,6 @@ import (
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
"github.com/filecoin-project/lotus/itests/kit"
"github.com/filecoin-project/lotus/node/config"
)
// TestDeployment smoke tests the deployment of a contract via the
@ -36,13 +35,7 @@ func TestDeployment(t *testing.T) {
client, _, ens := kit.EnsembleMinimal(
t,
kit.MockProofs(),
kit.ThroughRPC(),
kit.WithCfgOpt(func(cfg *config.FullNode) error {
cfg.ActorEvent.EnableRealTimeFilterAPI = true
return nil
}),
kit.EthTxHashLookup(),
)
kit.ThroughRPC())
ens.InterconnectAll().BeginMining(blockTime)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
@ -100,6 +93,7 @@ func TestDeployment(t *testing.T) {
mpoolTx, err := client.EthGetTransactionByHash(ctx, &hash)
require.NoError(t, err)
require.NotNil(t, mpoolTx)
// require that the hashes are identical
require.Equal(t, hash, mpoolTx.Hash)

View File

@ -9,7 +9,6 @@ import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
@ -86,7 +85,7 @@ func TestEthNewPendingTransactionFilter(t *testing.T) {
kit.QuietAllLogsExcept("events", "messagepool")
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.RealTimeFilterAPI())
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.WithEthRPC())
ens.InterconnectAll().BeginMining(10 * time.Millisecond)
// create a new address where to send funds.
@ -190,7 +189,7 @@ func TestEthNewBlockFilter(t *testing.T) {
kit.QuietAllLogsExcept("events", "messagepool")
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.RealTimeFilterAPI())
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.WithEthRPC())
ens.InterconnectAll().BeginMining(10 * time.Millisecond)
// create a new address where to send funds.
@ -294,7 +293,7 @@ func TestEthNewFilterCatchAll(t *testing.T) {
kit.QuietAllLogsExcept("events", "messagepool")
blockTime := 100 * time.Millisecond
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.RealTimeFilterAPI(), kit.EthTxHashLookup())
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.WithEthRPC())
ens.InterconnectAll().BeginMining(blockTime)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
@ -373,9 +372,8 @@ func TestEthGetLogsAll(t *testing.T) {
kit.QuietAllLogsExcept("events", "messagepool")
blockTime := 100 * time.Millisecond
dbpath := filepath.Join(t.TempDir(), "actorevents.db")
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.HistoricFilterAPI(dbpath))
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
ens.InterconnectAll().BeginMining(blockTime)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
@ -418,9 +416,8 @@ func TestEthGetLogsByTopic(t *testing.T) {
kit.QuietAllLogsExcept("events", "messagepool")
blockTime := 100 * time.Millisecond
dbpath := filepath.Join(t.TempDir(), "actorevents.db")
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.HistoricFilterAPI(dbpath))
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
ens.InterconnectAll().BeginMining(blockTime)
invocations := 1
@ -457,7 +454,7 @@ func TestEthSubscribeLogs(t *testing.T) {
kit.QuietAllLogsExcept("events", "messagepool")
blockTime := 100 * time.Millisecond
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.RealTimeFilterAPI())
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
ens.InterconnectAll().BeginMining(blockTime)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
@ -517,9 +514,8 @@ func TestEthGetLogs(t *testing.T) {
kit.QuietAllLogsExcept("events", "messagepool")
blockTime := 100 * time.Millisecond
dbpath := filepath.Join(t.TempDir(), "actorevents.db")
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.HistoricFilterAPI(dbpath))
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
ens.InterconnectAll().BeginMining(blockTime)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
@ -1078,9 +1074,8 @@ func TestEthGetLogsWithBlockRanges(t *testing.T) {
kit.QuietAllLogsExcept("events", "messagepool")
blockTime := 100 * time.Millisecond
dbpath := filepath.Join(t.TempDir(), "actorevents.db")
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.HistoricFilterAPI(dbpath))
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
ens.InterconnectAll().BeginMining(blockTime)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
@ -1908,15 +1903,98 @@ func ParseEthLog(in map[string]interface{}) (*ethtypes.EthLog, error) {
return el, err
}
func decodeLogBytes(orig []byte) []byte {
if len(orig) == 0 {
return orig
func invokeContractAndWaitUntilAllOnChain(t *testing.T, client *kit.TestFullNode, iterations int) (ethtypes.EthAddress, map[ethtypes.EthHash]msgInTipset) {
require := require.New(t)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
blockTime := 100 * time.Millisecond
// install contract
contractHex, err := os.ReadFile("contracts/events.bin")
require.NoError(err)
contract, err := hex.DecodeString(string(contractHex))
require.NoError(err)
fromAddr, err := client.WalletDefaultAddress(ctx)
require.NoError(err)
result := client.EVM().DeployContract(ctx, fromAddr, contract)
idAddr, err := address.NewIDAddress(result.ActorID)
require.NoError(err)
t.Logf("actor ID address is %s", idAddr)
msgChan := make(chan msgInTipset, iterations)
waitAllCh := make(chan struct{})
go func() {
headChangeCh, err := client.ChainNotify(ctx)
require.NoError(err)
<-headChangeCh // skip hccurrent
count := 0
for {
select {
case headChanges := <-headChangeCh:
for _, change := range headChanges {
if change.Type == store.HCApply || change.Type == store.HCRevert {
msgs, err := client.ChainGetMessagesInTipset(ctx, change.Val.Key())
require.NoError(err)
count += len(msgs)
for _, m := range msgs {
select {
case msgChan <- msgInTipset{msg: m, ts: change.Val, reverted: change.Type == store.HCRevert}:
default:
}
}
if count == iterations {
close(msgChan)
close(waitAllCh)
return
}
}
}
}
}
}()
time.Sleep(blockTime * 6)
for i := 0; i < iterations; i++ {
// log a four topic event with data
ret := client.EVM().InvokeSolidity(ctx, fromAddr, idAddr, []byte{0x00, 0x00, 0x00, 0x02}, nil)
require.True(ret.Receipt.ExitCode.IsSuccess(), "contract execution failed")
}
decoded, err := cbg.ReadByteArray(bytes.NewReader(orig), uint64(len(orig)))
if err != nil {
return orig
select {
case <-waitAllCh:
case <-time.After(time.Minute):
t.Errorf("timeout to wait for pack messages")
}
return decoded
received := make(map[ethtypes.EthHash]msgInTipset)
for m := range msgChan {
eh, err := ethtypes.EthHashFromCid(m.msg.Cid)
require.NoError(err)
received[eh] = m
}
require.Equal(iterations, len(received), "all messages on chain")
head, err := client.ChainHead(ctx)
require.NoError(err)
actor, err := client.StateGetActor(ctx, idAddr, head.Key())
require.NoError(err)
require.NotNil(actor.Address)
ethContractAddr, err := ethtypes.EthAddressFromFilecoinAddress(*actor.Address)
require.NoError(err)
return ethContractAddr, received
}
func paddedEthBytes(orig []byte) ethtypes.EthBytes {
@ -2050,3 +2128,14 @@ func (e *ethFilterBuilder) Topic4OneOf(hs ...ethtypes.EthHash) *ethFilterBuilder
e.filter.Topics[3] = hs
return e
}
func decodeLogBytes(orig []byte) []byte {
if len(orig) == 0 {
return orig
}
decoded, err := cbg.ReadByteArray(bytes.NewReader(orig), uint64(len(orig)))
if err != nil {
return orig
}
return decoded
}

View File

@ -16,7 +16,6 @@ import (
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
"github.com/filecoin-project/lotus/itests/kit"
"github.com/filecoin-project/lotus/node/config"
)
// TestTransactionHashLookup tests to see if lotus correctly stores a mapping from ethereum transaction hash to
@ -29,7 +28,6 @@ func TestTransactionHashLookup(t *testing.T) {
t,
kit.MockProofs(),
kit.ThroughRPC(),
kit.EthTxHashLookup(),
)
ens.InterconnectAll().BeginMining(blocktime)
@ -112,86 +110,6 @@ func TestTransactionHashLookup(t *testing.T) {
require.Equal(t, uint64(*chainTx.TransactionIndex), uint64(0)) // only transaction
}
// TestTransactionHashLookupNoDb tests to see if looking up eth transactions by hash breaks without the lookup table
func TestTransactionHashLookupNoDb(t *testing.T) {
kit.QuietMiningLogs()
blocktime := 1 * time.Second
client, _, ens := kit.EnsembleMinimal(
t,
kit.MockProofs(),
kit.ThroughRPC(),
kit.WithCfgOpt(func(cfg *config.FullNode) error {
cfg.Fevm.EnableEthHashToFilecoinCidMapping = false
return nil
}),
)
ens.InterconnectAll().BeginMining(blocktime)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
// install contract
contractHex, err := os.ReadFile("./contracts/SimpleCoin.hex")
require.NoError(t, err)
contract, err := hex.DecodeString(string(contractHex))
require.NoError(t, err)
// create a new Ethereum account
key, ethAddr, deployer := client.EVM().NewAccount()
// send some funds to the f410 address
kit.SendFunds(ctx, t, client, deployer, types.FromFil(10))
gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{
From: &ethAddr,
Data: contract,
})
require.NoError(t, err)
maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx)
require.NoError(t, err)
// now deploy a contract from the embryo, and validate it went well
tx := ethtypes.EthTxArgs{
ChainID: build.Eip155ChainId,
Value: big.Zero(),
Nonce: 0,
MaxFeePerGas: types.NanoFil,
MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas),
GasLimit: int(gaslimit),
Input: contract,
V: big.Zero(),
R: big.Zero(),
S: big.Zero(),
}
client.EVM().SignTransaction(&tx, key.PrivateKey)
rawTxHash, err := tx.TxHash()
require.NoError(t, err)
hash := client.EVM().SubmitTransaction(ctx, &tx)
require.Equal(t, rawTxHash, hash)
// We shouldn't be able to find the tx
mpoolTx, err := client.EthGetTransactionByHash(ctx, &hash)
require.NoError(t, err)
require.Nil(t, mpoolTx)
// Wait for message to land on chain, we can't know exactly when because we can't find it.
time.Sleep(20 * blocktime)
receipt, err := client.EthGetTransactionReceipt(ctx, hash)
require.NoError(t, err)
require.Nil(t, receipt)
// We still shouldn't be able to find the tx
chainTx, err := client.EthGetTransactionByHash(ctx, &hash)
require.NoError(t, err)
require.Nil(t, chainTx)
}
// TestTransactionHashLookupBlsFilecoinMessage tests to see if lotus can find a BLS Filecoin Message using the transaction hash
func TestTransactionHashLookupBlsFilecoinMessage(t *testing.T) {
kit.QuietMiningLogs()
@ -201,7 +119,6 @@ func TestTransactionHashLookupBlsFilecoinMessage(t *testing.T) {
t,
kit.MockProofs(),
kit.ThroughRPC(),
kit.EthTxHashLookup(),
)
ens.InterconnectAll().BeginMining(blocktime)
@ -271,7 +188,6 @@ func TestTransactionHashLookupSecpFilecoinMessage(t *testing.T) {
t,
kit.MockProofs(),
kit.ThroughRPC(),
kit.EthTxHashLookup(),
)
ens.InterconnectAll().BeginMining(blocktime)
@ -348,7 +264,6 @@ func TestTransactionHashLookupNonexistentMessage(t *testing.T) {
t,
kit.MockProofs(),
kit.ThroughRPC(),
kit.EthTxHashLookup(),
)
ens.InterconnectAll().BeginMining(blocktime)
@ -379,7 +294,6 @@ func TestEthGetMessageCidByTransactionHashEthTx(t *testing.T) {
t,
kit.MockProofs(),
kit.ThroughRPC(),
kit.EthTxHashLookup(),
)
ens.InterconnectAll().BeginMining(blocktime)
@ -476,7 +390,6 @@ func TestEthGetMessageCidByTransactionHashSecp(t *testing.T) {
t,
kit.MockProofs(),
kit.ThroughRPC(),
kit.EthTxHashLookup(),
)
ens.InterconnectAll().BeginMining(blocktime)
@ -547,7 +460,6 @@ func TestEthGetMessageCidByTransactionHashBLS(t *testing.T) {
t,
kit.MockProofs(),
kit.ThroughRPC(),
kit.EthTxHashLookup(),
)
ens.InterconnectAll().BeginMining(blocktime)

View File

@ -21,7 +21,7 @@ import (
func TestValueTransferValidSignature(t *testing.T) {
blockTime := 100 * time.Millisecond
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.EthTxHashLookup())
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
ens.InterconnectAll().BeginMining(blockTime)
@ -106,7 +106,7 @@ func TestLegacyTransaction(t *testing.T) {
func TestContractDeploymentValidSignature(t *testing.T) {
blockTime := 100 * time.Millisecond
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.EthTxHashLookup())
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
ens.InterconnectAll().BeginMining(blockTime)
@ -167,7 +167,7 @@ func TestContractDeploymentValidSignature(t *testing.T) {
func TestContractInvocation(t *testing.T) {
blockTime := 100 * time.Millisecond
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.EthTxHashLookup())
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
ens.InterconnectAll().BeginMining(blockTime)

View File

@ -132,3 +132,10 @@ func TestFEVMDelegateCall(t *testing.T) {
require.NoError(t, err)
require.Equal(t, result, expectedResultActor)
}
func TestEVMRpcDisable(t *testing.T) {
client, _, _ := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.DisableEthRPC())
_, err := client.EthBlockNumber(context.Background())
require.ErrorContains(t, err, "module disabled, enable with Fevm.EnableEthRPC")
}

View File

@ -135,13 +135,21 @@ func HeightAtLeast(target abi.ChainEpoch) ChainPredicate {
}
}
// BlockMinedBy returns a ChainPredicate that is satisfied when we observe the
// first block mined by the specified miner.
func BlockMinedBy(miner address.Address) ChainPredicate {
// BlocksMinedByAll returns a ChainPredicate that is satisfied when we observe a
// tipset including blocks from all the specified miners, in no particular order.
func BlocksMinedByAll(miner ...address.Address) ChainPredicate {
return func(ts *types.TipSet) bool {
seen := make([]bool, len(miner))
var done int
for _, b := range ts.Blocks() {
if b.Miner == miner {
return true
for i, m := range miner {
if b.Miner != m || seen[i] {
continue
}
seen[i] = true
if done++; done == len(miner) {
return true
}
}
}
return false

View File

@ -58,6 +58,15 @@ var DefaultNodeOpts = nodeOpts{
sectors: DefaultPresealsPerBootstrapMiner,
sectorSize: abi.SectorSize(2 << 10), // 2KiB.
cfgOpts: []CfgOption{
func(cfg *config.FullNode) error {
// test defaults
cfg.Fevm.EnableEthRPC = true
return nil
},
},
workerTasks: []sealtasks.TaskType{sealtasks.TTFetch, sealtasks.TTCommit1, sealtasks.TTFinalize, sealtasks.TTFinalizeUnsealed},
workerStorageOpt: func(store paths.Store) paths.Store { return store },
}
@ -281,25 +290,16 @@ func SplitstoreMessges() NodeOpt {
})
}
func RealTimeFilterAPI() NodeOpt {
func WithEthRPC() NodeOpt {
return WithCfgOpt(func(cfg *config.FullNode) error {
cfg.ActorEvent.EnableRealTimeFilterAPI = true
cfg.Fevm.EnableEthRPC = true
return nil
})
}
func HistoricFilterAPI(dbpath string) NodeOpt {
func DisableEthRPC() NodeOpt {
return WithCfgOpt(func(cfg *config.FullNode) error {
cfg.ActorEvent.EnableRealTimeFilterAPI = true
cfg.ActorEvent.EnableHistoricFilterAPI = true
cfg.ActorEvent.ActorEventDatabasePath = dbpath
return nil
})
}
func EthTxHashLookup() NodeOpt {
return WithCfgOpt(func(cfg *config.FullNode) error {
cfg.Fevm.EnableEthHashToFilecoinCidMapping = true
cfg.Fevm.EnableEthRPC = false
return nil
})
}

View File

@ -68,7 +68,7 @@ func (delegatedSigner) Verify(sig []byte, a address.Address, msg []byte) error {
}
if maybeaddr != a {
return fmt.Errorf("signature did not match")
return fmt.Errorf("signature did not match maybeaddr: %s, signer: %s", maybeaddr, a)
}
return nil

View File

@ -259,9 +259,10 @@ func ConfigFullNode(c interface{}) Option {
// Actor event filtering support
Override(new(events.EventAPI), From(new(modules.EventAPI))),
// in lite-mode Eth event api is provided by gateway
ApplyIf(isFullNode, Override(new(full.EthEventAPI), modules.EthEventAPI(cfg.ActorEvent))),
ApplyIf(isFullNode, Override(new(full.EthEventAPI), modules.EthEventAPI(cfg.Fevm))),
Override(new(full.EthModuleAPI), modules.EthModuleAPI(cfg.Fevm)),
If(cfg.Fevm.EnableEthRPC, Override(new(full.EthModuleAPI), modules.EthModuleAPI(cfg.Fevm))),
If(!cfg.Fevm.EnableEthRPC, Override(new(full.EthModuleAPI), &full.EthModuleDummy{})),
)
}

View File

@ -99,17 +99,17 @@ func DefaultFullNode() *FullNode {
},
},
Cluster: *DefaultUserRaftConfig(),
ActorEvent: ActorEventConfig{
EnableRealTimeFilterAPI: false,
EnableHistoricFilterAPI: false,
FilterTTL: Duration(time.Hour * 24),
MaxFilters: 100,
MaxFilterResults: 10000,
MaxFilterHeightRange: 2880, // conservative limit of one day
},
Fevm: FevmConfig{
EnableEthHashToFilecoinCidMapping: false,
EthTxHashMappingLifetimeDays: 0,
EnableEthRPC: false,
EthTxHashMappingLifetimeDays: 0,
Events: Events{
DisableRealTimeFilterAPI: false,
DisableHistoricFilterAPI: false,
FilterTTL: Duration(time.Hour * 24),
MaxFilters: 100,
MaxFilterResults: 10000,
MaxFilterHeightRange: 2880, // conservative limit of one day
},
},
}
}

View File

@ -29,56 +29,6 @@ var Doc = map[string][]DocField{
Comment: ``,
},
},
"ActorEventConfig": []DocField{
{
Name: "EnableRealTimeFilterAPI",
Type: "bool",
Comment: `EnableRealTimeFilterAPI enables APIs that can create and query filters for actor events as they are emitted.`,
},
{
Name: "EnableHistoricFilterAPI",
Type: "bool",
Comment: `EnableHistoricFilterAPI enables APIs that can create and query filters for actor events that occurred in the past.
A queryable index of events will be maintained.`,
},
{
Name: "FilterTTL",
Type: "Duration",
Comment: `FilterTTL specifies the time to live for actor event filters. Filters that haven't been accessed longer than
this time become eligible for automatic deletion.`,
},
{
Name: "MaxFilters",
Type: "int",
Comment: `MaxFilters specifies the maximum number of filters that may exist at any one time.`,
},
{
Name: "MaxFilterResults",
Type: "int",
Comment: `MaxFilterResults specifies the maximum number of results that can be accumulated by an actor event filter.`,
},
{
Name: "MaxFilterHeightRange",
Type: "uint64",
Comment: `MaxFilterHeightRange specifies the maximum range of heights that can be used in a filter (to avoid querying
the entire chain)`,
},
{
Name: "ActorEventDatabasePath",
Type: "string",
Comment: `ActorEventDatabasePath is the full path to a sqlite database that will be used to index actor events to
support the historic filter APIs. If the database does not exist it will be created. The directory containing
the database must already exist and be writeable. If a relative path is provided here, sqlite treats it as
relative to the CWD (current working directory).`,
},
},
"Backup": []DocField{
{
Name: "DisableMetadataLog",
@ -391,6 +341,59 @@ see https://lotus.filecoin.io/storage-providers/advanced-configurations/market/#
Comment: ``,
},
},
"Events": []DocField{
{
Name: "DisableRealTimeFilterAPI",
Type: "bool",
Comment: `EnableEthRPC enables APIs that
DisableRealTimeFilterAPI will disable the RealTimeFilterAPI that can create and query filters for actor events as they are emitted.
The API is enabled when EnableEthRPC is true, but can be disabled selectively with this flag.`,
},
{
Name: "DisableHistoricFilterAPI",
Type: "bool",
Comment: `DisableHistoricFilterAPI will disable the HistoricFilterAPI that can create and query filters for actor events
that occurred in the past. HistoricFilterAPI maintains a queryable index of events.
The API is enabled when EnableEthRPC is true, but can be disabled selectively with this flag.`,
},
{
Name: "FilterTTL",
Type: "Duration",
Comment: `FilterTTL specifies the time to live for actor event filters. Filters that haven't been accessed longer than
this time become eligible for automatic deletion.`,
},
{
Name: "MaxFilters",
Type: "int",
Comment: `MaxFilters specifies the maximum number of filters that may exist at any one time.`,
},
{
Name: "MaxFilterResults",
Type: "int",
Comment: `MaxFilterResults specifies the maximum number of results that can be accumulated by an actor event filter.`,
},
{
Name: "MaxFilterHeightRange",
Type: "uint64",
Comment: `MaxFilterHeightRange specifies the maximum range of heights that can be used in a filter (to avoid querying
the entire chain)`,
},
{
Name: "DatabasePath",
Type: "string",
Comment: `DatabasePath is the full path to a sqlite database that will be used to index actor events to
support the historic filter APIs. If the database does not exist it will be created. The directory containing
the database must already exist and be writeable. If a relative path is provided here, sqlite treats it as
relative to the CWD (current working directory).`,
},
},
"FeeConfig": []DocField{
{
Name: "DefaultMaxFee",
@ -401,11 +404,11 @@ see https://lotus.filecoin.io/storage-providers/advanced-configurations/market/#
},
"FevmConfig": []DocField{
{
Name: "EnableEthHashToFilecoinCidMapping",
Name: "EnableEthRPC",
Type: "bool",
Comment: `EnableEthHashToFilecoinCidMapping enables storing a mapping of eth transaction hashes to filecoin message Cids
You will not be able to look up ethereum transactions by their hash if this is disabled.`,
Comment: `EnableEthRPC enables eth_ rpc, and enables storing a mapping of eth transaction hashes to filecoin message Cids.
This will also enable the RealTimeFilterAPI and HistoricFilterAPI by default, but they can be disabled by config options above.`,
},
{
Name: "EthTxHashMappingLifetimeDays",
@ -414,6 +417,12 @@ You will not be able to look up ethereum transactions by their hash if this is d
Comment: `EthTxHashMappingLifetimeDays the transaction hash lookup database will delete mappings that have been stored for more than x days
Set to 0 to keep all mappings`,
},
{
Name: "Events",
Type: "Events",
Comment: ``,
},
},
"FullNode": []DocField{
{
@ -446,12 +455,6 @@ Set to 0 to keep all mappings`,
Comment: ``,
},
{
Name: "ActorEvent",
Type: "ActorEventConfig",
Comment: ``,
},
{
Name: "Fevm",
Type: "FevmConfig",

View File

@ -27,7 +27,6 @@ type FullNode struct {
Fees FeeConfig
Chainstore Chainstore
Cluster UserRaftConfig
ActorEvent ActorEventConfig
Fevm FevmConfig
}
@ -660,13 +659,28 @@ type UserRaftConfig struct {
Tracing bool
}
type ActorEventConfig struct {
// EnableRealTimeFilterAPI enables APIs that can create and query filters for actor events as they are emitted.
EnableRealTimeFilterAPI bool
type FevmConfig struct {
// EnableEthRPC enables eth_ rpc, and enables storing a mapping of eth transaction hashes to filecoin message Cids.
// This will also enable the RealTimeFilterAPI and HistoricFilterAPI by default, but they can be disabled by config options above.
EnableEthRPC bool
// EnableHistoricFilterAPI enables APIs that can create and query filters for actor events that occurred in the past.
// A queryable index of events will be maintained.
EnableHistoricFilterAPI bool
// EthTxHashMappingLifetimeDays the transaction hash lookup database will delete mappings that have been stored for more than x days
// Set to 0 to keep all mappings
EthTxHashMappingLifetimeDays int
Events Events
}
type Events struct {
// EnableEthRPC enables APIs that
// DisableRealTimeFilterAPI will disable the RealTimeFilterAPI that can create and query filters for actor events as they are emitted.
// The API is enabled when EnableEthRPC is true, but can be disabled selectively with this flag.
DisableRealTimeFilterAPI bool
// DisableHistoricFilterAPI will disable the HistoricFilterAPI that can create and query filters for actor events
// that occurred in the past. HistoricFilterAPI maintains a queryable index of events.
// The API is enabled when EnableEthRPC is true, but can be disabled selectively with this flag.
DisableHistoricFilterAPI bool
// FilterTTL specifies the time to live for actor event filters. Filters that haven't been accessed longer than
// this time become eligible for automatic deletion.
@ -682,23 +696,14 @@ type ActorEventConfig struct {
// the entire chain)
MaxFilterHeightRange uint64
// ActorEventDatabasePath is the full path to a sqlite database that will be used to index actor events to
// DatabasePath is the full path to a sqlite database that will be used to index actor events to
// support the historic filter APIs. If the database does not exist it will be created. The directory containing
// the database must already exist and be writeable. If a relative path is provided here, sqlite treats it as
// relative to the CWD (current working directory).
ActorEventDatabasePath string
DatabasePath string
// Others, not implemented yet:
// Set a limit on the number of active websocket subscriptions (may be zero)
// Set a timeout for subscription clients
// Set upper bound on index size
}
type FevmConfig struct {
// EnableEthHashToFilecoinCidMapping enables storing a mapping of eth transaction hashes to filecoin message Cids
// You will not be able to look up ethereum transactions by their hash if this is disabled.
EnableEthHashToFilecoinCidMapping bool
// EthTxHashMappingLifetimeDays the transaction hash lookup database will delete mappings that have been stored for more than x days
// Set to 0 to keep all mappings
EthTxHashMappingLifetimeDays int
}

View File

@ -4,106 +4,118 @@ import (
"context"
"errors"
"github.com/ipfs/go-cid"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
)
var ErrImplementMe = errors.New("Not implemented yet")
var ErrModuleDisabled = errors.New("module disabled, enable with Fevm.EnableEthRPC / LOTUS_FEVM_ENABLEETHPRC")
type EthModuleDummy struct{}
func (e *EthModuleDummy) EthGetMessageCidByTransactionHash(ctx context.Context, txHash *ethtypes.EthHash) (*cid.Cid, error) {
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) {
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthBlockNumber(ctx context.Context) (ethtypes.EthUint64, error) {
return 0, ErrImplementMe
return 0, ErrModuleDisabled
}
func (e *EthModuleDummy) EthAccounts(ctx context.Context) ([]ethtypes.EthAddress, error) {
return nil, ErrImplementMe
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetBlockTransactionCountByNumber(ctx context.Context, blkNum ethtypes.EthUint64) (ethtypes.EthUint64, error) {
return 0, ErrImplementMe
return 0, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetBlockTransactionCountByHash(ctx context.Context, blkHash ethtypes.EthHash) (ethtypes.EthUint64, error) {
return 0, ErrImplementMe
return 0, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthHash, fullTxInfo bool) (ethtypes.EthBlock, error) {
return ethtypes.EthBlock{}, ErrImplementMe
return ethtypes.EthBlock{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (ethtypes.EthBlock, error) {
return ethtypes.EthBlock{}, ErrImplementMe
return ethtypes.EthBlock{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.EthHash) (*ethtypes.EthTx, error) {
return nil, ErrImplementMe
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) {
return 0, ErrImplementMe
return 0, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*api.EthTxReceipt, error) {
return nil, ErrImplementMe
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) {
return ethtypes.EthTx{}, ErrImplementMe
return ethtypes.EthTx{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum ethtypes.EthUint64, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) {
return ethtypes.EthTx{}, ErrImplementMe
return ethtypes.EthTx{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkOpt string) (ethtypes.EthBytes, error) {
return nil, ErrImplementMe
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) {
return nil, ErrImplementMe
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) {
return ethtypes.EthBigIntZero, ErrImplementMe
return ethtypes.EthBigIntZero, ErrModuleDisabled
}
func (e *EthModuleDummy) EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint64, newestBlk string, rewardPercentiles []float64) (ethtypes.EthFeeHistory, error) {
return ethtypes.EthFeeHistory{}, ErrImplementMe
return ethtypes.EthFeeHistory{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthChainId(ctx context.Context) (ethtypes.EthUint64, error) {
return 0, ErrImplementMe
return 0, ErrModuleDisabled
}
func (e *EthModuleDummy) NetVersion(ctx context.Context) (string, error) {
return "", ErrImplementMe
return "", ErrModuleDisabled
}
func (e *EthModuleDummy) NetListening(ctx context.Context) (bool, error) {
return false, ErrImplementMe
return false, ErrModuleDisabled
}
func (e *EthModuleDummy) EthProtocolVersion(ctx context.Context) (ethtypes.EthUint64, error) {
return 0, ErrImplementMe
return 0, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) {
return ethtypes.EthBigIntZero, ErrImplementMe
return ethtypes.EthBigIntZero, ErrModuleDisabled
}
func (e *EthModuleDummy) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) {
return 0, ErrImplementMe
return 0, ErrModuleDisabled
}
func (e *EthModuleDummy) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) {
return nil, ErrImplementMe
return nil, ErrModuleDisabled
}
func (e *EthModuleDummy) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) {
return ethtypes.EthBigIntZero, ErrImplementMe
return ethtypes.EthBigIntZero, ErrModuleDisabled
}
func (e *EthModuleDummy) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) {
return ethtypes.EthHash{}, ErrImplementMe
return ethtypes.EthHash{}, ErrModuleDisabled
}
var _ EthModuleAPI = &EthModuleDummy{}

View File

@ -257,14 +257,11 @@ func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash *ethtype
return nil, nil
}
c := cid.Undef
if a.EthTxHashManager != nil {
var err error
c, err = a.EthTxHashManager.TransactionHashLookup.GetCidFromHash(*txHash)
if err != nil {
log.Debug("could not find transaction hash %s in lookup table", txHash.String())
}
c, err := a.EthTxHashManager.TransactionHashLookup.GetCidFromHash(*txHash)
if err != nil {
log.Debug("could not find transaction hash %s in lookup table", txHash.String())
}
// This isn't an eth transaction we have the mapping for, so let's look it up as a filecoin message
if c == cid.Undef {
c = txHash.ToCid()
@ -306,25 +303,22 @@ func (a *EthModule) EthGetMessageCidByTransactionHash(ctx context.Context, txHas
return nil, nil
}
c := cid.Undef
if a.EthTxHashManager != nil {
var err error
c, err = a.EthTxHashManager.TransactionHashLookup.GetCidFromHash(*txHash)
// We fall out of the first condition and continue
if errors.Is(err, ethhashlookup.ErrNotFound) {
log.Debug("could not find transaction hash %s in lookup table", txHash.String())
} else if err != nil {
return nil, xerrors.Errorf("database error: %w", err)
} else {
return &c, nil
}
c, err := a.EthTxHashManager.TransactionHashLookup.GetCidFromHash(*txHash)
// We fall out of the first condition and continue
if errors.Is(err, ethhashlookup.ErrNotFound) {
log.Debug("could not find transaction hash %s in lookup table", txHash.String())
} else if err != nil {
return nil, xerrors.Errorf("database error: %w", err)
} else {
return &c, nil
}
// This isn't an eth transaction we have the mapping for, so let's try looking it up as a filecoin message
if c == cid.Undef {
c = txHash.ToCid()
}
_, err := a.StateAPI.Chain.GetSignedMessage(ctx, c)
_, err = a.StateAPI.Chain.GetSignedMessage(ctx, c)
if err == nil {
// This is an Eth Tx, Secp message, Or BLS message in the mpool
return &c, nil
@ -369,14 +363,11 @@ func (a *EthModule) EthGetTransactionCount(ctx context.Context, sender ethtypes.
}
func (a *EthModule) EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*api.EthTxReceipt, error) {
c := cid.Undef
if a.EthTxHashManager != nil {
var err error
c, err = a.EthTxHashManager.TransactionHashLookup.GetCidFromHash(txHash)
if err != nil {
log.Debug("could not find transaction hash %s in lookup table", txHash.String())
}
c, err := a.EthTxHashManager.TransactionHashLookup.GetCidFromHash(txHash)
if err != nil {
log.Debug("could not find transaction hash %s in lookup table", txHash.String())
}
// This isn't an eth transaction we have the mapping for, so let's look it up as a filecoin message
if c == cid.Undef {
c = txHash.ToCid()

View File

@ -11,9 +11,11 @@ import (
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/messagesigner"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/lotus/chain/wallet/key"
"github.com/filecoin-project/lotus/lib/sigs"
)
@ -51,12 +53,20 @@ func (a *WalletAPI) WalletSignMessage(ctx context.Context, k address.Address, ms
return nil, xerrors.Errorf("failed to resolve ID address: %w", keyAddr)
}
keyInfo, err := a.Wallet.WalletExport(ctx, k)
if err != nil {
return nil, err
}
sb, err := messagesigner.SigningBytes(msg, key.ActSigType(keyInfo.Type))
if err != nil {
return nil, err
}
mb, err := msg.ToStorageBlock()
if err != nil {
return nil, xerrors.Errorf("serializing message: %w", err)
}
sig, err := a.Wallet.WalletSign(ctx, keyAddr, mb.Cid().Bytes(), api.MsgMeta{
sig, err := a.Wallet.WalletSign(ctx, keyAddr, sb, api.MsgMeta{
Type: api.MTChainMsg,
Extra: mb.RawData(),
})

View File

@ -2,6 +2,7 @@ package modules
import (
"context"
"path/filepath"
"time"
"github.com/multiformats/go-varint"
@ -20,6 +21,7 @@ import (
"github.com/filecoin-project/lotus/node/config"
"github.com/filecoin-project/lotus/node/impl/full"
"github.com/filecoin-project/lotus/node/modules/helpers"
"github.com/filecoin-project/lotus/node/repo"
)
type EventAPI struct {
@ -31,16 +33,16 @@ type EventAPI struct {
var _ events.EventAPI = &EventAPI{}
func EthEventAPI(cfg config.ActorEventConfig) func(helpers.MetricsCtx, fx.Lifecycle, *store.ChainStore, *stmgr.StateManager, EventAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI) (*full.EthEvent, error) {
return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventAPI, mp *messagepool.MessagePool, stateapi full.StateAPI, chainapi full.ChainAPI) (*full.EthEvent, error) {
func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *store.ChainStore, *stmgr.StateManager, EventAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI) (*full.EthEvent, error) {
return func(mctx helpers.MetricsCtx, r repo.LockedRepo, lc fx.Lifecycle, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventAPI, mp *messagepool.MessagePool, stateapi full.StateAPI, chainapi full.ChainAPI) (*full.EthEvent, error) {
ctx := helpers.LifecycleCtx(mctx, lc)
ee := &full.EthEvent{
Chain: cs,
MaxFilterHeightRange: abi.ChainEpoch(cfg.MaxFilterHeightRange),
MaxFilterHeightRange: abi.ChainEpoch(cfg.Events.MaxFilterHeightRange),
}
if !cfg.EnableRealTimeFilterAPI {
if !cfg.EnableEthRPC || cfg.Events.DisableRealTimeFilterAPI {
// all event functionality is disabled
// the historic filter API relies on the real time one
return ee, nil
@ -51,21 +53,32 @@ func EthEventAPI(cfg config.ActorEventConfig) func(helpers.MetricsCtx, fx.Lifecy
StateAPI: stateapi,
ChainAPI: chainapi,
}
ee.FilterStore = filter.NewMemFilterStore(cfg.MaxFilters)
ee.FilterStore = filter.NewMemFilterStore(cfg.Events.MaxFilters)
// Start garbage collection for filters
lc.Append(fx.Hook{
OnStart: func(context.Context) error {
go ee.GC(ctx, time.Duration(cfg.FilterTTL))
go ee.GC(ctx, time.Duration(cfg.Events.FilterTTL))
return nil
},
})
// Enable indexing of actor events
var eventIndex *filter.EventIndex
if cfg.EnableHistoricFilterAPI {
if !cfg.Events.DisableHistoricFilterAPI {
var dbPath string
if cfg.Events.DatabasePath == "" {
sqlitePath, err := r.SqlitePath()
if err != nil {
return nil, err
}
dbPath = filepath.Join(sqlitePath, "events.db")
} else {
dbPath = cfg.Events.DatabasePath
}
var err error
eventIndex, err = filter.NewEventIndex(cfg.ActorEventDatabasePath)
eventIndex, err = filter.NewEventIndex(dbPath)
if err != nil {
return nil, err
}
@ -103,13 +116,13 @@ func EthEventAPI(cfg config.ActorEventConfig) func(helpers.MetricsCtx, fx.Lifecy
return *actor.Address, true
},
MaxFilterResults: cfg.MaxFilterResults,
MaxFilterResults: cfg.Events.MaxFilterResults,
}
ee.TipSetFilterManager = &filter.TipSetFilterManager{
MaxFilterResults: cfg.MaxFilterResults,
MaxFilterResults: cfg.Events.MaxFilterResults,
}
ee.MemPoolFilterManager = &filter.MemPoolFilterManager{
MaxFilterResults: cfg.MaxFilterResults,
MaxFilterResults: cfg.Events.MaxFilterResults,
}
const ChainHeadConfidence = 1

View File

@ -19,26 +19,12 @@ import (
func EthModuleAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *store.ChainStore, *stmgr.StateManager, EventAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI, full.MpoolAPI) (*full.EthModule, error) {
return func(mctx helpers.MetricsCtx, r repo.LockedRepo, lc fx.Lifecycle, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventAPI, mp *messagepool.MessagePool, stateapi full.StateAPI, chainapi full.ChainAPI, mpoolapi full.MpoolAPI) (*full.EthModule, error) {
em := &full.EthModule{
Chain: cs,
Mpool: mp,
StateManager: sm,
ChainAPI: chainapi,
MpoolAPI: mpoolapi,
StateAPI: stateapi,
}
if !cfg.EnableEthHashToFilecoinCidMapping {
// mapping functionality disabled. Nothing to do here
return em, nil
}
dbPath, err := r.SqlitePath()
sqlitePath, err := r.SqlitePath()
if err != nil {
return nil, err
}
transactionHashLookup, err := ethhashlookup.NewTransactionHashLookup(filepath.Join(dbPath, "txhash.db"))
transactionHashLookup, err := ethhashlookup.NewTransactionHashLookup(filepath.Join(sqlitePath, "txhash.db"))
if err != nil {
return nil, err
}
@ -54,8 +40,6 @@ func EthModuleAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRep
TransactionHashLookup: transactionHashLookup,
}
em.EthTxHashManager = &ethTxHashManager
const ChainHeadConfidence = 1
ctx := helpers.LifecycleCtx(mctx, lc)
@ -80,6 +64,16 @@ func EthModuleAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRep
},
})
return em, nil
return &full.EthModule{
Chain: cs,
Mpool: mp,
StateManager: sm,
ChainAPI: chainapi,
MpoolAPI: mpoolapi,
StateAPI: stateapi,
EthTxHashManager: &ethTxHashManager,
}, nil
}
}