From c5ed5dd254fb2a26f155b73e1260bd4677264930 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Fri, 20 Jan 2023 14:01:25 +0000 Subject: [PATCH 01/39] itests: add event matrix tests for realtime eth filters and subscriptions --- chain/events/filter/event.go | 26 +- chain/events/filter/index.go | 7 - itests/eth_filter_test.go | 1423 ++++++++++++++++++++-------------- node/impl/full/eth.go | 13 +- 4 files changed, 876 insertions(+), 593 deletions(-) diff --git a/chain/events/filter/event.go b/chain/events/filter/event.go index 7d59ad8bd..d1010a262 100644 --- a/chain/events/filter/event.go +++ b/chain/events/filter/event.go @@ -20,7 +20,12 @@ import ( "github.com/filecoin-project/lotus/chain/types" ) -const indexed uint8 = 0x01 +func isIndexedValue(b uint8) bool { + // currently we mark the full entry as indexed if either the key + // or the value are indexed; in the future we will need finer-grained + // management of indices + return b&(types.EventFlagIndexedKey|types.EventFlagIndexedValue) > 0 +} type EventFilter struct { id types.FilterID @@ -209,7 +214,7 @@ func (f *EventFilter) matchKeys(ees []types.EventEntry) bool { matched := map[string]bool{} for _, ee := range ees { // Skip an entry that is not indexable - if ee.Flags&indexed != indexed { + if !isIndexedValue(ee.Flags) { continue } @@ -221,12 +226,15 @@ func (f *EventFilter) matchKeys(ees []types.EventEntry) bool { } wantlist, ok := f.keys[keyname] - if !ok { + if !ok || len(wantlist) == 0 { continue } for _, w := range wantlist { - if bytes.Equal(w, ee.Value) { + // TODO: remove this. Currently the filters use raw values but the value in the entry is cbor-encoded + // We want to make all internal values cbor-encoded as per https://github.com/filecoin-project/ref-fvm/issues/1345 + value := leftpad32(decodeLogBytes(ee.Value)) + if bytes.Equal(w, value) { matched[keyname] = true break } @@ -481,3 +489,13 @@ func (m *EventFilterManager) loadExecutedMessages(ctx context.Context, msgTs, rc return ems, nil } + +func leftpad32(orig []byte) []byte { + needed := 32 - len(orig) + if needed <= 0 { + return orig + } + ret := make([]byte, 32) + copy(ret[needed:], orig) + return ret +} diff --git a/chain/events/filter/index.go b/chain/events/filter/index.go index 1920a91fe..7fb89d1a8 100644 --- a/chain/events/filter/index.go +++ b/chain/events/filter/index.go @@ -153,13 +153,6 @@ func (ei *EventIndex) CollectEvents(ctx context.Context, te *TipSetEvents, rever return xerrors.Errorf("prepare insert entry: %w", err) } - isIndexedValue := func(b uint8) bool { - // currently we mark the full entry as indexed if either the key - // or the value are indexed; in the future we will need finer-grained - // management of indices - return b&(types.EventFlagIndexedKey|types.EventFlagIndexedValue) > 0 - } - for msgIdx, em := range ems { for evIdx, ev := range em.Events() { addr, found := addressLookups[ev.Emitter] diff --git a/itests/eth_filter_test.go b/itests/eth_filter_test.go index aba61f934..5fbe1cb6a 100644 --- a/itests/eth_filter_test.go +++ b/itests/eth_filter_test.go @@ -6,7 +6,6 @@ import ( "context" "encoding/binary" "encoding/hex" - "encoding/json" "fmt" "os" "sort" @@ -287,7 +286,7 @@ func TestEthNewBlockFilter(t *testing.T) { } } -func TestEthNewFilterCatchAll(t *testing.T) { +func TestEthNewFilterDefaultSpec(t *testing.T) { require := require.New(t) kit.QuietAllLogsExcept("events", "messagepool") @@ -367,7 +366,7 @@ func TestEthNewFilterCatchAll(t *testing.T) { AssertEthLogs(t, elogs, expected, received) } -func TestEthGetLogsAll(t *testing.T) { +func TestEthGetLogsBasic(t *testing.T) { require := require.New(t) kit.QuietAllLogsExcept("events", "messagepool") @@ -410,45 +409,7 @@ func TestEthGetLogsAll(t *testing.T) { AssertEthLogs(t, elogs, expected, received) } -func TestEthGetLogsByTopic(t *testing.T) { - require := require.New(t) - - kit.QuietAllLogsExcept("events", "messagepool") - - blockTime := 100 * time.Millisecond - - client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) - ens.InterconnectAll().BeginMining(blockTime) - - invocations := 1 - ethContractAddr, received := invokeLogFourData(t, client, invocations) - - // find log by known topic1 - var spec ethtypes.EthFilterSpec - err := json.Unmarshal([]byte(`{"fromBlock":"0x0","topics":["0x0000000000000000000000000000000000000000000000000000000000001111"]}`), &spec) - require.NoError(err) - - res, err := client.EthGetLogs(context.Background(), &spec) - require.NoError(err) - - expected := []ExpectedEthLog{ - { - Address: ethContractAddr, - Topics: []ethtypes.EthBytes{ - paddedEthBytes([]byte{0x11, 0x11}), - paddedEthBytes([]byte{0x22, 0x22}), - paddedEthBytes([]byte{0x33, 0x33}), - paddedEthBytes([]byte{0x44, 0x44}), - }, - Data: paddedEthBytes([]byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}), - }, - } - elogs, err := parseEthLogsFromFilterResult(res) - require.NoError(err) - AssertEthLogs(t, elogs, expected, received) -} - -func TestEthSubscribeLogs(t *testing.T) { +func TestEthSubscribeLogsNoTopicSpec(t *testing.T) { require := require.New(t) kit.QuietAllLogsExcept("events", "messagepool") @@ -522,539 +483,10 @@ func TestEthGetLogs(t *testing.T) { defer cancel() // Set up the test fixture with a standard list of invocations - contract1, contract2, messages := invokeEventMatrix(ctx, t, client) + contract1, contract2, invocations := prepareEventMatrixInvocations(ctx, t, client) + testCases := getCombinationFilterTestCases(contract1, contract2, "0x0") - testCases := []struct { - name string - spec *ethtypes.EthFilterSpec - expected []ExpectedEthLog - }{ - { - name: "find all EventZeroData events", - spec: newEthFilterBuilder().FromBlockEpoch(0).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventZeroData"])).Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventZeroData"], - }, - Data: nil, - }, - { - Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventZeroData"], - }, - Data: nil, - }, - }, - }, - { - name: "find all EventOneData events", - spec: newEthFilterBuilder().FromBlockEpoch(0).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventOneData"])).Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneData"], - }, - Data: packUint64Values(23), - }, - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneData"], - }, - Data: packUint64Values(44), - }, - }, - }, - { - name: "find all EventTwoData events", - spec: newEthFilterBuilder().FromBlockEpoch(0).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoData"])).Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoData"], - }, - Data: packUint64Values(555, 666), - }, - }, - }, - { - name: "find all EventThreeData events", - spec: newEthFilterBuilder().FromBlockEpoch(0).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventThreeData"])).Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeData"], - }, - Data: packUint64Values(1, 2, 3), - }, - }, - }, - { - name: "find all EventOneIndexed events", - spec: newEthFilterBuilder().FromBlockEpoch(0).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventOneIndexed"])).Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexed"], - paddedUint64(44), - }, - Data: nil, - }, - }, - }, - { - name: "find all EventTwoIndexed events", - spec: newEthFilterBuilder().FromBlockEpoch(0).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoIndexed"])).Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexed"], - paddedUint64(44), - paddedUint64(19), - }, - Data: nil, - }, - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexed"], - paddedUint64(40), - paddedUint64(20), - }, - Data: nil, - }, - }, - }, - { - name: "find all EventThreeIndexed events", - spec: newEthFilterBuilder().FromBlockEpoch(0).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventThreeIndexed"])).Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexed"], - paddedUint64(44), - paddedUint64(27), - paddedUint64(19), - }, - Data: nil, - }, - }, - }, - { - name: "find all EventOneIndexedWithData events", - spec: newEthFilterBuilder().FromBlockEpoch(0).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventOneIndexedWithData"])).Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(44), - }, - Data: paddedUint64(19), - }, - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(46), - }, - Data: paddedUint64(12), - }, - { - Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(50), - }, - Data: paddedUint64(9), - }, - }, - }, - { - name: "find all EventTwoIndexedWithData events", - spec: newEthFilterBuilder().FromBlockEpoch(0).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoIndexedWithData"])).Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(44), - paddedUint64(27), - }, - Data: paddedUint64(19), - }, - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(46), - paddedUint64(27), - }, - Data: paddedUint64(19), - }, - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(46), - paddedUint64(14), - }, - Data: paddedUint64(19), - }, - }, - }, - { - name: "find all EventThreeIndexedWithData events", - spec: newEthFilterBuilder().FromBlockEpoch(0).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventThreeIndexedWithData"])).Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexedWithData"], - paddedUint64(44), - paddedUint64(27), - paddedUint64(19), - }, - Data: paddedUint64(12), - }, - }, - }, - - { - name: "find all events from contract2", - spec: newEthFilterBuilder().FromBlockEpoch(0).AddressOneOf(contract2).Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventZeroData"], - }, - Data: nil, - }, - { - Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexed"], - paddedUint64(44), - paddedUint64(27), - paddedUint64(19), - }, - Data: nil, - }, - { - Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexed"], - paddedUint64(44), - paddedUint64(19), - }, - Data: nil, - }, - { - Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(50), - }, - Data: paddedUint64(9), - }, - }, - }, - - { - name: "find all events with topic2 of 44", - spec: newEthFilterBuilder().FromBlockEpoch(0).Topic2OneOf(paddedEthHash(paddedUint64(44))).Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexed"], - paddedUint64(44), - }, - Data: nil, - }, - { - Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexed"], - paddedUint64(44), - paddedUint64(19), - }, - Data: nil, - }, - { - Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexed"], - paddedUint64(44), - paddedUint64(27), - paddedUint64(19), - }, - Data: nil, - }, - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(44), - }, - Data: paddedUint64(19), - }, - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(44), - paddedUint64(27), - }, - Data: paddedUint64(19), - }, - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexedWithData"], - paddedUint64(44), - paddedUint64(27), - paddedUint64(19), - }, - Data: paddedUint64(12), - }, - }, - }, - - { - name: "find all events with topic2 of 44 from contract2", - spec: newEthFilterBuilder().FromBlockEpoch(0).AddressOneOf(contract2).Topic2OneOf(paddedEthHash(paddedUint64(44))).Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexed"], - paddedUint64(44), - paddedUint64(27), - paddedUint64(19), - }, - Data: nil, - }, - { - Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexed"], - paddedUint64(44), - paddedUint64(19), - }, - Data: nil, - }, - }, - }, - - { - name: "find all EventOneIndexedWithData events from contract1 or contract2", - spec: newEthFilterBuilder(). - FromBlockEpoch(0). - AddressOneOf(contract1, contract2). - Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventOneIndexedWithData"])). - Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(44), - }, - Data: paddedUint64(19), - }, - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(46), - }, - Data: paddedUint64(12), - }, - { - Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(50), - }, - Data: paddedUint64(9), - }, - }, - }, - - { - name: "find all events with topic2 of 46", - spec: newEthFilterBuilder().FromBlockEpoch(0).Topic2OneOf(paddedEthHash(paddedUint64(46))).Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(46), - }, - Data: paddedUint64(12), - }, - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(46), - paddedUint64(27), - }, - Data: paddedUint64(19), - }, - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(46), - paddedUint64(14), - }, - Data: paddedUint64(19), - }, - }, - }, - { - name: "find all events with topic2 of 50", - spec: newEthFilterBuilder().FromBlockEpoch(0).Topic2OneOf(paddedEthHash(paddedUint64(50))).Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(50), - }, - Data: paddedUint64(9), - }, - }, - }, - { - name: "find all events with topic2 of 46 or 50", - spec: newEthFilterBuilder().FromBlockEpoch(0).Topic2OneOf(paddedEthHash(paddedUint64(46)), paddedEthHash(paddedUint64(50))).Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(46), - }, - Data: paddedUint64(12), - }, - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(46), - paddedUint64(27), - }, - Data: paddedUint64(19), - }, - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(46), - paddedUint64(14), - }, - Data: paddedUint64(19), - }, - { - Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(50), - }, - Data: paddedUint64(9), - }, - }, - }, - - { - name: "find all events with topic1 of EventTwoIndexedWithData and topic3 of 27", - spec: newEthFilterBuilder(). - FromBlockEpoch(0). - Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoIndexedWithData"])). - Topic3OneOf(paddedEthHash(paddedUint64(27))). - Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(44), - paddedUint64(27), - }, - Data: paddedUint64(19), - }, - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(46), - paddedUint64(27), - }, - Data: paddedUint64(19), - }, - }, - }, - - { - name: "find all events with topic1 of EventTwoIndexedWithData or EventOneIndexed and topic2 of 44", - spec: newEthFilterBuilder(). - FromBlockEpoch(0). - Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoIndexedWithData"]), paddedEthHash(EventMatrixContract.Ev["EventOneIndexed"])). - Topic2OneOf(paddedEthHash(paddedUint64(44))). - Filter(), - - expected: []ExpectedEthLog{ - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(44), - paddedUint64(27), - }, - Data: paddedUint64(19), - }, - { - Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexed"], - paddedUint64(44), - }, - Data: nil, - }, - }, - }, - } + messages := invokeAndWaitUntilAllOnChain(t, client, invocations) for _, tc := range testCases { tc := tc // appease the lint despot @@ -1069,6 +501,184 @@ func TestEthGetLogs(t *testing.T) { } } +func TestEthGetFilterChanges(t *testing.T) { + require := require.New(t) + kit.QuietAllLogsExcept("events", "messagepool") + + blockTime := 100 * time.Millisecond + + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + // Set up the test fixture with a standard list of invocations + contract1, contract2, invocations := prepareEventMatrixInvocations(ctx, t, client) + + // Get the test cases + testCases := getCombinationFilterTestCases(contract1, contract2, "latest") + + testFilters := map[string]ethtypes.EthFilterID{} + // Create all the filters + for _, tc := range testCases { + filterID, err := client.EthNewFilter(ctx, tc.spec) + require.NoError(err) + testFilters[tc.name] = filterID + } + + // Perform all the invocations + messages := invokeAndWaitUntilAllOnChain(t, client, invocations) + + for _, tc := range testCases { + tc := tc // appease the lint despot + t.Run(tc.name, func(t *testing.T) { + filterID, ok := testFilters[tc.name] + require.True(ok) + + // Look for events that the filter has accumulated + res, err := client.EthGetFilterChanges(ctx, filterID) + require.NoError(err) + + elogs, err := parseEthLogsFromFilterResult(res) + require.NoError(err) + AssertEthLogs(t, elogs, tc.expected, messages) + }) + } +} + +func TestEthSubscribeLogs(t *testing.T) { + require := require.New(t) + kit.QuietAllLogsExcept("events", "messagepool") + + blockTime := 100 * time.Millisecond + + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + // Set up the test fixture with a standard list of invocations + contract1, contract2, invocations := prepareEventMatrixInvocations(ctx, t, client) + + // Get the test cases + testCases := getTopicFilterTestCases(contract1, contract2, "latest") + + testResponses := map[string]chan ethtypes.EthSubscriptionResponse{} + + // quit is used to signal that we're ready to start testing collected results + quit := make(chan struct{}) + + // Create all the filters + for _, tc := range testCases { + + // subscribe to topics in filter + subCh, err := client.EthSubscribe(ctx, "logs", ðtypes.EthSubscriptionParams{Topics: tc.spec.Topics}) + require.NoError(err) + + responseCh := make(chan ethtypes.EthSubscriptionResponse, len(invocations)) + testResponses[tc.name] = responseCh + + // start a goroutine to forward responses from subscription to a buffered channel with guaranteed capacity + go func(subCh <-chan ethtypes.EthSubscriptionResponse, responseCh chan<- ethtypes.EthSubscriptionResponse, quit chan struct{}) { + defer func() { + close(responseCh) + }() + for { + select { + case resp := <-subCh: + responseCh <- resp + case <-quit: + return + case <-ctx.Done(): + return + } + } + }(subCh, responseCh, quit) + + } + + // Perform all the invocations + messages := invokeAndWaitUntilAllOnChain(t, client, invocations) + + // wait a little for subscriptions to gather results and then tell all the goroutines to stop + time.Sleep(blockTime * 6) + close(quit) + + for _, tc := range testCases { + tc := tc // appease the lint despot + t.Run(tc.name, func(t *testing.T) { + responseCh, ok := testResponses[tc.name] + require.True(ok) + + var elogs []*ethtypes.EthLog + for resp := range responseCh { + rlist, ok := resp.Result.([]interface{}) + require.True(ok, "expected subscription result to be []interface{}, but was %T", resp.Result) + + for _, rentry := range rlist { + rmap, ok := rentry.(map[string]interface{}) + require.True(ok, "expected subscription result entry to be map[string]interface{}, but was %T", resp.Result) + + elog, err := ParseEthLog(rmap) + require.NoError(err) + + elogs = append(elogs, elog) + } + + } + AssertEthLogs(t, elogs, tc.expected, messages) + }) + } +} + +func TestEthGetFilterLogs(t *testing.T) { + require := require.New(t) + kit.QuietAllLogsExcept("events", "messagepool") + + blockTime := 100 * time.Millisecond + + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + // Set up the test fixture with a standard list of invocations + contract1, contract2, invocations := prepareEventMatrixInvocations(ctx, t, client) + + // Get the test cases + testCases := getCombinationFilterTestCases(contract1, contract2, "latest") + + testFilters := map[string]ethtypes.EthFilterID{} + // Create all the filters + for _, tc := range testCases { + filterID, err := client.EthNewFilter(ctx, tc.spec) + require.NoError(err) + testFilters[tc.name] = filterID + } + + // Perform all the invocations + messages := invokeAndWaitUntilAllOnChain(t, client, invocations) + + for _, tc := range testCases { + tc := tc // appease the lint despot + t.Run(tc.name, func(t *testing.T) { + filterID, ok := testFilters[tc.name] + require.True(ok) + + // Look for events that the filter has accumulated + res, err := client.EthGetFilterLogs(ctx, filterID) + require.NoError(err) + + elogs, err := parseEthLogsFromFilterResult(res) + require.NoError(err) + AssertEthLogs(t, elogs, tc.expected, messages) + }) + } +} + func TestEthGetLogsWithBlockRanges(t *testing.T) { require := require.New(t) kit.QuietAllLogsExcept("events", "messagepool") @@ -1283,6 +893,108 @@ func TestEthGetLogsWithBlockRanges(t *testing.T) { } } +func TestEthNewFilterMergesHistoricWithRealtime(t *testing.T) { + require := require.New(t) + + kit.QuietAllLogsExcept("events", "messagepool") + + blockTime := 100 * time.Millisecond + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + sender, contract := client.EVM().DeployContractFromFilename(ctx, EventMatrixContract.Filename) + + // generate some events before the creation of the filter + preInvocations := []Invocation{ + { + Sender: sender, + Target: contract, + Selector: EventMatrixContract.Fn["logEventOneData"], + Data: packUint64Values(1), + }, + { + Sender: sender, + Target: contract, + Selector: EventMatrixContract.Fn["logEventOneIndexed"], + Data: packUint64Values(2), + }, + } + + messages := invokeAndWaitUntilAllOnChain(t, client, preInvocations) + + // now install filter + spec := newEthFilterBuilder().FromBlock("earliest").Filter() + + filterID, err := client.EthNewFilter(ctx, spec) + require.NoError(err) + + // generate some events after the creation of the filter + postInvocations := []Invocation{ + { + Sender: sender, + Target: contract, + Selector: EventMatrixContract.Fn["logEventOneData"], + Data: packUint64Values(3), + }, + { + Sender: sender, + Target: contract, + Selector: EventMatrixContract.Fn["logEventOneIndexed"], + Data: packUint64Values(4), + }, + } + + postMessages := invokeAndWaitUntilAllOnChain(t, client, postInvocations) + for k, v := range postMessages { + messages[k] = v + } + + // collect filter results + res, err := client.EthGetFilterChanges(ctx, filterID) + require.NoError(err) + + ethContractAddr := getContractEthAddress(ctx, t, client, contract) + + // expect to see 2 messages from before the filter was installed and 2 after + expected := []ExpectedEthLog{ + { + Address: ethContractAddr, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneData"], + }, + Data: paddedUint64(1), + }, + { + Address: ethContractAddr, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexed"], + paddedUint64(2), + }, + }, + { + Address: ethContractAddr, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneData"], + }, + Data: paddedUint64(3), + }, + { + Address: ethContractAddr, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexed"], + paddedUint64(4), + }, + }, + } + + elogs, err := parseEthLogsFromFilterResult(res) + require.NoError(err) + AssertEthLogs(t, elogs, expected, messages) +} + // ------------------------------------------------------------------------------- // end of tests // ------------------------------------------------------------------------------- @@ -1451,7 +1163,7 @@ func invokeLogFourData(t *testing.T, client *kit.TestFullNode, iterations int) ( return ethAddr, messages } -func invokeEventMatrix(ctx context.Context, t *testing.T, client *kit.TestFullNode) (ethtypes.EthAddress, ethtypes.EthAddress, map[ethtypes.EthHash]msgInTipset) { +func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *kit.TestFullNode) (ethtypes.EthAddress, ethtypes.EthAddress, []Invocation) { sender1, contract1 := client.EVM().DeployContractFromFilename(ctx, EventMatrixContract.Filename) sender2, contract2 := client.EVM().DeployContractFromFilename(ctx, EventMatrixContract.Filename) @@ -1640,12 +1352,566 @@ func invokeEventMatrix(ctx context.Context, t *testing.T, client *kit.TestFullNo }, } - messages := invokeAndWaitUntilAllOnChain(t, client, invocations) ethAddr1 := getContractEthAddress(ctx, t, client, contract1) ethAddr2 := getContractEthAddress(ctx, t, client, contract2) + + return ethAddr1, ethAddr2, invocations +} + +func invokeEventMatrix(ctx context.Context, t *testing.T, client *kit.TestFullNode) (ethtypes.EthAddress, ethtypes.EthAddress, map[ethtypes.EthHash]msgInTipset) { + ethAddr1, ethAddr2, invocations := prepareEventMatrixInvocations(ctx, t, client) + messages := invokeAndWaitUntilAllOnChain(t, client, invocations) return ethAddr1, ethAddr2, messages } +type filterTestCase struct { + name string + spec *ethtypes.EthFilterSpec + expected []ExpectedEthLog +} + +// getTopicFilterTestCases returns filter test cases that only include topic criteria +func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock string) []filterTestCase { + return []filterTestCase{ + { + name: "find all EventZeroData events", + spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventZeroData"])).Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventZeroData"], + }, + Data: nil, + }, + { + Address: contract2, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventZeroData"], + }, + Data: nil, + }, + }, + }, + { + name: "find all EventOneData events", + spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventOneData"])).Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneData"], + }, + Data: packUint64Values(23), + }, + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneData"], + }, + Data: packUint64Values(44), + }, + }, + }, + { + name: "find all EventTwoData events", + spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoData"])).Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoData"], + }, + Data: packUint64Values(555, 666), + }, + }, + }, + { + name: "find all EventThreeData events", + spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventThreeData"])).Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventThreeData"], + }, + Data: packUint64Values(1, 2, 3), + }, + }, + }, + { + name: "find all EventOneIndexed events", + spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventOneIndexed"])).Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexed"], + paddedUint64(44), + }, + Data: nil, + }, + }, + }, + { + name: "find all EventTwoIndexed events", + spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoIndexed"])).Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract2, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoIndexed"], + paddedUint64(44), + paddedUint64(19), + }, + Data: nil, + }, + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoIndexed"], + paddedUint64(40), + paddedUint64(20), + }, + Data: nil, + }, + }, + }, + { + name: "find all EventThreeIndexed events", + spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventThreeIndexed"])).Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract2, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventThreeIndexed"], + paddedUint64(44), + paddedUint64(27), + paddedUint64(19), + }, + Data: nil, + }, + }, + }, + { + name: "find all EventOneIndexedWithData events", + spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventOneIndexedWithData"])).Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexedWithData"], + paddedUint64(44), + }, + Data: paddedUint64(19), + }, + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexedWithData"], + paddedUint64(46), + }, + Data: paddedUint64(12), + }, + { + Address: contract2, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexedWithData"], + paddedUint64(50), + }, + Data: paddedUint64(9), + }, + }, + }, + { + name: "find all EventTwoIndexedWithData events", + spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoIndexedWithData"])).Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoIndexedWithData"], + paddedUint64(44), + paddedUint64(27), + }, + Data: paddedUint64(19), + }, + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoIndexedWithData"], + paddedUint64(46), + paddedUint64(27), + }, + Data: paddedUint64(19), + }, + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoIndexedWithData"], + paddedUint64(46), + paddedUint64(14), + }, + Data: paddedUint64(19), + }, + }, + }, + { + name: "find all EventThreeIndexedWithData events", + spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventThreeIndexedWithData"])).Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventThreeIndexedWithData"], + paddedUint64(44), + paddedUint64(27), + paddedUint64(19), + }, + Data: paddedUint64(12), + }, + }, + }, + + { + name: "find all events with topic2 of 44", + spec: newEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(paddedEthHash(paddedUint64(44))).Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexed"], + paddedUint64(44), + }, + Data: nil, + }, + { + Address: contract2, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoIndexed"], + paddedUint64(44), + paddedUint64(19), + }, + Data: nil, + }, + { + Address: contract2, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventThreeIndexed"], + paddedUint64(44), + paddedUint64(27), + paddedUint64(19), + }, + Data: nil, + }, + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexedWithData"], + paddedUint64(44), + }, + Data: paddedUint64(19), + }, + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoIndexedWithData"], + paddedUint64(44), + paddedUint64(27), + }, + Data: paddedUint64(19), + }, + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventThreeIndexedWithData"], + paddedUint64(44), + paddedUint64(27), + paddedUint64(19), + }, + Data: paddedUint64(12), + }, + }, + }, + { + name: "find all events with topic2 of 46", + spec: newEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(paddedEthHash(paddedUint64(46))).Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexedWithData"], + paddedUint64(46), + }, + Data: paddedUint64(12), + }, + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoIndexedWithData"], + paddedUint64(46), + paddedUint64(27), + }, + Data: paddedUint64(19), + }, + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoIndexedWithData"], + paddedUint64(46), + paddedUint64(14), + }, + Data: paddedUint64(19), + }, + }, + }, + { + name: "find all events with topic2 of 50", + spec: newEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(paddedEthHash(paddedUint64(50))).Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract2, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexedWithData"], + paddedUint64(50), + }, + Data: paddedUint64(9), + }, + }, + }, + { + name: "find all events with topic2 of 46 or 50", + spec: newEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(paddedEthHash(paddedUint64(46)), paddedEthHash(paddedUint64(50))).Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexedWithData"], + paddedUint64(46), + }, + Data: paddedUint64(12), + }, + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoIndexedWithData"], + paddedUint64(46), + paddedUint64(27), + }, + Data: paddedUint64(19), + }, + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoIndexedWithData"], + paddedUint64(46), + paddedUint64(14), + }, + Data: paddedUint64(19), + }, + { + Address: contract2, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexedWithData"], + paddedUint64(50), + }, + Data: paddedUint64(9), + }, + }, + }, + + { + name: "find all events with topic1 of EventTwoIndexedWithData and topic3 of 27", + spec: newEthFilterBuilder(). + FromBlockEpoch(0). + Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoIndexedWithData"])). + Topic3OneOf(paddedEthHash(paddedUint64(27))). + Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoIndexedWithData"], + paddedUint64(44), + paddedUint64(27), + }, + Data: paddedUint64(19), + }, + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoIndexedWithData"], + paddedUint64(46), + paddedUint64(27), + }, + Data: paddedUint64(19), + }, + }, + }, + + { + name: "find all events with topic1 of EventTwoIndexedWithData or EventOneIndexed and topic2 of 44", + spec: newEthFilterBuilder(). + FromBlockEpoch(0). + Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoIndexedWithData"]), paddedEthHash(EventMatrixContract.Ev["EventOneIndexed"])). + Topic2OneOf(paddedEthHash(paddedUint64(44))). + Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoIndexedWithData"], + paddedUint64(44), + paddedUint64(27), + }, + Data: paddedUint64(19), + }, + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexed"], + paddedUint64(44), + }, + Data: nil, + }, + }, + }, + } +} + +// getAddressFilterTestCases returns filter test cases include address criteria +func getAddressFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock string) []filterTestCase { + return []filterTestCase{ + { + name: "find all events from contract2", + spec: newEthFilterBuilder().FromBlock(fromBlock).AddressOneOf(contract2).Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract2, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventZeroData"], + }, + Data: nil, + }, + { + Address: contract2, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventThreeIndexed"], + paddedUint64(44), + paddedUint64(27), + paddedUint64(19), + }, + Data: nil, + }, + { + Address: contract2, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoIndexed"], + paddedUint64(44), + paddedUint64(19), + }, + Data: nil, + }, + { + Address: contract2, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexedWithData"], + paddedUint64(50), + }, + Data: paddedUint64(9), + }, + }, + }, + + { + name: "find all events with topic2 of 44 from contract2", + spec: newEthFilterBuilder().FromBlock(fromBlock).AddressOneOf(contract2).Topic2OneOf(paddedEthHash(paddedUint64(44))).Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract2, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventThreeIndexed"], + paddedUint64(44), + paddedUint64(27), + paddedUint64(19), + }, + Data: nil, + }, + { + Address: contract2, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventTwoIndexed"], + paddedUint64(44), + paddedUint64(19), + }, + Data: nil, + }, + }, + }, + + { + name: "find all EventOneIndexedWithData events from contract1 or contract2", + spec: newEthFilterBuilder(). + FromBlockEpoch(0). + AddressOneOf(contract1, contract2). + Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventOneIndexedWithData"])). + Filter(), + + expected: []ExpectedEthLog{ + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexedWithData"], + paddedUint64(44), + }, + Data: paddedUint64(19), + }, + { + Address: contract1, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexedWithData"], + paddedUint64(46), + }, + Data: paddedUint64(12), + }, + { + Address: contract2, + Topics: []ethtypes.EthBytes{ + EventMatrixContract.Ev["EventOneIndexedWithData"], + paddedUint64(50), + }, + Data: paddedUint64(9), + }, + }, + }, + } +} + +func getCombinationFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock string) []filterTestCase { + topicCases := getTopicFilterTestCases(contract1, contract2, fromBlock) + addressCases := getAddressFilterTestCases(contract1, contract2, fromBlock) + + return append(topicCases, addressCases...) +} + type ExpectedEthLog struct { // Address is the address of the actor that produced the event log. Address ethtypes.EthAddress `json:"address"` @@ -1659,7 +1925,8 @@ type ExpectedEthLog struct { func AssertEthLogs(t *testing.T, actual []*ethtypes.EthLog, expected []ExpectedEthLog, messages map[ethtypes.EthHash]msgInTipset) { require := require.New(t) - // require.Equal(len(expected), len(actual), "number of results equal to expected") + + t.Logf("got %d ethlogs, wanted %d", len(actual), len(expected)) formatTopics := func(topics []ethtypes.EthBytes) string { ss := make([]string, len(topics)) @@ -1673,7 +1940,7 @@ func AssertEthLogs(t *testing.T, actual []*ethtypes.EthLog, expected []ExpectedE for _, elog := range actual { msg, exists := messages[elog.TransactionHash] - require.True(exists, "message seen on chain") + require.True(exists, "message not seen on chain") tsCid, err := msg.ts.Key().Cid() require.NoError(err) diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 7eb992a22..705301a1e 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -1016,6 +1016,8 @@ func (e *EthEvent) EthNewFilter(ctx context.Context, filterSpec *ethtypes.EthFil return ethtypes.EthFilterID{}, err } + fmt.Printf("REMOVEME: EthNewFilter.f=%+v\n", f) + return ethtypes.EthFilterID(f.ID()), nil } @@ -1140,13 +1142,16 @@ func (e *EthEvent) EthSubscribe(ctx context.Context, eventType string, params *e keys := map[string][][]byte{} if params != nil { for idx, vals := range params.Topics { + if len(vals) == 0 { + continue + } // Ethereum topics are emitted using `LOG{0..4}` opcodes resulting in topics1..4 key := fmt.Sprintf("topic%d", idx+1) - keyvals := make([][]byte, len(vals)) - for i, v := range vals { - keyvals[i] = v[:] + for _, v := range vals { + buf := make([]byte, len(v[:])) + copy(buf, v[:]) + keys[key] = append(keys[key], buf) } - keys[key] = keyvals } } From 7632f9bbba540fbc6b17962a9a8fd59d5238f49d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 17 Jan 2023 17:38:40 +0100 Subject: [PATCH 02/39] itests: Test FEVM recursive calls --- itests/contracts/RecCall.hex | 1 + itests/contracts/RecCall.sol | 16 +++++++ itests/contracts/StackFunc.hex | 1 + itests/contracts/StackFunc.sol | 12 +++++ itests/fevm_test.go | 81 +++++++++++++++++++++++++++++++++- itests/kit/evm.go | 21 ++++++--- 6 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 itests/contracts/RecCall.hex create mode 100644 itests/contracts/RecCall.sol create mode 100644 itests/contracts/StackFunc.hex create mode 100644 itests/contracts/StackFunc.sol diff --git a/itests/contracts/RecCall.hex b/itests/contracts/RecCall.hex new file mode 100644 index 000000000..b8de20213 --- /dev/null +++ b/itests/contracts/RecCall.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b5061025b806100206000396000f3fe60806040526004361061001e5760003560e01c8063cb7786d714610023575b600080fd5b61003d60048036038101906100389190610129565b61003f565b005b600083036100d15760008111156100cc573073ffffffffffffffffffffffffffffffffffffffff1663cb7786d7838460018561007b91906101ab565b6040518463ffffffff1660e01b8152600401610099939291906101ee565b600060405180830381600087803b1580156100b357600080fd5b505af11580156100c7573d6000803e3d6000fd5b505050505b6100e9565b6100e86001846100e191906101ab565b838361003f565b5b505050565b600080fd5b6000819050919050565b610106816100f3565b811461011157600080fd5b50565b600081359050610123816100fd565b92915050565b600080600060608486031215610142576101416100ee565b5b600061015086828701610114565b935050602061016186828701610114565b925050604061017286828701610114565b9150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006101b6826100f3565b91506101c1836100f3565b92508282039050818111156101d9576101d861017c565b5b92915050565b6101e8816100f3565b82525050565b600060608201905061020360008301866101df565b61021060208301856101df565b61021d60408301846101df565b94935050505056fea26469706673582212209a21ff59c642e2970917c07bf498271c2a6df8e3929677952c0c2d8031db15cc64736f6c63430008110033 \ No newline at end of file diff --git a/itests/contracts/RecCall.sol b/itests/contracts/RecCall.sol new file mode 100644 index 000000000..f89611f0e --- /dev/null +++ b/itests/contracts/RecCall.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract StackRecCall { + function exec1(uint256 n, uint256 m, uint256 r) public payable { + if(n == 0) { + if(r > 0) { + StackRecCall(address(this)).exec1(m, m, r-1); + } + + return; + } + + exec1(n-1, m, r); + } +} diff --git a/itests/contracts/StackFunc.hex b/itests/contracts/StackFunc.hex new file mode 100644 index 000000000..1a3d1202e --- /dev/null +++ b/itests/contracts/StackFunc.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50610162806100206000396000f3fe60806040526004361061001e5760003560e01c8063c38e07dd14610023575b600080fd5b61003d6004803603810190610038919061009c565b61003f565b005b600081031561005e5761005d60018261005891906100f8565b61003f565b5b50565b600080fd5b6000819050919050565b61007981610066565b811461008457600080fd5b50565b60008135905061009681610070565b92915050565b6000602082840312156100b2576100b1610061565b5b60006100c084828501610087565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061010382610066565b915061010e83610066565b9250828203905081811115610126576101256100c9565b5b9291505056fea2646970667358221220ee8f18bfd33b1e0156cfe68e9071dd32960b370c7e63ec53c62dd48e28cb5d3b64736f6c63430008110033 \ No newline at end of file diff --git a/itests/contracts/StackFunc.sol b/itests/contracts/StackFunc.sol new file mode 100644 index 000000000..7084901c6 --- /dev/null +++ b/itests/contracts/StackFunc.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract StackSelf { + function exec1(uint256 n) public payable { + if(n == 0) { + return; + } + + exec1(n-1); + } +} \ No newline at end of file diff --git a/itests/fevm_test.go b/itests/fevm_test.go index e05b0e2cc..d39b6c274 100644 --- a/itests/fevm_test.go +++ b/itests/fevm_test.go @@ -2,6 +2,7 @@ package itests import ( "context" + "encoding/binary" "encoding/hex" "testing" "time" @@ -10,6 +11,7 @@ import ( "github.com/filecoin-project/go-address" builtintypes "github.com/filecoin-project/go-state-types/builtin" + "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/go-state-types/manifest" "github.com/filecoin-project/lotus/chain/types" @@ -38,9 +40,9 @@ func inputDataFromFrom(ctx context.Context, t *testing.T, client *kit.TestFullNo func setupFEVMTest(t *testing.T) (context.Context, context.CancelFunc, *kit.TestFullNode) { kit.QuietMiningLogs() - blockTime := 100 * time.Millisecond + blockTime := 5 * time.Millisecond client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) - ens.InterconnectAll().BeginMining(blockTime) + ens.InterconnectAll().BeginMiningMustPost(blockTime) ctx, cancel := context.WithTimeout(context.Background(), time.Minute) return ctx, cancel, client } @@ -139,3 +141,78 @@ func TestEVMRpcDisable(t *testing.T) { _, err := client.EthBlockNumber(context.Background()) require.ErrorContains(t, err, "module disabled, enable with Fevm.EnableEthRPC") } + +// TestFEVMRecursiveFuncCall deploys a contract and makes a recursive function calls +func TestFEVMRecursiveFuncCall(t *testing.T) { + ctx, cancel, client := setupFEVMTest(t) + defer cancel() + + //install contract Actor + filenameActor := "contracts/StackFunc.hex" + fromAddr, actorAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor) + + testN := func(n int) func(t *testing.T) { + return func(t *testing.T) { + inputData := make([]byte, 32) + binary.BigEndian.PutUint64(inputData[24:], uint64(n)) + + result := client.EVM().InvokeContractByFuncName(ctx, fromAddr, actorAddr, "exec1(uint256)", inputData) + require.Equal(t, result, []byte{}) + } + } + + t.Run("n=0", testN(0)) + t.Run("n=1", testN(1)) + t.Run("n=20", testN(20)) + t.Run("n=200", testN(200)) + t.Run("n=293", testN(293)) +} + +// TestFEVMRecursiveActorCall deploys a contract and makes a recursive actor calls +func TestFEVMRecursiveActorCall(t *testing.T) { + ctx, cancel, client := setupFEVMTest(t) + defer cancel() + + //install contract Actor + filenameActor := "contracts/RecCall.hex" + fromAddr, actorAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor) + + testN := func(n, r int, ex exitcode.ExitCode) func(t *testing.T) { + return func(t *testing.T) { + inputData := make([]byte, 32*3) + binary.BigEndian.PutUint64(inputData[24:], uint64(n)) + binary.BigEndian.PutUint64(inputData[32+24:], uint64(n)) + binary.BigEndian.PutUint64(inputData[32+32+24:], uint64(r)) + + client.EVM().InvokeContractByFuncNameExpectExit(ctx, fromAddr, actorAddr, "exec1(uint256,uint256,uint256)", inputData, ex) + } + } + + t.Run("n=0,r=1", testN(0, 1, exitcode.Ok)) + t.Run("n=1,r=1", testN(1, 1, exitcode.Ok)) + t.Run("n=20,r=1", testN(20, 1, exitcode.Ok)) + t.Run("n=200,r=1", testN(200, 1, exitcode.Ok)) + t.Run("n=251,r=1", testN(251, 1, exitcode.Ok)) + + t.Run("n=252,r=1-fails", testN(252, 1, exitcode.ExitCode(23))) // 23 means stack overflow + + t.Run("n=0,r=10", testN(0, 10, exitcode.Ok)) + t.Run("n=1,r=10", testN(1, 10, exitcode.Ok)) + t.Run("n=20,r=10", testN(20, 10, exitcode.Ok)) + t.Run("n=200,r=10", testN(200, 10, exitcode.Ok)) + t.Run("n=251,r=10", testN(251, 10, exitcode.Ok)) + + t.Run("n=252,r=10-fails", testN(252, 10, exitcode.ExitCode(23))) + + t.Run("n=0,r=32", testN(0, 32, exitcode.Ok)) + t.Run("n=1,r=32", testN(1, 32, exitcode.Ok)) + t.Run("n=20,r=32", testN(20, 32, exitcode.Ok)) + t.Run("n=200,r=32", testN(200, 32, exitcode.Ok)) + t.Run("n=251,r=32", testN(251, 32, exitcode.Ok)) + + t.Run("n=0,r=254", testN(0, 254, exitcode.Ok)) + t.Run("n=251,r=170", testN(251, 170, exitcode.Ok)) + + t.Run("n=0,r=255-fails", testN(0, 255, exitcode.ExitCode(33))) // 33 means transaction reverted + t.Run("n=251,r=171-fails", testN(251, 171, exitcode.ExitCode(33))) +} diff --git a/itests/kit/evm.go b/itests/kit/evm.go index 46aaf52db..b3693ea45 100644 --- a/itests/kit/evm.go +++ b/itests/kit/evm.go @@ -21,8 +21,10 @@ import ( builtintypes "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/builtin/v10/eam" "github.com/filecoin-project/go-state-types/crypto" + "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" @@ -109,11 +111,12 @@ func (e *EVM) InvokeSolidity(ctx context.Context, sender address.Address, target params = buffer.Bytes() msg := &types.Message{ - To: target, - From: sender, - Value: big.Zero(), - Method: builtintypes.MethodsEVM.InvokeContract, - Params: params, + To: target, + From: sender, + Value: big.Zero(), + Method: builtintypes.MethodsEVM.InvokeContract, + GasLimit: build.BlockGasLimit, // note: we hardcode block gas limit due to slightly broken gas estimation - https://github.com/filecoin-project/lotus/issues/10041 + Params: params, } e.t.Log("sending invoke message") @@ -237,12 +240,18 @@ func (e *EVM) ComputeContractAddress(deployer ethtypes.EthAddress, nonce uint64) func (e *EVM) InvokeContractByFuncName(ctx context.Context, fromAddr address.Address, idAddr address.Address, funcSignature string, inputData []byte) []byte { entryPoint := CalcFuncSignature(funcSignature) wait := e.InvokeSolidity(ctx, fromAddr, idAddr, entryPoint, inputData) - require.True(e.t, wait.Receipt.ExitCode.IsSuccess(), "contract execution failed") + require.True(e.t, wait.Receipt.ExitCode.IsSuccess(), "contract execution failed: %d", wait.Receipt.ExitCode) result, err := cbg.ReadByteArray(bytes.NewBuffer(wait.Receipt.Return), uint64(len(wait.Receipt.Return))) require.NoError(e.t, err) return result } +func (e *EVM) InvokeContractByFuncNameExpectExit(ctx context.Context, fromAddr address.Address, idAddr address.Address, funcSignature string, inputData []byte, exit exitcode.ExitCode) { + entryPoint := CalcFuncSignature(funcSignature) + wait := e.InvokeSolidity(ctx, fromAddr, idAddr, entryPoint, inputData) + require.Equal(e.t, exit, wait.Receipt.ExitCode) +} + // function signatures are the first 4 bytes of the hash of the function name and types func CalcFuncSignature(funcName string) []byte { hasher := sha3.NewLegacyKeccak256() From 972e68a8c126aed1d9aaf48c8bc1a12917a16f78 Mon Sep 17 00:00:00 2001 From: Aayush Date: Fri, 20 Jan 2023 10:21:00 -0500 Subject: [PATCH 03/39] feat: minor improvements to Ethereum delegated siggys --- chain/types/ethtypes/eth_transactions.go | 29 ++++++++++++------------ 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/chain/types/ethtypes/eth_transactions.go b/chain/types/ethtypes/eth_transactions.go index b6ae22169..18b922234 100644 --- a/chain/types/ethtypes/eth_transactions.go +++ b/chain/types/ethtypes/eth_transactions.go @@ -12,13 +12,11 @@ import ( "github.com/filecoin-project/go-address" gocrypto "github.com/filecoin-project/go-crypto" - "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" builtintypes "github.com/filecoin-project/go-state-types/builtin" typescrypto "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/lotus/build" - "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/types" ) @@ -63,6 +61,7 @@ func EthTxArgsFromMessage(msg *types.Message) (EthTxArgs, error) { to *EthAddress params []byte paramsReader = bytes.NewReader(msg.Params) + err error ) if msg.Version != 0 { @@ -72,11 +71,10 @@ func EthTxArgsFromMessage(msg *types.Message) (EthTxArgs, error) { if msg.To == builtintypes.EthereumAddressManagerActorAddr { switch msg.Method { case builtintypes.MethodsEAM.CreateExternal: - var create abi.CborBytes - if err := create.UnmarshalCBOR(paramsReader); err != nil { - return EthTxArgs{}, err + params, err = cbg.ReadByteArray(paramsReader, uint64(len(msg.Params))) + if err != nil { + return EthTxArgs{}, xerrors.Errorf("failed to read params byte array: %w", err) } - params = create default: return EthTxArgs{}, fmt.Errorf("unsupported EAM method") } @@ -103,11 +101,6 @@ func EthTxArgsFromMessage(msg *types.Message) (EthTxArgs, error) { if err != nil { return EthTxArgs{}, xerrors.Errorf("failed to read params byte array: %w", err) } - - if len(params) == 0 { - // Otherwise, we don't get a guaranteed round-trip. - return EthTxArgs{}, xerrors.Errorf("cannot invoke contracts with empty parameters from an eth-account") - } } } @@ -115,6 +108,11 @@ func EthTxArgsFromMessage(msg *types.Message) (EthTxArgs, error) { return EthTxArgs{}, xerrors.Errorf("extra data found in params") } + if len(params) == 0 && msg.Method != builtintypes.MethodSend { + // Otherwise, we don't get a guaranteed round-trip. + return EthTxArgs{}, xerrors.Errorf("msgs with empty parameters from an eth-account must be Sends (MethodNum: %d)", msg.Method) + } + return EthTxArgs{ ChainID: build.Eip155ChainId, Nonce: int(msg.Nonce), @@ -143,12 +141,13 @@ func (tx *EthTxArgs) ToUnsignedMessage(from address.Address) (*types.Message, er if len(tx.Input) == 0 { return nil, xerrors.New("cannot call CreateExternal without params") } - inputParams := abi.CborBytes(tx.Input) - params, err = actors.SerializeParams(&inputParams) - if err != nil { - return nil, fmt.Errorf("failed to serialize Create params: %w", err) + + buf := new(bytes.Buffer) + if err = cbg.WriteByteArray(buf, tx.Input); err != nil { + return nil, xerrors.Errorf("failed to serialize Create params: %w", err) } + params = buf.Bytes() } else { to, err = tx.To.ToFilecoinAddress() if err != nil { From 640f0dc8b6f34d9b1f0d9c2680dca2b649656764 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Sun, 22 Jan 2023 16:39:40 +0000 Subject: [PATCH 04/39] Add back <> parameter for docker push This commit reverts a change with the previous fix for this flow: - https://github.com/filecoin-project/lotus/pull/10088 --- .circleci/config.yml | 2 +- .circleci/template.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 374e41899..b67a7a7d0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -473,7 +473,7 @@ jobs: - run: name: Docker push command: | - docker push filecoin/<>:<> + docker push filecoin/<>:<>-<> if [[ ! -z $CIRCLE_SHA ]]; then docker image tag filecoin/<>:<>-<> filecoin/<>:"${CIRCLE_SHA:0:7}"-<> docker push filecoin/<>:"${CIRCLE_SHA:0:7}"-<> diff --git a/.circleci/template.yml b/.circleci/template.yml index 44db05beb..d756f1136 100644 --- a/.circleci/template.yml +++ b/.circleci/template.yml @@ -473,7 +473,7 @@ jobs: - run: name: Docker push command: | - docker push filecoin/<>:<> + docker push filecoin/<>:<>-<> if [["[[ ! -z $CIRCLE_SHA ]]"]]; then docker image tag filecoin/<>:<>-<> filecoin/<>:"${CIRCLE_SHA:0:7}"-<> docker push filecoin/<>:"${CIRCLE_SHA:0:7}"-<> From 68e20cebd7ff82784f39704b3738b5494d3aa11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 19 Jan 2023 17:21:34 +0100 Subject: [PATCH 05/39] itests: Test EAM interactions from accounts --- itests/eth_account_abstraction_test.go | 50 ++++++++++++++++++++++++++ itests/kit/node_full.go | 47 ++++++++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/itests/eth_account_abstraction_test.go b/itests/eth_account_abstraction_test.go index 692e4646f..86b7e3e9b 100644 --- a/itests/eth_account_abstraction_test.go +++ b/itests/eth_account_abstraction_test.go @@ -11,6 +11,8 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" + builtin2 "github.com/filecoin-project/go-state-types/builtin" + "github.com/filecoin-project/go-state-types/builtin/v10/eam" "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/lotus/api" @@ -313,3 +315,51 @@ func TestEthAccountAbstractionFailsFromEvmActor(t *testing.T) { require.Error(t, err, "expected gas estimation to fail") require.Contains(t, err.Error(), "SysErrSenderInvalid") } + +func TestEthAccountManagerPermissions(t *testing.T) { + kit.QuietMiningLogs() + + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + ens.InterconnectAll().BeginMining(10 * time.Millisecond) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + // setup f1/f3/f4 accounts + + wsp, err := client.WalletNew(ctx, types.KTSecp256k1) + require.NoError(t, err) + + wbl, err := client.WalletNew(ctx, types.KTBLS) + require.NoError(t, err) + + wdl, err := client.WalletNew(ctx, types.KTDelegated) + require.NoError(t, err) + + def := client.DefaultKey.Address + + // send some funds + client.ExpectSend(ctx, def, wsp, types.FromFil(10), "") + client.ExpectSend(ctx, def, wbl, types.FromFil(10), "") + client.ExpectSend(ctx, def, wdl, types.FromFil(10), "") + require.NoError(t, err) + + // make sure that EAM only allows CreateExternal to be called by accounts + client.ExpectSend(ctx, wsp, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create, &eam.CreateParams{Nonce: 0})) + client.ExpectSend(ctx, wbl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create, &eam.CreateParams{Nonce: 0})) + client.ExpectSend(ctx, wdl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create, &eam.CreateParams{Nonce: 0})) + + client.ExpectSend(ctx, wsp, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create2, &eam.Create2Params{})) + client.ExpectSend(ctx, wbl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create2, &eam.Create2Params{})) + client.ExpectSend(ctx, wdl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create2, &eam.Create2Params{})) + + contractHex, err := os.ReadFile("contracts/SimpleCoin.hex") + require.NoError(t, err) + contract, err := hex.DecodeString(string(contractHex)) + require.NoError(t, err) + contractParams := abi.CborBytes(contract) + + client.ExpectSend(ctx, wsp, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "", client.MakeSendCall(builtin2.MethodsEAM.CreateExternal, &contractParams)) + client.ExpectSend(ctx, wbl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "", client.MakeSendCall(builtin2.MethodsEAM.CreateExternal, &contractParams)) + client.ExpectSend(ctx, wdl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "", client.MakeSendCall(builtin2.MethodsEAM.CreateExternal, &contractParams)) +} diff --git a/itests/kit/node_full.go b/itests/kit/node_full.go index 4546f5a03..682ae118a 100644 --- a/itests/kit/node_full.go +++ b/itests/kit/node_full.go @@ -1,6 +1,7 @@ package kit import ( + "bytes" "context" "fmt" "testing" @@ -10,9 +11,11 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-multiaddr" "github.com/stretchr/testify/require" + cbg "github.com/whyrusleeping/cbor-gen" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api/v1api" @@ -124,6 +127,50 @@ func (f *TestFullNode) AssignPrivKey(pkey *Libp2p) { f.Pkey = pkey } +type SendCall struct { + Method abi.MethodNum + Params []byte +} + +func (f *TestFullNode) MakeSendCall(m abi.MethodNum, params cbg.CBORMarshaler) SendCall { + var b bytes.Buffer + err := params.MarshalCBOR(&b) + require.NoError(f.t, err) + return SendCall{ + Method: m, + Params: b.Bytes(), + } +} + +func (f *TestFullNode) ExpectSend(ctx context.Context, from, to address.Address, value types.BigInt, errContains string, sc ...SendCall) *types.SignedMessage { + msg := &types.Message{From: from, To: to, Value: value} + + if len(sc) == 1 { + msg.Method = sc[0].Method + msg.Params = sc[0].Params + } + + _, err := f.GasEstimateMessageGas(ctx, msg, nil, types.EmptyTSK) + if errContains != "" { + require.ErrorContains(f.t, err, errContains) + return nil + } + require.NoError(f.t, err) + + if errContains == "" { + m, err := f.MpoolPushMessage(ctx, msg, nil) + require.NoError(f.t, err) + + r, err := f.StateWaitMsg(ctx, m.Cid(), 1, api.LookbackNoLimit, true) + require.NoError(f.t, err) + + require.Equal(f.t, exitcode.Ok, r.Receipt.ExitCode) + return m + } + + return nil +} + // ChainPredicate encapsulates a chain condition. type ChainPredicate func(set *types.TipSet) bool From 804bef72c9f33fe5ec3b3df5ca74f3869817e58a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 23 Jan 2023 12:48:21 +0100 Subject: [PATCH 06/39] fix: cli: Wait for all results in GetAsks --- cli/client.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cli/client.go b/cli/client.go index 1c41262a5..a8355f9a1 100644 --- a/cli/client.go +++ b/cli/client.go @@ -1517,6 +1517,8 @@ func GetAsks(ctx context.Context, api lapi.FullNode) ([]QueriedAsk, error) { } }(miner) } + + wg.Wait() }() loop: @@ -1590,6 +1592,8 @@ loop: lk.Unlock() }(miner) } + + wg.Wait() }() loop2: From ddd5ff9c42c0cd797e9ba7d1d9197d4666bcc035 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Mon, 23 Jan 2023 14:14:25 +0000 Subject: [PATCH 07/39] fix: event filters use cbor encoding internally (#10085) --- chain/events/filter/event.go | 23 ++-------- chain/events/filter/index.go | 20 +------- node/impl/full/eth.go | 88 +++++++++++++++++++++++++----------- 3 files changed, 67 insertions(+), 64 deletions(-) diff --git a/chain/events/filter/event.go b/chain/events/filter/event.go index d1010a262..dfb61f111 100644 --- a/chain/events/filter/event.go +++ b/chain/events/filter/event.go @@ -105,18 +105,18 @@ func (f *EventFilter) CollectEvents(ctx context.Context, te *TipSetEvents, rever continue } - decodedEntries := make([]types.EventEntry, len(ev.Entries)) + entries := make([]types.EventEntry, len(ev.Entries)) for i, entry := range ev.Entries { - decodedEntries[i] = types.EventEntry{ + entries[i] = types.EventEntry{ Flags: entry.Flags, Key: entry.Key, - Value: decodeLogBytes(entry.Value), + Value: entry.Value, } } // event matches filter, so record it cev := &CollectedEvent{ - Entries: decodedEntries, + Entries: entries, EmitterAddr: addr, EventIdx: evIdx, Reverted: revert, @@ -231,10 +231,7 @@ func (f *EventFilter) matchKeys(ees []types.EventEntry) bool { } for _, w := range wantlist { - // TODO: remove this. Currently the filters use raw values but the value in the entry is cbor-encoded - // We want to make all internal values cbor-encoded as per https://github.com/filecoin-project/ref-fvm/issues/1345 - value := leftpad32(decodeLogBytes(ee.Value)) - if bytes.Equal(w, value) { + if bytes.Equal(w, ee.Value) { matched[keyname] = true break } @@ -489,13 +486,3 @@ func (m *EventFilterManager) loadExecutedMessages(ctx context.Context, msgTs, rc return ems, nil } - -func leftpad32(orig []byte) []byte { - needed := 32 - len(orig) - if needed <= 0 { - return orig - } - ret := make([]byte, 32) - copy(ret[needed:], orig) - return ret -} diff --git a/chain/events/filter/index.go b/chain/events/filter/index.go index 7fb89d1a8..1b69dfd10 100644 --- a/chain/events/filter/index.go +++ b/chain/events/filter/index.go @@ -1,7 +1,6 @@ package filter import ( - "bytes" "context" "database/sql" "errors" @@ -11,7 +10,6 @@ import ( "github.com/ipfs/go-cid" _ "github.com/mattn/go-sqlite3" - cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" @@ -191,13 +189,12 @@ func (ei *EventIndex) CollectEvents(ctx context.Context, te *TipSetEvents, rever } for _, entry := range ev.Entries { - value := decodeLogBytes(entry.Value) _, err := stmtEntry.Exec( lastID, // event_id isIndexedValue(entry.Flags), // indexed []byte{entry.Flags}, // flags entry.Key, // key - value, // value + entry.Value, // value ) if err != nil { return xerrors.Errorf("exec insert entry: %w", err) @@ -213,21 +210,6 @@ func (ei *EventIndex) CollectEvents(ctx context.Context, te *TipSetEvents, rever return nil } -// decodeLogBytes decodes a CBOR-serialized array into its original form. -// -// This function swallows errors and returns the original array if it failed -// to decode. -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 -} - // PrefillFilter fills a filter's collection of events from the historic index func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter) error { clauses := []string{} diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 705301a1e..7d56ce854 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -980,17 +980,9 @@ func (e *EthEvent) installEthFilterSpec(ctx context.Context, filterSpec *ethtype addresses = append(addresses, a) } - for idx, vals := range filterSpec.Topics { - if len(vals) == 0 { - continue - } - // Ethereum topics are emitted using `LOG{0..4}` opcodes resulting in topics1..4 - key := fmt.Sprintf("topic%d", idx+1) - for _, v := range vals { - buf := make([]byte, len(v[:])) - copy(buf, v[:]) - keys[key] = append(keys[key], buf) - } + keys, err := parseEthTopics(filterSpec.Topics) + if err != nil { + return nil, err } return e.EventFilterManager.Install(ctx, minHeight, maxHeight, tipsetCid, addresses, keys) @@ -1141,17 +1133,12 @@ func (e *EthEvent) EthSubscribe(ctx context.Context, eventType string, params *e case EthSubscribeEventTypeLogs: keys := map[string][][]byte{} if params != nil { - for idx, vals := range params.Topics { - if len(vals) == 0 { - continue - } - // Ethereum topics are emitted using `LOG{0..4}` opcodes resulting in topics1..4 - key := fmt.Sprintf("topic%d", idx+1) - for _, v := range vals { - buf := make([]byte, len(v[:])) - copy(buf, v[:]) - keys[key] = append(keys[key], buf) - } + var err error + keys, err = parseEthTopics(params.Topics) + if err != nil { + // clean up any previous filters added and stop the sub + _, _ = e.EthUnsubscribe(ctx, sub.id) + return nil, err } } @@ -1238,7 +1225,10 @@ func ethFilterResultFromEvents(evs []*filter.CollectedEvent, sa StateAPI) (*etht var err error for _, entry := range ev.Entries { - value := ethtypes.EthBytes(leftpad32(entry.Value)) // value has already been cbor-decoded but see https://github.com/filecoin-project/ref-fvm/issues/1345 + value, err := cborDecodeTopicValue(entry.Value) + if err != nil { + return nil, err + } if entry.Key == ethtypes.EthTopic1 || entry.Key == ethtypes.EthTopic2 || entry.Key == ethtypes.EthTopic3 || entry.Key == ethtypes.EthTopic4 { log.Topics = append(log.Topics, value) } else { @@ -1892,10 +1882,6 @@ func EthTxHashGC(ctx context.Context, retentionDays int, manager *EthTxHashManag } } -// TODO we could also emit full EVM words from the EVM runtime, but not doing so -// makes the contract slightly cheaper (and saves storage bytes), at the expense -// of having to left pad in the API, which is a pretty acceptable tradeoff at -// face value. There may be other protocol implications to consider. func leftpad32(orig []byte) []byte { needed := 32 - len(orig) if needed <= 0 { @@ -1905,3 +1891,51 @@ func leftpad32(orig []byte) []byte { copy(ret[needed:], orig) return ret } + +func trimLeadingZeros(b []byte) []byte { + for i := range b { + if b[i] != 0 { + return b[i:] + } + } + return []byte{} +} + +func cborEncodeTopicValue(orig []byte) ([]byte, error) { + var buf bytes.Buffer + err := cbg.WriteByteArray(&buf, trimLeadingZeros(orig)) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func cborDecodeTopicValue(orig []byte) ([]byte, error) { + if len(orig) == 0 { + return orig, nil + } + decoded, err := cbg.ReadByteArray(bytes.NewReader(orig), uint64(len(orig))) + if err != nil { + return nil, err + } + return leftpad32(decoded), nil +} + +func parseEthTopics(topics ethtypes.EthTopicSpec) (map[string][][]byte, error) { + keys := map[string][][]byte{} + for idx, vals := range topics { + if len(vals) == 0 { + continue + } + // Ethereum topics are emitted using `LOG{0..4}` opcodes resulting in topics1..4 + key := fmt.Sprintf("topic%d", idx+1) + for _, v := range vals { + encodedVal, err := cborEncodeTopicValue(v[:]) + if err != nil { + return nil, xerrors.Errorf("failed to encode topic value") + } + keys[key] = append(keys[key], encodedVal) + } + } + return keys, nil +} From c6d279c853e72d7e061d02fd5707b30383acae8e Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Mon, 23 Jan 2023 14:43:36 +0000 Subject: [PATCH 08/39] Decode topic values in EthGetTransactionReceipt --- node/impl/full/eth.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 7d56ce854..1fb33c790 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -1771,7 +1771,10 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook } for _, entry := range evt.Entries { - value := ethtypes.EthBytes(leftpad32(entry.Value)) // value has already been cbor-decoded but see https://github.com/filecoin-project/ref-fvm/issues/1345 + value, err := cborDecodeTopicValue(entry.Value) + if err != nil { + return api.EthTxReceipt{}, xerrors.Errorf("failed to decode event log value: %w", err) + } if entry.Key == ethtypes.EthTopic1 || entry.Key == ethtypes.EthTopic2 || entry.Key == ethtypes.EthTopic3 || entry.Key == ethtypes.EthTopic4 { l.Topics = append(l.Topics, value) } else { From 46746f349553b57d019fbed84ea81c109992afd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 17 Jan 2023 17:38:40 +0100 Subject: [PATCH 09/39] itests: Test FEVM recursive calls --- itests/fevm_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/itests/fevm_test.go b/itests/fevm_test.go index d39b6c274..7fc04e86d 100644 --- a/itests/fevm_test.go +++ b/itests/fevm_test.go @@ -151,21 +151,21 @@ func TestFEVMRecursiveFuncCall(t *testing.T) { filenameActor := "contracts/StackFunc.hex" fromAddr, actorAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor) - testN := func(n int) func(t *testing.T) { + testN := func(n int, ex exitcode.ExitCode) func(t *testing.T) { return func(t *testing.T) { inputData := make([]byte, 32) binary.BigEndian.PutUint64(inputData[24:], uint64(n)) - result := client.EVM().InvokeContractByFuncName(ctx, fromAddr, actorAddr, "exec1(uint256)", inputData) - require.Equal(t, result, []byte{}) + client.EVM().InvokeContractByFuncNameExpectExit(ctx, fromAddr, actorAddr, "exec1(uint256)", inputData, ex) } } - t.Run("n=0", testN(0)) - t.Run("n=1", testN(1)) - t.Run("n=20", testN(20)) - t.Run("n=200", testN(200)) - t.Run("n=293", testN(293)) + t.Run("n=0", testN(0, exitcode.Ok)) + t.Run("n=1", testN(1, exitcode.Ok)) + t.Run("n=20", testN(20, exitcode.Ok)) + t.Run("n=200", testN(200, exitcode.Ok)) + t.Run("n=507", testN(507, exitcode.Ok)) + t.Run("n=508", testN(508, exitcode.ExitCode(23))) // 23 means stack overflow } // TestFEVMRecursiveActorCall deploys a contract and makes a recursive actor calls From 88647827501564f2db014269aa97c23494ad9b51 Mon Sep 17 00:00:00 2001 From: Aayush Date: Tue, 24 Jan 2023 13:16:45 -0500 Subject: [PATCH 10/39] fix: EthAPI: Drop hack in GetStorageAt --- node/impl/full/eth.go | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 7eb992a22..28f02f1a0 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -500,18 +500,8 @@ func (a *EthModule) EthGetStorageAt(ctx context.Context, ethAddr ethtypes.EthAdd return nil, fmt.Errorf("failed to construct system sender address: %w", err) } - // TODO super duper hack (raulk). The EVM runtime actor uses the U256 parameter type in - // GetStorageAtParams, which serializes as a hex-encoded string. It should serialize - // as bytes. We didn't get to fix in time for Iron, so for now we just pass - // through the hex-encoded value passed through the Eth JSON-RPC API, by remarshalling it. - // We don't fix this at origin (builtin-actors) because we are not updating the bundle - // for Iron. - tmp, err := position.MarshalJSON() - if err != nil { - panic(err) - } params, err := actors.SerializeParams(&evm.GetStorageAtParams{ - StorageKey: tmp[1 : len(tmp)-1], // TODO strip the JSON-encoding quotes -- yuck + StorageKey: position, }) if err != nil { return nil, fmt.Errorf("failed to serialize parameters: %w", err) From ee6af9de6161f83a77ddad04b52bb74ac3fd780c Mon Sep 17 00:00:00 2001 From: ychiao Date: Tue, 24 Jan 2023 22:56:04 -0500 Subject: [PATCH 11/39] Eth JSON-RPC: implement web3_clientVersion --- api/api_full.go | 3 +++ api/eth_aliases.go | 2 ++ api/mocks/mock_full.go | 15 +++++++++++++++ api/proxy_gen.go | 13 +++++++++++++ build/openrpc/full.json.gz | Bin 32943 -> 32994 bytes build/openrpc/gateway.json.gz | Bin 8494 -> 8494 bytes build/openrpc/miner.json.gz | Bin 16044 -> 16047 bytes build/openrpc/worker.json.gz | Bin 5224 -> 5222 bytes documentation/en/api-v1-unstable-methods.md | 15 +++++++++++++++ node/impl/full/dummy.go | 4 ++++ node/impl/full/eth.go | 5 +++++ 11 files changed, 57 insertions(+) diff --git a/api/api_full.go b/api/api_full.go index b17fad3b5..ec7b23170 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -836,6 +836,9 @@ type FullNode interface { // Unsubscribe from a websocket subscription EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) //perm:write + // Returns the client version + Web3ClientVersion(ctx context.Context) (string, error) //perm:read + // CreateBackup creates node backup onder the specified file name. The // method requires that the lotus daemon is running with the // LOTUS_BACKUP_BASE_PATH environment variable set to some path, and that diff --git a/api/eth_aliases.go b/api/eth_aliases.go index cf69bfff7..ccf0317d9 100644 --- a/api/eth_aliases.go +++ b/api/eth_aliases.go @@ -41,4 +41,6 @@ func CreateEthRPCAliases(as apitypes.Aliaser) { as.AliasMethod("net_version", "Filecoin.NetVersion") as.AliasMethod("net_listening", "Filecoin.NetListening") + + as.AliasMethod("web3_clientVersion", "Filecoin.Web3ClientVersion") } diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index b32fc7d8b..02ded88a5 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -4096,3 +4096,18 @@ func (mr *MockFullNodeMockRecorder) WalletVerify(arg0, arg1, arg2, arg3 interfac mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WalletVerify", reflect.TypeOf((*MockFullNode)(nil).WalletVerify), arg0, arg1, arg2, arg3) } + +// Web3ClientVersion mocks base method. +func (m *MockFullNode) Web3ClientVersion(arg0 context.Context) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Web3ClientVersion", arg0) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Web3ClientVersion indicates an expected call of Web3ClientVersion. +func (mr *MockFullNodeMockRecorder) Web3ClientVersion(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Web3ClientVersion", reflect.TypeOf((*MockFullNode)(nil).Web3ClientVersion), arg0) +} diff --git a/api/proxy_gen.go b/api/proxy_gen.go index aaa1d87c7..a7345f098 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -582,6 +582,8 @@ type FullNodeStruct struct { WalletValidateAddress func(p0 context.Context, p1 string) (address.Address, error) `perm:"read"` WalletVerify func(p0 context.Context, p1 address.Address, p2 []byte, p3 *crypto.Signature) (bool, error) `perm:"read"` + + Web3ClientVersion func(p0 context.Context) (string, error) `perm:"read"` } } @@ -3936,6 +3938,17 @@ func (s *FullNodeStub) WalletVerify(p0 context.Context, p1 address.Address, p2 [ return false, ErrNotSupported } +func (s *FullNodeStruct) Web3ClientVersion(p0 context.Context) (string, error) { + if s.Internal.Web3ClientVersion == nil { + return "", ErrNotSupported + } + return s.Internal.Web3ClientVersion(p0) +} + +func (s *FullNodeStub) Web3ClientVersion(p0 context.Context) (string, error) { + return "", ErrNotSupported +} + func (s *GatewayStruct) ChainGetBlockMessages(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) { if s.Internal.ChainGetBlockMessages == nil { return nil, ErrNotSupported diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index d7a354461cda8da023b97b3769dbcf447ce3dfde..4fc58a93a6f9db32082e1c3941745e4fd50f2902 100644 GIT binary patch delta 29653 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 iT^&nCbB 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
}C=w5%R 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 P0RR7rD4*SpoCW~^`&+i~ diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index 74a9f32217501ea2086005946499a082506f1aaf..d145c8939094c574aac4b8fbf5b83121686d0a13 100644 GIT binary patch delta 8399 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 delta 8442 zcmaKxWl-D=?B{_NclY9KDO#XFahKvyptvnwio5*K#oZl>7I$b_ytqqocZV)k7TJ6L zH+M7l>fUAYCX-~6$@i05>00STDqzJ-q`~Gc;b4}3s&)Bm@;8a8rt>FVv}N7UVAPKd zt0R60yvUe#WeO1UjJvfU+VPR+h4A5(ax1Ov&v-8Ny_fu7g2|+m`h14@Ps#85SW`hT z5OtmCF>FWC7jR)^hjF@#feob+UcBoN3qftLPDXA9lO+g9{pE$l(CpX?cn(sjSRGyw zF+c+6N&DNbz$a{ue9Z8~^pkLpj=5ZUg72p!5aZ`g3tz2{XW7+#-LQC|uX5PWYTcg< z-o1v69xMHFG;bgDQ`kd9@7=J_Qu-+jdvkXBrr6ghPJ1}sx7?iO+~O8z?Vac8Q~V<1 ziLqgOzRLkKq%+uuK!I`kkCF5umTy=nrXg*X3|{jt61_ zepmp2) z!F9T|-H&qJu=Q>14!s=sf&cY79z>Txg?3!Zhcw>X~yLLKVe zRTNtf^17YOS~Y=kiD0~7&qKGeoJx-MlXSN!(2~Z;YPN@#AmgI-TLv^&vw)>Fq#TJ{MqQJ zM&-=O-CN$Nl8yEL*WQwrgD8Jzdj#UKF0v54lF@V(CJW@>!Yc(5NgqNTxQ{mvTT5kT zuei->pD6FZa^k2Ppk~=G`LUsNbo8PqYA{$|xN@@r1YaEeExS5A+MkfI^vQJW?D_J< zxyTTq-y;D$s_O0)H(3HF$HZA`w67+ejB#<@S3|wL8~z^j-%hMNcv=7O;(I={^{L`Y zzr*dy0jN%?>)&|8I=fr+G`zlPo#FxiLZLyN8hh;gZAja=-Fv4#Br*Df0ZI@vQhFKTH$-yAv?MYpn-RM`hgi7{ zw}1~Gxz#(<<3#I;?{gr=AiI$k#y_&oX@PV*_KW-IjW|**wgB?}>k74^^oge~1W8mlZhp3d@5Rq1x7ZjWC|hl$5cPIdM+GW+-U@F_QO{bvn8WL>#@T!(`tnSI z*n_5A$$m9Ym#!a{TgSuyNZ+HhPxq~E=#&)A$82?XyD4t+Us*6b4TgAO&M&G0uU)_4 zN^@cVG-AuJ^dEENel9biBDQ4UzR#}F@G*abc)Z+98$P@f%&22pa~Hoi&J#FO<{ zd`P(ZZjj1+MTM1Urustl^wCSn9o(9bcHR8l`fy%P;hJoz1#M;G#mG<};NlOwm|9pc zd$FvrgIMG9Kh>2AzjIwU4fm_!{#Hc8Q$TBk%$!pet-7j4!aA?bjHK<9b@w;hx_#Es z%pR3h&lWUqLiK_C%irg(x>VuC*WQ0jf(j|{D^cY}tQd+b=sPLH{<ZH-iW3rf{@w1NzdqaFJ9iV5mL7jQWb6GPsRA%DR8V^JI|q= z#ukdAZN?66AkUSgvj~-KWrkRaJH`LF&~qM7=y|k+7Zc_%8=qeO2Fh9;7hqy2)tSpl zHG5WGe{X(S$49f*#2ec^U}Uc@Uzv*y{Z;sGy?Y@G2A!MU;NtK>-R}f@BlyZ`dQ*s}Q zn=;lgKh!e_Mfh7C06xIK#N^?IIJ0bZ8pM5-MIbi=Pd1U{QU;5>{jc8JwnYZ$=&A!9 z0iwl&cdj-mTmx66jEabay?qszZjrCne_U(s!6U7hCRNtr4U>AC6E;s1BuDl+`h(mS z{dctIZOK#E&e76>p17*+-xLfBRBpCcV*|z&Szb~(9ERE(DOea-z{Tz6L=mbnT@0Y@ zXMgF~jnLJp^Ft@r$-|j%>zpZBR3@K;*Y4T7Kg^#XojE92`^WP z-%ZA7C5C#}o!*V^BtXi3krD52ZZenh^l*?9ny?;NOd5x;?&0kR@ zi)DfO{-778W#M~ZOh5RP?TW6o1Qrz365k(G*zA+cS6JqiSrD0S|YM5BH#1owj ze+t-U6dP7Fy7zbF8&$^%q%3JRby{D|_?Yq%SHGgEgxqAo@FgcGsfIuRTN%4q7WMNPduQLAcXmZ)CWf3#f!j!QU(Lqn4#MZfzs|Ybrq94UH$@Qk-M23_X zM!AKYOjJ?`Fksnd<`K#Jel+eVCde+D#tFAxGC(Q;5=BHZL_(9z%7~%=khT@}%vaa> z2}G1^$bJws^_t$ ztLaD0e{BvKizHB3=Awt6n z>6tI5x&*a>DMt^SUuKXz->K8?$e5v2QDzSTdHN(xLPkY5m61_oJMX=eac^dS1-kqu zZtEd!t;fdaQ?Kru)4qDmzRJli@)olSPia9f6HkyXc*t0k13f6b^x+&OP@v_!x{42J zpl3XpP@mEuacw%NpmbruBcCjvff(2JvDihuKq{c*zPu^8R(0s4H@FKQGWZgVHiZ5N z7|_N0&Ap*G`me~ws4Fj$o9Mg2zv^%!oEKx&wC&^%5%zgAVk%U|oL<=1D(^C>{aB-+ zSDI$CgAZu`&^ezP?kUWZ&i9bn{!#WyDIb~F zyYGOUc-%fAJP!Ee0~vKrh>*2|0X)qGhm<0_16$IG1~6>ecKukEq>Ej z#YQvDC-gQ`au`LQCzY%y;isLQ6fBdkNGC`kPoZaDe=>2F?2TJrDzjCVhguMy$0wx@ z121p$Nv+;-x0M;oi`{n#{t4a{Q}FL*M#%wLjWyJu9I#aCN4g#Ab21}5qpY`v!n8X& z9y8}xnQc9N$>izK`n_-#ZVn&7d<0wBA~PBs9lD>nB-3yNU2wr zxkpy(>r-!%g`)ID6yfl_n6@~fu+64CMJLF2$!+hBYHT2t<44RQ5o6z@oxFF9G&lal zWiw2gyPN=!WQA}&h<{C~B-bb4ph_Dvj}!1yU%qI=ce@%Tv>=wH5)^C!jwLrk|5;S;?174#magyi*|Up~ zQ8N|b<&6C~e^c&HYL~DES|u+Hy!)}T@&+9}T0j?)&HKh5Xe2XYGdPfl(40x$4>i0s z{rPE=g>b9J2cDnvXs=8TJG=*M1r8IIg%hyJ87XqjoI`rW2+;&NxS8Cr zT-+9P2j;Ofwnl^Dp8hpf@e7(Jx{V$*`=dY~FL`6-e>z|#<&_22xbZhjvQ@mNT=*+12wwdq zZ8heJxcu22ly^LNs>^v*CcL6Mx#0ay9OkAklz|Vt zivek73`XSiD=PUpQQD*e^9CY#oV%Zy?E+7q9H~n@8Z(=jh@}!>-*`wCu4R+ zgX}SE0HDq3n$!-w_z>e!9Z|p*d2O@3lf%Le0co&xW>l&I*vT%LiMw)`oohnogDSG0RxM3}Iq5Bybm=Exls=vnnVz2>o z8tD)G`=gk(uf6fDxk(gD#cXR*B4kwm9iRY__Ep1oc(->~d^#wd@xi316&q+Vt~i%= zB)9L-SPfs{n!mFtY<1`yO*UZ*Z}fpX;o)dwXl33$TC?LqMQ7SHpEPGa3Dd?h=U%P_ zv!PNszjSH@hhuSkHY-BGCjC;;BaIq3`_8$$kxZ?Xv2TIO8L2c4$918RXt~$RSn0c2 zgRz%L4AKxN#gOa7>MRVqG7Tv8e4E_GjEdcM5XwLk4>xr}*=0W)8o28d?{Im|jD4I9 zk`zK|r$y$E-d{(`YU5Bt`gH0rC&~R`knT6>6%SeWs)eH?{wwIGH5Bn7m*9A&pHmDcx{2+8X?2YD z1QUM|t;D5wnO=h_aQ+4$VZG*YEw{$NM}KlkZV~Uv1j5LJss{NQ;(gWoKvgbHXuF{D z7fHO1uLaDBe{M-YceBZV=IYghjbf8khW9F5YGQao-tCn)FoN! z8?G@EQe|M)Th~_Q0%JWk?=L2aS2P=rGSE_YL+z$2N>vGxk3&_!N%ol0XV^+q--fC} zaK+xA!SHH=KyIUdaicLh|AoM|r5w)ICNEo!?B}(0eAjAad1k_)Tdh_B4LK2!yM5gM zfXr<~z52`X1=TpAkd(XBhJIdKP@BUD?L=*N3&ws#g&HGSnKwPq4%J;DZH`rTR%p0K zD@j7{Ba)VTr&9*lK|U*1m1I7d#mW+geRWAz*&h96O)nlJ9O_YB#Pdg8HbqRzlm94f z(dxm1VXVK0pPGG-5+k=@EOVt*zaQXzMZ!ndxa<#QmAK~OqbgFkWL=vZ>RH3-kGQ2i zPmq1T^;Ue>?i#yAl=>iMzozUZtW32lPx01zn^>>J1*rhYGyE;nJi>P5gao z8A&~ftU=RFto+jXGW#%FlC`v2=W7G0=sRs&2}Zsza{)h_T4U-TjW zI{E^Tp~xF2pHr#nkXm#`d|{kF!riB3Tk%VGnOwg9UF`P6!d(=MLXz`;(FAxRROV*O zR&qU%CBt)30P*R?KAEA0dAO?|qr0+tg5%n3 zrYkGsQmo5gwQk&er_hYIjj%stYyn_=)RZH?|K%0qY@)2>*>K2HG7DxX?8}TE5qBTO$&fzp$ zUv9-|_^(?opN|P~4Iu{T|1!WLl&MofUgpJuIPxoBdY|dsP8P?q;cQ|l8*{c;KFI$8 zfOivhS?63u461@eiG91e%=EO&X!E6dUf3nv%wWo4(*}t_S1XH>N2g6;$?jlz(dcfk zIu8nPb4v(iwdm4@(aUCxurUwj-&oba~nG{sg@T-zK=3JNnJSJch1P{+RKWKyg$ zQ04|r|NBnSyMxxIdd#_aIO|`bQ>H>%jxfPbf)Tra)xOn&o(DM;*C za!r<8!+aN_Jp-CsD)ObPYpMz+mmU;DrvzODhkuwyj&jjR?ooIz1D>e6LFL!A)}GQp z8}(dB!smNE{FbYthAcT11=dzmXa>+j0cz8Z^DnyVW?H zvbNa|1>j<;jz(e?(U;nw9RzN%JUdw%lHtRj%COkNbMpQe_u90(Ho4$D$_!c}1TAr|)i>!bBPN$R1s1(*3@goma=@x`nZ zpjx9+F)nRIc5w_Ey1i3&^+tic;H*(cQXlXhFnpz~kq@oh5B#y9(_rr|5{Q;*T(A`q zf-P=VV!#q$z1i4~rzX1Iaw(nilv1-PSsWE1fYR@vpup7iRpMMq=>l`WrMCV&#K*a+ z1=X~y@Ut-<{BkOOEVuP_>Lw(KO}n@iID~Q>64F4@i#R&3gTvN0@5TH-jMNd2;%Gx_7bQNPFMZ5Z~_o@I0is%-C z0Pb0KnLUOjbaIByV3g)mkk|&6>P?i3dyHq+DstW`i3!HYa=mO|#NoIIMqG!#i4&GW z7O$P<+Frf|^2WJvd{-t8Rcg@p3W1kVYZ5t4qkA?+Dm6;^3-bS-vf6WXW8C9t;~4>pxt z>2nOv5ILQv-N{c?U{l9?*5^H%jeR;e_;kfO(m;Qn;XHw?C%eD-yE}4`pY<^D&$>4b z-K%$ta|Ndb-H6NXwyRx)`JwyGhFka3p$6jDi}%0nPtQkrOXZ6W_t!xA>Lvnz3M_|r zGW8Hi8=G16dM6={qra=8zV0gAVUQ;}(T^dERV1HH#dt#2?z zg&H~W4K_v}k#t$b-v{qLloN9u5g0f_++_uXH|S3OesF)a3_Bdfvk!$E9FyZy-CU-$ zgwFo(f6+xn1+eJz`a6JR^QmoG+`Io$q6ZCV;R&+6eT|%wad=0hPmoLw#d5^r^)izS z91-1Mwjbd5B-w5bO(}`5FG8@95^6TR6x$*jf|^2~-NCgsZxcZrDLYV+?ih z_0@aP_s5iKr2Z7*=wz8=M zc4eVFvx-GoR^$X_7XEl}{^V*OrD8(ZD50XNZPPo;)bewj7E2(c)F~2~LlqOkW9&U! zF$iYN$mIt%F~T$b4tUZ#ZK#S(^Brq-{>*-i_&4e4M5XKd4yiXE8k+iWcbbNbs|40_G)-|Ryq`Kv z6tI(V;>nQogGb5~C+XjcldWBQyCs7S#^!eQn}_C(!)E}}z9&kow1&s=7A?l8OF!|0 zd$FqMmeS>3ywmktzh^QcE3|~qSGE6Kd3G{Ty+Y^NCoh4vVooDzT1NDgM6R$Xj?0Is zfD6MX+IsD+08Ga6!DxTiQ|-l4$KBQgcRJia91qhtO>m#AVR1t1QZXa&KVP5;qZ&=q z*2}bka$vF?tXV!ryqY1b-9<>uZ1DvBM}9y7#bBWx7$h}5n6(`&hOkmRe0Q@@D6ZU5 zXRbIS*%{2jVZ#sy!Onv`8jB^eKBk|lHRPY1pPyIcN>R8_53oe=2Um|W-%&{B)% z;5}3EplmB_jb!|3_+a}9L%7Z}O%SZvr**W)4oK;hB({iQyU_<>#_2W%D|rg1DZZEt z9SP()Yb1tlc|B5{PzIMrsVS&*VsvY{n@d~brMP2OSlZHrPOv~<)h7A8u~M83;3~32 zp``-HY-oG!&*X6M&%(Ug?L7Kx69FTj%jwO${3TC}ClMVXY5!D@MqTG`Ts6UO&al`#`hSyabfE$#IZ=KfmrLf0yx*NE z?OsIpYWpWU?ImOm{q&EcPHf%XLY9#^bqQ(eTh8G_UZ+)~BaE(oxxNF8n%@^vZX(i2 zsb)*+Zt>CF#N@ytpQ(iT&ck-)q^| zACs|am5a0NJLN*M*L>J|N!D2YsKK*W?TY*&h}!*H_sQqazx9vSxm}b7xWf_5${F_R YUEPR41Of#K34suJEmG*$i;VQY05$r8-2eap diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index d3750150c33b0714c410f71f2df105cf89fb9a69..b2a53e031c8e1d6baf37e6dcefd13e53135a8f2b 100644 GIT binary patch delta 14676 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%0Du3l^xJs_&k7^n&6>3tg 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 T^Z5S)00960TW`apk{Lm%0b->`z?S7hkkm2X zt7Lv9l}cq#SRQLT)Yq3^BOWeH?YlS+K?{?ahujBjpcBup&^z%uQ>CYBomif#>3vC- z{)wQ8Tu(YBOUqg_U34I2#&;1@qV6x8TDL7Xf{h&6|Akh*76nSDlwr?p^KM3c9d=`hLaS=6AVY!-y*4s5RI-yJ1TO;A%ac?DWPhtU6af=x;h*Rd zITkkHwPQlc8@`cQ1_y=U1^uk*8Y2NYZDVZ_M4%w^x6B-C=kDfUntTd<%=tT4G(Zc!L{_!7Yxw03|-;>1@t)`kj#oBT;ST2#SKIqa~ ztnvv&7l(WA)0b$5nT*I}DSbcd<2ZZwk!b@1Zbt(* zk%*oP+oOT1oPW)sWg#*Pwh1v|<@Wu&th7r=OnX6)=f&v2POwYgsLK$8U7k{mAX!Bv zZV>Ch&CC7Wn=;P*#?9BA(vp>YXfwdj&yAd;D`U4o@E`wtsGvN+Z#>&&mKxtPcyBSSrje zJ|1N%+jM3+x-JW&4*$|Fz1xZjZO)ohtp;Q2XL^D-D3seC{`C0agVdT2Ad8u35_SqW zX-3jA>~xC*zUl2yfq54HuTLw=l1l$WEaiW%KFtmC-*fcu41LL3ah+ z6?8X??!I2?m~z`0Mww1g$_%-QMd;+0R(La2W|3U*w2`=x>mhOk zEW92mp03n=EWR5UT@I$b@n|&ZpsA*GbaRM06Ac-iF&wDFsXl>yIFe-PFN9}v8rN#AK7-~LERUE?MGg8d8j z|B~5%Ps?Wim&iiUL)U(AKQcL)eyPfXUP3bC4Vp?+-+vT=Pq$J8PqoZk+d$Y; z+FUkj;P>g&LzIn_s>{+9{)vLWtMBe_N-WduL4D7UXnLb5@T2mC@&Sk>8hw~1Rm0l(8Za=Ah*iU2U$&XU%iW8-{g z+pGm}>U);2{C{m}SsCFU{=>*zcN_X)SufnUw0@p(MOIb0Z7hfk#QlxZRhjX*d`ks~AiMHfNN1v!7oIVXy_^$QnO zUy<5nu4z%~u!MGJJ zQ$z|aSgXh>nQ!GHE`KAQG)K-D0>N!_2d{PSX+`#|q%eJ%*~!MPn3ir#>-1^8X9`jnq42t6 z2gbdeZP4JeQ9rzu(|RH~t4GdV69<|mGC!>-IwvOju|0rd^Tw~>3jah@{=pNO9|!a+ z-Ut?HK~r&e@lF!QO^5)zk^y)r!`OddJ5NMN^nPhYSG18{^_J#?=4- delta 2530 zcmV<82_5$4DCj7#4FdveeUlFZJ%8(i9M^3>63d2WFczViae47n0lZVmiEUf{ym0}Y zn`9y;C}aC~$roR312O@5-G55l#*iSZW1g&s|5f2xp_FbHZpoPiIL+%4M(23_)=$&|-snS!mPFPRX^u8oZ z|3uJ4t|y(6rDd&|E;^7h4t=pCx!A6ek|3WKYivp$bsEf&r>DZspTB3|M zS^Ahn%CP6Qc{iiJ4!f~Lp;fh3kRd^aUK<(e53`da1TO+G%9B(CWPgrxC;}$X!ava^ zax84XYsZ9?H+&^_hr(zy*N|uMHQ*N--{kV^Iwd zg3Qe$L6E;`2-vnp`2H3dr11U1_Y2=&#rF@CUbgT5f(UXU4ISRU(CQzqy{ND&pIZ*^ z@}hyw?in^Y(IL0`rhkBODj#bI=~Lrkxx@ukcs>4fPy zoUYW#PiP>hJ$mMRu+pG5pcBozM+@g~`^SHr<;q?-e@_-uw3=%A6l=@XV7WxP`k+f| zvC1b9T_AdZ=smR<(f`84v>z|*D_RlCcLfwhOkbiIW-=m^rS$!*kK^p!N2U!7xE&4L zL?U`FY>x)2a(_05mW9YH*e1k;mD~68veGUgG3^CGo)@D7JHak}qb@@Xc6mxMf@Bqy zxIwG~lRJy7p^XxRoWW^qvue~xM0P}t8Vn~_lty;i_UL@M54aej_+HeWN~fcZkKMwF z?7v3lS>XHYKv`MZiFkUWt9P;#?iBjv6TP4`ZPDlf6vjsKYZ|2{T@3GYs*b8xxM3L ztZ{j&WPdDYbJ4avNeg6Rzi%d(KzH>kF>I5WGxMDu8t4&^QWQ12?T>1KyU#ZJjXTHJ zMub&uFA*k~%noD1n-ZRj3TIN_n>&XU+Km)niurDYFQC4uqN_6ANJl+uFrA>gg6;~s zE9h<(-F>~(G3B;1j53{~lo@hy!^$>tG#Xcf$_2HQ4{3U{w+C|se~8}CK0N!I{eZ5h z?Lz+^1D-RqBRP5R!c9JB(){6Hw#julZbX8nuEUKtQe1%Bx+{Q=e*tcuGBB!Ndu6x3 z42F{i4R8X(QImKL#D8t8OQUgFHk<}Bjdw6%R-cbB zs~LouHBpe}me{@^&2O9l2-5rlMn;h4wvJ4{0DZE+yWIXt`cC`!_D4$U8ZYq|>|e0| z7tH>LijvL#FOh|yhpzqLeq?eo|3q$H)*sz%y@X`O8#I-uzJDnKpKhfHo@$x7wt=vx zw7G25!0*$khbS8vW#`ZQ8iVT{LE)TC)ct3RGsxkB`I9!OGT#&4lFpZ z;J^pNw5Dal^c8~EnSUJtZ=hu)Coe=9a*epE0)o`2`>F0;ozMnZ$)2b4;oC$T-T z_Z(DmhE3ib=SWkUg8X=Ii5+GE{)OP5wvcj>>z^J!d}t;zy;Agf(N=AtwZHJzrnf%@ zn>!YpE4lZ%1CNr9al0>5({M08RA+gd-gs9z--t%LfhT93i!Oqk3v&LF$+@oYBj>zv zpOKu^Pk+q_`ReaWTA5Eb7P%R3pjbd_nT582LuV=SOXQUT@dV<%HpJ`aoD;>|`h|ig+1$PXCwanEQYGRQr}AvP!QB z2T~$G%eGUj3(BEFPaBURRP>HIQ2QNSpY%EtXzHDbsSGFm@kCV(y)s%9?zxC%jc2pb zK~AvSAJDu194vluT4p3?E(t@>qR_kd{Az-04|8oXQr@8!7#FR13YKbSNjer@oo@KM zjVG$0WQS<@FegfajY)s71YAMEgN(K5cTij%>b(_AX!o1Y?orPE=VnZJF96);E0}7e z2Az5=fX?kwQ8WaC+vW~l>)z9f>{&@+`ZBYV zja@M<-I&(t(|XSoq%cC^b;k~j#yQ)d!Dpj>cq^y%L~>S-oV$M}4m3?+<0(u%HV zBfaV^(|gaBNEvd2Em7-eM*7f*SNF9>`Tz!TEr7U7O9ZQmVAZ+^=UB;!Aqs7olWc~$ svL|X=_mEHZ!X%UT6CDAnlNA&`6zfUr_1*ga0{{U3|Kb@iE3Ga809lgbGXMYp diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 2c853754b..445b24f82 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -291,6 +291,8 @@ * [WalletSignMessage](#WalletSignMessage) * [WalletValidateAddress](#WalletValidateAddress) * [WalletVerify](#WalletVerify) +* [Web3](#Web3) + * [Web3ClientVersion](#Web3ClientVersion) ## @@ -9207,3 +9209,16 @@ Inputs: Response: `true` +## Web3 + + +### Web3ClientVersion +Returns the client version + + +Perms: read + +Inputs: `null` + +Response: `"string value"` + diff --git a/node/impl/full/dummy.go b/node/impl/full/dummy.go index aa1450212..1a0c2fda7 100644 --- a/node/impl/full/dummy.go +++ b/node/impl/full/dummy.go @@ -118,4 +118,8 @@ func (e *EthModuleDummy) EthSendRawTransaction(ctx context.Context, rawTx ethtyp return ethtypes.EthHash{}, ErrModuleDisabled } +func (a *EthModuleDummy) Web3ClientVersion(ctx context.Context) (string, error) { + return "", ErrModuleDisabled +} + var _ EthModuleAPI = &EthModuleDummy{} diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 7eb992a22..cc4bdfdde 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -64,6 +64,7 @@ type EthModuleAPI interface { EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) + Web3ClientVersion(ctx context.Context) (string, error) } type EthEventAPI interface { @@ -714,6 +715,10 @@ func (a *EthModule) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.Et return ethtypes.EthHashFromTxBytes(rawTx), nil } +func (a *EthModule) Web3ClientVersion(ctx context.Context) (string, error) { + return build.UserVersion(), nil +} + func (a *EthModule) ethCallToFilecoinMessage(ctx context.Context, tx ethtypes.EthCall) (*types.Message, error) { var from address.Address if tx.From == nil || *tx.From == (ethtypes.EthAddress{}) { From 358964d2c87e4c83588d9244600eb5f092eb2233 Mon Sep 17 00:00:00 2001 From: ychiao Date: Tue, 24 Jan 2023 22:59:46 -0500 Subject: [PATCH 12/39] fix lint --- node/impl/full/dummy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/impl/full/dummy.go b/node/impl/full/dummy.go index 1a0c2fda7..182772189 100644 --- a/node/impl/full/dummy.go +++ b/node/impl/full/dummy.go @@ -118,7 +118,7 @@ func (e *EthModuleDummy) EthSendRawTransaction(ctx context.Context, rawTx ethtyp return ethtypes.EthHash{}, ErrModuleDisabled } -func (a *EthModuleDummy) Web3ClientVersion(ctx context.Context) (string, error) { +func (e *EthModuleDummy) Web3ClientVersion(ctx context.Context) (string, error) { return "", ErrModuleDisabled } From b38d203e8316d3cbacbefabc9c1dad246304bfcf Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Wed, 25 Jan 2023 09:44:52 +0000 Subject: [PATCH 13/39] Remove debug logging --- node/impl/full/eth.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 1fb33c790..cbfde1969 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -1007,9 +1007,6 @@ func (e *EthEvent) EthNewFilter(ctx context.Context, filterSpec *ethtypes.EthFil return ethtypes.EthFilterID{}, err } - - fmt.Printf("REMOVEME: EthNewFilter.f=%+v\n", f) - return ethtypes.EthFilterID(f.ID()), nil } From 7aed7d3fd0ec20f21c46e684e5c93952f9202774 Mon Sep 17 00:00:00 2001 From: Aayush Date: Tue, 24 Jan 2023 14:04:28 -0500 Subject: [PATCH 14/39] fix: EthAPI: Handle EthCall return correctly --- node/impl/full/eth.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 28f02f1a0..49a5387af 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -817,8 +817,9 @@ func (a *EthModule) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (et func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) { msg, err := a.ethCallToFilecoinMessage(ctx, tx) if err != nil { - return nil, err + return nil, xerrors.Errorf("failed to convert ethcall to filecoin message: %w", err) } + ts, err := a.parseBlkParam(ctx, blkParam) if err != nil { return nil, xerrors.Errorf("cannot parse block param: %s", blkParam) @@ -826,11 +827,17 @@ 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, err + return nil, xerrors.Errorf("failed to apply message: %w", err) } - if len(invokeResult.MsgRct.Return) > 0 { + + if msg.To == builtintypes.EthereumAddressManagerActorAddr { + // As far as I can tell, the Eth API always returns empty on contract deployment + return ethtypes.EthBytes{}, nil + + } else if len(invokeResult.MsgRct.Return) > 0 { return cbg.ReadByteArray(bytes.NewReader(invokeResult.MsgRct.Return), uint64(len(invokeResult.MsgRct.Return))) } + return ethtypes.EthBytes{}, nil } From 8842b21ec95b73cc6de85f0fa82ae3cbf60ddd7c Mon Sep 17 00:00:00 2001 From: Aayush Date: Wed, 25 Jan 2023 12:35:18 -0500 Subject: [PATCH 15/39] feat: vm: Assert empty object CID when dumping state --- chain/vm/invoker.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/chain/vm/invoker.go b/chain/vm/invoker.go index 47bd2e326..2dd1f37e6 100644 --- a/chain/vm/invoker.go +++ b/chain/vm/invoker.go @@ -291,7 +291,10 @@ func DumpActorState(i *ActorRegistry, act *types.Actor, b []byte) (interface{}, um := actInfo.vmActor.State() if um == nil { - // TODO::FVM @arajasek I would like to assert that we have the empty object here + if act.Code != EmptyObjectCid { + return nil, xerrors.Errorf("actor with code %s should only have empty object (%s) as its Head, instead has %s", act.Code, EmptyObjectCid, act.Head) + } + return nil, nil } if err := um.UnmarshalCBOR(bytes.NewReader(b)); err != nil { From 038404ef3190370c525c12c058f5c9f9f742fe2b Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 26 Jan 2023 01:24:44 -0500 Subject: [PATCH 16/39] Update codeql to v2 --- .github/workflows/codeql-analysis.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 65d5fd93a..6b27bf6f8 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -16,7 +16,9 @@ on: branches: [ master ] pull_request: # The branches below must be a subset of the branches above - branches: [ master ] + branches: + - master + - 'release/*' jobs: analyze: @@ -33,15 +35,15 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - - uses: actions/setup-go@v1 + - uses: actions/setup-go@v3 with: go-version: '1.18.8' # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -52,7 +54,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -66,4 +68,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 From 36b4c8a9e90f7f623dbbe9883275492d2798e211 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 26 Jan 2023 02:20:23 -0500 Subject: [PATCH 17/39] Enable code ql for release branches --- .github/workflows/codeql-analysis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6b27bf6f8..0cba5457e 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,7 +13,9 @@ name: "CodeQL" on: push: - branches: [ master ] + branches: + - master + - 'release/*' pull_request: # The branches below must be a subset of the branches above branches: @@ -45,7 +47,7 @@ jobs: - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: - languages: ${{ matrix.language }} + languages: go # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. From 8f6c5693913501750410d2dbcf91fde8a675e44f Mon Sep 17 00:00:00 2001 From: Phi Date: Thu, 26 Jan 2023 15:39:51 +0100 Subject: [PATCH 18/39] Add actors to circ-supply - Consider funds to EAM as locked - Add evm, placeholder and ethaccount to circ supply --- chain/actors/builtin/builtin.go | 1 + chain/stmgr/supply.go | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/chain/actors/builtin/builtin.go b/chain/actors/builtin/builtin.go index 116ed35d0..b5e6b9789 100644 --- a/chain/actors/builtin/builtin.go +++ b/chain/actors/builtin/builtin.go @@ -26,6 +26,7 @@ import ( var SystemActorAddr = builtin.SystemActorAddr var BurntFundsActorAddr = builtin.BurntFundsActorAddr var CronActorAddr = builtin.CronActorAddr +var EthereumAddressManager = builtin.EthereumAddressManagerActorAddr var SaftAddress = makeAddress("t0122") var ReserveAddress = makeAddress("t090") var RootVerifierAddress = makeAddress("t080") diff --git a/chain/stmgr/supply.go b/chain/stmgr/supply.go index 89708097e..64fcddd2c 100644 --- a/chain/stmgr/supply.go +++ b/chain/stmgr/supply.go @@ -403,7 +403,8 @@ func (sm *StateManager) GetCirculatingSupply(ctx context.Context, height abi.Cha a == builtin.CronActorAddr || a == builtin.BurntFundsActorAddr || a == builtin.SaftAddress || - a == builtin.ReserveAddress: + a == builtin.ReserveAddress || + a == builtin.EthereumAddressManager: unCirc = big.Add(unCirc, actor.Balance) @@ -421,7 +422,12 @@ func (sm *StateManager) GetCirculatingSupply(ctx context.Context, height abi.Cha circ = big.Add(circ, big.Sub(actor.Balance, lb)) unCirc = big.Add(unCirc, lb) - case builtin.IsAccountActor(actor.Code) || builtin.IsPaymentChannelActor(actor.Code): + case builtin.IsAccountActor(actor.Code) || + builtin.IsPaymentChannelActor(actor.Code) || + builtin.IsEthAccountActor(actor.Code) || + builtin.IsEvmActor(actor.Code) || + builtin.IsPlaceholderActor(actor.Code): + circ = big.Add(circ, actor.Balance) case builtin.IsStorageMinerActor(actor.Code): From 35ddf9e8f0faa9cc8ecabff83bff334090b71e45 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Thu, 12 Jan 2023 21:04:55 -0500 Subject: [PATCH 19/39] devnets starts with nv17 --- build/params_2k.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/params_2k.go b/build/params_2k.go index 081007dd1..466ad0bee 100644 --- a/build/params_2k.go +++ b/build/params_2k.go @@ -59,7 +59,7 @@ var UpgradeSkyrHeight = abi.ChainEpoch(-19) var UpgradeSharkHeight = abi.ChainEpoch(-20) -var UpgradeHyggeHeight = abi.ChainEpoch(-21) +var UpgradeHyggeHeight = abi.ChainEpoch(30) var DrandSchedule = map[abi.ChainEpoch]DrandEnum{ 0: DrandMainnet, From 4fb9e2fd6a6b123fd55518afa10cf17e4dca228f Mon Sep 17 00:00:00 2001 From: Phi Date: Thu, 26 Jan 2023 16:26:39 +0100 Subject: [PATCH 20/39] make jen - Add builtin.EthereumAddressManagerActorAddr to builtin.go.template and make jen - Rename to EthereumAddressManagerActorAddr to match pattern of other actors (CronActorAddr/etc) --- chain/actors/builtin/builtin.go | 2 +- chain/actors/builtin/builtin.go.template | 1 + chain/stmgr/supply.go | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/chain/actors/builtin/builtin.go b/chain/actors/builtin/builtin.go index b5e6b9789..414a11e72 100644 --- a/chain/actors/builtin/builtin.go +++ b/chain/actors/builtin/builtin.go @@ -26,7 +26,7 @@ import ( var SystemActorAddr = builtin.SystemActorAddr var BurntFundsActorAddr = builtin.BurntFundsActorAddr var CronActorAddr = builtin.CronActorAddr -var EthereumAddressManager = builtin.EthereumAddressManagerActorAddr +var EthereumAddressManagerActorAddr = builtin.EthereumAddressManagerActorAddr var SaftAddress = makeAddress("t0122") var ReserveAddress = makeAddress("t090") var RootVerifierAddress = makeAddress("t080") diff --git a/chain/actors/builtin/builtin.go.template b/chain/actors/builtin/builtin.go.template index 977217b1a..3b737c47e 100644 --- a/chain/actors/builtin/builtin.go.template +++ b/chain/actors/builtin/builtin.go.template @@ -26,6 +26,7 @@ import ( var SystemActorAddr = builtin.SystemActorAddr var BurntFundsActorAddr = builtin.BurntFundsActorAddr var CronActorAddr = builtin.CronActorAddr +var EthereumAddressManagerActorAddr = builtin.EthereumAddressManagerActorAddr var SaftAddress = makeAddress("t0122") var ReserveAddress = makeAddress("t090") var RootVerifierAddress = makeAddress("t080") diff --git a/chain/stmgr/supply.go b/chain/stmgr/supply.go index 64fcddd2c..a48ff36c7 100644 --- a/chain/stmgr/supply.go +++ b/chain/stmgr/supply.go @@ -404,7 +404,7 @@ func (sm *StateManager) GetCirculatingSupply(ctx context.Context, height abi.Cha a == builtin.BurntFundsActorAddr || a == builtin.SaftAddress || a == builtin.ReserveAddress || - a == builtin.EthereumAddressManager: + a == builtin.EthereumAddressManagerActorAddr: unCirc = big.Add(unCirc, actor.Balance) From 19e188da9fa853f171d4b36c052d54bc84a8ddd2 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Thu, 26 Jan 2023 10:59:00 +0000 Subject: [PATCH 21/39] init git submodules in build-docker CI --- .circleci/config.yml | 4 ++++ .circleci/template.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index b67a7a7d0..0329f91d8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -426,6 +426,10 @@ jobs: steps: - setup_remote_docker - checkout + - git_fetch_all_tags + - run: git submodule sync + - run: git submodule update --init + - docker/check: docker-username: DOCKERHUB_USERNAME docker-password: DOCKERHUB_PASSWORD diff --git a/.circleci/template.yml b/.circleci/template.yml index d756f1136..7bef6ae41 100644 --- a/.circleci/template.yml +++ b/.circleci/template.yml @@ -426,6 +426,10 @@ jobs: steps: - setup_remote_docker - checkout + - git_fetch_all_tags + - run: git submodule sync + - run: git submodule update --init + - docker/check: docker-username: DOCKERHUB_USERNAME docker-password: DOCKERHUB_PASSWORD From 8c58ecc897ad8c5d216cfd385119e7c7adace796 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Thu, 26 Jan 2023 12:25:20 +0000 Subject: [PATCH 22/39] Remove old Snapcraft and AppDir folders --- .gitignore | 3 -- AppDir/usr/share/icons/icon.svg | 1 - AppImageBuilder.yml | 71 ------------------------ snap/local/icon.svg | 1 - snap/snapcraft.yaml | 96 --------------------------------- 5 files changed, 172 deletions(-) delete mode 100644 AppDir/usr/share/icons/icon.svg delete mode 100644 AppImageBuilder.yml delete mode 100644 snap/local/icon.svg delete mode 100644 snap/snapcraft.yaml diff --git a/.gitignore b/.gitignore index 5ff2726f2..4221c67b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ -/AppDir -/appimage-builder-cache -*.AppImage /lotus /lotus-miner /lotus-worker diff --git a/AppDir/usr/share/icons/icon.svg b/AppDir/usr/share/icons/icon.svg deleted file mode 100644 index da992296a..000000000 --- a/AppDir/usr/share/icons/icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/AppImageBuilder.yml b/AppImageBuilder.yml deleted file mode 100644 index 4f1f56bbb..000000000 --- a/AppImageBuilder.yml +++ /dev/null @@ -1,71 +0,0 @@ -version: 1 -AppDir: - path: ./AppDir - app_info: - id: io.filecoin.lotus - name: Lotus - icon: icon - version: latest - exec: usr/bin/lotus - exec_args: $@ - apt: - arch: amd64 - allow_unauthenticated: true - sources: - - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal main restricted - - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates main restricted - - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal universe - - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates universe - - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal multiverse - - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates multiverse - - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-backports main restricted - universe multiverse - - sourceline: deb http://security.ubuntu.com/ubuntu focal-security main restricted - - sourceline: deb http://security.ubuntu.com/ubuntu focal-security universe - - sourceline: deb http://security.ubuntu.com/ubuntu focal-security multiverse - - sourceline: deb https://cli-assets.heroku.com/apt ./ - - sourceline: deb http://ppa.launchpad.net/openjdk-r/ppa/ubuntu focal main - - sourceline: deb http://ppa.launchpad.net/git-core/ppa/ubuntu focal main - - sourceline: deb http://archive.canonical.com/ubuntu focal partner - include: - - ocl-icd-libopencl1 - - libhwloc15 - exclude: [] - files: - include: - - /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 - - /usr/lib/x86_64-linux-gnu/libpthread-2.31.so - - /usr/lib/x86_64-linux-gnu/libm-2.31.so - - /usr/lib/x86_64-linux-gnu/libdl-2.31.so - - /usr/lib/x86_64-linux-gnu/libc-2.31.so - - /usr/lib/x86_64-linux-gnu/libudev.so.1.6.17 - exclude: - - usr/share/man - - usr/share/doc/*/README.* - - usr/share/doc/*/changelog.* - - usr/share/doc/*/NEWS.* - - usr/share/doc/*/TODO.* - test: - fedora: - image: appimagecrafters/tests-env:fedora-30 - command: ./AppRun - use_host_x: false - debian: - image: appimagecrafters/tests-env:debian-stable - command: ./AppRun - use_host_x: false - arch: - image: appimagecrafters/tests-env:archlinux-latest - command: ./AppRun - use_host_x: false - centos: - image: appimagecrafters/tests-env:centos-7 - command: ./AppRun - use_host_x: false - ubuntu: - image: appimagecrafters/tests-env:ubuntu-xenial - command: ./AppRun - use_host_x: false -AppImage: - arch: x86_64 - diff --git a/snap/local/icon.svg b/snap/local/icon.svg deleted file mode 100644 index da992296a..000000000 --- a/snap/local/icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml deleted file mode 100644 index 8c7323a2b..000000000 --- a/snap/snapcraft.yaml +++ /dev/null @@ -1,96 +0,0 @@ -name: lotus-filecoin -base: core20 -version: latest -summary: filecoin daemon/client -icon: snap/local/icon.svg -description: | - Filecoin is a peer-to-peer network that stores files on the internet - with built-in economic incentives to ensure files are stored reliably over time - - For documentation and additional information, please see the following resources - - https://filecoin.io - - https://fil.org - - https://lotus.filecoin.io - - https://github.com/filecoin-project/lotus - -confinement: strict - -parts: - lotus: - plugin: make - source: ./ - build-snaps: - - go - - rustup - build-packages: - - git - - jq - - libhwloc-dev - - ocl-icd-opencl-dev - - pkg-config - stage-packages: - - libhwloc15 - - ocl-icd-libopencl1 - override-build: | - LDFLAGS="" make lotus lotus-miner lotus-worker - cp lotus lotus-miner lotus-worker $SNAPCRAFT_PART_INSTALL - cp scripts/snap-lotus-entrypoint.sh $SNAPCRAFT_PART_INSTALL - -layout: - /var/lib/lotus: - symlink: $SNAP_COMMON/lotus - /var/lib/lotus-miner: - symlink: $SNAP_COMMON/lotus-miner - /var/lib/lotus-worker: - symlink: $SNAP_COMMON/lotus-worker - -apps: - lotus: - command: lotus - plugs: - - network - - network-bind - - home - environment: - FIL_PROOFS_PARAMETER_CACHE: $SNAP_USER_COMMON/filecoin-proof-parameters - LOTUS_PATH: $SNAP_COMMON/lotus - LOTUS_MINER_PATH: $SNAP_COMMON/lotus-miner - LOTUS_WORKER_PATH: $SNAP_COMMON/lotus-worker - lotus-miner: - command: lotus-miner - plugs: - - network - - network-bind - - opengl - environment: - FIL_PROOFS_PARAMETER_CACHE: $SNAP_USER_COMMON/filecoin-proof-parameters - LOTUS_PATH: $SNAP_COMMON/lotus - LOTUS_MINER_PATH: $SNAP_COMMON/lotus-miner - LOTUS_WORKER_PATH: $SNAP_COMMON/lotus-worker - lotus-worker: - command: lotus-worker - plugs: - - network - - network-bind - - opengl - environment: - FIL_PROOFS_PARAMETER_CACHE: $SNAP_USER_COMMON/filecoin-proof-parameters - LOTUS_PATH: $SNAP_COMMON/lotus - LOTUS_MINER_PATH: $SNAP_COMMON/lotus-miner - LOTUS_WORKER_PATH: $SNAP_COMMON/lotus-worker - lotus-daemon: - command: snap-lotus-entrypoint.sh - daemon: simple - install-mode: disable - plugs: - - network - - network-bind - environment: - FIL_PROOFS_PARAMETER_CACHE: $SNAP_COMMON/filecoin-proof-parameters - LOTUS_PATH: $SNAP_COMMON/lotus - LOTUS_MINER_PATH: $SNAP_COMMON/lotus-miner - LOTUS_WORKER_PATH: $SNAP_COMMON/lotus-worker From e55d6c8d652fa3ce932cdbf1940bac5d3c921f35 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Thu, 26 Jan 2023 13:06:23 +0000 Subject: [PATCH 23/39] Always include checked in files in docker context The .dockerignore file is symlinked to the .gitignore file, and checked in files should not be removed from our docker context otherwise they result in dirty git state when we build our images. --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 4221c67b7..2e9dcd0ff 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,8 @@ build/builtin-actors/v* build/builtin-actors/*.car dist/ + + +# The following files are checked into git and result +# in dirty git state if removed from the docker context +!extern/filecoin-ffi/rust/filecoin.pc From a0994ac4b4a6416302d7b82a3a1867301e21fcff Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Thu, 26 Jan 2023 13:25:47 +0000 Subject: [PATCH 24/39] Check git state in Dockerfile to catch .dockerignore bugs --- Dockerfile | 2 ++ scripts/docker-git-state-check.sh | 12 ++++++++++++ 2 files changed, 14 insertions(+) create mode 100755 scripts/docker-git-state-check.sh diff --git a/Dockerfile b/Dockerfile index 5c147e930..81089517b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,6 +33,8 @@ RUN set -eux; \ COPY ./ /opt/filecoin WORKDIR /opt/filecoin +RUN scripts/docker-git-state-check.sh + ### make configurable filecoin-ffi build ARG FFI_BUILD_FROM_SOURCE=0 ENV FFI_BUILD_FROM_SOURCE=${FFI_BUILD_FROM_SOURCE} diff --git a/scripts/docker-git-state-check.sh b/scripts/docker-git-state-check.sh new file mode 100755 index 000000000..6075ebf84 --- /dev/null +++ b/scripts/docker-git-state-check.sh @@ -0,0 +1,12 @@ +set -e + +if [ -z "$(git status --porcelain)" ]; then + echo "PASSED: Working directory clean" +else + echo "FAILED: Working directory not clean." + echo "This is likely because the .dockerignore file has removed something checked into git." + echo "Add the missing files listed below to the .dockerignore to fix this issue:" + echo "$(git status)" + exit 1 +fi + From c93717bf7799ab2055dc292402a549031f756460 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 26 Jan 2023 09:22:06 -0800 Subject: [PATCH 25/39] fix: don't replace the method in EthSendRawTransaction (#10129) This will just cause signature validation to fail. --- node/impl/full/eth.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index a66487233..14c69b2e1 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -690,13 +690,6 @@ func (a *EthModule) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.Et return ethtypes.EmptyEthHash, err } - _, err = a.StateAPI.StateGetActor(ctx, smsg.Message.To, types.EmptyTSK) - if err != nil { - // if actor does not exist on chain yet, set the method to 0 because - // placeholders only implement method 0 - smsg.Message.Method = builtinactors.MethodSend - } - _, err = a.MpoolAPI.MpoolPush(ctx, smsg) if err != nil { return ethtypes.EmptyEthHash, err From a64744f21303c5278a363916b85acbae410e9901 Mon Sep 17 00:00:00 2001 From: Aayush Date: Thu, 26 Jan 2023 14:57:04 -0500 Subject: [PATCH 26/39] fix: devnets: default to starting from nv17 --- build/params_2k.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/params_2k.go b/build/params_2k.go index 466ad0bee..962b2f9ba 100644 --- a/build/params_2k.go +++ b/build/params_2k.go @@ -23,7 +23,7 @@ var NetworkBundle = "devnet" var BundleOverrides map[actorstypes.Version]string var ActorDebugging = true -const GenesisNetworkVersion = network.Version18 +const GenesisNetworkVersion = network.Version17 var UpgradeBreezeHeight = abi.ChainEpoch(-1) From 7edfe9d759c6b03ce2a9e752693d1d77e3299d61 Mon Sep 17 00:00:00 2001 From: Aayush Date: Fri, 27 Jan 2023 15:32:08 -0500 Subject: [PATCH 27/39] Update to go-state-types v0.10.0-alpha-11 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 485bfb5a4..242fb9137 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/filecoin-project/go-legs v0.4.4 github.com/filecoin-project/go-padreader v0.0.1 github.com/filecoin-project/go-paramfetch v0.0.4 - github.com/filecoin-project/go-state-types v0.10.0-alpha-10 + github.com/filecoin-project/go-state-types v0.10.0-alpha-11 github.com/filecoin-project/go-statemachine v1.0.2 github.com/filecoin-project/go-statestore v0.2.0 github.com/filecoin-project/go-storedcounter v0.1.0 diff --git a/go.sum b/go.sum index 6ae2f7c4b..9ffaf17d6 100644 --- a/go.sum +++ b/go.sum @@ -356,8 +356,8 @@ github.com/filecoin-project/go-state-types v0.1.0/go.mod h1:ezYnPf0bNkTsDibL/psS github.com/filecoin-project/go-state-types v0.1.6/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.1.8/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.1.10/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= -github.com/filecoin-project/go-state-types v0.10.0-alpha-10 h1:QUpSayVFUADlrtzCh7SDNlbuaNSlYPBR46Nt7WpFl9I= -github.com/filecoin-project/go-state-types v0.10.0-alpha-10/go.mod h1:FPgQE05BFwZxKw/vCuIaIrzfJKo4RPQQMMPGd43dAFI= +github.com/filecoin-project/go-state-types v0.10.0-alpha-11 h1:lfrbmLXaC3vQk1gQCUwtTuY1U2ANrgDsJ7+VapBjRCo= +github.com/filecoin-project/go-state-types v0.10.0-alpha-11/go.mod h1:aLIas+W8BWAfpLWEPUOGMPBdhcVwoCG4pIQSQk26024= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= github.com/filecoin-project/go-statemachine v1.0.2 h1:421SSWBk8GIoCoWYYTE/d+qCWccgmRH0uXotXRDjUbc= github.com/filecoin-project/go-statemachine v1.0.2/go.mod h1:jZdXXiHa61n4NmgWFG4w8tnqgvZVHYbJ3yW7+y8bF54= From 395cdd721e7ead60e4b7853b3023f50daeed697f Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Mon, 30 Jan 2023 10:38:20 -0500 Subject: [PATCH 28/39] simplify transaction hash db queries, prevent fd leaks --- .../eth_transaction_hash_lookup.go | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/chain/ethhashlookup/eth_transaction_hash_lookup.go b/chain/ethhashlookup/eth_transaction_hash_lookup.go index 85cb84db1..d93680912 100644 --- a/chain/ethhashlookup/eth_transaction_hash_lookup.go +++ b/chain/ethhashlookup/eth_transaction_hash_lookup.go @@ -67,34 +67,28 @@ func (ei *EthTxHashLookup) UpsertHash(txHash ethtypes.EthHash, c cid.Cid) error } func (ei *EthTxHashLookup) GetCidFromHash(txHash ethtypes.EthHash) (cid.Cid, error) { - q, err := ei.db.Query("SELECT cid FROM eth_tx_hashes WHERE hash = :hash;", sql.Named("hash", txHash.String())) - if err != nil { - return cid.Undef, err - } + row := ei.db.QueryRow("SELECT cid FROM eth_tx_hashes WHERE hash = :hash;", sql.Named("hash", txHash.String())) var c string - if !q.Next() { - return cid.Undef, ErrNotFound - } - err = q.Scan(&c) + err := row.Scan(&c) if err != nil { + if err == sql.ErrNoRows { + return cid.Undef, ErrNotFound + } return cid.Undef, err } return cid.Decode(c) } func (ei *EthTxHashLookup) GetHashFromCid(c cid.Cid) (ethtypes.EthHash, error) { - q, err := ei.db.Query("SELECT hash FROM eth_tx_hashes WHERE cid = :cid;", sql.Named("cid", c.String())) - if err != nil { - return ethtypes.EmptyEthHash, err - } + row := ei.db.QueryRow("SELECT hash FROM eth_tx_hashes WHERE cid = :cid;", sql.Named("cid", c.String())) var hashString string - if !q.Next() { - return ethtypes.EmptyEthHash, ErrNotFound - } - err = q.Scan(&hashString) + err := row.Scan(&c) if err != nil { + if err == sql.ErrNoRows { + return ethtypes.EmptyEthHash, ErrNotFound + } return ethtypes.EmptyEthHash, err } return ethtypes.ParseEthHash(hashString) From af72e6f6ac9fee56d71db9e0f6db187a6689ce01 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Mon, 30 Jan 2023 13:40:14 -0500 Subject: [PATCH 29/39] Refactor: Unify EthTx to FilecoinMessage methods v2 (#10095) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor: Unify EthTx to FilecoinMessage methods * Filecoin messages can again be converted to Eth Txs * All BLS messages should calculated tx hash with unsigned message * Refactor newEthTxReceipt * fill in from and to for non-eth transactions * Hoist nil check out of newEthTxFromMessageLookup --------- Co-authored-by: Aayush Co-authored-by: Raúl Kripalani --- chain/messagesigner/messagesigner.go | 2 +- chain/signatures.go | 2 +- chain/types/ethtypes/eth_transactions.go | 39 ++++- itests/eth_account_abstraction_test.go | 8 +- node/impl/full/eth.go | 193 ++++++++++------------- 5 files changed, 125 insertions(+), 119 deletions(-) diff --git a/chain/messagesigner/messagesigner.go b/chain/messagesigner/messagesigner.go index 6a622dd57..84dd6d0aa 100644 --- a/chain/messagesigner/messagesigner.go +++ b/chain/messagesigner/messagesigner.go @@ -202,7 +202,7 @@ func (ms *MessageSigner) dstoreKey(addr address.Address) datastore.Key { func SigningBytes(msg *types.Message, sigType crypto.SigType) ([]byte, error) { if sigType == crypto.SigTypeDelegated { - txArgs, err := ethtypes.EthTxArgsFromMessage(msg) + txArgs, err := ethtypes.EthTxArgsFromUnsignedEthMessage(msg) if err != nil { return nil, xerrors.Errorf("failed to reconstruct eth transaction: %w", err) } diff --git a/chain/signatures.go b/chain/signatures.go index c70ef5b74..1dc67fd2c 100644 --- a/chain/signatures.go +++ b/chain/signatures.go @@ -22,7 +22,7 @@ func AuthenticateMessage(msg *types.SignedMessage, signer address.Address) error typ := msg.Signature.Type switch typ { case crypto.SigTypeDelegated: - txArgs, err := ethtypes.EthTxArgsFromMessage(&msg.Message) + txArgs, err := ethtypes.EthTxArgsFromUnsignedEthMessage(&msg.Message) if err != nil { return xerrors.Errorf("failed to reconstruct eth transaction: %w", err) } diff --git a/chain/types/ethtypes/eth_transactions.go b/chain/types/ethtypes/eth_transactions.go index 18b922234..9f9df2381 100644 --- a/chain/types/ethtypes/eth_transactions.go +++ b/chain/types/ethtypes/eth_transactions.go @@ -56,7 +56,44 @@ type EthTxArgs struct { S big.Int `json:"s"` } -func EthTxArgsFromMessage(msg *types.Message) (EthTxArgs, error) { +// EthTxFromSignedEthMessage does NOT populate: +// - BlockHash +// - BlockNumber +// - TransactionIndex +// - From +// - Hash +func EthTxFromSignedEthMessage(smsg *types.SignedMessage) (EthTx, error) { + if smsg.Signature.Type != typescrypto.SigTypeDelegated { + return EthTx{}, xerrors.Errorf("signature is not delegated type, is type: %d", smsg.Signature.Type) + } + + txArgs, err := EthTxArgsFromUnsignedEthMessage(&smsg.Message) + if err != nil { + return EthTx{}, xerrors.Errorf("failed to convert the unsigned message: %w", err) + } + + r, s, v, err := RecoverSignature(smsg.Signature) + if err != nil { + return EthTx{}, xerrors.Errorf("failed to recover signature: %w", err) + } + + return EthTx{ + Nonce: EthUint64(txArgs.Nonce), + ChainID: EthUint64(txArgs.ChainID), + To: txArgs.To, + Value: EthBigInt(txArgs.Value), + Type: Eip1559TxType, + Gas: EthUint64(txArgs.GasLimit), + MaxFeePerGas: EthBigInt(txArgs.MaxFeePerGas), + MaxPriorityFeePerGas: EthBigInt(txArgs.MaxPriorityFeePerGas), + V: v, + R: r, + S: s, + Input: txArgs.Input, + }, nil +} + +func EthTxArgsFromUnsignedEthMessage(msg *types.Message) (EthTxArgs, error) { var ( to *EthAddress params []byte diff --git a/itests/eth_account_abstraction_test.go b/itests/eth_account_abstraction_test.go index 86b7e3e9b..e6d9723bf 100644 --- a/itests/eth_account_abstraction_test.go +++ b/itests/eth_account_abstraction_test.go @@ -72,7 +72,7 @@ func TestEthAccountAbstraction(t *testing.T) { msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK) require.NoError(t, err) - txArgs, err := ethtypes.EthTxArgsFromMessage(msgFromPlaceholder) + txArgs, err := ethtypes.EthTxArgsFromUnsignedEthMessage(msgFromPlaceholder) require.NoError(t, err) digest, err := txArgs.ToRlpUnsignedMsg() @@ -108,7 +108,7 @@ func TestEthAccountAbstraction(t *testing.T) { msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK) require.NoError(t, err) - txArgs, err = ethtypes.EthTxArgsFromMessage(msgFromPlaceholder) + txArgs, err = ethtypes.EthTxArgsFromUnsignedEthMessage(msgFromPlaceholder) require.NoError(t, err) digest, err = txArgs.ToRlpUnsignedMsg() @@ -180,7 +180,7 @@ func TestEthAccountAbstractionFailure(t *testing.T) { require.NoError(t, err) msgFromPlaceholder.Value = abi.TokenAmount(types.MustParseFIL("1000")) - txArgs, err := ethtypes.EthTxArgsFromMessage(msgFromPlaceholder) + txArgs, err := ethtypes.EthTxArgsFromUnsignedEthMessage(msgFromPlaceholder) require.NoError(t, err) digest, err := txArgs.ToRlpUnsignedMsg() @@ -218,7 +218,7 @@ func TestEthAccountAbstractionFailure(t *testing.T) { msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK) require.NoError(t, err) - txArgs, err = ethtypes.EthTxArgsFromMessage(msgFromPlaceholder) + txArgs, err = ethtypes.EthTxArgsFromUnsignedEthMessage(msgFromPlaceholder) require.NoError(t, err) digest, err = txArgs.ToRlpUnsignedMsg() diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 14c69b2e1..5dbca7357 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -212,7 +212,7 @@ func (a *EthModule) EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthH if err != nil { return ethtypes.EthBlock{}, xerrors.Errorf("error loading tipset %s: %w", ts, err) } - return newEthBlockFromFilecoinTipSet(ctx, ts, fullTxInfo, a.Chain, a.ChainAPI, a.StateAPI) + return newEthBlockFromFilecoinTipSet(ctx, ts, fullTxInfo, a.Chain, a.StateAPI) } func (a *EthModule) parseBlkParam(ctx context.Context, blkParam string) (tipset *types.TipSet, err error) { @@ -249,7 +249,7 @@ func (a *EthModule) EthGetBlockByNumber(ctx context.Context, blkParam string, fu if err != nil { return ethtypes.EthBlock{}, err } - return newEthBlockFromFilecoinTipSet(ctx, ts, fullTxInfo, a.Chain, a.ChainAPI, a.StateAPI) + return newEthBlockFromFilecoinTipSet(ctx, ts, fullTxInfo, a.Chain, a.StateAPI) } func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.EthHash) (*ethtypes.EthTx, error) { @@ -270,8 +270,8 @@ func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash *ethtype // first, try to get the cid from mined transactions msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, c, api.LookbackNoLimit, true) - if err == nil { - tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, -1, a.Chain, a.StateAPI) + if err == nil && msgLookup != nil { + tx, err := newEthTxFromMessageLookup(ctx, msgLookup, -1, a.Chain, a.StateAPI) if err == nil { return &tx, nil } @@ -287,7 +287,7 @@ func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash *ethtype for _, p := range pending { if p.Cid() == c { - tx, err := NewEthTxFromFilecoinMessage(ctx, p, a.StateAPI) + tx, err := newEthTxFromSignedMessage(ctx, p, a.StateAPI) if err != nil { return nil, fmt.Errorf("could not convert Filecoin message into tx: %s", err) } @@ -336,7 +336,7 @@ func (a *EthModule) EthGetMessageCidByTransactionHash(ctx context.Context, txHas } func (a *EthModule) EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) { - hash, err := EthTxHashFromFilecoinMessageCid(ctx, cid, a.StateAPI) + hash, err := EthTxHashFromMessageCid(ctx, cid, a.StateAPI) if hash == ethtypes.EmptyEthHash { // not found return nil, nil @@ -379,7 +379,7 @@ func (a *EthModule) EthGetTransactionReceipt(ctx context.Context, txHash ethtype return nil, nil } - tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, -1, a.Chain, a.StateAPI) + tx, err := newEthTxFromMessageLookup(ctx, msgLookup, -1, a.Chain, a.StateAPI) if err != nil { return nil, nil } @@ -605,7 +605,7 @@ func (a *EthModule) EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint for ts.Height() >= abi.ChainEpoch(oldestBlkHeight) { // Unfortunately we need to rebuild the full message view so we can // totalize gas used in the tipset. - block, err := newEthBlockFromFilecoinTipSet(ctx, ts, false, a.Chain, a.ChainAPI, a.StateAPI) + block, err := newEthBlockFromFilecoinTipSet(ctx, ts, false, a.Chain, a.StateAPI) if err != nil { return ethtypes.EthFeeHistory{}, fmt.Errorf("cannot create eth block: %v", err) } @@ -1233,7 +1233,7 @@ func ethFilterResultFromEvents(evs []*filter.CollectedEvent, sa StateAPI) (*etht return nil, err } - log.TransactionHash, err = EthTxHashFromFilecoinMessageCid(context.TODO(), ev.MsgCid, sa) + log.TransactionHash, err = EthTxHashFromMessageCid(context.TODO(), ev.MsgCid, sa) if err != nil { return nil, err } @@ -1275,7 +1275,7 @@ func ethFilterResultFromMessages(cs []*types.SignedMessage, sa StateAPI) (*ethty res := ðtypes.EthFilterResult{} for _, c := range cs { - hash, err := EthTxHashFromSignedFilecoinMessage(context.TODO(), c, sa) + hash, err := EthTxHashFromSignedMessage(context.TODO(), c, sa) if err != nil { return nil, err } @@ -1376,7 +1376,7 @@ func (e *ethSubscription) start(ctx context.Context) { case *filter.CollectedEvent: resp.Result, err = ethFilterResultFromEvents([]*filter.CollectedEvent{vt}, e.StateAPI) case *types.TipSet: - eb, err := newEthBlockFromFilecoinTipSet(ctx, vt, true, e.Chain, e.ChainAPI, e.StateAPI) + eb, err := newEthBlockFromFilecoinTipSet(ctx, vt, true, e.Chain, e.StateAPI) if err != nil { break } @@ -1410,7 +1410,7 @@ func (e *ethSubscription) stop() { } } -func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTxInfo bool, cs *store.ChainStore, ca ChainAPI, sa StateAPI) (ethtypes.EthBlock, error) { +func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTxInfo bool, cs *store.ChainStore, sa StateAPI) (ethtypes.EthBlock, error) { parent, err := cs.LoadTipSet(ctx, ts.Parents()) if err != nil { return ethtypes.EthBlock{}, err @@ -1449,7 +1449,7 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx } gasUsed += msgLookup.Receipt.GasUsed - tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, txIdx, cs, sa) + tx, err := newEthTxFromMessageLookup(ctx, msgLookup, txIdx, cs, sa) if err != nil { return ethtypes.EthBlock{}, nil } @@ -1509,11 +1509,11 @@ func lookupEthAddress(ctx context.Context, addr address.Address, sa StateAPI) (e return ethtypes.EthAddressFromFilecoinAddress(idAddr) } -func EthTxHashFromFilecoinMessageCid(ctx context.Context, c cid.Cid, sa StateAPI) (ethtypes.EthHash, error) { +func EthTxHashFromMessageCid(ctx context.Context, c cid.Cid, sa StateAPI) (ethtypes.EthHash, error) { smsg, err := sa.Chain.GetSignedMessage(ctx, c) if err == nil { // This is an Eth Tx, Secp message, Or BLS message in the mpool - return EthTxHashFromSignedFilecoinMessage(ctx, smsg, sa) + return EthTxHashFromSignedMessage(ctx, smsg, sa) } _, err = sa.Chain.GetMessage(ctx, c) @@ -1525,93 +1525,44 @@ func EthTxHashFromFilecoinMessageCid(ctx context.Context, c cid.Cid, sa StateAPI return ethtypes.EmptyEthHash, nil } -func EthTxHashFromSignedFilecoinMessage(ctx context.Context, smsg *types.SignedMessage, sa StateAPI) (ethtypes.EthHash, error) { +func EthTxHashFromSignedMessage(ctx context.Context, smsg *types.SignedMessage, sa StateAPI) (ethtypes.EthHash, error) { if smsg.Signature.Type == crypto.SigTypeDelegated { - ethTx, err := NewEthTxFromFilecoinMessage(ctx, smsg, sa) + ethTx, err := newEthTxFromSignedMessage(ctx, smsg, sa) if err != nil { return ethtypes.EmptyEthHash, err } return ethTx.Hash, nil + } else if smsg.Signature.Type == crypto.SigTypeSecp256k1 { + return ethtypes.EthHashFromCid(smsg.Cid()) + } else { // BLS message + return ethtypes.EthHashFromCid(smsg.Message.Cid()) } - - return ethtypes.EthHashFromCid(smsg.Cid()) } -func NewEthTxFromFilecoinMessage(ctx context.Context, smsg *types.SignedMessage, sa StateAPI) (ethtypes.EthTx, error) { - // Ignore errors here so we can still parse non-eth messages - fromEthAddr, _ := lookupEthAddress(ctx, smsg.Message.From, sa) - toEthAddr, _ := lookupEthAddress(ctx, smsg.Message.To, sa) - - toAddr := &toEthAddr - input := smsg.Message.Params +func newEthTxFromSignedMessage(ctx context.Context, smsg *types.SignedMessage, sa StateAPI) (ethtypes.EthTx, error) { + var tx ethtypes.EthTx var err error - // Check to see if we need to decode as contract deployment. - // We don't need to resolve the to address, because there's only one form (an ID). - if smsg.Message.To == builtintypes.EthereumAddressManagerActorAddr { - switch smsg.Message.Method { - case builtintypes.MethodsEAM.Create: - toAddr = nil - var params eam.CreateParams - err = params.UnmarshalCBOR(bytes.NewReader(smsg.Message.Params)) - input = params.Initcode - case builtintypes.MethodsEAM.Create2: - toAddr = nil - var params eam.Create2Params - err = params.UnmarshalCBOR(bytes.NewReader(smsg.Message.Params)) - input = params.Initcode - case builtintypes.MethodsEAM.CreateExternal: - toAddr = nil - var params abi.CborBytes - err = params.UnmarshalCBOR(bytes.NewReader(smsg.Message.Params)) - input = []byte(params) - } - if err != nil { - return ethtypes.EthTx{}, err - } - } - // Otherwise, try to decode as a cbor byte array. - // TODO: Actually check if this is an ethereum call. This code will work for demo purposes, but is not correct. - if toAddr != nil { - if decodedParams, err := cbg.ReadByteArray(bytes.NewReader(smsg.Message.Params), uint64(len(smsg.Message.Params))); err == nil { - input = decodedParams - } - } - - r, s, v, err := ethtypes.RecoverSignature(smsg.Signature) - if err != nil { - // we don't want to return error if the message is not an Eth tx - r, s, v = ethtypes.EthBigIntZero, ethtypes.EthBigIntZero, ethtypes.EthBigIntZero - } - - tx := ethtypes.EthTx{ - Nonce: ethtypes.EthUint64(smsg.Message.Nonce), - ChainID: ethtypes.EthUint64(build.Eip155ChainId), - From: fromEthAddr, - To: toAddr, - Value: ethtypes.EthBigInt(smsg.Message.Value), - Type: ethtypes.EthUint64(2), - Input: input, - Gas: ethtypes.EthUint64(smsg.Message.GasLimit), - MaxFeePerGas: ethtypes.EthBigInt(smsg.Message.GasFeeCap), - MaxPriorityFeePerGas: ethtypes.EthBigInt(smsg.Message.GasPremium), - V: v, - R: r, - S: s, - } // This is an eth tx if smsg.Signature.Type == crypto.SigTypeDelegated { + tx, err = ethtypes.EthTxFromSignedEthMessage(smsg) + if err != nil { + return ethtypes.EthTx{}, xerrors.Errorf("failed to convert from signed message: %w", err) + } + tx.Hash, err = tx.TxHash() if err != nil { - return tx, err + return ethtypes.EthTx{}, err } - } else if smsg.Signature.Type == crypto.SigTypeUnknown { // BLS Filecoin message - tx.Hash, err = ethtypes.EthHashFromCid(smsg.Message.Cid()) + } else if smsg.Signature.Type == crypto.SigTypeSecp256k1 { // Secp Filecoin Message + tx = ethTxFromNativeMessage(ctx, smsg.VMMessage(), sa) + tx.Hash, err = ethtypes.EthHashFromCid(smsg.Cid()) if err != nil { return tx, err } - } else { // Secp Filecoin Message - tx.Hash, err = ethtypes.EthHashFromCid(smsg.Cid()) + } else { // BLS Filecoin message + tx = ethTxFromNativeMessage(ctx, smsg.VMMessage(), sa) + tx.Hash, err = ethtypes.EthHashFromCid(smsg.Message.Cid()) if err != nil { return tx, err } @@ -1620,14 +1571,32 @@ func NewEthTxFromFilecoinMessage(ctx context.Context, smsg *types.SignedMessage, return tx, nil } -// newEthTxFromFilecoinMessageLookup creates an ethereum transaction from filecoin message lookup. If a negative txIdx is passed -// into the function, it looksup the transaction index of the message in the tipset, otherwise it uses the txIdx passed into the -// function -func newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLookup, txIdx int, cs *store.ChainStore, sa StateAPI) (ethtypes.EthTx, error) { - if msgLookup == nil { - return ethtypes.EthTx{}, fmt.Errorf("msg does not exist") +// ethTxFromNativeMessage does NOT populate: +// - BlockHash +// - BlockNumber +// - TransactionIndex +// - Hash +func ethTxFromNativeMessage(ctx context.Context, msg *types.Message, sa StateAPI) ethtypes.EthTx { + // We don't care if we error here, conversion is best effort for non-eth transactions + from, _ := lookupEthAddress(ctx, msg.From, sa) + to, _ := lookupEthAddress(ctx, msg.To, sa) + return ethtypes.EthTx{ + To: &to, + From: from, + Nonce: ethtypes.EthUint64(msg.Nonce), + ChainID: ethtypes.EthUint64(build.Eip155ChainId), + Value: ethtypes.EthBigInt(msg.Value), + Type: ethtypes.Eip1559TxType, + Gas: ethtypes.EthUint64(msg.GasLimit), + MaxFeePerGas: ethtypes.EthBigInt(msg.GasFeeCap), + MaxPriorityFeePerGas: ethtypes.EthBigInt(msg.GasPremium), } +} +// newEthTxFromMessageLookup creates an ethereum transaction from filecoin message lookup. If a negative txIdx is passed +// into the function, it looks up the transaction index of the message in the tipset, otherwise it uses the txIdx passed into the +// function +func newEthTxFromMessageLookup(ctx context.Context, msgLookup *api.MsgLookup, txIdx int, cs *store.ChainStore, sa StateAPI) (ethtypes.EthTx, error) { ts, err := cs.LoadTipSet(ctx, msgLookup.TipSet) if err != nil { return ethtypes.EthTx{}, err @@ -1676,13 +1645,13 @@ func newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLo smsg = &types.SignedMessage{ Message: *msg, Signature: crypto.Signature{ - Type: crypto.SigTypeUnknown, + Type: crypto.SigTypeBLS, Data: nil, }, } } - tx, err := NewEthTxFromFilecoinMessage(ctx, smsg, sa) + tx, err := newEthTxFromSignedMessage(ctx, smsg, sa) if err != nil { return ethtypes.EthTx{}, err } @@ -1728,16 +1697,6 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook LogsBloom: ethtypes.EmptyEthBloom[:], } - if receipt.To == nil && lookup.Receipt.ExitCode.IsSuccess() { - // Create and Create2 return the same things. - var ret eam.CreateReturn - if err := ret.UnmarshalCBOR(bytes.NewReader(lookup.Receipt.Return)); err != nil { - return api.EthTxReceipt{}, xerrors.Errorf("failed to parse contract creation result: %w", err) - } - addr := ethtypes.EthAddress(ret.EthAddress) - receipt.ContractAddress = &addr - } - if lookup.Receipt.ExitCode.IsSuccess() { receipt.Status = 1 } @@ -1745,6 +1704,24 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook receipt.Status = 0 } + receipt.GasUsed = ethtypes.EthUint64(lookup.Receipt.GasUsed) + + // TODO: handle CumulativeGasUsed + receipt.CumulativeGasUsed = ethtypes.EmptyEthInt + + effectiveGasPrice := big.Div(replay.GasCost.TotalCost, big.NewInt(lookup.Receipt.GasUsed)) + receipt.EffectiveGasPrice = ethtypes.EthBigInt(effectiveGasPrice) + + if receipt.To == nil && lookup.Receipt.ExitCode.IsSuccess() { + // Create and Create2 return the same things. + var ret eam.CreateExternalReturn + if err := ret.UnmarshalCBOR(bytes.NewReader(lookup.Receipt.Return)); err != nil { + return api.EthTxReceipt{}, xerrors.Errorf("failed to parse contract creation result: %w", err) + } + addr := ethtypes.EthAddress(ret.EthAddress) + receipt.ContractAddress = &addr + } + if len(events) > 0 { // TODO return a dummy non-zero bloom to signal that there are logs // need to figure out how worth it is to populate with a real bloom @@ -1788,14 +1765,6 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook } } - receipt.GasUsed = ethtypes.EthUint64(lookup.Receipt.GasUsed) - - // TODO: handle CumulativeGasUsed - receipt.CumulativeGasUsed = ethtypes.EmptyEthInt - - effectiveGasPrice := big.Div(replay.GasCost.TotalCost, big.NewInt(lookup.Receipt.GasUsed)) - receipt.EffectiveGasPrice = ethtypes.EthBigInt(effectiveGasPrice) - return receipt, nil } @@ -1811,7 +1780,7 @@ func (m *EthTxHashManager) Apply(ctx context.Context, from, to *types.TipSet) er continue } - hash, err := EthTxHashFromSignedFilecoinMessage(ctx, smsg, m.StateAPI) + hash, err := EthTxHashFromSignedMessage(ctx, smsg, m.StateAPI) if err != nil { return err } @@ -1848,7 +1817,7 @@ func WaitForMpoolUpdates(ctx context.Context, ch <-chan api.MpoolUpdate, manager continue } - ethTx, err := NewEthTxFromFilecoinMessage(ctx, u.Message, manager.StateAPI) + ethTx, err := newEthTxFromSignedMessage(ctx, u.Message, manager.StateAPI) if err != nil { log.Errorf("error converting filecoin message to eth tx: %s", err) } From 6491becbe1ae83d4f9c59b61105c63a85cf3e12a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 16 Jan 2023 14:54:26 +0100 Subject: [PATCH 30/39] rpc: Switch eth_subscribe to reverse calls --- api/api_full.go | 10 +- api/proxy_gen.go | 1158 +++++++++++++++++++++------------------- api/v0api/proxy_gen.go | 462 ++++++++-------- gateway/handler.go | 2 +- gen/api/proxygen.go | 8 +- go.mod | 2 +- go.sum | 2 + node/impl/full/eth.go | 49 +- node/rpc.go | 2 +- 9 files changed, 881 insertions(+), 814 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index ec7b23170..20d374b50 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -6,6 +6,8 @@ import ( "fmt" "time" + "github.com/filecoin-project/go-jsonrpc" + "github.com/google/uuid" blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" @@ -831,7 +833,7 @@ type FullNode interface { // - logs: notify new event logs that match a criteria // params contains additional parameters used with the log event type // The client will receive a stream of EthSubscriptionResponse values until EthUnsubscribe is called. - EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) //perm:write + EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) //perm:write // Unsubscribe from a websocket subscription EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) //perm:write @@ -849,6 +851,12 @@ type FullNode interface { RaftLeader(ctx context.Context) (peer.ID, error) //perm:read } +// reverse interface to the client, called after EthSubscribe +type EthSubscriber interface { + // note: the parameter is ethtypes.EthSubscriptionResponse serialized as json object + EthSubscription(ctx context.Context, r jsonrpc.RawParams) error //rpc_method:eth_subscription notify:true +} + type StorageAsk struct { Response *storagemarket.StorageAsk diff --git a/api/proxy_gen.go b/api/proxy_gen.go index a7345f098..43089c6fa 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -7,21 +7,13 @@ import ( "encoding/json" "time" - "github.com/google/uuid" - blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" - "github.com/libp2p/go-libp2p/core/metrics" - "github.com/libp2p/go-libp2p/core/network" - "github.com/libp2p/go-libp2p/core/peer" - "github.com/libp2p/go-libp2p/core/protocol" - "golang.org/x/xerrors" - "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-bitfield" datatransfer "github.com/filecoin-project/go-data-transfer" "github.com/filecoin-project/go-fil-markets/piecestore" "github.com/filecoin-project/go-fil-markets/retrievalmarket" "github.com/filecoin-project/go-fil-markets/storagemarket" + "github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/go-jsonrpc/auth" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/builtin/v8/paych" @@ -31,7 +23,6 @@ import ( "github.com/filecoin-project/go-state-types/dline" abinetwork "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/go-state-types/proof" - apitypes "github.com/filecoin-project/lotus/api/types" builtinactors "github.com/filecoin-project/lotus/chain/actors/builtin" lminer "github.com/filecoin-project/lotus/chain/actors/builtin/miner" @@ -44,47 +35,59 @@ import ( "github.com/filecoin-project/lotus/storage/sealer/fsutil" "github.com/filecoin-project/lotus/storage/sealer/sealtasks" "github.com/filecoin-project/lotus/storage/sealer/storiface" + "github.com/google/uuid" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + "github.com/libp2p/go-libp2p/core/metrics" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/protocol" + "golang.org/x/xerrors" ) var ErrNotSupported = xerrors.New("method not supported") type ChainIOStruct struct { - Internal struct { - ChainHasObj func(p0 context.Context, p1 cid.Cid) (bool, error) `` + Internal ChainIOMethods +} - ChainPutObj func(p0 context.Context, p1 blocks.Block) error `` +type ChainIOMethods struct { + ChainHasObj func(p0 context.Context, p1 cid.Cid) (bool, error) `` - ChainReadObj func(p0 context.Context, p1 cid.Cid) ([]byte, error) `` - } + ChainPutObj func(p0 context.Context, p1 blocks.Block) error `` + + ChainReadObj func(p0 context.Context, p1 cid.Cid) ([]byte, error) `` } type ChainIOStub struct { } type CommonStruct struct { - Internal struct { - AuthNew func(p0 context.Context, p1 []auth.Permission) ([]byte, error) `perm:"admin"` + Internal CommonMethods +} - AuthVerify func(p0 context.Context, p1 string) ([]auth.Permission, error) `perm:"read"` +type CommonMethods struct { + AuthNew func(p0 context.Context, p1 []auth.Permission) ([]byte, error) `perm:"admin"` - Closing func(p0 context.Context) (<-chan struct{}, error) `perm:"read"` + AuthVerify func(p0 context.Context, p1 string) ([]auth.Permission, error) `perm:"read"` - Discover func(p0 context.Context) (apitypes.OpenRPCDocument, error) `perm:"read"` + Closing func(p0 context.Context) (<-chan struct{}, error) `perm:"read"` - LogAlerts func(p0 context.Context) ([]alerting.Alert, error) `perm:"admin"` + Discover func(p0 context.Context) (apitypes.OpenRPCDocument, error) `perm:"read"` - LogList func(p0 context.Context) ([]string, error) `perm:"write"` + LogAlerts func(p0 context.Context) ([]alerting.Alert, error) `perm:"admin"` - LogSetLevel func(p0 context.Context, p1 string, p2 string) error `perm:"write"` + LogList func(p0 context.Context) ([]string, error) `perm:"write"` - Session func(p0 context.Context) (uuid.UUID, error) `perm:"read"` + LogSetLevel func(p0 context.Context, p1 string, p2 string) error `perm:"write"` - Shutdown func(p0 context.Context) error `perm:"admin"` + Session func(p0 context.Context) (uuid.UUID, error) `perm:"read"` - StartTime func(p0 context.Context) (time.Time, error) `perm:"read"` + Shutdown func(p0 context.Context) error `perm:"admin"` - Version func(p0 context.Context) (APIVersion, error) `perm:"read"` - } + StartTime func(p0 context.Context) (time.Time, error) `perm:"read"` + + Version func(p0 context.Context) (APIVersion, error) `perm:"read"` } type CommonStub struct { @@ -95,8 +98,10 @@ type CommonNetStruct struct { NetStruct - Internal struct { - } + Internal CommonNetMethods +} + +type CommonNetMethods struct { } type CommonNetStub struct { @@ -105,486 +110,499 @@ type CommonNetStub struct { NetStub } +type EthSubscriberStruct struct { + Internal EthSubscriberMethods +} + +type EthSubscriberMethods struct { + EthSubscription func(p0 context.Context, p1 jsonrpc.RawParams) error `notify:"true"rpc_method:"eth_subscription"` +} + +type EthSubscriberStub struct { +} + type FullNodeStruct struct { CommonStruct NetStruct - Internal struct { - ChainBlockstoreInfo func(p0 context.Context) (map[string]interface{}, error) `perm:"read"` + Internal FullNodeMethods +} - ChainCheckBlockstore func(p0 context.Context) error `perm:"admin"` +type FullNodeMethods struct { + ChainBlockstoreInfo func(p0 context.Context) (map[string]interface{}, error) `perm:"read"` - ChainDeleteObj func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` + ChainCheckBlockstore func(p0 context.Context) error `perm:"admin"` - ChainExport func(p0 context.Context, p1 abi.ChainEpoch, p2 bool, p3 types.TipSetKey) (<-chan []byte, error) `perm:"read"` + ChainDeleteObj func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` - ChainGetBlock func(p0 context.Context, p1 cid.Cid) (*types.BlockHeader, error) `perm:"read"` + ChainExport func(p0 context.Context, p1 abi.ChainEpoch, p2 bool, p3 types.TipSetKey) (<-chan []byte, error) `perm:"read"` - ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) `perm:"read"` + ChainGetBlock func(p0 context.Context, p1 cid.Cid) (*types.BlockHeader, error) `perm:"read"` - ChainGetEvents func(p0 context.Context, p1 cid.Cid) ([]types.Event, error) `perm:"read"` + ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) `perm:"read"` - ChainGetGenesis func(p0 context.Context) (*types.TipSet, error) `perm:"read"` + ChainGetEvents func(p0 context.Context, p1 cid.Cid) ([]types.Event, error) `perm:"read"` - ChainGetMessage func(p0 context.Context, p1 cid.Cid) (*types.Message, error) `perm:"read"` + ChainGetGenesis func(p0 context.Context) (*types.TipSet, error) `perm:"read"` - ChainGetMessagesInTipset func(p0 context.Context, p1 types.TipSetKey) ([]Message, error) `perm:"read"` + ChainGetMessage func(p0 context.Context, p1 cid.Cid) (*types.Message, error) `perm:"read"` - ChainGetNode func(p0 context.Context, p1 string) (*IpldObject, error) `perm:"read"` + ChainGetMessagesInTipset func(p0 context.Context, p1 types.TipSetKey) ([]Message, error) `perm:"read"` - ChainGetParentMessages func(p0 context.Context, p1 cid.Cid) ([]Message, error) `perm:"read"` + ChainGetNode func(p0 context.Context, p1 string) (*IpldObject, error) `perm:"read"` - ChainGetParentReceipts func(p0 context.Context, p1 cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"` + ChainGetParentMessages func(p0 context.Context, p1 cid.Cid) ([]Message, error) `perm:"read"` - ChainGetPath func(p0 context.Context, p1 types.TipSetKey, p2 types.TipSetKey) ([]*HeadChange, error) `perm:"read"` + ChainGetParentReceipts func(p0 context.Context, p1 cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"` - ChainGetTipSet func(p0 context.Context, p1 types.TipSetKey) (*types.TipSet, error) `perm:"read"` + ChainGetPath func(p0 context.Context, p1 types.TipSetKey, p2 types.TipSetKey) ([]*HeadChange, error) `perm:"read"` - ChainGetTipSetAfterHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) `perm:"read"` + ChainGetTipSet func(p0 context.Context, p1 types.TipSetKey) (*types.TipSet, error) `perm:"read"` - ChainGetTipSetByHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) `perm:"read"` + ChainGetTipSetAfterHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) `perm:"read"` - ChainHasObj func(p0 context.Context, p1 cid.Cid) (bool, error) `perm:"read"` + ChainGetTipSetByHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) `perm:"read"` - ChainHead func(p0 context.Context) (*types.TipSet, error) `perm:"read"` + ChainHasObj func(p0 context.Context, p1 cid.Cid) (bool, error) `perm:"read"` - ChainNotify func(p0 context.Context) (<-chan []*HeadChange, error) `perm:"read"` + ChainHead func(p0 context.Context) (*types.TipSet, error) `perm:"read"` - ChainPrune func(p0 context.Context, p1 PruneOpts) error `perm:"admin"` + ChainNotify func(p0 context.Context) (<-chan []*HeadChange, error) `perm:"read"` - ChainPutObj func(p0 context.Context, p1 blocks.Block) error `perm:"admin"` + ChainPrune func(p0 context.Context, p1 PruneOpts) error `perm:"admin"` - ChainReadObj func(p0 context.Context, p1 cid.Cid) ([]byte, error) `perm:"read"` + ChainPutObj func(p0 context.Context, p1 blocks.Block) error `perm:"admin"` - ChainSetHead func(p0 context.Context, p1 types.TipSetKey) error `perm:"admin"` + ChainReadObj func(p0 context.Context, p1 cid.Cid) ([]byte, error) `perm:"read"` - ChainStatObj func(p0 context.Context, p1 cid.Cid, p2 cid.Cid) (ObjStat, error) `perm:"read"` + ChainSetHead func(p0 context.Context, p1 types.TipSetKey) error `perm:"admin"` - ChainTipSetWeight func(p0 context.Context, p1 types.TipSetKey) (types.BigInt, error) `perm:"read"` + ChainStatObj func(p0 context.Context, p1 cid.Cid, p2 cid.Cid) (ObjStat, error) `perm:"read"` - ClientCalcCommP func(p0 context.Context, p1 string) (*CommPRet, error) `perm:"write"` + ChainTipSetWeight func(p0 context.Context, p1 types.TipSetKey) (types.BigInt, error) `perm:"read"` - ClientCancelDataTransfer func(p0 context.Context, p1 datatransfer.TransferID, p2 peer.ID, p3 bool) error `perm:"write"` + ClientCalcCommP func(p0 context.Context, p1 string) (*CommPRet, error) `perm:"write"` - ClientCancelRetrievalDeal func(p0 context.Context, p1 retrievalmarket.DealID) error `perm:"write"` + ClientCancelDataTransfer func(p0 context.Context, p1 datatransfer.TransferID, p2 peer.ID, p3 bool) error `perm:"write"` - ClientDataTransferUpdates func(p0 context.Context) (<-chan DataTransferChannel, error) `perm:"write"` + ClientCancelRetrievalDeal func(p0 context.Context, p1 retrievalmarket.DealID) error `perm:"write"` - ClientDealPieceCID func(p0 context.Context, p1 cid.Cid) (DataCIDSize, error) `perm:"read"` + ClientDataTransferUpdates func(p0 context.Context) (<-chan DataTransferChannel, error) `perm:"write"` - ClientDealSize func(p0 context.Context, p1 cid.Cid) (DataSize, error) `perm:"read"` + ClientDealPieceCID func(p0 context.Context, p1 cid.Cid) (DataCIDSize, error) `perm:"read"` - ClientExport func(p0 context.Context, p1 ExportRef, p2 FileRef) error `perm:"admin"` + ClientDealSize func(p0 context.Context, p1 cid.Cid) (DataSize, error) `perm:"read"` - ClientFindData func(p0 context.Context, p1 cid.Cid, p2 *cid.Cid) ([]QueryOffer, error) `perm:"read"` + ClientExport func(p0 context.Context, p1 ExportRef, p2 FileRef) error `perm:"admin"` - ClientGenCar func(p0 context.Context, p1 FileRef, p2 string) error `perm:"write"` + ClientFindData func(p0 context.Context, p1 cid.Cid, p2 *cid.Cid) ([]QueryOffer, error) `perm:"read"` - ClientGetDealInfo func(p0 context.Context, p1 cid.Cid) (*DealInfo, error) `perm:"read"` + ClientGenCar func(p0 context.Context, p1 FileRef, p2 string) error `perm:"write"` - ClientGetDealStatus func(p0 context.Context, p1 uint64) (string, error) `perm:"read"` + ClientGetDealInfo func(p0 context.Context, p1 cid.Cid) (*DealInfo, error) `perm:"read"` - ClientGetDealUpdates func(p0 context.Context) (<-chan DealInfo, error) `perm:"write"` + ClientGetDealStatus func(p0 context.Context, p1 uint64) (string, error) `perm:"read"` - ClientGetRetrievalUpdates func(p0 context.Context) (<-chan RetrievalInfo, error) `perm:"write"` + ClientGetDealUpdates func(p0 context.Context) (<-chan DealInfo, error) `perm:"write"` - ClientHasLocal func(p0 context.Context, p1 cid.Cid) (bool, error) `perm:"write"` + ClientGetRetrievalUpdates func(p0 context.Context) (<-chan RetrievalInfo, error) `perm:"write"` - ClientImport func(p0 context.Context, p1 FileRef) (*ImportRes, error) `perm:"admin"` + ClientHasLocal func(p0 context.Context, p1 cid.Cid) (bool, error) `perm:"write"` - ClientListDataTransfers func(p0 context.Context) ([]DataTransferChannel, error) `perm:"write"` + ClientImport func(p0 context.Context, p1 FileRef) (*ImportRes, error) `perm:"admin"` - ClientListDeals func(p0 context.Context) ([]DealInfo, error) `perm:"write"` + ClientListDataTransfers func(p0 context.Context) ([]DataTransferChannel, error) `perm:"write"` - ClientListImports func(p0 context.Context) ([]Import, error) `perm:"write"` + ClientListDeals func(p0 context.Context) ([]DealInfo, error) `perm:"write"` - ClientListRetrievals func(p0 context.Context) ([]RetrievalInfo, error) `perm:"write"` + ClientListImports func(p0 context.Context) ([]Import, error) `perm:"write"` - ClientMinerQueryOffer func(p0 context.Context, p1 address.Address, p2 cid.Cid, p3 *cid.Cid) (QueryOffer, error) `perm:"read"` + ClientListRetrievals func(p0 context.Context) ([]RetrievalInfo, error) `perm:"write"` - ClientQueryAsk func(p0 context.Context, p1 peer.ID, p2 address.Address) (*StorageAsk, error) `perm:"read"` + ClientMinerQueryOffer func(p0 context.Context, p1 address.Address, p2 cid.Cid, p3 *cid.Cid) (QueryOffer, error) `perm:"read"` - ClientRemoveImport func(p0 context.Context, p1 imports.ID) error `perm:"admin"` + ClientQueryAsk func(p0 context.Context, p1 peer.ID, p2 address.Address) (*StorageAsk, error) `perm:"read"` - ClientRestartDataTransfer func(p0 context.Context, p1 datatransfer.TransferID, p2 peer.ID, p3 bool) error `perm:"write"` + ClientRemoveImport func(p0 context.Context, p1 imports.ID) error `perm:"admin"` - ClientRetrieve func(p0 context.Context, p1 RetrievalOrder) (*RestrievalRes, error) `perm:"admin"` + ClientRestartDataTransfer func(p0 context.Context, p1 datatransfer.TransferID, p2 peer.ID, p3 bool) error `perm:"write"` - ClientRetrieveTryRestartInsufficientFunds func(p0 context.Context, p1 address.Address) error `perm:"write"` + ClientRetrieve func(p0 context.Context, p1 RetrievalOrder) (*RestrievalRes, error) `perm:"admin"` - ClientRetrieveWait func(p0 context.Context, p1 retrievalmarket.DealID) error `perm:"admin"` + ClientRetrieveTryRestartInsufficientFunds func(p0 context.Context, p1 address.Address) error `perm:"write"` - ClientStartDeal func(p0 context.Context, p1 *StartDealParams) (*cid.Cid, error) `perm:"admin"` + ClientRetrieveWait func(p0 context.Context, p1 retrievalmarket.DealID) error `perm:"admin"` - ClientStatelessDeal func(p0 context.Context, p1 *StartDealParams) (*cid.Cid, error) `perm:"write"` + ClientStartDeal func(p0 context.Context, p1 *StartDealParams) (*cid.Cid, error) `perm:"admin"` - CreateBackup func(p0 context.Context, p1 string) error `perm:"admin"` + ClientStatelessDeal func(p0 context.Context, p1 *StartDealParams) (*cid.Cid, error) `perm:"write"` - EthAccounts func(p0 context.Context) ([]ethtypes.EthAddress, error) `perm:"read"` + CreateBackup func(p0 context.Context, p1 string) error `perm:"admin"` - EthBlockNumber func(p0 context.Context) (ethtypes.EthUint64, error) `perm:"read"` + EthAccounts func(p0 context.Context) ([]ethtypes.EthAddress, error) `perm:"read"` - EthCall func(p0 context.Context, p1 ethtypes.EthCall, p2 string) (ethtypes.EthBytes, error) `perm:"read"` + EthBlockNumber func(p0 context.Context) (ethtypes.EthUint64, error) `perm:"read"` - EthChainId func(p0 context.Context) (ethtypes.EthUint64, error) `perm:"read"` + EthCall func(p0 context.Context, p1 ethtypes.EthCall, p2 string) (ethtypes.EthBytes, error) `perm:"read"` - EthEstimateGas func(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) `perm:"read"` + EthChainId func(p0 context.Context) (ethtypes.EthUint64, error) `perm:"read"` - EthFeeHistory func(p0 context.Context, p1 ethtypes.EthUint64, p2 string, p3 []float64) (ethtypes.EthFeeHistory, error) `perm:"read"` + EthEstimateGas func(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) `perm:"read"` - EthGasPrice func(p0 context.Context) (ethtypes.EthBigInt, error) `perm:"read"` + EthFeeHistory func(p0 context.Context, p1 ethtypes.EthUint64, p2 string, p3 []float64) (ethtypes.EthFeeHistory, error) `perm:"read"` - EthGetBalance func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBigInt, error) `perm:"read"` + EthGasPrice func(p0 context.Context) (ethtypes.EthBigInt, error) `perm:"read"` - EthGetBlockByHash func(p0 context.Context, p1 ethtypes.EthHash, p2 bool) (ethtypes.EthBlock, error) `perm:"read"` + EthGetBalance func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBigInt, error) `perm:"read"` - EthGetBlockByNumber func(p0 context.Context, p1 string, p2 bool) (ethtypes.EthBlock, error) `perm:"read"` + EthGetBlockByHash func(p0 context.Context, p1 ethtypes.EthHash, p2 bool) (ethtypes.EthBlock, error) `perm:"read"` - EthGetBlockTransactionCountByHash func(p0 context.Context, p1 ethtypes.EthHash) (ethtypes.EthUint64, error) `perm:"read"` + EthGetBlockByNumber func(p0 context.Context, p1 string, p2 bool) (ethtypes.EthBlock, error) `perm:"read"` - EthGetBlockTransactionCountByNumber func(p0 context.Context, p1 ethtypes.EthUint64) (ethtypes.EthUint64, error) `perm:"read"` + EthGetBlockTransactionCountByHash func(p0 context.Context, p1 ethtypes.EthHash) (ethtypes.EthUint64, error) `perm:"read"` - EthGetCode func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBytes, error) `perm:"read"` + EthGetBlockTransactionCountByNumber func(p0 context.Context, p1 ethtypes.EthUint64) (ethtypes.EthUint64, error) `perm:"read"` - EthGetFilterChanges func(p0 context.Context, p1 ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) `perm:"write"` + EthGetCode func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBytes, error) `perm:"read"` - EthGetFilterLogs func(p0 context.Context, p1 ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) `perm:"write"` + EthGetFilterChanges func(p0 context.Context, p1 ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) `perm:"write"` - EthGetLogs func(p0 context.Context, p1 *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) `perm:"read"` + EthGetFilterLogs func(p0 context.Context, p1 ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) `perm:"write"` - EthGetMessageCidByTransactionHash func(p0 context.Context, p1 *ethtypes.EthHash) (*cid.Cid, error) `perm:"read"` + EthGetLogs func(p0 context.Context, p1 *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) `perm:"read"` - EthGetStorageAt func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 string) (ethtypes.EthBytes, error) `perm:"read"` + EthGetMessageCidByTransactionHash func(p0 context.Context, p1 *ethtypes.EthHash) (*cid.Cid, error) `perm:"read"` - EthGetTransactionByBlockHashAndIndex func(p0 context.Context, p1 ethtypes.EthHash, p2 ethtypes.EthUint64) (ethtypes.EthTx, error) `perm:"read"` + EthGetStorageAt func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 string) (ethtypes.EthBytes, error) `perm:"read"` - EthGetTransactionByBlockNumberAndIndex func(p0 context.Context, p1 ethtypes.EthUint64, p2 ethtypes.EthUint64) (ethtypes.EthTx, error) `perm:"read"` + EthGetTransactionByBlockHashAndIndex func(p0 context.Context, p1 ethtypes.EthHash, p2 ethtypes.EthUint64) (ethtypes.EthTx, error) `perm:"read"` - EthGetTransactionByHash func(p0 context.Context, p1 *ethtypes.EthHash) (*ethtypes.EthTx, error) `perm:"read"` + EthGetTransactionByBlockNumberAndIndex func(p0 context.Context, p1 ethtypes.EthUint64, p2 ethtypes.EthUint64) (ethtypes.EthTx, error) `perm:"read"` - EthGetTransactionCount func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthUint64, error) `perm:"read"` + EthGetTransactionByHash func(p0 context.Context, p1 *ethtypes.EthHash) (*ethtypes.EthTx, error) `perm:"read"` - EthGetTransactionHashByCid func(p0 context.Context, p1 cid.Cid) (*ethtypes.EthHash, error) `perm:"read"` + EthGetTransactionCount func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthUint64, error) `perm:"read"` - EthGetTransactionReceipt func(p0 context.Context, p1 ethtypes.EthHash) (*EthTxReceipt, error) `perm:"read"` + EthGetTransactionHashByCid func(p0 context.Context, p1 cid.Cid) (*ethtypes.EthHash, error) `perm:"read"` - EthMaxPriorityFeePerGas func(p0 context.Context) (ethtypes.EthBigInt, error) `perm:"read"` + EthGetTransactionReceipt func(p0 context.Context, p1 ethtypes.EthHash) (*EthTxReceipt, error) `perm:"read"` - EthNewBlockFilter func(p0 context.Context) (ethtypes.EthFilterID, error) `perm:"write"` + EthMaxPriorityFeePerGas func(p0 context.Context) (ethtypes.EthBigInt, error) `perm:"read"` - EthNewFilter func(p0 context.Context, p1 *ethtypes.EthFilterSpec) (ethtypes.EthFilterID, error) `perm:"write"` + EthNewBlockFilter func(p0 context.Context) (ethtypes.EthFilterID, error) `perm:"write"` - EthNewPendingTransactionFilter func(p0 context.Context) (ethtypes.EthFilterID, error) `perm:"write"` + EthNewFilter func(p0 context.Context, p1 *ethtypes.EthFilterSpec) (ethtypes.EthFilterID, error) `perm:"write"` - EthProtocolVersion func(p0 context.Context) (ethtypes.EthUint64, error) `perm:"read"` + EthNewPendingTransactionFilter func(p0 context.Context) (ethtypes.EthFilterID, error) `perm:"write"` - EthSendRawTransaction func(p0 context.Context, p1 ethtypes.EthBytes) (ethtypes.EthHash, error) `perm:"read"` + EthProtocolVersion func(p0 context.Context) (ethtypes.EthUint64, error) `perm:"read"` - EthSubscribe func(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) `perm:"write"` + EthSendRawTransaction func(p0 context.Context, p1 ethtypes.EthBytes) (ethtypes.EthHash, error) `perm:"read"` - EthUninstallFilter func(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) `perm:"write"` + EthSubscribe func(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) `perm:"write"` - EthUnsubscribe func(p0 context.Context, p1 ethtypes.EthSubscriptionID) (bool, error) `perm:"write"` + EthUninstallFilter func(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) `perm:"write"` - GasEstimateFeeCap func(p0 context.Context, p1 *types.Message, p2 int64, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` + EthUnsubscribe func(p0 context.Context, p1 ethtypes.EthSubscriptionID) (bool, error) `perm:"write"` - GasEstimateGasLimit func(p0 context.Context, p1 *types.Message, p2 types.TipSetKey) (int64, error) `perm:"read"` + GasEstimateFeeCap func(p0 context.Context, p1 *types.Message, p2 int64, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` - GasEstimateGasPremium func(p0 context.Context, p1 uint64, p2 address.Address, p3 int64, p4 types.TipSetKey) (types.BigInt, error) `perm:"read"` + GasEstimateGasLimit func(p0 context.Context, p1 *types.Message, p2 types.TipSetKey) (int64, error) `perm:"read"` - GasEstimateMessageGas func(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) `perm:"read"` + GasEstimateGasPremium func(p0 context.Context, p1 uint64, p2 address.Address, p3 int64, p4 types.TipSetKey) (types.BigInt, error) `perm:"read"` - MarketAddBalance func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) `perm:"sign"` + GasEstimateMessageGas func(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) `perm:"read"` - MarketGetReserved func(p0 context.Context, p1 address.Address) (types.BigInt, error) `perm:"sign"` + MarketAddBalance func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) `perm:"sign"` - MarketReleaseFunds func(p0 context.Context, p1 address.Address, p2 types.BigInt) error `perm:"sign"` + MarketGetReserved func(p0 context.Context, p1 address.Address) (types.BigInt, error) `perm:"sign"` - MarketReserveFunds func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) `perm:"sign"` + MarketReleaseFunds func(p0 context.Context, p1 address.Address, p2 types.BigInt) error `perm:"sign"` - MarketWithdraw func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) `perm:"sign"` + MarketReserveFunds func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) `perm:"sign"` - MinerCreateBlock func(p0 context.Context, p1 *BlockTemplate) (*types.BlockMsg, error) `perm:"write"` + MarketWithdraw func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) `perm:"sign"` - MinerGetBaseInfo func(p0 context.Context, p1 address.Address, p2 abi.ChainEpoch, p3 types.TipSetKey) (*MiningBaseInfo, error) `perm:"read"` + MinerCreateBlock func(p0 context.Context, p1 *BlockTemplate) (*types.BlockMsg, error) `perm:"write"` - MpoolBatchPush func(p0 context.Context, p1 []*types.SignedMessage) ([]cid.Cid, error) `perm:"write"` + MinerGetBaseInfo func(p0 context.Context, p1 address.Address, p2 abi.ChainEpoch, p3 types.TipSetKey) (*MiningBaseInfo, error) `perm:"read"` - MpoolBatchPushMessage func(p0 context.Context, p1 []*types.Message, p2 *MessageSendSpec) ([]*types.SignedMessage, error) `perm:"sign"` + MpoolBatchPush func(p0 context.Context, p1 []*types.SignedMessage) ([]cid.Cid, error) `perm:"write"` - MpoolBatchPushUntrusted func(p0 context.Context, p1 []*types.SignedMessage) ([]cid.Cid, error) `perm:"write"` + MpoolBatchPushMessage func(p0 context.Context, p1 []*types.Message, p2 *MessageSendSpec) ([]*types.SignedMessage, error) `perm:"sign"` - MpoolCheckMessages func(p0 context.Context, p1 []*MessagePrototype) ([][]MessageCheckStatus, error) `perm:"read"` + MpoolBatchPushUntrusted func(p0 context.Context, p1 []*types.SignedMessage) ([]cid.Cid, error) `perm:"write"` - MpoolCheckPendingMessages func(p0 context.Context, p1 address.Address) ([][]MessageCheckStatus, error) `perm:"read"` + MpoolCheckMessages func(p0 context.Context, p1 []*MessagePrototype) ([][]MessageCheckStatus, error) `perm:"read"` - MpoolCheckReplaceMessages func(p0 context.Context, p1 []*types.Message) ([][]MessageCheckStatus, error) `perm:"read"` + MpoolCheckPendingMessages func(p0 context.Context, p1 address.Address) ([][]MessageCheckStatus, error) `perm:"read"` - MpoolClear func(p0 context.Context, p1 bool) error `perm:"write"` + MpoolCheckReplaceMessages func(p0 context.Context, p1 []*types.Message) ([][]MessageCheckStatus, error) `perm:"read"` - MpoolGetConfig func(p0 context.Context) (*types.MpoolConfig, error) `perm:"read"` + MpoolClear func(p0 context.Context, p1 bool) error `perm:"write"` - MpoolGetNonce func(p0 context.Context, p1 address.Address) (uint64, error) `perm:"read"` + MpoolGetConfig func(p0 context.Context) (*types.MpoolConfig, error) `perm:"read"` - MpoolPending func(p0 context.Context, p1 types.TipSetKey) ([]*types.SignedMessage, error) `perm:"read"` + MpoolGetNonce func(p0 context.Context, p1 address.Address) (uint64, error) `perm:"read"` - MpoolPush func(p0 context.Context, p1 *types.SignedMessage) (cid.Cid, error) `perm:"write"` + MpoolPending func(p0 context.Context, p1 types.TipSetKey) ([]*types.SignedMessage, error) `perm:"read"` - MpoolPushMessage func(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec) (*types.SignedMessage, error) `perm:"sign"` + MpoolPush func(p0 context.Context, p1 *types.SignedMessage) (cid.Cid, error) `perm:"write"` - MpoolPushUntrusted func(p0 context.Context, p1 *types.SignedMessage) (cid.Cid, error) `perm:"write"` + MpoolPushMessage func(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec) (*types.SignedMessage, error) `perm:"sign"` - MpoolSelect func(p0 context.Context, p1 types.TipSetKey, p2 float64) ([]*types.SignedMessage, error) `perm:"read"` + MpoolPushUntrusted func(p0 context.Context, p1 *types.SignedMessage) (cid.Cid, error) `perm:"write"` - MpoolSetConfig func(p0 context.Context, p1 *types.MpoolConfig) error `perm:"admin"` + MpoolSelect func(p0 context.Context, p1 types.TipSetKey, p2 float64) ([]*types.SignedMessage, error) `perm:"read"` - MpoolSub func(p0 context.Context) (<-chan MpoolUpdate, error) `perm:"read"` + MpoolSetConfig func(p0 context.Context, p1 *types.MpoolConfig) error `perm:"admin"` - MsigAddApprove func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (*MessagePrototype, error) `perm:"sign"` + MpoolSub func(p0 context.Context) (<-chan MpoolUpdate, error) `perm:"read"` - MsigAddCancel func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (*MessagePrototype, error) `perm:"sign"` + MsigAddApprove func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (*MessagePrototype, error) `perm:"sign"` - MsigAddPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) `perm:"sign"` + MsigAddCancel func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (*MessagePrototype, error) `perm:"sign"` - MsigApprove func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (*MessagePrototype, error) `perm:"sign"` + MsigAddPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) `perm:"sign"` - MsigApproveTxnHash func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (*MessagePrototype, error) `perm:"sign"` + MsigApprove func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (*MessagePrototype, error) `perm:"sign"` - MsigCancel func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (*MessagePrototype, error) `perm:"sign"` + MsigApproveTxnHash func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (*MessagePrototype, error) `perm:"sign"` - MsigCancelTxnHash func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (*MessagePrototype, error) `perm:"sign"` + MsigCancel func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (*MessagePrototype, error) `perm:"sign"` - MsigCreate func(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (*MessagePrototype, error) `perm:"sign"` + MsigCancelTxnHash func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (*MessagePrototype, error) `perm:"sign"` - MsigGetAvailableBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) `perm:"read"` + MsigCreate func(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (*MessagePrototype, error) `perm:"sign"` - MsigGetPending func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]*MsigTransaction, error) `perm:"read"` + MsigGetAvailableBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) `perm:"read"` - MsigGetVested func(p0 context.Context, p1 address.Address, p2 types.TipSetKey, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` + MsigGetPending func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]*MsigTransaction, error) `perm:"read"` - MsigGetVestingSchedule func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MsigVesting, error) `perm:"read"` + MsigGetVested func(p0 context.Context, p1 address.Address, p2 types.TipSetKey, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` - MsigPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (*MessagePrototype, error) `perm:"sign"` + MsigGetVestingSchedule func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MsigVesting, error) `perm:"read"` - MsigRemoveSigner func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) `perm:"sign"` + MsigPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (*MessagePrototype, error) `perm:"sign"` - MsigSwapApprove func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (*MessagePrototype, error) `perm:"sign"` + MsigRemoveSigner func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) `perm:"sign"` - MsigSwapCancel func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (*MessagePrototype, error) `perm:"sign"` + MsigSwapApprove func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (*MessagePrototype, error) `perm:"sign"` - MsigSwapPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (*MessagePrototype, error) `perm:"sign"` + MsigSwapCancel func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (*MessagePrototype, error) `perm:"sign"` - NetListening func(p0 context.Context) (bool, error) `perm:"read"` + MsigSwapPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (*MessagePrototype, error) `perm:"sign"` - NetVersion func(p0 context.Context) (string, error) `perm:"read"` + NetListening func(p0 context.Context) (bool, error) `perm:"read"` - NodeStatus func(p0 context.Context, p1 bool) (NodeStatus, error) `perm:"read"` + NetVersion func(p0 context.Context) (string, error) `perm:"read"` - PaychAllocateLane func(p0 context.Context, p1 address.Address) (uint64, error) `perm:"sign"` + NodeStatus func(p0 context.Context, p1 bool) (NodeStatus, error) `perm:"read"` - PaychAvailableFunds func(p0 context.Context, p1 address.Address) (*ChannelAvailableFunds, error) `perm:"sign"` + PaychAllocateLane func(p0 context.Context, p1 address.Address) (uint64, error) `perm:"sign"` - PaychAvailableFundsByFromTo func(p0 context.Context, p1 address.Address, p2 address.Address) (*ChannelAvailableFunds, error) `perm:"sign"` + PaychAvailableFunds func(p0 context.Context, p1 address.Address) (*ChannelAvailableFunds, error) `perm:"sign"` - PaychCollect func(p0 context.Context, p1 address.Address) (cid.Cid, error) `perm:"sign"` + PaychAvailableFundsByFromTo func(p0 context.Context, p1 address.Address, p2 address.Address) (*ChannelAvailableFunds, error) `perm:"sign"` - PaychFund func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (*ChannelInfo, error) `perm:"sign"` + PaychCollect func(p0 context.Context, p1 address.Address) (cid.Cid, error) `perm:"sign"` - PaychGet func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 PaychGetOpts) (*ChannelInfo, error) `perm:"sign"` + PaychFund func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (*ChannelInfo, error) `perm:"sign"` - PaychGetWaitReady func(p0 context.Context, p1 cid.Cid) (address.Address, error) `perm:"sign"` + PaychGet func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 PaychGetOpts) (*ChannelInfo, error) `perm:"sign"` - PaychList func(p0 context.Context) ([]address.Address, error) `perm:"read"` + PaychGetWaitReady func(p0 context.Context, p1 cid.Cid) (address.Address, error) `perm:"sign"` - PaychNewPayment func(p0 context.Context, p1 address.Address, p2 address.Address, p3 []VoucherSpec) (*PaymentInfo, error) `perm:"sign"` + PaychList func(p0 context.Context) ([]address.Address, error) `perm:"read"` - PaychSettle func(p0 context.Context, p1 address.Address) (cid.Cid, error) `perm:"sign"` + PaychNewPayment func(p0 context.Context, p1 address.Address, p2 address.Address, p3 []VoucherSpec) (*PaymentInfo, error) `perm:"sign"` - PaychStatus func(p0 context.Context, p1 address.Address) (*PaychStatus, error) `perm:"read"` + PaychSettle func(p0 context.Context, p1 address.Address) (cid.Cid, error) `perm:"sign"` - PaychVoucherAdd func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher, p3 []byte, p4 types.BigInt) (types.BigInt, error) `perm:"write"` + PaychStatus func(p0 context.Context, p1 address.Address) (*PaychStatus, error) `perm:"read"` - PaychVoucherCheckSpendable func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher, p3 []byte, p4 []byte) (bool, error) `perm:"read"` + PaychVoucherAdd func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher, p3 []byte, p4 types.BigInt) (types.BigInt, error) `perm:"write"` - PaychVoucherCheckValid func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher) error `perm:"read"` + PaychVoucherCheckSpendable func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher, p3 []byte, p4 []byte) (bool, error) `perm:"read"` - PaychVoucherCreate func(p0 context.Context, p1 address.Address, p2 types.BigInt, p3 uint64) (*VoucherCreateResult, error) `perm:"sign"` + PaychVoucherCheckValid func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher) error `perm:"read"` - PaychVoucherList func(p0 context.Context, p1 address.Address) ([]*paych.SignedVoucher, error) `perm:"write"` + PaychVoucherCreate func(p0 context.Context, p1 address.Address, p2 types.BigInt, p3 uint64) (*VoucherCreateResult, error) `perm:"sign"` - PaychVoucherSubmit func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher, p3 []byte, p4 []byte) (cid.Cid, error) `perm:"sign"` + PaychVoucherList func(p0 context.Context, p1 address.Address) ([]*paych.SignedVoucher, error) `perm:"write"` - RaftLeader func(p0 context.Context) (peer.ID, error) `perm:"read"` + PaychVoucherSubmit func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher, p3 []byte, p4 []byte) (cid.Cid, error) `perm:"sign"` - RaftState func(p0 context.Context) (*RaftStateData, error) `perm:"read"` + RaftLeader func(p0 context.Context) (peer.ID, error) `perm:"read"` - StateAccountKey func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `perm:"read"` + RaftState func(p0 context.Context) (*RaftStateData, error) `perm:"read"` - StateActorCodeCIDs func(p0 context.Context, p1 abinetwork.Version) (map[string]cid.Cid, error) `perm:"read"` + StateAccountKey func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `perm:"read"` - StateActorManifestCID func(p0 context.Context, p1 abinetwork.Version) (cid.Cid, error) `perm:"read"` + StateActorCodeCIDs func(p0 context.Context, p1 abinetwork.Version) (map[string]cid.Cid, error) `perm:"read"` - StateAllMinerFaults func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) ([]*Fault, error) `perm:"read"` + StateActorManifestCID func(p0 context.Context, p1 abinetwork.Version) (cid.Cid, error) `perm:"read"` - StateCall func(p0 context.Context, p1 *types.Message, p2 types.TipSetKey) (*InvocResult, error) `perm:"read"` + StateAllMinerFaults func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) ([]*Fault, error) `perm:"read"` - StateChangedActors func(p0 context.Context, p1 cid.Cid, p2 cid.Cid) (map[string]types.Actor, error) `perm:"read"` + StateCall func(p0 context.Context, p1 *types.Message, p2 types.TipSetKey) (*InvocResult, error) `perm:"read"` - StateCirculatingSupply func(p0 context.Context, p1 types.TipSetKey) (abi.TokenAmount, error) `perm:"read"` + StateChangedActors func(p0 context.Context, p1 cid.Cid, p2 cid.Cid) (map[string]types.Actor, error) `perm:"read"` - StateCompute func(p0 context.Context, p1 abi.ChainEpoch, p2 []*types.Message, p3 types.TipSetKey) (*ComputeStateOutput, error) `perm:"read"` + StateCirculatingSupply func(p0 context.Context, p1 types.TipSetKey) (abi.TokenAmount, error) `perm:"read"` - StateComputeDataCID func(p0 context.Context, p1 address.Address, p2 abi.RegisteredSealProof, p3 []abi.DealID, p4 types.TipSetKey) (cid.Cid, error) `perm:"read"` + StateCompute func(p0 context.Context, p1 abi.ChainEpoch, p2 []*types.Message, p3 types.TipSetKey) (*ComputeStateOutput, error) `perm:"read"` - StateDealProviderCollateralBounds func(p0 context.Context, p1 abi.PaddedPieceSize, p2 bool, p3 types.TipSetKey) (DealCollateralBounds, error) `perm:"read"` + StateComputeDataCID func(p0 context.Context, p1 address.Address, p2 abi.RegisteredSealProof, p3 []abi.DealID, p4 types.TipSetKey) (cid.Cid, error) `perm:"read"` - StateDecodeParams func(p0 context.Context, p1 address.Address, p2 abi.MethodNum, p3 []byte, p4 types.TipSetKey) (interface{}, error) `perm:"read"` + StateDealProviderCollateralBounds func(p0 context.Context, p1 abi.PaddedPieceSize, p2 bool, p3 types.TipSetKey) (DealCollateralBounds, error) `perm:"read"` - StateEncodeParams func(p0 context.Context, p1 cid.Cid, p2 abi.MethodNum, p3 json.RawMessage) ([]byte, error) `perm:"read"` + StateDecodeParams func(p0 context.Context, p1 address.Address, p2 abi.MethodNum, p3 []byte, p4 types.TipSetKey) (interface{}, error) `perm:"read"` - StateGetActor func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) `perm:"read"` + StateEncodeParams func(p0 context.Context, p1 cid.Cid, p2 abi.MethodNum, p3 json.RawMessage) ([]byte, error) `perm:"read"` - StateGetAllocation func(p0 context.Context, p1 address.Address, p2 verifregtypes.AllocationId, p3 types.TipSetKey) (*verifregtypes.Allocation, error) `perm:"read"` + StateGetActor func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) `perm:"read"` - StateGetAllocationForPendingDeal func(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*verifregtypes.Allocation, error) `perm:"read"` + StateGetAllocation func(p0 context.Context, p1 address.Address, p2 verifregtypes.AllocationId, p3 types.TipSetKey) (*verifregtypes.Allocation, error) `perm:"read"` - StateGetAllocations func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) `perm:"read"` + StateGetAllocationForPendingDeal func(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*verifregtypes.Allocation, error) `perm:"read"` - StateGetBeaconEntry func(p0 context.Context, p1 abi.ChainEpoch) (*types.BeaconEntry, error) `perm:"read"` + StateGetAllocations func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) `perm:"read"` - StateGetClaim func(p0 context.Context, p1 address.Address, p2 verifregtypes.ClaimId, p3 types.TipSetKey) (*verifregtypes.Claim, error) `perm:"read"` + StateGetBeaconEntry func(p0 context.Context, p1 abi.ChainEpoch) (*types.BeaconEntry, error) `perm:"read"` - StateGetClaims func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (map[verifregtypes.ClaimId]verifregtypes.Claim, error) `perm:"read"` + StateGetClaim func(p0 context.Context, p1 address.Address, p2 verifregtypes.ClaimId, p3 types.TipSetKey) (*verifregtypes.Claim, error) `perm:"read"` - StateGetNetworkParams func(p0 context.Context) (*NetworkParams, error) `perm:"read"` + StateGetClaims func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (map[verifregtypes.ClaimId]verifregtypes.Claim, error) `perm:"read"` - StateGetRandomnessFromBeacon func(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) `perm:"read"` + StateGetNetworkParams func(p0 context.Context) (*NetworkParams, error) `perm:"read"` - StateGetRandomnessFromTickets func(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) `perm:"read"` + StateGetRandomnessFromBeacon func(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) `perm:"read"` - StateListActors func(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) `perm:"read"` + StateGetRandomnessFromTickets func(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) `perm:"read"` - StateListMessages func(p0 context.Context, p1 *MessageMatch, p2 types.TipSetKey, p3 abi.ChainEpoch) ([]cid.Cid, error) `perm:"read"` + StateListActors func(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) `perm:"read"` - StateListMiners func(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) `perm:"read"` + StateListMessages func(p0 context.Context, p1 *MessageMatch, p2 types.TipSetKey, p3 abi.ChainEpoch) ([]cid.Cid, error) `perm:"read"` - StateLookupID func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `perm:"read"` + StateListMiners func(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) `perm:"read"` - StateLookupRobustAddress func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `perm:"read"` + StateLookupID func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `perm:"read"` - StateMarketBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MarketBalance, error) `perm:"read"` + StateLookupRobustAddress func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `perm:"read"` - StateMarketDeals func(p0 context.Context, p1 types.TipSetKey) (map[string]*MarketDeal, error) `perm:"read"` + StateMarketBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MarketBalance, error) `perm:"read"` - StateMarketParticipants func(p0 context.Context, p1 types.TipSetKey) (map[string]MarketBalance, error) `perm:"read"` + StateMarketDeals func(p0 context.Context, p1 types.TipSetKey) (map[string]*MarketDeal, error) `perm:"read"` - StateMarketStorageDeal func(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*MarketDeal, error) `perm:"read"` + StateMarketParticipants func(p0 context.Context, p1 types.TipSetKey) (map[string]MarketBalance, error) `perm:"read"` - StateMinerActiveSectors func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]*miner.SectorOnChainInfo, error) `perm:"read"` + StateMarketStorageDeal func(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*MarketDeal, error) `perm:"read"` - StateMinerAllocated func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*bitfield.BitField, error) `perm:"read"` + StateMinerActiveSectors func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]*miner.SectorOnChainInfo, error) `perm:"read"` - StateMinerAvailableBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) `perm:"read"` + StateMinerAllocated func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*bitfield.BitField, error) `perm:"read"` - StateMinerDeadlines func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]Deadline, error) `perm:"read"` + StateMinerAvailableBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) `perm:"read"` - StateMinerFaults func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (bitfield.BitField, error) `perm:"read"` + StateMinerDeadlines func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]Deadline, error) `perm:"read"` - StateMinerInfo func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MinerInfo, error) `perm:"read"` + StateMinerFaults func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (bitfield.BitField, error) `perm:"read"` - StateMinerInitialPledgeCollateral func(p0 context.Context, p1 address.Address, p2 miner.SectorPreCommitInfo, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` + StateMinerInfo func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MinerInfo, error) `perm:"read"` - StateMinerPartitions func(p0 context.Context, p1 address.Address, p2 uint64, p3 types.TipSetKey) ([]Partition, error) `perm:"read"` + StateMinerInitialPledgeCollateral func(p0 context.Context, p1 address.Address, p2 miner.SectorPreCommitInfo, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` - StateMinerPower func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*MinerPower, error) `perm:"read"` + StateMinerPartitions func(p0 context.Context, p1 address.Address, p2 uint64, p3 types.TipSetKey) ([]Partition, error) `perm:"read"` - StateMinerPreCommitDepositForPower func(p0 context.Context, p1 address.Address, p2 miner.SectorPreCommitInfo, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` + StateMinerPower func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*MinerPower, error) `perm:"read"` - StateMinerProvingDeadline func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*dline.Info, error) `perm:"read"` + StateMinerPreCommitDepositForPower func(p0 context.Context, p1 address.Address, p2 miner.SectorPreCommitInfo, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` - StateMinerRecoveries func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (bitfield.BitField, error) `perm:"read"` + StateMinerProvingDeadline func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*dline.Info, error) `perm:"read"` - StateMinerSectorAllocated func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (bool, error) `perm:"read"` + StateMinerRecoveries func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (bitfield.BitField, error) `perm:"read"` - StateMinerSectorCount func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MinerSectors, error) `perm:"read"` + StateMinerSectorAllocated func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (bool, error) `perm:"read"` - StateMinerSectors func(p0 context.Context, p1 address.Address, p2 *bitfield.BitField, p3 types.TipSetKey) ([]*miner.SectorOnChainInfo, error) `perm:"read"` + StateMinerSectorCount func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MinerSectors, error) `perm:"read"` - StateNetworkName func(p0 context.Context) (dtypes.NetworkName, error) `perm:"read"` + StateMinerSectors func(p0 context.Context, p1 address.Address, p2 *bitfield.BitField, p3 types.TipSetKey) ([]*miner.SectorOnChainInfo, error) `perm:"read"` - StateNetworkVersion func(p0 context.Context, p1 types.TipSetKey) (apitypes.NetworkVersion, error) `perm:"read"` + StateNetworkName func(p0 context.Context) (dtypes.NetworkName, error) `perm:"read"` - StateReadState func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*ActorState, error) `perm:"read"` + StateNetworkVersion func(p0 context.Context, p1 types.TipSetKey) (apitypes.NetworkVersion, error) `perm:"read"` - StateReplay func(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid) (*InvocResult, error) `perm:"read"` + StateReadState func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*ActorState, error) `perm:"read"` - StateSearchMsg func(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) `perm:"read"` + StateReplay func(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid) (*InvocResult, error) `perm:"read"` - StateSectorExpiration func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*lminer.SectorExpiration, error) `perm:"read"` + StateSearchMsg func(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) `perm:"read"` - StateSectorGetInfo func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorOnChainInfo, error) `perm:"read"` + StateSectorExpiration func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*lminer.SectorExpiration, error) `perm:"read"` - StateSectorPartition func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*lminer.SectorLocation, error) `perm:"read"` + StateSectorGetInfo func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorOnChainInfo, error) `perm:"read"` - StateSectorPreCommitInfo func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorPreCommitOnChainInfo, error) `perm:"read"` + StateSectorPartition func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*lminer.SectorLocation, error) `perm:"read"` - StateVMCirculatingSupplyInternal func(p0 context.Context, p1 types.TipSetKey) (CirculatingSupply, error) `perm:"read"` + StateSectorPreCommitInfo func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorPreCommitOnChainInfo, error) `perm:"read"` - StateVerifiedClientStatus func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*abi.StoragePower, error) `perm:"read"` + StateVMCirculatingSupplyInternal func(p0 context.Context, p1 types.TipSetKey) (CirculatingSupply, error) `perm:"read"` - StateVerifiedRegistryRootKey func(p0 context.Context, p1 types.TipSetKey) (address.Address, error) `perm:"read"` + StateVerifiedClientStatus func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*abi.StoragePower, error) `perm:"read"` - StateVerifierStatus func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*abi.StoragePower, error) `perm:"read"` + StateVerifiedRegistryRootKey func(p0 context.Context, p1 types.TipSetKey) (address.Address, error) `perm:"read"` - StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) `perm:"read"` + StateVerifierStatus func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*abi.StoragePower, error) `perm:"read"` - SyncCheckBad func(p0 context.Context, p1 cid.Cid) (string, error) `perm:"read"` + StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) `perm:"read"` - SyncCheckpoint func(p0 context.Context, p1 types.TipSetKey) error `perm:"admin"` + SyncCheckBad func(p0 context.Context, p1 cid.Cid) (string, error) `perm:"read"` - SyncIncomingBlocks func(p0 context.Context) (<-chan *types.BlockHeader, error) `perm:"read"` + SyncCheckpoint func(p0 context.Context, p1 types.TipSetKey) error `perm:"admin"` - SyncMarkBad func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` + SyncIncomingBlocks func(p0 context.Context) (<-chan *types.BlockHeader, error) `perm:"read"` - SyncState func(p0 context.Context) (*SyncState, error) `perm:"read"` + SyncMarkBad func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` - SyncSubmitBlock func(p0 context.Context, p1 *types.BlockMsg) error `perm:"write"` + SyncState func(p0 context.Context) (*SyncState, error) `perm:"read"` - SyncUnmarkAllBad func(p0 context.Context) error `perm:"admin"` + SyncSubmitBlock func(p0 context.Context, p1 *types.BlockMsg) error `perm:"write"` - SyncUnmarkBad func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` + SyncUnmarkAllBad func(p0 context.Context) error `perm:"admin"` - SyncValidateTipset func(p0 context.Context, p1 types.TipSetKey) (bool, error) `perm:"read"` + SyncUnmarkBad func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` - WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) `perm:"read"` + SyncValidateTipset func(p0 context.Context, p1 types.TipSetKey) (bool, error) `perm:"read"` - WalletDefaultAddress func(p0 context.Context) (address.Address, error) `perm:"write"` + WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) `perm:"read"` - WalletDelete func(p0 context.Context, p1 address.Address) error `perm:"admin"` + WalletDefaultAddress func(p0 context.Context) (address.Address, error) `perm:"write"` - WalletExport func(p0 context.Context, p1 address.Address) (*types.KeyInfo, error) `perm:"admin"` + WalletDelete func(p0 context.Context, p1 address.Address) error `perm:"admin"` - WalletHas func(p0 context.Context, p1 address.Address) (bool, error) `perm:"write"` + WalletExport func(p0 context.Context, p1 address.Address) (*types.KeyInfo, error) `perm:"admin"` - WalletImport func(p0 context.Context, p1 *types.KeyInfo) (address.Address, error) `perm:"admin"` + WalletHas func(p0 context.Context, p1 address.Address) (bool, error) `perm:"write"` - WalletList func(p0 context.Context) ([]address.Address, error) `perm:"write"` + WalletImport func(p0 context.Context, p1 *types.KeyInfo) (address.Address, error) `perm:"admin"` - WalletNew func(p0 context.Context, p1 types.KeyType) (address.Address, error) `perm:"write"` + WalletList func(p0 context.Context) ([]address.Address, error) `perm:"write"` - WalletSetDefault func(p0 context.Context, p1 address.Address) error `perm:"write"` + WalletNew func(p0 context.Context, p1 types.KeyType) (address.Address, error) `perm:"write"` - WalletSign func(p0 context.Context, p1 address.Address, p2 []byte) (*crypto.Signature, error) `perm:"sign"` + WalletSetDefault func(p0 context.Context, p1 address.Address) error `perm:"write"` - WalletSignMessage func(p0 context.Context, p1 address.Address, p2 *types.Message) (*types.SignedMessage, error) `perm:"sign"` + WalletSign func(p0 context.Context, p1 address.Address, p2 []byte) (*crypto.Signature, error) `perm:"sign"` - WalletValidateAddress func(p0 context.Context, p1 string) (address.Address, error) `perm:"read"` + WalletSignMessage func(p0 context.Context, p1 address.Address, p2 *types.Message) (*types.SignedMessage, error) `perm:"sign"` - WalletVerify func(p0 context.Context, p1 address.Address, p2 []byte, p3 *crypto.Signature) (bool, error) `perm:"read"` + WalletValidateAddress func(p0 context.Context, p1 string) (address.Address, error) `perm:"read"` - Web3ClientVersion func(p0 context.Context) (string, error) `perm:"read"` - } + WalletVerify func(p0 context.Context, p1 address.Address, p2 []byte, p3 *crypto.Signature) (bool, error) `perm:"read"` + + Web3ClientVersion func(p0 context.Context) (string, error) `perm:"read"` } type FullNodeStub struct { @@ -594,215 +612,221 @@ type FullNodeStub struct { } type GatewayStruct struct { - Internal struct { - ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) `` + Internal GatewayMethods +} - ChainGetGenesis func(p0 context.Context) (*types.TipSet, error) `` +type GatewayMethods struct { + ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) `` - ChainGetMessage func(p0 context.Context, p1 cid.Cid) (*types.Message, error) `` + ChainGetGenesis func(p0 context.Context) (*types.TipSet, error) `` - ChainGetParentMessages func(p0 context.Context, p1 cid.Cid) ([]Message, error) `` + ChainGetMessage func(p0 context.Context, p1 cid.Cid) (*types.Message, error) `` - ChainGetParentReceipts func(p0 context.Context, p1 cid.Cid) ([]*types.MessageReceipt, error) `` + ChainGetParentMessages func(p0 context.Context, p1 cid.Cid) ([]Message, error) `` - ChainGetPath func(p0 context.Context, p1 types.TipSetKey, p2 types.TipSetKey) ([]*HeadChange, error) `` + ChainGetParentReceipts func(p0 context.Context, p1 cid.Cid) ([]*types.MessageReceipt, error) `` - ChainGetTipSet func(p0 context.Context, p1 types.TipSetKey) (*types.TipSet, error) `` + ChainGetPath func(p0 context.Context, p1 types.TipSetKey, p2 types.TipSetKey) ([]*HeadChange, error) `` - ChainGetTipSetAfterHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) `` + ChainGetTipSet func(p0 context.Context, p1 types.TipSetKey) (*types.TipSet, error) `` - ChainGetTipSetByHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) `` + ChainGetTipSetAfterHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) `` - ChainHasObj func(p0 context.Context, p1 cid.Cid) (bool, error) `` + ChainGetTipSetByHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) `` - ChainHead func(p0 context.Context) (*types.TipSet, error) `` + ChainHasObj func(p0 context.Context, p1 cid.Cid) (bool, error) `` - ChainNotify func(p0 context.Context) (<-chan []*HeadChange, error) `` + ChainHead func(p0 context.Context) (*types.TipSet, error) `` - ChainPutObj func(p0 context.Context, p1 blocks.Block) error `` + ChainNotify func(p0 context.Context) (<-chan []*HeadChange, error) `` - ChainReadObj func(p0 context.Context, p1 cid.Cid) ([]byte, error) `` + ChainPutObj func(p0 context.Context, p1 blocks.Block) error `` - Discover func(p0 context.Context) (apitypes.OpenRPCDocument, error) `` + ChainReadObj func(p0 context.Context, p1 cid.Cid) ([]byte, error) `` - EthAccounts func(p0 context.Context) ([]ethtypes.EthAddress, error) `` + Discover func(p0 context.Context) (apitypes.OpenRPCDocument, error) `` - EthBlockNumber func(p0 context.Context) (ethtypes.EthUint64, error) `` + EthAccounts func(p0 context.Context) ([]ethtypes.EthAddress, error) `` - EthCall func(p0 context.Context, p1 ethtypes.EthCall, p2 string) (ethtypes.EthBytes, error) `` + EthBlockNumber func(p0 context.Context) (ethtypes.EthUint64, error) `` - EthChainId func(p0 context.Context) (ethtypes.EthUint64, error) `` + EthCall func(p0 context.Context, p1 ethtypes.EthCall, p2 string) (ethtypes.EthBytes, error) `` - EthEstimateGas func(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) `` + EthChainId func(p0 context.Context) (ethtypes.EthUint64, error) `` - EthFeeHistory func(p0 context.Context, p1 ethtypes.EthUint64, p2 string, p3 []float64) (ethtypes.EthFeeHistory, error) `` + EthEstimateGas func(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) `` - EthGasPrice func(p0 context.Context) (ethtypes.EthBigInt, error) `` + EthFeeHistory func(p0 context.Context, p1 ethtypes.EthUint64, p2 string, p3 []float64) (ethtypes.EthFeeHistory, error) `` - EthGetBalance func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBigInt, error) `` + EthGasPrice func(p0 context.Context) (ethtypes.EthBigInt, error) `` - EthGetBlockByHash func(p0 context.Context, p1 ethtypes.EthHash, p2 bool) (ethtypes.EthBlock, error) `` + EthGetBalance func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBigInt, error) `` - EthGetBlockByNumber func(p0 context.Context, p1 string, p2 bool) (ethtypes.EthBlock, error) `` + EthGetBlockByHash func(p0 context.Context, p1 ethtypes.EthHash, p2 bool) (ethtypes.EthBlock, error) `` - EthGetBlockTransactionCountByHash func(p0 context.Context, p1 ethtypes.EthHash) (ethtypes.EthUint64, error) `` + EthGetBlockByNumber func(p0 context.Context, p1 string, p2 bool) (ethtypes.EthBlock, error) `` - EthGetBlockTransactionCountByNumber func(p0 context.Context, p1 ethtypes.EthUint64) (ethtypes.EthUint64, error) `` + EthGetBlockTransactionCountByHash func(p0 context.Context, p1 ethtypes.EthHash) (ethtypes.EthUint64, error) `` - EthGetCode func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBytes, error) `` + EthGetBlockTransactionCountByNumber func(p0 context.Context, p1 ethtypes.EthUint64) (ethtypes.EthUint64, error) `` - EthGetFilterChanges func(p0 context.Context, p1 ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) `` + EthGetCode func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBytes, error) `` - EthGetFilterLogs func(p0 context.Context, p1 ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) `` + EthGetFilterChanges func(p0 context.Context, p1 ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) `` - EthGetLogs func(p0 context.Context, p1 *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) `` + EthGetFilterLogs func(p0 context.Context, p1 ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) `` - EthGetStorageAt func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 string) (ethtypes.EthBytes, error) `` + EthGetLogs func(p0 context.Context, p1 *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) `` - EthGetTransactionByBlockHashAndIndex func(p0 context.Context, p1 ethtypes.EthHash, p2 ethtypes.EthUint64) (ethtypes.EthTx, error) `` + EthGetStorageAt func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 string) (ethtypes.EthBytes, error) `` - EthGetTransactionByBlockNumberAndIndex func(p0 context.Context, p1 ethtypes.EthUint64, p2 ethtypes.EthUint64) (ethtypes.EthTx, error) `` + EthGetTransactionByBlockHashAndIndex func(p0 context.Context, p1 ethtypes.EthHash, p2 ethtypes.EthUint64) (ethtypes.EthTx, error) `` - EthGetTransactionByHash func(p0 context.Context, p1 *ethtypes.EthHash) (*ethtypes.EthTx, error) `` + EthGetTransactionByBlockNumberAndIndex func(p0 context.Context, p1 ethtypes.EthUint64, p2 ethtypes.EthUint64) (ethtypes.EthTx, error) `` - EthGetTransactionCount func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthUint64, error) `` + EthGetTransactionByHash func(p0 context.Context, p1 *ethtypes.EthHash) (*ethtypes.EthTx, error) `` - EthGetTransactionReceipt func(p0 context.Context, p1 ethtypes.EthHash) (*EthTxReceipt, error) `` + EthGetTransactionCount func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthUint64, error) `` - EthMaxPriorityFeePerGas func(p0 context.Context) (ethtypes.EthBigInt, error) `` + EthGetTransactionReceipt func(p0 context.Context, p1 ethtypes.EthHash) (*EthTxReceipt, error) `` - EthNewBlockFilter func(p0 context.Context) (ethtypes.EthFilterID, error) `` + EthMaxPriorityFeePerGas func(p0 context.Context) (ethtypes.EthBigInt, error) `` - EthNewFilter func(p0 context.Context, p1 *ethtypes.EthFilterSpec) (ethtypes.EthFilterID, error) `` + EthNewBlockFilter func(p0 context.Context) (ethtypes.EthFilterID, error) `` - EthNewPendingTransactionFilter func(p0 context.Context) (ethtypes.EthFilterID, error) `` + EthNewFilter func(p0 context.Context, p1 *ethtypes.EthFilterSpec) (ethtypes.EthFilterID, error) `` - EthProtocolVersion func(p0 context.Context) (ethtypes.EthUint64, error) `` + EthNewPendingTransactionFilter func(p0 context.Context) (ethtypes.EthFilterID, error) `` - EthSendRawTransaction func(p0 context.Context, p1 ethtypes.EthBytes) (ethtypes.EthHash, error) `` + EthProtocolVersion func(p0 context.Context) (ethtypes.EthUint64, error) `` - EthSubscribe func(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) `` + EthSendRawTransaction func(p0 context.Context, p1 ethtypes.EthBytes) (ethtypes.EthHash, error) `` - EthUninstallFilter func(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) `` + EthSubscribe func(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) `` - EthUnsubscribe func(p0 context.Context, p1 ethtypes.EthSubscriptionID) (bool, error) `` + EthUninstallFilter func(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) `` - GasEstimateMessageGas func(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) `` + EthUnsubscribe func(p0 context.Context, p1 ethtypes.EthSubscriptionID) (bool, error) `` - MpoolPush func(p0 context.Context, p1 *types.SignedMessage) (cid.Cid, error) `` + GasEstimateMessageGas func(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) `` - MsigGetAvailableBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) `` + MpoolPush func(p0 context.Context, p1 *types.SignedMessage) (cid.Cid, error) `` - MsigGetPending func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]*MsigTransaction, error) `` + MsigGetAvailableBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) `` - MsigGetVested func(p0 context.Context, p1 address.Address, p2 types.TipSetKey, p3 types.TipSetKey) (types.BigInt, error) `` + MsigGetPending func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]*MsigTransaction, error) `` - MsigGetVestingSchedule func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MsigVesting, error) `` + MsigGetVested func(p0 context.Context, p1 address.Address, p2 types.TipSetKey, p3 types.TipSetKey) (types.BigInt, error) `` - NetListening func(p0 context.Context) (bool, error) `` + MsigGetVestingSchedule func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MsigVesting, error) `` - NetVersion func(p0 context.Context) (string, error) `` + NetListening func(p0 context.Context) (bool, error) `` - StateAccountKey func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `` + NetVersion func(p0 context.Context) (string, error) `` - StateDealProviderCollateralBounds func(p0 context.Context, p1 abi.PaddedPieceSize, p2 bool, p3 types.TipSetKey) (DealCollateralBounds, error) `` + StateAccountKey func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `` - StateGetActor func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) `` + StateDealProviderCollateralBounds func(p0 context.Context, p1 abi.PaddedPieceSize, p2 bool, p3 types.TipSetKey) (DealCollateralBounds, error) `` - StateListMiners func(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) `` + StateGetActor func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) `` - StateLookupID func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `` + StateListMiners func(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) `` - StateMarketBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MarketBalance, error) `` + StateLookupID func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `` - StateMarketStorageDeal func(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*MarketDeal, error) `` + StateMarketBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MarketBalance, error) `` - StateMinerInfo func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MinerInfo, error) `` + StateMarketStorageDeal func(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*MarketDeal, error) `` - StateMinerPower func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*MinerPower, error) `` + StateMinerInfo func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MinerInfo, error) `` - StateMinerProvingDeadline func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*dline.Info, error) `` + StateMinerPower func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*MinerPower, error) `` - StateNetworkVersion func(p0 context.Context, p1 types.TipSetKey) (apitypes.NetworkVersion, error) `` + StateMinerProvingDeadline func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*dline.Info, error) `` - StateReadState func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*ActorState, error) `perm:"read"` + StateNetworkVersion func(p0 context.Context, p1 types.TipSetKey) (apitypes.NetworkVersion, error) `` - StateSearchMsg func(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) `` + StateReadState func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*ActorState, error) `perm:"read"` - StateSectorGetInfo func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorOnChainInfo, error) `` + StateSearchMsg func(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) `` - StateVerifiedClientStatus func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*abi.StoragePower, error) `` + StateSectorGetInfo func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorOnChainInfo, error) `` - StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) `` + StateVerifiedClientStatus func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*abi.StoragePower, error) `` - Version func(p0 context.Context) (APIVersion, error) `` + StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) `` - WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) `` - } + Version func(p0 context.Context) (APIVersion, error) `` + + WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) `` } type GatewayStub struct { } type NetStruct struct { - Internal struct { - ID func(p0 context.Context) (peer.ID, error) `perm:"read"` + Internal NetMethods +} - NetAddrsListen func(p0 context.Context) (peer.AddrInfo, error) `perm:"read"` +type NetMethods struct { + ID func(p0 context.Context) (peer.ID, error) `perm:"read"` - NetAgentVersion func(p0 context.Context, p1 peer.ID) (string, error) `perm:"read"` + NetAddrsListen func(p0 context.Context) (peer.AddrInfo, error) `perm:"read"` - NetAutoNatStatus func(p0 context.Context) (NatInfo, error) `perm:"read"` + NetAgentVersion func(p0 context.Context, p1 peer.ID) (string, error) `perm:"read"` - NetBandwidthStats func(p0 context.Context) (metrics.Stats, error) `perm:"read"` + NetAutoNatStatus func(p0 context.Context) (NatInfo, error) `perm:"read"` - NetBandwidthStatsByPeer func(p0 context.Context) (map[string]metrics.Stats, error) `perm:"read"` + NetBandwidthStats func(p0 context.Context) (metrics.Stats, error) `perm:"read"` - NetBandwidthStatsByProtocol func(p0 context.Context) (map[protocol.ID]metrics.Stats, error) `perm:"read"` + NetBandwidthStatsByPeer func(p0 context.Context) (map[string]metrics.Stats, error) `perm:"read"` - NetBlockAdd func(p0 context.Context, p1 NetBlockList) error `perm:"admin"` + NetBandwidthStatsByProtocol func(p0 context.Context) (map[protocol.ID]metrics.Stats, error) `perm:"read"` - NetBlockList func(p0 context.Context) (NetBlockList, error) `perm:"read"` + NetBlockAdd func(p0 context.Context, p1 NetBlockList) error `perm:"admin"` - NetBlockRemove func(p0 context.Context, p1 NetBlockList) error `perm:"admin"` + NetBlockList func(p0 context.Context) (NetBlockList, error) `perm:"read"` - NetConnect func(p0 context.Context, p1 peer.AddrInfo) error `perm:"write"` + NetBlockRemove func(p0 context.Context, p1 NetBlockList) error `perm:"admin"` - NetConnectedness func(p0 context.Context, p1 peer.ID) (network.Connectedness, error) `perm:"read"` + NetConnect func(p0 context.Context, p1 peer.AddrInfo) error `perm:"write"` - NetDisconnect func(p0 context.Context, p1 peer.ID) error `perm:"write"` + NetConnectedness func(p0 context.Context, p1 peer.ID) (network.Connectedness, error) `perm:"read"` - NetFindPeer func(p0 context.Context, p1 peer.ID) (peer.AddrInfo, error) `perm:"read"` + NetDisconnect func(p0 context.Context, p1 peer.ID) error `perm:"write"` - NetLimit func(p0 context.Context, p1 string) (NetLimit, error) `perm:"read"` + NetFindPeer func(p0 context.Context, p1 peer.ID) (peer.AddrInfo, error) `perm:"read"` - NetPeerInfo func(p0 context.Context, p1 peer.ID) (*ExtendedPeerInfo, error) `perm:"read"` + NetLimit func(p0 context.Context, p1 string) (NetLimit, error) `perm:"read"` - NetPeers func(p0 context.Context) ([]peer.AddrInfo, error) `perm:"read"` + NetPeerInfo func(p0 context.Context, p1 peer.ID) (*ExtendedPeerInfo, error) `perm:"read"` - NetPing func(p0 context.Context, p1 peer.ID) (time.Duration, error) `perm:"read"` + NetPeers func(p0 context.Context) ([]peer.AddrInfo, error) `perm:"read"` - NetProtectAdd func(p0 context.Context, p1 []peer.ID) error `perm:"admin"` + NetPing func(p0 context.Context, p1 peer.ID) (time.Duration, error) `perm:"read"` - NetProtectList func(p0 context.Context) ([]peer.ID, error) `perm:"read"` + NetProtectAdd func(p0 context.Context, p1 []peer.ID) error `perm:"admin"` - NetProtectRemove func(p0 context.Context, p1 []peer.ID) error `perm:"admin"` + NetProtectList func(p0 context.Context) ([]peer.ID, error) `perm:"read"` - NetPubsubScores func(p0 context.Context) ([]PubsubScore, error) `perm:"read"` + NetProtectRemove func(p0 context.Context, p1 []peer.ID) error `perm:"admin"` - NetSetLimit func(p0 context.Context, p1 string, p2 NetLimit) error `perm:"admin"` + NetPubsubScores func(p0 context.Context) ([]PubsubScore, error) `perm:"read"` - NetStat func(p0 context.Context, p1 string) (NetStat, error) `perm:"read"` - } + NetSetLimit func(p0 context.Context, p1 string, p2 NetLimit) error `perm:"admin"` + + NetStat func(p0 context.Context, p1 string) (NetStat, error) `perm:"read"` } type NetStub struct { } type SignableStruct struct { - Internal struct { - Sign func(p0 context.Context, p1 SignFunc) error `` - } + Internal SignableMethods +} + +type SignableMethods struct { + Sign func(p0 context.Context, p1 SignFunc) error `` } type SignableStub struct { @@ -813,271 +837,273 @@ type StorageMinerStruct struct { NetStruct - Internal struct { - ActorAddress func(p0 context.Context) (address.Address, error) `perm:"read"` + Internal StorageMinerMethods +} - ActorAddressConfig func(p0 context.Context) (AddressConfig, error) `perm:"read"` +type StorageMinerMethods struct { + ActorAddress func(p0 context.Context) (address.Address, error) `perm:"read"` - ActorSectorSize func(p0 context.Context, p1 address.Address) (abi.SectorSize, error) `perm:"read"` + ActorAddressConfig func(p0 context.Context) (AddressConfig, error) `perm:"read"` - ActorWithdrawBalance func(p0 context.Context, p1 abi.TokenAmount) (cid.Cid, error) `perm:"admin"` + ActorSectorSize func(p0 context.Context, p1 address.Address) (abi.SectorSize, error) `perm:"read"` - BeneficiaryWithdrawBalance func(p0 context.Context, p1 abi.TokenAmount) (cid.Cid, error) `perm:"admin"` + ActorWithdrawBalance func(p0 context.Context, p1 abi.TokenAmount) (cid.Cid, error) `perm:"admin"` - CheckProvable func(p0 context.Context, p1 abi.RegisteredPoStProof, p2 []storiface.SectorRef) (map[abi.SectorNumber]string, error) `perm:"admin"` + BeneficiaryWithdrawBalance func(p0 context.Context, p1 abi.TokenAmount) (cid.Cid, error) `perm:"admin"` - ComputeDataCid func(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data) (abi.PieceInfo, error) `perm:"admin"` + CheckProvable func(p0 context.Context, p1 abi.RegisteredPoStProof, p2 []storiface.SectorRef) (map[abi.SectorNumber]string, error) `perm:"admin"` - ComputeProof func(p0 context.Context, p1 []builtinactors.ExtendedSectorInfo, p2 abi.PoStRandomness, p3 abi.ChainEpoch, p4 abinetwork.Version) ([]builtinactors.PoStProof, error) `perm:"read"` + ComputeDataCid func(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data) (abi.PieceInfo, error) `perm:"admin"` - ComputeWindowPoSt func(p0 context.Context, p1 uint64, p2 types.TipSetKey) ([]miner.SubmitWindowedPoStParams, error) `perm:"admin"` + ComputeProof func(p0 context.Context, p1 []builtinactors.ExtendedSectorInfo, p2 abi.PoStRandomness, p3 abi.ChainEpoch, p4 abinetwork.Version) ([]builtinactors.PoStProof, error) `perm:"read"` - CreateBackup func(p0 context.Context, p1 string) error `perm:"admin"` + ComputeWindowPoSt func(p0 context.Context, p1 uint64, p2 types.TipSetKey) ([]miner.SubmitWindowedPoStParams, error) `perm:"admin"` - DagstoreGC func(p0 context.Context) ([]DagstoreShardResult, error) `perm:"admin"` + CreateBackup func(p0 context.Context, p1 string) error `perm:"admin"` - DagstoreInitializeAll func(p0 context.Context, p1 DagstoreInitializeAllParams) (<-chan DagstoreInitializeAllEvent, error) `perm:"write"` + DagstoreGC func(p0 context.Context) ([]DagstoreShardResult, error) `perm:"admin"` - DagstoreInitializeShard func(p0 context.Context, p1 string) error `perm:"write"` + DagstoreInitializeAll func(p0 context.Context, p1 DagstoreInitializeAllParams) (<-chan DagstoreInitializeAllEvent, error) `perm:"write"` - DagstoreListShards func(p0 context.Context) ([]DagstoreShardInfo, error) `perm:"read"` + DagstoreInitializeShard func(p0 context.Context, p1 string) error `perm:"write"` - DagstoreLookupPieces func(p0 context.Context, p1 cid.Cid) ([]DagstoreShardInfo, error) `perm:"admin"` + DagstoreListShards func(p0 context.Context) ([]DagstoreShardInfo, error) `perm:"read"` - DagstoreRecoverShard func(p0 context.Context, p1 string) error `perm:"write"` + DagstoreLookupPieces func(p0 context.Context, p1 cid.Cid) ([]DagstoreShardInfo, error) `perm:"admin"` - DagstoreRegisterShard func(p0 context.Context, p1 string) error `perm:"admin"` + DagstoreRecoverShard func(p0 context.Context, p1 string) error `perm:"write"` - DealsConsiderOfflineRetrievalDeals func(p0 context.Context) (bool, error) `perm:"admin"` + DagstoreRegisterShard func(p0 context.Context, p1 string) error `perm:"admin"` - DealsConsiderOfflineStorageDeals func(p0 context.Context) (bool, error) `perm:"admin"` + DealsConsiderOfflineRetrievalDeals func(p0 context.Context) (bool, error) `perm:"admin"` - DealsConsiderOnlineRetrievalDeals func(p0 context.Context) (bool, error) `perm:"admin"` + DealsConsiderOfflineStorageDeals func(p0 context.Context) (bool, error) `perm:"admin"` - DealsConsiderOnlineStorageDeals func(p0 context.Context) (bool, error) `perm:"admin"` + DealsConsiderOnlineRetrievalDeals func(p0 context.Context) (bool, error) `perm:"admin"` - DealsConsiderUnverifiedStorageDeals func(p0 context.Context) (bool, error) `perm:"admin"` + DealsConsiderOnlineStorageDeals func(p0 context.Context) (bool, error) `perm:"admin"` - DealsConsiderVerifiedStorageDeals func(p0 context.Context) (bool, error) `perm:"admin"` + DealsConsiderUnverifiedStorageDeals func(p0 context.Context) (bool, error) `perm:"admin"` - DealsImportData func(p0 context.Context, p1 cid.Cid, p2 string) error `perm:"admin"` + DealsConsiderVerifiedStorageDeals func(p0 context.Context) (bool, error) `perm:"admin"` - DealsList func(p0 context.Context) ([]*MarketDeal, error) `perm:"admin"` + DealsImportData func(p0 context.Context, p1 cid.Cid, p2 string) error `perm:"admin"` - DealsPieceCidBlocklist func(p0 context.Context) ([]cid.Cid, error) `perm:"admin"` + DealsList func(p0 context.Context) ([]*MarketDeal, error) `perm:"admin"` - DealsSetConsiderOfflineRetrievalDeals func(p0 context.Context, p1 bool) error `perm:"admin"` + DealsPieceCidBlocklist func(p0 context.Context) ([]cid.Cid, error) `perm:"admin"` - DealsSetConsiderOfflineStorageDeals func(p0 context.Context, p1 bool) error `perm:"admin"` + DealsSetConsiderOfflineRetrievalDeals func(p0 context.Context, p1 bool) error `perm:"admin"` - DealsSetConsiderOnlineRetrievalDeals func(p0 context.Context, p1 bool) error `perm:"admin"` + DealsSetConsiderOfflineStorageDeals func(p0 context.Context, p1 bool) error `perm:"admin"` - DealsSetConsiderOnlineStorageDeals func(p0 context.Context, p1 bool) error `perm:"admin"` + DealsSetConsiderOnlineRetrievalDeals func(p0 context.Context, p1 bool) error `perm:"admin"` - DealsSetConsiderUnverifiedStorageDeals func(p0 context.Context, p1 bool) error `perm:"admin"` + DealsSetConsiderOnlineStorageDeals func(p0 context.Context, p1 bool) error `perm:"admin"` - DealsSetConsiderVerifiedStorageDeals func(p0 context.Context, p1 bool) error `perm:"admin"` + DealsSetConsiderUnverifiedStorageDeals func(p0 context.Context, p1 bool) error `perm:"admin"` - DealsSetPieceCidBlocklist func(p0 context.Context, p1 []cid.Cid) error `perm:"admin"` + DealsSetConsiderVerifiedStorageDeals func(p0 context.Context, p1 bool) error `perm:"admin"` - IndexerAnnounceAllDeals func(p0 context.Context) error `perm:"admin"` + DealsSetPieceCidBlocklist func(p0 context.Context, p1 []cid.Cid) error `perm:"admin"` - IndexerAnnounceDeal func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` + IndexerAnnounceAllDeals func(p0 context.Context) error `perm:"admin"` - MarketCancelDataTransfer func(p0 context.Context, p1 datatransfer.TransferID, p2 peer.ID, p3 bool) error `perm:"write"` + IndexerAnnounceDeal func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` - MarketDataTransferDiagnostics func(p0 context.Context, p1 peer.ID) (*TransferDiagnostics, error) `perm:"write"` + MarketCancelDataTransfer func(p0 context.Context, p1 datatransfer.TransferID, p2 peer.ID, p3 bool) error `perm:"write"` - MarketDataTransferUpdates func(p0 context.Context) (<-chan DataTransferChannel, error) `perm:"write"` + MarketDataTransferDiagnostics func(p0 context.Context, p1 peer.ID) (*TransferDiagnostics, error) `perm:"write"` - MarketGetAsk func(p0 context.Context) (*storagemarket.SignedStorageAsk, error) `perm:"read"` + MarketDataTransferUpdates func(p0 context.Context) (<-chan DataTransferChannel, error) `perm:"write"` - MarketGetDealUpdates func(p0 context.Context) (<-chan storagemarket.MinerDeal, error) `perm:"read"` + MarketGetAsk func(p0 context.Context) (*storagemarket.SignedStorageAsk, error) `perm:"read"` - MarketGetRetrievalAsk func(p0 context.Context) (*retrievalmarket.Ask, error) `perm:"read"` + MarketGetDealUpdates func(p0 context.Context) (<-chan storagemarket.MinerDeal, error) `perm:"read"` - MarketImportDealData func(p0 context.Context, p1 cid.Cid, p2 string) error `perm:"write"` + MarketGetRetrievalAsk func(p0 context.Context) (*retrievalmarket.Ask, error) `perm:"read"` - MarketListDataTransfers func(p0 context.Context) ([]DataTransferChannel, error) `perm:"write"` + MarketImportDealData func(p0 context.Context, p1 cid.Cid, p2 string) error `perm:"write"` - MarketListDeals func(p0 context.Context) ([]*MarketDeal, error) `perm:"read"` + MarketListDataTransfers func(p0 context.Context) ([]DataTransferChannel, error) `perm:"write"` - MarketListIncompleteDeals func(p0 context.Context) ([]storagemarket.MinerDeal, error) `perm:"read"` + MarketListDeals func(p0 context.Context) ([]*MarketDeal, error) `perm:"read"` - MarketListRetrievalDeals func(p0 context.Context) ([]retrievalmarket.ProviderDealState, error) `perm:"read"` + MarketListIncompleteDeals func(p0 context.Context) ([]storagemarket.MinerDeal, error) `perm:"read"` - MarketPendingDeals func(p0 context.Context) (PendingDealInfo, error) `perm:"write"` + MarketListRetrievalDeals func(p0 context.Context) ([]retrievalmarket.ProviderDealState, error) `perm:"read"` - MarketPublishPendingDeals func(p0 context.Context) error `perm:"admin"` + MarketPendingDeals func(p0 context.Context) (PendingDealInfo, error) `perm:"write"` - MarketRestartDataTransfer func(p0 context.Context, p1 datatransfer.TransferID, p2 peer.ID, p3 bool) error `perm:"write"` + MarketPublishPendingDeals func(p0 context.Context) error `perm:"admin"` - MarketRetryPublishDeal func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` + MarketRestartDataTransfer func(p0 context.Context, p1 datatransfer.TransferID, p2 peer.ID, p3 bool) error `perm:"write"` - MarketSetAsk func(p0 context.Context, p1 types.BigInt, p2 types.BigInt, p3 abi.ChainEpoch, p4 abi.PaddedPieceSize, p5 abi.PaddedPieceSize) error `perm:"admin"` + MarketRetryPublishDeal func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` - MarketSetRetrievalAsk func(p0 context.Context, p1 *retrievalmarket.Ask) error `perm:"admin"` + MarketSetAsk func(p0 context.Context, p1 types.BigInt, p2 types.BigInt, p3 abi.ChainEpoch, p4 abi.PaddedPieceSize, p5 abi.PaddedPieceSize) error `perm:"admin"` - MiningBase func(p0 context.Context) (*types.TipSet, error) `perm:"read"` + MarketSetRetrievalAsk func(p0 context.Context, p1 *retrievalmarket.Ask) error `perm:"admin"` - PiecesGetCIDInfo func(p0 context.Context, p1 cid.Cid) (*piecestore.CIDInfo, error) `perm:"read"` + MiningBase func(p0 context.Context) (*types.TipSet, error) `perm:"read"` - PiecesGetPieceInfo func(p0 context.Context, p1 cid.Cid) (*piecestore.PieceInfo, error) `perm:"read"` + PiecesGetCIDInfo func(p0 context.Context, p1 cid.Cid) (*piecestore.CIDInfo, error) `perm:"read"` - PiecesListCidInfos func(p0 context.Context) ([]cid.Cid, error) `perm:"read"` + PiecesGetPieceInfo func(p0 context.Context, p1 cid.Cid) (*piecestore.PieceInfo, error) `perm:"read"` - PiecesListPieces func(p0 context.Context) ([]cid.Cid, error) `perm:"read"` + PiecesListCidInfos func(p0 context.Context) ([]cid.Cid, error) `perm:"read"` - PledgeSector func(p0 context.Context) (abi.SectorID, error) `perm:"write"` + PiecesListPieces func(p0 context.Context) ([]cid.Cid, error) `perm:"read"` - RecoverFault func(p0 context.Context, p1 []abi.SectorNumber) ([]cid.Cid, error) `perm:"admin"` + PledgeSector func(p0 context.Context) (abi.SectorID, error) `perm:"write"` - ReturnAddPiece func(p0 context.Context, p1 storiface.CallID, p2 abi.PieceInfo, p3 *storiface.CallError) error `perm:"admin"` + RecoverFault func(p0 context.Context, p1 []abi.SectorNumber) ([]cid.Cid, error) `perm:"admin"` - ReturnDataCid func(p0 context.Context, p1 storiface.CallID, p2 abi.PieceInfo, p3 *storiface.CallError) error `perm:"admin"` + ReturnAddPiece func(p0 context.Context, p1 storiface.CallID, p2 abi.PieceInfo, p3 *storiface.CallError) error `perm:"admin"` - ReturnDownloadSector func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` + ReturnDataCid func(p0 context.Context, p1 storiface.CallID, p2 abi.PieceInfo, p3 *storiface.CallError) error `perm:"admin"` - ReturnFetch func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` + ReturnDownloadSector func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` - ReturnFinalizeReplicaUpdate func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` + ReturnFetch func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` - ReturnFinalizeSector func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` + ReturnFinalizeReplicaUpdate func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` - ReturnGenerateSectorKeyFromData func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` + ReturnFinalizeSector func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` - ReturnMoveStorage func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` + ReturnGenerateSectorKeyFromData func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` - ReturnProveReplicaUpdate1 func(p0 context.Context, p1 storiface.CallID, p2 storiface.ReplicaVanillaProofs, p3 *storiface.CallError) error `perm:"admin"` + ReturnMoveStorage func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` - ReturnProveReplicaUpdate2 func(p0 context.Context, p1 storiface.CallID, p2 storiface.ReplicaUpdateProof, p3 *storiface.CallError) error `perm:"admin"` + ReturnProveReplicaUpdate1 func(p0 context.Context, p1 storiface.CallID, p2 storiface.ReplicaVanillaProofs, p3 *storiface.CallError) error `perm:"admin"` - ReturnReadPiece func(p0 context.Context, p1 storiface.CallID, p2 bool, p3 *storiface.CallError) error `perm:"admin"` + ReturnProveReplicaUpdate2 func(p0 context.Context, p1 storiface.CallID, p2 storiface.ReplicaUpdateProof, p3 *storiface.CallError) error `perm:"admin"` - ReturnReleaseUnsealed func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` + ReturnReadPiece func(p0 context.Context, p1 storiface.CallID, p2 bool, p3 *storiface.CallError) error `perm:"admin"` - ReturnReplicaUpdate func(p0 context.Context, p1 storiface.CallID, p2 storiface.ReplicaUpdateOut, p3 *storiface.CallError) error `perm:"admin"` + ReturnReleaseUnsealed func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` - ReturnSealCommit1 func(p0 context.Context, p1 storiface.CallID, p2 storiface.Commit1Out, p3 *storiface.CallError) error `perm:"admin"` + ReturnReplicaUpdate func(p0 context.Context, p1 storiface.CallID, p2 storiface.ReplicaUpdateOut, p3 *storiface.CallError) error `perm:"admin"` - ReturnSealCommit2 func(p0 context.Context, p1 storiface.CallID, p2 storiface.Proof, p3 *storiface.CallError) error `perm:"admin"` + ReturnSealCommit1 func(p0 context.Context, p1 storiface.CallID, p2 storiface.Commit1Out, p3 *storiface.CallError) error `perm:"admin"` - ReturnSealPreCommit1 func(p0 context.Context, p1 storiface.CallID, p2 storiface.PreCommit1Out, p3 *storiface.CallError) error `perm:"admin"` + ReturnSealCommit2 func(p0 context.Context, p1 storiface.CallID, p2 storiface.Proof, p3 *storiface.CallError) error `perm:"admin"` - ReturnSealPreCommit2 func(p0 context.Context, p1 storiface.CallID, p2 storiface.SectorCids, p3 *storiface.CallError) error `perm:"admin"` + ReturnSealPreCommit1 func(p0 context.Context, p1 storiface.CallID, p2 storiface.PreCommit1Out, p3 *storiface.CallError) error `perm:"admin"` - ReturnUnsealPiece func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` + ReturnSealPreCommit2 func(p0 context.Context, p1 storiface.CallID, p2 storiface.SectorCids, p3 *storiface.CallError) error `perm:"admin"` - RuntimeSubsystems func(p0 context.Context) (MinerSubsystems, error) `perm:"read"` + ReturnUnsealPiece func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` - SealingAbort func(p0 context.Context, p1 storiface.CallID) error `perm:"admin"` + RuntimeSubsystems func(p0 context.Context) (MinerSubsystems, error) `perm:"read"` - SealingRemoveRequest func(p0 context.Context, p1 uuid.UUID) error `perm:"admin"` + SealingAbort func(p0 context.Context, p1 storiface.CallID) error `perm:"admin"` - SealingSchedDiag func(p0 context.Context, p1 bool) (interface{}, error) `perm:"admin"` + SealingRemoveRequest func(p0 context.Context, p1 uuid.UUID) error `perm:"admin"` - SectorAbortUpgrade func(p0 context.Context, p1 abi.SectorNumber) error `perm:"admin"` + SealingSchedDiag func(p0 context.Context, p1 bool) (interface{}, error) `perm:"admin"` - SectorAddPieceToAny func(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data, p3 PieceDealInfo) (SectorOffset, error) `perm:"admin"` + SectorAbortUpgrade func(p0 context.Context, p1 abi.SectorNumber) error `perm:"admin"` - SectorCommitFlush func(p0 context.Context) ([]sealiface.CommitBatchRes, error) `perm:"admin"` + SectorAddPieceToAny func(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data, p3 PieceDealInfo) (SectorOffset, error) `perm:"admin"` - SectorCommitPending func(p0 context.Context) ([]abi.SectorID, error) `perm:"admin"` + SectorCommitFlush func(p0 context.Context) ([]sealiface.CommitBatchRes, error) `perm:"admin"` - SectorGetExpectedSealDuration func(p0 context.Context) (time.Duration, error) `perm:"read"` + SectorCommitPending func(p0 context.Context) ([]abi.SectorID, error) `perm:"admin"` - SectorGetSealDelay func(p0 context.Context) (time.Duration, error) `perm:"read"` + SectorGetExpectedSealDuration func(p0 context.Context) (time.Duration, error) `perm:"read"` - SectorMarkForUpgrade func(p0 context.Context, p1 abi.SectorNumber, p2 bool) error `perm:"admin"` + SectorGetSealDelay func(p0 context.Context) (time.Duration, error) `perm:"read"` - SectorMatchPendingPiecesToOpenSectors func(p0 context.Context) error `perm:"admin"` + SectorMarkForUpgrade func(p0 context.Context, p1 abi.SectorNumber, p2 bool) error `perm:"admin"` - SectorNumAssignerMeta func(p0 context.Context) (NumAssignerMeta, error) `perm:"read"` + SectorMatchPendingPiecesToOpenSectors func(p0 context.Context) error `perm:"admin"` - SectorNumFree func(p0 context.Context, p1 string) error `perm:"admin"` + SectorNumAssignerMeta func(p0 context.Context) (NumAssignerMeta, error) `perm:"read"` - SectorNumReservations func(p0 context.Context) (map[string]bitfield.BitField, error) `perm:"read"` + SectorNumFree func(p0 context.Context, p1 string) error `perm:"admin"` - SectorNumReserve func(p0 context.Context, p1 string, p2 bitfield.BitField, p3 bool) error `perm:"admin"` + SectorNumReservations func(p0 context.Context) (map[string]bitfield.BitField, error) `perm:"read"` - SectorNumReserveCount func(p0 context.Context, p1 string, p2 uint64) (bitfield.BitField, error) `perm:"admin"` + SectorNumReserve func(p0 context.Context, p1 string, p2 bitfield.BitField, p3 bool) error `perm:"admin"` - SectorPreCommitFlush func(p0 context.Context) ([]sealiface.PreCommitBatchRes, error) `perm:"admin"` + SectorNumReserveCount func(p0 context.Context, p1 string, p2 uint64) (bitfield.BitField, error) `perm:"admin"` - SectorPreCommitPending func(p0 context.Context) ([]abi.SectorID, error) `perm:"admin"` + SectorPreCommitFlush func(p0 context.Context) ([]sealiface.PreCommitBatchRes, error) `perm:"admin"` - SectorReceive func(p0 context.Context, p1 RemoteSectorMeta) error `perm:"admin"` + SectorPreCommitPending func(p0 context.Context) ([]abi.SectorID, error) `perm:"admin"` - SectorRemove func(p0 context.Context, p1 abi.SectorNumber) error `perm:"admin"` + SectorReceive func(p0 context.Context, p1 RemoteSectorMeta) error `perm:"admin"` - SectorSetExpectedSealDuration func(p0 context.Context, p1 time.Duration) error `perm:"write"` + SectorRemove func(p0 context.Context, p1 abi.SectorNumber) error `perm:"admin"` - SectorSetSealDelay func(p0 context.Context, p1 time.Duration) error `perm:"write"` + SectorSetExpectedSealDuration func(p0 context.Context, p1 time.Duration) error `perm:"write"` - SectorStartSealing func(p0 context.Context, p1 abi.SectorNumber) error `perm:"write"` + SectorSetSealDelay func(p0 context.Context, p1 time.Duration) error `perm:"write"` - SectorTerminate func(p0 context.Context, p1 abi.SectorNumber) error `perm:"admin"` + SectorStartSealing func(p0 context.Context, p1 abi.SectorNumber) error `perm:"write"` - SectorTerminateFlush func(p0 context.Context) (*cid.Cid, error) `perm:"admin"` + SectorTerminate func(p0 context.Context, p1 abi.SectorNumber) error `perm:"admin"` - SectorTerminatePending func(p0 context.Context) ([]abi.SectorID, error) `perm:"admin"` + SectorTerminateFlush func(p0 context.Context) (*cid.Cid, error) `perm:"admin"` - SectorsList func(p0 context.Context) ([]abi.SectorNumber, error) `perm:"read"` + SectorTerminatePending func(p0 context.Context) ([]abi.SectorID, error) `perm:"admin"` - SectorsListInStates func(p0 context.Context, p1 []SectorState) ([]abi.SectorNumber, error) `perm:"read"` + SectorsList func(p0 context.Context) ([]abi.SectorNumber, error) `perm:"read"` - SectorsRefs func(p0 context.Context) (map[string][]SealedRef, error) `perm:"read"` + SectorsListInStates func(p0 context.Context, p1 []SectorState) ([]abi.SectorNumber, error) `perm:"read"` - SectorsStatus func(p0 context.Context, p1 abi.SectorNumber, p2 bool) (SectorInfo, error) `perm:"read"` + SectorsRefs func(p0 context.Context) (map[string][]SealedRef, error) `perm:"read"` - SectorsSummary func(p0 context.Context) (map[SectorState]int, error) `perm:"read"` + SectorsStatus func(p0 context.Context, p1 abi.SectorNumber, p2 bool) (SectorInfo, error) `perm:"read"` - SectorsUnsealPiece func(p0 context.Context, p1 storiface.SectorRef, p2 storiface.UnpaddedByteIndex, p3 abi.UnpaddedPieceSize, p4 abi.SealRandomness, p5 *cid.Cid) error `perm:"admin"` + SectorsSummary func(p0 context.Context) (map[SectorState]int, error) `perm:"read"` - SectorsUpdate func(p0 context.Context, p1 abi.SectorNumber, p2 SectorState) error `perm:"admin"` + SectorsUnsealPiece func(p0 context.Context, p1 storiface.SectorRef, p2 storiface.UnpaddedByteIndex, p3 abi.UnpaddedPieceSize, p4 abi.SealRandomness, p5 *cid.Cid) error `perm:"admin"` - StorageAddLocal func(p0 context.Context, p1 string) error `perm:"admin"` + SectorsUpdate func(p0 context.Context, p1 abi.SectorNumber, p2 SectorState) error `perm:"admin"` - StorageAttach func(p0 context.Context, p1 storiface.StorageInfo, p2 fsutil.FsStat) error `perm:"admin"` + StorageAddLocal func(p0 context.Context, p1 string) error `perm:"admin"` - StorageAuthVerify func(p0 context.Context, p1 string) ([]auth.Permission, error) `perm:"read"` + StorageAttach func(p0 context.Context, p1 storiface.StorageInfo, p2 fsutil.FsStat) error `perm:"admin"` - StorageBestAlloc func(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType) ([]storiface.StorageInfo, error) `perm:"admin"` + StorageAuthVerify func(p0 context.Context, p1 string) ([]auth.Permission, error) `perm:"read"` - StorageDeclareSector func(p0 context.Context, p1 storiface.ID, p2 abi.SectorID, p3 storiface.SectorFileType, p4 bool) error `perm:"admin"` + StorageBestAlloc func(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType) ([]storiface.StorageInfo, error) `perm:"admin"` - StorageDetach func(p0 context.Context, p1 storiface.ID, p2 string) error `perm:"admin"` + StorageDeclareSector func(p0 context.Context, p1 storiface.ID, p2 abi.SectorID, p3 storiface.SectorFileType, p4 bool) error `perm:"admin"` - StorageDetachLocal func(p0 context.Context, p1 string) error `perm:"admin"` + StorageDetach func(p0 context.Context, p1 storiface.ID, p2 string) error `perm:"admin"` - StorageDropSector func(p0 context.Context, p1 storiface.ID, p2 abi.SectorID, p3 storiface.SectorFileType) error `perm:"admin"` + StorageDetachLocal func(p0 context.Context, p1 string) error `perm:"admin"` - StorageFindSector func(p0 context.Context, p1 abi.SectorID, p2 storiface.SectorFileType, p3 abi.SectorSize, p4 bool) ([]storiface.SectorStorageInfo, error) `perm:"admin"` + StorageDropSector func(p0 context.Context, p1 storiface.ID, p2 abi.SectorID, p3 storiface.SectorFileType) error `perm:"admin"` - StorageGetLocks func(p0 context.Context) (storiface.SectorLocks, error) `perm:"admin"` + StorageFindSector func(p0 context.Context, p1 abi.SectorID, p2 storiface.SectorFileType, p3 abi.SectorSize, p4 bool) ([]storiface.SectorStorageInfo, error) `perm:"admin"` - StorageInfo func(p0 context.Context, p1 storiface.ID) (storiface.StorageInfo, error) `perm:"admin"` + StorageGetLocks func(p0 context.Context) (storiface.SectorLocks, error) `perm:"admin"` - StorageList func(p0 context.Context) (map[storiface.ID][]storiface.Decl, error) `perm:"admin"` + StorageInfo func(p0 context.Context, p1 storiface.ID) (storiface.StorageInfo, error) `perm:"admin"` - StorageLocal func(p0 context.Context) (map[storiface.ID]string, error) `perm:"admin"` + StorageList func(p0 context.Context) (map[storiface.ID][]storiface.Decl, error) `perm:"admin"` - StorageLock func(p0 context.Context, p1 abi.SectorID, p2 storiface.SectorFileType, p3 storiface.SectorFileType) error `perm:"admin"` + StorageLocal func(p0 context.Context) (map[storiface.ID]string, error) `perm:"admin"` - StorageRedeclareLocal func(p0 context.Context, p1 *storiface.ID, p2 bool) error `perm:"admin"` + StorageLock func(p0 context.Context, p1 abi.SectorID, p2 storiface.SectorFileType, p3 storiface.SectorFileType) error `perm:"admin"` - StorageReportHealth func(p0 context.Context, p1 storiface.ID, p2 storiface.HealthReport) error `perm:"admin"` + StorageRedeclareLocal func(p0 context.Context, p1 *storiface.ID, p2 bool) error `perm:"admin"` - StorageStat func(p0 context.Context, p1 storiface.ID) (fsutil.FsStat, error) `perm:"admin"` + StorageReportHealth func(p0 context.Context, p1 storiface.ID, p2 storiface.HealthReport) error `perm:"admin"` - StorageTryLock func(p0 context.Context, p1 abi.SectorID, p2 storiface.SectorFileType, p3 storiface.SectorFileType) (bool, error) `perm:"admin"` + StorageStat func(p0 context.Context, p1 storiface.ID) (fsutil.FsStat, error) `perm:"admin"` - WorkerConnect func(p0 context.Context, p1 string) error `perm:"admin"` + StorageTryLock func(p0 context.Context, p1 abi.SectorID, p2 storiface.SectorFileType, p3 storiface.SectorFileType) (bool, error) `perm:"admin"` - WorkerJobs func(p0 context.Context) (map[uuid.UUID][]storiface.WorkerJob, error) `perm:"admin"` + WorkerConnect func(p0 context.Context, p1 string) error `perm:"admin"` - WorkerStats func(p0 context.Context) (map[uuid.UUID]storiface.WorkerStats, error) `perm:"admin"` - } + WorkerJobs func(p0 context.Context) (map[uuid.UUID][]storiface.WorkerJob, error) `perm:"admin"` + + WorkerStats func(p0 context.Context) (map[uuid.UUID]storiface.WorkerStats, error) `perm:"admin"` } type StorageMinerStub struct { @@ -1087,102 +1113,106 @@ type StorageMinerStub struct { } type WalletStruct struct { - Internal struct { - WalletDelete func(p0 context.Context, p1 address.Address) error `perm:"admin"` + Internal WalletMethods +} - WalletExport func(p0 context.Context, p1 address.Address) (*types.KeyInfo, error) `perm:"admin"` +type WalletMethods struct { + WalletDelete func(p0 context.Context, p1 address.Address) error `perm:"admin"` - WalletHas func(p0 context.Context, p1 address.Address) (bool, error) `perm:"admin"` + WalletExport func(p0 context.Context, p1 address.Address) (*types.KeyInfo, error) `perm:"admin"` - WalletImport func(p0 context.Context, p1 *types.KeyInfo) (address.Address, error) `perm:"admin"` + WalletHas func(p0 context.Context, p1 address.Address) (bool, error) `perm:"admin"` - WalletList func(p0 context.Context) ([]address.Address, error) `perm:"admin"` + WalletImport func(p0 context.Context, p1 *types.KeyInfo) (address.Address, error) `perm:"admin"` - WalletNew func(p0 context.Context, p1 types.KeyType) (address.Address, error) `perm:"admin"` + WalletList func(p0 context.Context) ([]address.Address, error) `perm:"admin"` - WalletSign func(p0 context.Context, p1 address.Address, p2 []byte, p3 MsgMeta) (*crypto.Signature, error) `perm:"admin"` - } + WalletNew func(p0 context.Context, p1 types.KeyType) (address.Address, error) `perm:"admin"` + + WalletSign func(p0 context.Context, p1 address.Address, p2 []byte, p3 MsgMeta) (*crypto.Signature, error) `perm:"admin"` } type WalletStub struct { } type WorkerStruct struct { - Internal struct { - AddPiece func(p0 context.Context, p1 storiface.SectorRef, p2 []abi.UnpaddedPieceSize, p3 abi.UnpaddedPieceSize, p4 storiface.Data) (storiface.CallID, error) `perm:"admin"` + Internal WorkerMethods +} - DataCid func(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data) (storiface.CallID, error) `perm:"admin"` +type WorkerMethods struct { + AddPiece func(p0 context.Context, p1 storiface.SectorRef, p2 []abi.UnpaddedPieceSize, p3 abi.UnpaddedPieceSize, p4 storiface.Data) (storiface.CallID, error) `perm:"admin"` - DownloadSectorData func(p0 context.Context, p1 storiface.SectorRef, p2 bool, p3 map[storiface.SectorFileType]storiface.SectorLocation) (storiface.CallID, error) `perm:"admin"` + DataCid func(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data) (storiface.CallID, error) `perm:"admin"` - Enabled func(p0 context.Context) (bool, error) `perm:"admin"` + DownloadSectorData func(p0 context.Context, p1 storiface.SectorRef, p2 bool, p3 map[storiface.SectorFileType]storiface.SectorLocation) (storiface.CallID, error) `perm:"admin"` - Fetch func(p0 context.Context, p1 storiface.SectorRef, p2 storiface.SectorFileType, p3 storiface.PathType, p4 storiface.AcquireMode) (storiface.CallID, error) `perm:"admin"` + Enabled func(p0 context.Context) (bool, error) `perm:"admin"` - FinalizeReplicaUpdate func(p0 context.Context, p1 storiface.SectorRef) (storiface.CallID, error) `perm:"admin"` + Fetch func(p0 context.Context, p1 storiface.SectorRef, p2 storiface.SectorFileType, p3 storiface.PathType, p4 storiface.AcquireMode) (storiface.CallID, error) `perm:"admin"` - FinalizeSector func(p0 context.Context, p1 storiface.SectorRef) (storiface.CallID, error) `perm:"admin"` + FinalizeReplicaUpdate func(p0 context.Context, p1 storiface.SectorRef) (storiface.CallID, error) `perm:"admin"` - GenerateSectorKeyFromData func(p0 context.Context, p1 storiface.SectorRef, p2 cid.Cid) (storiface.CallID, error) `perm:"admin"` + FinalizeSector func(p0 context.Context, p1 storiface.SectorRef) (storiface.CallID, error) `perm:"admin"` - GenerateWindowPoSt func(p0 context.Context, p1 abi.RegisteredPoStProof, p2 abi.ActorID, p3 []storiface.PostSectorChallenge, p4 int, p5 abi.PoStRandomness) (storiface.WindowPoStResult, error) `perm:"admin"` + GenerateSectorKeyFromData func(p0 context.Context, p1 storiface.SectorRef, p2 cid.Cid) (storiface.CallID, error) `perm:"admin"` - GenerateWinningPoSt func(p0 context.Context, p1 abi.RegisteredPoStProof, p2 abi.ActorID, p3 []storiface.PostSectorChallenge, p4 abi.PoStRandomness) ([]proof.PoStProof, error) `perm:"admin"` + GenerateWindowPoSt func(p0 context.Context, p1 abi.RegisteredPoStProof, p2 abi.ActorID, p3 []storiface.PostSectorChallenge, p4 int, p5 abi.PoStRandomness) (storiface.WindowPoStResult, error) `perm:"admin"` - Info func(p0 context.Context) (storiface.WorkerInfo, error) `perm:"admin"` + GenerateWinningPoSt func(p0 context.Context, p1 abi.RegisteredPoStProof, p2 abi.ActorID, p3 []storiface.PostSectorChallenge, p4 abi.PoStRandomness) ([]proof.PoStProof, error) `perm:"admin"` - MoveStorage func(p0 context.Context, p1 storiface.SectorRef, p2 storiface.SectorFileType) (storiface.CallID, error) `perm:"admin"` + Info func(p0 context.Context) (storiface.WorkerInfo, error) `perm:"admin"` - Paths func(p0 context.Context) ([]storiface.StoragePath, error) `perm:"admin"` + MoveStorage func(p0 context.Context, p1 storiface.SectorRef, p2 storiface.SectorFileType) (storiface.CallID, error) `perm:"admin"` - ProcessSession func(p0 context.Context) (uuid.UUID, error) `perm:"admin"` + Paths func(p0 context.Context) ([]storiface.StoragePath, error) `perm:"admin"` - ProveReplicaUpdate1 func(p0 context.Context, p1 storiface.SectorRef, p2 cid.Cid, p3 cid.Cid, p4 cid.Cid) (storiface.CallID, error) `perm:"admin"` + ProcessSession func(p0 context.Context) (uuid.UUID, error) `perm:"admin"` - ProveReplicaUpdate2 func(p0 context.Context, p1 storiface.SectorRef, p2 cid.Cid, p3 cid.Cid, p4 cid.Cid, p5 storiface.ReplicaVanillaProofs) (storiface.CallID, error) `perm:"admin"` + ProveReplicaUpdate1 func(p0 context.Context, p1 storiface.SectorRef, p2 cid.Cid, p3 cid.Cid, p4 cid.Cid) (storiface.CallID, error) `perm:"admin"` - ReleaseUnsealed func(p0 context.Context, p1 storiface.SectorRef, p2 []storiface.Range) (storiface.CallID, error) `perm:"admin"` + ProveReplicaUpdate2 func(p0 context.Context, p1 storiface.SectorRef, p2 cid.Cid, p3 cid.Cid, p4 cid.Cid, p5 storiface.ReplicaVanillaProofs) (storiface.CallID, error) `perm:"admin"` - Remove func(p0 context.Context, p1 abi.SectorID) error `perm:"admin"` + ReleaseUnsealed func(p0 context.Context, p1 storiface.SectorRef, p2 []storiface.Range) (storiface.CallID, error) `perm:"admin"` - ReplicaUpdate func(p0 context.Context, p1 storiface.SectorRef, p2 []abi.PieceInfo) (storiface.CallID, error) `perm:"admin"` + Remove func(p0 context.Context, p1 abi.SectorID) error `perm:"admin"` - SealCommit1 func(p0 context.Context, p1 storiface.SectorRef, p2 abi.SealRandomness, p3 abi.InteractiveSealRandomness, p4 []abi.PieceInfo, p5 storiface.SectorCids) (storiface.CallID, error) `perm:"admin"` + ReplicaUpdate func(p0 context.Context, p1 storiface.SectorRef, p2 []abi.PieceInfo) (storiface.CallID, error) `perm:"admin"` - SealCommit2 func(p0 context.Context, p1 storiface.SectorRef, p2 storiface.Commit1Out) (storiface.CallID, error) `perm:"admin"` + SealCommit1 func(p0 context.Context, p1 storiface.SectorRef, p2 abi.SealRandomness, p3 abi.InteractiveSealRandomness, p4 []abi.PieceInfo, p5 storiface.SectorCids) (storiface.CallID, error) `perm:"admin"` - SealPreCommit1 func(p0 context.Context, p1 storiface.SectorRef, p2 abi.SealRandomness, p3 []abi.PieceInfo) (storiface.CallID, error) `perm:"admin"` + SealCommit2 func(p0 context.Context, p1 storiface.SectorRef, p2 storiface.Commit1Out) (storiface.CallID, error) `perm:"admin"` - SealPreCommit2 func(p0 context.Context, p1 storiface.SectorRef, p2 storiface.PreCommit1Out) (storiface.CallID, error) `perm:"admin"` + SealPreCommit1 func(p0 context.Context, p1 storiface.SectorRef, p2 abi.SealRandomness, p3 []abi.PieceInfo) (storiface.CallID, error) `perm:"admin"` - Session func(p0 context.Context) (uuid.UUID, error) `perm:"admin"` + SealPreCommit2 func(p0 context.Context, p1 storiface.SectorRef, p2 storiface.PreCommit1Out) (storiface.CallID, error) `perm:"admin"` - SetEnabled func(p0 context.Context, p1 bool) error `perm:"admin"` + Session func(p0 context.Context) (uuid.UUID, error) `perm:"admin"` - Shutdown func(p0 context.Context) error `perm:"admin"` + SetEnabled func(p0 context.Context, p1 bool) error `perm:"admin"` - StorageAddLocal func(p0 context.Context, p1 string) error `perm:"admin"` + Shutdown func(p0 context.Context) error `perm:"admin"` - StorageDetachAll func(p0 context.Context) error `perm:"admin"` + StorageAddLocal func(p0 context.Context, p1 string) error `perm:"admin"` - StorageDetachLocal func(p0 context.Context, p1 string) error `perm:"admin"` + StorageDetachAll func(p0 context.Context) error `perm:"admin"` - StorageLocal func(p0 context.Context) (map[storiface.ID]string, error) `perm:"admin"` + StorageDetachLocal func(p0 context.Context, p1 string) error `perm:"admin"` - StorageRedeclareLocal func(p0 context.Context, p1 *storiface.ID, p2 bool) error `perm:"admin"` + StorageLocal func(p0 context.Context) (map[storiface.ID]string, error) `perm:"admin"` - TaskDisable func(p0 context.Context, p1 sealtasks.TaskType) error `perm:"admin"` + StorageRedeclareLocal func(p0 context.Context, p1 *storiface.ID, p2 bool) error `perm:"admin"` - TaskEnable func(p0 context.Context, p1 sealtasks.TaskType) error `perm:"admin"` + TaskDisable func(p0 context.Context, p1 sealtasks.TaskType) error `perm:"admin"` - TaskTypes func(p0 context.Context) (map[sealtasks.TaskType]struct{}, error) `perm:"admin"` + TaskEnable func(p0 context.Context, p1 sealtasks.TaskType) error `perm:"admin"` - UnsealPiece func(p0 context.Context, p1 storiface.SectorRef, p2 storiface.UnpaddedByteIndex, p3 abi.UnpaddedPieceSize, p4 abi.SealRandomness, p5 cid.Cid) (storiface.CallID, error) `perm:"admin"` + TaskTypes func(p0 context.Context) (map[sealtasks.TaskType]struct{}, error) `perm:"admin"` - Version func(p0 context.Context) (Version, error) `perm:"admin"` + UnsealPiece func(p0 context.Context, p1 storiface.SectorRef, p2 storiface.UnpaddedByteIndex, p3 abi.UnpaddedPieceSize, p4 abi.SealRandomness, p5 cid.Cid) (storiface.CallID, error) `perm:"admin"` - WaitQuiet func(p0 context.Context) error `perm:"admin"` - } + Version func(p0 context.Context) (Version, error) `perm:"admin"` + + WaitQuiet func(p0 context.Context) error `perm:"admin"` } type WorkerStub struct { @@ -1342,6 +1372,17 @@ func (s *CommonStub) Version(p0 context.Context) (APIVersion, error) { return *new(APIVersion), ErrNotSupported } +func (s *EthSubscriberStruct) EthSubscription(p0 context.Context, p1 jsonrpc.RawParams) error { + if s.Internal.EthSubscription == nil { + return ErrNotSupported + } + return s.Internal.EthSubscription(p0, p1) +} + +func (s *EthSubscriberStub) EthSubscription(p0 context.Context, p1 jsonrpc.RawParams) error { + return ErrNotSupported +} + func (s *FullNodeStruct) ChainBlockstoreInfo(p0 context.Context) (map[string]interface{}, error) { if s.Internal.ChainBlockstoreInfo == nil { return *new(map[string]interface{}), ErrNotSupported @@ -2277,15 +2318,15 @@ func (s *FullNodeStub) EthSendRawTransaction(p0 context.Context, p1 ethtypes.Eth return *new(ethtypes.EthHash), ErrNotSupported } -func (s *FullNodeStruct) EthSubscribe(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) { +func (s *FullNodeStruct) EthSubscribe(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) { if s.Internal.EthSubscribe == nil { - return nil, ErrNotSupported + return *new(ethtypes.EthSubscriptionID), ErrNotSupported } return s.Internal.EthSubscribe(p0, p1, p2) } -func (s *FullNodeStub) EthSubscribe(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) { - return nil, ErrNotSupported +func (s *FullNodeStub) EthSubscribe(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) { + return *new(ethtypes.EthSubscriptionID), ErrNotSupported } func (s *FullNodeStruct) EthUninstallFilter(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) { @@ -6955,6 +6996,7 @@ func (s *WorkerStub) WaitQuiet(p0 context.Context) error { var _ ChainIO = new(ChainIOStruct) var _ Common = new(CommonStruct) var _ CommonNet = new(CommonNetStruct) +var _ EthSubscriber = new(EthSubscriberStruct) var _ FullNode = new(FullNodeStruct) var _ Gateway = new(GatewayStruct) var _ Net = new(NetStruct) diff --git a/api/v0api/proxy_gen.go b/api/v0api/proxy_gen.go index b1a07dacc..f66588b8e 100644 --- a/api/v0api/proxy_gen.go +++ b/api/v0api/proxy_gen.go @@ -5,11 +5,6 @@ package v0api import ( "context" - blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" - "github.com/libp2p/go-libp2p/core/peer" - "golang.org/x/xerrors" - "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-bitfield" datatransfer "github.com/filecoin-project/go-data-transfer" @@ -22,7 +17,6 @@ import ( "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/dline" abinetwork "github.com/filecoin-project/go-state-types/network" - "github.com/filecoin-project/lotus/api" apitypes "github.com/filecoin-project/lotus/api/types" lminer "github.com/filecoin-project/lotus/chain/actors/builtin/miner" @@ -30,6 +24,10 @@ import ( marketevents "github.com/filecoin-project/lotus/markets/loggers" "github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/lotus/node/repo/imports" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + "github.com/libp2p/go-libp2p/core/peer" + "golang.org/x/xerrors" ) var ErrNotSupported = xerrors.New("method not supported") @@ -39,383 +37,385 @@ type FullNodeStruct struct { NetStruct - Internal struct { - BeaconGetEntry func(p0 context.Context, p1 abi.ChainEpoch) (*types.BeaconEntry, error) `perm:"read"` + Internal FullNodeMethods +} - ChainDeleteObj func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` +type FullNodeMethods struct { + BeaconGetEntry func(p0 context.Context, p1 abi.ChainEpoch) (*types.BeaconEntry, error) `perm:"read"` - ChainExport func(p0 context.Context, p1 abi.ChainEpoch, p2 bool, p3 types.TipSetKey) (<-chan []byte, error) `perm:"read"` + ChainDeleteObj func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` - ChainGetBlock func(p0 context.Context, p1 cid.Cid) (*types.BlockHeader, error) `perm:"read"` + ChainExport func(p0 context.Context, p1 abi.ChainEpoch, p2 bool, p3 types.TipSetKey) (<-chan []byte, error) `perm:"read"` - ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*api.BlockMessages, error) `perm:"read"` + ChainGetBlock func(p0 context.Context, p1 cid.Cid) (*types.BlockHeader, error) `perm:"read"` - ChainGetGenesis func(p0 context.Context) (*types.TipSet, error) `perm:"read"` + ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*api.BlockMessages, error) `perm:"read"` - ChainGetMessage func(p0 context.Context, p1 cid.Cid) (*types.Message, error) `perm:"read"` + ChainGetGenesis func(p0 context.Context) (*types.TipSet, error) `perm:"read"` - ChainGetMessagesInTipset func(p0 context.Context, p1 types.TipSetKey) ([]api.Message, error) `perm:"read"` + ChainGetMessage func(p0 context.Context, p1 cid.Cid) (*types.Message, error) `perm:"read"` - ChainGetNode func(p0 context.Context, p1 string) (*api.IpldObject, error) `perm:"read"` + ChainGetMessagesInTipset func(p0 context.Context, p1 types.TipSetKey) ([]api.Message, error) `perm:"read"` - ChainGetParentMessages func(p0 context.Context, p1 cid.Cid) ([]api.Message, error) `perm:"read"` + ChainGetNode func(p0 context.Context, p1 string) (*api.IpldObject, error) `perm:"read"` - ChainGetParentReceipts func(p0 context.Context, p1 cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"` + ChainGetParentMessages func(p0 context.Context, p1 cid.Cid) ([]api.Message, error) `perm:"read"` - ChainGetPath func(p0 context.Context, p1 types.TipSetKey, p2 types.TipSetKey) ([]*api.HeadChange, error) `perm:"read"` + ChainGetParentReceipts func(p0 context.Context, p1 cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"` - ChainGetRandomnessFromBeacon func(p0 context.Context, p1 types.TipSetKey, p2 crypto.DomainSeparationTag, p3 abi.ChainEpoch, p4 []byte) (abi.Randomness, error) `perm:"read"` + ChainGetPath func(p0 context.Context, p1 types.TipSetKey, p2 types.TipSetKey) ([]*api.HeadChange, error) `perm:"read"` - ChainGetRandomnessFromTickets func(p0 context.Context, p1 types.TipSetKey, p2 crypto.DomainSeparationTag, p3 abi.ChainEpoch, p4 []byte) (abi.Randomness, error) `perm:"read"` + ChainGetRandomnessFromBeacon func(p0 context.Context, p1 types.TipSetKey, p2 crypto.DomainSeparationTag, p3 abi.ChainEpoch, p4 []byte) (abi.Randomness, error) `perm:"read"` - ChainGetTipSet func(p0 context.Context, p1 types.TipSetKey) (*types.TipSet, error) `perm:"read"` + ChainGetRandomnessFromTickets func(p0 context.Context, p1 types.TipSetKey, p2 crypto.DomainSeparationTag, p3 abi.ChainEpoch, p4 []byte) (abi.Randomness, error) `perm:"read"` - ChainGetTipSetByHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) `perm:"read"` + ChainGetTipSet func(p0 context.Context, p1 types.TipSetKey) (*types.TipSet, error) `perm:"read"` - ChainHasObj func(p0 context.Context, p1 cid.Cid) (bool, error) `perm:"read"` + ChainGetTipSetByHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) `perm:"read"` - ChainHead func(p0 context.Context) (*types.TipSet, error) `perm:"read"` + ChainHasObj func(p0 context.Context, p1 cid.Cid) (bool, error) `perm:"read"` - ChainNotify func(p0 context.Context) (<-chan []*api.HeadChange, error) `perm:"read"` + ChainHead func(p0 context.Context) (*types.TipSet, error) `perm:"read"` - ChainPutObj func(p0 context.Context, p1 blocks.Block) error `` + ChainNotify func(p0 context.Context) (<-chan []*api.HeadChange, error) `perm:"read"` - ChainReadObj func(p0 context.Context, p1 cid.Cid) ([]byte, error) `perm:"read"` + ChainPutObj func(p0 context.Context, p1 blocks.Block) error `` - ChainSetHead func(p0 context.Context, p1 types.TipSetKey) error `perm:"admin"` + ChainReadObj func(p0 context.Context, p1 cid.Cid) ([]byte, error) `perm:"read"` - ChainStatObj func(p0 context.Context, p1 cid.Cid, p2 cid.Cid) (api.ObjStat, error) `perm:"read"` + ChainSetHead func(p0 context.Context, p1 types.TipSetKey) error `perm:"admin"` - ChainTipSetWeight func(p0 context.Context, p1 types.TipSetKey) (types.BigInt, error) `perm:"read"` + ChainStatObj func(p0 context.Context, p1 cid.Cid, p2 cid.Cid) (api.ObjStat, error) `perm:"read"` - ClientCalcCommP func(p0 context.Context, p1 string) (*api.CommPRet, error) `perm:"write"` + ChainTipSetWeight func(p0 context.Context, p1 types.TipSetKey) (types.BigInt, error) `perm:"read"` - ClientCancelDataTransfer func(p0 context.Context, p1 datatransfer.TransferID, p2 peer.ID, p3 bool) error `perm:"write"` + ClientCalcCommP func(p0 context.Context, p1 string) (*api.CommPRet, error) `perm:"write"` - ClientCancelRetrievalDeal func(p0 context.Context, p1 retrievalmarket.DealID) error `perm:"write"` + ClientCancelDataTransfer func(p0 context.Context, p1 datatransfer.TransferID, p2 peer.ID, p3 bool) error `perm:"write"` - ClientDataTransferUpdates func(p0 context.Context) (<-chan api.DataTransferChannel, error) `perm:"write"` + ClientCancelRetrievalDeal func(p0 context.Context, p1 retrievalmarket.DealID) error `perm:"write"` - ClientDealPieceCID func(p0 context.Context, p1 cid.Cid) (api.DataCIDSize, error) `perm:"read"` + ClientDataTransferUpdates func(p0 context.Context) (<-chan api.DataTransferChannel, error) `perm:"write"` - ClientDealSize func(p0 context.Context, p1 cid.Cid) (api.DataSize, error) `perm:"read"` + ClientDealPieceCID func(p0 context.Context, p1 cid.Cid) (api.DataCIDSize, error) `perm:"read"` - ClientFindData func(p0 context.Context, p1 cid.Cid, p2 *cid.Cid) ([]api.QueryOffer, error) `perm:"read"` + ClientDealSize func(p0 context.Context, p1 cid.Cid) (api.DataSize, error) `perm:"read"` - ClientGenCar func(p0 context.Context, p1 api.FileRef, p2 string) error `perm:"write"` + ClientFindData func(p0 context.Context, p1 cid.Cid, p2 *cid.Cid) ([]api.QueryOffer, error) `perm:"read"` - ClientGetDealInfo func(p0 context.Context, p1 cid.Cid) (*api.DealInfo, error) `perm:"read"` + ClientGenCar func(p0 context.Context, p1 api.FileRef, p2 string) error `perm:"write"` - ClientGetDealStatus func(p0 context.Context, p1 uint64) (string, error) `perm:"read"` + ClientGetDealInfo func(p0 context.Context, p1 cid.Cid) (*api.DealInfo, error) `perm:"read"` - ClientGetDealUpdates func(p0 context.Context) (<-chan api.DealInfo, error) `perm:"write"` + ClientGetDealStatus func(p0 context.Context, p1 uint64) (string, error) `perm:"read"` - ClientGetRetrievalUpdates func(p0 context.Context) (<-chan api.RetrievalInfo, error) `perm:"write"` + ClientGetDealUpdates func(p0 context.Context) (<-chan api.DealInfo, error) `perm:"write"` - ClientHasLocal func(p0 context.Context, p1 cid.Cid) (bool, error) `perm:"write"` + ClientGetRetrievalUpdates func(p0 context.Context) (<-chan api.RetrievalInfo, error) `perm:"write"` - ClientImport func(p0 context.Context, p1 api.FileRef) (*api.ImportRes, error) `perm:"admin"` + ClientHasLocal func(p0 context.Context, p1 cid.Cid) (bool, error) `perm:"write"` - ClientListDataTransfers func(p0 context.Context) ([]api.DataTransferChannel, error) `perm:"write"` + ClientImport func(p0 context.Context, p1 api.FileRef) (*api.ImportRes, error) `perm:"admin"` - ClientListDeals func(p0 context.Context) ([]api.DealInfo, error) `perm:"write"` + ClientListDataTransfers func(p0 context.Context) ([]api.DataTransferChannel, error) `perm:"write"` - ClientListImports func(p0 context.Context) ([]api.Import, error) `perm:"write"` + ClientListDeals func(p0 context.Context) ([]api.DealInfo, error) `perm:"write"` - ClientListRetrievals func(p0 context.Context) ([]api.RetrievalInfo, error) `perm:"write"` + ClientListImports func(p0 context.Context) ([]api.Import, error) `perm:"write"` - ClientMinerQueryOffer func(p0 context.Context, p1 address.Address, p2 cid.Cid, p3 *cid.Cid) (api.QueryOffer, error) `perm:"read"` + ClientListRetrievals func(p0 context.Context) ([]api.RetrievalInfo, error) `perm:"write"` - ClientQueryAsk func(p0 context.Context, p1 peer.ID, p2 address.Address) (*storagemarket.StorageAsk, error) `perm:"read"` + ClientMinerQueryOffer func(p0 context.Context, p1 address.Address, p2 cid.Cid, p3 *cid.Cid) (api.QueryOffer, error) `perm:"read"` - ClientRemoveImport func(p0 context.Context, p1 imports.ID) error `perm:"admin"` + ClientQueryAsk func(p0 context.Context, p1 peer.ID, p2 address.Address) (*storagemarket.StorageAsk, error) `perm:"read"` - ClientRestartDataTransfer func(p0 context.Context, p1 datatransfer.TransferID, p2 peer.ID, p3 bool) error `perm:"write"` + ClientRemoveImport func(p0 context.Context, p1 imports.ID) error `perm:"admin"` - ClientRetrieve func(p0 context.Context, p1 RetrievalOrder, p2 *api.FileRef) error `perm:"admin"` + ClientRestartDataTransfer func(p0 context.Context, p1 datatransfer.TransferID, p2 peer.ID, p3 bool) error `perm:"write"` - ClientRetrieveTryRestartInsufficientFunds func(p0 context.Context, p1 address.Address) error `perm:"write"` + ClientRetrieve func(p0 context.Context, p1 RetrievalOrder, p2 *api.FileRef) error `perm:"admin"` - ClientRetrieveWithEvents func(p0 context.Context, p1 RetrievalOrder, p2 *api.FileRef) (<-chan marketevents.RetrievalEvent, error) `perm:"admin"` + ClientRetrieveTryRestartInsufficientFunds func(p0 context.Context, p1 address.Address) error `perm:"write"` - ClientStartDeal func(p0 context.Context, p1 *api.StartDealParams) (*cid.Cid, error) `perm:"admin"` + ClientRetrieveWithEvents func(p0 context.Context, p1 RetrievalOrder, p2 *api.FileRef) (<-chan marketevents.RetrievalEvent, error) `perm:"admin"` - ClientStatelessDeal func(p0 context.Context, p1 *api.StartDealParams) (*cid.Cid, error) `perm:"write"` + ClientStartDeal func(p0 context.Context, p1 *api.StartDealParams) (*cid.Cid, error) `perm:"admin"` - CreateBackup func(p0 context.Context, p1 string) error `perm:"admin"` + ClientStatelessDeal func(p0 context.Context, p1 *api.StartDealParams) (*cid.Cid, error) `perm:"write"` - GasEstimateFeeCap func(p0 context.Context, p1 *types.Message, p2 int64, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` + CreateBackup func(p0 context.Context, p1 string) error `perm:"admin"` - GasEstimateGasLimit func(p0 context.Context, p1 *types.Message, p2 types.TipSetKey) (int64, error) `perm:"read"` + GasEstimateFeeCap func(p0 context.Context, p1 *types.Message, p2 int64, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` - GasEstimateGasPremium func(p0 context.Context, p1 uint64, p2 address.Address, p3 int64, p4 types.TipSetKey) (types.BigInt, error) `perm:"read"` + GasEstimateGasLimit func(p0 context.Context, p1 *types.Message, p2 types.TipSetKey) (int64, error) `perm:"read"` - GasEstimateMessageGas func(p0 context.Context, p1 *types.Message, p2 *api.MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) `perm:"read"` + GasEstimateGasPremium func(p0 context.Context, p1 uint64, p2 address.Address, p3 int64, p4 types.TipSetKey) (types.BigInt, error) `perm:"read"` - MarketAddBalance func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) `perm:"sign"` + GasEstimateMessageGas func(p0 context.Context, p1 *types.Message, p2 *api.MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) `perm:"read"` - MarketGetReserved func(p0 context.Context, p1 address.Address) (types.BigInt, error) `perm:"sign"` + MarketAddBalance func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) `perm:"sign"` - MarketReleaseFunds func(p0 context.Context, p1 address.Address, p2 types.BigInt) error `perm:"sign"` + MarketGetReserved func(p0 context.Context, p1 address.Address) (types.BigInt, error) `perm:"sign"` - MarketReserveFunds func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) `perm:"sign"` + MarketReleaseFunds func(p0 context.Context, p1 address.Address, p2 types.BigInt) error `perm:"sign"` - MarketWithdraw func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) `perm:"sign"` + MarketReserveFunds func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) `perm:"sign"` - MinerCreateBlock func(p0 context.Context, p1 *api.BlockTemplate) (*types.BlockMsg, error) `perm:"write"` + MarketWithdraw func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) `perm:"sign"` - MinerGetBaseInfo func(p0 context.Context, p1 address.Address, p2 abi.ChainEpoch, p3 types.TipSetKey) (*api.MiningBaseInfo, error) `perm:"read"` + MinerCreateBlock func(p0 context.Context, p1 *api.BlockTemplate) (*types.BlockMsg, error) `perm:"write"` - MpoolBatchPush func(p0 context.Context, p1 []*types.SignedMessage) ([]cid.Cid, error) `perm:"write"` + MinerGetBaseInfo func(p0 context.Context, p1 address.Address, p2 abi.ChainEpoch, p3 types.TipSetKey) (*api.MiningBaseInfo, error) `perm:"read"` - MpoolBatchPushMessage func(p0 context.Context, p1 []*types.Message, p2 *api.MessageSendSpec) ([]*types.SignedMessage, error) `perm:"sign"` + MpoolBatchPush func(p0 context.Context, p1 []*types.SignedMessage) ([]cid.Cid, error) `perm:"write"` - MpoolBatchPushUntrusted func(p0 context.Context, p1 []*types.SignedMessage) ([]cid.Cid, error) `perm:"write"` + MpoolBatchPushMessage func(p0 context.Context, p1 []*types.Message, p2 *api.MessageSendSpec) ([]*types.SignedMessage, error) `perm:"sign"` - MpoolClear func(p0 context.Context, p1 bool) error `perm:"write"` + MpoolBatchPushUntrusted func(p0 context.Context, p1 []*types.SignedMessage) ([]cid.Cid, error) `perm:"write"` - MpoolGetConfig func(p0 context.Context) (*types.MpoolConfig, error) `perm:"read"` + MpoolClear func(p0 context.Context, p1 bool) error `perm:"write"` - MpoolGetNonce func(p0 context.Context, p1 address.Address) (uint64, error) `perm:"read"` + MpoolGetConfig func(p0 context.Context) (*types.MpoolConfig, error) `perm:"read"` - MpoolPending func(p0 context.Context, p1 types.TipSetKey) ([]*types.SignedMessage, error) `perm:"read"` + MpoolGetNonce func(p0 context.Context, p1 address.Address) (uint64, error) `perm:"read"` - MpoolPush func(p0 context.Context, p1 *types.SignedMessage) (cid.Cid, error) `perm:"write"` + MpoolPending func(p0 context.Context, p1 types.TipSetKey) ([]*types.SignedMessage, error) `perm:"read"` - MpoolPushMessage func(p0 context.Context, p1 *types.Message, p2 *api.MessageSendSpec) (*types.SignedMessage, error) `perm:"sign"` + MpoolPush func(p0 context.Context, p1 *types.SignedMessage) (cid.Cid, error) `perm:"write"` - MpoolPushUntrusted func(p0 context.Context, p1 *types.SignedMessage) (cid.Cid, error) `perm:"write"` + MpoolPushMessage func(p0 context.Context, p1 *types.Message, p2 *api.MessageSendSpec) (*types.SignedMessage, error) `perm:"sign"` - MpoolSelect func(p0 context.Context, p1 types.TipSetKey, p2 float64) ([]*types.SignedMessage, error) `perm:"read"` + MpoolPushUntrusted func(p0 context.Context, p1 *types.SignedMessage) (cid.Cid, error) `perm:"write"` - MpoolSetConfig func(p0 context.Context, p1 *types.MpoolConfig) error `perm:"admin"` + MpoolSelect func(p0 context.Context, p1 types.TipSetKey, p2 float64) ([]*types.SignedMessage, error) `perm:"read"` - MpoolSub func(p0 context.Context) (<-chan api.MpoolUpdate, error) `perm:"read"` + MpoolSetConfig func(p0 context.Context, p1 *types.MpoolConfig) error `perm:"admin"` - MsigAddApprove func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (cid.Cid, error) `perm:"sign"` + MpoolSub func(p0 context.Context) (<-chan api.MpoolUpdate, error) `perm:"read"` - MsigAddCancel func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (cid.Cid, error) `perm:"sign"` + MsigAddApprove func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (cid.Cid, error) `perm:"sign"` - MsigAddPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (cid.Cid, error) `perm:"sign"` + MsigAddCancel func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (cid.Cid, error) `perm:"sign"` - MsigApprove func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (cid.Cid, error) `perm:"sign"` + MsigAddPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (cid.Cid, error) `perm:"sign"` - MsigApproveTxnHash func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (cid.Cid, error) `perm:"sign"` + MsigApprove func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (cid.Cid, error) `perm:"sign"` - MsigCancel func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (cid.Cid, error) `perm:"sign"` + MsigApproveTxnHash func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (cid.Cid, error) `perm:"sign"` - MsigCreate func(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (cid.Cid, error) `perm:"sign"` + MsigCancel func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (cid.Cid, error) `perm:"sign"` - MsigGetAvailableBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) `perm:"read"` + MsigCreate func(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (cid.Cid, error) `perm:"sign"` - MsigGetPending func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]*api.MsigTransaction, error) `perm:"read"` + MsigGetAvailableBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) `perm:"read"` - MsigGetVested func(p0 context.Context, p1 address.Address, p2 types.TipSetKey, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` + MsigGetPending func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]*api.MsigTransaction, error) `perm:"read"` - MsigGetVestingSchedule func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (api.MsigVesting, error) `perm:"read"` + MsigGetVested func(p0 context.Context, p1 address.Address, p2 types.TipSetKey, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` - MsigPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (cid.Cid, error) `perm:"sign"` + MsigGetVestingSchedule func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (api.MsigVesting, error) `perm:"read"` - MsigRemoveSigner func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (cid.Cid, error) `perm:"sign"` + MsigPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (cid.Cid, error) `perm:"sign"` - MsigSwapApprove func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (cid.Cid, error) `perm:"sign"` + MsigRemoveSigner func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (cid.Cid, error) `perm:"sign"` - MsigSwapCancel func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (cid.Cid, error) `perm:"sign"` + MsigSwapApprove func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (cid.Cid, error) `perm:"sign"` - MsigSwapPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (cid.Cid, error) `perm:"sign"` + MsigSwapCancel func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (cid.Cid, error) `perm:"sign"` - PaychAllocateLane func(p0 context.Context, p1 address.Address) (uint64, error) `perm:"sign"` + MsigSwapPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (cid.Cid, error) `perm:"sign"` - PaychAvailableFunds func(p0 context.Context, p1 address.Address) (*api.ChannelAvailableFunds, error) `perm:"sign"` + PaychAllocateLane func(p0 context.Context, p1 address.Address) (uint64, error) `perm:"sign"` - PaychAvailableFundsByFromTo func(p0 context.Context, p1 address.Address, p2 address.Address) (*api.ChannelAvailableFunds, error) `perm:"sign"` + PaychAvailableFunds func(p0 context.Context, p1 address.Address) (*api.ChannelAvailableFunds, error) `perm:"sign"` - PaychCollect func(p0 context.Context, p1 address.Address) (cid.Cid, error) `perm:"sign"` + PaychAvailableFundsByFromTo func(p0 context.Context, p1 address.Address, p2 address.Address) (*api.ChannelAvailableFunds, error) `perm:"sign"` - PaychGet func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (*api.ChannelInfo, error) `perm:"sign"` + PaychCollect func(p0 context.Context, p1 address.Address) (cid.Cid, error) `perm:"sign"` - PaychGetWaitReady func(p0 context.Context, p1 cid.Cid) (address.Address, error) `perm:"sign"` + PaychGet func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (*api.ChannelInfo, error) `perm:"sign"` - PaychList func(p0 context.Context) ([]address.Address, error) `perm:"read"` + PaychGetWaitReady func(p0 context.Context, p1 cid.Cid) (address.Address, error) `perm:"sign"` - PaychNewPayment func(p0 context.Context, p1 address.Address, p2 address.Address, p3 []api.VoucherSpec) (*api.PaymentInfo, error) `perm:"sign"` + PaychList func(p0 context.Context) ([]address.Address, error) `perm:"read"` - PaychSettle func(p0 context.Context, p1 address.Address) (cid.Cid, error) `perm:"sign"` + PaychNewPayment func(p0 context.Context, p1 address.Address, p2 address.Address, p3 []api.VoucherSpec) (*api.PaymentInfo, error) `perm:"sign"` - PaychStatus func(p0 context.Context, p1 address.Address) (*api.PaychStatus, error) `perm:"read"` + PaychSettle func(p0 context.Context, p1 address.Address) (cid.Cid, error) `perm:"sign"` - PaychVoucherAdd func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher, p3 []byte, p4 types.BigInt) (types.BigInt, error) `perm:"write"` + PaychStatus func(p0 context.Context, p1 address.Address) (*api.PaychStatus, error) `perm:"read"` - PaychVoucherCheckSpendable func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher, p3 []byte, p4 []byte) (bool, error) `perm:"read"` + PaychVoucherAdd func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher, p3 []byte, p4 types.BigInt) (types.BigInt, error) `perm:"write"` - PaychVoucherCheckValid func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher) error `perm:"read"` + PaychVoucherCheckSpendable func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher, p3 []byte, p4 []byte) (bool, error) `perm:"read"` - PaychVoucherCreate func(p0 context.Context, p1 address.Address, p2 types.BigInt, p3 uint64) (*api.VoucherCreateResult, error) `perm:"sign"` + PaychVoucherCheckValid func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher) error `perm:"read"` - PaychVoucherList func(p0 context.Context, p1 address.Address) ([]*paych.SignedVoucher, error) `perm:"write"` + PaychVoucherCreate func(p0 context.Context, p1 address.Address, p2 types.BigInt, p3 uint64) (*api.VoucherCreateResult, error) `perm:"sign"` - PaychVoucherSubmit func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher, p3 []byte, p4 []byte) (cid.Cid, error) `perm:"sign"` + PaychVoucherList func(p0 context.Context, p1 address.Address) ([]*paych.SignedVoucher, error) `perm:"write"` - StateAccountKey func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `perm:"read"` + PaychVoucherSubmit func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher, p3 []byte, p4 []byte) (cid.Cid, error) `perm:"sign"` - StateActorCodeCIDs func(p0 context.Context, p1 abinetwork.Version) (map[string]cid.Cid, error) `perm:"read"` + StateAccountKey func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `perm:"read"` - StateActorManifestCID func(p0 context.Context, p1 abinetwork.Version) (cid.Cid, error) `perm:"read"` + StateActorCodeCIDs func(p0 context.Context, p1 abinetwork.Version) (map[string]cid.Cid, error) `perm:"read"` - StateAllMinerFaults func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) ([]*api.Fault, error) `perm:"read"` + StateActorManifestCID func(p0 context.Context, p1 abinetwork.Version) (cid.Cid, error) `perm:"read"` - StateCall func(p0 context.Context, p1 *types.Message, p2 types.TipSetKey) (*api.InvocResult, error) `perm:"read"` + StateAllMinerFaults func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) ([]*api.Fault, error) `perm:"read"` - StateChangedActors func(p0 context.Context, p1 cid.Cid, p2 cid.Cid) (map[string]types.Actor, error) `perm:"read"` + StateCall func(p0 context.Context, p1 *types.Message, p2 types.TipSetKey) (*api.InvocResult, error) `perm:"read"` - StateCirculatingSupply func(p0 context.Context, p1 types.TipSetKey) (abi.TokenAmount, error) `perm:"read"` + StateChangedActors func(p0 context.Context, p1 cid.Cid, p2 cid.Cid) (map[string]types.Actor, error) `perm:"read"` - StateCompute func(p0 context.Context, p1 abi.ChainEpoch, p2 []*types.Message, p3 types.TipSetKey) (*api.ComputeStateOutput, error) `perm:"read"` + StateCirculatingSupply func(p0 context.Context, p1 types.TipSetKey) (abi.TokenAmount, error) `perm:"read"` - StateDealProviderCollateralBounds func(p0 context.Context, p1 abi.PaddedPieceSize, p2 bool, p3 types.TipSetKey) (api.DealCollateralBounds, error) `perm:"read"` + StateCompute func(p0 context.Context, p1 abi.ChainEpoch, p2 []*types.Message, p3 types.TipSetKey) (*api.ComputeStateOutput, error) `perm:"read"` - StateDecodeParams func(p0 context.Context, p1 address.Address, p2 abi.MethodNum, p3 []byte, p4 types.TipSetKey) (interface{}, error) `perm:"read"` + StateDealProviderCollateralBounds func(p0 context.Context, p1 abi.PaddedPieceSize, p2 bool, p3 types.TipSetKey) (api.DealCollateralBounds, error) `perm:"read"` - StateGetActor func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) `perm:"read"` + StateDecodeParams func(p0 context.Context, p1 address.Address, p2 abi.MethodNum, p3 []byte, p4 types.TipSetKey) (interface{}, error) `perm:"read"` - StateGetAllocation func(p0 context.Context, p1 address.Address, p2 verifregtypes.AllocationId, p3 types.TipSetKey) (*verifregtypes.Allocation, error) `perm:"read"` + StateGetActor func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) `perm:"read"` - StateGetAllocationForPendingDeal func(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*verifregtypes.Allocation, error) `perm:"read"` + StateGetAllocation func(p0 context.Context, p1 address.Address, p2 verifregtypes.AllocationId, p3 types.TipSetKey) (*verifregtypes.Allocation, error) `perm:"read"` - StateGetAllocations func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) `perm:"read"` + StateGetAllocationForPendingDeal func(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*verifregtypes.Allocation, error) `perm:"read"` - StateGetClaim func(p0 context.Context, p1 address.Address, p2 verifregtypes.ClaimId, p3 types.TipSetKey) (*verifregtypes.Claim, error) `perm:"read"` + StateGetAllocations func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) `perm:"read"` - StateGetClaims func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (map[verifregtypes.ClaimId]verifregtypes.Claim, error) `perm:"read"` + StateGetClaim func(p0 context.Context, p1 address.Address, p2 verifregtypes.ClaimId, p3 types.TipSetKey) (*verifregtypes.Claim, error) `perm:"read"` - StateGetNetworkParams func(p0 context.Context) (*api.NetworkParams, error) `perm:"read"` + StateGetClaims func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (map[verifregtypes.ClaimId]verifregtypes.Claim, error) `perm:"read"` - StateGetRandomnessFromBeacon func(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) `perm:"read"` + StateGetNetworkParams func(p0 context.Context) (*api.NetworkParams, error) `perm:"read"` - StateGetRandomnessFromTickets func(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) `perm:"read"` + StateGetRandomnessFromBeacon func(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) `perm:"read"` - StateGetReceipt func(p0 context.Context, p1 cid.Cid, p2 types.TipSetKey) (*types.MessageReceipt, error) `perm:"read"` + StateGetRandomnessFromTickets func(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) `perm:"read"` - StateListActors func(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) `perm:"read"` + StateGetReceipt func(p0 context.Context, p1 cid.Cid, p2 types.TipSetKey) (*types.MessageReceipt, error) `perm:"read"` - StateListMessages func(p0 context.Context, p1 *api.MessageMatch, p2 types.TipSetKey, p3 abi.ChainEpoch) ([]cid.Cid, error) `perm:"read"` + StateListActors func(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) `perm:"read"` - StateListMiners func(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) `perm:"read"` + StateListMessages func(p0 context.Context, p1 *api.MessageMatch, p2 types.TipSetKey, p3 abi.ChainEpoch) ([]cid.Cid, error) `perm:"read"` - StateLookupID func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `perm:"read"` + StateListMiners func(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) `perm:"read"` - StateMarketBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (api.MarketBalance, error) `perm:"read"` + StateLookupID func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `perm:"read"` - StateMarketDeals func(p0 context.Context, p1 types.TipSetKey) (map[string]*api.MarketDeal, error) `perm:"read"` + StateMarketBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (api.MarketBalance, error) `perm:"read"` - StateMarketParticipants func(p0 context.Context, p1 types.TipSetKey) (map[string]api.MarketBalance, error) `perm:"read"` + StateMarketDeals func(p0 context.Context, p1 types.TipSetKey) (map[string]*api.MarketDeal, error) `perm:"read"` - StateMarketStorageDeal func(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*api.MarketDeal, error) `perm:"read"` + StateMarketParticipants func(p0 context.Context, p1 types.TipSetKey) (map[string]api.MarketBalance, error) `perm:"read"` - StateMinerActiveSectors func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]*miner.SectorOnChainInfo, error) `perm:"read"` + StateMarketStorageDeal func(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*api.MarketDeal, error) `perm:"read"` - StateMinerAvailableBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) `perm:"read"` + StateMinerActiveSectors func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]*miner.SectorOnChainInfo, error) `perm:"read"` - StateMinerDeadlines func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]api.Deadline, error) `perm:"read"` + StateMinerAvailableBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) `perm:"read"` - StateMinerFaults func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (bitfield.BitField, error) `perm:"read"` + StateMinerDeadlines func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]api.Deadline, error) `perm:"read"` - StateMinerInfo func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (api.MinerInfo, error) `perm:"read"` + StateMinerFaults func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (bitfield.BitField, error) `perm:"read"` - StateMinerInitialPledgeCollateral func(p0 context.Context, p1 address.Address, p2 miner.SectorPreCommitInfo, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` + StateMinerInfo func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (api.MinerInfo, error) `perm:"read"` - StateMinerPartitions func(p0 context.Context, p1 address.Address, p2 uint64, p3 types.TipSetKey) ([]api.Partition, error) `perm:"read"` + StateMinerInitialPledgeCollateral func(p0 context.Context, p1 address.Address, p2 miner.SectorPreCommitInfo, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` - StateMinerPower func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*api.MinerPower, error) `perm:"read"` + StateMinerPartitions func(p0 context.Context, p1 address.Address, p2 uint64, p3 types.TipSetKey) ([]api.Partition, error) `perm:"read"` - StateMinerPreCommitDepositForPower func(p0 context.Context, p1 address.Address, p2 miner.SectorPreCommitInfo, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` + StateMinerPower func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*api.MinerPower, error) `perm:"read"` - StateMinerProvingDeadline func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*dline.Info, error) `perm:"read"` + StateMinerPreCommitDepositForPower func(p0 context.Context, p1 address.Address, p2 miner.SectorPreCommitInfo, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` - StateMinerRecoveries func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (bitfield.BitField, error) `perm:"read"` + StateMinerProvingDeadline func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*dline.Info, error) `perm:"read"` - StateMinerSectorAllocated func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (bool, error) `perm:"read"` + StateMinerRecoveries func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (bitfield.BitField, error) `perm:"read"` - StateMinerSectorCount func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (api.MinerSectors, error) `perm:"read"` + StateMinerSectorAllocated func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (bool, error) `perm:"read"` - StateMinerSectors func(p0 context.Context, p1 address.Address, p2 *bitfield.BitField, p3 types.TipSetKey) ([]*miner.SectorOnChainInfo, error) `perm:"read"` + StateMinerSectorCount func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (api.MinerSectors, error) `perm:"read"` - StateNetworkName func(p0 context.Context) (dtypes.NetworkName, error) `perm:"read"` + StateMinerSectors func(p0 context.Context, p1 address.Address, p2 *bitfield.BitField, p3 types.TipSetKey) ([]*miner.SectorOnChainInfo, error) `perm:"read"` - StateNetworkVersion func(p0 context.Context, p1 types.TipSetKey) (apitypes.NetworkVersion, error) `perm:"read"` + StateNetworkName func(p0 context.Context) (dtypes.NetworkName, error) `perm:"read"` - StateReadState func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*api.ActorState, error) `perm:"read"` + StateNetworkVersion func(p0 context.Context, p1 types.TipSetKey) (apitypes.NetworkVersion, error) `perm:"read"` - StateReplay func(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid) (*api.InvocResult, error) `perm:"read"` + StateReadState func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*api.ActorState, error) `perm:"read"` - StateSearchMsg func(p0 context.Context, p1 cid.Cid) (*api.MsgLookup, error) `perm:"read"` + StateReplay func(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid) (*api.InvocResult, error) `perm:"read"` - StateSearchMsgLimited func(p0 context.Context, p1 cid.Cid, p2 abi.ChainEpoch) (*api.MsgLookup, error) `perm:"read"` + StateSearchMsg func(p0 context.Context, p1 cid.Cid) (*api.MsgLookup, error) `perm:"read"` - StateSectorExpiration func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*lminer.SectorExpiration, error) `perm:"read"` + StateSearchMsgLimited func(p0 context.Context, p1 cid.Cid, p2 abi.ChainEpoch) (*api.MsgLookup, error) `perm:"read"` - StateSectorGetInfo func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorOnChainInfo, error) `perm:"read"` + StateSectorExpiration func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*lminer.SectorExpiration, error) `perm:"read"` - StateSectorPartition func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*lminer.SectorLocation, error) `perm:"read"` + StateSectorGetInfo func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorOnChainInfo, error) `perm:"read"` - StateSectorPreCommitInfo func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (miner.SectorPreCommitOnChainInfo, error) `perm:"read"` + StateSectorPartition func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*lminer.SectorLocation, error) `perm:"read"` - StateVMCirculatingSupplyInternal func(p0 context.Context, p1 types.TipSetKey) (api.CirculatingSupply, error) `perm:"read"` + StateSectorPreCommitInfo func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (miner.SectorPreCommitOnChainInfo, error) `perm:"read"` - StateVerifiedClientStatus func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*abi.StoragePower, error) `perm:"read"` + StateVMCirculatingSupplyInternal func(p0 context.Context, p1 types.TipSetKey) (api.CirculatingSupply, error) `perm:"read"` - StateVerifiedRegistryRootKey func(p0 context.Context, p1 types.TipSetKey) (address.Address, error) `perm:"read"` + StateVerifiedClientStatus func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*abi.StoragePower, error) `perm:"read"` - StateVerifierStatus func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*abi.StoragePower, error) `perm:"read"` + StateVerifiedRegistryRootKey func(p0 context.Context, p1 types.TipSetKey) (address.Address, error) `perm:"read"` - StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64) (*api.MsgLookup, error) `perm:"read"` + StateVerifierStatus func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*abi.StoragePower, error) `perm:"read"` - StateWaitMsgLimited func(p0 context.Context, p1 cid.Cid, p2 uint64, p3 abi.ChainEpoch) (*api.MsgLookup, error) `perm:"read"` + StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64) (*api.MsgLookup, error) `perm:"read"` - SyncCheckBad func(p0 context.Context, p1 cid.Cid) (string, error) `perm:"read"` + StateWaitMsgLimited func(p0 context.Context, p1 cid.Cid, p2 uint64, p3 abi.ChainEpoch) (*api.MsgLookup, error) `perm:"read"` - SyncCheckpoint func(p0 context.Context, p1 types.TipSetKey) error `perm:"admin"` + SyncCheckBad func(p0 context.Context, p1 cid.Cid) (string, error) `perm:"read"` - SyncIncomingBlocks func(p0 context.Context) (<-chan *types.BlockHeader, error) `perm:"read"` + SyncCheckpoint func(p0 context.Context, p1 types.TipSetKey) error `perm:"admin"` - SyncMarkBad func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` + SyncIncomingBlocks func(p0 context.Context) (<-chan *types.BlockHeader, error) `perm:"read"` - SyncState func(p0 context.Context) (*api.SyncState, error) `perm:"read"` + SyncMarkBad func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` - SyncSubmitBlock func(p0 context.Context, p1 *types.BlockMsg) error `perm:"write"` + SyncState func(p0 context.Context) (*api.SyncState, error) `perm:"read"` - SyncUnmarkAllBad func(p0 context.Context) error `perm:"admin"` + SyncSubmitBlock func(p0 context.Context, p1 *types.BlockMsg) error `perm:"write"` - SyncUnmarkBad func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` + SyncUnmarkAllBad func(p0 context.Context) error `perm:"admin"` - SyncValidateTipset func(p0 context.Context, p1 types.TipSetKey) (bool, error) `perm:"read"` + SyncUnmarkBad func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` - WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) `perm:"read"` + SyncValidateTipset func(p0 context.Context, p1 types.TipSetKey) (bool, error) `perm:"read"` - WalletDefaultAddress func(p0 context.Context) (address.Address, error) `perm:"write"` + WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) `perm:"read"` - WalletDelete func(p0 context.Context, p1 address.Address) error `perm:"admin"` + WalletDefaultAddress func(p0 context.Context) (address.Address, error) `perm:"write"` - WalletExport func(p0 context.Context, p1 address.Address) (*types.KeyInfo, error) `perm:"admin"` + WalletDelete func(p0 context.Context, p1 address.Address) error `perm:"admin"` - WalletHas func(p0 context.Context, p1 address.Address) (bool, error) `perm:"write"` + WalletExport func(p0 context.Context, p1 address.Address) (*types.KeyInfo, error) `perm:"admin"` - WalletImport func(p0 context.Context, p1 *types.KeyInfo) (address.Address, error) `perm:"admin"` + WalletHas func(p0 context.Context, p1 address.Address) (bool, error) `perm:"write"` - WalletList func(p0 context.Context) ([]address.Address, error) `perm:"write"` + WalletImport func(p0 context.Context, p1 *types.KeyInfo) (address.Address, error) `perm:"admin"` - WalletNew func(p0 context.Context, p1 types.KeyType) (address.Address, error) `perm:"write"` + WalletList func(p0 context.Context) ([]address.Address, error) `perm:"write"` - WalletSetDefault func(p0 context.Context, p1 address.Address) error `perm:"write"` + WalletNew func(p0 context.Context, p1 types.KeyType) (address.Address, error) `perm:"write"` - WalletSign func(p0 context.Context, p1 address.Address, p2 []byte) (*crypto.Signature, error) `perm:"sign"` + WalletSetDefault func(p0 context.Context, p1 address.Address) error `perm:"write"` - WalletSignMessage func(p0 context.Context, p1 address.Address, p2 *types.Message) (*types.SignedMessage, error) `perm:"sign"` + WalletSign func(p0 context.Context, p1 address.Address, p2 []byte) (*crypto.Signature, error) `perm:"sign"` - WalletValidateAddress func(p0 context.Context, p1 string) (address.Address, error) `perm:"read"` + WalletSignMessage func(p0 context.Context, p1 address.Address, p2 *types.Message) (*types.SignedMessage, error) `perm:"sign"` - WalletVerify func(p0 context.Context, p1 address.Address, p2 []byte, p3 *crypto.Signature) (bool, error) `perm:"read"` - } + WalletValidateAddress func(p0 context.Context, p1 string) (address.Address, error) `perm:"read"` + + WalletVerify func(p0 context.Context, p1 address.Address, p2 []byte, p3 *crypto.Signature) (bool, error) `perm:"read"` } type FullNodeStub struct { @@ -425,71 +425,73 @@ type FullNodeStub struct { } type GatewayStruct struct { - Internal struct { - ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*api.BlockMessages, error) `` + Internal GatewayMethods +} - ChainGetMessage func(p0 context.Context, p1 cid.Cid) (*types.Message, error) `` +type GatewayMethods struct { + ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*api.BlockMessages, error) `` - ChainGetTipSet func(p0 context.Context, p1 types.TipSetKey) (*types.TipSet, error) `` + ChainGetMessage func(p0 context.Context, p1 cid.Cid) (*types.Message, error) `` - ChainGetTipSetByHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) `` + ChainGetTipSet func(p0 context.Context, p1 types.TipSetKey) (*types.TipSet, error) `` - ChainHasObj func(p0 context.Context, p1 cid.Cid) (bool, error) `` + ChainGetTipSetByHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) `` - ChainHead func(p0 context.Context) (*types.TipSet, error) `` + ChainHasObj func(p0 context.Context, p1 cid.Cid) (bool, error) `` - ChainNotify func(p0 context.Context) (<-chan []*api.HeadChange, error) `` + ChainHead func(p0 context.Context) (*types.TipSet, error) `` - ChainPutObj func(p0 context.Context, p1 blocks.Block) error `` + ChainNotify func(p0 context.Context) (<-chan []*api.HeadChange, error) `` - ChainReadObj func(p0 context.Context, p1 cid.Cid) ([]byte, error) `` + ChainPutObj func(p0 context.Context, p1 blocks.Block) error `` - GasEstimateMessageGas func(p0 context.Context, p1 *types.Message, p2 *api.MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) `` + ChainReadObj func(p0 context.Context, p1 cid.Cid) ([]byte, error) `` - MpoolPush func(p0 context.Context, p1 *types.SignedMessage) (cid.Cid, error) `` + GasEstimateMessageGas func(p0 context.Context, p1 *types.Message, p2 *api.MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) `` - MsigGetAvailableBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) `` + MpoolPush func(p0 context.Context, p1 *types.SignedMessage) (cid.Cid, error) `` - MsigGetPending func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]*api.MsigTransaction, error) `` + MsigGetAvailableBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) `` - MsigGetVested func(p0 context.Context, p1 address.Address, p2 types.TipSetKey, p3 types.TipSetKey) (types.BigInt, error) `` + MsigGetPending func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]*api.MsigTransaction, error) `` - StateAccountKey func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `` + MsigGetVested func(p0 context.Context, p1 address.Address, p2 types.TipSetKey, p3 types.TipSetKey) (types.BigInt, error) `` - StateDealProviderCollateralBounds func(p0 context.Context, p1 abi.PaddedPieceSize, p2 bool, p3 types.TipSetKey) (api.DealCollateralBounds, error) `` + StateAccountKey func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `` - StateGetActor func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) `` + StateDealProviderCollateralBounds func(p0 context.Context, p1 abi.PaddedPieceSize, p2 bool, p3 types.TipSetKey) (api.DealCollateralBounds, error) `` - StateGetReceipt func(p0 context.Context, p1 cid.Cid, p2 types.TipSetKey) (*types.MessageReceipt, error) `` + StateGetActor func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) `` - StateListMiners func(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) `` + StateGetReceipt func(p0 context.Context, p1 cid.Cid, p2 types.TipSetKey) (*types.MessageReceipt, error) `` - StateLookupID func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `` + StateListMiners func(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) `` - StateMarketBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (api.MarketBalance, error) `` + StateLookupID func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `` - StateMarketStorageDeal func(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*api.MarketDeal, error) `` + StateMarketBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (api.MarketBalance, error) `` - StateMinerInfo func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (api.MinerInfo, error) `` + StateMarketStorageDeal func(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*api.MarketDeal, error) `` - StateMinerPower func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*api.MinerPower, error) `` + StateMinerInfo func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (api.MinerInfo, error) `` - StateMinerProvingDeadline func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*dline.Info, error) `` + StateMinerPower func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*api.MinerPower, error) `` - StateNetworkVersion func(p0 context.Context, p1 types.TipSetKey) (abinetwork.Version, error) `` + StateMinerProvingDeadline func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*dline.Info, error) `` - StateSearchMsg func(p0 context.Context, p1 cid.Cid) (*api.MsgLookup, error) `` + StateNetworkVersion func(p0 context.Context, p1 types.TipSetKey) (abinetwork.Version, error) `` - StateSectorGetInfo func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorOnChainInfo, error) `` + StateSearchMsg func(p0 context.Context, p1 cid.Cid) (*api.MsgLookup, error) `` - StateVerifiedClientStatus func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*abi.StoragePower, error) `` + StateSectorGetInfo func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorOnChainInfo, error) `` - StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64) (*api.MsgLookup, error) `` + StateVerifiedClientStatus func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*abi.StoragePower, error) `` - Version func(p0 context.Context) (api.APIVersion, error) `` + StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64) (*api.MsgLookup, error) `` - WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) `` - } + Version func(p0 context.Context) (api.APIVersion, error) `` + + WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) `` } type GatewayStub struct { diff --git a/gateway/handler.go b/gateway/handler.go index e9bee6d52..8760598cc 100644 --- a/gateway/handler.go +++ b/gateway/handler.go @@ -34,7 +34,7 @@ func Handler(gwapi lapi.Gateway, api lapi.FullNode, rateLimit int64, connPerMinu m := mux.NewRouter() serveRpc := func(path string, hnd interface{}) { - rpcServer := jsonrpc.NewServer(append(opts, jsonrpc.WithServerErrors(lapi.RPCErrors))...) + rpcServer := jsonrpc.NewServer(append(opts, jsonrpc.WithReverseClient[lapi.EthSubscriberMethods]("Filecoin"), jsonrpc.WithServerErrors(lapi.RPCErrors))...) rpcServer.Register("Filecoin", hnd) rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover") diff --git a/gen/api/proxygen.go b/gen/api/proxygen.go index f756c0d0c..75fbc668c 100644 --- a/gen/api/proxygen.go +++ b/gen/api/proxygen.go @@ -259,7 +259,7 @@ func generate(path, pkg, outpkg, outfile string) error { if len(tf) != 2 { continue } - if tf[0] != "perm" { // todo: allow more tag types + if tf[0] != "perm" && tf[0] != "rpc_method" && tf[0] != "notify" { // todo: allow more tag types continue } info.Methods[mname].Tags[tf[0]] = tf @@ -302,12 +302,14 @@ type {{.Num}}Struct struct { {{range .Include}} {{.}}Struct {{end}} - Internal struct { + Internal {{.Num}}Methods +} + +type {{.Num}}Methods struct { {{range .Methods}} {{.Num}} func({{.NamedParams}}) ({{.Results}}) `+"`"+`{{range .Tags}}{{index . 0}}:"{{index . 1}}"{{end}}`+"`"+` {{end}} } -} type {{.Num}}Stub struct { {{range .Include}} diff --git a/go.mod b/go.mod index 242fb9137..d5059ac30 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,7 @@ require ( github.com/filecoin-project/go-fil-commcid v0.1.0 github.com/filecoin-project/go-fil-commp-hashhash v0.1.0 github.com/filecoin-project/go-fil-markets v1.25.2 - github.com/filecoin-project/go-jsonrpc v0.1.9 + github.com/filecoin-project/go-jsonrpc v0.1.10-0.20230116095704-da92324a76e1 github.com/filecoin-project/go-legs v0.4.4 github.com/filecoin-project/go-padreader v0.0.1 github.com/filecoin-project/go-paramfetch v0.0.4 diff --git a/go.sum b/go.sum index 9ffaf17d6..a6d00a0b3 100644 --- a/go.sum +++ b/go.sum @@ -342,6 +342,8 @@ github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0 h1:rVVNq0x6RGQIzCo1iiJlGFm9AG github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0/go.mod h1:bxmzgT8tmeVQA1/gvBwFmYdT8SOFUwB3ovSUfG1Ux0g= github.com/filecoin-project/go-jsonrpc v0.1.9 h1:HRWLxo7HAWzI3xZGeFG4LZJoYpms+Q+8kwmMTLnyS3A= github.com/filecoin-project/go-jsonrpc v0.1.9/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4= +github.com/filecoin-project/go-jsonrpc v0.1.10-0.20230116095704-da92324a76e1 h1:GcF3gSvesv1epB2SrTfalrYvhFzT1UnmSS1Bh+jprQY= +github.com/filecoin-project/go-jsonrpc v0.1.10-0.20230116095704-da92324a76e1/go.mod h1:jBSvPTl8V1N7gSTuCR4bis8wnQnIjHbRPpROol6iQKM= github.com/filecoin-project/go-legs v0.4.4 h1:mpMmAOOnamaz0CV9rgeKhEWA8j9kMC+f+UGCGrxKaZo= github.com/filecoin-project/go-legs v0.4.4/go.mod h1:JQ3hA6xpJdbR8euZ2rO0jkxaMxeidXf0LDnVuqPAe9s= github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20/go.mod h1:mPn+LRRd5gEKNAtc+r3ScpW2JRU/pj4NBKdADYWHiak= diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 5dbca7357..ef7f7ff6d 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -3,8 +3,10 @@ package full import ( "bytes" "context" + "encoding/json" "errors" "fmt" + "github.com/filecoin-project/go-jsonrpc" "strconv" "sync" "time" @@ -75,7 +77,7 @@ type EthEventAPI interface { EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error) - EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) + EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) } @@ -1100,16 +1102,19 @@ const ( EthSubscribeEventTypeLogs = "logs" ) -func (e *EthEvent) EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) { +func (e *EthEvent) EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) { if e.SubManager == nil { - return nil, api.ErrNotSupported + return ethtypes.EthSubscriptionID{}, api.ErrNotSupported } - // Note that go-jsonrpc will set the method field of the response to "xrpc.ch.val" but the ethereum api expects the name of the - // method to be "eth_subscription". This probably doesn't matter in practice. - sub, err := e.SubManager.StartSubscription(ctx) + ethCb, ok := jsonrpc.ExtractReverseClient[api.EthSubscriberMethods](ctx) + if !ok { + return ethtypes.EthSubscriptionID{}, xerrors.Errorf("connection doesn't support callbacks") + } + + sub, err := e.SubManager.StartSubscription(ctx, ethCb.EthSubscription) if err != nil { - return nil, err + return ethtypes.EthSubscriptionID{}, err } switch eventType { @@ -1118,7 +1123,7 @@ func (e *EthEvent) EthSubscribe(ctx context.Context, eventType string, params *e if err != nil { // clean up any previous filters added and stop the sub _, _ = e.EthUnsubscribe(ctx, sub.id) - return nil, err + return ethtypes.EthSubscriptionID{}, err } sub.addFilter(ctx, f) @@ -1138,14 +1143,14 @@ func (e *EthEvent) EthSubscribe(ctx context.Context, eventType string, params *e if err != nil { // clean up any previous filters added and stop the sub _, _ = e.EthUnsubscribe(ctx, sub.id) - return nil, err + return ethtypes.EthSubscriptionID{}, err } sub.addFilter(ctx, f) default: - return nil, xerrors.Errorf("unsupported event type: %s", eventType) + return ethtypes.EthSubscriptionID{}, xerrors.Errorf("unsupported event type: %s", eventType) } - return sub.out, nil + return sub.id, nil } func (e *EthEvent) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) { @@ -1294,7 +1299,7 @@ type EthSubscriptionManager struct { subs map[ethtypes.EthSubscriptionID]*ethSubscription } -func (e *EthSubscriptionManager) StartSubscription(ctx context.Context) (*ethSubscription, error) { // nolint +func (e *EthSubscriptionManager) StartSubscription(ctx context.Context, out ethSubscriptionCallback) (*ethSubscription, error) { // nolint rawid, err := uuid.NewRandom() if err != nil { return nil, xerrors.Errorf("new uuid: %w", err) @@ -1310,7 +1315,7 @@ func (e *EthSubscriptionManager) StartSubscription(ctx context.Context) (*ethSub ChainAPI: e.ChainAPI, id: id, in: make(chan interface{}, 200), - out: make(chan ethtypes.EthSubscriptionResponse, 20), + out: out, quit: quit, } @@ -1340,13 +1345,15 @@ func (e *EthSubscriptionManager) StopSubscription(ctx context.Context, id ethtyp return sub.filters, nil } +type ethSubscriptionCallback func(context.Context, jsonrpc.RawParams) error + type ethSubscription struct { Chain *store.ChainStore StateAPI StateAPI ChainAPI ChainAPI id ethtypes.EthSubscriptionID in chan interface{} - out chan ethtypes.EthSubscriptionResponse + out ethSubscriptionCallback mu sync.Mutex filters []filter.Filter @@ -1390,10 +1397,15 @@ func (e *ethSubscription) start(ctx context.Context) { continue } - select { - case e.out <- resp: - default: - // Skip if client is not reading responses + outParam, err := json.Marshal(resp) + if err != nil { + log.Warnw("marshaling subscription response", "sub", e.id, "error", err) + continue + } + + if err := e.out(ctx, outParam); err != nil { + log.Warnw("sending subscription response", "sub", e.id, "error", err) + continue } } } @@ -1405,7 +1417,6 @@ func (e *ethSubscription) stop() { if e.quit != nil { e.quit() - close(e.out) e.quit = nil } } diff --git a/node/rpc.go b/node/rpc.go index a96e6e3ac..c846646fe 100644 --- a/node/rpc.go +++ b/node/rpc.go @@ -75,7 +75,7 @@ func FullNodeHandler(a v1api.FullNode, permissioned bool, opts ...jsonrpc.Server m := mux.NewRouter() serveRpc := func(path string, hnd interface{}) { - rpcServer := jsonrpc.NewServer(append(opts, jsonrpc.WithServerErrors(api.RPCErrors))...) + rpcServer := jsonrpc.NewServer(append(opts, jsonrpc.WithReverseClient[api.EthSubscriberMethods]("Filecoin"), jsonrpc.WithServerErrors(api.RPCErrors))...) rpcServer.Register("Filecoin", hnd) rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover") From 1286d769889f10c72e2136f74f03dcf6e227ae12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 16 Jan 2023 15:28:55 +0100 Subject: [PATCH 31/39] gateway: eth_subscribe support --- api/api_full.go | 3 +- api/api_gateway.go | 2 +- api/client/client.go | 4 +- api/mocks/mock_full.go | 4 +- api/proxy_gen.go | 28 +++--- api/v0api/proxy_gen.go | 10 +- build/openrpc/full.json.gz | Bin 32994 -> 33267 bytes build/openrpc/gateway.json.gz | Bin 8494 -> 8582 bytes build/openrpc/miner.json.gz | Bin 16047 -> 16048 bytes build/openrpc/worker.json.gz | Bin 5222 -> 5225 bytes cli/util/api.go | 24 ++++- cmd/lotus-gateway/main.go | 6 +- documentation/en/api-v1-unstable-methods.md | 71 +++++++-------- gateway/eth_sub.go | 70 ++++++++++++++ gateway/handler.go | 4 +- gateway/node.go | 6 +- gateway/node_test.go | 8 +- gateway/proxy_eth.go | 96 +++++++++++++++++--- go.mod | 2 +- go.sum | 7 +- itests/gateway_test.go | 2 +- node/impl/full/eth.go | 2 +- 22 files changed, 255 insertions(+), 94 deletions(-) create mode 100644 gateway/eth_sub.go diff --git a/api/api_full.go b/api/api_full.go index 20d374b50..e9cf4a58f 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -6,8 +6,6 @@ import ( "fmt" "time" - "github.com/filecoin-project/go-jsonrpc" - "github.com/google/uuid" blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" @@ -18,6 +16,7 @@ import ( datatransfer "github.com/filecoin-project/go-data-transfer" "github.com/filecoin-project/go-fil-markets/retrievalmarket" "github.com/filecoin-project/go-fil-markets/storagemarket" + "github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/builtin/v8/paych" diff --git a/api/api_gateway.go b/api/api_gateway.go index c78710026..fdadd2ae6 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -102,6 +102,6 @@ type Gateway interface { EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error) - EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) + EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) } diff --git a/api/client/client.go b/api/client/client.go index 32583097e..8b159c5b1 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -35,10 +35,10 @@ func NewFullNodeRPCV0(ctx context.Context, addr string, requestHeader http.Heade } // NewFullNodeRPCV1 creates a new http jsonrpc client. -func NewFullNodeRPCV1(ctx context.Context, addr string, requestHeader http.Header) (api.FullNode, jsonrpc.ClientCloser, error) { +func NewFullNodeRPCV1(ctx context.Context, addr string, requestHeader http.Header, opts ...jsonrpc.Option) (api.FullNode, jsonrpc.ClientCloser, error) { var res v1api.FullNodeStruct closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin", - api.GetInternalStructs(&res), requestHeader, jsonrpc.WithErrors(api.RPCErrors)) + api.GetInternalStructs(&res), requestHeader, append([]jsonrpc.Option{jsonrpc.WithErrors(api.RPCErrors)}, opts...)...) return &res, closer, err } diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index 02ded88a5..30df05226 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -1388,10 +1388,10 @@ func (mr *MockFullNodeMockRecorder) EthSendRawTransaction(arg0, arg1 interface{} } // EthSubscribe mocks base method. -func (m *MockFullNode) EthSubscribe(arg0 context.Context, arg1 string, arg2 *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) { +func (m *MockFullNode) EthSubscribe(arg0 context.Context, arg1 string, arg2 *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "EthSubscribe", arg0, arg1, arg2) - ret0, _ := ret[0].(<-chan ethtypes.EthSubscriptionResponse) + ret0, _ := ret[0].(ethtypes.EthSubscriptionID) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 43089c6fa..9b8890a7c 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -7,6 +7,15 @@ import ( "encoding/json" "time" + "github.com/google/uuid" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + "github.com/libp2p/go-libp2p/core/metrics" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/protocol" + "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-bitfield" datatransfer "github.com/filecoin-project/go-data-transfer" @@ -23,6 +32,7 @@ import ( "github.com/filecoin-project/go-state-types/dline" abinetwork "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/go-state-types/proof" + apitypes "github.com/filecoin-project/lotus/api/types" builtinactors "github.com/filecoin-project/lotus/chain/actors/builtin" lminer "github.com/filecoin-project/lotus/chain/actors/builtin/miner" @@ -35,14 +45,6 @@ import ( "github.com/filecoin-project/lotus/storage/sealer/fsutil" "github.com/filecoin-project/lotus/storage/sealer/sealtasks" "github.com/filecoin-project/lotus/storage/sealer/storiface" - "github.com/google/uuid" - blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" - "github.com/libp2p/go-libp2p/core/metrics" - "github.com/libp2p/go-libp2p/core/network" - "github.com/libp2p/go-libp2p/core/peer" - "github.com/libp2p/go-libp2p/core/protocol" - "golang.org/x/xerrors" ) var ErrNotSupported = xerrors.New("method not supported") @@ -702,7 +704,7 @@ type GatewayMethods struct { EthSendRawTransaction func(p0 context.Context, p1 ethtypes.EthBytes) (ethtypes.EthHash, error) `` - EthSubscribe func(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) `` + EthSubscribe func(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) `` EthUninstallFilter func(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) `` @@ -4463,15 +4465,15 @@ func (s *GatewayStub) EthSendRawTransaction(p0 context.Context, p1 ethtypes.EthB return *new(ethtypes.EthHash), ErrNotSupported } -func (s *GatewayStruct) EthSubscribe(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) { +func (s *GatewayStruct) EthSubscribe(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) { if s.Internal.EthSubscribe == nil { - return nil, ErrNotSupported + return *new(ethtypes.EthSubscriptionID), ErrNotSupported } return s.Internal.EthSubscribe(p0, p1, p2) } -func (s *GatewayStub) EthSubscribe(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) { - return nil, ErrNotSupported +func (s *GatewayStub) EthSubscribe(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) { + return *new(ethtypes.EthSubscriptionID), ErrNotSupported } func (s *GatewayStruct) EthUninstallFilter(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) { diff --git a/api/v0api/proxy_gen.go b/api/v0api/proxy_gen.go index f66588b8e..4acf2e111 100644 --- a/api/v0api/proxy_gen.go +++ b/api/v0api/proxy_gen.go @@ -5,6 +5,11 @@ package v0api import ( "context" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + "github.com/libp2p/go-libp2p/core/peer" + "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-bitfield" datatransfer "github.com/filecoin-project/go-data-transfer" @@ -17,6 +22,7 @@ import ( "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/dline" abinetwork "github.com/filecoin-project/go-state-types/network" + "github.com/filecoin-project/lotus/api" apitypes "github.com/filecoin-project/lotus/api/types" lminer "github.com/filecoin-project/lotus/chain/actors/builtin/miner" @@ -24,10 +30,6 @@ import ( marketevents "github.com/filecoin-project/lotus/markets/loggers" "github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/lotus/node/repo/imports" - blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" - "github.com/libp2p/go-libp2p/core/peer" - "golang.org/x/xerrors" ) var ErrNotSupported = xerrors.New("method not supported") diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 4fc58a93a6f9db32082e1c3941745e4fd50f2902..4524108b1c12310be48c34b90d68fc7f028cd230 100644 GIT binary patch literal 33267 zcmZUaV~l9Q)~4IGZQHhO+qSLK?mo@awr$(CZQIt@_s(SUC9{96s!A$5ReNXQeHMNc z1i*ivpUW<9=M9c}@OM>(c7K@!C7xsx?mBzht$XT-nZrF}?!*S{lgvN~iT-q;RuB{I z#BukY5bz!NKorvnCbJeZ8YBxAf!vRkzhVdGVejD}-3y$jkInDe-dT3wiHKJ?pO+4k zyo;QtK7~HRy+dz|96bCNF5E2?2B}Dz1uZ>G-dFh>(=~SHk*~q+mCmPyUb14$`vK@MonGt@oimG~DLkp1 zpQIGIcl|qHaHKd}4^_rbJ1M(EDDOL%@C=wgy}<gc%yxqZ^N59vD2JU})V zGM%Ke)JE{12lxR7@=4vtfcp&r0qP)INwfZIpe2I0Y4;OpHwyv8>WiPG-$m5w?63Vh zex9;^x`{pC?-dJIwQDX=e>_iel?OBPM*#|yK|5i54~ScN9gC;ekL6PHfs~2J^?u6qR5F)86PWlHCz5%FTHT*#T=4tJ-npK zn+YT2QXWBgq;9&jXKC)b!f_!_`?}qJ+!$+`Oyn)>hK0O<*^kx!H422Za_GYHNeAn%4_1t7Iq;0PYL4-?AM6U;5e z=`&7C+Tj|z&uzw>HZoNt>Oj&_9lLDSQvVnQD4+)d{8b1DN1hpF z>=p&V$;nzw511?c=-LFh$fHczBUFhykmTTSM%?oVR*Wlfjn+j7UmHQ#gAwSBWA~~W zhZ&-BVu3wv2Q-3d0}x+q?!^R81*}U|4I_M2?u(}j*iCI81*E?wTTKIy(V;PGwJ_#- z;BXt`btdqv~#kwq^WY&Lr932-`Dv-M`IAA#lIf}H0#f_MaK&My z{yWbf(ehO@Q4k^gZ5{Oef&YV`c2E|EGuKY{(C`48Xv{_2>0G;`tMUdR7?z;i4_t}a zmdm{F2Iuy^H4S-KTe}@wZE={X=Op{_F(bFXvSBCw?aFczWWgBdj1gY8dK{)k(nH9T zeLF2^-m!4yGt2L_(RHP#v9Gi{4=r_c+C1kb@rrB&6Eg?ugz(I19otE^NiI%F8LT}rpoDmjJ$llD@1eVOmd?VpOosHeF zj~Og28B&x$V!b4%as-_d?!voupT+TeA|k+4IePe(*Ccvr2pLGXDDhSyx~1YR%bs?Z z;)GNJ#y!tw0*#glB(HhB#k@jk8izch#-<69Ueof`4c zLdiHb_JCo?^WS}ti_l@mL1_yT`2q0xlMA3qoqotDO_hyyvXw*Tqf>y)zaoJEfEWa% zE9RLqI7`L~u_rY_pZJS+fWQtMls}H>hfbPfMO3`gX&tJm;j~JkQ zrZDilBp`->p1qW*z1l9aIy?zT2~gN^@) zUysfo0d#yhIzV#dYWRFO0aNh1eZD+^x_!Q1 zzJHmY%s9mArlzU0oN0QZ&syHUnAf?e_VMSj3$MH0;?>7`L?&S$@tWKqaE1DP-t!13 z^JJWZ=NxYPo(OPa9u`hw_iatztx0Z&;=}o(|F!=Exa~+G?C!&4ez5hC;r+u;Jl})O z>3<_@|B*=9wdXGU@(s3qQ#(!1*qAbUXRq|N%um9!{z-W1R>)Xr@+S>F|A^ipdJ7F* zQ};&PX`UT8+lp+lNdw6_Pf(S&L?*z_DPhT$GBcL!>J-!lr$X-_vu6-KB?{j=jucv< zhx=IRqgG$-5fNJ#RJLA%#dX%Gg6?ll95<-Up$xmezZ)n6q>Ors*iLUP`v)s(mR-U= zU7)|mcJ(ZT-zx@K(6x0AiJvYdOWs*+kCRb4kum9~+qkrb-_U^p8*&8>(XP}X-qQd$ zb+EV|f{GH{Z&d;wZCRiB(5&?ww%1^ z35;{jmSMHkmeo)F#Z*mthS<`ly&^v{t_FkVrNDn~`@+HMglJ}iR|-#6pj*>(Q$zXQ z_YKj!9D@@K1CSom7gqiexZVp>Em>;ehG~riW`lFk2GS_{xyRFaB_n|0MRyzI&L1_3WMEBCS*QzLQ0tD z6BI4~%@CDxmT4w3F@n>-YG5oj+at zETKO6zYT>HfBv23QFjOaeA2D^3Bt$iz64r}K>xg@xO>@~!1rI9ibu7N8MI!$*$~v0 z>l-1;lc*}$#`=4ps`QrxSgmQRnSrFL4J!r?5+yitc`Mi-?A(5~Ld~BvFT5B^L6v<+ ztp*pdT&lVK2l1|RwRoY5Tv({7nrdtX{i%uf&sb#px}YUnx0@@Fskz`fGHZJnaSRZz z(8O3WND&hwaF_O+yRJO;Qq_oKPxr)=0+~kh*eL5q*#1dY62WG{-hl zdUqHGhE*b>NYr}At}+Gyy2Ca7Tb{s@MAooAn+S{fv<8d6)~4F z99m7ga!A1-1Q;Hx<5tuaRh52P^UJhSAftS=F?ULu<^@g=oltec6`u9B}MiMP42jX)=iR@MaPi0q* zXW$?^VaA}`jbTU!!4lro;IeodGnrC+Q(KSJm7bAWsSq(^ORo{gt|61pZ;s60s6<%CWc_Kn z&u^;tJyoNz4F&2;A9KEfyUwyE`F(X`qdIblKj&`PL4Ao?;i<7;2}y4U}Pz#WO{vB8rKV#_XcrXD=enVkPj5>r{2489k)m3F#Q+~GbG#W&1?HnR3 z4okWxkPz`SQHD~VouQ^(V2up;Ik&=gr$K=z-hlJhCBTrJDnn|mEfz48t|wq_9JV)O z>nqb|OKA&&uEjp;sgQCxJiSpjEZeORUw*|m=V7YjW$ob8_W{6rBFyRQU$~#IjP>ph zTL{c|*=tMB=wSsBF-;0|R~H@e*;qT!7I`b4K8~YY28^yjNdufq&63SF@ z;RS-Gf$ zz-T?J)b@?6lg5U|7Iie56GM?##>Y%!uB_~p6x_4-C}r_6BXsCyr3I0VHH9fgEkZ1& zEAClmv+Q61A-vKss%_e2*!_VRk_Iw1y&eaK|`zAkI!G0>c8JU^^=A`rz^4aA?lM_DMC3*GrN0Lq4It1M9rf8I}M-*5Rh{d?R;gKB$1sV9cTtW0BmC_EO z`-PM-HVL{($Fs9A##>dOZK2v|E}{Tcv$G^R5e3Flp$L#&(OUBd5tt5vqyo-etoJKD zWYo!~+%lw?JIH>wex3k2I2=UExyB&|$DM#*HDM%^q=BPCz_7sNO@mi^l+gI%Ah^l~KV{wi2+vgP-A8WZi5r}0UMO8t;VXA;r%vLUnz189nt8&@j3TUJ3$NqYo6P3;% zsPw8P+6E5;RgI^Vr8Lxio+N&{^BdP^?2(k+J-JNfH;9#&ZO*PSi%OTqW!2A`QgZAV z?nfRiDntY?$@b&xlftWizQBM!&=(*=p$VVY#*Yi5G4_Gga?Qaa+afcf1j>=M$YAUu zkM@CV69EMN0fJ+UZ!gN=Qbl|i?}e5^xP8i=kj9>DMRe#?dhf2!F`J?(Dtx5i08jL? zOupxZxW{VHO(mboZ#E|WPeM#CGYw2*F~P0XjT9QJ6q_^JGo$FhaxzEEkwes9q|z0V zP4v&xLo5?O5UnLB=rDxozK>G~X%@G9EZv7*L8HNU3H22!WwZz%g?QOx(ORFpQmkIF z$)VHQh2WjV6?>7PeYRt*mtHG)+j(L3a{DmB(&{qPVs~DaHm{wyZQY@8GNHw@h}pxd z<&q*kIP?aQGgMYM{r8G#s;bx`iUwLoLBBCXYjOQ$1~VarmSJNN1p~ogN5as@sT1L2 z(PB4*yJv>lt3cAM5h30$ZLYfN8_>KIM`hEPnezXFiijBAWiM6Ko*F@O3$_g9p0Tt< zJy#^~kT5HcWl_U1AgV=n%8BN1!YBu}LU=9PZEGA{s-C4wb~7S90X|Xw(W_W7KOdBs zFNU-?pt+CNck=AI?wgT&U3tmLnkkQJ+xL{j>*DEpi64mfW>R*CK9a@(mxw+?O>S&y zY4NT(`l)Gdfj|8DA)Vyf(bYL6#---tpLguW1xD)e`+O(dqw5;7Ti@mm#DSvqy=_1q z20`Dcst0OlTY`*Rhr7CQ;YPoeD~5rRJ=ed&&Xx4$Z6sA0HZ+YLNfiDU-fZCp=F7{= zK)RIkEsr8$gI2wCi~U={B$T*`$V8lERN0V*m3@F7^pRTJw2%*Ge*-M-ivgcyiLy*X$E*8>O zve+__x#~Dnj@mUBH-vW80PH&6w+w{Liqmu@!b2i54~f^;>@!v=h$knct(mw=3!WhA zV6%qNH>z7UW^SU0x~%hvKFZ58kJrAHkCeC0s2L;qSGB~)z{}LCFnAKp_U~>O%ivbDXHM9< zn`2`KhZ1pzLyW5AA!&cb!fV?#$v8%m>joTbLHkttg~!2U7b!tc%*}#=2FlryNUDkr z5=KPXs!Cb--FlqIEPK)!MkBOrMP`vsbfj2XqGKrJ&W0zXUg?d|rGbh{ly3A&)`grg zzVTpnUWy`f!lXHy@p=wKpzzjnzTlEZ)uqp_T+bA6&|GsL@eNt!#k;q5+Qu%h2@^o$ z-aK?givzW6q@FhVf=QZA<8uD9@yp{D$QTH)qs!*qT14t56URozMV#%$^p1vmcUJ}( zwaWJMwhClY!x>T-kIE4!A}D=dlzRCts8Q5VB-Q#ppW)ykQDDCIu(Ip8uo&_rh|=UB z@@t&@;u64|B|Ns??D${zIhHZ|VN!kG&ocC-_RS*=pBH&EiGuP9r!>1%)caTdRn}QxyHir!VpPI5u5$={lUJ@dZ#&Sr!g6g~b9YPxA4RPtrd`jeSDm0A70k z07Jx0&m^*Mb>FY6`?Jx5CD2otB9odoTxKjXsvS2Qw?MsY>|0%3_zQG%|HAJ0ahLKa z(zCJzZ7ZdUF47NC4Kt=8mRT9(pRtM(r>}@oh%h1%#re<__mWi8Fo)1V)nq7#FYu8qk?GaCx3^=B3~TwoamnU>@1z}=qc zToh?#Q=CD30^L{xa)bJtn{=&H;# z$rI(uh&T>9FjuK##7mRb&A87$zVFif$zz2?PCIX~6oK4o7bp>08Y z1v%Vv)Ix-E?>duot^Sk@ZjG6?J7O)=mCPH)x0~T zaYZU^NxDM@nU`8r`(g2xPj438uXI@>cZ3}XY81fQ!L zH&?3ALYRjkSknBeT{4HFj3)X(=%D>d$Us88Oj0^%Y_s`w$-AD6*|Sx~O>N;SWH}Uk z@?;N?^w4II(z5#c@}e~x%&YSSZxoyzTF*7h?KgaG!{b+{PVzRkEfw$UoIMwbu7gkw z5~C=F7UvkWZ!;G`{vkgu=XxV~Bb9C(^5==@9sX6wFAhX4Ksy0+JrHCL)8@}=!}Qn^J{Y7FO{5VCt3 z{7E=Xkk*GZ+ASSzvChv2H3$GWU_JqBWhN=z6VWyx!P?kt#j#HCy}W{_a;^(ayu%)x z`f6v4s3*mpTBC%qL)cNeU-dABp1-0qoT@z2N`l!GoHUJ=>nA%vfkAXAO`YlN;wFq7 zv39-IzxdKM3b|4-gX#Q7C$;!GA6DwOw(fcprY=eEJ~a)M*ho zboIeR7zAVOJY;`X7-DNLYz{tw;x0soZ|C>iW)Ebd;!SQc95qQ-OENHDcGwRLII{Ra zATD?)Iwz?QRPAhjO1tCE;Aq0i#aiN?bp&OVfWZIh!uFunHL0CuZ_=;hWVFbt#f;x` zi=yS1j+xb9dAv`V-sNgpNN%r~(|`0FZsvspYCBM{LeAr*+c&Lex{i^?GAbNR3+`cr zRRP572z>>VgK`44OWrpE0e;F#a(#gaI~?8<1BeLd!g9}`Nck^hD$g+cZaV(^K)~0d;j<|4PV0tAY$hz5V0xR76KS5 z3=bIE+N&Kc7vITRTY>y3=qD>;eNWbxI?(}1)Ek&ht|jw?%sdttvxF&BeBKN;kM{|t zck%wUy5X;#kg@wu9u7BbTH?=RT12}k=Q%=7xS&aW76ZCq+frh#Mp|xlfK3$(u;p|V z`p*&wsDzy~(s&<^BfuCAhYMGdlpHByFHGrESCh+rTM!>=VGcE&XCe zDRPjFT1?#6SHeM1i4W1<&B=Zqt43tR`A`KTr)&3k#U?t<5XeyEQYyK&J7qB^z209; zw*+WqlYE(4MDX~(G>8#?ER8Zf!#JaJEO(8A!n!MpM0|I~Zi+Jz;N!{)u5^~PHaVFjW0-1ijyGHHQ$z2u;Z0W18EV=J_WoOUZFL#m!2K=+2#uR1FSSie^qdhcH`| zIhjl`C2<8Wwei}7id!h1tZBy)W@=nZ3tN;g>{^dza~Y!cu@T#qWS%IpOcb0S&g0}a z2=4)yC4HR{L=n~*r|}AwB6a|29{V4$@#1+1AC{=d3wpU``$3^vWYr4l3x)3;9Na@t zDAQWNGW^%pEVw4KVOeZ&T1bHODCBCwa(0OiLjz#`cdqs`nGkF8GJ z>#gCtixRgCgRPjtYa7 z)<6~#MBHO@?aU7AK!T%o}T;@z9IS1pK>lNo2;&~Im zm}1Q0z-;jgy7mNEqRjpO)ay9>$L=XiaSBLx>g~dxl`&3=M>4R)5`Up@WPLRnhPq61 zFR&J3v6Lsb#Rqs2{-y|XqPr$Px8l*E`&dFUhSVISuy&wUT#4#uHzc^Q*IH#BQ5bjL z#ZN}c%HXZ@X0CA;g-bn2nqpOZUD*L=<=B=wtlTb3% zQ)4(St#}?Xa!H8WQXh<*JDJ>&5fhF)N!iwEN{0d|O52uM$SQ&xBYYP)T;5}j>HEYR zME-NJj+32a961@xiK!WR6H66*Rhy9rg$swF1iRo;b@+V=kI5|+>kRFU&GqADwwC6$ zrl~4hJ6q@fHT5=b^YC^5NR$Wag*x;1SHbsrv;8rOEH(s?g@aLT>6~X4f#%lrtF&M| z7;}NL@&e`pQXa&>Ax7KYSR#lI`pas8Wo;mz&l5^;VViPdPN^?E!Hqj%k)ESvsh-Iz zu#EnvnjOUwl+y@Lw6yBsyEFt%)iML}D|mRxFsVCk=&ebSoUKHb`> z4fE<9O+kb0dqP;U!SAb)-XQvi@x}xlK$08;!5fo}E3wF!q?bFm-6M5+B>DRhlFu4N zBwIBn>3j@rQG&Ccs1zA5VH%6GD6CL8qqq>W{INsa>n$(_Q;{<{=V%}hpfgD2fbfWO z+*O|W6~yWD3me=4k0D+uQqPUzV|>MCz1>bZ#U9# zowv5Go}1k}y8K#tei4PMo~!3)@U3mXnh&#@j*gwHt4mk!KV5eA{yiHT8&}WX9b4PE zD_8JfTiXUL9St?#zsno>QvS17Pwws9U)R^wuUi){9bH*1TYY}+?z3AvZ+9lLL-QeV zGa!13^Sgj-mcjd+VbrtdDGUS7&kKh|!e*PT`ur;pL_Q}rY#*D6GlMhy4XCk(Iy<`Xb zwSO;f@-PZUVkISfsyClRj23fN99$NK>z&4k5mv=PX%cQpv)_X5L4>NinXhb;LcK{R?f9gCB)EFkTzg(KyOdTnt!l&$cKglEOovNA!nxD& zLype~9cRL$uCXKEtn!=gg=k?a+uQYNE-Ky_RRGjqU<-fx<7+hw@o-tOFT!2nYPCo_ zYVNH$U)Bc~PEM&6^OYm!DEaRyrkHCX^);hFTrDj62!%;+`_bW@--O=%K(7K7VGQH%pgQSw03zPi=n*yl1yI zR?x4Hq&Cg_QWe>xWkdgTn<;93HIT0b9$uQ)nZF}{27X+JYth2B3G4z}LHGZkfekSD zv5^#kp#j#@u!_AXa^8qtptq7e9bu-}cOm~iSbkU`i zWLv~CrnlmO$A-}M$hZo^k^E3K<0|rxI;THdc)A{g1<=r2fFUt77sj3mM2-q+dK7iv z^eWjGN1*v+D#W&z)MrNXPgY|hy5_7%jMt;%M|ry1O`vK{(}y9d$5jzG3Fgy(oBjJF z*TY^3@Fv3?RlsQV>iank8&)$YMI-=l@vzugIi1ZG(!e>pMQ@l_Dokp$1q%?JT>2gW zxa$s@cGuDGB;>GGqiNN3_w&m_!tM`{8HLC4Tk+X6u(+?&IV+lB8X=xqsKk`8XQP$ZdeThFYS znY-vx&!)X*Zr8RFOsPHsrIg92dI;0nSF9P=-AbbNfxvVEBxM^alXYB&(rBTS+X!c_ zi@Z&v?q@$b1K=;{a2C;#8IItcDN-@UY-0Z6;ta7nXsAvw<+KNAHii!5Ux=AMFL{3#l(=`90O&M8Te(lsgseSoX(1j4rw2Gbjel-0EoO7 z2NXjOnN;EEG9Z-!;Uho5CxioPkb^*dYl;l(`ILiEYUR)>DB!^GPGGwgjQ|{yFE7;q zLJkl&aMHa?V6r5b&{Zuo5OZ%8Iiwp2%@=Jl0GJoP?dlB>|C2@jXe}Q!YMSMh1qRSY zJmIJGETNIFfoV>)EZbSMDx}lUs!QKb>6yB3yl-LCJeg4O0c?)2Xd9?0%Q9EPvtq8g zB<~?JMH@s>6HT`#vx9hX`>ZW_qFzER(lX^hfnSGbHoqts4^Eggr}-bozZ`B^gFG{& zAX+8>Dub51XoIEQ?ld0*Oc;GK?EPVJz$_pPV%J`zF3pJW+V>5pNN`jXZqgFQ9bva| zj%=ZrZ9*5Aul0H&@jMnG(J4P)#N0>hV9E^eaii__{%_KK|J*m}TOnRe9OpFao_@u+ zLd^+Qg~hQUcc&vw;dgRi!{2yCo?J-&%Pxt@)-{Qm_tx}U@7KYlbw zF_S=jW12x+xQMdz?^7OU{qcPhmM=U#@(d{UU|WLcBjn4oJNfnA7Q#=t5AyrAKNt5F z`*A<%xj)|{{9ONmw;~K!eSZP9M_ra~STK)~JeaM!vCO~jH}=|Iy# z#gCyouv;J49z6qGJyz+3H;?hTUf>Z(Uv>(ru{n|&9!>%m%_uQllH55{&Qc*ecjo4i zIy5D#2*?4$HobCZ`DNP*R>DSS3SMc`j1XMReY&Pk}z0V)e&u?9%tWz$4sP*~Jr zq}i~85JlLj*sG#ACV&ym3-f1|uz?Xc23q9+PPSB!VS*Ef^Mc({P!-}iK>a6@!fC>A zwyujFVnUm!WK6DW?ZrVy8BwdE#?n#ts8x3|qN~~w3arNwm{l1k=%?wnk-{%ujbsjo zBkCEJx?+Y?6}g(&y(B4i5AH+?O$zdB)X1c}=Qy(7C@; zw;fY4!%xA$z=o+Ao2p(>I16_x{&?%&*4+BLI-`clE@f*Gs#ipfU0#v?0UWGx zGorhi7LyL3EAg-Nsy`nqFL_dux3-$@q#Xmjg#=>{$8Wzze9V14bfrzB31lKByD6Z4 z-YD|1*bV8X$sczab5hvLg|0a$HSR%iai}{>IDl-Y11`XHJW|WrJWLb?aN&T}Vpd#U z1AbckGPOQnqU;B|E24U?PiktjF_lf~n|6~t2PbXXCiV-&fU;Bk!cK5Pfmss#E1RZ+ zT(KHQc`oir;GJE>xGy8+$&ti9JEutTNgfA`L9ja+1b}!wC5WJgH7Bs7Xk6bW)#p{Q zp*Po+fP!virbht(i{eILt~yAPOD+a_SaOi7yT0Yiu_<+yUsUhHY%B?Cfy#iKi$iSX z7jb2Z9}wk?&S!KOzy>KS8#tmxB+W3>`@@Oy%LL?F6wE(lx00Nu9OjS8uM8&;klkqv z>m`IGrFy5yrDOR}b~n~Sn1)3+0MG?(GbbTmOjb2LS2nejEpO+N{0&6X++oOR1?;pg zh3!9&ErRTSR?i1T%3@vuK;(CShX*qoEO4T-=0%t-a|Nj8Z&4%FNUANPGAto6A0n4g zaKIrLjUTVm)21|m_!rtZ)}n54?j2Xap(M-qJvf{c#J?a$t!M=rUBfeF>V+U)2D%>CTheMHRC|vvk z)ktlqm0tdW=7Xw$P~_(^OFm8bTQP8Pvjb%Xq;T24a!!kL#0W?^ec$f*yCL;#yK&#sCm}cAJ}gH+OU&e% zrFdnZdQ)PH@}F{UOsj@_ImiaF?USk(l-vc`tZsOCvR^oKx@7p44_dVl3bK)u8cHbS zdr4*T{hL%U>VO;|6A}TAZU@Hfi)%+U{&(w;P-=mL)IMqwdE%I=%6HrinY7PgBZ6p&f;VgY7LwM$wAlHHS_8Tz4bXnkAkCpHf z_TbsWO81-PXOU4RrQ;Cc7(Bpn+i% z2FU_!bE^KoAb4xM)o>!UcAUb18we&;EG@(`PjbDDRyyu~nA&x69rO}%Zgnq^{j%2* zDqTBH*BsT^y2lFUfV7B?Zw*p%)0}qKWSv)e!&hgldrHkn%rZ-$Dxo5CB{s_F^Qz;b zU00I(3{`rC#-~(z03cZipD3~Ry1#doti21YX>4mcQthDgxo4X4E3n*>O%2)!!N(k~;>FaGZH^&&eo;eH6#EFa`q>=o3&qnF3GGB+vV(0; zowLAwCcAO%!kvTIQ_v@SKS%ltVW_-GlX#bXsh$5#=b!g4#tWBUOTQ3p4Bza(8Z{#+ z;*SAim$<|HKtO#+d9`2U#JGfUd)a~6hI=mbhT9qzE4$2y*s+K7-zy6n@i@(MXHm#e zok|kx$iz05K!=gS=*QPZMby&r`d)5gnQ!Q6&PvE3mp95yMBV|>6$i>&ifM{CUAA+Z!okqw0?(wt51JYtSv_@J&B9p{Rj)GPfbB;w9q-M>^vzu_(94 zv4x~ThcBcIZ$Q+5#|%trR~Q$YULD26=2WJ4BFppC?!@3zLIUk~H3aeh5Ociia>jNM zB0!(oxz(+RrEK{T0QB8~a5~ny5$hxqDFTkU#6JW$mGccp-SBt+ z%*K)?NyEKG(}I|np;0?(BEG#7&~6NXzS2x(#{w}%VBVf$aL)qAcuWD10$6Q|6+lmI zNEc``?4pIB7S!_Rh3QUpLoqatjuI6#35boArNKYwa3WA5<$xv>7p4Z*zI`kA27hYV zVUg212%Z>pY`oozudXZ*g!6T`GdBD-cJ!XRkm7J<{n7I$sLWf;w{QG(RX-onpi2Hb zj|M7egf@uMKm8g9UFDj+Z+W5mR;UWB5^9IMpb%|#aMq&fVAZ1T<}XqBK7)wJR0^t+ zlxO0XGs@IUwQwrDSK*^CYoM0A#?(lP9OsR9tNI3EPZ@>*V(b$&`q1e!80krUT$MT# z`R2D)#1wzpCeRIzyBL%@qre19rmZw>uH{R?nR8PakvYSpZK!%8^7$*{Ay5D*i?qU0 zGys||WB3sb*RtBu9@nLw7&6S{UcS?mh4O+tWbl{~h^x>Fvl>lS(2Ybv(D+j7B_xTa z3bUPt`!^tEC~g}OjW>gl4@zK+p)uAVDbr^*J?C>Aq;)I1(Z;>z{+9EKO@bdVwf~xV zt8^I*kEHOHT9QrB245f!xiQU|q*ZEwJ5EHx0n-yWl=6eSJSx!v`X*IqLFIQ4^Hetx zQ8As64VTh*+O>yE(!Bn+81Xsl`X{>Q2z2z6z9t z(VC5ZsWsNq27CX=_ zIKd(YWXjbwfOaz#g3);{!XghNx@<(%`5@_;vLZTlipQ!a7Fn0k67DyhjGgG*TA+Q}$MSd%1u2chx@GybG$=(;0|2o$dQ#j3(%7#_C4 z^a8~1cwEYN|90(Ul%gmbheMAxZ%ujsy{SD3yn|r`%cUnV3m_CX2%cp{1eyc=Cz4@5 z{p&mUniJZuKpEJ>?Y+?yYVelpOULqyP1*2l#TsEp!_5vCnwy)OnyQv-wFcJLFKqg+ z)Qh;S2^Z276B^JmSIOF@lJCt{B&?E;&0(3+G*xX>>r{u-P_$^l z+#+}wQLzI_RaY1y6}>vd@j`s;OFTsitnLAUunM{{J-S$x4JC*{@XT;#75nZ;u>fRn z?8(r8dPPEcCnQeu#IvxmA6oZQN2L6-vopp1EdA;&>BHO`y%XkuF}Ax!Lt|*!e>-;06jfQ8LG4Ymhg zcI=IZqOQc=MUCU+=$I=x{aGY%d(sl90htsUulq3;8fR%=;t3YC3A(+?#ocMjp z^YfPTQioSLjjf<$GfKqd4(!(!Xg6v`TXD#+q-m#R099P)yb!_-LaXgU z|0*;kdUc|VXd`v4?H*2-_64KBY4I98X7~GwJ#J++>kR=lk|Uf-@%IthxSauN5wESe zb@CY0@_?>XLCHH~d?N^5ptB+eIClX_V7QDL`_?3>F&M1qg|*S39P@*6%id>D8hAnV zw~>48&^R=AO#%}^3Ej-k6E z&bmQ&{9#UTXamPxTGqkL?<|sr5+k$2>1YOR3By?TwAs@>?T0~Ru$B16dg**KJ&3R% zwj70|2x5SWonWcNcUcu_1?{1ANa^zPh^H|AVV@X;tI)~;64{^;FqAMSwJEsorssN} zI@r@`OV<-i-9wVG-_v8{R=FXX+qK}N4t9Wz_VLN#JjnGVtLsNzk7q{{shge-RAOdT zge58O3d}%v)vnQ*yr%piJRePLqARa9i*FR2r@jxWW^Q@o{Vc3!G$nqubBBg$>_V{z`N{!A`Nf)-Ai zfbEZbJ#^xp<+`dWyAzRxg^r^Us0SDWDue}*uy3B2=MeYu!_4uG6pEtzi1zO0Xb=`T zm_oXKQ-Rk4(=nTVBM%!;JFlhVjx_sPLKERjVx0b{SeXOr?0dlvIe829xbOgCV_@7?XZK(u(4dFsxVH4Zo#eN^?2inON27hG3?_8RA{(*y+ zaQtnd%f7s@7?dFE;5X>g#kGaqvx)${Aau)7{lA;gST(Ial&0j1qEAC#Iu2h-kWqW8 zGV@h(z-@{G;*hOB*wBiszku<;?icqYxgEd_L@uBKAJJ!8`%YVzz&OUFN!)UJf2}KO z9ycoH_YSdNb+v*u6{qXd5y& z^eDB#PL{6<+He(Bh@{hk;J1@PFCkg){{Zel5x++Prfxf@ei?@W2gs++B>e&rH~HAA zUaNXdw;Al9S)1@-6xm+Ioe)~!VluvTCdi$6jlF6aGRxVBf~F>DsKpXZSE#cD-KN?s z-A5xUG0R_4x816Dz(pKlk14-e+ETO@ot2P_BxGV@=f*Mp&Scx~Ood8>es*QHT?!DS z_QVh5=5@F8RmNMK=x_`DxaOW&wynD6OOh=U3oFa>>aHAJSAFoNB3b$}nbKQIlb(s8 zD$eZ`1!wIoaW16o4C>|S(-((Q<#VX7=G4bZ4s_(RuG5_@3#k`4vbhZXy6L zAglQFx6o5w9i7x>6wqnvIwZA60UZbVoTc6f5Ilu9NF7F)Kr%*vl5P?W$5@=xYa-ye zqM*~0ipEs=xEL5}#*o|9Ia~GWxF(pm*n!d`6<&-)13)O3G(-<%kisNDY(hOZaV?TZ z?@)kTFq{KQ&^3V`6+|$fq?*?w@?3pd>&)#_KY(qoO|n?hVo8f7EtYJGCHE_`|6krg zf0?V367^SngM87M+ZOUQCOXg>jtI z7UMU?^DXCKPj$Zt%~vD&id>*YIKI`mkb0Rq7sGiA*ezhUfc+^0b_;^Lf}n= zz`cr$`2t`q69%2AzlxBFmXL4TvCrvVo5xNhd5JJ2mH4%+X3_mX@U1p^6?GKqOTb$YYOULy4{+Nb(d5(fB>@&naLdJXB+oJH+pj-6z9VXDje6EW8stKnv zt=Fho#^}#HmaODDSH1|OrN_Ajm&GG*1b_=ZW;2ADo=SCwSWh(*c8$|S0ljJsEo z-R>ODoyno+QNi&?Nb37Qa}*4{pfACTaMQnBVdRJx>y7O?ilT_&a(%I>uDv2%5$*dv zmF?g)U2JbkGH|Jx7p7Z(HzRI+L{>n5HMuBHF?RPz?D)ZFN-hvX!EI5g zkm1~$ZxHiKL^zhLwUpbd^WQTxOS17NojDT}Tp;8kl;}o%m`F6A^_+9*@vB?AncFt| zJ?hp^e&D8cQ=h#dk?af!b&r&Lgo86zyd2>q%8Na)iJ4Hh{YK8)qKUSA;^n8T8sC;A zBP-AcM{_aNuV@=8GNiS{4t-ndGh}5|YBbT+>TH#ps#2fUrnLnC764cPU;)5$0Ra0Y z`HQ3`JEoo|y4XnL>&oWVu!G&LW9%-}-_-sjZO<-%;F!9| z0Wa<**qH#GTq#cO>WH_medydEH)=(U-AkR9>60imfo!ssc9FHC2sdoMbAP)eeT7_E z0t+>hBcf)TNsSYR!xb=Kj7I>$vcYxa^t$0AOgLcGbhI^3vK0l2~Es6VY4O1iS{{CWrv)bGG$m zG?HN?0xQZjprVRoT6}$*CkGCkF(l)UG?g%2CzHT1Qk9s17lZ;h<8%r+b|kOiILQ&j zl>DwZ7E7OuUxqygV)@JWz=2`_h6wm5(D3yR^9h}CsdWYNh0y_TNYoq(M?f~ICIE7` zz-O@p<;`s977=hK`Yk(1<+>2Pjy_Vd>-whXp+_YK9?qjP?=hncP&NXHgM`YnH%Y;22nfQ}-stp3T)Dkady9fB3R(gM^-IpnhD$??)dhm? zyfGY#D3sx%(4_`mY$@zpOvu`wzW2dFdnPC3x{l(>3xR#B8+ZMo4vSvAf!u zduzsR-rP$~F*AAcvvVjtIyz9}3Qy5T>fC(7B)Wt&dIs;KXP+>+L|mdD{p9qX2a@`s z!)cJTQ4$CIPauay$NPW%sv*zP=10hWUF##_w@zECYjvAay>57h-TY*I(W#%rcQQG@ zlls)PD&WR9y*AWFg$8#*q~;Co^vg9exTCO@RI5Vdq$0u$m*ypM0>nQ+HYrY|vD`xK zcI25Qc?dC){v#zsqDl7w;qipWLIVY3J+cKv#23B9V*-_K`PsRWv!Y)qH6F@W5r3ec zp_qCQDV3TFR>T#FI5m|_gjG2Intej^c8n986m>^GP@?cx!D4DT#Y$3KH2i+4Ra;|3 zmzp$6Iy4*O2UBwu-_?3QV+(GtB#(kLQ#5{QWWA}n?FT3-x~qs!j5xO8i8efOOG zy}Cw8l45P(9k<)e{C>&{{%zHDZ zvC8Q{vjx&3=BfgHq#CF#KvEqyi!$3(fl}!kF8b8<981>4W8mVcB}=WJ#rj#SpXJ&4 zS=!x16N=hQ_vGYNW0R^XK5}iMIE(OghPgH6d|eUjepPl3jVX>N$ho=n5pm(LsUxhA zRNJ^#{b`VBxtyhz(k@r_*+N+_#6AUovV0*hNVN4QK z$xBO?$D@+WvRL$`K0RrIHf4jwNOK8@8Fs_wfU~gD!b%G(Ev&S#^2x)>!V6AjULG>( zhlv0j-5e1wo;D!rRi(WOA%IM)u^bqZBqq3nUhsWotFw{tn60UtrnYmx*w zQMutMj=BKUB4vw|EmD4NNO?mOLJzk4RT=lRh-{t%B0<1BVEX%y;{9baz`8J1M4`j5 zDUQ`Wv(eQ^`teJAds%mXV;7+H0oL*XKJ+nq3vw*Tu^{Kkf}Bmw3N+XrRAtl))1=IX zQ@k9`Q6yFtNoRT>HO0*;&ZTC%YlGu4> ztA4~&YzSiCLv|jZDV{Z8(%ht|1(whCoNJ0%o7^Z1z1iXqo3EzOMN@?Q#2~%NlWuYW zSFJmPh#|X)`=%sio)oHwjzedJ|AFdX7m}%8KN|50`jwHk{S8~HsJx;8Qi=X4`w7l?%!`l_he`M@7=+`9nhTt{}1W=eeB=gPl8)#a*H~0 zpY>sO*zKJsElJF`CD{S>8s{`PrY<@@JE@-}Fi)aXZVoX){Eh}UTN)v}1TUu0|E{#~ z4}<-ld}9^DGEcgRyi9kjJo2T0wWP$959kO$&y&B0Gwg9p02DQpl#Iov9wu1&$+g zahIS{OXAfMbBRV;-q|keh>3(ta+MKsAr8#Qx2djS1)beZu)7KENV?v3NdhgkP(AMx zCMb9ZXC7|~>ryl-q}-OgQ})!S&ZNW~{{G{Yq_SA-rP{0xZI)#kNWE;gObp>PTcOm0 z@^WqR8r}P14gvjIURF&VXBNm?uQ350r8h(9+yEp7DPt~k>@&pMlzxd1vO3AP-P`Up z6@J!6ZZ8U5PP(GF^YDey=*j7wjEhpUM@2C;53bf!jACRI2ld>|n;*{E>y;#>k%H+M z7Mw^olJ25rnM{ABmZ09-DUs=~&d4ow79X&VT5DzhF3G!r;Fj$MGmTJ{yq&6UNzf5W zaxA5~=!z8CbjATBbKnbX!??gJ6G+C0ZC#UV5>tl0?_s2fhh#aGKb2JfjR7A90_ZSN zX$xFU&@~B*Vg?B1nu-f3dCjT+S}O-#hyhHL*A)sd_8OCGqG)fmrZUps+w=%`>c#@$W>;q~27LA(Qc@+we!uW3O%F9n|EQmb=`6>H);mD73dXM=rby4SFyW6>tYPXny z_mF*M;tTJ0JEymZaCSi{Z)^CKAUvjwXQEfy`L+@Ba>$P$L+=neQhgOqzTBeVlyN+T zVm>BbNc4M-2=w?o`REFVAM^r^W+bG1MLG0TFFM;Q;vP9OLGvnrO8nYs_PEpRR3PF= zUx8aWqTSBPEPzTD?QKW@s7X12(NU4lN|a^ldeo6`g$)#%9dP?^x2JLUuX8EoWadcGZL}U^kwq z1uxZvZs+6_GaMIgTF%hKyEXkFx27E4nz3kfoJ_T84QgvpFCQfw>)<-h=#=KI6D842 zsCvVxq4d2;;Se}9`e=nA)RD4`1!!(T7FA4GMm2^tW#u$ja^XnTT|trQR_{xuEv?$k zMn+lPnnk8xT^gYcShrrnTx__%l3sgOkXu3CnjqKCwVJEgDu6Ntuwfy6y?#j&YPAg~ z_vKs}nAR}PB}JlaOEikx^ZVi7>G);kp_fD!?}jE@G>c(Qkgp%|lI@|%#%YShWWWob zpfIlQZbBJSJDW`Jkb)uNcL=F1OMXZ7o~tY)!=SIp)t8em2jFr-XPyiA1T(QE10E>u%jDhJE}CEi>fjtD2C_%duZm0!5MI4>JTQseY#dSVua zS*PFIJHE4WZKos?sal0}>dzV^mPuBwaVX*Yk6cvipeU+UQHuIAZvMVhwA5y>HkZN79|zDyq7#tJ)i4T~;m$mv zH`IV4HG63w^aq)Fo}dK4(4)=`17gJ^S172syQ-<<&`x&(st@6bt_li^E58$J2s#s0 zGSpmg2~p-yD#T=jvL>rbGaoqu__ z{%n+PIQ3;FcsxO(A|(0RUHReW;xmYL2|{9mq`ndhzp0)Y;Q*vtjD{m!SgoXp9jT}h zM*(1nGqHrCi4yTcT;Msxh816i8|E zIk6lCsKOjWFTK(oDFUDx8696g8X<>A5OqOfwx*-(jY(%2Siv| zOwFk=WYa~O8AJ;lk^@Ek_XlNilnwIbQuSAQ&b=UTh_^1#7z^H!dx_*Mr6Yakv3Prepm%m6Z$8e)BAMojPBXZQOwqX^ zFuMe~h&#PG&OrEJ6ws**)#reY041+gh>we)_grrYDVaAb+gNX$#CpNCq45ki+m74a z8T8Zp%}qL%>&f!ljrki$?RKM0YyPB<{;aDye^YW3rMfdwiNag#qTrZ%9^@#1-VvP< zx8dQ>DvDLn#plpPw?02uyTW%j*AfeNBy z9P$|hkj1fbtliMJ%v@Mu{M(?vlJGY%ag8*Xpq}7B)I*Ti@STU%r$q}qH=rejj1r#W zC>jFZtH6eu*UZ9-J)a8aT4M**hM2N%tFE>@K>}%L-%vwc7gMNf#wu6W=h;*nufsV< zC1xuYdSnD0^z)a2ii=rl@BB=rqcx1x6hDx2H1$GdcIYyCGa;CA@_!6X4sULpOPQL=Rb@ASr?ymCsZs09ir*>vS~Q-WH3 zD=O17L#rvpR8k8elGj3_>sl3sC(qOdg7mD~3_2U(tl3nBnpwAInpMvD0N#Zs^*U?g zGA_QNOJjz0fD-GeU;5isx3<1Vyf_7sk!}fVFL$56J|1Y6-YsmhJ%c9E34`2iQTs^; zTPO7Lll<+T^xKfW~^IMl{acvV=HgHH_3?RJvVVnC!ZtO_k;*sTpDX zctisLbt0Uk<1SI;?Buzs$?~bQGNWUAB}RwU_cio=*;mgi|B8}ENWjC0@XkR`9ytp) zjl8W=#I7h_AWPE~P!;79P2v!BS zM3Je5=5RZpAkEE+AVRLoh8A=gmj=*5MxvaZ06GE?_^PgaxCJhBZP8IYx+dY<1SOrd zYD8zmZ8uP5Q7q_=Epy~#al&)yF0h*?s_JIU)cPSq6`?-4&j(4pj%@Sx%S7HJx0iQ(go-9%LrMVKhws@0nA!Gdcn zGJGUe*-$+)>$J`? zm3N%h+FiVf4M`CVAVEYqe;2(i--{TVE3Ds9~hoP$8wy#iYg&)hnf_l!uiWVEW1fy{=^9E{;iyVQD zdXQb|1ju>XC6zEN1 z4X4VOD7;jKR_);|yVYte&4;EM-g6gT3a1oOE!9gkcLn_tcK(=6|1*QU_~m(k9rO`1{)PH1Q$+RgJ!DsK>SHpF zlhdbZ977K~w1hqi7=|T7@g;y{T*CFS2WMG^!mb80R??l%G`K_K(gv?)!LWqU4|5*{ zZh&vg_?b|LdXSgA{xZ2F&@X;>c{2}6-b`R{Q^wCYGc>XG{^zyDzF})V*RLst=I(3t4rAEEf9II6?yH;vc&PvA`RWp*TM$L@S ztx+>4QEJpp3xzrrQ-ZNZ#f-SEQ?p1B)Tp1{lDcGnL0&MK)i`S2#JDPAw!*iV zZlIC#uw-~@WDqN38?*Q_Z)x`=12sl806|o%J?CQi7hKu4-)c?(JcZ~Fn>lP_UAbw= zQk<_n`P$$kAKj@ByB>*a)>JnRwyP44PM>|t$wunF{2-QRf*F+Vx|&-HPnc4{W2YxS7YCQk~Ys0z-59Nt1yG$Ni}^U3+-*b+B5mSO zo0KT3*QpJYIsK~KR$}GoWS&b%osdGA+s)M`#M`;UKHqYEux1zge2LAV0D`X9}F~dQg>aN-7jg&@wiHs=sO*LAA1a8zJhAfsLSf zT3{ngMJd21kjwM6GOQfAz^8X8G96@m4Ujg6H6W}3vC#&^PE|UckCfib{HAv`hqaeG z7E@b1=~kq2v2t>er+0yrT)!{B zrb2MHDj|wmz87>jW4vsnYskYCp~n!7DF#+ACA0lC7MN*1ld#U84L$6D8#FJLpb5!3 zq5&Y3yoQcLX9@F$ffv#`VR+nCn2-U)otIYXTB&QL?!!pky{b$tpI~r7~;Y^q#+`I_yYfn-f{rhL{jUy9CC<^=Rk`eZ&*>8#N=5f#JJ&{n?G zSAr|bR(x9Vxj=kYFYN4FlZ^>AbM>J^HFuj_lpT_(yDbs6_^hwX9=xeau%W){34&fj z=$s&6?%JF}|GV-d|9BM>FqGc1tp&L=^2D18@vvtL+?+|)LO_hkhz3)YtsV~PjB7`y z%zM9DA#KH8y#y5b`BIo^qOh2WAmrQqy}{lc`ac zAV-1VM7~grWFG(OswE``MP;lgX#OK){HyPZZ<}GmgKUz16b^yP0dlbY>sNazK9XOv zQD%^ux6|h^~ zrsqNObQ;0HCT4uHQ8PT)idmoOs}V3vv6^(4R@?RpFZyw13mexFHdY(X6$NWT$5L~; zJv{SBruL@F;GONNQ|oFv=(XJZ>be}6<`>r0HpUaJwwKncs6)ms)FemcmU#NEcVUs+ zE99&KsFydo!dXz?LtxzkkM9>Hnln{|y0|y{hyNGWhz?;rJG*57gAq z&(T?K2nfbE{j3x;R|FZ0;At?N<~v8evsYnJ%4!eW4Fz@93y=@K*go zZmXzPSy>5#NG`E-(%sxO$Ew;lohwk{&7@jQy*MM7W9XfG$Q>j7?DXD84oB_@oL`_j z7`U0|p{7sPxj?=L9ptJZIvk^jm+(!NnkxARnzuS&msotS|31`dYQ_b~l^Yh&d6*KJB7GZ7k)sdbrWX0Fz4u3#cIC6SorX0S056Q;!@Ia1HU z$S$fTwL|%4c85Z0x#jVCvFKSoTxqjTLhbac(zhsVrEc{_`NP09ux&_VIb8c{i1`R3 z&)qu0{GIs25YL9qx9CUBcFP#5M!ZDX5?{ZjcKg3oi&q zF2Es-Z8!6a#jTr)Tk~ru?_n(}VlblHD@w7jrlNCDm4!yF_3#ejdxBULsj%0a6*wMryq3(*I6+_}3eKZlv(!;o zEBCG3H<9~`{R|D=3_{{RD`dP^m8eB7?80qFb73i}FPSPJBx5EbMailZ0b!Yjf>HO! zR4htv3W5gGfvi^US-EF))W38=Cf}IEt$w*9>G;rZG`LAW`Hb%J55@8gsCO7<{Yg(9 zRsMJa$rz=hHqQ|X&fLyHr`JCj{6Xor_kZ92{l~xld5b>$5BJ~i9kSr-|Igl+Hn(kS z4gVF4zO?Dcy58a}-(tsE-fO$Lwv)W`RGGOD2}yWF0V)AXRy+Og&tM@5t^z1JUc^pk zVv)qb!FJBUS^wXu_x4%%cPwbq2yuAxAFP8uQ|9sr*_he2}v9T*_WPHcOr>igY zh9|}Pv;%{5Z<7v+N5@Am-@H0{eRT3V(*TBCu3qeEl`X|AMVs>gE|^%IxNI;YXpB9q zCOVlPJcU|T%Qwx5R?X^}v8=sO zi@Is&#OfQckRJgr{VLYsvjy^`cT{2qRmz?nz2H>tI7?AI?~hyJuG_MMt^j!r^C4jc zx7`vE^DAKr9qhM-j-vqj)7({EIcx)eNn;Iuii@R-lVonCUAk+bh)l5_N~cEXoC$u8 z;PbjRF`ppzi5R<$l_>?{BHl!p@RQ>TS>}%)2~ayxCdHxFw;=hfq41I!s*Aj0;AgrR zEYinu?ZQMPiCqqMR2WhF?}8w?jBAGosjFJ& zpI#%6E|K8y`8O9v3~T%>8`-L!ZW+^r45fkFecl|SYunspI49uQ2Jez7@BBfs-1_By zZ8kN@L}ZxH7K~c7-!k|l1!T#XWkIcWBTnLR*qSyK4p|py@kD!cbk@y|bGc^bsXmU` zcQ$Ghqruz1d)IJvy5>m#mPWGwL@>a7{m%d2^rwP?aQnJ z*#S8~E+Fj8XEKwpy@4P*#ZPx6&rGHN@}M^Bo!ox0{_;JNbo&F{o_$t;d|XQXDlG3; zt6wuWZL9Ugew1ASU-i|g^E-8Zr_S%x`JFodvCGY!I)59X=Wt^dwl6ALtuoZeMb^nc z+EQsCVGQg&qjSBttv)D>&03L=!^?w#7X`B9y^ud*jXi)IFgiy72^uQJ6PQF2E>MW6 zKO~U&8L=jAyceQCDkEaxm=f0IV(WGGAV9=OOeWRQXA!64aq{fDbd8gpq+!ky^mpwvCytQ% zAloQ`uUzaZUS3c-8$oZDMdCg30;nXa3_gwmpfN-%uUIudR-b0p!lp%La;+PbOoUxx zFu4P4K2B+tP6frG>Ny+ONxnY|E}UB|y+{ z8_L4jG1>~lCVHldP7`Z7PQ+eJj1-ERq%*{2xy4xF$6BnP__XMFM+hAgCREi=$-T7E z-(BD(QmQlMa;pATKAX0!GI0_9t3R70AG_5%UFhl?oFNplz75E=p zuI)~`@5%#p<$=2LKwWtt`|0h<19jzro@r#(tGd*aSBS4@IJ*+>8p-MgfD%#t1^Crh zB8TzaMj6}qwnoG*0(KXzv)I>2(qr)kU><`-BUeStt#__E_l8 zYfeT*2aS(MIzlm`&8H;BFmq7!;aKW-(7b_iyl7L;*j;k$FF6%^rFbQ;{O*2?TL-Tj zTelu5J$h4@&*U0GUzH+nM2eE;Te>>G(9smnUFFIe}_>_1`*PEa2}#-ceU6M#8bL6+Delx=$yP;XYuO{K)m!H^8e=P`i#H<+Qd zkVmBD+edz1+QFgf+Y^20UZo*~axkV5@c|{kn?g+bLn3;aBR-`*Sm7X!`vs7w83qil zl@qEKeq(qm$jC1mo<@q$N~}anxDe+$#De+h?=S-ar8DG%XrWNY;~1r+8!&-vFGPzF zdod_V_9Xu4X9D#R(sPb{um>;^kYs5^hO`r{7#i|{v5^LsY;uP6*u@0RA@`>8bMaU* zS3NxO%!5IILa>5_Gr%c8ghin);fU^_$Ah)*>dy!a=e#0iCfpLA1t zyWHAQz_BOZ?k%7vs9D^Ln5-9?B+JD4Q^=-+?4_}t?G?gMROeCI-b~BmG{f`EE2?_-L{WkMOZa8Cvn_noVBb&F&U|j>+L_ z?EQ5zUmi~9bjDXB|855Fj+qoIFWKbUlZ$hHhq;ny_xrtT$;!$R_z2loSt;U8zjwYA z=ir)B-dc_Z*})lQ65^$z>g}y@4rsRTNzxO_hZKNmr z$$x57PGLCFH0Mf@vRvDMdh)Hf&=lWZV1jyY4-Sv|y$=!bG{MV!BuM=8azd! z`~XOpi)~v*DT|5PA0?o@i5+q(*xNxNrFUO5bk&9}K{wr~B`?*6cJM2%DKf<@=Y`vT zXBpAHv|W&;Erv@w6m2F&vj~@klq|zlZE7%$22C~dv6&l_8@curGcK$8RaC2wEpi}e zCfKl`ncBhk#;xmheJG}~M$4|b`dm*Ko_YW0-WTW*c7*46G}!J*Njl@=*Wm}N>p z3kR~Sp>Z>EGH@8ToCZfOo+;N8lw951Rx*3ja_%;{lsT99E4li)J1nX@pldthy10Fc z+m4WTguG2c-XJey#{g;=z;m|Hm72+%!Y2)c?ib53IkYkJ8kPOMy@3-e~fwU5r8sl;{ zU}Zck+JJ`yf@BGjjL$`gd-8em<=`@BlQoQeLvr@{J>XMFv>DehXzCgH#l!wD$~@OM zfL`l4kU+&ggX;SsQRa)-pBcp(gZM!K-@(0^bLSD`ra8AbC6(4(IqE*pvFvhO|Aj$7 zS8;^Je^V2uXe^1#02Ix?YWGO%Xb!-TNYE5W^3|D%J2P=-Chp9{ZN%qhS@`{VSMML} zAGDA@==g!M-;|fNiFrgxRU6}#m6^S5Tdd}}kIPV_I)9HmrIS%aw6*im7J2OVKIzcD zCV6c?C1HV^?>>#yIL7LBH^34_QM%3C3@`DH+&-rBDc?Dd>N4l6xLPCJx#86_3*{ct zBK{Vb84}X2EH0$k25sI;3!}D7u2R4$zGJGK`H*YjU3n|()LQbsg+!ZOt%}>FMto>y z`txFGDvoft{`}-3isZBUG0j z_6@oy<1-t9I;mSLYRe6^b5z(Db{Qi+M42jEsY{6RSm$N|FAJ1T7)vseY3ffWi7@aTL2#Ra6F zcp_$TSi#boS^yJ3LtizT?sWPo zm`eA&g`f|+nnbOW6m~~a*hGZu^>7s}Y~%;|>D#{xdbkobUCW9&0rDCWKU*%lpsp*? z&3z?Iq0?SY5+TvVllUGGR7+Z6g`fMCMLI+ zT)HdsIx21=S6x|tzwMrM{i?3c)3?hr9C}e8D=P~rGFZm~V>2nc2B3X%?E;96LUT>G zH958mZQQeK*N=BYs_ioQ;f zgh-7OjC5p2L^z%!fCV07=piAbLM4TI)Ne5bZzs^$3p9moQsTj1{K7_H`GA9OGTKG+ z=MOlz#Kio08ijz7C9_~v}N{oc}>+8O}awiX4Y4NLwgFN ziV<35{Vm1IR2TV>L*T(Qlu|LPc%HJ_A*B!sb>3cp2oKkBh-3nY!%=`@a6^(p+DU~+ z$6y^a2Ki%G)DV<>VX^xA_q6bjl3I zo9nQygip4I2A7Mbn8RN!ioUyWUSpNN})xXmvNgp|tM<(=N%0i=?$<%1@rLW*3-GI2 z-r75!0k8ayH|!02PM5qahBc~rvui0jGl0iQiPNmR_z3!O1yG&Swjb-$R7KDbK}Jb? z>aI>gd}L(@HGurwCXX%jQJGRkjiuK|N^Xv;QmVzrY(X(87HqU8II_95JyFH2)DavB zubkvGAT}{}7UV{CEJzEBi47AZ#!Frzzc1rolFG({;!yGn2WW{xaS~?$uHYJsLpleT zcV2)Gp|-w?f}&TherS;CAjOKOlu^(*`0x(>sMx~|)O zf5WA=-0xzt!yz&_lVmxr2Dd+ zDwoDy*tyuXSFfvL*97s1Msv*Np>0OY)Am(@;BV@7BEPw0lMMo)0k93BFpFQkU}$#j zK>|hyL_!kc6fpS;KtRwcSOYIa(u%KCPRb@c7!rc`N?h{zG^EjFs;s+&`t2pT&dsGI z)&GQ`uVPPSokX+V_bv8j>VST`{-8r>i`EH2m;xlL^d@1p-!VC(5m6< z@DOX1uBoh;D7emh$YE)_AAQlu7ka}r>_7hQ8NXild-AW-HAkft#sz>cP%!CMztaf7_YtbM?a@=IoLmC^c7 zFvuTM?`JkE5lcTcD#MZ5_*Hpb7naw=-Pht8C9MS>l zlz%ovJmtpCkk3&~o2x*B+Xr(ueQSxuIb%hZX6rUI8#!#M)f$;0+-?_|BU+oW6DWfL&%ZB-lF zY}#!^s_e6+J9Q*eF<~FZ@es;T3*ZFe&L-R0WSYdmjcrDL?(wvdOTKBB%_To5nfz>#T0=7Gz5h5&UNbnT(@+z z$wP#oJXUgWR1rM<9R>m7r!auTb9d9ow6;ve@B;j*jZ=8?neaMdkO5zl`o)Cjl=mf% ze>FYGqGI}YjPx_@*pW%J5GRI=eL$uN`bkR3X{!>)qsyOAb}-F_s-SSm%Ps*x{tO>i zM0}{d`5uj76!5A9Hg-oj3#8on$|03g8PI!5;Z%Wt-D%F@RhF(Py}tVD<2Z_aB_V9@ zwAJMuYUyyitcXvDmpVWV+z`#i*6gkFzHCC<_PU(IY!lCru#3}O-DCFBUq zkW5%%9H=cA#FIZx*(I&=N=skcIHld{>XHGI1uf*XiwkgtBbSTK%K)~mA3%k5>zOdu zR~6MO)$#ZQn~IyCplt#_3)N8qvL%g?iQ*;-A$l#>4`uIzc(>_8rnaf<&%}jT>GN`SOjyyLHgNY|MbH zrKdA@wS7K2-q&U2eK;!frXsXM9i9u!n$j52v|LT}#}8+MB7oJ9lLEvB1V!UpM@l3X z0cKoQku=poXe;hrw!sTSDl9|U7Z#SEWS9Tw$4*jzSh)on8{Xix%ID}wpMf{~6+S;| z)vwU1;Ssn(t9twAztQ?u8qM8V>=jzwD}PnEZ(^Cv%npn{_X6ZkP&g!d95OX2iRLK89t`5<2Vl=^ zeF*lz>BWtZu+#0=vh`Ijq#c_POuJ1FhK?7uLDY3b7rn zaR$RpXW!^tIa-w%IpzN{TfF{~k`_zXJ-UtEDUvUa-W*o=7wKJtC#325!4u+A)!~mH zqcs=I^*1kEFE}0Cq#1%Yu~+eFiwb3pdVT2zqi2RE#5_=9APhuIrIjfbZFsH+sy1V< zT3SjZ;(MwR4yt=dq z|7f)E(PSAO;bV_7wBpk>o4mZ6-7Wqclf%{6`|D)BJeJ;ifx(~n+ks~A7Mj!pRpbsve|f6mR~&3=DetH#=$p~O|E zH~*1iy7?VA&i!60Hd&_dcsOceEKNs)IvUiF2APq~=H7p_qW*JDrvK(;t!s502(+DV zbq-3^B|t4t6JtTHgL^d`6bG|Ok(kFW4#P~{WngSKP#GF^YBe8u)K@;(5UfxT?CDrW zJ&4wB1wTF2d#B~U#}j>0>klqVr@Gg|HXFO^C}q{ef{w`AvrTo|s&iX160=rf?IBh; za5&3&k@o>K3$w|%YC1*T=no{`v9G{{;X5|NjWA6O#j{1^^+DspkLy literal 32994 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%0Di6a^A#g*kgj36Cg?;82tn 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 ZxStO*vA*BD002KfiTr{^RJou4{|6Ct7%Kn( diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index d145c8939094c574aac4b8fbf5b83121686d0a13..688e01963366a9415a90b54451b0ff6a5fb1f9f0 100644 GIT binary patch delta 8442 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 delta 8337 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 diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index b2a53e031c8e1d6baf37e6dcefd13e53135a8f2b..5582011e53875be4206e9a04bbc0c6b8b0479ff3 100644 GIT binary patch delta 13789 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 S|1SUl|No)%0tHF+8v_77Wi4R< diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index cc24b84900760c236390a361c651559253cd134f..452cdab557321e5fc603d5e550c53f3d8e4154ef 100644 GIT binary patch delta 4070 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 W_rEG#|9=1g0RR7tLLp48E&%|()6%j4 diff --git a/cli/util/api.go b/cli/util/api.go index b35974c69..3d06f9e31 100644 --- a/cli/util/api.go +++ b/cli/util/api.go @@ -319,11 +319,33 @@ func GetFullNodeAPIV1Single(ctx *cli.Context) (v1api.FullNode, jsonrpc.ClientClo return v1API, closer, nil } -func GetFullNodeAPIV1(ctx *cli.Context) (v1api.FullNode, jsonrpc.ClientCloser, error) { +type GetFullNodeOptions struct { + ethSubHandler api.EthSubscriber +} + +type GetFullNodeOption func(*GetFullNodeOptions) + +func FullNodeWithEthSubscribtionHandler(sh api.EthSubscriber) GetFullNodeOption { + return func(opts *GetFullNodeOptions) { + opts.ethSubHandler = sh + } +} + +func GetFullNodeAPIV1(ctx *cli.Context, opts ...GetFullNodeOption) (v1api.FullNode, jsonrpc.ClientCloser, error) { if tn, ok := ctx.App.Metadata["testnode-full"]; ok { return tn.(v1api.FullNode), func() {}, nil } + var options GetFullNodeOptions + for _, opt := range opts { + opt(&options) + } + + var rpcOpts []jsonrpc.Option + if options.ethSubHandler != nil { + rpcOpts = append(rpcOpts, jsonrpc.WithClientHandler("Filecoin", options.ethSubHandler), jsonrpc.WithClientHandlerAlias("eth_subscription", "Filecoin.EthSubscription")) + } + heads, err := GetRawAPIMulti(ctx, repo.FullNode, "v1") if err != nil { return nil, nil, err diff --git a/cmd/lotus-gateway/main.go b/cmd/lotus-gateway/main.go index 89b2152dd..2023551ef 100644 --- a/cmd/lotus-gateway/main.go +++ b/cmd/lotus-gateway/main.go @@ -162,7 +162,9 @@ var runCmd = &cli.Command{ log.Fatalf("Cannot register the view: %v", err) } - api, closer, err := lcli.GetFullNodeAPIV1(cctx) + subHnd := gateway.NewEthSubHandler() + + api, closer, err := lcli.GetFullNodeAPIV1(cctx, cliutil.FullNodeWithEthSubscribtionHandler(subHnd)) if err != nil { return err } @@ -195,7 +197,7 @@ var runCmd = &cli.Command{ return xerrors.Errorf("failed to convert endpoint address to multiaddr: %w", err) } - gwapi := gateway.NewNode(api, lookbackCap, waitLookback, rateLimit, rateLimitTimeout) + gwapi := gateway.NewNode(api, subHnd, lookbackCap, waitLookback, rateLimit, rateLimitTimeout) h, err := gateway.Handler(gwapi, api, perConnRateLimit, connPerMinute, serverOptions...) if err != nil { return xerrors.Errorf("failed to set up gateway HTTP handler") diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 445b24f82..299d71308 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -3073,43 +3073,40 @@ Inputs: Response: ```json -{ - "subscription": [ - 55, - 105, - 12, - 254, - 198, - 193, - 191, - 76, - 59, - 146, - 136, - 199, - 165, - 215, - 131, - 233, - 135, - 49, - 233, - 11, - 10, - 76, - 23, - 124, - 42, - 55, - 76, - 122, - 148, - 39, - 53, - 94 - ], - "result": {} -} +[ + 55, + 105, + 12, + 254, + 198, + 193, + 191, + 76, + 59, + 146, + 136, + 199, + 165, + 215, + 131, + 233, + 135, + 49, + 233, + 11, + 10, + 76, + 23, + 124, + 42, + 55, + 76, + 122, + 148, + 39, + 53, + 94 +] ``` ### EthUninstallFilter diff --git a/gateway/eth_sub.go b/gateway/eth_sub.go new file mode 100644 index 000000000..2f82fc32b --- /dev/null +++ b/gateway/eth_sub.go @@ -0,0 +1,70 @@ +package gateway + +import ( + "context" + "sync" + + "github.com/filecoin-project/go-jsonrpc" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/types/ethtypes" +) + +type EthSubHandler struct { + queued map[ethtypes.EthSubscriptionID][]ethtypes.EthSubscriptionResponse + sinks map[ethtypes.EthSubscriptionID]func(context.Context, *ethtypes.EthSubscriptionResponse) error + + lk sync.Mutex +} + +func NewEthSubHandler() *EthSubHandler { + return &EthSubHandler{ + queued: make(map[ethtypes.EthSubscriptionID][]ethtypes.EthSubscriptionResponse), + sinks: make(map[ethtypes.EthSubscriptionID]func(context.Context, *ethtypes.EthSubscriptionResponse) error), + } +} + +func (e *EthSubHandler) addSub(ctx context.Context, id ethtypes.EthSubscriptionID, sink func(context.Context, *ethtypes.EthSubscriptionResponse) error) error { + e.lk.Lock() + defer e.lk.Unlock() + + for _, p := range e.queued[id] { + if err := sink(ctx, &p); err != nil { + return err + } + } + delete(e.queued, id) + e.sinks[id] = sink + return nil +} + +func (e *EthSubHandler) removeSub(id ethtypes.EthSubscriptionID) { + e.lk.Lock() + defer e.lk.Unlock() + + delete(e.sinks, id) + delete(e.queued, id) +} + +func (e *EthSubHandler) EthSubscription(ctx context.Context, r jsonrpc.RawParams) error { + p, err := jsonrpc.DecodeParams[ethtypes.EthSubscriptionResponse](r) + if err != nil { + return err + } + + e.lk.Lock() + + sink := e.sinks[p.SubscriptionID] + + if sink == nil { + e.queued[p.SubscriptionID] = append(e.queued[p.SubscriptionID], p) + e.lk.Unlock() + return nil + } + + e.lk.Unlock() + + return sink(ctx, &p) // todo track errors and auto-unsubscribe on rpc conn close? +} + +var _ api.EthSubscriber = (*EthSubHandler)(nil) diff --git a/gateway/handler.go b/gateway/handler.go index 8760598cc..54ab2467f 100644 --- a/gateway/handler.go +++ b/gateway/handler.go @@ -27,7 +27,7 @@ const perConnLimiterKey perConnLimiterKeyType = "limiter" type filterTrackerKeyType string -const filterTrackerKey filterTrackerKeyType = "filterTracker" +const statefulCallTrackerKey filterTrackerKeyType = "statefulCallTracker" // Handler returns a gateway http.Handler, to be mounted as-is on the server. func Handler(gwapi lapi.Gateway, api lapi.FullNode, rateLimit int64, connPerMinute int64, opts ...jsonrpc.ServerOption) (http.Handler, error) { @@ -90,7 +90,7 @@ func (h RateLimiterHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { r = r.WithContext(context.WithValue(r.Context(), perConnLimiterKey, h.limiter)) // also add a filter tracker to the context - r = r.WithContext(context.WithValue(r.Context(), filterTrackerKey, newFilterTracker())) + r = r.WithContext(context.WithValue(r.Context(), statefulCallTrackerKey, newStatefulCallTracker())) h.handler.ServeHTTP(w, r) } diff --git a/gateway/node.go b/gateway/node.go index f18189ae2..bcb515b3c 100644 --- a/gateway/node.go +++ b/gateway/node.go @@ -117,7 +117,7 @@ type TargetAPI interface { EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error) - EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) + EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) } @@ -125,6 +125,7 @@ var _ TargetAPI = *new(api.FullNode) // gateway depends on latest type Node struct { target TargetAPI + subHnd *EthSubHandler lookbackCap time.Duration stateWaitLookbackLimit abi.ChainEpoch rateLimiter *rate.Limiter @@ -141,7 +142,7 @@ var ( ) // NewNode creates a new gateway node. -func NewNode(api TargetAPI, lookbackCap time.Duration, stateWaitLookbackLimit abi.ChainEpoch, rateLimit int64, rateLimitTimeout time.Duration) *Node { +func NewNode(api TargetAPI, sHnd *EthSubHandler, lookbackCap time.Duration, stateWaitLookbackLimit abi.ChainEpoch, rateLimit int64, rateLimitTimeout time.Duration) *Node { var limit rate.Limit if rateLimit == 0 { limit = rate.Inf @@ -150,6 +151,7 @@ func NewNode(api TargetAPI, lookbackCap time.Duration, stateWaitLookbackLimit ab } return &Node{ target: api, + subHnd: sHnd, lookbackCap: lookbackCap, stateWaitLookbackLimit: stateWaitLookbackLimit, rateLimiter: rate.NewLimiter(limit, stateRateLimitTokens), diff --git a/gateway/node_test.go b/gateway/node_test.go index 805767dc6..aa33e8bfb 100644 --- a/gateway/node_test.go +++ b/gateway/node_test.go @@ -89,7 +89,7 @@ func TestGatewayAPIChainGetTipSetByHeight(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { mock := &mockGatewayDepsAPI{} - a := NewNode(mock, DefaultLookbackCap, DefaultStateWaitLookbackLimit, 0, time.Minute) + a := NewNode(mock, nil, DefaultLookbackCap, DefaultStateWaitLookbackLimit, 0, time.Minute) // Create tipsets from genesis up to tskh and return the highest ts := mock.createTipSets(tt.args.tskh, tt.args.genesisTS) @@ -245,7 +245,7 @@ func TestGatewayVersion(t *testing.T) { //stm: @GATEWAY_NODE_GET_VERSION_001 ctx := context.Background() mock := &mockGatewayDepsAPI{} - a := NewNode(mock, DefaultLookbackCap, DefaultStateWaitLookbackLimit, 0, time.Minute) + a := NewNode(mock, nil, DefaultLookbackCap, DefaultStateWaitLookbackLimit, 0, time.Minute) v, err := a.Version(ctx) require.NoError(t, err) @@ -256,7 +256,7 @@ func TestGatewayLimitTokensAvailable(t *testing.T) { ctx := context.Background() mock := &mockGatewayDepsAPI{} tokens := 3 - a := NewNode(mock, DefaultLookbackCap, DefaultStateWaitLookbackLimit, int64(tokens), time.Minute) + a := NewNode(mock, nil, DefaultLookbackCap, DefaultStateWaitLookbackLimit, int64(tokens), time.Minute) require.NoError(t, a.limit(ctx, tokens), "requests should not be limited when there are enough tokens available") } @@ -264,7 +264,7 @@ func TestGatewayLimitTokensNotAvailable(t *testing.T) { ctx := context.Background() mock := &mockGatewayDepsAPI{} tokens := 3 - a := NewNode(mock, DefaultLookbackCap, DefaultStateWaitLookbackLimit, int64(1), time.Millisecond) + a := NewNode(mock, nil, DefaultLookbackCap, DefaultStateWaitLookbackLimit, int64(1), time.Millisecond) var err error // try to be rate limited for i := 0; i <= 1000; i++ { diff --git a/gateway/proxy_eth.go b/gateway/proxy_eth.go index fcad213d9..9518b2a15 100644 --- a/gateway/proxy_eth.go +++ b/gateway/proxy_eth.go @@ -3,12 +3,14 @@ package gateway import ( "bytes" "context" + "encoding/json" "fmt" "sync" "time" "golang.org/x/xerrors" + "github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" @@ -352,7 +354,7 @@ func (gw *Node) EthGetFilterChanges(ctx context.Context, id ethtypes.EthFilterID return nil, err } - ft := filterTrackerFromContext(ctx) + ft := statefulCallFromContext(ctx) ft.lk.Lock() _, ok := ft.userFilters[id] ft.lk.Unlock() @@ -369,7 +371,7 @@ func (gw *Node) EthGetFilterLogs(ctx context.Context, id ethtypes.EthFilterID) ( return nil, err } - ft := filterTrackerFromContext(ctx) + ft := statefulCallFromContext(ctx) ft.lk.Lock() _, ok := ft.userFilters[id] ft.lk.Unlock() @@ -417,7 +419,7 @@ func (gw *Node) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) } // check if the filter belongs to this connection - ft := filterTrackerFromContext(ctx) + ft := statefulCallFromContext(ctx) ft.lk.Lock() defer ft.lk.Unlock() @@ -434,18 +436,82 @@ func (gw *Node) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) return ok, nil } -func (gw *Node) EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) { - return nil, xerrors.Errorf("not implemented") +func (gw *Node) EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) { + if err := gw.limit(ctx, stateRateLimitTokens); err != nil { + return ethtypes.EthSubscriptionID{}, err + } + + if gw.subHnd == nil { + return ethtypes.EthSubscriptionID{}, xerrors.New("subscription support not enabled") + } + + ethCb, ok := jsonrpc.ExtractReverseClient[api.EthSubscriberMethods](ctx) + if !ok { + return ethtypes.EthSubscriptionID{}, xerrors.Errorf("connection doesn't support callbacks") + } + + ft := statefulCallFromContext(ctx) + ft.lk.Lock() + defer ft.lk.Unlock() + + if len(ft.userSubscriptions) >= EthMaxFiltersPerConn { + return ethtypes.EthSubscriptionID{}, fmt.Errorf("too many subscriptions") + } + + sub, err := gw.target.EthSubscribe(ctx, eventType, params) + if err != nil { + return ethtypes.EthSubscriptionID{}, err + } + + err = gw.subHnd.addSub(ctx, sub, func(ctx context.Context, response *ethtypes.EthSubscriptionResponse) error { + outParam, err := json.Marshal(response) + if err != nil { + return err + } + + return ethCb.EthSubscription(ctx, outParam) + }) + if err != nil { + return ethtypes.EthSubscriptionID{}, err + } + + ft.userSubscriptions[sub] = time.Now() + + return sub, err } func (gw *Node) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) { - return false, xerrors.Errorf("not implemented") + if err := gw.limit(ctx, stateRateLimitTokens); err != nil { + return false, err + } + + // check if the filter belongs to this connection + ft := statefulCallFromContext(ctx) + ft.lk.Lock() + defer ft.lk.Unlock() + + if _, ok := ft.userSubscriptions[id]; !ok { + return false, nil + } + + ok, err := gw.target.EthUnsubscribe(ctx, id) + if err != nil { + return false, err + } + + delete(ft.userSubscriptions, id) + + if gw.subHnd != nil { + gw.subHnd.removeSub(id) + } + + return ok, nil } var EthMaxFiltersPerConn = 16 // todo make this configurable func addUserFilterLimited(ctx context.Context, cb func() (ethtypes.EthFilterID, error)) (ethtypes.EthFilterID, error) { - ft := filterTrackerFromContext(ctx) + ft := statefulCallFromContext(ctx) ft.lk.Lock() defer ft.lk.Unlock() @@ -463,19 +529,21 @@ func addUserFilterLimited(ctx context.Context, cb func() (ethtypes.EthFilterID, return id, nil } -func filterTrackerFromContext(ctx context.Context) *filterTracker { - return ctx.Value(filterTrackerKey).(*filterTracker) +func statefulCallFromContext(ctx context.Context) *statefulCallTracker { + return ctx.Value(statefulCallTrackerKey).(*statefulCallTracker) } -type filterTracker struct { +type statefulCallTracker struct { lk sync.Mutex - userFilters map[ethtypes.EthFilterID]time.Time + userFilters map[ethtypes.EthFilterID]time.Time + userSubscriptions map[ethtypes.EthSubscriptionID]time.Time } // called per request (ws connection) -func newFilterTracker() *filterTracker { - return &filterTracker{ - userFilters: make(map[ethtypes.EthFilterID]time.Time), +func newStatefulCallTracker() *statefulCallTracker { + return &statefulCallTracker{ + userFilters: make(map[ethtypes.EthFilterID]time.Time), + userSubscriptions: make(map[ethtypes.EthSubscriptionID]time.Time), } } diff --git a/go.mod b/go.mod index d5059ac30..120a10db1 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,7 @@ require ( github.com/filecoin-project/go-fil-commcid v0.1.0 github.com/filecoin-project/go-fil-commp-hashhash v0.1.0 github.com/filecoin-project/go-fil-markets v1.25.2 - github.com/filecoin-project/go-jsonrpc v0.1.10-0.20230116095704-da92324a76e1 + github.com/filecoin-project/go-jsonrpc v0.2.0 github.com/filecoin-project/go-legs v0.4.4 github.com/filecoin-project/go-padreader v0.0.1 github.com/filecoin-project/go-paramfetch v0.0.4 diff --git a/go.sum b/go.sum index a6d00a0b3..95813bdcd 100644 --- a/go.sum +++ b/go.sum @@ -340,10 +340,8 @@ github.com/filecoin-project/go-hamt-ipld/v2 v2.0.0/go.mod h1:7aWZdaQ1b16BVoQUYR+ github.com/filecoin-project/go-hamt-ipld/v3 v3.0.1/go.mod h1:gXpNmr3oQx8l3o7qkGyDjJjYSRX7hp/FGOStdqrWyDI= github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0 h1:rVVNq0x6RGQIzCo1iiJlGFm9AGIZzeifggxtKMU7zmI= github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0/go.mod h1:bxmzgT8tmeVQA1/gvBwFmYdT8SOFUwB3ovSUfG1Ux0g= -github.com/filecoin-project/go-jsonrpc v0.1.9 h1:HRWLxo7HAWzI3xZGeFG4LZJoYpms+Q+8kwmMTLnyS3A= -github.com/filecoin-project/go-jsonrpc v0.1.9/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4= -github.com/filecoin-project/go-jsonrpc v0.1.10-0.20230116095704-da92324a76e1 h1:GcF3gSvesv1epB2SrTfalrYvhFzT1UnmSS1Bh+jprQY= -github.com/filecoin-project/go-jsonrpc v0.1.10-0.20230116095704-da92324a76e1/go.mod h1:jBSvPTl8V1N7gSTuCR4bis8wnQnIjHbRPpROol6iQKM= +github.com/filecoin-project/go-jsonrpc v0.2.0 h1:lhcu0Oa7xxwdclU4EyBPgnZihiZOaTFiAnW6P3NRqko= +github.com/filecoin-project/go-jsonrpc v0.2.0/go.mod h1:jBSvPTl8V1N7gSTuCR4bis8wnQnIjHbRPpROol6iQKM= github.com/filecoin-project/go-legs v0.4.4 h1:mpMmAOOnamaz0CV9rgeKhEWA8j9kMC+f+UGCGrxKaZo= github.com/filecoin-project/go-legs v0.4.4/go.mod h1:JQ3hA6xpJdbR8euZ2rO0jkxaMxeidXf0LDnVuqPAe9s= github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20/go.mod h1:mPn+LRRd5gEKNAtc+r3ScpW2JRU/pj4NBKdADYWHiak= @@ -826,7 +824,6 @@ github.com/ipfs/go-log/v2 v2.0.1/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBW github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= -github.com/ipfs/go-log/v2 v2.0.8/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= github.com/ipfs/go-log/v2 v2.1.2/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= diff --git a/itests/gateway_test.go b/itests/gateway_test.go index b9c861bf3..d20b3bd1a 100644 --- a/itests/gateway_test.go +++ b/itests/gateway_test.go @@ -290,7 +290,7 @@ func startNodes( ens.InterconnectAll().BeginMining(blocktime) // Create a gateway server in front of the full node - gwapi := gateway.NewNode(full, lookbackCap, stateWaitLookbackLimit, 0, time.Minute) + gwapi := gateway.NewNode(full, nil, lookbackCap, stateWaitLookbackLimit, 0, time.Minute) handler, err := gateway.Handler(gwapi, full, 0, 0) require.NoError(t, err) diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index ef7f7ff6d..898776d10 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -6,7 +6,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/filecoin-project/go-jsonrpc" "strconv" "sync" "time" @@ -18,6 +17,7 @@ import ( "golang.org/x/xerrors" "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" builtintypes "github.com/filecoin-project/go-state-types/builtin" From 965b1cf03cdaca6e4916e930ebcb7d6d6974abf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 26 Jan 2023 15:20:49 +0100 Subject: [PATCH 32/39] itests: Fix TestEthSubscribeLogs --- cli/util/api.go | 2 +- gateway/eth_sub.go | 5 +++-- gateway/proxy_eth.go | 4 ++-- itests/eth_filter_test.go | 14 +++++++------- itests/kit/ensemble.go | 3 ++- itests/kit/node_full.go | 5 +++++ itests/kit/rpc.go | 9 ++++++++- node/impl/full/eth.go | 3 ++- node/modules/actorevent.go | 1 + 9 files changed, 31 insertions(+), 15 deletions(-) diff --git a/cli/util/api.go b/cli/util/api.go index 3d06f9e31..1d6928c3f 100644 --- a/cli/util/api.go +++ b/cli/util/api.go @@ -359,7 +359,7 @@ func GetFullNodeAPIV1(ctx *cli.Context, opts ...GetFullNodeOption) (v1api.FullNo var closers []jsonrpc.ClientCloser for _, head := range heads { - v1api, closer, err := client.NewFullNodeRPCV1(ctx.Context, head.addr, head.header) + v1api, closer, err := client.NewFullNodeRPCV1(ctx.Context, head.addr, head.header, rpcOpts...) if err != nil { log.Warnf("Not able to establish connection to node with addr: ", head.addr) continue diff --git a/gateway/eth_sub.go b/gateway/eth_sub.go index 2f82fc32b..76d913983 100644 --- a/gateway/eth_sub.go +++ b/gateway/eth_sub.go @@ -24,11 +24,12 @@ func NewEthSubHandler() *EthSubHandler { } } -func (e *EthSubHandler) addSub(ctx context.Context, id ethtypes.EthSubscriptionID, sink func(context.Context, *ethtypes.EthSubscriptionResponse) error) error { +func (e *EthSubHandler) AddSub(ctx context.Context, id ethtypes.EthSubscriptionID, sink func(context.Context, *ethtypes.EthSubscriptionResponse) error) error { e.lk.Lock() defer e.lk.Unlock() for _, p := range e.queued[id] { + p := p // copy if err := sink(ctx, &p); err != nil { return err } @@ -38,7 +39,7 @@ func (e *EthSubHandler) addSub(ctx context.Context, id ethtypes.EthSubscriptionI return nil } -func (e *EthSubHandler) removeSub(id ethtypes.EthSubscriptionID) { +func (e *EthSubHandler) RemoveSub(id ethtypes.EthSubscriptionID) { e.lk.Lock() defer e.lk.Unlock() diff --git a/gateway/proxy_eth.go b/gateway/proxy_eth.go index 9518b2a15..4e8136835 100644 --- a/gateway/proxy_eth.go +++ b/gateway/proxy_eth.go @@ -463,7 +463,7 @@ func (gw *Node) EthSubscribe(ctx context.Context, eventType string, params *etht return ethtypes.EthSubscriptionID{}, err } - err = gw.subHnd.addSub(ctx, sub, func(ctx context.Context, response *ethtypes.EthSubscriptionResponse) error { + err = gw.subHnd.AddSub(ctx, sub, func(ctx context.Context, response *ethtypes.EthSubscriptionResponse) error { outParam, err := json.Marshal(response) if err != nil { return err @@ -502,7 +502,7 @@ func (gw *Node) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionI delete(ft.userSubscriptions, id) if gw.subHnd != nil { - gw.subHnd.removeSub(id) + gw.subHnd.RemoveSub(id) } return ok, nil diff --git a/itests/eth_filter_test.go b/itests/eth_filter_test.go index 5fbe1cb6a..1127ffdb2 100644 --- a/itests/eth_filter_test.go +++ b/itests/eth_filter_test.go @@ -438,15 +438,15 @@ func TestEthSubscribeLogsNoTopicSpec(t *testing.T) { t.Logf("actor ID address is %s", idAddr) // install filter - respCh, err := client.EthSubscribe(ctx, "logs", nil) + subId, err := client.EthSubscribe(ctx, "logs", nil) require.NoError(err) - subResponses := []ethtypes.EthSubscriptionResponse{} - go func() { - for resp := range respCh { - subResponses = append(subResponses, resp) - } - }() + var subResponses []ethtypes.EthSubscriptionResponse + err = client.EthSubRouter.AddSub(ctx, subId, func(ctx context.Context, resp *ethtypes.EthSubscriptionResponse) error { + subResponses = append(subResponses, *resp) + return nil + }) + require.NoError(err) const iterations = 10 ethContractAddr, messages := invokeLogFourData(t, client, iterations) diff --git a/itests/kit/ensemble.go b/itests/kit/ensemble.go index 8b92202dc..6d4ca1c12 100644 --- a/itests/kit/ensemble.go +++ b/itests/kit/ensemble.go @@ -47,6 +47,7 @@ import ( "github.com/filecoin-project/lotus/chain/wallet/key" "github.com/filecoin-project/lotus/cmd/lotus-seed/seed" "github.com/filecoin-project/lotus/cmd/lotus-worker/sealworker" + "github.com/filecoin-project/lotus/gateway" "github.com/filecoin-project/lotus/genesis" "github.com/filecoin-project/lotus/markets/idxprov" "github.com/filecoin-project/lotus/markets/idxprov/idxprov_test" @@ -210,7 +211,7 @@ func (n *Ensemble) FullNode(full *TestFullNode, opts ...NodeOpt) *Ensemble { n.genesis.accounts = append(n.genesis.accounts, genacc) } - *full = TestFullNode{t: n.t, options: options, DefaultKey: key} + *full = TestFullNode{t: n.t, options: options, DefaultKey: key, EthSubRouter: gateway.NewEthSubHandler()} n.inactive.fullnodes = append(n.inactive.fullnodes, full) return n diff --git a/itests/kit/node_full.go b/itests/kit/node_full.go index 682ae118a..3e80ed688 100644 --- a/itests/kit/node_full.go +++ b/itests/kit/node_full.go @@ -22,6 +22,7 @@ import ( "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/wallet/key" cliutil "github.com/filecoin-project/lotus/cli/util" + "github.com/filecoin-project/lotus/gateway" "github.com/filecoin-project/lotus/node" ) @@ -46,6 +47,10 @@ type TestFullNode struct { Stop node.StopFunc + // gateway handler makes it convenient to register callbalks per topic, so we + // also use it for tests + EthSubRouter *gateway.EthSubHandler + options nodeOpts } diff --git a/itests/kit/rpc.go b/itests/kit/rpc.go index f8c2c6e53..5d40ac3e9 100644 --- a/itests/kit/rpc.go +++ b/itests/kit/rpc.go @@ -13,6 +13,8 @@ import ( manet "github.com/multiformats/go-multiaddr/net" "github.com/stretchr/testify/require" + "github.com/filecoin-project/go-jsonrpc" + "github.com/filecoin-project/lotus/api/client" "github.com/filecoin-project/lotus/cmd/lotus-worker/sealworker" "github.com/filecoin-project/lotus/node" @@ -52,7 +54,12 @@ func fullRpc(t *testing.T, f *TestFullNode) (*TestFullNode, Closer) { fmt.Printf("FULLNODE RPC ENV FOR CLI DEBUGGING `export FULLNODE_API_INFO=%s`\n", "ws://"+srv.Listener.Addr().String()) sendItestdNotif("FULLNODE_API_INFO", t.Name(), "ws://"+srv.Listener.Addr().String()) - cl, stop, err := client.NewFullNodeRPCV1(context.Background(), "ws://"+srv.Listener.Addr().String()+"/rpc/v1", nil) + rpcOpts := []jsonrpc.Option{ + jsonrpc.WithClientHandler("Filecoin", f.EthSubRouter), + jsonrpc.WithClientHandlerAlias("eth_subscription", "Filecoin.EthSubscription"), + } + + cl, stop, err := client.NewFullNodeRPCV1(context.Background(), "ws://"+srv.Listener.Addr().String()+"/rpc/v1", nil, rpcOpts...) require.NoError(t, err) f.ListenAddr, f.ListenURL, f.FullNode = maddr, srv.URL, cl diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 898776d10..83ea6fcd5 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -134,6 +134,7 @@ type EthEvent struct { FilterStore filter.FilterStore SubManager *EthSubscriptionManager MaxFilterHeightRange abi.ChainEpoch + SubscribtionCtx context.Context } var _ EthEventAPI = (*EthEvent)(nil) @@ -1112,7 +1113,7 @@ func (e *EthEvent) EthSubscribe(ctx context.Context, eventType string, params *e return ethtypes.EthSubscriptionID{}, xerrors.Errorf("connection doesn't support callbacks") } - sub, err := e.SubManager.StartSubscription(ctx, ethCb.EthSubscription) + sub, err := e.SubManager.StartSubscription(e.SubscribtionCtx, ethCb.EthSubscription) if err != nil { return ethtypes.EthSubscriptionID{}, err } diff --git a/node/modules/actorevent.go b/node/modules/actorevent.go index eb5afb8e6..55a79a59a 100644 --- a/node/modules/actorevent.go +++ b/node/modules/actorevent.go @@ -40,6 +40,7 @@ func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo ee := &full.EthEvent{ Chain: cs, MaxFilterHeightRange: abi.ChainEpoch(cfg.Events.MaxFilterHeightRange), + SubscribtionCtx: ctx, } if !cfg.EnableEthRPC || cfg.Events.DisableRealTimeFilterAPI { From 9701b116419a92d96669a99936c8d7ef8715ee19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 26 Jan 2023 15:50:18 +0100 Subject: [PATCH 33/39] ethtypes: Serialize EthFilterID/EthSubscriptionID correctly --- build/openrpc/full.json.gz | Bin 33267 -> 33162 bytes build/openrpc/gateway.json.gz | Bin 8582 -> 8487 bytes chain/types/ethtypes/eth_types.go | 48 +++- chain/types/ethtypes/eth_types_test.go | 42 +++ documentation/en/api-v1-unstable-methods.md | 292 +------------------- 5 files changed, 86 insertions(+), 296 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 4524108b1c12310be48c34b90d68fc7f028cd230..4757ee49b471aaf7a2aa2b98934e7dff3f6c01fa 100644 GIT binary patch delta 30489 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 EKUm=&*Z=?k delta 30587 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ν@r6ivJ$DaX7g=H?~V4hcVk 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 Grv?D=c4(Xc diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index 688e01963366a9415a90b54451b0ff6a5fb1f9f0..050ab611e97f605204623467a2df05e693cb3e38 100644 GIT binary patch literal 8487 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 VeEk0S{{jF2|Nl+25H@4R0RZW&g^U0I literal 8582 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 M{|vsy4b#T~06#F<)c^nh diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index 235cc7c79..64c08c2ae 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -363,6 +363,18 @@ func (h *EthHash) UnmarshalJSON(b []byte) error { return nil } +func (h EthHash) String() string { + return "0x" + hex.EncodeToString(h[:]) +} + +// Should ONLY be used for blocks and Filecoin messages. Eth transactions expect a different hashing scheme. +func (h EthHash) ToCid() cid.Cid { + // err is always nil + mh, _ := multihash.EncodeName(h[:], "blake2b-256") + + return cid.NewCidV1(cid.DagCBOR, mh) +} + func decodeHexString(s string, expectedLen int) ([]byte, error) { s = handleHexStringPrefix(s) if len(s) != expectedLen*2 { @@ -420,18 +432,6 @@ func EthHashFromTxBytes(b []byte) EthHash { return ethHash } -func (h EthHash) String() string { - return "0x" + hex.EncodeToString(h[:]) -} - -// Should ONLY be used for blocks and Filecoin messages. Eth transactions expect a different hashing scheme. -func (h EthHash) ToCid() cid.Cid { - // err is always nil - mh, _ := multihash.EncodeName(h[:], "blake2b-256") - - return cid.NewCidV1(cid.DagCBOR, mh) -} - type EthFeeHistory struct { OldestBlock uint64 `json:"oldestBlock"` BaseFeePerGas []EthBigInt `json:"baseFeePerGas"` @@ -441,9 +441,33 @@ type EthFeeHistory struct { type EthFilterID EthHash +func (h EthFilterID) MarshalJSON() ([]byte, error) { + return (EthHash)(h).MarshalJSON() +} + +func (h *EthFilterID) UnmarshalJSON(b []byte) error { + return (*EthHash)(h).UnmarshalJSON(b) +} + +func (h EthFilterID) String() string { + return (EthHash)(h).String() +} + // An opaque identifier generated by the Lotus node to refer to an active subscription. type EthSubscriptionID EthHash +func (h EthSubscriptionID) MarshalJSON() ([]byte, error) { + return (EthHash)(h).MarshalJSON() +} + +func (h *EthSubscriptionID) UnmarshalJSON(b []byte) error { + return (*EthHash)(h).UnmarshalJSON(b) +} + +func (h EthSubscriptionID) String() string { + return (EthHash)(h).String() +} + type EthFilterSpec struct { // Interpreted as an epoch or one of "latest" for last mined block, "earliest" for first, // "pending" for not yet committed messages. diff --git a/chain/types/ethtypes/eth_types_test.go b/chain/types/ethtypes/eth_types_test.go index 89c38ba29..e42d2bdc3 100644 --- a/chain/types/ethtypes/eth_types_test.go +++ b/chain/types/ethtypes/eth_types_test.go @@ -93,6 +93,48 @@ func TestEthHash(t *testing.T) { h1, err := EthHashFromCid(c) require.Nil(t, err) require.Equal(t, h, h1) + + jm, err := json.Marshal(h) + require.NoError(t, err) + require.Equal(t, hash, string(jm)) + } +} + +func TestEthFilterID(t *testing.T) { + testcases := []string{ + `"0x013dbb9442ca9667baccc6230fcd5c1c4b2d4d2870f4bd20681d4d47cfd15184"`, + `"0xab8653edf9f51785664a643b47605a7ba3d917b5339a0724e7642c114d0e4738"`, + } + + for _, hash := range testcases { + var h EthFilterID + err := h.UnmarshalJSON([]byte(hash)) + + require.Nil(t, err) + require.Equal(t, h.String(), strings.Replace(hash, `"`, "", -1)) + + jm, err := json.Marshal(h) + require.NoError(t, err) + require.Equal(t, hash, string(jm)) + } +} + +func TestEthSubscriptionID(t *testing.T) { + testcases := []string{ + `"0x013dbb9442ca9667baccc6230fcd5c1c4b2d4d2870f4bd20681d4d47cfd15184"`, + `"0xab8653edf9f51785664a643b47605a7ba3d917b5339a0724e7642c114d0e4738"`, + } + + for _, hash := range testcases { + var h EthSubscriptionID + err := h.UnmarshalJSON([]byte(hash)) + + require.Nil(t, err) + require.Equal(t, h.String(), strings.Replace(hash, `"`, "", -1)) + + jm, err := json.Marshal(h) + require.NoError(t, err) + require.Equal(t, hash, string(jm)) } } diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 299d71308..c9d5f8814 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -2520,40 +2520,7 @@ Perms: write Inputs: ```json [ - [ - 55, - 105, - 12, - 254, - 198, - 193, - 191, - 76, - 59, - 146, - 136, - 199, - 165, - 215, - 131, - 233, - 135, - 49, - 233, - 11, - 10, - 76, - 23, - 124, - 42, - 55, - 76, - 122, - 148, - 39, - 53, - 94 - ] + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" ] ``` @@ -2574,40 +2541,7 @@ Perms: write Inputs: ```json [ - [ - 55, - 105, - 12, - 254, - 198, - 193, - 191, - 76, - 59, - 146, - 136, - 199, - 165, - 215, - 131, - 233, - 135, - 49, - 233, - 11, - 10, - 76, - 23, - 124, - 42, - 55, - 76, - 122, - 148, - 39, - 53, - 94 - ] + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" ] ``` @@ -2881,43 +2815,7 @@ Perms: write Inputs: `null` -Response: -```json -[ - 55, - 105, - 12, - 254, - 198, - 193, - 191, - 76, - 59, - 146, - 136, - 199, - 165, - 215, - 131, - 233, - 135, - 49, - 233, - 11, - 10, - 76, - 23, - 124, - 42, - 55, - 76, - 122, - 148, - 39, - 53, - 94 -] -``` +Response: `"0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"` ### EthNewFilter Installs a persistent filter based on given filter spec. @@ -2938,43 +2836,7 @@ Inputs: ] ``` -Response: -```json -[ - 55, - 105, - 12, - 254, - 198, - 193, - 191, - 76, - 59, - 146, - 136, - 199, - 165, - 215, - 131, - 233, - 135, - 49, - 233, - 11, - 10, - 76, - 23, - 124, - 42, - 55, - 76, - 122, - 148, - 39, - 53, - 94 -] -``` +Response: `"0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"` ### EthNewPendingTransactionFilter Installs a persistent filter to notify when new messages arrive in the message pool. @@ -2984,43 +2846,7 @@ Perms: write Inputs: `null` -Response: -```json -[ - 55, - 105, - 12, - 254, - 198, - 193, - 191, - 76, - 59, - 146, - 136, - 199, - 165, - 215, - 131, - 233, - 135, - 49, - 233, - 11, - 10, - 76, - 23, - 124, - 42, - 55, - 76, - 122, - 148, - 39, - 53, - 94 -] -``` +Response: `"0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"` ### EthProtocolVersion @@ -3071,43 +2897,7 @@ Inputs: ] ``` -Response: -```json -[ - 55, - 105, - 12, - 254, - 198, - 193, - 191, - 76, - 59, - 146, - 136, - 199, - 165, - 215, - 131, - 233, - 135, - 49, - 233, - 11, - 10, - 76, - 23, - 124, - 42, - 55, - 76, - 122, - 148, - 39, - 53, - 94 -] -``` +Response: `"0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"` ### EthUninstallFilter Uninstalls a filter with given id. @@ -3118,40 +2908,7 @@ Perms: write Inputs: ```json [ - [ - 55, - 105, - 12, - 254, - 198, - 193, - 191, - 76, - 59, - 146, - 136, - 199, - 165, - 215, - 131, - 233, - 135, - 49, - 233, - 11, - 10, - 76, - 23, - 124, - 42, - 55, - 76, - 122, - 148, - 39, - 53, - 94 - ] + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" ] ``` @@ -3166,40 +2923,7 @@ Perms: write Inputs: ```json [ - [ - 55, - 105, - 12, - 254, - 198, - 193, - 191, - 76, - 59, - 146, - 136, - 199, - 165, - 215, - 131, - 233, - 135, - 49, - 233, - 11, - 10, - 76, - 23, - 124, - 42, - 55, - 76, - 122, - 148, - 39, - 53, - 94 - ] + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" ] ``` From ad14d71978f9d63ff6268b5fbedb781a05fbe384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 31 Jan 2023 11:00:15 +0100 Subject: [PATCH 34/39] eth rpc: Params are optional in eth_subscribe --- api/api_full.go | 2 +- api/api_gateway.go | 3 +- api/mocks/mock_full.go | 9 ++-- api/proxy_gen.go | 16 +++---- build/openrpc/full.json.gz | Bin 33162 -> 33148 bytes build/openrpc/gateway.json.gz | Bin 8487 -> 8472 bytes chain/types/ethtypes/eth_types.go | 37 ++++++++++++++++ documentation/en/api-v1-unstable-methods.md | 9 +--- gateway/node.go | 3 +- gateway/proxy_eth.go | 10 ++++- itests/eth_filter_test.go | 45 +++++++++----------- lib/result/result.go | 8 +++- node/impl/full/eth.go | 19 ++++++--- 13 files changed, 103 insertions(+), 58 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index e9cf4a58f..2ac92a764 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -832,7 +832,7 @@ type FullNode interface { // - logs: notify new event logs that match a criteria // params contains additional parameters used with the log event type // The client will receive a stream of EthSubscriptionResponse values until EthUnsubscribe is called. - EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) //perm:write + EthSubscribe(ctx context.Context, params jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) //perm:write // Unsubscribe from a websocket subscription EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) //perm:write diff --git a/api/api_gateway.go b/api/api_gateway.go index fdadd2ae6..2c91fc6f5 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -7,6 +7,7 @@ import ( "github.com/ipfs/go-cid" "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/builtin/v9/miner" "github.com/filecoin-project/go-state-types/dline" @@ -102,6 +103,6 @@ type Gateway interface { EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error) - EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) + EthSubscribe(ctx context.Context, params jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) } diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index 30df05226..c944e2de1 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -23,6 +23,7 @@ import ( bitfield "github.com/filecoin-project/go-bitfield" datatransfer "github.com/filecoin-project/go-data-transfer" retrievalmarket "github.com/filecoin-project/go-fil-markets/retrievalmarket" + jsonrpc "github.com/filecoin-project/go-jsonrpc" auth "github.com/filecoin-project/go-jsonrpc/auth" abi "github.com/filecoin-project/go-state-types/abi" big "github.com/filecoin-project/go-state-types/big" @@ -1388,18 +1389,18 @@ func (mr *MockFullNodeMockRecorder) EthSendRawTransaction(arg0, arg1 interface{} } // EthSubscribe mocks base method. -func (m *MockFullNode) EthSubscribe(arg0 context.Context, arg1 string, arg2 *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) { +func (m *MockFullNode) EthSubscribe(arg0 context.Context, arg1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EthSubscribe", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "EthSubscribe", arg0, arg1) ret0, _ := ret[0].(ethtypes.EthSubscriptionID) ret1, _ := ret[1].(error) return ret0, ret1 } // EthSubscribe indicates an expected call of EthSubscribe. -func (mr *MockFullNodeMockRecorder) EthSubscribe(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockFullNodeMockRecorder) EthSubscribe(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthSubscribe", reflect.TypeOf((*MockFullNode)(nil).EthSubscribe), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthSubscribe", reflect.TypeOf((*MockFullNode)(nil).EthSubscribe), arg0, arg1) } // EthUninstallFilter mocks base method. diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 9b8890a7c..7a6b0d508 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -302,7 +302,7 @@ type FullNodeMethods struct { EthSendRawTransaction func(p0 context.Context, p1 ethtypes.EthBytes) (ethtypes.EthHash, error) `perm:"read"` - EthSubscribe func(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) `perm:"write"` + EthSubscribe func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) `perm:"write"` EthUninstallFilter func(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) `perm:"write"` @@ -704,7 +704,7 @@ type GatewayMethods struct { EthSendRawTransaction func(p0 context.Context, p1 ethtypes.EthBytes) (ethtypes.EthHash, error) `` - EthSubscribe func(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) `` + EthSubscribe func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) `` EthUninstallFilter func(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) `` @@ -2320,14 +2320,14 @@ func (s *FullNodeStub) EthSendRawTransaction(p0 context.Context, p1 ethtypes.Eth return *new(ethtypes.EthHash), ErrNotSupported } -func (s *FullNodeStruct) EthSubscribe(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) { +func (s *FullNodeStruct) EthSubscribe(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) { if s.Internal.EthSubscribe == nil { return *new(ethtypes.EthSubscriptionID), ErrNotSupported } - return s.Internal.EthSubscribe(p0, p1, p2) + return s.Internal.EthSubscribe(p0, p1) } -func (s *FullNodeStub) EthSubscribe(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) { +func (s *FullNodeStub) EthSubscribe(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) { return *new(ethtypes.EthSubscriptionID), ErrNotSupported } @@ -4465,14 +4465,14 @@ func (s *GatewayStub) EthSendRawTransaction(p0 context.Context, p1 ethtypes.EthB return *new(ethtypes.EthHash), ErrNotSupported } -func (s *GatewayStruct) EthSubscribe(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) { +func (s *GatewayStruct) EthSubscribe(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) { if s.Internal.EthSubscribe == nil { return *new(ethtypes.EthSubscriptionID), ErrNotSupported } - return s.Internal.EthSubscribe(p0, p1, p2) + return s.Internal.EthSubscribe(p0, p1) } -func (s *GatewayStub) EthSubscribe(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) { +func (s *GatewayStub) EthSubscribe(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) { return *new(ethtypes.EthSubscriptionID), ErrNotSupported } diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 4757ee49b471aaf7a2aa2b98934e7dff3f6c01fa..0211629fe149ce42317eaa73ac55aa7a554ff27f 100644 GIT binary patch delta 23645 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 ALI3~& delta 23700 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 Pd`z||c}xyMK|=f&&p$>A delta 8328 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 S`2PX`0RR6@v=BC9#sL6;3o`Wp diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index 64c08c2ae..584a7d2be 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -619,6 +619,43 @@ type EthLog struct { BlockNumber EthUint64 `json:"blockNumber"` } +// EthSubscribeParams handles raw jsonrpc params for eth_subscribe +type EthSubscribeParams struct { + EventType string + Params *EthSubscriptionParams +} + +func (e *EthSubscribeParams) UnmarshalJSON(b []byte) error { + var params []json.RawMessage + err := json.Unmarshal(b, ¶ms) + if err != nil { + return err + } + switch len(params) { + case 2: + err = json.Unmarshal(params[1], &e.Params) + if err != nil { + return err + } + fallthrough + case 1: + err = json.Unmarshal(params[0], &e.EventType) + if err != nil { + return err + } + default: + return xerrors.Errorf("expected 1 or 2 params, got %d", len(params)) + } + return nil +} + +func (e EthSubscribeParams) MarshalJSON() ([]byte, error) { + if e.Params != nil { + return json.Marshal([]interface{}{e.EventType, e.Params}) + } + return json.Marshal([]interface{}{e.EventType}) +} + type EthSubscriptionParams struct { // List of topics to be matched. // Optional, default: empty list diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index c9d5f8814..c8c650395 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -2886,14 +2886,7 @@ Perms: write Inputs: ```json [ - "string value", - { - "topics": [ - [ - "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" - ] - ] - } + "Bw==" ] ``` diff --git a/gateway/node.go b/gateway/node.go index bcb515b3c..56f1f6134 100644 --- a/gateway/node.go +++ b/gateway/node.go @@ -12,6 +12,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-bitfield" + "github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/dline" "github.com/filecoin-project/go-state-types/network" @@ -117,7 +118,7 @@ type TargetAPI interface { EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error) - EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) + EthSubscribe(ctx context.Context, params jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) } diff --git a/gateway/proxy_eth.go b/gateway/proxy_eth.go index 4e8136835..cf65f484c 100644 --- a/gateway/proxy_eth.go +++ b/gateway/proxy_eth.go @@ -436,7 +436,13 @@ func (gw *Node) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) return ok, nil } -func (gw *Node) EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) { +func (gw *Node) EthSubscribe(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) { + // validate params + _, err := jsonrpc.DecodeParams[ethtypes.EthSubscribeParams](p) + if err != nil { + return ethtypes.EthSubscriptionID{}, xerrors.Errorf("decoding params: %w", err) + } + if err := gw.limit(ctx, stateRateLimitTokens); err != nil { return ethtypes.EthSubscriptionID{}, err } @@ -458,7 +464,7 @@ func (gw *Node) EthSubscribe(ctx context.Context, eventType string, params *etht return ethtypes.EthSubscriptionID{}, fmt.Errorf("too many subscriptions") } - sub, err := gw.target.EthSubscribe(ctx, eventType, params) + sub, err := gw.target.EthSubscribe(ctx, p) if err != nil { return ethtypes.EthSubscriptionID{}, err } diff --git a/itests/eth_filter_test.go b/itests/eth_filter_test.go index 1127ffdb2..22fb17757 100644 --- a/itests/eth_filter_test.go +++ b/itests/eth_filter_test.go @@ -6,6 +6,7 @@ import ( "context" "encoding/binary" "encoding/hex" + "encoding/json" "fmt" "os" "sort" @@ -21,6 +22,7 @@ import ( "golang.org/x/xerrors" "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" @@ -29,6 +31,7 @@ import ( "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/itests/kit" + res "github.com/filecoin-project/lotus/lib/result" ) // SolidityContractDef holds information about one of the test contracts @@ -438,7 +441,7 @@ func TestEthSubscribeLogsNoTopicSpec(t *testing.T) { t.Logf("actor ID address is %s", idAddr) // install filter - subId, err := client.EthSubscribe(ctx, "logs", nil) + subId, err := client.EthSubscribe(ctx, res.Wrap[jsonrpc.RawParams](json.Marshal(ethtypes.EthSubscribeParams{EventType: "logs"})).Assert(require.NoError)) require.NoError(err) var subResponses []ethtypes.EthSubscriptionResponse @@ -567,44 +570,33 @@ func TestEthSubscribeLogs(t *testing.T) { testResponses := map[string]chan ethtypes.EthSubscriptionResponse{} - // quit is used to signal that we're ready to start testing collected results - quit := make(chan struct{}) - // Create all the filters for _, tc := range testCases { - // subscribe to topics in filter - subCh, err := client.EthSubscribe(ctx, "logs", ðtypes.EthSubscriptionParams{Topics: tc.spec.Topics}) + subParam, err := json.Marshal(ethtypes.EthSubscribeParams{ + EventType: "logs", + Params: ðtypes.EthSubscriptionParams{Topics: tc.spec.Topics}, + }) + require.NoError(err) + + subId, err := client.EthSubscribe(ctx, subParam) require.NoError(err) responseCh := make(chan ethtypes.EthSubscriptionResponse, len(invocations)) testResponses[tc.name] = responseCh - // start a goroutine to forward responses from subscription to a buffered channel with guaranteed capacity - go func(subCh <-chan ethtypes.EthSubscriptionResponse, responseCh chan<- ethtypes.EthSubscriptionResponse, quit chan struct{}) { - defer func() { - close(responseCh) - }() - for { - select { - case resp := <-subCh: - responseCh <- resp - case <-quit: - return - case <-ctx.Done(): - return - } - } - }(subCh, responseCh, quit) - + err = client.EthSubRouter.AddSub(ctx, subId, func(ctx context.Context, resp *ethtypes.EthSubscriptionResponse) error { + responseCh <- *resp + return nil + }) + require.NoError(err) } // Perform all the invocations messages := invokeAndWaitUntilAllOnChain(t, client, invocations) - // wait a little for subscriptions to gather results and then tell all the goroutines to stop + // wait a little for subscriptions to gather results time.Sleep(blockTime * 6) - close(quit) for _, tc := range testCases { tc := tc // appease the lint despot @@ -612,6 +604,9 @@ func TestEthSubscribeLogs(t *testing.T) { responseCh, ok := testResponses[tc.name] require.True(ok) + // don't expect any more responses + close(responseCh) + var elogs []*ethtypes.EthLog for resp := range responseCh { rlist, ok := resp.Result.([]interface{}) diff --git a/lib/result/result.go b/lib/result/result.go index bec839d7a..56a9ffab7 100644 --- a/lib/result/result.go +++ b/lib/result/result.go @@ -30,6 +30,12 @@ func Wrap[T any](value T, err error) Result[T] { } } -func (r *Result[T]) Unwrap() (T, error) { +func (r Result[T]) Unwrap() (T, error) { return r.Value, r.Error } + +func (r Result[T]) Assert(noErrFn func(err error, msgAndArgs ...interface{})) T { + noErrFn(r.Error) + + return r.Value +} diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 83ea6fcd5..938631d43 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -77,7 +77,7 @@ type EthEventAPI interface { EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error) - EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) + EthSubscribe(ctx context.Context, params jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) } @@ -1103,7 +1103,12 @@ const ( EthSubscribeEventTypeLogs = "logs" ) -func (e *EthEvent) EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (ethtypes.EthSubscriptionID, error) { +func (e *EthEvent) EthSubscribe(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) { + params, err := jsonrpc.DecodeParams[ethtypes.EthSubscribeParams](p) + if err != nil { + return ethtypes.EthSubscriptionID{}, xerrors.Errorf("decoding params: %w", err) + } + if e.SubManager == nil { return ethtypes.EthSubscriptionID{}, api.ErrNotSupported } @@ -1118,7 +1123,7 @@ func (e *EthEvent) EthSubscribe(ctx context.Context, eventType string, params *e return ethtypes.EthSubscriptionID{}, err } - switch eventType { + switch params.EventType { case EthSubscribeEventTypeHeads: f, err := e.TipSetFilterManager.Install(ctx) if err != nil { @@ -1130,13 +1135,13 @@ func (e *EthEvent) EthSubscribe(ctx context.Context, eventType string, params *e case EthSubscribeEventTypeLogs: keys := map[string][][]byte{} - if params != nil { + if params.Params != nil { var err error - keys, err = parseEthTopics(params.Topics) + keys, err = parseEthTopics(params.Params.Topics) if err != nil { // clean up any previous filters added and stop the sub _, _ = e.EthUnsubscribe(ctx, sub.id) - return nil, err + return ethtypes.EthSubscriptionID{}, err } } @@ -1148,7 +1153,7 @@ func (e *EthEvent) EthSubscribe(ctx context.Context, eventType string, params *e } sub.addFilter(ctx, f) default: - return ethtypes.EthSubscriptionID{}, xerrors.Errorf("unsupported event type: %s", eventType) + return ethtypes.EthSubscriptionID{}, xerrors.Errorf("unsupported event type: %s", params.EventType) } return sub.id, nil From 070aca2b651a9ddd08e35467a00b4c3153a4b835 Mon Sep 17 00:00:00 2001 From: Maciej Witowski Date: Tue, 31 Jan 2023 17:17:03 +0100 Subject: [PATCH 35/39] Eth JSON-RPC: from in eth_getTransactionByHash is not correctly populated #1614 --- chain/types/ethtypes/eth_transactions.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/chain/types/ethtypes/eth_transactions.go b/chain/types/ethtypes/eth_transactions.go index 9f9df2381..3d6ed7a32 100644 --- a/chain/types/ethtypes/eth_transactions.go +++ b/chain/types/ethtypes/eth_transactions.go @@ -77,10 +77,16 @@ func EthTxFromSignedEthMessage(smsg *types.SignedMessage) (EthTx, error) { return EthTx{}, xerrors.Errorf("failed to recover signature: %w", err) } + fromAddr, err := EthAddressFromFilecoinAddress(smsg.Message.From) + if err != nil { + return EthTx{}, xerrors.Errorf("failed to get from: %w", err) + } + return EthTx{ Nonce: EthUint64(txArgs.Nonce), ChainID: EthUint64(txArgs.ChainID), To: txArgs.To, + From: fromAddr, Value: EthBigInt(txArgs.Value), Type: Eip1559TxType, Gas: EthUint64(txArgs.GasLimit), From 5f2ef98de6750bcdaf054243a24feadc8885e01a Mon Sep 17 00:00:00 2001 From: Maciej Witowski Date: Tue, 31 Jan 2023 18:18:54 +0100 Subject: [PATCH 36/39] Move assigning `from` address to the API level --- chain/types/ethtypes/eth_transactions.go | 6 ------ node/impl/full/eth.go | 7 +++++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/chain/types/ethtypes/eth_transactions.go b/chain/types/ethtypes/eth_transactions.go index 3d6ed7a32..9f9df2381 100644 --- a/chain/types/ethtypes/eth_transactions.go +++ b/chain/types/ethtypes/eth_transactions.go @@ -77,16 +77,10 @@ func EthTxFromSignedEthMessage(smsg *types.SignedMessage) (EthTx, error) { return EthTx{}, xerrors.Errorf("failed to recover signature: %w", err) } - fromAddr, err := EthAddressFromFilecoinAddress(smsg.Message.From) - if err != nil { - return EthTx{}, xerrors.Errorf("failed to get from: %w", err) - } - return EthTx{ Nonce: EthUint64(txArgs.Nonce), ChainID: EthUint64(txArgs.ChainID), To: txArgs.To, - From: fromAddr, Value: EthBigInt(txArgs.Value), Type: Eip1559TxType, Gas: EthUint64(txArgs.GasLimit), diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 938631d43..a70b3acc1 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -1571,6 +1571,13 @@ func newEthTxFromSignedMessage(ctx context.Context, smsg *types.SignedMessage, s if err != nil { return ethtypes.EthTx{}, err } + + fromAddr, err := lookupEthAddress(ctx, smsg.Message.From, sa) + if err != nil { + return ethtypes.EthTx{}, xerrors.Errorf("failed to resolve Ethereum address: %w", err) + } + + tx.From = fromAddr } else if smsg.Signature.Type == crypto.SigTypeSecp256k1 { // Secp Filecoin Message tx = ethTxFromNativeMessage(ctx, smsg.VMMessage(), sa) tx.Hash, err = ethtypes.EthHashFromCid(smsg.Cid()) From dcbfca53d97b9334833e3d57c3b3502d2fd76ef0 Mon Sep 17 00:00:00 2001 From: Maciej Witowski Date: Tue, 31 Jan 2023 18:54:05 +0100 Subject: [PATCH 37/39] Improve the error message --- node/impl/full/eth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index a70b3acc1..a9e5a1cb4 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -1569,7 +1569,7 @@ func newEthTxFromSignedMessage(ctx context.Context, smsg *types.SignedMessage, s tx.Hash, err = tx.TxHash() if err != nil { - return ethtypes.EthTx{}, err + return ethtypes.EthTx{}, xerrors.Errorf("failed to calculate hash for ethTx: %w", err) } fromAddr, err := lookupEthAddress(ctx, smsg.Message.From, sa) From 99b879d014d0037cf3bf47c70de740b9ac366b81 Mon Sep 17 00:00:00 2001 From: Aayush Date: Tue, 31 Jan 2023 12:49:04 -0500 Subject: [PATCH 38/39] Add test for EthGetTransactionByHash --- itests/eth_transactions_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/itests/eth_transactions_test.go b/itests/eth_transactions_test.go index 0c8f1baa5..6ba63608a 100644 --- a/itests/eth_transactions_test.go +++ b/itests/eth_transactions_test.go @@ -83,6 +83,11 @@ func TestValueTransferValidSignature(t *testing.T) { // Success. require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status) + + ethTx, err := client.EthGetTransactionByHash(ctx, &hash) + require.Nil(t, err) + require.EqualValues(t, ethAddr, ethTx.From) + } func TestLegacyTransaction(t *testing.T) { From 6f987b162a62ecb490ed7c919c80fa656e4f6e27 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Thu, 19 Jan 2023 11:02:09 -0500 Subject: [PATCH 39/39] fix: should not serve non v0 api in v1 --- node/rpc.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/rpc.go b/node/rpc.go index c846646fe..cacd33526 100644 --- a/node/rpc.go +++ b/node/rpc.go @@ -94,8 +94,9 @@ func FullNodeHandler(a v1api.FullNode, permissioned bool, opts ...jsonrpc.Server fnapi = api.PermissionedFullAPI(fnapi) } + var v0 v0api.FullNode = &(struct{ v0api.FullNode }{&v0api.WrapperV1Full{FullNode: fnapi}}) serveRpc("/rpc/v1", fnapi) - serveRpc("/rpc/v0", &v0api.WrapperV1Full{FullNode: fnapi}) + serveRpc("/rpc/v0", v0) // Import handler handleImportFunc := handleImport(a.(*impl.FullNodeAPI))