fix: gas: update ffi & correct the message inclusion cost in nv18 (#10228)
Co-authored-by: Raúl Kripalani <raul@protocol.ai>
This commit is contained in:
parent
54ed1f12ff
commit
80fccba93f
@ -213,6 +213,16 @@ var Prices = map[abi.ChainEpoch]Pricelist{
|
|||||||
|
|
||||||
verifyReplicaUpdate: 36316136,
|
verifyReplicaUpdate: 36316136,
|
||||||
},
|
},
|
||||||
|
build.UpgradeHyggeHeight: &pricelistV0{
|
||||||
|
computeGasMulti: 1,
|
||||||
|
storageGasMulti: 1300, // only applies to messages/return values.
|
||||||
|
|
||||||
|
onChainMessageComputeBase: 38863 + 475000, // includes the actor update cost
|
||||||
|
onChainMessageStorageBase: 36,
|
||||||
|
onChainMessageStoragePerByte: 1,
|
||||||
|
|
||||||
|
onChainReturnValuePerByte: 1,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// PricelistByEpoch finds the latest prices for the given epoch
|
// PricelistByEpoch finds the latest prices for the given epoch
|
||||||
|
2
extern/filecoin-ffi
vendored
2
extern/filecoin-ffi
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 32df7d1f15e46406c5efafb5de5c2c70ec34d685
|
Subproject commit 9e4fe75471f635a4ba91f17ee76f75200eb91de8
|
@ -58,7 +58,7 @@ func buildInputFromuint64(number uint64) []byte {
|
|||||||
// recursive delegate calls that fail due to gas limits are currently getting to 229 iterations
|
// recursive delegate calls that fail due to gas limits are currently getting to 229 iterations
|
||||||
// before running out of gas
|
// before running out of gas
|
||||||
func recursiveDelegatecallFail(ctx context.Context, t *testing.T, client *kit.TestFullNode, filename string, count uint64) {
|
func recursiveDelegatecallFail(ctx context.Context, t *testing.T, client *kit.TestFullNode, filename string, count uint64) {
|
||||||
expectedIterationsBeforeFailing := int(229)
|
expectedIterationsBeforeFailing := int(220)
|
||||||
fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, filename)
|
fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, filename)
|
||||||
t.Log("recursion count - ", count)
|
t.Log("recursion count - ", count)
|
||||||
inputData := buildInputFromuint64(count)
|
inputData := buildInputFromuint64(count)
|
||||||
@ -158,7 +158,7 @@ func TestFEVMRecursiveDelegatecallCount(t *testing.T) {
|
|||||||
ctx, cancel, client := kit.SetupFEVMTest(t)
|
ctx, cancel, client := kit.SetupFEVMTest(t)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
highestSuccessCount := uint64(235)
|
highestSuccessCount := uint64(225)
|
||||||
|
|
||||||
filename := "contracts/RecursiveDelegeatecall.hex"
|
filename := "contracts/RecursiveDelegeatecall.hex"
|
||||||
recursiveDelegatecallSuccess(ctx, t, client, filename, uint64(1))
|
recursiveDelegatecallSuccess(ctx, t, client, filename, uint64(1))
|
||||||
|
@ -2,16 +2,23 @@ package itests
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"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/big"
|
||||||
|
"github.com/filecoin-project/go-state-types/exitcode"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
|
"github.com/filecoin-project/lotus/build"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||||
"github.com/filecoin-project/lotus/chain/actors/builtin/account"
|
"github.com/filecoin-project/lotus/chain/actors/builtin/account"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/chain/vm"
|
||||||
"github.com/filecoin-project/lotus/itests/kit"
|
"github.com/filecoin-project/lotus/itests/kit"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,8 +44,9 @@ func TestEstimateGasNoFunds(t *testing.T) {
|
|||||||
sm, err := client.MpoolPushMessage(ctx, msg, nil)
|
sm, err := client.MpoolPushMessage(ctx, msg, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.StateWaitMsg(ctx, sm.Cid(), 3, api.LookbackNoLimit, true)
|
ret, err := client.StateWaitMsg(ctx, sm.Cid(), 3, api.LookbackNoLimit, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.True(t, ret.Receipt.ExitCode.IsSuccess())
|
||||||
|
|
||||||
// Make sure we can estimate gas even if we have no funds.
|
// Make sure we can estimate gas even if we have no funds.
|
||||||
msg2 := &types.Message{
|
msg2 := &types.Message{
|
||||||
@ -52,3 +60,110 @@ func TestEstimateGasNoFunds(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotZero(t, limit)
|
require.NotZero(t, limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure that we correctly calculate the inclusion cost. Especially, make sure the FVM and Lotus
|
||||||
|
// agree and that:
|
||||||
|
// 1. The FVM will never charge _less_ than the inclusion cost.
|
||||||
|
// 2. The FVM will never fine a storage provider for including a message that costs exactly the
|
||||||
|
// inclusion cost.
|
||||||
|
func TestEstimateInclusion(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
kit.QuietMiningLogs()
|
||||||
|
|
||||||
|
// We need this to be "correct" in this test so that lotus can get the correct gas value
|
||||||
|
// (which, unfortunately, looks at the height and not the current network version).
|
||||||
|
oldPrices := vm.Prices
|
||||||
|
vm.Prices = map[abi.ChainEpoch]vm.Pricelist{
|
||||||
|
0: oldPrices[build.UpgradeHyggeHeight],
|
||||||
|
}
|
||||||
|
t.Cleanup(func() { vm.Prices = oldPrices })
|
||||||
|
|
||||||
|
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs())
|
||||||
|
ens.InterconnectAll().BeginMining(10 * time.Millisecond)
|
||||||
|
|
||||||
|
// First, try sending a message that should have no fees beyond the inclusion cost. I.e., it
|
||||||
|
// does absolutely nothing:
|
||||||
|
msg := &types.Message{
|
||||||
|
From: client.DefaultKey.Address,
|
||||||
|
To: client.DefaultKey.Address,
|
||||||
|
Value: big.Zero(),
|
||||||
|
GasLimit: 0,
|
||||||
|
GasFeeCap: abi.NewTokenAmount(10000),
|
||||||
|
GasPremium: big.Zero(),
|
||||||
|
}
|
||||||
|
|
||||||
|
burntBefore, err := client.WalletBalance(ctx, builtin.BurntFundsActorAddr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
balanceBefore, err := client.WalletBalance(ctx, client.DefaultKey.Address)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Sign the message and compute the correct inclusion cost.
|
||||||
|
var smsg *types.SignedMessage
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
var err error
|
||||||
|
smsg, err = client.WalletSignMessage(ctx, client.DefaultKey.Address, msg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
estimatedGas := vm.PricelistByEpoch(math.MaxInt).OnChainMessage(smsg.ChainLength()).Total()
|
||||||
|
if estimatedGas == msg.GasLimit {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Try 10 times to get the right gas value.
|
||||||
|
require.Less(t, i, 10, "unable to estimate gas: %s != %s", estimatedGas, msg.GasLimit)
|
||||||
|
msg.GasLimit = estimatedGas
|
||||||
|
}
|
||||||
|
|
||||||
|
cid, err := client.MpoolPush(ctx, smsg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
ret, err := client.StateWaitMsg(ctx, cid, 3, api.LookbackNoLimit, true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, ret.Receipt.ExitCode.IsSuccess())
|
||||||
|
require.Equal(t, msg.GasLimit, ret.Receipt.GasUsed)
|
||||||
|
|
||||||
|
// Then try sending a message of the same size that tries to create an actor. This should
|
||||||
|
// get successfully included, but fail with out of gas:
|
||||||
|
|
||||||
|
// Mutate the last byte to get a new address of the same length.
|
||||||
|
toBytes := msg.To.Bytes()
|
||||||
|
toBytes[len(toBytes)-1] += 1 //nolint:golint
|
||||||
|
newAddr, err := address.NewFromBytes(toBytes)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
msg.Nonce = 1
|
||||||
|
msg.To = newAddr
|
||||||
|
smsg, err = client.WalletSignMessage(ctx, client.DefaultKey.Address, msg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
cid, err = client.MpoolPush(ctx, smsg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
ret, err = client.StateWaitMsg(ctx, cid, 3, api.LookbackNoLimit, true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, ret.Receipt.ExitCode, exitcode.SysErrOutOfGas)
|
||||||
|
require.Equal(t, msg.GasLimit, ret.Receipt.GasUsed)
|
||||||
|
|
||||||
|
// Now make sure that the client is the only contributor to the burnt funds actor (the
|
||||||
|
// miners should not have been fined for either message).
|
||||||
|
|
||||||
|
burntAfter, err := client.WalletBalance(ctx, builtin.BurntFundsActorAddr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
balanceAfter, err := client.WalletBalance(ctx, client.DefaultKey.Address)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
burnt := big.Sub(burntAfter, burntBefore)
|
||||||
|
spent := big.Sub(balanceBefore, balanceAfter)
|
||||||
|
|
||||||
|
require.Equal(t, burnt, spent)
|
||||||
|
|
||||||
|
// Finally, try to submit a message with too little gas. This should fail.
|
||||||
|
|
||||||
|
msg.Nonce = 2
|
||||||
|
msg.To = msg.From
|
||||||
|
msg.GasLimit -= 1 //nolint:golint
|
||||||
|
|
||||||
|
smsg, err = client.WalletSignMessage(ctx, client.DefaultKey.Address, msg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = client.MpoolPush(ctx, smsg)
|
||||||
|
require.ErrorContains(t, err, "will not be included in a block")
|
||||||
|
require.ErrorContains(t, err, "cannot be less than the cost of storing a message")
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user