Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: ETH RPC API: ETH Call should use the parent state root of the subsequent tipset #11905

Merged
merged 7 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 38 additions & 32 deletions chain/stmgr/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,17 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.
msg.Value = types.NewInt(0)
}

return sm.callInternal(ctx, msg, nil, ts, cid.Undef, sm.GetNetworkVersion, false, false)
return sm.callInternal(ctx, msg, nil, ts, cid.Undef, sm.GetNetworkVersion, false, false, false)
}

// ApplyOnStateWithGas applies the given message on top of the given state root with gas tracing enabled
func (sm *StateManager) ApplyOnStateWithGas(ctx context.Context, stateCid cid.Cid, msg *types.Message, ts *types.TipSet) (*api.InvocResult, error) {
return sm.callInternal(ctx, msg, nil, ts, stateCid, sm.GetNetworkVersion, true, false, true)
}

// CallWithGas calculates the state for a given tipset, and then applies the given message on top of that state.
func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, priorMsgs []types.ChainMsg, ts *types.TipSet, applyTsMessages bool) (*api.InvocResult, error) {
return sm.callInternal(ctx, msg, priorMsgs, ts, cid.Undef, sm.GetNetworkVersion, true, applyTsMessages)
return sm.callInternal(ctx, msg, priorMsgs, ts, cid.Undef, sm.GetNetworkVersion, true, applyTsMessages, false)
}

// CallAtStateAndVersion allows you to specify a message to execute on the given stateCid and network version.
Expand All @@ -64,14 +69,13 @@ func (sm *StateManager) CallAtStateAndVersion(ctx context.Context, msg *types.Me
nvGetter := func(context.Context, abi.ChainEpoch) network.Version {
return v
}

return sm.callInternal(ctx, msg, nil, nil, stateCid, nvGetter, true, false)
return sm.callInternal(ctx, msg, nil, nil, stateCid, nvGetter, true, false, false)
}

// - If no tipset is specified, the first tipset without an expensive migration or one in its parent is used.
// - If executing a message at a given tipset or its parent would trigger an expensive migration, the call will
// fail with ErrExpensiveFork.
func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, priorMsgs []types.ChainMsg, ts *types.TipSet, stateCid cid.Cid, nvGetter rand.NetworkVersionGetter, checkGas, applyTsMessages bool) (*api.InvocResult, error) {
func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, priorMsgs []types.ChainMsg, ts *types.TipSet, stateCid cid.Cid, nvGetter rand.NetworkVersionGetter, checkGas, applyTsMessages bool, noReExec bool) (*api.InvocResult, error) {
rvagg marked this conversation as resolved.
Show resolved Hide resolved
ctx, span := trace.StartSpan(ctx, "statemanager.callInternal")
defer span.End()

Expand Down Expand Up @@ -117,24 +121,6 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr
if stateCid == cid.Undef {
stateCid = ts.ParentState()
}
tsMsgs, err := sm.cs.MessagesForTipset(ctx, ts)
if err != nil {
return nil, xerrors.Errorf("failed to lookup messages for parent tipset: %w", err)
}

if applyTsMessages {
priorMsgs = append(tsMsgs, priorMsgs...)
} else {
var filteredTsMsgs []types.ChainMsg
for _, tsMsg := range tsMsgs {
//TODO we should technically be normalizing the filecoin address of from when we compare here
if tsMsg.VMMessage().From == msg.VMMessage().From {
filteredTsMsgs = append(filteredTsMsgs, tsMsg)
}
}
priorMsgs = append(filteredTsMsgs, priorMsgs...)
}

// Technically, the tipset we're passing in here should be ts+1, but that may not exist.
stateCid, err = sm.HandleStateForks(ctx, stateCid, ts.Height(), nil, ts)
if err != nil {
Expand Down Expand Up @@ -169,18 +155,38 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr
if err != nil {
return nil, xerrors.Errorf("failed to set up vm: %w", err)
}
for i, m := range priorMsgs {
_, err = vmi.ApplyMessage(ctx, m)

if !noReExec {
tsMsgs, err := sm.cs.MessagesForTipset(ctx, ts)
if err != nil {
return nil, xerrors.Errorf("applying prior message (%d, %s): %w", i, m.Cid(), err)
return nil, xerrors.Errorf("failed to lookup messages for parent tipset: %w", err)
}
}

// We flush to get the VM's view of the state tree after applying the above messages
// This is needed to get the correct nonce from the actor state to match the VM
stateCid, err = vmi.Flush(ctx)
if err != nil {
return nil, xerrors.Errorf("flushing vm: %w", err)
if applyTsMessages {
priorMsgs = append(tsMsgs, priorMsgs...)
} else {
var filteredTsMsgs []types.ChainMsg
for _, tsMsg := range tsMsgs {
//TODO we should technically be normalizing the filecoin address of from when we compare here
if tsMsg.VMMessage().From == msg.VMMessage().From {
filteredTsMsgs = append(filteredTsMsgs, tsMsg)
}
}
priorMsgs = append(filteredTsMsgs, priorMsgs...)
}
for i, m := range priorMsgs {
_, err = vmi.ApplyMessage(ctx, m)
if err != nil {
return nil, xerrors.Errorf("applying prior message (%d, %s): %w", i, m.Cid(), err)
}
}

// We flush to get the VM's view of the state tree after applying the above messages
// This is needed to get the correct nonce from the actor state to match the VM
stateCid, err = vmi.Flush(ctx)
if err != nil {
return nil, xerrors.Errorf("flushing vm: %w", err)
}
}

stTree, err := state.LoadStateTree(cbor.NewCborStore(buffStore), stateCid)
Expand Down
14 changes: 7 additions & 7 deletions node/impl/full/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -980,14 +980,14 @@ func (a *EthModule) applyMessage(ctx context.Context, msg *types.Message, tsk ty
return nil, xerrors.Errorf("cannot get tipset: %w", err)
}

applyTsMessages := true
if os.Getenv("LOTUS_SKIP_APPLY_TS_MESSAGE_CALL_WITH_GAS") == "1" {
applyTsMessages = false
}

// Try calling until we find a height with no migration.
for {
res, err = a.StateManager.CallWithGas(ctx, msg, []types.ChainMsg{}, ts, applyTsMessages)
st, _, err := a.StateManager.TipSetState(ctx, ts)
aarshkshah1992 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, xerrors.Errorf("cannot get tipset state: %w", err)
}

res, err = a.StateManager.ApplyOnStateWithGas(ctx, st, msg, ts)
if err != stmgr.ErrExpensiveFork {
break
}
Expand All @@ -997,7 +997,7 @@ func (a *EthModule) applyMessage(ctx context.Context, msg *types.Message, tsk ty
}
}
if err != nil {
return nil, xerrors.Errorf("CallWithGas failed: %w", err)
return nil, xerrors.Errorf("ApplyWithGasOnState failed: %w", err)
}
if res.MsgRct.ExitCode.IsError() {
reason := parseEthRevert(res.MsgRct.Return)
Expand Down