Add gas estimation
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
This commit is contained in:
parent
e1c9c4297b
commit
310fa67f9d
@ -101,6 +101,16 @@ type FullNode interface {
|
||||
// ChainExport returns a stream of bytes with CAR dump of chain data.
|
||||
ChainExport(context.Context, types.TipSetKey) (<-chan []byte, error)
|
||||
|
||||
// GasEstimateGasLimit estimates gas used by the message and returns it.
|
||||
// It fails if message fails to execute.
|
||||
GasEstimateGasLimit(context.Context, *types.Message, types.TipSetKey) (int64, error)
|
||||
|
||||
// GasEstimateGasPrice estimates what gas price should be used for a
|
||||
// message to have high likelihood of inclusion in `nblocksincl` epochs.
|
||||
|
||||
GasEstimateGasPrice(_ context.Context, nblocksincl uint64,
|
||||
sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error)
|
||||
|
||||
// MethodGroup: Sync
|
||||
// The Sync method group contains methods for interacting with and
|
||||
// observing the lotus sync service.
|
||||
@ -143,8 +153,8 @@ type FullNode interface {
|
||||
MpoolGetNonce(context.Context, address.Address) (uint64, error)
|
||||
MpoolSub(context.Context) (<-chan MpoolUpdate, error)
|
||||
|
||||
// MpoolEstimateGasPrice estimates what gas price should be used for a
|
||||
// message to have high likelihood of inclusion in `nblocksincl` epochs.
|
||||
// MpoolEstimateGasPrice is depracated
|
||||
// Deprecated: use GasEstimateGasPrice instead
|
||||
MpoolEstimateGasPrice(ctx context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error)
|
||||
|
||||
// MethodGroup: Miner
|
||||
|
@ -82,18 +82,20 @@ type FullNodeStruct struct {
|
||||
ChainGetPath func(context.Context, types.TipSetKey, types.TipSetKey) ([]*api.HeadChange, error) `perm:"read"`
|
||||
ChainExport func(context.Context, types.TipSetKey) (<-chan []byte, error) `perm:"read"`
|
||||
|
||||
GasEstimateGasPrice func(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
GasEstimateGasLimit func(context.Context, *types.Message, types.TipSetKey) (int64, error) `perm:"read"`
|
||||
|
||||
SyncState func(context.Context) (*api.SyncState, error) `perm:"read"`
|
||||
SyncSubmitBlock func(ctx context.Context, blk *types.BlockMsg) error `perm:"write"`
|
||||
SyncIncomingBlocks func(ctx context.Context) (<-chan *types.BlockHeader, error) `perm:"read"`
|
||||
SyncMarkBad func(ctx context.Context, bcid cid.Cid) error `perm:"admin"`
|
||||
SyncCheckBad func(ctx context.Context, bcid cid.Cid) (string, error) `perm:"read"`
|
||||
|
||||
MpoolPending func(context.Context, types.TipSetKey) ([]*types.SignedMessage, error) `perm:"read"`
|
||||
MpoolPush func(context.Context, *types.SignedMessage) (cid.Cid, error) `perm:"write"`
|
||||
MpoolPushMessage func(context.Context, *types.Message) (*types.SignedMessage, error) `perm:"sign"`
|
||||
MpoolGetNonce func(context.Context, address.Address) (uint64, error) `perm:"read"`
|
||||
MpoolSub func(context.Context) (<-chan api.MpoolUpdate, error) `perm:"read"`
|
||||
MpoolEstimateGasPrice func(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
MpoolPending func(context.Context, types.TipSetKey) ([]*types.SignedMessage, error) `perm:"read"`
|
||||
MpoolPush func(context.Context, *types.SignedMessage) (cid.Cid, error) `perm:"write"`
|
||||
MpoolPushMessage func(context.Context, *types.Message) (*types.SignedMessage, error) `perm:"sign"`
|
||||
MpoolGetNonce func(context.Context, address.Address) (uint64, error) `perm:"read"`
|
||||
MpoolSub func(context.Context) (<-chan api.MpoolUpdate, error) `perm:"read"`
|
||||
|
||||
MinerGetBaseInfo func(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*api.MiningBaseInfo, error) `perm:"read"`
|
||||
MinerCreateBlock func(context.Context, *api.BlockTemplate) (*types.BlockMsg, error) `perm:"write"`
|
||||
@ -400,6 +402,16 @@ func (c *FullNodeStruct) ClientGenCar(ctx context.Context, ref api.FileRef, outp
|
||||
return c.Internal.ClientGenCar(ctx, ref, outpath)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) GasEstimateGasPrice(ctx context.Context, nblocksincl uint64,
|
||||
sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
return c.Internal.GasEstimateGasPrice(ctx, nblocksincl, sender, gaslimit, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) GasEstimateGasLimit(ctx context.Context, msg *types.Message,
|
||||
tsk types.TipSetKey) (int64, error) {
|
||||
return c.Internal.GasEstimateGasLimit(ctx, msg, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MpoolPending(ctx context.Context, tsk types.TipSetKey) ([]*types.SignedMessage, error) {
|
||||
return c.Internal.MpoolPending(ctx, tsk)
|
||||
}
|
||||
@ -417,7 +429,7 @@ func (c *FullNodeStruct) MpoolSub(ctx context.Context) (<-chan api.MpoolUpdate,
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MpoolEstimateGasPrice(ctx context.Context, nblocksincl uint64, sender address.Address, limit int64, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
return c.Internal.MpoolEstimateGasPrice(ctx, nblocksincl, sender, limit, tsk)
|
||||
return c.Internal.GasEstimateGasPrice(ctx, nblocksincl, sender, limit, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MinerGetBaseInfo(ctx context.Context, maddr address.Address, epoch abi.ChainEpoch, tsk types.TipSetKey) (*api.MiningBaseInfo, error) {
|
||||
|
@ -53,7 +53,7 @@ func (ve Version) EqMajorMinor(v2 Version) bool {
|
||||
}
|
||||
|
||||
// APIVersion is a semver version of the rpc api exposed
|
||||
var APIVersion Version = newVer(0, 8, 0)
|
||||
var APIVersion Version = newVer(0, 8, 1)
|
||||
|
||||
//nolint:varcheck,deadcode
|
||||
const (
|
||||
|
@ -875,6 +875,7 @@ func (mp *MessagePool) loadLocal() error {
|
||||
|
||||
const MinGasPrice = 0
|
||||
|
||||
//TODO: remove replaced by Gas module
|
||||
func (mp *MessagePool) EstimateGasPrice(ctx context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
// TODO: something smarter obviously
|
||||
switch nblocksincl {
|
||||
|
@ -4,7 +4,9 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/ipfs/go-cid"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
@ -84,6 +86,73 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.
|
||||
return sm.CallRaw(ctx, msg, state, r, ts.Height())
|
||||
}
|
||||
|
||||
func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.InvocResult, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "statemanager.CallWithGas")
|
||||
defer span.End()
|
||||
|
||||
if ts == nil {
|
||||
ts = sm.cs.GetHeaviestTipSet()
|
||||
}
|
||||
|
||||
state := ts.ParentState()
|
||||
|
||||
r := store.NewChainRand(sm.cs, ts.Cids(), ts.Height())
|
||||
|
||||
if span.IsRecordingEvents() {
|
||||
span.AddAttributes(
|
||||
trace.Int64Attribute("gas_limit", msg.GasLimit),
|
||||
trace.Int64Attribute("gas_price", int64(msg.GasPrice.Uint64())),
|
||||
trace.StringAttribute("value", msg.Value.String()),
|
||||
)
|
||||
}
|
||||
|
||||
fromKey, err := sm.ResolveToKeyAddress(ctx, msg.From, ts)
|
||||
var msgApply types.ChainMsg
|
||||
switch fromKey.Protocol() {
|
||||
case address.BLS:
|
||||
msgApply = msg
|
||||
case address.SECP256K1:
|
||||
msgApply = &types.SignedMessage{
|
||||
Message: *msg,
|
||||
Signature: crypto.Signature{
|
||||
Type: crypto.SigTypeSecp256k1,
|
||||
Data: make([]byte, 65),
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
vmi, err := vm.NewVM(state, ts.Height(), r, sm.cs.Blockstore(), sm.cs.VMSys())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to set up vm: %w", err)
|
||||
}
|
||||
|
||||
fromActor, err := vmi.StateTree().GetActor(msg.From)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("call raw get actor: %s", err)
|
||||
}
|
||||
|
||||
msg.Nonce = fromActor.Nonce
|
||||
|
||||
ret, err := vmi.ApplyMessage(ctx, msgApply)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("apply message failed: %w", err)
|
||||
}
|
||||
|
||||
var errs string
|
||||
if ret.ActorErr != nil {
|
||||
errs = ret.ActorErr.Error()
|
||||
}
|
||||
|
||||
return &api.InvocResult{
|
||||
Msg: msg,
|
||||
MsgRct: &ret.MessageReceipt,
|
||||
ExecutionTrace: ret.ExecutionTrace,
|
||||
Error: errs,
|
||||
Duration: ret.Duration,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var errHaltExecution = fmt.Errorf("halt")
|
||||
|
||||
func (sm *StateManager) Replay(ctx context.Context, ts *types.TipSet, mcid cid.Cid) (*types.Message, *vm.ApplyRet, error) {
|
||||
|
@ -77,7 +77,6 @@ var sendCmd = &cli.Command{
|
||||
From: fromAddr,
|
||||
To: toAddr,
|
||||
Value: types.BigInt(val),
|
||||
GasLimit: 100_000_000,
|
||||
GasPrice: gp,
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,13 @@
|
||||
package impl
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/node/impl/client"
|
||||
"github.com/filecoin-project/lotus/node/impl/common"
|
||||
"github.com/filecoin-project/lotus/node/impl/full"
|
||||
@ -18,6 +22,7 @@ type FullNodeAPI struct {
|
||||
full.ChainAPI
|
||||
client.API
|
||||
full.MpoolAPI
|
||||
full.GasAPI
|
||||
market.MarketAPI
|
||||
paych.PaychAPI
|
||||
full.StateAPI
|
||||
@ -26,4 +31,10 @@ type FullNodeAPI struct {
|
||||
full.SyncAPI
|
||||
}
|
||||
|
||||
// MpoolEstimateGasPrice estimates gas price
|
||||
// Deprecated: used GasEstimateGasPrice instead
|
||||
func (fa *FullNodeAPI) MpoolEstimateGasPrice(ctx context.Context, nblocksincl uint64, sender address.Address, limit int64, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
return fa.GasEstimateGasPrice(ctx, nblocksincl, sender, limit, tsk)
|
||||
}
|
||||
|
||||
var _ api.FullNode = &FullNodeAPI{}
|
||||
|
55
node/impl/full/gas.go
Normal file
55
node/impl/full/gas.go
Normal file
@ -0,0 +1,55 @@
|
||||
package full
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
|
||||
"go.uber.org/fx"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type GasAPI struct {
|
||||
fx.In
|
||||
Stmgr *stmgr.StateManager
|
||||
Cs *store.ChainStore
|
||||
}
|
||||
|
||||
const MinGasPrice = 1
|
||||
|
||||
func (a *GasAPI) GasEstimateGasPrice(ctx context.Context, nblocksincl uint64,
|
||||
sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
|
||||
// TODO: something smarter obviously
|
||||
switch nblocksincl {
|
||||
case 0:
|
||||
return types.NewInt(MinGasPrice + 2), nil
|
||||
case 1:
|
||||
return types.NewInt(MinGasPrice + 1), nil
|
||||
default:
|
||||
return types.NewInt(MinGasPrice), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *GasAPI) GasEstimateGasLimit(ctx context.Context, msgIn *types.Message,
|
||||
tsk types.TipSetKey) (int64, error) {
|
||||
|
||||
msg := &(*msgIn)
|
||||
msg.GasLimit = build.BlockGasLimit
|
||||
|
||||
ts, err := a.Cs.GetTipSetFromKey(tsk)
|
||||
if err != nil {
|
||||
return -1, xerrors.Errorf("could not get tipset: %w", err)
|
||||
}
|
||||
|
||||
res, err := a.Stmgr.CallWithGas(ctx, msg, ts)
|
||||
if res.MsgRct.ExitCode != exitcode.Ok {
|
||||
return -1, xerrors.Errorf("could not apply message: exit %s, reason: %s", res.MsgRct.ExitCode, res.Error)
|
||||
}
|
||||
|
||||
return res.MsgRct.GasUsed, nil
|
||||
}
|
@ -18,6 +18,7 @@ type MpoolAPI struct {
|
||||
fx.In
|
||||
|
||||
WalletAPI
|
||||
GasAPI
|
||||
|
||||
Chain *store.ChainStore
|
||||
|
||||
@ -86,15 +87,26 @@ func (a *MpoolAPI) MpoolPush(ctx context.Context, smsg *types.SignedMessage) (ci
|
||||
return a.Mpool.Push(smsg)
|
||||
}
|
||||
|
||||
// GasMargin sets by how much should gas limit be increased over test execution
|
||||
var GasMargin = 1.2
|
||||
|
||||
func (a *MpoolAPI) MpoolPushMessage(ctx context.Context, msg *types.Message) (*types.SignedMessage, error) {
|
||||
if msg.Nonce != 0 {
|
||||
return nil, xerrors.Errorf("MpoolPushMessage expects message nonce to be 0, was %d", msg.Nonce)
|
||||
}
|
||||
if msg.GasLimit == 0 {
|
||||
msg.GasLimit = 100_000_000 // TODO: gas limit estimation
|
||||
gasLimit, err := a.GasEstimateGasLimit(ctx, msg, types.TipSetKey{})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("estimating gas limit: %w", err)
|
||||
}
|
||||
msg.GasLimit = int64(float64(gasLimit) * GasMargin)
|
||||
}
|
||||
if types.BigCmp(msg.GasPrice, types.NewInt(0)) == 0 {
|
||||
msg.GasPrice = types.NewInt(1) // TODO: gas price estimation
|
||||
gasPrice, err := a.GasEstimateGasPrice(ctx, 2, msg.From, msg.GasLimit, types.TipSetKey{})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("estimating gas price: %w", err)
|
||||
}
|
||||
msg.GasPrice = gasPrice
|
||||
}
|
||||
|
||||
return a.Mpool.PushWithNonce(ctx, msg.From, func(from address.Address, nonce uint64) (*types.SignedMessage, error) {
|
||||
@ -124,7 +136,3 @@ func (a *MpoolAPI) MpoolGetNonce(ctx context.Context, addr address.Address) (uin
|
||||
func (a *MpoolAPI) MpoolSub(ctx context.Context) (<-chan api.MpoolUpdate, error) {
|
||||
return a.Mpool.Updates(ctx)
|
||||
}
|
||||
|
||||
func (a *MpoolAPI) MpoolEstimateGasPrice(ctx context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
return a.Mpool.EstimateGasPrice(ctx, nblocksincl, sender, gaslimit, tsk)
|
||||
}
|
||||
|
@ -7,4 +7,4 @@ export TRUST_PARAMS=1
|
||||
tag=${TAG:-debug}
|
||||
|
||||
go run -tags=$tag ./cmd/lotus wallet import ~/.genesis-sectors/pre-seal-t01000.key
|
||||
go run -tags=$tag ./cmd/lotus-miner init --actor=t01000 --genesis-miner --pre-sealed-sectors=~/.genesis-sectors --pre-sealed-metadata=~/.genesis-sectors/pre-seal-t01000.json
|
||||
go run -tags=$tag ./cmd/lotus-storage-miner init --actor=t01000 --genesis-miner --pre-sealed-sectors=~/.genesis-sectors --pre-sealed-metadata=~/.genesis-sectors/pre-seal-t01000.json
|
||||
|
Loading…
Reference in New Issue
Block a user