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

feat: compute a better gas limit for recursive external contract calls #5696

Merged
merged 3 commits into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion app/submodule/chain/account_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ func (accountAPI *accountAPI) StateAccountKey(ctx context.Context, addr address.
if err != nil {
return address.Undef, fmt.Errorf("loading tipset %s: %w", tsk, err)
}
return accountAPI.chain.Stmgr.ResolveToKeyAddress(ctx, addr, ts)
return accountAPI.chain.Stmgr.ResolveToDeterministicAddress(ctx, addr, ts)
}
2 changes: 1 addition & 1 deletion app/submodule/chain/chaininfo_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ func (cia *chainInfoAPI) ResolveToKeyAddr(ctx context.Context, addr address.Addr
if ts == nil {
ts = cia.chain.ChainReader.GetHead()
}
return cia.chain.Stmgr.ResolveToKeyAddress(ctx, addr, ts)
return cia.chain.Stmgr.ResolveToDeterministicAddress(ctx, addr, ts)
}

// ************Drand****************//
Expand Down
133 changes: 131 additions & 2 deletions app/submodule/eth/eth_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ 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/builtin/v10/evm"
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/venus/pkg/chain"
"github.com/filecoin-project/venus/pkg/constants"
"github.com/filecoin-project/venus/pkg/crypto"
"github.com/filecoin-project/venus/pkg/ethhashlookup"
"github.com/filecoin-project/venus/pkg/events"
"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"
"github.com/filecoin-project/venus/pkg/vm/vmcontext"
"github.com/filecoin-project/venus/venus-shared/actors"
Expand Down Expand Up @@ -751,12 +754,138 @@ func (a *ethAPI) EthEstimateGas(ctx context.Context, tx types.EthCall) (types.Et
// gas estimation actually run.
msg.GasLimit = 0

msg, err = a.mpool.GasEstimateMessageGas(ctx, msg, nil, types.EmptyTSK)
ts, err := a.chain.ChainHead(ctx)
if err != nil {
return types.EthUint64(0), err
}
msg, err = a.mpool.GasEstimateMessageGas(ctx, msg, nil, ts.Key())
if err != nil {
return types.EthUint64(0), fmt.Errorf("failed to estimate gas: %w", err)
}

expectedGas, err := ethGasSearch(ctx, a.em.chainModule.Stmgr, a.em.mpoolModule.MPool, msg, ts)
if err != nil {
log.Errorw("expected gas", "err", err)
}

return types.EthUint64(expectedGas), nil
}

// gasSearch does an exponential search to find a gas value to execute the
// message with. It first finds a high gas limit that allows the message to execute
// by doubling the previous gas limit until it succeeds then does a binary
// search till it gets within a range of 1%
func gasSearch(
ctx context.Context,
smgr *statemanger.Stmgr,
msgIn *types.Message,
priorMsgs []types.ChainMsg,
ts *types.TipSet,
) (int64, error) {
msg := *msgIn

high := msg.GasLimit
low := msg.GasLimit

canSucceed := func(limit int64) (bool, error) {
msg.GasLimit = limit

res, err := smgr.CallWithGas(ctx, &msg, priorMsgs, ts)
if err != nil {
return false, fmt.Errorf("CallWithGas failed: %w", err)
}

if res.Receipt.ExitCode.IsSuccess() {
return true, nil
}

return false, nil
}

for {
ok, err := canSucceed(high)
if err != nil {
return -1, fmt.Errorf("searching for high gas limit failed: %w", err)
}
if ok {
break
}

low = high
high = high * 2

if high > constants.BlockGasLimit {
high = constants.BlockGasLimit
break
}
}

checkThreshold := high / 100
for (high - low) > checkThreshold {
median := (low + high) / 2
ok, err := canSucceed(median)
if err != nil {
return -1, fmt.Errorf("searching for optimal gas limit failed: %w", err)
}

if ok {
high = median
} else {
low = median
}

checkThreshold = median / 100
}

return high, nil
}

func traceContainsExitCode(et types.ExecutionTrace, ex exitcode.ExitCode) bool {
if et.MsgRct.ExitCode == ex {
return true
}

for _, et := range et.Subcalls {
if traceContainsExitCode(et, ex) {
return true
}
}

return false
}

// ethGasSearch executes a message for gas estimation using the previously estimated gas.
// If the message fails due to an out of gas error then a gas search is performed.
// See gasSearch.
func ethGasSearch(
ctx context.Context,
stmgr *statemanger.Stmgr,
mpool *messagepool.MessagePool,
msgIn *types.Message,
ts *types.TipSet,
) (int64, error) {
msg := *msgIn
currTS := ts

res, priorMsgs, ts, err := mpool.GasEstimateCallWithGas(ctx, &msg, currTS)
if err != nil {
return -1, fmt.Errorf("gas estimation failed: %w", err)
}

if res.MsgRct.ExitCode.IsSuccess() {
return msg.GasLimit, nil
}
if traceContainsExitCode(res.ExecutionTrace, exitcode.SysErrOutOfGas) {
ret, err := gasSearch(ctx, stmgr, &msg, priorMsgs, ts)
if err != nil {
return -1, fmt.Errorf("gas estimation search failed: %w", err)
}

ret = int64(float64(ret) * mpool.GetConfig().GasLimitOverestimation)
return ret, nil
}

return types.EthUint64(msg.GasLimit), nil
return -1, fmt.Errorf("message execution failed: exit %s, reason: %s", res.MsgRct.ExitCode, res.Error)
}

func (a *ethAPI) EthCall(ctx context.Context, tx types.EthCall, blkParam string) (types.EthBytes, error) {
Expand Down
2 changes: 1 addition & 1 deletion app/submodule/mining/mining_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (miningAPI *MiningAPI) MinerGetBaseInfo(ctx context.Context, maddr address.
if err != nil {
return nil, fmt.Errorf("failed to load latest state: %v", err)
}
worker, err := st.ResolveToKeyAddr(ctx, info.Worker)
worker, err := st.ResolveToDeterministicAddress(ctx, info.Worker)
if err != nil {
return nil, fmt.Errorf("resolving worker address: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion app/submodule/wallet/wallet_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func (walletAPI *WalletAPI) WalletDelete(ctx context.Context, addr address.Addre

// WalletSign signs the given bytes using the given address.
func (walletAPI *WalletAPI) WalletSign(ctx context.Context, k address.Address, msg []byte, meta types.MsgMeta) (*crypto.Signature, error) {
keyAddr, err := walletAPI.walletModule.Chain.Stmgr.ResolveToKeyAddress(ctx, k, nil)
keyAddr, err := walletAPI.walletModule.Chain.Stmgr.ResolveToDeterministicAddress(ctx, k, nil)
if err != nil {
return nil, fmt.Errorf("ResolveTokeyAddress failed:%v", err)
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/chain/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -1374,15 +1374,15 @@ func (store *Store) LookupID(ctx context.Context, ts *types.TipSet, addr address
return st.LookupID(addr)
}

// ResolveToKeyAddr get key address of specify address.
// ResolveToDeterministicAddress get key address of specify address.
// if ths addr is bls/secpk address, return directly, other get the pubkey and generate address
func (store *Store) ResolveToKeyAddr(ctx context.Context, ts *types.TipSet, addr address.Address) (address.Address, error) {
func (store *Store) ResolveToDeterministicAddress(ctx context.Context, ts *types.TipSet, addr address.Address) (address.Address, error) {
st, err := store.StateView(ctx, ts)
if err != nil {
return address.Undef, errors.Wrap(err, "failed to load latest state")
}

return st.ResolveToKeyAddr(ctx, addr)
return st.ResolveToDeterministicAddress(ctx, addr)
}

// StateView return state view at ts epoch
Expand Down
2 changes: 1 addition & 1 deletion pkg/consensus/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,7 @@ func (bv *BlockValidator) checkBlockMessages(ctx context.Context,

// Verify that all secp message signatures are correct
for i, msg := range blksecpMsgs {
signer, err := stateView.ResolveToKeyAddr(ctx, msg.Message.From)
signer, err := stateView.ResolveToDeterministicAddress(ctx, msg.Message.From)
if err != nil {
return errors.Wrapf(err, "failed to load signer address for %v", signer)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/consensusfault/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
)

type FaultStateView interface {
ResolveToKeyAddr(ctx context.Context, address address.Address) (address.Address, error)
ResolveToDeterministicAddress(ctx context.Context, address address.Address) (address.Address, error)
MinerInfo(ctx context.Context, maddr address.Address, nv network.Version) (*miner.MinerInfo, error)
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/fvm/fvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ func (x *FvmExtern) workerKeyAtLookback(ctx context.Context, minerID address.Add
if err != nil {
return address.Undef, 0, err
}
raddr, err := vm.ResolveToKeyAddr(ctx, st, info.Worker, cstWithGas)
raddr, err := vm.ResolveToDeterministicAddress(ctx, st, info.Worker, cstWithGas)
if err != nil {
return address.Undef, 0, err
}
Expand Down
54 changes: 52 additions & 2 deletions pkg/messagepool/gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/filecoin-project/venus/pkg/constants"
"github.com/filecoin-project/venus/pkg/fork"
"github.com/filecoin-project/venus/pkg/vm"
"github.com/filecoin-project/venus/pkg/vm/vmcontext"
"github.com/filecoin-project/venus/venus-shared/actors/builtin"
"github.com/filecoin-project/venus/venus-shared/types"
)
Expand Down Expand Up @@ -202,7 +203,7 @@ func (mp *MessagePool) GasEstimateGasLimit(ctx context.Context, msgIn *types.Mes
msg.GasFeeCap = big.NewInt(int64(constants.MinimumBaseFee) + 1)
msg.GasPremium = big.NewInt(1)

fromA, err := mp.sm.ResolveToKeyAddress(ctx, msgIn.From, currTS)
fromA, err := mp.sm.ResolveToDeterministicAddress(ctx, msgIn.From, currTS)
if err != nil {
return -1, fmt.Errorf("getting key address: %w", err)
}
Expand All @@ -219,6 +220,55 @@ func (mp *MessagePool) GasEstimateGasLimit(ctx context.Context, msgIn *types.Mes
return mp.evalMessageGasLimit(ctx, msgIn, priorMsgs, ts)
}

// GasEstimateCallWithGas invokes a message "msgIn" on the earliest available tipset with pending
// messages in the message pool. The function returns the result of the message invocation, the
// pending messages, the tipset used for the invocation, and an error if occurred.
// The returned information can be used to make subsequent calls to CallWithGas with the same parameters.
func (mp *MessagePool) GasEstimateCallWithGas(
ctx context.Context,
msgIn *types.Message,
currTS *types.TipSet,
) (*types.InvocResult, []types.ChainMsg, *types.TipSet, error) {
msg := *msgIn
fromA, err := mp.sm.ResolveToDeterministicAddress(ctx, msgIn.From, currTS)
if err != nil {
return nil, []types.ChainMsg{}, nil, fmt.Errorf("getting key address: %w", err)
}

pending, ts := mp.PendingFor(ctx, fromA)
priorMsgs := make([]types.ChainMsg, 0, len(pending))
for _, m := range pending {
if m.Message.Nonce == msg.Nonce {
break
}
priorMsgs = append(priorMsgs, m)
}

// Try calling until we find a height with no migration.
var res *vmcontext.Ret
for {
res, err = mp.sm.CallWithGas(ctx, &msg, priorMsgs, ts)
if err != fork.ErrExpensiveFork {
break
}
ts, err = mp.api.ChainTipSet(ctx, ts.Parents())
if err != nil {
return nil, []types.ChainMsg{}, nil, fmt.Errorf("getting parent tipset: %w", err)
}
}
if err != nil {
return nil, []types.ChainMsg{}, nil, fmt.Errorf("CallWithGas failed: %w", err)
}

return &types.InvocResult{
MsgCid: msg.Cid(),
Msg: &msg,
MsgRct: &res.Receipt,
ExecutionTrace: res.GasTracker.ExecutionTrace,
Duration: res.Duration,
}, priorMsgs, ts, nil
}

func (mp *MessagePool) evalMessageGasLimit(ctx context.Context, msgIn *types.Message, priorMsgs []types.ChainMsg, ts *types.TipSet) (int64, error) {
msg := *msgIn
msg.GasLimit = constants.BlockGasLimit
Expand Down Expand Up @@ -364,7 +414,7 @@ func (mp *MessagePool) GasBatchEstimateMessageGas(ctx context.Context, estimateM
return nil, fmt.Errorf("getting tipset: %w", err)
}

fromA, err := mp.sm.ResolveToKeyAddress(ctx, estimateMessages[0].Msg.From, currTS)
fromA, err := mp.sm.ResolveToDeterministicAddress(ctx, estimateMessages[0].Msg.From, currTS)
if err != nil {
return nil, fmt.Errorf("getting key address: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/messagepool/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,15 @@ func (mpp *mpoolProvider) StateAccountKeyAtFinality(ctx context.Context, addr ad
return address.Undef, fmt.Errorf("failed to load lookback tipset: %w", err)
}
}
return mpp.stmgr.ResolveToKeyAddress(ctx, addr, ts)
return mpp.stmgr.ResolveToDeterministicAddress(ctx, addr, ts)
}

func (mpp *mpoolProvider) StateNetworkVersion(ctx context.Context, height abi.ChainEpoch) network.Version {
return mpp.stmgr.GetNetworkVersion(ctx, height)
}

func (mpp *mpoolProvider) StateAccountKey(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
return mpp.stmgr.ResolveToKeyAddress(ctx, addr, ts)
return mpp.stmgr.ResolveToDeterministicAddress(ctx, addr, ts)
}

func (mpp *mpoolProvider) MessagesForBlock(ctx context.Context, h *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) {
Expand Down
7 changes: 4 additions & 3 deletions pkg/migration/migrate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"
"path/filepath"
"testing"
"time"

"github.com/filecoin-project/venus/fixtures/networks"
"github.com/filecoin-project/venus/pkg/config"
Expand All @@ -30,9 +31,8 @@ func TestMigration(t *testing.T) {
for nt, paramsCfg := range cfgs {
cfg := config.NewDefaultConfig()
cfg.NetworkParams.NetworkType = nt
repoPath := t.TempDir()
assert.Nil(t, os.RemoveAll(repoPath))
t.Log(repoPath)
repoPath := filepath.Join(os.TempDir(), fmt.Sprintf("TestMigration%d", time.Now().UnixNano()))

assert.Nil(t, repo.InitFSRepo(repoPath, 0, cfg))

assert.Nil(t, TryToMigrate(repoPath))
Expand All @@ -41,6 +41,7 @@ func TestMigration(t *testing.T) {
newCfg := fsRepo.Config()
assert.Equal(t, paramsCfg.NetworkType, newCfg.NetworkParams.NetworkType)
assert.EqualValuesf(t, config.NewDefaultConfig().NetworkParams.ForkUpgradeParam, newCfg.NetworkParams.ForkUpgradeParam, fmt.Sprintf("current network type %d", paramsCfg.NetworkType))
assert.NoError(t, fsRepo.Close())

cfgTmp, err := config.ReadFile(filepath.Join(repoPath, "config.json"))
assert.NoError(t, err)
Expand Down
2 changes: 1 addition & 1 deletion pkg/paychmgr/mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (sm *mockStateManager) setPaychState(a address.Address, actor *types.Actor,
sm.paychState[a] = mockPchState{actor, state}
}

func (sm *mockStateManager) ResolveToKeyAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
func (sm *mockStateManager) ResolveToDeterministicAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
sm.lk.Lock()
defer sm.lk.Unlock()
keyAddr, ok := sm.accountState[addr]
Expand Down
2 changes: 1 addition & 1 deletion pkg/paychmgr/paych.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch add
return nil, err
}

from, err := ca.api.ResolveToKeyAddress(ctx, f, nil)
from, err := ca.api.ResolveToDeterministicAddress(ctx, f, nil)
if err != nil {
return nil, err
}
Expand Down
Loading