diff --git a/api/api_full.go b/api/api_full.go index 6e9b999de16..a6a5f79e8fa 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -115,14 +115,17 @@ type FullNode interface { // becomes available BeaconGetEntry(ctx context.Context, epoch abi.ChainEpoch) (*types.BeaconEntry, error) + // GasEstimateFeeCap estimates gas fee cap + GasEstimateFeeCap(context.Context, int64, types.TipSetKey) (types.BigInt, 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 + // GasEsitmateGasPremium estimates what gas price should be used for a // message to have high likelihood of inclusion in `nblocksincl` epochs. - GasEstimateGasPrice(_ context.Context, nblocksincl uint64, + GasEsitmateGasPremium(_ context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) // MethodGroup: Sync @@ -170,10 +173,6 @@ type FullNode interface { MpoolGetNonce(context.Context, address.Address) (uint64, error) MpoolSub(context.Context) (<-chan MpoolUpdate, error) - // 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 MinerGetBaseInfo(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*MiningBaseInfo, error) diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 561783abcb2..a780c87d416 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -87,8 +87,9 @@ type FullNodeStruct struct { BeaconGetEntry func(ctx context.Context, epoch abi.ChainEpoch) (*types.BeaconEntry, 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"` + GasEsitmateGasPremium 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"` + GasEstimateFeeCap func(context.Context, int64, types.TipSetKey) (types.BigInt, error) `perm:"read"` SyncState func(context.Context) (*api.SyncState, error) `perm:"read"` SyncSubmitBlock func(ctx context.Context, blk *types.BlockMsg) error `perm:"write"` @@ -430,9 +431,13 @@ func (c *FullNodeStruct) ClientDealSize(ctx context.Context, root cid.Cid) (api. return c.Internal.ClientDealSize(ctx, root) } -func (c *FullNodeStruct) GasEstimateGasPrice(ctx context.Context, nblocksincl uint64, +func (c *FullNodeStruct) GasEsitmateGasPremium(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) + return c.Internal.GasEsitmateGasPremium(ctx, nblocksincl, sender, gaslimit, tsk) +} +func (c *FullNodeStruct) GasEstimateFeeCap(ctx context.Context, maxqueueblks int64, + tsk types.TipSetKey) (types.BigInt, error) { + return c.Internal.GasEstimateFeeCap(ctx, maxqueueblks, tsk) } func (c *FullNodeStruct) GasEstimateGasLimit(ctx context.Context, msg *types.Message, @@ -460,10 +465,6 @@ func (c *FullNodeStruct) MpoolSub(ctx context.Context) (<-chan api.MpoolUpdate, return c.Internal.MpoolSub(ctx) } -func (c *FullNodeStruct) MpoolEstimateGasPrice(ctx context.Context, nblocksincl uint64, sender address.Address, limit int64, tsk types.TipSetKey) (types.BigInt, error) { - 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) { return c.Internal.MinerGetBaseInfo(ctx, maddr, epoch, tsk) } diff --git a/api/test/paych.go b/api/test/paych.go index 81348ecfa05..2892b2334c8 100644 --- a/api/test/paych.go +++ b/api/test/paych.go @@ -60,7 +60,7 @@ func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) { t.Fatal(err) } - sendFunds(ctx, t, paymentCreator, receiverAddr, abi.NewTokenAmount(1e10)) + sendFunds(ctx, t, paymentCreator, receiverAddr, abi.NewTokenAmount(1e18)) // setup the payment channel createrAddr, err := paymentCreator.WalletDefaultAddress(ctx) @@ -218,10 +218,9 @@ func waitForBlocks(ctx context.Context, t *testing.T, bm *blockMiner, paymentRec // Add a real block m, err := paymentReceiver.MpoolPushMessage(ctx, &types.Message{ - To: builtin.BurntFundsActorAddr, - From: receiverAddr, - Value: types.NewInt(0), - GasPrice: big.Zero(), + To: builtin.BurntFundsActorAddr, + From: receiverAddr, + Value: types.NewInt(0), }) if err != nil { t.Fatal(err) @@ -303,11 +302,9 @@ func sendFunds(ctx context.Context, t *testing.T, sender TestNode, addr address. } msg := &types.Message{ - From: senderAddr, - To: addr, - Value: amount, - GasLimit: 0, - GasPrice: abi.NewTokenAmount(0), + From: senderAddr, + To: addr, + Value: amount, } sm, err := sender.MpoolPushMessage(ctx, msg) diff --git a/build/params_shared_vals.go b/build/params_shared_vals.go index 36da8d990a8..cfcab014049 100644 --- a/build/params_shared_vals.go +++ b/build/params_shared_vals.go @@ -88,8 +88,12 @@ const VerifSigCacheSize = 32000 // Limits // TODO: If this is gonna stay, it should move to specs-actors -const BlockMessageLimit = 512 -const BlockGasLimit = 7_500_000_000 +const BlockMessageLimit = 10000 +const BlockGasLimit = 10_000_000_000 +const BlockGasTarget = BlockGasLimit / 2 +const BaseFeeMaxChangeDenom = 8 // 12.5% +const InitialBaseFee = 100e6 +const MinimumBaseFee = 100 // Actor consts // TODO: Pull from actors when its made not private diff --git a/build/version.go b/build/version.go index 1f7af7fe302..b7fe7fa942d 100644 --- a/build/version.go +++ b/build/version.go @@ -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, 1) +var APIVersion Version = newVer(0, 9, 0) //nolint:varcheck,deadcode const ( diff --git a/chain/blocksync/cbor_gen.go b/chain/blocksync/cbor_gen.go index a19a05a8d7d..585421ff529 100644 --- a/chain/blocksync/cbor_gen.go +++ b/chain/blocksync/cbor_gen.go @@ -7,7 +7,7 @@ import ( "io" "github.com/filecoin-project/lotus/chain/types" - "github.com/ipfs/go-cid" + cid "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" xerrors "golang.org/x/xerrors" ) diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 8c4bb90c708..d4851a93324 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -489,8 +489,9 @@ func getRandomMessages(cg *ChainGen) ([]*types.SignedMessage, error) { Method: 0, - GasLimit: 100_000_000, - GasPrice: types.NewInt(0), + GasLimit: 100_000_000, + GasFeeCap: types.NewInt(0), + GasPremium: types.NewInt(0), } sig, err := cg.w.Sign(context.TODO(), cg.banker, msg.Cid().Bytes()) diff --git a/chain/gen/genesis/genesis.go b/chain/gen/genesis/genesis.go index ee8140d9f1d..1731d780a50 100644 --- a/chain/gen/genesis/genesis.go +++ b/chain/gen/genesis/genesis.go @@ -21,6 +21,7 @@ import ( "github.com/filecoin-project/specs-actors/actors/crypto" "github.com/filecoin-project/specs-actors/actors/util/adt" + "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" @@ -321,7 +322,16 @@ func VerifyPreSealedData(ctx context.Context, cs *store.ChainStore, stateroot ci verifNeeds := make(map[address.Address]abi.PaddedPieceSize) var sum abi.PaddedPieceSize - vm, err := vm.NewVM(stateroot, 0, &fakeRand{}, cs.Blockstore(), mkFakedSigSyscalls(cs.VMSys()), nil) + vmopt := vm.VMOpts{ + StateBase: stateroot, + Epoch: 0, + Rand: &fakeRand{}, + Bstore: cs.Blockstore(), + Syscalls: mkFakedSigSyscalls(cs.VMSys()), + VestedCalc: nil, + BaseFee: types.NewInt(0), + } + vm, err := vm.NewVM(&vmopt) if err != nil { return cid.Undef, xerrors.Errorf("failed to create NewVM: %w", err) } @@ -443,6 +453,7 @@ func MakeGenesisBlock(ctx context.Context, bs bstore.Blockstore, sys vm.SyscallB Data: make([]byte, 32), }, }, + ParentBaseFee: abi.NewTokenAmount(build.InitialBaseFee), } sb, err := b.ToStorageBlock() diff --git a/chain/gen/genesis/miners.go b/chain/gen/genesis/miners.go index 3766402a19b..37b3d53e0dc 100644 --- a/chain/gen/genesis/miners.go +++ b/chain/gen/genesis/miners.go @@ -4,9 +4,10 @@ import ( "bytes" "context" "fmt" - "github.com/filecoin-project/lotus/chain/state" "math/rand" + "github.com/filecoin-project/lotus/chain/state" + "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" cbg "github.com/whyrusleeping/cbor-gen" @@ -60,7 +61,17 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid return big.Zero(), nil } - vm, err := vm.NewVM(sroot, 0, &fakeRand{}, cs.Blockstore(), mkFakedSigSyscalls(cs.VMSys()), vc) + vmopt := &vm.VMOpts{ + StateBase: sroot, + Epoch: 0, + Rand: &fakeRand{}, + Bstore: cs.Blockstore(), + Syscalls: mkFakedSigSyscalls(cs.VMSys()), + VestedCalc: vc, + BaseFee: types.NewInt(0), + } + + vm, err := vm.NewVM(vmopt) if err != nil { return cid.Undef, xerrors.Errorf("failed to create NewVM: %w", err) } diff --git a/chain/gen/genesis/util.go b/chain/gen/genesis/util.go index 0a5d7e16b6c..15ada30cb4b 100644 --- a/chain/gen/genesis/util.go +++ b/chain/gen/genesis/util.go @@ -37,7 +37,6 @@ func doExecValue(ctx context.Context, vm *vm.VM, to, from address.Address, value Method: method, Params: params, GasLimit: 1_000_000_000_000_000, - GasPrice: types.NewInt(0), Value: value, Nonce: act.Nonce, }) diff --git a/chain/gen/mining.go b/chain/gen/mining.go index 9664b9ab9ff..260c96808ea 100644 --- a/chain/gen/mining.go +++ b/chain/gen/mining.go @@ -109,6 +109,12 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wal } next.ParentWeight = pweight + baseFee, err := sm.ChainStore().ComputeBaseFee(ctx, pts) + if err != nil { + return nil, xerrors.Errorf("computing base fee: %w", err) + } + next.ParentBaseFee = baseFee + cst := cbor.NewCborStore(sm.ChainStore().Blockstore()) tree, err := state.LoadStateTree(cst, st) if err != nil { diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index c83ac8dbd49..00cf4fa3a56 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -126,17 +126,17 @@ func (ms *msgSet) add(m *types.SignedMessage) (bool, error) { if has { if m.Cid() != exms.Cid() { // check if RBF passes - minPrice := exms.Message.GasPrice + minPrice := exms.Message.GasPremium minPrice = types.BigAdd(minPrice, types.BigDiv(types.BigMul(minPrice, rbfNum), rbfDenom)) minPrice = types.BigAdd(minPrice, types.NewInt(1)) - if types.BigCmp(m.Message.GasPrice, minPrice) >= 0 { - log.Infow("add with RBF", "oldprice", exms.Message.GasPrice, - "newprice", m.Message.GasPrice, "addr", m.Message.From, "nonce", m.Message.Nonce) + if types.BigCmp(m.Message.GasPremium, minPrice) >= 0 { + log.Infow("add with RBF", "oldpremium", exms.Message.GasPremium, + "newpremium", m.Message.GasPremium, "addr", m.Message.From, "nonce", m.Message.Nonce) } else { log.Info("add with duplicate nonce") return false, xerrors.Errorf("message from %s with nonce %d already in mpool,"+ - " increase GasPrice to %s from %s to trigger replace by fee", - m.Message.From, m.Message.Nonce, minPrice, m.Message.GasPrice) + " increase GasPremium to %s from %s to trigger replace by fee", + m.Message.From, m.Message.Nonce, minPrice, m.Message.GasPremium) } } } @@ -154,6 +154,7 @@ type Provider interface { MessagesForBlock(*types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) MessagesForTipset(*types.TipSet) ([]types.ChainMsg, error) LoadTipSet(tsk types.TipSetKey) (*types.TipSet, error) + ChainComputeBaseFee(ctx context.Context, ts *types.TipSet) (types.BigInt, error) } type mpoolProvider struct { @@ -162,7 +163,7 @@ type mpoolProvider struct { } func NewProvider(sm *stmgr.StateManager, ps *pubsub.PubSub) Provider { - return &mpoolProvider{sm, ps} + return &mpoolProvider{sm: sm, ps: ps} } func (mpp *mpoolProvider) SubscribeHeadChanges(cb func(rev, app []*types.TipSet) error) *types.TipSet { @@ -199,6 +200,14 @@ func (mpp *mpoolProvider) LoadTipSet(tsk types.TipSetKey) (*types.TipSet, error) return mpp.sm.ChainStore().LoadTipSet(tsk) } +func (mpp *mpoolProvider) ChainComputeBaseFee(ctx context.Context, ts *types.TipSet) (types.BigInt, error) { + baseFee, err := mpp.sm.ChainStore().ComputeBaseFee(ctx, ts) + if err != nil { + return types.NewInt(0), xerrors.Errorf("computing base fee at %s: %w", ts, err) + } + return baseFee, nil +} + func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName) (*MessagePool, error) { cache, _ := lru.New2Q(build.BlsSignatureCacheSize) verifcache, _ := lru.New2Q(build.VerifSigCacheSize) diff --git a/chain/messagepool/messagepool_test.go b/chain/messagepool/messagepool_test.go index 9709931a9f7..a1d26ad9193 100644 --- a/chain/messagepool/messagepool_test.go +++ b/chain/messagepool/messagepool_test.go @@ -87,7 +87,7 @@ func (tma *testMpoolAPI) PubSubPublish(string, []byte) error { func (tma *testMpoolAPI) StateGetActor(addr address.Address, ts *types.TipSet) (*types.Actor, error) { balance, ok := tma.balance[addr] if !ok { - balance = types.NewInt(90000000) + balance = types.NewInt(1000e6) tma.balance[addr] = balance } return &types.Actor{ @@ -140,6 +140,10 @@ func (tma *testMpoolAPI) LoadTipSet(tsk types.TipSetKey) (*types.TipSet, error) return nil, fmt.Errorf("tipset not found") } +func (tma *testMpoolAPI) ChainComputeBaseFee(ctx context.Context, ts *types.TipSet) (types.BigInt, error) { + return types.NewInt(100), nil +} + func assertNonce(t *testing.T, mp *MessagePool, addr address.Address, val uint64) { t.Helper() n, err := mp.GetNonce(addr) diff --git a/chain/messagepool/pruning.go b/chain/messagepool/pruning.go index 11719049d50..3ebd6d20349 100644 --- a/chain/messagepool/pruning.go +++ b/chain/messagepool/pruning.go @@ -7,6 +7,7 @@ import ( "github.com/filecoin-project/lotus/chain/types" "github.com/ipfs/go-cid" + "golang.org/x/xerrors" ) func (mp *MessagePool) pruneExcessMessages() error { @@ -30,6 +31,11 @@ func (mp *MessagePool) pruneMessages(ctx context.Context, ts *types.TipSet) erro log.Infof("message pruning took %s", time.Since(start)) }() + baseFee, err := mp.api.ChainComputeBaseFee(ctx, ts) + if err != nil { + return xerrors.Errorf("computing basefee: %w", err) + } + pending, _ := mp.getPendingMessages(ts, ts) // Collect all messages to track which ones to remove and create chains for block inclusion @@ -39,7 +45,7 @@ func (mp *MessagePool) pruneMessages(ctx context.Context, ts *types.TipSet) erro for _, m := range mset { pruneMsgs[m.Message.Cid()] = m } - actorChains := mp.createMessageChains(actor, mset, ts) + actorChains := mp.createMessageChains(actor, mset, baseFee, ts) chains = append(chains, actorChains...) } diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index db83aa128bb..a8327b884e8 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -30,20 +30,21 @@ type msgChain struct { func (mp *MessagePool) SelectMessages(ts *types.TipSet) ([]*types.SignedMessage, error) { mp.curTsLk.Lock() - curTs := mp.curTs - mp.curTsLk.Unlock() + defer mp.curTsLk.Unlock() mp.lk.Lock() defer mp.lk.Unlock() - return mp.selectMessages(curTs, ts) + return mp.selectMessages(mp.curTs, ts) } func (mp *MessagePool) selectMessages(curTs, ts *types.TipSet) ([]*types.SignedMessage, error) { start := time.Now() - defer func() { - log.Infof("message selection took %s", time.Since(start)) - }() + + baseFee, err := mp.api.ChainComputeBaseFee(context.TODO(), ts) + if err != nil { + return nil, xerrors.Errorf("computing basefee: %w", err) + } // 0. Load messages for the target tipset; if it is the same as the current tipset in the mpool // then this is just the pending messages @@ -51,11 +52,19 @@ func (mp *MessagePool) selectMessages(curTs, ts *types.TipSet) ([]*types.SignedM if err != nil { return nil, err } + if len(pending) == 0 { + return nil, nil + } + + // defer only here so if we have no pending messages we don't spam + defer func() { + log.Infof("message selection took %s", time.Since(start)) + }() // 1. Create a list of dependent message chains with maximal gas reward per limit consumed var chains []*msgChain for actor, mset := range pending { - next := mp.createMessageChains(actor, mset, ts) + next := mp.createMessageChains(actor, mset, baseFee, ts) chains = append(chains, next...) } @@ -63,6 +72,13 @@ func (mp *MessagePool) selectMessages(curTs, ts *types.TipSet) ([]*types.SignedM sort.Slice(chains, func(i, j int) bool { return chains[i].Before(chains[j]) }) + if len(chains) != 0 && chains[0].gasPerf < 0 { + log.Warnw("all messages in mpool have negative has performance", "bestGasPerf", chains[0].gasPerf) + //for i, m := range chains[0].msgs { + //log.Warnf("msg %d %+v", i, m.Message) + //} + return nil, nil + } // 3. Merge the head chains to produce the list of messages selected for inclusion, subject to // the block gas limit. @@ -72,7 +88,7 @@ func (mp *MessagePool) selectMessages(curTs, ts *types.TipSet) ([]*types.SignedM last := len(chains) for i, chain := range chains { // does it fit in the block? - if chain.gasLimit <= gasLimit { + if chain.gasLimit <= gasLimit && chain.gasPerf > 0 { gasLimit -= chain.gasLimit result = append(result, chain.msgs...) continue @@ -92,7 +108,7 @@ func (mp *MessagePool) selectMessages(curTs, ts *types.TipSet) ([]*types.SignedM tailLoop: for gasLimit >= minGas && last < len(chains) { // trim - chains[last].Trim(gasLimit, mp, ts) + chains[last].Trim(gasLimit, mp, baseFee, ts) // push down if it hasn't been invalidated if chains[last].valid { @@ -111,7 +127,7 @@ tailLoop: continue } // does it fit in the bock? - if chain.gasLimit <= gasLimit { + if chain.gasLimit <= gasLimit && chain.gasPerf > 0 { gasLimit -= chain.gasLimit result = append(result, chain.msgs...) continue @@ -208,7 +224,7 @@ func (mp *MessagePool) getPendingMessages(curTs, ts *types.TipSet) (map[address. if dupNonce { // duplicate nonce, selfishly keep the message with the highest GasPrice // if the gas prices are the same, keep the one with the highest GasLimit - switch m.Message.GasPrice.Int.Cmp(other.Message.GasPrice.Int) { + switch m.Message.GasPremium.Int.Cmp(other.Message.GasPremium.Int) { case 0: if m.Message.GasLimit > other.Message.GasLimit { mset[m.Message.Nonce] = m @@ -232,20 +248,12 @@ func (mp *MessagePool) getPendingMessages(curTs, ts *types.TipSet) (map[address. } } -func (mp *MessagePool) getGasReward(msg *types.SignedMessage, ts *types.TipSet) *big.Int { - al := func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*types.Actor, error) { - return mp.api.StateGetActor(addr, ts) - } - gasUsed, err := gasguess.GuessGasUsed(context.TODO(), types.EmptyTSK, msg, al) - if err != nil { - gasUsed = int64(gasguess.MaxGas) - if gasUsed > msg.Message.GasLimit/2 { - gasUsed = msg.Message.GasLimit / 2 - } - // if we start seeing this warning we may have a problem with spammers! - log.Warnf("Cannot guess gas usage for message: %s; using %d", err, gasUsed) +func (mp *MessagePool) getGasReward(msg *types.SignedMessage, baseFee types.BigInt, ts *types.TipSet) *big.Int { + gasReward := abig.Mul(msg.Message.GasPremium, types.NewInt(uint64(msg.Message.GasLimit))) + maxReward := types.BigSub(msg.Message.GasFeeCap, baseFee) + if types.BigCmp(maxReward, gasReward) < 0 { + gasReward = maxReward } - gasReward := abig.Mul(msg.Message.GasPrice, types.NewInt(uint64(gasUsed))) return gasReward.Int } @@ -258,7 +266,7 @@ func (mp *MessagePool) getGasPerf(gasReward *big.Int, gasLimit int64) float64 { return r } -func (mp *MessagePool) createMessageChains(actor address.Address, mset map[uint64]*types.SignedMessage, ts *types.TipSet) []*msgChain { +func (mp *MessagePool) createMessageChains(actor address.Address, mset map[uint64]*types.SignedMessage, baseFee types.BigInt, ts *types.TipSet) []*msgChain { // collect all messages msgs := make([]*types.SignedMessage, 0, len(mset)) for _, m := range mset { @@ -320,7 +328,7 @@ func (mp *MessagePool) createMessageChains(actor address.Address, mset map[uint6 balance = new(big.Int).Sub(balance, value) } - gasReward := mp.getGasReward(m, ts) + gasReward := mp.getGasReward(m, baseFee, ts) rewards = append(rewards, gasReward) } @@ -417,11 +425,11 @@ func (mc *msgChain) Before(other *msgChain) bool { (mc.gasPerf == other.gasPerf && mc.gasReward.Cmp(other.gasReward) > 0) } -func (mc *msgChain) Trim(gasLimit int64, mp *MessagePool, ts *types.TipSet) { +func (mc *msgChain) Trim(gasLimit int64, mp *MessagePool, baseFee types.BigInt, ts *types.TipSet) { i := len(mc.msgs) - 1 for i >= 0 && mc.gasLimit > gasLimit { gasLimit -= mc.msgs[i].Message.GasLimit - gasReward := mp.getGasReward(mc.msgs[i], ts) + gasReward := mp.getGasReward(mc.msgs[i], baseFee, ts) mc.gasReward = new(big.Int).Sub(mc.gasReward, gasReward) mc.gasLimit -= mc.msgs[i].Message.GasLimit if mc.gasLimit > 0 { diff --git a/chain/messagepool/selection_test.go b/chain/messagepool/selection_test.go index 177da4531eb..a90aa9aa8f4 100644 --- a/chain/messagepool/selection_test.go +++ b/chain/messagepool/selection_test.go @@ -20,13 +20,14 @@ import ( func makeTestMessage(w *wallet.Wallet, from, to address.Address, nonce uint64, gasLimit int64, gasPrice uint64) *types.SignedMessage { msg := &types.Message{ - From: from, - To: to, - Method: 2, - Value: types.FromFil(0), - Nonce: nonce, - GasLimit: gasLimit, - GasPrice: types.NewInt(gasPrice), + From: from, + To: to, + Method: 2, + Value: types.FromFil(0), + Nonce: nonce, + GasLimit: gasLimit, + GasFeeCap: types.NewInt(100 + gasPrice), + GasPremium: types.NewInt(gasPrice), } sig, err := w.Sign(context.TODO(), from, msg.Cid().Bytes()) if err != nil { @@ -89,8 +90,9 @@ func TestMessageChains(t *testing.T) { m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1)) mset[uint64(i)] = m } + baseFee := types.NewInt(0) - chains := mp.createMessageChains(a1, mset, ts) + chains := mp.createMessageChains(a1, mset, baseFee, ts) if len(chains) != 1 { t.Fatal("expected a single chain") } @@ -111,7 +113,7 @@ func TestMessageChains(t *testing.T) { mset[uint64(i)] = m } - chains = mp.createMessageChains(a1, mset, ts) + chains = mp.createMessageChains(a1, mset, baseFee, ts) if len(chains) != 10 { t.Fatal("expected 10 chains") } @@ -135,7 +137,7 @@ func TestMessageChains(t *testing.T) { mset[uint64(i)] = m } - chains = mp.createMessageChains(a1, mset, ts) + chains = mp.createMessageChains(a1, mset, baseFee, ts) if len(chains) != 2 { t.Fatal("expected 1 chain") } @@ -166,7 +168,7 @@ func TestMessageChains(t *testing.T) { mset[uint64(i)] = m } - chains = mp.createMessageChains(a1, mset, ts) + chains = mp.createMessageChains(a1, mset, baseFee, ts) if len(chains) != 4 { t.Fatal("expected 4 chains") } @@ -199,7 +201,7 @@ func TestMessageChains(t *testing.T) { mset[uint64(i)] = m } - chains = mp.createMessageChains(a1, mset, ts) + chains = mp.createMessageChains(a1, mset, baseFee, ts) if len(chains) != 1 { t.Fatal("expected a single chain") } @@ -225,7 +227,7 @@ func TestMessageChains(t *testing.T) { mset[uint64(i)] = m } - chains = mp.createMessageChains(a1, mset, ts) + chains = mp.createMessageChains(a1, mset, baseFee, ts) if len(chains) != 1 { t.Fatal("expected a single chain") } @@ -248,7 +250,7 @@ func TestMessageChains(t *testing.T) { mset[uint64(i)] = makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1)) } - chains = mp.createMessageChains(a1, mset, ts) + chains = mp.createMessageChains(a1, mset, baseFee, ts) if len(chains) != 1 { t.Fatal("expected a single chain") } @@ -262,16 +264,16 @@ func TestMessageChains(t *testing.T) { } // test5: insufficient balance for all messages - tma.setBalanceRaw(a1, types.NewInt(uint64(3*gasLimit+1))) + tma.setBalanceRaw(a1, types.NewInt(uint64((300)*gasLimit+1))) mset = make(map[uint64]*types.SignedMessage) for i := 0; i < 10; i++ { mset[uint64(i)] = makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1)) } - chains = mp.createMessageChains(a1, mset, ts) + chains = mp.createMessageChains(a1, mset, baseFee, ts) if len(chains) != 1 { - t.Fatal("expected a single chain") + t.Fatalf("expected a single chain: got %d", len(chains)) } if len(chains[0].msgs) != 2 { t.Fatalf("expected %d message in the chain but got %d", 2, len(chains[0].msgs)) diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index cd411236500..286d1dae785 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -22,7 +22,17 @@ func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate ctx, span := trace.StartSpan(ctx, "statemanager.CallRaw") defer span.End() - vmi, err := vm.NewVM(bstate, bheight, r, sm.cs.Blockstore(), sm.cs.VMSys(), sm.GetVestedFunds) + vmopt := &vm.VMOpts{ + StateBase: bstate, + Epoch: bheight, + Rand: r, + Bstore: sm.cs.Blockstore(), + Syscalls: sm.cs.VMSys(), + VestedCalc: sm.GetVestedFunds, + BaseFee: types.NewInt(0), + } + + vmi, err := vm.NewVM(vmopt) if err != nil { return nil, xerrors.Errorf("failed to set up vm: %w", err) } @@ -30,9 +40,13 @@ func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate if msg.GasLimit == 0 { msg.GasLimit = build.BlockGasLimit } - if msg.GasPrice == types.EmptyInt { - msg.GasPrice = types.NewInt(0) + if msg.GasFeeCap == types.EmptyInt { + msg.GasFeeCap = types.NewInt(0) + } + if msg.GasPremium == types.EmptyInt { + msg.GasPremium = types.NewInt(0) } + if msg.Value == types.EmptyInt { msg.Value = types.NewInt(0) } @@ -40,7 +54,7 @@ func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate if span.IsRecordingEvents() { span.AddAttributes( trace.Int64Attribute("gas_limit", msg.GasLimit), - trace.Int64Attribute("gas_price", int64(msg.GasPrice.Uint64())), + trace.StringAttribute("gas_feecap", msg.GasFeeCap.String()), trace.StringAttribute("value", msg.Value.String()), ) } @@ -101,12 +115,21 @@ func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, pri if span.IsRecordingEvents() { span.AddAttributes( trace.Int64Attribute("gas_limit", msg.GasLimit), - trace.Int64Attribute("gas_price", int64(msg.GasPrice.Uint64())), + trace.StringAttribute("gas_feecap", msg.GasFeeCap.String()), trace.StringAttribute("value", msg.Value.String()), ) } - vmi, err := vm.NewVM(state, ts.Height(), r, sm.cs.Blockstore(), sm.cs.VMSys(), sm.GetVestedFunds) + vmopt := &vm.VMOpts{ + StateBase: state, + Epoch: ts.Height(), + Rand: r, + Bstore: sm.cs.Blockstore(), + Syscalls: sm.cs.VMSys(), + VestedCalc: sm.GetVestedFunds, + BaseFee: ts.Blocks()[0].ParentBaseFee, + } + vmi, err := vm.NewVM(vmopt) if err != nil { return nil, xerrors.Errorf("failed to set up vm: %w", err) } diff --git a/chain/stmgr/forks_test.go b/chain/stmgr/forks_test.go index 6c368685252..caa63c879fb 100644 --- a/chain/stmgr/forks_test.go +++ b/chain/stmgr/forks_test.go @@ -25,11 +25,9 @@ import ( . "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/vm" - "github.com/filecoin-project/lotus/lib/blockstore" _ "github.com/filecoin-project/lotus/lib/sigs/bls" _ "github.com/filecoin-project/lotus/lib/sigs/secp" - "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" logging "github.com/ipfs/go-log" cbg "github.com/whyrusleeping/cbor-gen" @@ -151,8 +149,8 @@ func TestForkHeightTriggers(t *testing.T) { } inv.Register(builtin.PaymentChannelActorCodeID, &testActor{}, &testActorState{}) - sm.SetVMConstructor(func(c cid.Cid, h abi.ChainEpoch, r vm.Rand, b blockstore.Blockstore, s vm.SyscallBuilder, vc vm.VestedCalculator) (*vm.VM, error) { - nvm, err := vm.NewVM(c, h, r, b, s, sm.GetVestedFunds) + sm.SetVMConstructor(func(vmopt *vm.VMOpts) (*vm.VM, error) { + nvm, err := vm.NewVM(vmopt) if err != nil { return nil, err } @@ -175,7 +173,6 @@ func TestForkHeightTriggers(t *testing.T) { Method: builtin.MethodsInit.Exec, Params: enc, GasLimit: types.TestGasLimit, - GasPrice: types.NewInt(0), } sig, err := cg.Wallet().Sign(ctx, cg.Banker(), m.Cid().Bytes()) if err != nil { @@ -202,7 +199,6 @@ func TestForkHeightTriggers(t *testing.T) { Params: nil, Nonce: nonce, GasLimit: types.TestGasLimit, - GasPrice: types.NewInt(0), } nonce++ diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index c7b503ebfd5..566692b389f 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -3,9 +3,10 @@ package stmgr import ( "context" "fmt" - "github.com/filecoin-project/specs-actors/actors/builtin/multisig" "sync" + "github.com/filecoin-project/specs-actors/actors/builtin/multisig" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" @@ -14,7 +15,6 @@ import ( "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/vm" - "github.com/filecoin-project/lotus/lib/blockstore" "github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/abi/big" @@ -41,7 +41,7 @@ type StateManager struct { compWait map[string]chan struct{} stlk sync.Mutex genesisMsigLk sync.Mutex - newVM func(cid.Cid, abi.ChainEpoch, vm.Rand, blockstore.Blockstore, vm.SyscallBuilder, vm.VestedCalculator) (*vm.VM, error) + newVM func(*vm.VMOpts) (*vm.VM, error) genesisMsigs []multisig.State } @@ -150,8 +150,19 @@ type BlockMessages struct { type ExecCallback func(cid.Cid, *types.Message, *vm.ApplyRet) error -func (sm *StateManager) ApplyBlocks(ctx context.Context, parentEpoch abi.ChainEpoch, pstate cid.Cid, bms []BlockMessages, epoch abi.ChainEpoch, r vm.Rand, cb ExecCallback) (cid.Cid, cid.Cid, error) { - vmi, err := sm.newVM(pstate, epoch, r, sm.cs.Blockstore(), sm.cs.VMSys(), sm.GetVestedFunds) +func (sm *StateManager) ApplyBlocks(ctx context.Context, parentEpoch abi.ChainEpoch, pstate cid.Cid, bms []BlockMessages, epoch abi.ChainEpoch, r vm.Rand, cb ExecCallback, baseFee abi.TokenAmount) (cid.Cid, cid.Cid, error) { + + vmopt := &vm.VMOpts{ + StateBase: pstate, + Epoch: epoch, + Rand: r, + Bstore: sm.cs.Blockstore(), + Syscalls: sm.cs.VMSys(), + VestedCalc: sm.GetVestedFunds, + BaseFee: baseFee, + } + + vmi, err := sm.newVM(vmopt) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("instantiating VM failed: %w", err) } @@ -164,14 +175,15 @@ func (sm *StateManager) ApplyBlocks(ctx context.Context, parentEpoch abi.ChainEp } cronMsg := &types.Message{ - To: builtin.CronActorAddr, - From: builtin.SystemActorAddr, - Nonce: ca.Nonce, - Value: types.NewInt(0), - GasPrice: types.NewInt(0), - GasLimit: build.BlockGasLimit * 10000, // Make super sure this is never too little - Method: builtin.MethodsCron.EpochTick, - Params: nil, + To: builtin.CronActorAddr, + From: builtin.SystemActorAddr, + Nonce: ca.Nonce, + Value: types.NewInt(0), + GasFeeCap: types.NewInt(0), + GasPremium: types.NewInt(0), + GasLimit: build.BlockGasLimit * 10000, // Make super sure this is never too little + Method: builtin.MethodsCron.EpochTick, + Params: nil, } ret, err := vmi.ApplyImplicitMessage(ctx, cronMsg) if err != nil { @@ -223,7 +235,7 @@ func (sm *StateManager) ApplyBlocks(ctx context.Context, parentEpoch abi.ChainEp } receipts = append(receipts, &r.MessageReceipt) - gasReward = big.Add(gasReward, big.Mul(m.GasPrice, big.NewInt(r.GasUsed))) + gasReward = big.Add(gasReward, r.MinerTip) penalty = big.Add(penalty, r.Penalty) if cb != nil { @@ -251,14 +263,15 @@ func (sm *StateManager) ApplyBlocks(ctx context.Context, parentEpoch abi.ChainEp } rwMsg := &types.Message{ - From: builtin.SystemActorAddr, - To: builtin.RewardActorAddr, - Nonce: sysAct.Nonce, - Value: types.NewInt(0), - GasPrice: types.NewInt(0), - GasLimit: 1 << 30, - Method: builtin.MethodsReward.AwardBlockReward, - Params: params, + From: builtin.SystemActorAddr, + To: builtin.RewardActorAddr, + Nonce: sysAct.Nonce, + Value: types.NewInt(0), + GasFeeCap: types.NewInt(0), + GasPremium: types.NewInt(0), + GasLimit: 1 << 30, + Method: builtin.MethodsReward.AwardBlockReward, + Params: params, } ret, err := vmi.ApplyImplicitMessage(ctx, rwMsg) if err != nil { @@ -354,8 +367,9 @@ func (sm *StateManager) computeTipSetState(ctx context.Context, blks []*types.Bl blkmsgs = append(blkmsgs, bm) } + baseFee := blks[0].ParentBaseFee - return sm.ApplyBlocks(ctx, parentEpoch, pstate, blkmsgs, blks[0].Height, r, cb) + return sm.ApplyBlocks(ctx, parentEpoch, pstate, blkmsgs, blks[0].Height, r, cb, baseFee) } func (sm *StateManager) parentState(ts *types.TipSet) cid.Cid { @@ -764,7 +778,7 @@ func (sm *StateManager) ValidateChain(ctx context.Context, ts *types.TipSet) err return nil } -func (sm *StateManager) SetVMConstructor(nvm func(cid.Cid, abi.ChainEpoch, vm.Rand, blockstore.Blockstore, vm.SyscallBuilder, vm.VestedCalculator) (*vm.VM, error)) { +func (sm *StateManager) SetVMConstructor(nvm func(*vm.VMOpts) (*vm.VM, error)) { sm.newVM = nvm } diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index 3a7ae4121ed..d1ad13420f1 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -440,7 +440,16 @@ func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch, } r := store.NewChainRand(sm.cs, ts.Cids(), height) - vmi, err := vm.NewVM(base, height, r, sm.cs.Blockstore(), sm.cs.VMSys(), sm.GetVestedFunds) + vmopt := &vm.VMOpts{ + StateBase: base, + Epoch: height, + Rand: r, + Bstore: sm.cs.Blockstore(), + Syscalls: sm.cs.VMSys(), + VestedCalc: sm.GetVestedFunds, + BaseFee: ts.Blocks()[0].ParentBaseFee, + } + vmi, err := vm.NewVM(vmopt) if err != nil { return cid.Undef, nil, err } @@ -595,7 +604,16 @@ func (sm *StateManager) CirculatingSupply(ctx context.Context, ts *types.TipSet) } r := store.NewChainRand(sm.cs, ts.Cids(), ts.Height()) - vmi, err := vm.NewVM(st, ts.Height(), r, sm.cs.Blockstore(), sm.cs.VMSys(), sm.GetVestedFunds) + vmopt := &vm.VMOpts{ + StateBase: st, + Epoch: ts.Height(), + Rand: r, + Bstore: sm.cs.Blockstore(), + Syscalls: sm.cs.VMSys(), + VestedCalc: sm.GetVestedFunds, + BaseFee: ts.Blocks()[0].ParentBaseFee, + } + vmi, err := vm.NewVM(vmopt) if err != nil { return big.Zero(), err } diff --git a/chain/store/basefee.go b/chain/store/basefee.go new file mode 100644 index 00000000000..7148ff9ba0e --- /dev/null +++ b/chain/store/basefee.go @@ -0,0 +1,45 @@ +package store + +import ( + "context" + + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/specs-actors/actors/abi" + "github.com/filecoin-project/specs-actors/actors/abi/big" + "golang.org/x/xerrors" +) + +func computeNextBaseFee(baseFee types.BigInt, gasLimitUsed int64, noOfBlocks int) types.BigInt { + delta := gasLimitUsed/int64(noOfBlocks) - build.BlockGasTarget + + change := big.Mul(baseFee, big.NewInt(delta)) + change = big.Div(change, big.NewInt(build.BlockGasTarget)) + change = big.Div(change, big.NewInt(build.BaseFeeMaxChangeDenom)) + + nextBaseFee := big.Add(baseFee, change) + if big.Cmp(nextBaseFee, big.NewInt(build.MinimumBaseFee)) < 0 { + nextBaseFee = big.NewInt(build.MinimumBaseFee) + } + return nextBaseFee +} + +func (cs *ChainStore) ComputeBaseFee(ctx context.Context, ts *types.TipSet) (abi.TokenAmount, error) { + zero := abi.NewTokenAmount(0) + totalLimit := int64(0) + for _, b := range ts.Blocks() { + msg1, msg2, err := cs.MessagesForBlock(b) + if err != nil { + return zero, xerrors.Errorf("error getting messages for: %s: %w", b.Cid(), err) + } + for _, m := range msg1 { + totalLimit += m.GasLimit + } + for _, m := range msg2 { + totalLimit += m.Message.GasLimit + } + } + parentBaseFee := ts.Blocks()[0].ParentBaseFee + + return computeNextBaseFee(parentBaseFee, totalLimit, len(ts.Blocks())), nil +} diff --git a/chain/store/basefee_test.go b/chain/store/basefee_test.go new file mode 100644 index 00000000000..c2186a711b9 --- /dev/null +++ b/chain/store/basefee_test.go @@ -0,0 +1,34 @@ +package store + +import ( + "fmt" + "testing" + + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/types" + "github.com/stretchr/testify/assert" +) + +func TestBaseFee(t *testing.T) { + tests := []struct { + basefee uint64 + limitUsed int64 + noOfBlocks int + output uint64 + }{ + {100e6, 0, 1, 87.5e6}, + {100e6, 0, 5, 87.5e6}, + {100e6, build.BlockGasTarget, 1, 100e6}, + {100e6, build.BlockGasTarget * 2, 2, 100e6}, + {100e6, build.BlockGasLimit * 2, 2, 112.5e6}, + {100e6, build.BlockGasLimit * 1.5, 2, 106.25e6}, + } + + for _, test := range tests { + test := test + t.Run(fmt.Sprintf("%v", test), func(t *testing.T) { + output := computeNextBaseFee(types.NewInt(test.basefee), test.limitUsed, test.noOfBlocks) + assert.Equal(t, fmt.Sprintf("%d", test.output), output.String()) + }) + } +} diff --git a/chain/store/weight.go b/chain/store/weight.go index adbc076fdfd..3fce45692e2 100644 --- a/chain/store/weight.go +++ b/chain/store/weight.go @@ -22,7 +22,7 @@ func (cs *ChainStore) Weight(ctx context.Context, ts *types.TipSet) (types.BigIn } // >>> w[r] <<< + wFunction(totalPowerAtTipset(ts)) * 2^8 + (wFunction(totalPowerAtTipset(ts)) * sum(ts.blocks[].ElectionProof.WinCount) * wRatio_num * 2^8) / (e * wRatio_den) - var out = new(big.Int).Set(ts.Blocks()[0].ParentWeight.Int) + var out = new(big.Int).Set(ts.ParentWeight().Int) // >>> wFunction(totalPowerAtTipset(ts)) * 2^8 <<< + (wFunction(totalPowerAtTipset(ts)) * sum(ts.blocks[].ElectionProof.WinCount) * wRatio_num * 2^8) / (e * wRatio_den) diff --git a/chain/sync.go b/chain/sync.go index 1db9e5a27b2..9c5b2f24b2b 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -199,8 +199,8 @@ func (syncer *Syncer) InformNewHead(from peer.ID, fts *store.FullTipSet) bool { syncer.Bsync.AddPeer(from) - bestPweight := syncer.store.GetHeaviestTipSet().Blocks()[0].ParentWeight - targetWeight := fts.TipSet().Blocks()[0].ParentWeight + bestPweight := syncer.store.GetHeaviestTipSet().ParentWeight() + targetWeight := fts.TipSet().ParentWeight() if targetWeight.LessThan(bestPweight) { var miners []string for _, blk := range fts.TipSet().Blocks() { @@ -671,8 +671,6 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er return xerrors.Errorf("failed to get latest beacon entry: %w", err) } - //nulls := h.Height - (baseTs.Height() + 1) - // fast checks first nulls := h.Height - (baseTs.Height() + 1) if tgtTs := baseTs.MinTimestamp() + build.BlockDelaySecs*uint64(nulls+1); h.Timestamp != tgtTs { @@ -701,6 +699,27 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er return nil }) + baseFeeCheck := async.Err(func() error { + baseFee, err := syncer.store.ComputeBaseFee(ctx, baseTs) + if err != nil { + return xerrors.Errorf("computing base fee: %w", err) + } + if types.BigCmp(baseFee, b.Header.ParentBaseFee) != 0 { + return xerrors.Errorf("base fee doesn't match: %s (header) != %s (computed)", + b.Header.ParentBaseFee, baseFee) + } + return nil + }) + pweight, err := syncer.store.Weight(ctx, baseTs) + if err != nil { + return xerrors.Errorf("getting parent weight: %w", err) + } + + if types.BigCmp(pweight, b.Header.ParentWeight) != 0 { + return xerrors.Errorf("parrent weight different: %s (header) != %s (computed)", + b.Header.ParentWeight, pweight) + } + // Stuff that needs stateroot / worker address stateroot, precp, err := syncer.sm.TipSetState(ctx, baseTs) if err != nil { @@ -843,6 +862,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er wproofCheck, winnerCheck, msgsCheck, + baseFeeCheck, } var merr error @@ -994,7 +1014,6 @@ func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock return xerrors.Errorf("failed to get actor: %w", err) } - // redundant check if !act.IsAccountActor() { return xerrors.New("Sender must be an account actor") } diff --git a/chain/types/blockheader.go b/chain/types/blockheader.go index a1d8b45b480..8950fd91a1b 100644 --- a/chain/types/blockheader.go +++ b/chain/types/blockheader.go @@ -65,6 +65,9 @@ type BlockHeader struct { ForkSignaling uint64 // 14 + // ParentBaseFee is the base fee after executing parent tipset + ParentBaseFee abi.TokenAmount // 15 + // internal validated bool // true if the signature has been validated } diff --git a/chain/types/blockheader_test.go b/chain/types/blockheader_test.go index 4cd46bde144..884fc670cb1 100644 --- a/chain/types/blockheader_test.go +++ b/chain/types/blockheader_test.go @@ -44,6 +44,7 @@ func testBlockHeader(t testing.TB) *BlockHeader { Height: 85919298723, ParentStateRoot: c, BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS, Data: []byte("boo! im a signature")}, + ParentBaseFee: NewInt(3432432843291), } } @@ -107,7 +108,8 @@ func TestInteropBH(t *testing.T) { Type: crypto.SigTypeBLS, Data: []byte{0x3}, }, - BLSAggregate: &crypto.Signature{}, + BLSAggregate: &crypto.Signature{}, + ParentBaseFee: NewInt(1000000000), } bhsb, err := bh.SigningBytes() @@ -116,7 +118,7 @@ func TestInteropBH(t *testing.T) { t.Fatal(err) } - gfc := "8f5501d04cb15021bf6bd003073d79e2238d4e61f1ad2281430102038200420a0b818205410c818200410781d82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619cc430003e802d82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619ccd82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619ccd82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619cc410001f603" + gfc := "905501d04cb15021bf6bd003073d79e2238d4e61f1ad2281430102038200420a0b818205410c818200410781d82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619cc430003e802d82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619ccd82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619ccd82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619cc410001f60345003b9aca00" require.Equal(t, gfc, hex.EncodeToString(bhsb)) } diff --git a/chain/types/cbor_gen.go b/chain/types/cbor_gen.go index 9f504b7c9b5..eee2b3c44f8 100644 --- a/chain/types/cbor_gen.go +++ b/chain/types/cbor_gen.go @@ -9,14 +9,14 @@ import ( "github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/crypto" "github.com/filecoin-project/specs-actors/actors/runtime/exitcode" - "github.com/ipfs/go-cid" + cid "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" xerrors "golang.org/x/xerrors" ) var _ = xerrors.Errorf -var lengthBufBlockHeader = []byte{143} +var lengthBufBlockHeader = []byte{144} func (t *BlockHeader) MarshalCBOR(w io.Writer) error { if t == nil { @@ -142,6 +142,10 @@ func (t *BlockHeader) MarshalCBOR(w io.Writer) error { return err } + // t.ParentBaseFee (big.Int) (struct) + if err := t.ParentBaseFee.MarshalCBOR(w); err != nil { + return err + } return nil } @@ -159,7 +163,7 @@ func (t *BlockHeader) UnmarshalCBOR(r io.Reader) error { return fmt.Errorf("cbor input should be of type array") } - if extra != 15 { + if extra != 16 { return fmt.Errorf("cbor input had wrong number of fields") } @@ -439,6 +443,15 @@ func (t *BlockHeader) UnmarshalCBOR(r io.Reader) error { } t.ForkSignaling = uint64(extra) + } + // t.ParentBaseFee (big.Int) (struct) + + { + + if err := t.ParentBaseFee.UnmarshalCBOR(br); err != nil { + return xerrors.Errorf("unmarshaling t.ParentBaseFee: %w", err) + } + } return nil } @@ -619,7 +632,7 @@ func (t *ElectionProof) UnmarshalCBOR(r io.Reader) error { return nil } -var lengthBufMessage = []byte{137} +var lengthBufMessage = []byte{138} func (t *Message) MarshalCBOR(w io.Writer) error { if t == nil { @@ -664,11 +677,6 @@ func (t *Message) MarshalCBOR(w io.Writer) error { return err } - // t.GasPrice (big.Int) (struct) - if err := t.GasPrice.MarshalCBOR(w); err != nil { - return err - } - // t.GasLimit (int64) (int64) if t.GasLimit >= 0 { if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.GasLimit)); err != nil { @@ -680,6 +688,16 @@ func (t *Message) MarshalCBOR(w io.Writer) error { } } + // t.GasFeeCap (big.Int) (struct) + if err := t.GasFeeCap.MarshalCBOR(w); err != nil { + return err + } + + // t.GasPremium (big.Int) (struct) + if err := t.GasPremium.MarshalCBOR(w); err != nil { + return err + } + // t.Method (abi.MethodNum) (uint64) if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Method)); err != nil { @@ -715,7 +733,7 @@ func (t *Message) UnmarshalCBOR(r io.Reader) error { return fmt.Errorf("cbor input should be of type array") } - if extra != 9 { + if extra != 10 { return fmt.Errorf("cbor input had wrong number of fields") } @@ -784,15 +802,6 @@ func (t *Message) UnmarshalCBOR(r io.Reader) error { return xerrors.Errorf("unmarshaling t.Value: %w", err) } - } - // t.GasPrice (big.Int) (struct) - - { - - if err := t.GasPrice.UnmarshalCBOR(br); err != nil { - return xerrors.Errorf("unmarshaling t.GasPrice: %w", err) - } - } // t.GasLimit (int64) (int64) { @@ -819,6 +828,24 @@ func (t *Message) UnmarshalCBOR(r io.Reader) error { t.GasLimit = int64(extraI) } + // t.GasFeeCap (big.Int) (struct) + + { + + if err := t.GasFeeCap.UnmarshalCBOR(br); err != nil { + return xerrors.Errorf("unmarshaling t.GasFeeCap: %w", err) + } + + } + // t.GasPremium (big.Int) (struct) + + { + + if err := t.GasPremium.UnmarshalCBOR(br); err != nil { + return xerrors.Errorf("unmarshaling t.GasPremium: %w", err) + } + + } // t.Method (abi.MethodNum) (uint64) { diff --git a/chain/types/message.go b/chain/types/message.go index 9dc3dbd26f1..eb03edd67ad 100644 --- a/chain/types/message.go +++ b/chain/types/message.go @@ -32,10 +32,11 @@ type Message struct { Nonce uint64 - Value BigInt + Value abi.TokenAmount - GasPrice BigInt - GasLimit int64 + GasLimit int64 + GasFeeCap abi.TokenAmount + GasPremium abi.TokenAmount Method abi.MethodNum Params []byte @@ -106,7 +107,7 @@ func (m *Message) Cid() cid.Cid { } func (m *Message) RequiredFunds() BigInt { - return BigMul(m.GasPrice, NewInt(uint64(m.GasLimit))) + return BigMul(m.GasFeeCap, NewInt(uint64(m.GasLimit))) } func (m *Message) VMMessage() *Message { @@ -138,8 +139,12 @@ func (m *Message) ValidForBlockInclusion(minGas int64) error { return xerrors.New("'Value' field cannot be greater than total filecoin supply") } - if m.GasPrice.LessThan(big.Zero()) { - return xerrors.New("'GasPrice' field cannot be negative") + if m.GasFeeCap.LessThan(big.Zero()) { + return xerrors.New("'GasFeeCap' field cannot be negative") + } + + if m.GasPremium.LessThan(big.Zero()) { + return xerrors.New("'GasPremium' field cannot be negative") } if m.GasLimit > build.BlockGasLimit { diff --git a/chain/types/mock/chain.go b/chain/types/mock/chain.go index d818cb5ac1b..33b13d4086a 100644 --- a/chain/types/mock/chain.go +++ b/chain/types/mock/chain.go @@ -23,12 +23,13 @@ func Address(i uint64) address.Address { func MkMessage(from, to address.Address, nonce uint64, w *wallet.Wallet) *types.SignedMessage { msg := &types.Message{ - To: to, - From: from, - Value: types.NewInt(1), - Nonce: nonce, - GasLimit: 1000000, - GasPrice: types.NewInt(0), + To: to, + From: from, + Value: types.NewInt(1), + Nonce: nonce, + GasLimit: 1000000, + GasFeeCap: types.NewInt(100), + GasPremium: types.NewInt(1), } sig, err := w.Sign(context.TODO(), from, msg.Cid().Bytes()) diff --git a/chain/types/types_test.go b/chain/types/types_test.go index a2b47ad5179..1056fc430e1 100644 --- a/chain/types/types_test.go +++ b/chain/types/types_test.go @@ -22,13 +22,14 @@ func blsaddr(n int64) address.Address { func BenchmarkSerializeMessage(b *testing.B) { m := &Message{ - To: blsaddr(1), - From: blsaddr(2), - Nonce: 197, - Method: 1231254, - Params: []byte("some bytes, idk. probably at least ten of them"), - GasLimit: 126723, - GasPrice: NewInt(1776234), + To: blsaddr(1), + From: blsaddr(2), + Nonce: 197, + Method: 1231254, + Params: []byte("some bytes, idk. probably at least ten of them"), + GasLimit: 126723, + GasPremium: NewInt(1245667), + GasFeeCap: NewInt(1245667), } b.ReportAllocs() diff --git a/chain/types_test.go b/chain/types_test.go index 3b49a8d9443..7d68da68df5 100644 --- a/chain/types_test.go +++ b/chain/types_test.go @@ -13,14 +13,15 @@ func TestSignedMessageJsonRoundtrip(t *testing.T) { from, _ := address.NewIDAddress(603911192) smsg := &types.SignedMessage{ Message: types.Message{ - To: to, - From: from, - Params: []byte("some bytes, idk"), - Method: 1235126, - Value: types.NewInt(123123), - GasPrice: types.NewInt(1234), - GasLimit: 100_000_000, - Nonce: 123123, + To: to, + From: from, + Params: []byte("some bytes, idk"), + Method: 1235126, + Value: types.NewInt(123123), + GasFeeCap: types.NewInt(1234), + GasPremium: types.NewInt(132414234), + GasLimit: 100_000_000, + Nonce: 123123, }, } diff --git a/chain/validation/applier.go b/chain/validation/applier.go index 378324a430a..008b0a90705 100644 --- a/chain/validation/applier.go +++ b/chain/validation/applier.go @@ -2,6 +2,7 @@ package validation import ( "context" + "golang.org/x/xerrors" "github.com/filecoin-project/specs-actors/actors/abi" @@ -89,6 +90,7 @@ func (a *Applier) ApplyTipSetMessages(epoch abi.ChainEpoch, blocks []vtypes.Bloc } var receipts []vtypes.MessageReceipt + // TODO: base fee sroot, _, err := sm.ApplyBlocks(context.TODO(), epoch-1, a.stateWrapper.Root(), bms, epoch, &randWrapper{rnd}, func(c cid.Cid, msg *types.Message, ret *vm.ApplyRet) error { if msg.From == builtin.SystemActorAddr { return nil // ignore reward and cron calls @@ -104,7 +106,7 @@ func (a *Applier) ApplyTipSetMessages(epoch abi.ChainEpoch, blocks []vtypes.Bloc GasUsed: vtypes.GasUnits(ret.GasUsed), }) return nil - }) + }, abi.NewTokenAmount(100)) if err != nil { return vtypes.ApplyTipSetResult{}, err } @@ -136,7 +138,17 @@ func (a *Applier) applyMessage(epoch abi.ChainEpoch, lm types.ChainMsg) (vtypes. ctx := context.TODO() base := a.stateWrapper.Root() - lotusVM, err := vm.NewVM(base, epoch, &vmRand{}, a.stateWrapper.bs, a.syscalls, nil) + vmopt := &vm.VMOpts{ + StateBase: base, + Epoch: epoch, + Rand: &vmRand{}, + Bstore: a.stateWrapper.bs, + Syscalls: a.syscalls, + VestedCalc: nil, + BaseFee: abi.NewTokenAmount(100), + } + + lotusVM, err := vm.NewVM(vmopt) // need to modify the VM invoker to add the puppet actor chainValInvoker := vm.NewInvoker() chainValInvoker.Register(puppet.PuppetActorCodeID, puppet.Actor{}, puppet.State{}) @@ -177,9 +189,10 @@ func toLotusMsg(msg *vtypes.Message) *types.Message { Nonce: msg.CallSeqNum, Method: msg.Method, - Value: msg.Value, - GasPrice: msg.GasPrice, - GasLimit: msg.GasLimit, + Value: msg.Value, + GasLimit: msg.GasLimit, + GasFeeCap: msg.GasFeeCap, + GasPremium: msg.GasPremium, Params: msg.Params, } diff --git a/chain/vectors/gen/main.go b/chain/vectors/gen/main.go index 2ebcb9a60b7..ce8d137e800 100644 --- a/chain/vectors/gen/main.go +++ b/chain/vectors/gen/main.go @@ -17,6 +17,7 @@ import ( "github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/abi/big" "github.com/filecoin-project/specs-actors/actors/builtin/power" + "github.com/filecoin-project/specs-actors/actors/builtin/verifreg" "github.com/filecoin-project/specs-actors/actors/crypto" _ "github.com/filecoin-project/lotus/lib/sigs/bls" @@ -24,6 +25,7 @@ import ( ) func init() { + verifreg.MinVerifiedDealSize = big.NewInt(2048) power.ConsensusMinerMinPower = big.NewInt(2048) } @@ -137,7 +139,8 @@ func MakeUnsignedMessageVectors() []vectors.UnsignedMessageVector { if err != nil { panic(err) } - to, err := address.NewIDAddress(rand.Uint64()) + uint63mask := uint64(1<<63 - 1) + to, err := address.NewIDAddress(rand.Uint64() & uint63mask) if err != nil { panic(err) } @@ -146,14 +149,15 @@ func MakeUnsignedMessageVectors() []vectors.UnsignedMessageVector { rand.Read(params) msg := &types.Message{ - To: to, - From: from, - Value: types.NewInt(rand.Uint64()), - Method: abi.MethodNum(rand.Uint64()), - GasPrice: types.NewInt(rand.Uint64()), - GasLimit: rand.Int63(), - Nonce: rand.Uint64(), - Params: params, + To: to, + From: from, + Value: types.NewInt(rand.Uint64()), + Method: abi.MethodNum(rand.Uint64()), + GasFeeCap: types.NewInt(rand.Uint64()), + GasPremium: types.NewInt(rand.Uint64()), + GasLimit: rand.Int63(), + Nonce: rand.Uint64() & (1<<63 - 1), + Params: params, } ser, err := msg.Serialize() diff --git a/chain/vm/burn.go b/chain/vm/burn.go index f24ee5cd096..bad132de95b 100644 --- a/chain/vm/burn.go +++ b/chain/vm/burn.go @@ -1,7 +1,8 @@ package vm import ( - "math/big" + "github.com/filecoin-project/specs-actors/actors/abi" + "github.com/filecoin-project/specs-actors/actors/abi/big" ) const ( @@ -9,9 +10,21 @@ const ( gasOveruseDenom = 10 ) -// ComputeGasOutputs computes amount of gas to be refunded and amount of gas to be burned +type GasOutputs struct { + BaseFeeBurn abi.TokenAmount + OverEstimationBurn abi.TokenAmount + + MinerPenalty abi.TokenAmount + MinerTip abi.TokenAmount + Refund abi.TokenAmount + + GasRefund int64 + GasBurned int64 +} + +// ComputeGasOverestimationBurn computes amount of gas to be refunded and amount of gas to be burned // Result is (refund, burn) -func ComputeGasOutputs(gasUsed, gasLimit int64) (int64, int64) { +func ComputeGasOverestimationBurn(gasUsed, gasLimit int64) (int64, int64) { if gasUsed == 0 { return 0, gasLimit } @@ -37,8 +50,48 @@ func ComputeGasOutputs(gasUsed, gasLimit int64) (int64, int64) { // needs bigint, as it overflows in pathological case gasLimit > 2^32 gasUsed = gasLimit / 2 gasToBurn := big.NewInt(gasLimit - gasUsed) - gasToBurn = gasToBurn.Mul(gasToBurn, big.NewInt(over)) - gasToBurn = gasToBurn.Div(gasToBurn, big.NewInt(gasUsed)) + gasToBurn = big.Mul(gasToBurn, big.NewInt(over)) + gasToBurn = big.Div(gasToBurn, big.NewInt(gasUsed)) return gasLimit - gasUsed - gasToBurn.Int64(), gasToBurn.Int64() } + +func ComputeGasOutputs(gasUsed, gasLimit int64, baseFee, feeCap, gasPremium abi.TokenAmount) GasOutputs { + gasUsedBig := big.NewInt(gasUsed) + out := GasOutputs{ + BaseFeeBurn: big.Zero(), + OverEstimationBurn: big.Zero(), + MinerPenalty: big.Zero(), + MinerTip: big.Zero(), + Refund: big.Zero(), + } + + baseFeeToPay := baseFee + if baseFee.Cmp(feeCap.Int) > 0 { + baseFeeToPay = feeCap + out.MinerPenalty = big.Mul(big.Sub(baseFee, feeCap), gasUsedBig) + } + out.BaseFeeBurn = big.Mul(baseFeeToPay, gasUsedBig) + + minerTip := gasPremium + if big.Cmp(big.Add(baseFeeToPay, minerTip), feeCap) > 0 { + minerTip = big.Sub(feeCap, baseFeeToPay) + } + out.MinerTip = big.Mul(minerTip, big.NewInt(gasLimit)) + + out.GasRefund, out.GasBurned = ComputeGasOverestimationBurn(gasUsed, gasLimit) + + if out.GasBurned != 0 { + gasBurnedBig := big.NewInt(out.GasBurned) + out.OverEstimationBurn = big.Mul(baseFeeToPay, gasBurnedBig) + minerPenalty := big.Mul(big.Sub(baseFee, baseFeeToPay), gasBurnedBig) + out.MinerPenalty = big.Add(out.MinerPenalty, minerPenalty) + } + + requiredFunds := big.Mul(big.NewInt(gasLimit), feeCap) + refund := big.Sub(requiredFunds, out.BaseFeeBurn) + refund = big.Sub(refund, out.MinerTip) + refund = big.Sub(refund, out.OverEstimationBurn) + out.Refund = refund + return out +} diff --git a/chain/vm/burn_test.go b/chain/vm/burn_test.go index e39fde520a9..58e1336057b 100644 --- a/chain/vm/burn_test.go +++ b/chain/vm/burn_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + "github.com/filecoin-project/lotus/chain/types" "github.com/stretchr/testify/assert" ) @@ -31,9 +32,47 @@ func TestGasBurn(t *testing.T) { for _, test := range tests { test := test t.Run(fmt.Sprintf("%v", test), func(t *testing.T) { - refund, toBurn := ComputeGasOutputs(test.used, test.limit) + refund, toBurn := ComputeGasOverestimationBurn(test.used, test.limit) assert.Equal(t, test.refund, refund, "refund") assert.Equal(t, test.burn, toBurn, "burned") }) } } + +func TestGasOutputs(t *testing.T) { + baseFee := types.NewInt(10) + tests := []struct { + used int64 + limit int64 + + feeCap uint64 + premium uint64 + + BaseFeeBurn uint64 + OverEstimationBurn uint64 + MinerPenalty uint64 + MinerTip uint64 + Refund uint64 + }{ + {100, 110, 11, 1, 1000, 0, 0, 110, 100}, + {100, 130, 11, 1, 1000, 60, 0, 130, 240}, + {100, 110, 10, 1, 1000, 0, 0, 0, 100}, + {100, 110, 6, 1, 600, 0, 400, 0, 60}, + } + + for _, test := range tests { + test := test + t.Run(fmt.Sprintf("%v", test), func(t *testing.T) { + output := ComputeGasOutputs(test.used, test.limit, baseFee, types.NewInt(test.feeCap), types.NewInt(test.premium)) + i2s := func(i uint64) string { + return fmt.Sprintf("%d", i) + } + assert.Equal(t, i2s(test.BaseFeeBurn), output.BaseFeeBurn.String(), "BaseFeeBurn") + assert.Equal(t, i2s(test.OverEstimationBurn), output.OverEstimationBurn.String(), "OverEstimationBurn") + assert.Equal(t, i2s(test.MinerPenalty), output.MinerPenalty.String(), "MinerPenalty") + assert.Equal(t, i2s(test.MinerTip), output.MinerTip.String(), "MinerTip") + assert.Equal(t, i2s(test.Refund), output.Refund.String(), "Refund") + }) + } + +} diff --git a/chain/vm/vm.go b/chain/vm/vm.go index 6866473e48c..2d1623cd21c 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -7,6 +7,9 @@ import ( "reflect" "time" + bstore "github.com/filecoin-project/lotus/lib/blockstore" + + "github.com/filecoin-project/specs-actors/actors/abi/big" "github.com/filecoin-project/specs-actors/actors/builtin" block "github.com/ipfs/go-block-format" @@ -149,28 +152,40 @@ type VM struct { inv *Invoker rand Rand vc VestedCalculator + baseFee abi.TokenAmount Syscalls SyscallBuilder } -func NewVM(base cid.Cid, height abi.ChainEpoch, r Rand, cbs blockstore.Blockstore, syscalls SyscallBuilder, vestedCalc VestedCalculator) (*VM, error) { - buf := bufbstore.NewBufferedBstore(cbs) +type VMOpts struct { + StateBase cid.Cid + Epoch abi.ChainEpoch + Rand Rand + Bstore bstore.Blockstore + Syscalls SyscallBuilder + VestedCalc VestedCalculator + BaseFee abi.TokenAmount +} + +func NewVM(opts *VMOpts) (*VM, error) { + buf := bufbstore.NewBufferedBstore(opts.Bstore) cst := cbor.NewCborStore(buf) - state, err := state.LoadStateTree(cst, base) + state, err := state.LoadStateTree(cst, opts.StateBase) if err != nil { return nil, err } return &VM{ cstate: state, - base: base, + base: opts.StateBase, cst: cst, buf: buf, - blockHeight: height, + blockHeight: opts.Epoch, inv: NewInvoker(), - rand: r, // TODO: Probably should be a syscall - vc: vestedCalc, - Syscalls: syscalls, + rand: opts.Rand, // TODO: Probably should be a syscall + vc: opts.VestedCalc, + Syscalls: opts.Syscalls, + baseFee: opts.BaseFee, }, nil } @@ -182,6 +197,7 @@ type ApplyRet struct { types.MessageReceipt ActorErr aerrors.ActorError Penalty types.BigInt + MinerTip types.BigInt ExecutionTrace types.ExecutionTrace Duration time.Duration } @@ -277,8 +293,12 @@ func checkMessage(msg *types.Message) error { return xerrors.Errorf("message has negative gas limit") } - if msg.GasPrice == types.EmptyInt { - return xerrors.Errorf("message gas no gas price set") + if msg.GasFeeCap == types.EmptyInt { + return xerrors.Errorf("message fee cap not set") + } + + if msg.GasPremium == types.EmptyInt { + return xerrors.Errorf("message gas premium not set") } if msg.Value == types.EmptyInt { @@ -301,6 +321,7 @@ func (vm *VM) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*Ap ActorErr: actorErr, ExecutionTrace: rt.executionTrace, Penalty: types.NewInt(0), + MinerTip: types.NewInt(0), Duration: time.Since(start), }, actorErr } @@ -333,14 +354,15 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, ExitCode: exitcode.SysErrOutOfGas, GasUsed: 0, }, - Penalty: types.BigMul(msg.GasPrice, types.NewInt(uint64(msgGasCost))), + Penalty: types.BigMul(vm.baseFee, abi.NewTokenAmount(msgGasCost)), Duration: time.Since(start), + MinerTip: big.Zero(), }, nil } st := vm.cstate - minerPenaltyAmount := types.BigMul(msg.GasPrice, types.NewInt(uint64(msgGasCost))) + minerPenaltyAmount := types.BigMul(vm.baseFee, abi.NewTokenAmount(msg.GasLimit)) fromActor, err := st.GetActor(msg.From) // this should never happen, but is currently still exercised by some tests if err != nil { @@ -353,6 +375,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, ActorErr: aerrors.Newf(exitcode.SysErrSenderInvalid, "actor not found: %s", msg.From), Penalty: minerPenaltyAmount, Duration: time.Since(start), + MinerTip: big.Zero(), }, nil } return nil, xerrors.Errorf("failed to look up from actor: %w", err) @@ -368,6 +391,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, ActorErr: aerrors.Newf(exitcode.SysErrSenderInvalid, "send from not account actor: %s", fromActor.Code), Penalty: minerPenaltyAmount, Duration: time.Since(start), + MinerTip: big.Zero(), }, nil } @@ -381,10 +405,11 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, "actor nonce invalid: msg:%d != state:%d", msg.Nonce, fromActor.Nonce), Penalty: minerPenaltyAmount, Duration: time.Since(start), + MinerTip: big.Zero(), }, nil } - gascost := types.BigMul(types.NewInt(uint64(msg.GasLimit)), msg.GasPrice) + gascost := types.BigMul(types.NewInt(uint64(msg.GasLimit)), msg.GasFeeCap) if fromActor.Balance.LessThan(gascost) { return &ApplyRet{ MessageReceipt: types.MessageReceipt{ @@ -395,6 +420,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, "actor balance less than needed: %s < %s", types.FIL(fromActor.Balance), types.FIL(gascost)), Penalty: minerPenaltyAmount, Duration: time.Since(start), + MinerTip: big.Zero(), }, nil } @@ -454,25 +480,25 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, if gasUsed < 0 { gasUsed = 0 } - gasRefund, gasToBurn := ComputeGasOutputs(gasUsed, msg.GasLimit) + gasOutputs := ComputeGasOutputs(gasUsed, msg.GasLimit, vm.baseFee, msg.GasFeeCap, msg.GasPremium) - // refund unused gas - refund := types.BigMul(types.NewInt(uint64(gasRefund)), msg.GasPrice) - if err := vm.transferFromGasHolder(msg.From, gasHolder, refund); err != nil { - return nil, xerrors.Errorf("failed to refund gas") + if err := vm.transferFromGasHolder(builtin.BurntFundsActorAddr, gasHolder, + gasOutputs.BaseFeeBurn); err != nil { + return nil, xerrors.Errorf("failed to burn base fee: %w", err) } - if gasToBurn > 0 { - // burn overallocated gas - burn := types.BigMul(types.NewInt(uint64(gasToBurn)), msg.GasPrice) - if err := vm.transferFromGasHolder(builtin.BurntFundsActorAddr, gasHolder, burn); err != nil { - return nil, xerrors.Errorf("failed to burn over estimated gas") - } + if err := vm.transferFromGasHolder(builtin.RewardActorAddr, gasHolder, gasOutputs.MinerTip); err != nil { + return nil, xerrors.Errorf("failed to give miner gas reward: %w", err) } - gasReward := types.BigMul(msg.GasPrice, types.NewInt(uint64(gasUsed))) - if err := vm.transferFromGasHolder(builtin.RewardActorAddr, gasHolder, gasReward); err != nil { - return nil, xerrors.Errorf("failed to give miner gas reward: %w", err) + if err := vm.transferFromGasHolder(builtin.BurntFundsActorAddr, gasHolder, + gasOutputs.OverEstimationBurn); err != nil { + return nil, xerrors.Errorf("failed to burn overestimation fee: %w", err) + } + + // refund unused gas + if err := vm.transferFromGasHolder(msg.From, gasHolder, gasOutputs.Refund); err != nil { + return nil, xerrors.Errorf("failed to refund gas: %w", err) } if types.BigCmp(types.NewInt(0), gasHolder.Balance) != 0 { @@ -487,7 +513,8 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, }, ActorErr: actorErr, ExecutionTrace: rt.executionTrace, - Penalty: types.NewInt(0), + Penalty: gasOutputs.MinerPenalty, + MinerTip: gasOutputs.MinerTip, Duration: time.Since(start), }, nil } @@ -766,6 +793,10 @@ func (vm *VM) transferFromGasHolder(addr address.Address, gasHolder *types.Actor return xerrors.Errorf("attempted to transfer negative value from gas holder") } + if amt.Equals(big.NewInt(0)) { + return nil + } + return vm.cstate.MutateActor(addr, func(a *types.Actor) error { if err := deductFunds(gasHolder, amt); err != nil { return err diff --git a/cli/chain.go b/cli/chain.go index f373422f793..12f632b36b8 100644 --- a/cli/chain.go +++ b/cli/chain.go @@ -430,22 +430,22 @@ var chainListCmd = &cli.Command{ psum := big.NewInt(0) for _, m := range msgs.BlsMessages { limitSum += m.GasLimit - psum = big.Add(psum, m.GasPrice) + psum = big.Add(psum, m.GasPremium) } for _, m := range msgs.SecpkMessages { limitSum += m.Message.GasLimit - psum = big.Add(psum, m.Message.GasPrice) + psum = big.Add(psum, m.Message.GasPremium) } lenmsgs := len(msgs.BlsMessages) + len(msgs.SecpkMessages) - avgprice := big.Zero() + avgpremium := big.Zero() if lenmsgs > 0 { - avgprice = big.Div(psum, big.NewInt(int64(lenmsgs))) + avgpremium = big.Div(psum, big.NewInt(int64(lenmsgs))) } - fmt.Printf("\t%s: \t%d msgs, gasLimit: %d / %d (%0.2f%%), avgPrice: %s\n", b.Miner, len(msgs.BlsMessages)+len(msgs.SecpkMessages), limitSum, build.BlockGasLimit, 100*float64(limitSum)/float64(build.BlockGasLimit), avgprice) + fmt.Printf("\t%s: \t%d msgs, gasLimit: %d / %d (%0.2f%%), avgPrice: %s\n", b.Miner, len(msgs.BlsMessages)+len(msgs.SecpkMessages), limitSum, build.BlockGasLimit, 100*float64(limitSum)/float64(build.BlockGasLimit), avgpremium) } if i < len(tss)-1 { msgs, err := api.ChainGetParentMessages(ctx, tss[i+1].Blocks()[0].Cid()) @@ -1030,9 +1030,9 @@ var chainGasPriceCmd = &cli.Command{ nb := []int{1, 2, 3, 5, 10, 20, 50, 100, 300} for _, nblocks := range nb { - addr := builtin.SystemActorAddr // TODO: make real when used in GasEstimateGasPrice + addr := builtin.SystemActorAddr // TODO: make real when used in GasEsitmateGasPremium - est, err := api.GasEstimateGasPrice(ctx, uint64(nblocks), addr, 10000, types.EmptyTSK) + est, err := api.GasEsitmateGasPremium(ctx, uint64(nblocks), addr, 10000, types.EmptyTSK) if err != nil { return err } diff --git a/cli/mpool.go b/cli/mpool.go index 169becf455f..dff4a811052 100644 --- a/cli/mpool.go +++ b/cli/mpool.go @@ -246,8 +246,12 @@ var mpoolReplaceCmd = &cli.Command{ Name: "replace", Usage: "replace a message in the mempool", Flags: []cli.Flag{ - &cli.Int64Flag{ - Name: "gas-price", + &cli.StringFlag{ + Name: "gas-feecap", + Usage: "gas feecap for new message", + }, + &cli.StringFlag{ + Name: "gas-premium", Usage: "gas price for new message", }, &cli.Int64Flag{ @@ -304,7 +308,15 @@ var mpoolReplaceCmd = &cli.Command{ msg := found.Message msg.GasLimit = cctx.Int64("gas-limit") - msg.GasPrice = types.NewInt(uint64(cctx.Int64("gas-price"))) + msg.GasPremium, err = types.BigFromString(cctx.String("gas-premium")) + if err != nil { + return fmt.Errorf("parsing gas-premium: %w", err) + } + // TODO: estiamte fee cap here + msg.GasFeeCap, err = types.BigFromString(cctx.String("gas-feecap")) + if err != nil { + return fmt.Errorf("parsing gas-feecap: %w", err) + } smsg, err := api.WalletSignMessage(ctx, msg.From, &msg) if err != nil { diff --git a/cli/send.go b/cli/send.go index a528f355e01..703f95df7b8 100644 --- a/cli/send.go +++ b/cli/send.go @@ -27,10 +27,15 @@ var sendCmd = &cli.Command{ Usage: "optionally specify the account to send funds from", }, &cli.StringFlag{ - Name: "gas-price", + Name: "gas-premium", Usage: "specify gas price to use in AttoFIL", Value: "0", }, + &cli.StringFlag{ + Name: "gas-feecap", + Usage: "specify gas fee cap to use in AttoFIL", + Value: "0", + }, &cli.Int64Flag{ Name: "gas-limit", Usage: "specify gas limit", @@ -99,6 +104,10 @@ var sendCmd = &cli.Command{ if err != nil { return err } + gfc, err := types.BigFromString(cctx.String("gas-feecap")) + if err != nil { + return err + } method := abi.MethodNum(cctx.Uint64("method")) @@ -122,13 +131,14 @@ var sendCmd = &cli.Command{ } msg := &types.Message{ - From: fromAddr, - To: toAddr, - Value: types.BigInt(val), - GasPrice: gp, - GasLimit: cctx.Int64("gas-limit"), - Method: method, - Params: params, + From: fromAddr, + To: toAddr, + Value: types.BigInt(val), + GasPremium: gp, + GasFeeCap: gfc, + GasLimit: cctx.Int64("gas-limit"), + Method: method, + Params: params, } if cctx.Int64("nonce") > 0 { diff --git a/cli/state.go b/cli/state.go index 86c2f314d4c..5f640055482 100644 --- a/cli/state.go +++ b/cli/state.go @@ -5,7 +5,6 @@ import ( "context" "encoding/json" "fmt" - "github.com/multiformats/go-multiaddr" "html/template" "io" "os" @@ -15,6 +14,8 @@ import ( "strings" "time" + "github.com/multiformats/go-multiaddr" + "github.com/ipfs/go-cid" "github.com/libp2p/go-libp2p-core/peer" "github.com/multiformats/go-multihash" @@ -1383,13 +1384,11 @@ var stateCallCmd = &cli.Command{ } ret, err := api.StateCall(ctx, &types.Message{ - From: froma, - To: toa, - Value: types.BigInt(value), - GasLimit: 10000000000, - GasPrice: types.NewInt(0), - Method: abi.MethodNum(method), - Params: params, + From: froma, + To: toa, + Value: types.BigInt(value), + Method: abi.MethodNum(method), + Params: params, }, ts.Key()) if err != nil { return fmt.Errorf("state call failed: %s", err) diff --git a/cmd/chain-noise/main.go b/cmd/chain-noise/main.go index d429a8d594b..a8ce8a4fa5b 100644 --- a/cmd/chain-noise/main.go +++ b/cmd/chain-noise/main.go @@ -74,11 +74,9 @@ func sendSmallFundsTxs(ctx context.Context, api api.FullNode, from address.Addre select { case <-tick.C: msg := &types.Message{ - From: from, - To: sendSet[rand.Intn(20)], - Value: types.NewInt(1), - GasLimit: 0, - GasPrice: types.NewInt(0), + From: from, + To: sendSet[rand.Intn(20)], + Value: types.NewInt(1), } smsg, err := api.MpoolPushMessage(ctx, msg) diff --git a/cmd/lotus-chainwatch/processor/messages.go b/cmd/lotus-chainwatch/processor/messages.go index 65ad153d869..ac3c61e3d76 100644 --- a/cmd/lotus-chainwatch/processor/messages.go +++ b/cmd/lotus-chainwatch/processor/messages.go @@ -30,8 +30,9 @@ create table if not exists messages "to" text not null, nonce bigint not null, value text not null, - gasprice bigint not null, - gaslimit bigint not null, + gas_fee_cap bigint not null, + gas_premium bigint not null, + gas_limit bigint not null, method bigint, params bytea ); @@ -219,7 +220,7 @@ create temp table msgs (like messages excluding constraints) on commit drop; return xerrors.Errorf("prep temp: %w", err) } - stmt, err := tx.Prepare(`copy msgs (cid, "from", "to", nonce, "value", gasprice, gaslimit, method, params) from stdin `) + stmt, err := tx.Prepare(`copy msgs (cid, "from", "to", nonce, "value", gas_premium, gas_fee_cap, gas_limit, method, params) from stdin `) if err != nil { return err } @@ -231,7 +232,8 @@ create temp table msgs (like messages excluding constraints) on commit drop; m.To.String(), m.Nonce, m.Value.String(), - m.GasPrice.String(), + m.GasPremium.String(), + m.GasFeeCap.String(), m.GasLimit, m.Method, m.Params, diff --git a/cmd/lotus-fountain/main.go b/cmd/lotus-fountain/main.go index 9b4dcd14641..7cbeec90e02 100644 --- a/cmd/lotus-fountain/main.go +++ b/cmd/lotus-fountain/main.go @@ -279,9 +279,6 @@ func (h *handler) send(w http.ResponseWriter, r *http.Request) { Value: types.BigInt(h.sendPerRequest), From: h.from, To: to, - - GasPrice: types.NewInt(0), - GasLimit: 0, }) if err != nil { w.WriteHeader(400) @@ -353,9 +350,6 @@ func (h *handler) mkminer(w http.ResponseWriter, r *http.Request) { Value: types.BigInt(h.sendPerRequest), From: h.from, To: owner, - - GasPrice: types.NewInt(0), - GasLimit: 0, }) if err != nil { w.WriteHeader(400) @@ -390,9 +384,6 @@ func (h *handler) mkminer(w http.ResponseWriter, r *http.Request) { Method: builtin.MethodsPower.CreateMiner, Params: params, - - GasLimit: 0, - GasPrice: types.NewInt(0), } signed, err := h.api.MpoolPushMessage(r.Context(), createStorageMinerMsg) diff --git a/cmd/lotus-shed/mpool.go b/cmd/lotus-shed/mpool.go index b0678a5288c..c7a2e9b391c 100644 --- a/cmd/lotus-shed/mpool.go +++ b/cmd/lotus-shed/mpool.go @@ -51,7 +51,7 @@ var minerSelectMsgsCmd = &cli.Command{ to = "..." + to[len(to)-8:] } - fmt.Printf("%d: %s -> %s, method %d, gasPrice %s, gasLimit %d, val %s\n", i, from, to, f.Message.Method, f.Message.GasPrice, f.Message.GasLimit, types.FIL(f.Message.Value)) + fmt.Printf("%d: %s -> %s, method %d, gasFeecap %s, gasPremium %s, gasLimit %d, val %s\n", i, from, to, f.Message.Method, f.Message.GasFeeCap, f.Message.GasPremium, f.Message.GasLimit, types.FIL(f.Message.Value)) totalGas += f.Message.GasLimit } diff --git a/cmd/lotus-shed/verifreg.go b/cmd/lotus-shed/verifreg.go index b8a94416522..e8e600fcb97 100644 --- a/cmd/lotus-shed/verifreg.go +++ b/cmd/lotus-shed/verifreg.go @@ -143,12 +143,10 @@ var verifRegVerifyClientCmd = &cli.Command{ ctx := lcli.ReqContext(cctx) msg := &types.Message{ - To: builtin.VerifiedRegistryActorAddr, - From: fromk, - Method: builtin.MethodsVerifiedRegistry.AddVerifiedClient, - GasPrice: types.NewInt(1), - GasLimit: 0, - Params: params, + To: builtin.VerifiedRegistryActorAddr, + From: fromk, + Method: builtin.MethodsVerifiedRegistry.AddVerifiedClient, + Params: params, } smsg, err := api.MpoolPushMessage(ctx, msg) diff --git a/cmd/lotus-storage-miner/init.go b/cmd/lotus-storage-miner/init.go index a1d75ac1445..5007a712044 100644 --- a/cmd/lotus-storage-miner/init.go +++ b/cmd/lotus-storage-miner/init.go @@ -102,8 +102,8 @@ var initCmd = &cli.Command{ Usage: "don't use storageminer repo for sector storage", }, &cli.StringFlag{ - Name: "gas-price", - Usage: "set gas price for initialization messages in AttoFIL", + Name: "gas-premium", + Usage: "set gas premium for initialization messages in AttoFIL", Value: "0", }, }, @@ -116,7 +116,7 @@ var initCmd = &cli.Command{ } ssize := abi.SectorSize(sectorSizeInt) - gasPrice, err := types.BigFromString(cctx.String("gas-price")) + gasPrice, err := types.BigFromString(cctx.String("gas-premium")) if err != nil { return xerrors.Errorf("failed to parse gas-price flag: %s", err) } @@ -552,13 +552,12 @@ func configureStorageMiner(ctx context.Context, api lapi.FullNode, addr address. } msg := &types.Message{ - To: addr, - From: mi.Worker, - Method: builtin.MethodsMiner.ChangePeerID, - Params: enc, - Value: types.NewInt(0), - GasPrice: gasPrice, - GasLimit: 0, + To: addr, + From: mi.Worker, + Method: builtin.MethodsMiner.ChangePeerID, + Params: enc, + Value: types.NewInt(0), + GasPremium: gasPrice, } smsg, err := api.MpoolPushMessage(ctx, msg) @@ -637,8 +636,8 @@ func createStorageMiner(ctx context.Context, api lapi.FullNode, peerid peer.ID, Method: builtin.MethodsPower.CreateMiner, Params: params, - GasLimit: 0, - GasPrice: gasPrice, + GasLimit: 0, + GasPremium: gasPrice, } signed, err := api.MpoolPushMessage(ctx, createStorageMinerMsg) diff --git a/extern/serialization-vectors b/extern/serialization-vectors index 0cef69d4819..5bfb928910b 160000 --- a/extern/serialization-vectors +++ b/extern/serialization-vectors @@ -1 +1 @@ -Subproject commit 0cef69d481950d24f0e26e2698e585a8bf203913 +Subproject commit 5bfb928910b01ac8b940a693af2884f7f8276211 diff --git a/go.mod b/go.mod index 7cf743e40d2..c58dcffcdce 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/drand/drand v1.0.3-0.20200714175734-29705eaf09d4 github.com/drand/kyber v1.1.1 github.com/fatih/color v1.8.0 - github.com/filecoin-project/chain-validation v0.0.6-0.20200805210314-9844c5869f53 + github.com/filecoin-project/chain-validation v0.0.6-0.20200807023228-a084d8d9919e github.com/filecoin-project/filecoin-ffi v0.30.4-0.20200716204036-cddc56607e1d github.com/filecoin-project/go-address v0.0.2-0.20200504173055-8b6f2fb2b3ef github.com/filecoin-project/go-amt-ipld/v2 v2.1.1-0.20200731171407-e559a0579161 // indirect diff --git a/go.sum b/go.sum index fc68419cb00..b704676ab30 100644 --- a/go.sum +++ b/go.sum @@ -215,8 +215,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.8.0 h1:5bzFgL+oy7JITMTxUPJ00n7VxmYd/PdMp5mHFX40/RY= github.com/fatih/color v1.8.0/go.mod h1:3l45GVGkyrnYNl9HoIjnp2NnNWvh6hLAqD8yTfGjnw8= github.com/fd/go-nat v1.0.0/go.mod h1:BTBu/CKvMmOMUPkKVef1pngt2WFH/lg7E6yQnulfp6E= -github.com/filecoin-project/chain-validation v0.0.6-0.20200805210314-9844c5869f53 h1:XRrMct6lOMjVe11dOnurq6vsLayQQyotFJi0YW4Wp8E= -github.com/filecoin-project/chain-validation v0.0.6-0.20200805210314-9844c5869f53/go.mod h1:JICNIbIEZ+qNJ/PQlHxjei6SaeBbW+yV2r4BcShsXfI= +github.com/filecoin-project/chain-validation v0.0.6-0.20200807023228-a084d8d9919e h1:qHdxD1Of7i6yyFezaKNWk36CBNWGRHszp44oTq4oDQ0= +github.com/filecoin-project/chain-validation v0.0.6-0.20200807023228-a084d8d9919e/go.mod h1:jtOqSVob11urlSIM8Gw3XIrqmiqb+fBr7ZBIbOhzGPI= github.com/filecoin-project/go-address v0.0.0-20200107215422-da8eea2842b5/go.mod h1:SAOwJoakQ8EPjwNIsiakIQKsoKdkcbx8U3IapgCg9R0= github.com/filecoin-project/go-address v0.0.2-0.20200218010043-eb9bb40ed5be/go.mod h1:SAOwJoakQ8EPjwNIsiakIQKsoKdkcbx8U3IapgCg9R0= github.com/filecoin-project/go-address v0.0.2-0.20200504173055-8b6f2fb2b3ef h1:Wi5E+P1QfHP8IF27eUiTx5vYfqQZwfPxzq3oFEq8w8U= @@ -270,7 +270,6 @@ github.com/filecoin-project/specs-actors v0.3.0/go.mod h1:nQYnFbQ7Y0bHZyq6HDEuVl github.com/filecoin-project/specs-actors v0.6.1/go.mod h1:dRdy3cURykh2R8O/DKqy8olScl70rmIS7GrB4hB1IDY= github.com/filecoin-project/specs-actors v0.7.3-0.20200716231407-60a2ae96d2e6/go.mod h1:JOMUa7EijvpOO4ofD1yeHNmqohkmmnhTvz/IpB6so4c= github.com/filecoin-project/specs-actors v0.8.2/go.mod h1:Q3ACV5kBLvqPaYbthc/J1lGMJ5OwogmD9pzdtPRMdCw= -github.com/filecoin-project/specs-actors v0.8.5/go.mod h1:Q3ACV5kBLvqPaYbthc/J1lGMJ5OwogmD9pzdtPRMdCw= github.com/filecoin-project/specs-actors v0.8.7-0.20200805174427-9d42fb163883 h1:/LOix6EwCWshEuufhM8gAuCqP5jPWVMqPZn8HUQCw3Y= github.com/filecoin-project/specs-actors v0.8.7-0.20200805174427-9d42fb163883/go.mod h1:QRihI/fadrhWzt7HH6mT32upOdDFpSYCFnr3JEI1L50= github.com/filecoin-project/specs-storage v0.1.1-0.20200622113353-88a9704877ea h1:iixjULRQFPn7Q9KlIqfwLJnlAXO10bbkI+xy5GKGdLY= diff --git a/markets/storageadapter/client.go b/markets/storageadapter/client.go index e9da4e53224..a97bcf75879 100644 --- a/markets/storageadapter/client.go +++ b/markets/storageadapter/client.go @@ -123,12 +123,10 @@ func (c *ClientNodeAdapter) ListClientDeals(ctx context.Context, addr address.Ad func (c *ClientNodeAdapter) AddFunds(ctx context.Context, addr address.Address, amount abi.TokenAmount) (cid.Cid, error) { // (Provider Node API) smsg, err := c.MpoolPushMessage(ctx, &types.Message{ - To: builtin.StorageMarketActorAddr, - From: addr, - Value: amount, - GasPrice: types.NewInt(0), - GasLimit: 0, - Method: builtin.MethodsMarket.AddBalance, + To: builtin.StorageMarketActorAddr, + From: addr, + Value: amount, + Method: builtin.MethodsMarket.AddBalance, }) if err != nil { return cid.Undef, err diff --git a/markets/storageadapter/provider.go b/markets/storageadapter/provider.go index 30a614a6041..3c19c84233e 100644 --- a/markets/storageadapter/provider.go +++ b/markets/storageadapter/provider.go @@ -74,13 +74,11 @@ func (n *ProviderNodeAdapter) PublishDeals(ctx context.Context, deal storagemark // TODO: We may want this to happen after fetching data smsg, err := n.MpoolPushMessage(ctx, &types.Message{ - To: builtin.StorageMarketActorAddr, - From: mi.Worker, - Value: types.NewInt(0), - GasPrice: types.NewInt(0), - GasLimit: 0, - Method: builtin.MethodsMarket.PublishStorageDeals, - Params: params, + To: builtin.StorageMarketActorAddr, + From: mi.Worker, + Value: types.NewInt(0), + Method: builtin.MethodsMarket.PublishStorageDeals, + Params: params, }) if err != nil { return cid.Undef, err @@ -175,12 +173,10 @@ func (n *ProviderNodeAdapter) EnsureFunds(ctx context.Context, addr, wallet addr func (n *ProviderNodeAdapter) AddFunds(ctx context.Context, addr address.Address, amount abi.TokenAmount) (cid.Cid, error) { // (Provider Node API) smsg, err := n.MpoolPushMessage(ctx, &types.Message{ - To: builtin.StorageMarketActorAddr, - From: addr, - Value: amount, - GasPrice: types.NewInt(0), - GasLimit: 0, - Method: builtin.MethodsMarket.AddBalance, + To: builtin.StorageMarketActorAddr, + From: addr, + Value: amount, + Method: builtin.MethodsMarket.AddBalance, }) if err != nil { return cid.Undef, err diff --git a/node/hello/cbor_gen.go b/node/hello/cbor_gen.go index 5400c3f8a61..b9138672408 100644 --- a/node/hello/cbor_gen.go +++ b/node/hello/cbor_gen.go @@ -7,7 +7,7 @@ import ( "io" "github.com/filecoin-project/specs-actors/actors/abi" - "github.com/ipfs/go-cid" + cid "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" xerrors "golang.org/x/xerrors" ) diff --git a/node/impl/full.go b/node/impl/full.go index 47f09bfd3fc..feeb2974572 100644 --- a/node/impl/full.go +++ b/node/impl/full.go @@ -1,13 +1,9 @@ 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" @@ -32,10 +28,4 @@ type FullNodeAPI struct { full.BeaconAPI } -// 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{} diff --git a/node/impl/full/gas.go b/node/impl/full/gas.go index 5c556302c4d..b7f443c0af6 100644 --- a/node/impl/full/gas.go +++ b/node/impl/full/gas.go @@ -2,6 +2,7 @@ package full import ( "context" + "math" "sort" "github.com/filecoin-project/lotus/build" @@ -26,9 +27,22 @@ type GasAPI struct { } const MinGasPrice = 1 +const BaseFeeEstimNBlocks = 20 -func (a *GasAPI) GasEstimateGasPrice(ctx context.Context, nblocksincl uint64, - sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) { +func (a *GasAPI) GasEstimateFeeCap(ctx context.Context, maxqueueblks int64, + tsk types.TipSetKey) (types.BigInt, error) { + ts := a.Chain.GetHeaviestTipSet() + + parentBaseFee := ts.Blocks()[0].ParentBaseFee + increaseFactor := math.Pow(1+float64(1/build.BaseFeeMaxChangeDenom), BaseFeeEstimNBlocks) + + out := types.BigMul(parentBaseFee, types.NewInt(uint64(increaseFactor*(1<<8)))) + out = types.BigDiv(out, types.NewInt(1<<8)) + return out, nil +} + +func (a *GasAPI) GasEsitmateGasPremium(ctx context.Context, nblocksincl uint64, + sender address.Address, gaslimit int64, _ types.TipSetKey) (types.BigInt, error) { if nblocksincl == 0 { nblocksincl = 1 @@ -68,7 +82,7 @@ func (a *GasAPI) GasEstimateGasPrice(ctx context.Context, nblocksincl uint64, } prices = append(prices, gasMeta{ - price: msg.VMMessage().GasPrice, + price: msg.VMMessage().GasPremium, used: r.GasUsed, }) gasUsed += r.GasUsed @@ -111,7 +125,8 @@ func (a *GasAPI) GasEstimateGasLimit(ctx context.Context, msgIn *types.Message, msg := *msgIn msg.GasLimit = build.BlockGasLimit - msg.GasPrice = types.NewInt(1) + msg.GasFeeCap = types.NewInt(build.MinimumBaseFee + 1) + msg.GasPremium = types.NewInt(1) currTs := a.Chain.GetHeaviestTipSet() fromA, err := a.Stmgr.ResolveToKeyAddress(ctx, msgIn.From, currTs) diff --git a/node/impl/full/mpool.go b/node/impl/full/mpool.go index df4428801dd..d1976b1e9cb 100644 --- a/node/impl/full/mpool.go +++ b/node/impl/full/mpool.go @@ -111,12 +111,20 @@ func (a *MpoolAPI) MpoolPushMessage(ctx context.Context, msg *types.Message) (*t msg.GasLimit = int64(float64(gasLimit) * GasMargin) } - if msg.GasPrice == types.EmptyInt || types.BigCmp(msg.GasPrice, types.NewInt(0)) == 0 { - gasPrice, err := a.GasEstimateGasPrice(ctx, 2, msg.From, msg.GasLimit, types.TipSetKey{}) + if msg.GasPremium == types.EmptyInt || types.BigCmp(msg.GasPremium, types.NewInt(0)) == 0 { + gasPremium, err := a.GasEsitmateGasPremium(ctx, 2, msg.From, msg.GasLimit, types.TipSetKey{}) if err != nil { return nil, xerrors.Errorf("estimating gas price: %w", err) } - msg.GasPrice = gasPrice + msg.GasPremium = gasPremium + } + + if msg.GasFeeCap == types.EmptyInt || types.BigCmp(msg.GasFeeCap, types.NewInt(0)) == 0 { + feeCap, err := a.GasEstimateFeeCap(ctx, 20, types.EmptyTSK) + if err != nil { + return nil, xerrors.Errorf("estimating fee cap: %w", err) + } + msg.GasFeeCap = feeCap } return a.Mpool.PushWithNonce(ctx, msg.From, func(from address.Address, nonce uint64) (*types.SignedMessage, error) { diff --git a/node/impl/full/multisig.go b/node/impl/full/multisig.go index 0ec8b68c8c3..deb0d9856cb 100644 --- a/node/impl/full/multisig.go +++ b/node/impl/full/multisig.go @@ -45,10 +45,6 @@ func (a *MsigAPI) MsigCreate(ctx context.Context, req uint64, addrs []address.Ad return cid.Undef, xerrors.Errorf("must provide source address") } - if gp == types.EmptyInt { - gp = types.NewInt(0) - } - // Set up constructor parameters for multisig msigParams := &samsig.ConstructorParams{ Signers: addrs, @@ -74,12 +70,11 @@ func (a *MsigAPI) MsigCreate(ctx context.Context, req uint64, addrs []address.Ad // now we create the message to send this with msg := types.Message{ - To: builtin.InitActorAddr, - From: src, - Method: builtin.MethodsInit.Exec, - Params: enc, - Value: val, - GasPrice: gp, + To: builtin.InitActorAddr, + From: src, + Method: builtin.MethodsInit.Exec, + Params: enc, + Value: val, } // send the message out to the network diff --git a/node/impl/full/state.go b/node/impl/full/state.go index f6b9fe9db78..3b2953e025c 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -968,12 +968,10 @@ func (a *StateAPI) StateMinerPreCommitDepositForPower(ctx context.Context, maddr } ret, err := a.StateManager.Call(ctx, &types.Message{ - From: maddr, - To: builtin.StorageMarketActorAddr, - Method: builtin.MethodsMarket.VerifyDealsForActivation, - GasLimit: 0, - GasPrice: types.NewInt(0), - Params: params, + From: maddr, + To: builtin.StorageMarketActorAddr, + Method: builtin.MethodsMarket.VerifyDealsForActivation, + Params: params, }, ts) if err != nil { return types.EmptyInt, err @@ -1048,12 +1046,10 @@ func (a *StateAPI) StateMinerInitialPledgeCollateral(ctx context.Context, maddr } ret, err := a.StateManager.Call(ctx, &types.Message{ - From: maddr, - To: builtin.StorageMarketActorAddr, - Method: builtin.MethodsMarket.VerifyDealsForActivation, - GasLimit: 0, - GasPrice: types.NewInt(0), - Params: params, + From: maddr, + To: builtin.StorageMarketActorAddr, + Method: builtin.MethodsMarket.VerifyDealsForActivation, + Params: params, }, ts) if err != nil { return types.EmptyInt, err diff --git a/node/node_test.go b/node/node_test.go index 5d50c73dd17..a0fa3461e9f 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -103,13 +103,11 @@ func testStorageNode(ctx context.Context, t *testing.T, waddr address.Address, a require.NoError(t, err) msg := &types.Message{ - To: act, - From: waddr, - Method: builtin.MethodsMiner.ChangePeerID, - Params: enc, - Value: types.NewInt(0), - GasPrice: types.NewInt(0), - GasLimit: 0, + To: act, + From: waddr, + Method: builtin.MethodsMiner.ChangePeerID, + Params: enc, + Value: types.NewInt(0), } _, err = tnd.MpoolPushMessage(ctx, msg) diff --git a/paychmgr/simple.go b/paychmgr/simple.go index d9f9b0682f7..a5f190841d7 100644 --- a/paychmgr/simple.go +++ b/paychmgr/simple.go @@ -241,13 +241,11 @@ func (ca *channelAccessor) createPaych(ctx context.Context, from, to address.Add } msg := &types.Message{ - To: builtin.InitActorAddr, - From: from, - Value: amt, - Method: builtin.MethodsInit.Exec, - Params: enc, - GasLimit: 0, - GasPrice: types.NewInt(0), + To: builtin.InitActorAddr, + From: from, + Value: amt, + Method: builtin.MethodsInit.Exec, + Params: enc, } smsg, err := ca.api.MpoolPushMessage(ctx, msg) @@ -323,12 +321,10 @@ func (ca *channelAccessor) waitPaychCreateMsg(channelID string, mcid cid.Cid) er // addFunds sends a message to add funds to the channel and returns the message cid func (ca *channelAccessor) addFunds(ctx context.Context, channelInfo *ChannelInfo, amt types.BigInt, cb onCompleteFn) (*cid.Cid, error) { msg := &types.Message{ - To: *channelInfo.Channel, - From: channelInfo.Control, - Value: amt, - Method: 0, - GasLimit: 0, - GasPrice: types.NewInt(0), + To: *channelInfo.Channel, + From: channelInfo.Control, + Value: amt, + Method: 0, } smsg, err := ca.api.MpoolPushMessage(ctx, msg) diff --git a/storage/adapter_storage_miner.go b/storage/adapter_storage_miner.go index b74b7f46d38..119df69bc38 100644 --- a/storage/adapter_storage_miner.go +++ b/storage/adapter_storage_miner.go @@ -237,15 +237,16 @@ func (s SealingAPIAdapter) StateMarketStorageDeal(ctx context.Context, dealID ab return deal.Proposal, nil } +//TODO: rename/remove gasPrice and gasLimit func (s SealingAPIAdapter) SendMsg(ctx context.Context, from, to address.Address, method abi.MethodNum, value, gasPrice big.Int, gasLimit int64, params []byte) (cid.Cid, error) { msg := types.Message{ - To: to, - From: from, - Value: value, - GasPrice: gasPrice, - GasLimit: gasLimit, - Method: method, - Params: params, + To: to, + From: from, + Value: value, + GasPremium: gasPrice, + GasLimit: gasLimit, + Method: method, + Params: params, } smsg, err := s.delegate.MpoolPushMessage(ctx, &msg) diff --git a/tools/stats/metrics.go b/tools/stats/metrics.go index 52ef0c660f8..e4e557e59d9 100644 --- a/tools/stats/metrics.go +++ b/tools/stats/metrics.go @@ -281,7 +281,10 @@ func RecordTipsetMessagesPoints(ctx context.Context, api api.FullNode, pl *Point msgn := make(map[msgTag][]cid.Cid) for i, msg := range msgs { - p := NewPoint("chain.message_gasprice", msg.Message.GasPrice.Int64()) + // FIXME: use float so this doesn't overflow + p := NewPoint("chain.message_gaspremium", msg.Message.GasPremium.Int64()) + pl.AddPoint(p) + p = NewPoint("chain.message_gasfeecap", msg.Message.GasFeeCap.Int64()) pl.AddPoint(p) bs, err := msg.Message.Serialize()