diff --git a/app/submodule/chain/chaininfo_api.go b/app/submodule/chain/chaininfo_api.go index e6fa31e386..ca07438d81 100644 --- a/app/submodule/chain/chaininfo_api.go +++ b/app/submodule/chain/chaininfo_api.go @@ -291,25 +291,14 @@ func (cia *chainInfoAPI) ChainGetParentReceipts(ctx context.Context, bcid cid.Ci return nil, nil } - // TODO: need to get the number of messages better than this - pts, err := cia.chain.ChainReader.GetTipSet(ctx, types.NewTipSetKey(b.Parents...)) - if err != nil { - return nil, err - } - - cm, err := cia.chain.MessageStore.MessagesForTipset(pts) + receipts, err := cia.chain.MessageStore.LoadReceipts(ctx, b.ParentMessageReceipts) if err != nil { return nil, err } - var out []*types.MessageReceipt - for i := 0; i < len(cm); i++ { - r, err := cia.chain.ChainReader.GetParentReceipt(b, i) - if err != nil { - return nil, err - } - - out = append(out, r) + out := make([]*types.MessageReceipt, len(receipts)) + for i := range receipts { + out[i] = &receipts[i] } return out, nil diff --git a/app/submodule/eth/dummy.go b/app/submodule/eth/dummy.go index 2dff5ec037..09d626b787 100644 --- a/app/submodule/eth/dummy.go +++ b/app/submodule/eth/dummy.go @@ -8,6 +8,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-jsonrpc" + "github.com/filecoin-project/go-state-types/abi" v1 "github.com/filecoin-project/venus/venus-shared/api/chain/v1" "github.com/filecoin-project/venus/venus-shared/types" ) @@ -60,6 +61,10 @@ func (e *ethAPIDummy) EthGetTransactionByHash(ctx context.Context, txHash *types return nil, ErrModuleDisabled } +func (e *ethAPIDummy) EthGetTransactionByHashLimited(ctx context.Context, txHash *types.EthHash, limit abi.ChainEpoch) (*types.EthTx, error) { + return nil, ErrModuleDisabled +} + func (e *ethAPIDummy) EthGetTransactionCount(ctx context.Context, sender types.EthAddress, blkOpt string) (types.EthUint64, error) { return 0, ErrModuleDisabled } @@ -68,6 +73,10 @@ func (e *ethAPIDummy) EthGetTransactionReceipt(ctx context.Context, txHash types return nil, ErrModuleDisabled } +func (e *ethAPIDummy) EthGetTransactionReceiptLimited(ctx context.Context, txHash types.EthHash, limit abi.ChainEpoch) (*types.EthTxReceipt, error) { + return nil, ErrModuleDisabled +} + func (e *ethAPIDummy) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash types.EthHash, txIndex types.EthUint64) (types.EthTx, error) { return types.EthTx{}, ErrModuleDisabled } diff --git a/app/submodule/eth/eth_api.go b/app/submodule/eth/eth_api.go index fc00bd72e9..3e6e5cc7d3 100644 --- a/app/submodule/eth/eth_api.go +++ b/app/submodule/eth/eth_api.go @@ -29,6 +29,7 @@ import ( "github.com/filecoin-project/venus/pkg/fork" "github.com/filecoin-project/venus/pkg/messagepool" "github.com/filecoin-project/venus/pkg/statemanger" + "github.com/filecoin-project/venus/pkg/vm/gas" "github.com/filecoin-project/venus/venus-shared/actors" builtinactors "github.com/filecoin-project/venus/venus-shared/actors/builtin" builtinevm "github.com/filecoin-project/venus/venus-shared/actors/builtin/evm" @@ -43,6 +44,7 @@ import ( var log = logging.Logger("eth_api") var ErrNullRound = errors.New("requested epoch was a null round") +var ErrUnsupported = errors.New("unsupported method") func newEthAPI(em *EthSubModule) (*ethAPI, error) { a := ðAPI{ @@ -233,6 +235,10 @@ func (a *ethAPI) EthGetBlockByNumber(ctx context.Context, blkParam string, fullT } func (a *ethAPI) EthGetTransactionByHash(ctx context.Context, txHash *types.EthHash) (*types.EthTx, error) { + return a.EthGetTransactionByHashLimited(ctx, txHash, constants.LookbackNoLimit) +} + +func (a *ethAPI) EthGetTransactionByHashLimited(ctx context.Context, txHash *types.EthHash, limit abi.ChainEpoch) (*types.EthTx, error) { // Ethereum's behavior is to return null when the txHash is invalid, so we use nil to check if txHash is valid if txHash == nil { return nil, nil @@ -249,7 +255,7 @@ func (a *ethAPI) EthGetTransactionByHash(ctx context.Context, txHash *types.EthH } // first, try to get the cid from mined transactions - msgLookup, err := a.chain.StateSearchMsg(ctx, types.EmptyTSK, c, constants.LookbackNoLimit, true) + msgLookup, err := a.chain.StateSearchMsg(ctx, types.EmptyTSK, c, limit, true) if err == nil && msgLookup != nil { tx, err := newEthTxFromMessageLookup(ctx, msgLookup, -1, a.em.chainModule.MessageStore, a.chain) if err == nil { @@ -363,6 +369,10 @@ func (a *ethAPI) EthGetTransactionCount(ctx context.Context, sender types.EthAdd } func (a *ethAPI) EthGetTransactionReceipt(ctx context.Context, txHash types.EthHash) (*types.EthTxReceipt, error) { + return a.EthGetTransactionReceiptLimited(ctx, txHash, constants.LookbackNoLimit) +} + +func (a *ethAPI) EthGetTransactionReceiptLimited(ctx context.Context, txHash types.EthHash, limit abi.ChainEpoch) (*types.EthTxReceipt, error) { c, err := a.ethTxHashManager.TransactionHashLookup.GetCidFromHash(txHash) if err != nil { log.Debug("could not find transaction hash %s in lookup table", txHash.String()) @@ -373,7 +383,7 @@ func (a *ethAPI) EthGetTransactionReceipt(ctx context.Context, txHash types.EthH c = txHash.ToCid() } - msgLookup, err := a.chain.StateSearchMsg(ctx, types.EmptyTSK, c, constants.LookbackNoLimit, true) + msgLookup, err := a.chain.StateSearchMsg(ctx, types.EmptyTSK, c, limit, true) if err != nil || msgLookup == nil { return nil, nil } @@ -383,20 +393,15 @@ func (a *ethAPI) EthGetTransactionReceipt(ctx context.Context, txHash types.EthH return nil, nil } - replay, err := a.chain.StateReplay(ctx, types.EmptyTSK, c) - if err != nil { - return nil, nil - } - var events []types.Event - if rct := replay.MsgRct; rct != nil && rct.EventsRoot != nil { + if rct := msgLookup.Receipt; rct.EventsRoot != nil { events, err = a.chain.ChainGetEvents(ctx, *rct.EventsRoot) if err != nil { return nil, nil } } - receipt, err := newEthTxReceipt(ctx, tx, msgLookup, replay, events, a.chain) + receipt, err := newEthTxReceipt(ctx, tx, msgLookup, events, a.chain) if err != nil { return nil, nil } @@ -405,11 +410,11 @@ func (a *ethAPI) EthGetTransactionReceipt(ctx context.Context, txHash types.EthH } func (a *ethAPI) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash types.EthHash, txIndex types.EthUint64) (types.EthTx, error) { - return types.EthTx{}, nil + return types.EthTx{}, ErrUnsupported } func (a *ethAPI) EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum types.EthUint64, txIndex types.EthUint64) (types.EthTx, error) { - return types.EthTx{}, nil + return types.EthTx{}, ErrUnsupported } // EthGetCode returns string value of the compiled bytecode @@ -627,7 +632,6 @@ func (a *ethAPI) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (types. if params.BlkCount > 1024 { return types.EthFeeHistory{}, fmt.Errorf("block count should be smaller than 1024") } - rewardPercentiles := make([]float64, 0) if params.RewardPercentiles != nil { rewardPercentiles = append(rewardPercentiles, *params.RewardPercentiles...) @@ -646,48 +650,39 @@ func (a *ethAPI) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (types. return types.EthFeeHistory{}, fmt.Errorf("bad block parameter %s: %s", params.NewestBlkNum, err) } - oldestBlkHeight := uint64(1) - - // NOTE: baseFeePerGas should include the next block after the newest of the returned range, - // because the next base fee can be inferred from the messages in the newest block. - // However, this is NOT the case in Filecoin due to deferred execution, so the best - // we can do is duplicate the last value. - baseFeeArray := []types.EthBigInt{types.EthBigInt(ts.Blocks()[0].ParentBaseFee)} - gasUsedRatioArray := []float64{} - rewardsArray := make([][]types.EthBigInt, 0) + var ( + basefee = ts.Blocks()[0].ParentBaseFee + oldestBlkHeight = uint64(1) + + // NOTE: baseFeePerGas should include the next block after the newest of the returned range, + // because the next base fee can be inferred from the messages in the newest block. + // However, this is NOT the case in Filecoin due to deferred execution, so the best + // we can do is duplicate the last value. + baseFeeArray = []types.EthBigInt{types.EthBigInt(basefee)} + rewardsArray = make([][]types.EthBigInt, 0) + gasUsedRatioArray = []float64{} + blocksIncluded int + ) - blocksIncluded := 0 for blocksIncluded < int(params.BlkCount) && ts.Height() > 0 { - compOutput, err := a.chain.StateCompute(ctx, ts.Height(), nil, ts.Key()) + msgs, rcpts, err := a.messagesAndReceipts(ctx, ts) if err != nil { - return types.EthFeeHistory{}, fmt.Errorf("cannot lookup the status of for tipset: %v: %w", ts, err) + return types.EthFeeHistory{}, fmt.Errorf("failed to retrieve messages and receipts for height %d: %w", ts.Height(), err) } txGasRewards := gasRewardSorter{} - for _, msg := range compOutput.Trace { - if msg.Msg.From == builtintypes.SystemActorAddr { - continue - } - - smsgCid, err := getSignedMessage(ctx, a.em.chainModule.MessageStore, msg.MsgCid) - if err != nil { - return types.EthFeeHistory{}, fmt.Errorf("failed to get signed msg %s: %w", msg.MsgCid, err) - } - tx, err := newEthTxFromSignedMessage(ctx, smsgCid, a.chain) - if err != nil { - return types.EthFeeHistory{}, err - } - + for i, msg := range msgs { + effectivePremium := msg.VMMessage().EffectiveGasPremium(basefee) txGasRewards = append(txGasRewards, gasRewardTuple{ - reward: tx.Reward(ts.Blocks()[0].ParentBaseFee), - gas: uint64(msg.MsgRct.GasUsed), + premium: effectivePremium, + gasUsed: rcpts[i].GasUsed, }) } rewards, totalGasUsed := calculateRewardsAndGasUsed(rewardPercentiles, txGasRewards) // arrays should be reversed at the end - baseFeeArray = append(baseFeeArray, types.EthBigInt(ts.Blocks()[0].ParentBaseFee)) + baseFeeArray = append(baseFeeArray, types.EthBigInt(basefee)) gasUsedRatioArray = append(gasUsedRatioArray, float64(totalGasUsed)/float64(constants.BlockGasLimit)) rewardsArray = append(rewardsArray, rewards) oldestBlkHeight = uint64(ts.Height()) @@ -719,17 +714,11 @@ func (a *ethAPI) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (types. if params.RewardPercentiles != nil { ret.Reward = &rewardsArray } - return ret, nil } func (a *ethAPI) NetVersion(ctx context.Context) (string, error) { - // Note that networkId is not encoded in hex - nv, err := a.chain.StateNetworkVersion(ctx, types.EmptyTSK) - if err != nil { - return "", err - } - return strconv.FormatUint(uint64(nv), 10), nil + return strconv.FormatInt(int64(types2.Eip155ChainID), 10), nil } func (a *ethAPI) NetListening(ctx context.Context) (bool, error) { @@ -1335,7 +1324,7 @@ func newEthTxFromMessageLookup(ctx context.Context, msgLookup *types.MsgLookup, return tx, nil } -func newEthTxReceipt(ctx context.Context, tx types.EthTx, lookup *types.MsgLookup, replay *types.InvocResult, events []types.Event, ca v1.IChain) (types.EthTxReceipt, error) { +func newEthTxReceipt(ctx context.Context, tx types.EthTx, lookup *types.MsgLookup, events []types.Event, ca v1.IChain) (types.EthTxReceipt, error) { var ( transactionIndex types.EthUint64 blockHash types.EthHash @@ -1376,7 +1365,20 @@ func newEthTxReceipt(ctx context.Context, tx types.EthTx, lookup *types.MsgLooku // TODO: handle CumulativeGasUsed receipt.CumulativeGasUsed = types.EmptyEthInt - effectiveGasPrice := big.Div(replay.GasCost.TotalCost, big.NewInt(lookup.Receipt.GasUsed)) + // TODO: avoid loading the tipset twice (once here, once when we convert the message to a txn) + ts, err := ca.ChainGetTipSet(ctx, lookup.TipSet) + if err != nil { + return types.EthTxReceipt{}, fmt.Errorf("failed to lookup tipset %s when constructing the eth txn receipt: %w", lookup.TipSet, err) + } + + baseFee := ts.Blocks()[0].ParentBaseFee + gasOutputs := gas.ComputeGasOutputs(lookup.Receipt.GasUsed, int64(tx.Gas), baseFee, big.Int(tx.MaxFeePerGas), big.Int(tx.MaxPriorityFeePerGas), true) + totalSpent := big.Sum(gasOutputs.BaseFeeBurn, gasOutputs.MinerTip, gasOutputs.OverEstimationBurn) + + effectiveGasPrice := big.Zero() + if lookup.Receipt.GasUsed > 0 { + effectiveGasPrice = big.Div(totalSpent, big.NewInt(lookup.Receipt.GasUsed)) + } receipt.EffectiveGasPrice = types.EthBigInt(effectiveGasPrice) if receipt.To == nil && lookup.Receipt.ExitCode.IsSuccess() { @@ -1645,10 +1647,33 @@ func parseEthRevert(ret []byte) string { return types.EthBytes(cbytes).String() } -func calculateRewardsAndGasUsed(rewardPercentiles []float64, txGasRewards gasRewardSorter) ([]types.EthBigInt, uint64) { - var totalGasUsed uint64 +func (a *ethAPI) messagesAndReceipts(ctx context.Context, ts *types.TipSet) ([]types.ChainMsg, []types.MessageReceipt, error) { + msgs, err := a.em.chainModule.MessageStore.MessagesForTipset(ts) + if err != nil { + return nil, nil, fmt.Errorf("error loading messages for tipset: %v: %w", ts, err) + } + + _, rcptRoot, err := a.em.chainModule.Stmgr.RunStateTransition(ctx, ts, nil, false) + if err != nil { + return nil, nil, fmt.Errorf("failed to compute state: %w", err) + } + + rcpts, err := a.em.chainModule.MessageStore.LoadReceipts(ctx, rcptRoot) + if err != nil { + return nil, nil, fmt.Errorf("error loading receipts for tipset: %v: %w", ts, err) + } + + if len(msgs) != len(rcpts) { + return nil, nil, fmt.Errorf("receipts and message array lengths didn't match for tipset: %v: %w", ts, err) + } + + return msgs, rcpts, nil +} + +func calculateRewardsAndGasUsed(rewardPercentiles []float64, txGasRewards gasRewardSorter) ([]types.EthBigInt, int64) { + var gasUsedTotal int64 for _, tx := range txGasRewards { - totalGasUsed += tx.gas + gasUsedTotal += tx.gasUsed } rewards := make([]types.EthBigInt, len(rewardPercentiles)) @@ -1657,23 +1682,23 @@ func calculateRewardsAndGasUsed(rewardPercentiles []float64, txGasRewards gasRew } if len(txGasRewards) == 0 { - return rewards, totalGasUsed + return rewards, gasUsedTotal } sort.Stable(txGasRewards) var idx int - var sum uint64 + var sum int64 for i, percentile := range rewardPercentiles { - threshold := uint64(float64(totalGasUsed) * percentile / 100) + threshold := int64(float64(gasUsedTotal) * percentile / 100) for sum < threshold && idx < len(txGasRewards)-1 { - sum += txGasRewards[idx].gas + sum += txGasRewards[idx].gasUsed idx++ } - rewards[i] = txGasRewards[idx].reward + rewards[i] = types.EthBigInt(txGasRewards[idx].premium) } - return rewards, totalGasUsed + return rewards, gasUsedTotal } func getSignedMessage(ctx context.Context, ms *chain.MessageStore, msgCid cid.Cid) (*types.SignedMessage, error) { @@ -1696,8 +1721,8 @@ func getSignedMessage(ctx context.Context, ms *chain.MessageStore, msgCid cid.Ci } type gasRewardTuple struct { - gas uint64 - reward types.EthBigInt + gasUsed int64 + premium abi.TokenAmount } // sorted in ascending order @@ -1708,7 +1733,7 @@ func (g gasRewardSorter) Swap(i, j int) { g[i], g[j] = g[j], g[i] } func (g gasRewardSorter) Less(i, j int) bool { - return g[i].reward.Int.Cmp(g[j].reward.Int) == -1 + return g[i].premium.Int.Cmp(g[j].premium.Int) == -1 } var _ v1.IETH = ðAPI{} diff --git a/app/submodule/eth/eth_event_api.go b/app/submodule/eth/eth_event_api.go index 885b66dfa0..5e7cb3e9a8 100644 --- a/app/submodule/eth/eth_event_api.go +++ b/app/submodule/eth/eth_event_api.go @@ -22,6 +22,7 @@ import ( "github.com/google/uuid" "github.com/ipfs/go-cid" "github.com/multiformats/go-varint" + "github.com/zyedidia/generic/queue" ) const ChainHeadConfidence = 1 @@ -437,7 +438,7 @@ func (e *ethEventAPI) EthSubscribe(ctx context.Context, p jsonrpc.RawParams) (ty return types.EthSubscriptionID{}, fmt.Errorf("connection doesn't support callbacks") } - sub, err := e.SubManager.StartSubscription(e.SubscribtionCtx, ethCb.EthSubscription) + sub, err := e.SubManager.StartSubscription(e.SubscribtionCtx, ethCb.EthSubscription, e.uninstallFilter) if err != nil { return types.EthSubscriptionID{}, err } @@ -504,18 +505,11 @@ func (e *ethEventAPI) EthUnsubscribe(ctx context.Context, id types.EthSubscripti return false, api.ErrNotSupported } - filters, err := e.SubManager.StopSubscription(ctx, id) + err := e.SubManager.StopSubscription(ctx, id) if err != nil { return false, nil } - for _, f := range filters { - if err := e.uninstallFilter(ctx, f); err != nil { - // this will leave the filter a zombie, collecting events up to the maximum allowed - log.Warnf("failed to remove filter when unsubscribing: %v", err) - } - } - return true, nil } @@ -702,7 +696,7 @@ type EthSubscriptionManager struct { // nolint subs map[types.EthSubscriptionID]*ethSubscription } -func (e *EthSubscriptionManager) StartSubscription(ctx context.Context, out ethSubscriptionCallback) (*ethSubscription, error) { // nolint +func (e *EthSubscriptionManager) StartSubscription(ctx context.Context, out ethSubscriptionCallback, dropFilter func(context.Context, filter.Filter) error) (*ethSubscription, error) { // nolint rawid, err := uuid.NewRandom() if err != nil { return nil, fmt.Errorf("new uuid: %w", err) @@ -713,12 +707,16 @@ func (e *EthSubscriptionManager) StartSubscription(ctx context.Context, out ethS ctx, quit := context.WithCancel(ctx) sub := ðSubscription{ - chainAPI: e.ChainAPI, - messageStore: e.messageStore, - id: id, - in: make(chan interface{}, 200), - out: out, - quit: quit, + chainAPI: e.ChainAPI, + messageStore: e.messageStore, + uninstallFilter: dropFilter, + id: id, + in: make(chan interface{}, 200), + out: out, + quit: quit, + + toSend: queue.New[[]byte](), + sendCond: make(chan struct{}, 1), } e.mu.Lock() @@ -729,36 +727,45 @@ func (e *EthSubscriptionManager) StartSubscription(ctx context.Context, out ethS e.mu.Unlock() go sub.start(ctx) + go sub.startOut(ctx) return sub, nil } -func (e *EthSubscriptionManager) StopSubscription(ctx context.Context, id types.EthSubscriptionID) ([]filter.Filter, error) { +func (e *EthSubscriptionManager) StopSubscription(ctx context.Context, id types.EthSubscriptionID) error { e.mu.Lock() defer e.mu.Unlock() sub, ok := e.subs[id] if !ok { - return nil, fmt.Errorf("subscription not found") + return fmt.Errorf("subscription not found") } sub.stop() delete(e.subs, id) - return sub.filters, nil + return nil } type ethSubscriptionCallback func(context.Context, jsonrpc.RawParams) error +const maxSendQueue = 20000 + type ethSubscription struct { - chainAPI v1.IChain - messageStore *chain.MessageStore - id types.EthSubscriptionID - in chan interface{} - out ethSubscriptionCallback + chainAPI v1.IChain + messageStore *chain.MessageStore + uninstallFilter func(context.Context, filter.Filter) error + id types.EthSubscriptionID + in chan interface{} + out ethSubscriptionCallback mu sync.Mutex filters []filter.Filter quit func() + + sendLk sync.Mutex + sendQueueLen int + toSend *queue.Queue[[]byte] + sendCond chan struct{} } func (e *ethSubscription) addFilter(ctx context.Context, f filter.Filter) { @@ -769,6 +776,36 @@ func (e *ethSubscription) addFilter(ctx context.Context, f filter.Filter) { e.filters = append(e.filters, f) } +// sendOut processes the final subscription queue. It's here in case the subscriber +// is slow, and we need to buffer the messages. +func (e *ethSubscription) startOut(ctx context.Context) { + for { + select { + case <-ctx.Done(): + return + case <-e.sendCond: + e.sendLk.Lock() + + for !e.toSend.Empty() { + front := e.toSend.Dequeue() + e.sendQueueLen-- + + e.sendLk.Unlock() + + if err := e.out(ctx, front); err != nil { + log.Warnw("error sending subscription response, killing subscription", "sub", e.id, "error", err) + e.stop() + return + } + + e.sendLk.Lock() + } + + e.sendLk.Unlock() + } + } +} + func (e *ethSubscription) send(ctx context.Context, v interface{}) { resp := types.EthSubscriptionResponse{ SubscriptionID: e.id, @@ -781,10 +818,22 @@ func (e *ethSubscription) send(ctx context.Context, v interface{}) { return } - if err := e.out(ctx, outParam); err != nil { - log.Warnw("sending subscription response", "sub", e.id, "error", err) + e.sendLk.Lock() + defer e.sendLk.Unlock() + + e.toSend.Enqueue(outParam) + + e.sendQueueLen++ + if e.sendQueueLen > maxSendQueue { + log.Warnw("subscription send queue full, killing subscription", "sub", e.id) + e.stop() return } + + select { + case e.sendCond <- struct{}{}: + default: // already signalled, and we're holding the lock so we know that the event will be processed + } } func (e *ethSubscription) start(ctx context.Context) { @@ -828,10 +877,22 @@ func (e *ethSubscription) start(ctx context.Context) { func (e *ethSubscription) stop() { e.mu.Lock() - defer e.mu.Unlock() + if e.quit == nil { + e.mu.Unlock() + return + } if e.quit != nil { e.quit() e.quit = nil + e.mu.Unlock() + + for _, f := range e.filters { + // note: the context in actually unused in uninstallFilter + if err := e.uninstallFilter(context.TODO(), f); err != nil { + // this will leave the filter a zombie, collecting events up to the maximum allowed + log.Warnf("failed to remove filter when unsubscribing: %v", err) + } + } } } diff --git a/app/submodule/eth/eth_test.go b/app/submodule/eth/eth_test.go index 7b978f0d2b..05d9a10ade 100644 --- a/app/submodule/eth/eth_test.go +++ b/app/submodule/eth/eth_test.go @@ -116,11 +116,8 @@ func TestReward(t *testing.T) { {maxFeePerGas: big.NewInt(50), maxPriorityFeePerGas: big.NewInt(200), answer: big.NewInt(-50)}, } for _, tc := range testcases { - tx := types.EthTx{ - MaxFeePerGas: types.EthBigInt(tc.maxFeePerGas), - MaxPriorityFeePerGas: types.EthBigInt(tc.maxPriorityFeePerGas), - } - reward := tx.Reward(baseFee) + msg := &types.Message{GasFeeCap: tc.maxFeePerGas, GasPremium: tc.maxPriorityFeePerGas} + reward := msg.EffectiveGasPremium(baseFee) require.Equal(t, 0, reward.Int.Cmp(tc.answer.Int), reward, tc.answer) } } @@ -139,20 +136,20 @@ func TestRewardPercentiles(t *testing.T) { { percentiles: []float64{25, 50, 75, 100}, txGasRewards: []gasRewardTuple{ - {gas: uint64(0), reward: types.EthBigInt(big.NewInt(300))}, - {gas: uint64(100), reward: types.EthBigInt(big.NewInt(200))}, - {gas: uint64(350), reward: types.EthBigInt(big.NewInt(100))}, - {gas: uint64(500), reward: types.EthBigInt(big.NewInt(600))}, - {gas: uint64(300), reward: types.EthBigInt(big.NewInt(700))}, + {gasUsed: int64(0), premium: big.NewInt(300)}, + {gasUsed: int64(100), premium: big.NewInt(200)}, + {gasUsed: int64(350), premium: big.NewInt(100)}, + {gasUsed: int64(500), premium: big.NewInt(600)}, + {gasUsed: int64(300), premium: big.NewInt(700)}, }, answer: []int64{200, 700, 700, 700}, }, } for _, tc := range testcases { rewards, totalGasUsed := calculateRewardsAndGasUsed(tc.percentiles, tc.txGasRewards) - gasUsed := uint64(0) + var gasUsed int64 for _, tx := range tc.txGasRewards { - gasUsed += tx.gas + gasUsed += tx.gasUsed } ans := []types.EthBigInt{} for _, bi := range tc.answer { diff --git a/cmd/evm.go b/cmd/evm.go index ff4c38f808..ea2320e6fa 100644 --- a/cmd/evm.go +++ b/cmd/evm.go @@ -38,6 +38,7 @@ var evmCmd = &cmds.Command{ "stat": evmGetInfoCmd, "call": evmCallSimulateCmd, "contract-address": evmGetContractAddressCmd, + "bytecode": evmGetBytecode, }, } @@ -442,6 +443,48 @@ var evmInvokeCmd = &cmds.Command{ }, } +var evmGetBytecode = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Write the bytecode of a smart contract to a file", + }, + Arguments: []cmds.Argument{ + cmds.StringArg("contract-address", true, false, "contract address"), + cmds.StringArg("file-name", true, false, "file name"), + }, + Options: []cmds.Option{ + cmds.StringOption("bin", "write the bytecode as raw binary and don't hex-encode"), + }, + Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error { + if len(req.Arguments) != 2 { + return fmt.Errorf("must pass the contract address and file name") + } + + contractAddr, err := types.ParseEthAddress(req.Arguments[0]) + if err != nil { + return err + } + + fileName := req.Arguments[1] + ctx := requestContext(req) + api := getEnv(env) + + code, err := api.EthAPI.EthGetCode(ctx, contractAddr, "latest") + if err != nil { + return err + } + if bin, _ := req.Options["bin"].(bool); bin { + newCode := make([]byte, hex.EncodedLen(len(code))) + hex.Encode(newCode, code) + code = newCode + } + if err := os.WriteFile(fileName, code, 0o666); err != nil { + return fmt.Errorf("failed to write bytecode to file %s: %w", fileName, err) + } + + return printOneString(re, fmt.Sprintf("Code for %s written to %s\n", contractAddr, fileName)) + }, +} + func ethAddrFromFilecoinAddress(ctx context.Context, addr address.Address, chainAPI v1api.IChain) (types.EthAddress, address.Address, error) { var faddr address.Address var err error diff --git a/go.mod b/go.mod index 58d1354658..9aac488e65 100644 --- a/go.mod +++ b/go.mod @@ -103,6 +103,7 @@ require ( github.com/whyrusleeping/cbor-gen v0.0.0-20221021053955-c138aae13722 github.com/whyrusleeping/go-logging v0.0.1 github.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1 + github.com/zyedidia/generic v1.2.1 go.opencensus.io v0.23.0 go.uber.org/zap v1.23.0 golang.org/x/crypto v0.5.0 diff --git a/go.sum b/go.sum index 65f6feb88c..a72a455514 100644 --- a/go.sum +++ b/go.sum @@ -1812,6 +1812,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zyedidia/generic v1.2.1 h1:Zv5KS/N2m0XZZiuLS82qheRG4X1o5gsWreGb0hR7XDc= +github.com/zyedidia/generic v1.2.1/go.mod h1:ly2RBz4mnz1yeuVbQA/VFwGjK3mnHGRj1JuoG336Bis= go.dedis.ch/fixbuf v1.0.3 h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs= go.dedis.ch/fixbuf v1.0.3/go.mod h1:yzJMt34Wa5xD37V5RTdmp38cz3QhMagdGoem9anUalw= go.dedis.ch/kyber/v3 v3.0.4/go.mod h1:OzvaEnPvKlyrWyp3kGXlFdp7ap1VC6RkZDTaPikqhsQ= diff --git a/pkg/consensus/processor.go b/pkg/consensus/processor.go index 07f0dd172f..969a130a41 100644 --- a/pkg/consensus/processor.go +++ b/pkg/consensus/processor.go @@ -206,7 +206,7 @@ func (p *DefaultProcessor) ApplyBlocks(ctx context.Context, } if cb != nil { - if err := cb(mcid, m.VMMessage(), ret); err != nil { + if err := cb(m.Cid(), m.VMMessage(), ret); err != nil { return cid.Undef, nil, err } } diff --git a/venus-shared/actors/types/eth.go b/venus-shared/actors/types/eth.go index 97ee6fd2e3..bf6c41bc54 100644 --- a/venus-shared/actors/types/eth.go +++ b/venus-shared/actors/types/eth.go @@ -301,17 +301,21 @@ func EthAddressFromPubKey(pubk []byte) ([]byte, error) { return ethAddr, nil } +var maskedIDPrefix = [20 - 8]byte{0xff} + func IsEthAddress(addr address.Address) bool { if addr.Protocol() != address.Delegated { return false } payload := addr.Payload() - namespace, _, err := varint.FromUvarint(payload) + namespace, offset, err := varint.FromUvarint(payload) if err != nil { return false } - return namespace == builtintypes.EthereumAddressManagerActorID + payload = payload[offset:] + + return namespace == builtintypes.EthereumAddressManagerActorID && len(payload) == 20 && !bytes.HasPrefix(payload, maskedIDPrefix[:]) } func EthAddressFromFilecoinAddress(addr address.Address) (EthAddress, error) { @@ -332,9 +336,17 @@ func EthAddressFromFilecoinAddress(addr address.Address) (EthAddress, error) { return EthAddress{}, fmt.Errorf("invalid delegated address namespace in: %s", addr) } payload = payload[n:] - if namespace == builtintypes.EthereumAddressManagerActorID { - return CastEthAddress(payload) + if namespace != builtintypes.EthereumAddressManagerActorID { + return EthAddress{}, ErrInvalidAddress + } + ethAddr, err := CastEthAddress(payload) + if err != nil { + return EthAddress{}, err + } + if ethAddr.IsMaskedID() { + return EthAddress{}, fmt.Errorf("f410f addresses cannot embed masked-ID payloads: %s", ethAddr) } + return ethAddr, nil } return EthAddress{}, ErrInvalidAddress } @@ -382,8 +394,7 @@ func (ea *EthAddress) UnmarshalJSON(b []byte) error { } func (ea EthAddress) IsMaskedID() bool { - idmask := [12]byte{0xff} - return bytes.Equal(ea[:12], idmask[:]) + return bytes.HasPrefix(ea[:], maskedIDPrefix[:]) } func (ea EthAddress) ToFilecoinAddress() (address.Address, error) { diff --git a/venus-shared/actors/types/eth_transactions.go b/venus-shared/actors/types/eth_transactions.go index 733649fda3..e57b5f215e 100644 --- a/venus-shared/actors/types/eth_transactions.go +++ b/venus-shared/actors/types/eth_transactions.go @@ -40,14 +40,6 @@ type EthTx struct { S EthBigInt `json:"s"` } -func (tx *EthTx) Reward(blkBaseFee big.Int) EthBigInt { - availablePriorityFee := big.Sub(big.Int(tx.MaxFeePerGas), blkBaseFee) - if big.Cmp(big.Int(tx.MaxPriorityFeePerGas), availablePriorityFee) <= 0 { - return tx.MaxPriorityFeePerGas - } - return EthBigInt(availablePriorityFee) -} - type EthTxArgs struct { ChainID int `json:"chainId"` Nonce int `json:"nonce"` diff --git a/venus-shared/actors/types/message.go b/venus-shared/actors/types/message.go index 3bf903df01..83758406c6 100644 --- a/venus-shared/actors/types/message.go +++ b/venus-shared/actors/types/message.go @@ -7,6 +7,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/network" @@ -217,6 +218,19 @@ func (m *Message) ValidForBlockInclusion(minGas int64, version network.Version) return nil } +// EffectiveGasPremium returns the effective gas premium claimable by the miner +// given the supplied base fee. +// +// Filecoin clamps the gas premium at GasFeeCap - BaseFee, if lower than the +// specified premium. +func (m *Message) EffectiveGasPremium(baseFee abi.TokenAmount) abi.TokenAmount { + available := big.Sub(m.GasFeeCap, baseFee) + if big.Cmp(m.GasPremium, available) <= 0 { + return m.GasPremium + } + return available +} + func (m *Message) VMMessage() *Message { return m } diff --git a/venus-shared/api/chain/v1/eth.go b/venus-shared/api/chain/v1/eth.go index ce3499a8ce..d8d05b9a09 100644 --- a/venus-shared/api/chain/v1/eth.go +++ b/venus-shared/api/chain/v1/eth.go @@ -5,6 +5,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-jsonrpc" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/venus/venus-shared/types" "github.com/ipfs/go-cid" ) @@ -33,10 +34,12 @@ type IETH interface { EthGetBlockByHash(ctx context.Context, blkHash types.EthHash, fullTxInfo bool) (types.EthBlock, error) //perm:read EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (types.EthBlock, error) //perm:read EthGetTransactionByHash(ctx context.Context, txHash *types.EthHash) (*types.EthTx, error) //perm:read + EthGetTransactionByHashLimited(ctx context.Context, txHash *types.EthHash, limit abi.ChainEpoch) (*types.EthTx, error) //perm:read EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*types.EthHash, error) //perm:read EthGetMessageCidByTransactionHash(ctx context.Context, txHash *types.EthHash) (*cid.Cid, error) //perm:read EthGetTransactionCount(ctx context.Context, sender types.EthAddress, blkOpt string) (types.EthUint64, error) //perm:read EthGetTransactionReceipt(ctx context.Context, txHash types.EthHash) (*types.EthTxReceipt, error) //perm:read + EthGetTransactionReceiptLimited(ctx context.Context, txHash types.EthHash, limit abi.ChainEpoch) (*types.EthTxReceipt, error) //perm:read EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash types.EthHash, txIndex types.EthUint64) (types.EthTx, error) //perm:read EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum types.EthUint64, txIndex types.EthUint64) (types.EthTx, error) //perm:read @@ -99,7 +102,7 @@ type IETHEvent interface { // reverse interface to the client, called after EthSubscribe type EthSubscriber interface { - // note: the parameter is ethtypes.EthSubscriptionResponse serialized as json object + // note: the parameter is types.EthSubscriptionResponse serialized as json object EthSubscription(ctx context.Context, params jsonrpc.RawParams) error //rpc_method:eth_subscription notify:true } diff --git a/venus-shared/api/chain/v1/method.md b/venus-shared/api/chain/v1/method.md index 571a1a52da..b32f5d3e9e 100644 --- a/venus-shared/api/chain/v1/method.md +++ b/venus-shared/api/chain/v1/method.md @@ -83,9 +83,11 @@ curl http://:/rpc/v1 -X POST -H "Content-Type: application/json" -H " * [EthGetTransactionByBlockHashAndIndex](#ethgettransactionbyblockhashandindex) * [EthGetTransactionByBlockNumberAndIndex](#ethgettransactionbyblocknumberandindex) * [EthGetTransactionByHash](#ethgettransactionbyhash) + * [EthGetTransactionByHashLimited](#ethgettransactionbyhashlimited) * [EthGetTransactionCount](#ethgettransactioncount) * [EthGetTransactionHashByCid](#ethgettransactionhashbycid) * [EthGetTransactionReceipt](#ethgettransactionreceipt) + * [EthGetTransactionReceiptLimited](#ethgettransactionreceiptlimited) * [EthMaxPriorityFeePerGas](#ethmaxpriorityfeepergas) * [EthProtocolVersion](#ethprotocolversion) * [EthSendRawTransaction](#ethsendrawtransaction) @@ -2535,6 +2537,45 @@ Response: } ``` +### EthGetTransactionByHashLimited + + +Perms: read + +Inputs: +```json +[ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + 10101 +] +``` + +Response: +```json +{ + "chainId": "0x5", + "nonce": "0x5", + "hash": "0x0707070707070707070707070707070707070707070707070707070707070707", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": "0x5", + "transactionIndex": "0x5", + "from": "0x0707070707070707070707070707070707070707", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "value": "0x0", + "type": "0x5", + "input": "0x07", + "gas": "0x5", + "maxFeePerGas": "0x0", + "maxPriorityFeePerGas": "0x0", + "accessList": [ + "0x0707070707070707070707070707070707070707070707070707070707070707" + ], + "v": "0x0", + "r": "0x0", + "s": "0x0" +} +``` + ### EthGetTransactionCount @@ -2613,6 +2654,54 @@ Response: } ``` +### EthGetTransactionReceiptLimited + + +Perms: read + +Inputs: +```json +[ + "0x0707070707070707070707070707070707070707070707070707070707070707", + 10101 +] +``` + +Response: +```json +{ + "transactionHash": "0x0707070707070707070707070707070707070707070707070707070707070707", + "transactionIndex": "0x5", + "blockHash": "0x0707070707070707070707070707070707070707070707070707070707070707", + "blockNumber": "0x5", + "from": "0x0707070707070707070707070707070707070707", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "root": "0x0707070707070707070707070707070707070707070707070707070707070707", + "status": "0x5", + "contractAddress": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "cumulativeGasUsed": "0x5", + "gasUsed": "0x5", + "effectiveGasPrice": "0x0", + "logsBloom": "0x07", + "logs": [ + { + "address": "0x0707070707070707070707070707070707070707", + "data": "0x07", + "topics": [ + "0x0707070707070707070707070707070707070707070707070707070707070707" + ], + "removed": true, + "logIndex": "0x5", + "transactionIndex": "0x5", + "transactionHash": "0x0707070707070707070707070707070707070707070707070707070707070707", + "blockHash": "0x0707070707070707070707070707070707070707070707070707070707070707", + "blockNumber": "0x5" + } + ], + "type": "0x5" +} +``` + ### EthMaxPriorityFeePerGas diff --git a/venus-shared/api/chain/v1/mock/mock_fullnode.go b/venus-shared/api/chain/v1/mock/mock_fullnode.go index 94c25a62fd..b04cec704e 100644 --- a/venus-shared/api/chain/v1/mock/mock_fullnode.go +++ b/venus-shared/api/chain/v1/mock/mock_fullnode.go @@ -785,6 +785,21 @@ func (mr *MockFullNodeMockRecorder) EthGetTransactionByHash(arg0, arg1 interface return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthGetTransactionByHash", reflect.TypeOf((*MockFullNode)(nil).EthGetTransactionByHash), arg0, arg1) } +// EthGetTransactionByHashLimited mocks base method. +func (m *MockFullNode) EthGetTransactionByHashLimited(arg0 context.Context, arg1 *types.EthHash, arg2 abi.ChainEpoch) (*types.EthTx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EthGetTransactionByHashLimited", arg0, arg1, arg2) + ret0, _ := ret[0].(*types.EthTx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EthGetTransactionByHashLimited indicates an expected call of EthGetTransactionByHashLimited. +func (mr *MockFullNodeMockRecorder) EthGetTransactionByHashLimited(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthGetTransactionByHashLimited", reflect.TypeOf((*MockFullNode)(nil).EthGetTransactionByHashLimited), arg0, arg1, arg2) +} + // EthGetTransactionCount mocks base method. func (m *MockFullNode) EthGetTransactionCount(arg0 context.Context, arg1 types.EthAddress, arg2 string) (types.EthUint64, error) { m.ctrl.T.Helper() @@ -830,6 +845,21 @@ func (mr *MockFullNodeMockRecorder) EthGetTransactionReceipt(arg0, arg1 interfac return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthGetTransactionReceipt", reflect.TypeOf((*MockFullNode)(nil).EthGetTransactionReceipt), arg0, arg1) } +// EthGetTransactionReceiptLimited mocks base method. +func (m *MockFullNode) EthGetTransactionReceiptLimited(arg0 context.Context, arg1 types.EthHash, arg2 abi.ChainEpoch) (*types.EthTxReceipt, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EthGetTransactionReceiptLimited", arg0, arg1, arg2) + ret0, _ := ret[0].(*types.EthTxReceipt) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EthGetTransactionReceiptLimited indicates an expected call of EthGetTransactionReceiptLimited. +func (mr *MockFullNodeMockRecorder) EthGetTransactionReceiptLimited(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthGetTransactionReceiptLimited", reflect.TypeOf((*MockFullNode)(nil).EthGetTransactionReceiptLimited), arg0, arg1, arg2) +} + // EthMaxPriorityFeePerGas mocks base method. func (m *MockFullNode) EthMaxPriorityFeePerGas(arg0 context.Context) (types.EthBigInt, error) { m.ctrl.T.Helper() diff --git a/venus-shared/api/chain/v1/proxy_gen.go b/venus-shared/api/chain/v1/proxy_gen.go index bf320b13fc..f923c98bae 100644 --- a/venus-shared/api/chain/v1/proxy_gen.go +++ b/venus-shared/api/chain/v1/proxy_gen.go @@ -857,9 +857,11 @@ type IETHStruct struct { EthGetTransactionByBlockHashAndIndex func(ctx context.Context, blkHash types.EthHash, txIndex types.EthUint64) (types.EthTx, error) `perm:"read"` EthGetTransactionByBlockNumberAndIndex func(ctx context.Context, blkNum types.EthUint64, txIndex types.EthUint64) (types.EthTx, error) `perm:"read"` EthGetTransactionByHash func(ctx context.Context, txHash *types.EthHash) (*types.EthTx, error) `perm:"read"` + EthGetTransactionByHashLimited func(ctx context.Context, txHash *types.EthHash, limit abi.ChainEpoch) (*types.EthTx, error) `perm:"read"` EthGetTransactionCount func(ctx context.Context, sender types.EthAddress, blkOpt string) (types.EthUint64, error) `perm:"read"` EthGetTransactionHashByCid func(ctx context.Context, cid cid.Cid) (*types.EthHash, error) `perm:"read"` EthGetTransactionReceipt func(ctx context.Context, txHash types.EthHash) (*types.EthTxReceipt, error) `perm:"read"` + EthGetTransactionReceiptLimited func(ctx context.Context, txHash types.EthHash, limit abi.ChainEpoch) (*types.EthTxReceipt, error) `perm:"read"` EthMaxPriorityFeePerGas func(ctx context.Context) (types.EthBigInt, error) `perm:"read"` EthProtocolVersion func(ctx context.Context) (types.EthUint64, error) `perm:"read"` EthSendRawTransaction func(ctx context.Context, rawTx types.EthBytes) (types.EthHash, error) `perm:"read"` @@ -927,6 +929,9 @@ func (s *IETHStruct) EthGetTransactionByBlockNumberAndIndex(p0 context.Context, func (s *IETHStruct) EthGetTransactionByHash(p0 context.Context, p1 *types.EthHash) (*types.EthTx, error) { return s.Internal.EthGetTransactionByHash(p0, p1) } +func (s *IETHStruct) EthGetTransactionByHashLimited(p0 context.Context, p1 *types.EthHash, p2 abi.ChainEpoch) (*types.EthTx, error) { + return s.Internal.EthGetTransactionByHashLimited(p0, p1, p2) +} func (s *IETHStruct) EthGetTransactionCount(p0 context.Context, p1 types.EthAddress, p2 string) (types.EthUint64, error) { return s.Internal.EthGetTransactionCount(p0, p1, p2) } @@ -936,6 +941,9 @@ func (s *IETHStruct) EthGetTransactionHashByCid(p0 context.Context, p1 cid.Cid) func (s *IETHStruct) EthGetTransactionReceipt(p0 context.Context, p1 types.EthHash) (*types.EthTxReceipt, error) { return s.Internal.EthGetTransactionReceipt(p0, p1) } +func (s *IETHStruct) EthGetTransactionReceiptLimited(p0 context.Context, p1 types.EthHash, p2 abi.ChainEpoch) (*types.EthTxReceipt, error) { + return s.Internal.EthGetTransactionReceiptLimited(p0, p1, p2) +} func (s *IETHStruct) EthMaxPriorityFeePerGas(p0 context.Context) (types.EthBigInt, error) { return s.Internal.EthMaxPriorityFeePerGas(p0) } diff --git a/venus-shared/compatible-checks/api-diff.txt b/venus-shared/compatible-checks/api-diff.txt index 1ce3641f17..9d11363a96 100644 --- a/venus-shared/compatible-checks/api-diff.txt +++ b/venus-shared/compatible-checks/api-diff.txt @@ -164,6 +164,8 @@ github.com/filecoin-project/venus/venus-shared/api/chain/v1.FullNode <> github.c + Concurrent - CreateBackup - Discover + + EthGetTransactionByHashLimited + + EthGetTransactionReceiptLimited + GasBatchEstimateMessageGas > GasEstimateMessageGas {[func(context.Context, *types.Message, *types.MessageSendSpec, types.TipSetKey) (*types.Message, error) <> func(context.Context, *types.Message, *api.MessageSendSpec, types.TipSetKey) (*types.Message, error)] base=func in type: #2 input; nested={[*types.MessageSendSpec <> *api.MessageSendSpec] base=pointed type; nested={[types.MessageSendSpec <> api.MessageSendSpec] base=struct field; nested={[types.MessageSendSpec <> api.MessageSendSpec] base=exported fields count: 3 != 2; nested=nil}}}} + GetActor diff --git a/venus-shared/compatible-checks/api-perm.txt b/venus-shared/compatible-checks/api-perm.txt index 1abfaa8248..7bdc35ee2b 100644 --- a/venus-shared/compatible-checks/api-perm.txt +++ b/venus-shared/compatible-checks/api-perm.txt @@ -67,6 +67,8 @@ v1: github.com/filecoin-project/venus/venus-shared/api/chain/v1 <> github.com/fi - IMinerState.StateMinerSectorSize - IMinerState.StateMinerWorkerAddress - EthSubscriber.EthSubscription + - IETH.EthGetTransactionByHashLimited + - IETH.EthGetTransactionReceiptLimited - IMessagePool.GasBatchEstimateMessageGas - IMessagePool.MpoolDeleteByAdress - IMessagePool.MpoolPublishByAddr diff --git a/venus-shared/types/eth_test.go b/venus-shared/types/eth_test.go index 990fef09b1..2860b9782f 100644 --- a/venus-shared/types/eth_test.go +++ b/venus-shared/types/eth_test.go @@ -9,6 +9,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/builtin" ) type TestCase struct { @@ -177,6 +178,20 @@ func TestParseEthAddr(t *testing.T) { } } +func TestMaskedIDInF4(t *testing.T) { + addr, err := address.NewIDAddress(100) + require.NoError(t, err) + + eaddr, err := EthAddressFromFilecoinAddress(addr) + require.NoError(t, err) + + badaddr, err := address.NewDelegatedAddress(builtin.EthereumAddressManagerActorID, eaddr[:]) + require.NoError(t, err) + + _, err = EthAddressFromFilecoinAddress(badaddr) + require.Error(t, err) +} + func TestUnmarshalEthCall(t *testing.T) { data := `{"from":"0x4D6D86b31a112a05A473c4aE84afaF873f632325","to":"0xFe01CC39f5Ae8553D6914DBb9dC27D219fa22D7f","gas":"0x5","gasPrice":"0x6","value":"0x123","data":""}`