filecoin lotus 转账fil流程和gas计算

文章目录

  • 转账流程 和 gas计算
    • 完整信息转账
      • 签名
        • 签名过程
      • 消息广播
        • 广播过程
    • 自动计算gas发送交易
    • GAS
      • 计算gasLimit,gasPreium,gasFeeCap
        • GasEstimateGasLimit
          • 调用过程
          • 计算相关常量
        • GasEstimateGasPremium
        • GasEstimateFeeCap
    • 区块浏览器中展现的值
      • Base Fee Burn 和 Over Estimation Burn 计算方式
        • ComputeGasOverestimationBurn(gasUsed, gasLimit)

转账流程 和 gas计算

从cli命令send中, 有两种转账方式

  1. 指定所有参数,如gas,nonce
  2. 仅指定接收者和金额

完整信息转账

路径cli/send.go

message内容

msg := &types.Message{
			From:       fromAddr,
			To:         toAddr,
			Value:      types.BigInt(val),
			GasPremium: gp,
			GasFeeCap:  gfc,
			GasLimit:   cctx.Int64("gas-limit"),
			Method:     method,//0
			Params:     params,//可为nil
		}

签名

        sm, err := api.WalletSignMessage(ctx, fromAddr, msg)
			if err != nil {
				return err
			}

签名过程

路径node/impl/full/wallet.go

//K -> fromAddr
func (a *WalletAPI) WalletSignMessage(ctx context.Context, k address.Address, msg *types.Message) (*types.SignedMessage, error) {
	mcid := msg.Cid()//msg序列化,最后得到一个block
	//内部对msg序列化,得到[]byte,再针对这个数据进行sum,得到一个cid
	//返回一个block,内部数据就是[]byte,和cid

	sig, err := a.WalletSign(ctx, k, mcid.Bytes())//注意传输的实际就是msg序列化后的数据
	if err != nil {
		return nil, xerrors.Errorf("failed to sign message: %w", err)
	}

	return &types.SignedMessage{
		Message:   *msg,//msg原始数据
		Signature: *sig,//通过私钥对数据进行签名后的数据(签名类型,签名后的[]byte  (长度65) )
	}, nil
}

a.WalletSign

func (a *WalletAPI) WalletSign(ctx context.Context, k address.Address, msg []byte) (*crypto.Signature, error) {
	keyAddr, err := a.StateManager.ResolveToKeyAddress(ctx, k, nil)//内部应该是验证地址合法性,BLS/SECP256K1类型地址,禁止Actor类型
	if err != nil {
		return nil, xerrors.Errorf("failed to resolve ID address: %w", keyAddr)
	}
	return a.Wallet.Sign(ctx, keyAddr, msg)
}

a.Wallet.Sign
路径 chain/wallet/wallet.go

func (w *Wallet) Sign(ctx context.Context, addr address.Address, msg []byte) (*crypto.Signature, error) {
	ki, err := w.findKey(addr)//获取一个地址的公私钥
	if err != nil {
		return nil, err
	}
	if ki == nil {
		return nil, xerrors.Errorf("signing using key '%s': %w", addr.String(), types.ErrKeyInfoNotFound)
	}
    //通过私钥对msg进行签名
	return sigs.Sign(ActSigType(ki.Type), ki.PrivateKey, msg)
}

sigs.Sign
路径lib/sigs/sigs.go

// Sign takes in signature type, private key and message. Returns a signature for that message.
// Valid sigTypes are: "secp256k1" and "bls"
func Sign(sigType crypto.SigType, privkey []byte, msg []byte) (*crypto.Signature, error) {
	sv, ok := sigs[sigType]
	if !ok {
		return nil, fmt.Errorf("cannot sign message with signature of unsupported type: %v", sigType)
	}

	sb, err := sv.Sign(privkey, msg)
	if err != nil {
		return nil, err
	}
	return &crypto.Signature{
		Type: sigType,
		Data: sb,
	}, nil
}

sign 对应加密方式签名
路径

  • lib/sigs/secp/init.go
  • lib/sigs/secp/init.go
func (secpSigner) Sign(pk []byte, msg []byte) ([]byte, error) {
	b2sum := blake2b.Sum256(msg)
	sig, err := crypto.Sign(pk, b2sum[:])
	if err != nil {
		return nil, err
	}

	return sig, nil
}

过程完毕

消息广播

_, err = api.MpoolPush(ctx, sm)//
//sm的结构
type SignedMessage struct {
	Message   Message//原始信息
	Signature crypto.Signature//签名类型,使用私钥签名后的数据
}

广播过程

api.MpoolPush
路径 node/impl/full/mpool.go

func (a *MpoolAPI) MpoolPush(ctx context.Context, smsg *types.SignedMessage) (cid.Cid, error) {
	return a.Mpool.Push(smsg)
}

a.Mpool.Push
路径chain/messagepool/messagepool.go

func (mp *MessagePool) Push(m *types.SignedMessage) (cid.Cid, error) {
	err := mp.checkMessage(m)
	if err != nil {
		return cid.Undef, err
	}

	// serialize push access to reduce lock contention
	mp.addSema <- struct{}{}
	defer func() {
		<-mp.addSema
	}()

	mp.curTsLk.Lock()
	curTs := mp.curTs
	epoch := curTs.Height()
	mp.curTsLk.Unlock()
	if err := mp.verifyMsgBeforePush(m, epoch); err != nil {
		return cid.Undef, err
	}

	msgb, err := m.Serialize()
	if err != nil {
		return cid.Undef, err
	}

	mp.curTsLk.Lock()
	if mp.curTs != curTs {
		mp.curTsLk.Unlock()
		return cid.Undef, ErrTryAgain
	}

	if err := mp.addTs(m, mp.curTs); err != nil {
		mp.curTsLk.Unlock()
		return cid.Undef, err
	}
	mp.curTsLk.Unlock()

	mp.lk.Lock()
	if err := mp.addLocal(m, msgb); err != nil {
		mp.lk.Unlock()
		return cid.Undef, err
	}
	mp.lk.Unlock()

	return m.Cid(), mp.api.PubSubPublish(build.MessagesTopic(mp.netName), msgb)
}

mp.verifyMsgBeforePush

func (mp *MessagePool) verifyMsgBeforePush(m *types.SignedMessage, epoch abi.ChainEpoch) error {
	minGas := vm.PricelistByEpoch(epoch).OnChainMessage(m.ChainLength())//获取最小gas
	//gas计算,另外说明
    //验证消息参数是否正确,以及验证gas是否足够
	if err := m.VMMessage().ValidForBlockInclusion(minGas.Total()); err != nil {
		return xerrors.Errorf("message will not be included in a block: %w", err)
	}
	return nil
}

mp.Add(m)

func (mp *MessagePool) Add(m *types.SignedMessage) error {
	// big messages are bad, anti DOS
	if m.Size() > 32*1024 {
		return xerrors.Errorf("mpool message too large (%dB): %w", m.Size(), ErrMessageTooBig)
	}

	if m.Message.To == address.Undef {
		return ErrInvalidToAddr
	}
    //2_000_000_000 最大转账金额
	if !m.Message.Value.LessThan(types.TotalFilecoinInt) {
		return ErrMessageValueTooHigh
	}
    //验证签名, 内部是通过签名后的信息,和msg的原始序列化信息,得出公钥,
    //通过公钥导出地址,对比from是否一样
	if err := mp.VerifyMsgSig(m); err != nil {
		log.Warnf("mpooladd signature verification failed: %s", err)
		return err
	}

	// serialize push access to reduce lock contention
	mp.addSema <- struct{}{}
	defer func() {
		<-mp.addSema
	}()

	mp.curTsLk.Lock()
	defer mp.curTsLk.Unlock()
	return mp.addTs(m, mp.curTs)
}

mp.addTs

func (mp *MessagePool) addTs(m *types.SignedMessage, curTs *types.TipSet) error {
    //内部实现暂忽略,目的是获取到最小nonce
	snonce, err := mp.getStateNonce(m.Message.From, curTs)
	if err != nil {
		return xerrors.Errorf("failed to look up actor state nonce: %s: %w", err, ErrBroadcastAnyway)
	}
    
	if snonce > m.Message.Nonce {
		return xerrors.Errorf("minimum expected nonce is %d: %w", snonce, ErrNonceTooLow)
	}
    //获取余额
	balance, err := mp.getStateBalance(m.Message.From, curTs)
	if err != nil {
		return xerrors.Errorf("failed to check sender balance: %s: %w", err, ErrBroadcastAnyway)
	}
    //内部是capCap*limit,少了value判断
	if balance.LessThan(m.Message.RequiredFunds()) {
		return xerrors.Errorf("not enough funds (required: %s, balance: %s): %w", types.FIL(m.Message.RequiredFunds()), types.FIL(balance), ErrNotEnoughFunds)
	}

	mp.lk.Lock()
	defer mp.lk.Unlock()

	return mp.addLocked(m)
}

mp.addLocked(m)

func (mp *MessagePool) addLocked(m *types.SignedMessage) error {
	log.Debugf("mpooladd: %s %d", m.Message.From, m.Message.Nonce)
	if m.Signature.Type == crypto.SigTypeBLS {
		mp.blsSigCache.Add(m.Cid(), m.Signature)
	}

	if m.Message.GasLimit > build.BlockGasLimit {
		return xerrors.Errorf("given message has too high of a gas limit")
	}
    //一次put signMsg,一次Msg,都实现了ToStorageBlock,本地缓存
	if _, err := mp.api.PutMessage(m); err != nil {
		log.Warnf("mpooladd cs.PutMessage failed: %s", err)
		return err
	}

	if _, err := mp.api.PutMessage(&m.Message); err != nil {
		log.Warnf("mpooladd cs.PutMessage failed: %s", err)
		return err
	}

	mset, ok := mp.pending[m.Message.From]
	if !ok {
		mset = newMsgSet()
		mp.pending[m.Message.From] = mset
	}
    //添加消息池子map,
	incr, err := mset.add(m, mp)
	if err != nil {
		log.Info(err)
		return err
	}

	if incr {
	//如果是新增,池子+1
		mp.currentSize++
		if mp.currentSize > mp.cfg.SizeLimitHigh {//如果交易池msg量大于配置数量,发送减少信号
		//注意内部也有一个冷却时间,冷却时间外会实际出发减少操作
		//查看runLoop()
			// send signal to prune messages if it hasnt already been sent
			select {
			case mp.pruneTrigger <- struct{}{}:
			default:
			}
		}
	}

	mp.changes.Pub(api.MpoolUpdate{
		Type:    api.MpoolAdd,
		Message: m,
	}, localUpdates)
	return nil
}

mset.add()

func (ms *msgSet) add(m *types.SignedMessage, mp *MessagePool) (bool, error) {
    //如果本地是0,或者大于本地记录,则将下一次的默认nonce自增1
	if len(ms.msgs) == 0 || m.Message.Nonce >= ms.nextNonce {
		ms.nextNonce = m.Message.Nonce + 1
	}
	exms, has := ms.msgs[m.Message.Nonce]
	if has {
	    //如果存在同nonce的交易在交易池
	    //且两次的交易信息不一样,需要判断能否覆盖,需要手续费大于一定值
		if m.Cid() != exms.Cid() {
			// check if RBF passes //=>ReplaceByFeeRatioDefault 检查覆盖手续费是否足够
			minPrice := exms.Message.GasPremium
			//rbfNumBig、rbfNumBig都是常量
			//min + min/(256*(1.25-1))/256   //256*0.25=64
			//min = min + min/64/256
			minPrice = types.BigAdd(minPrice, types.BigDiv(types.BigMul(minPrice, rbfNumBig), rbfNumBig))
			//再基础上+1, 必须大于等于计算得来的gasPrice
			minPrice = types.BigAdd(minPrice, types.NewInt(1))
			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 GasPremium to %s from %s to trigger replace by fee: %w",
					m.Message.From, m.Message.Nonce, minPrice, m.Message.GasPremium,
					ErrRBFTooLowPremium)
			}
		}
	}
	
	ms.msgs[m.Message.Nonce] = m

	return !has, nil
}

mp.addLocal(m, msgb)

//msgb是m序列化得来的[]byte
func (mp *MessagePool) addLocal(m *types.SignedMessage, msgb []byte) error {
	mp.localAddrs[m.Message.From] = struct{}{}//只是标记是否有交易要重新push
    
    //本地缓存消息
	if err := mp.localMsgs.Put(datastore.NewKey(string(m.Cid().Bytes())), msgb); err != nil {
		return xerrors.Errorf("persisting local message: %w", err)
	}

	return nil
}

自动计算gas发送交易

命令
send --from=xxx xxxto 0.1

node/impl/full/mpool.go

func (a *MpoolAPI) MpoolPushMessage(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec) (*types.SignedMessage, error) {
    //获取地址
	{
		fromA, err := a.Stmgr.ResolveToKeyAddress(ctx, msg.From, nil)
		if err != nil {
			return nil, xerrors.Errorf("getting key address: %w", err)
		}
		done, err := a.PushLocks.TakeLock(ctx, fromA)
		if err != nil {
			return nil, xerrors.Errorf("taking lock: %w", err)
		}
		defer done()
	}

	if msg.Nonce != 0 {
		return nil, xerrors.Errorf("MpoolPushMessage expects message nonce to be 0, was %d", msg.Nonce)
	}
    //计算gasLimit,gasPreium,gasFeeCap
	msg, err := a.GasAPI.GasEstimateMessageGas(ctx, msg, spec, types.EmptyTSK)
	if err != nil {
		return nil, xerrors.Errorf("GasEstimateMessageGas error: %w", err)
	}

	sign := func(from address.Address, nonce uint64) (*types.SignedMessage, error) {
		msg.Nonce = nonce
		if msg.From.Protocol() == address.ID {
			log.Warnf("Push from ID address (%s), adjusting to %s", msg.From, from)
			msg.From = from
		}
        //获取余额
		b, err := a.WalletBalance(ctx, msg.From)
		if err != nil {
			return nil, xerrors.Errorf("mpool push: getting origin balance: %w", err)
		}
        //判断价格是否小于转账值
		if b.LessThan(msg.Value) {
			return nil, xerrors.Errorf("mpool push: not enough funds: %s < %s", b, msg.Value)
		}
        //调用签名,这个签名/推送中有说明
		return a.WalletSignMessage(ctx, from, msg)
	}

	var m *types.SignedMessage
again:
	m, err = a.Mpool.PushWithNonce(ctx, msg.From, sign)
	if err == messagepool.ErrTryAgain {
		log.Debugf("temporary failure while pushing message: %s; retrying", err)
		goto again //如果发送过程中有相同nonce先push到交易池,需重新推送
	}
	return m, err
}

a.Mpool.PushWithNonce(ctx, msg.From, sign)
路径 chain/messagepool/messagepool.go

func (mp *MessagePool) PushWithNonce(ctx context.Context, addr address.Address, cb func(address.Address, uint64) (*types.SignedMessage, error)) (*types.SignedMessage, error) {
	// serialize push access to reduce lock contention
	mp.addSema <- struct{}{}
	defer func() {
		<-mp.addSema
	}()

	mp.curTsLk.Lock()
	mp.lk.Lock()

	curTs := mp.curTs

	fromKey := addr
	if fromKey.Protocol() == address.ID {
		var err error
		fromKey, err = mp.api.StateAccountKey(ctx, fromKey, mp.curTs)
		if err != nil {
			mp.lk.Unlock()
			mp.curTsLk.Unlock()
			return nil, xerrors.Errorf("resolving sender key: %w", err)
		}
	}

	nonce, err := mp.getNonceLocked(fromKey, mp.curTs)
	if err != nil {
		mp.lk.Unlock()
		mp.curTsLk.Unlock()
		return nil, xerrors.Errorf("get nonce locked failed: %w", err)
	}

	// release the locks for signing
	mp.lk.Unlock()
	mp.curTsLk.Unlock()

	msg, err := cb(fromKey, nonce)
	if err != nil {
		return nil, err
	}

	// reacquire the locks and check state for consistency
	mp.curTsLk.Lock()
	defer mp.curTsLk.Unlock()

	if mp.curTs != curTs {
		return nil, ErrTryAgain
	}

	mp.lk.Lock()
	defer mp.lk.Unlock()
    //再次获取是防止有同nonce交易发出去了
	nonce2, err := mp.getNonceLocked(fromKey, mp.curTs)
	if err != nil {
		return nil, xerrors.Errorf("get nonce locked failed: %w", err)
	}

	if nonce2 != nonce {
		return nil, ErrTryAgain
	}
    //后面的流程和push中的一样
	if err := mp.verifyMsgBeforePush(msg, mp.curTs.Height()); err != nil {
		return nil, err
	}

	msgb, err := msg.Serialize()
	if err != nil {
		return nil, err
	}

	if err := mp.addLocked(msg); err != nil {
		return nil, xerrors.Errorf("add locked failed: %w", err)
	}
	if err := mp.addLocal(msg, msgb); err != nil {
		log.Errorf("addLocal failed: %+v", err)
	}

	return msg, mp.api.PubSubPublish(build.MessagesTopic(mp.netName), msgb)
}

GAS

需先了解EIP1559的概念

计算gasLimit,gasPreium,gasFeeCap

路径 node/impl/full/gas.go

func (a *GasAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, _ types.TipSetKey) (*types.Message, error) {
	if msg.GasLimit == 0 {
	    //
		gasLimit, err := a.GasEstimateGasLimit(ctx, msg, types.TipSetKey{})
		if err != nil {
			return nil, xerrors.Errorf("estimating gas used: %w", err)
		}
		msg.GasLimit = int64(float64(gasLimit) * a.Mpool.GetConfig().GasLimitOverestimation)
	}

	if msg.GasPremium == types.EmptyInt || types.BigCmp(msg.GasPremium, types.NewInt(0)) == 0 {
	    //内部实现没太看明白, 取最近的N*2个块所有的GasPremium均值(此处n是 2,往前推)
		gasPremium, err := a.GasEstimateGasPremium(ctx, 2, msg.From, msg.GasLimit, types.TipSetKey{})
		if err != nil {
			return nil, xerrors.Errorf("estimating gas price: %w", err)
		}
		msg.GasPremium = gasPremium
	}

	if msg.GasFeeCap == types.EmptyInt || types.BigCmp(msg.GasFeeCap, types.NewInt(0)) == 0 {
		feeCap, err := a.GasEstimateFeeCap(ctx, msg, 10, types.EmptyTSK)
		if err != nil {
			return nil, xerrors.Errorf("estimating fee cap: %w", err)
		}
		msg.GasFeeCap = big.Add(feeCap, msg.GasPremium)
	}

	capGasFee(msg, spec.Get().MaxFee)

	return msg, nil
}

GasEstimateGasLimit

调用过程
// node/impl/full/gas.go
func GasEstimateGasLimit(){
    msg.GasLimit = build.BlockGasLimit//10_000_000_000
	msg.GasFeeCap = types.NewInt(uint64(build.MinimumBaseFee) + 1)//101
	msg.GasPremium = types.NewInt(1)//1
    a.Stmgr.CallWithGas()
}
// chain/stmgr/call.go
func CallWithGas(){
    ret, err := vmi.ApplyMessage(ctx, msgApply)
}
// chain/vm/vm.go
func ApplyMessage(){
    msgGas := pl.OnChainMessage(cmsg.ChainLength())
    ret, actorErr, rt := vm.send(ctx, msg, nil, &msgGas, start)
}

func send(){
    //计算1
    if gasCharge != nil {//即上面的 msgGas
		if err := rt.chargeGasSafe(*gasCharge); err != nil {
			// this should never happen
			return nil, aerrors.Wrap(err, "not enough gas for initial message charge, this should not happen"), rt
		}
	}
	//计算2
	if aerr := rt.chargeGasSafe(rt.Pricelist().OnMethodInvocation(msg.Value, msg.Method)); aerr != nil {
			return nil, aerrors.Wrap(aerr, "not enough gas for method invocation")
	}
}
// chain/vm/runtime.go
func (rt *Runtime) chargeGasInternal(gas GasCharge,){
    toUse := gas.ComputeGas*GasComputeMulti + gas.StorageGas*GasStorageMulti
    rt.gasUsed += toUse
}

//计算1的 GasCharge
// chain/vm/gas_v0.go
func (pl *pricelistV0) OnChainMessage(msgSize int) GasCharge {
	return newGasCharge("OnChainMessage", pl.onChainMessageComputeBase,
		pl.onChainMessageStorageBase+pl.onChainMessageStoragePerByte*int64(msgSize))
}
//计算2的 GasCharge
const (
	MethodSend        = abi.MethodNum(0)
	MethodConstructor = abi.MethodNum(1)
)
func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) GasCharge {
	ret := pl.sendBase
	extra := ""

	if big.Cmp(value, abi.NewTokenAmount(0)) != 0 {
		ret += pl.sendTransferFunds
		if methodNum == builtin.MethodSend {//转账method是 0
			// transfer only
			ret += pl.sendTransferOnlyPremium
		}
		extra += "t"
	}

	if methodNum != builtin.MethodSend {
		extra += "i"
		// running actors is cheaper becase we hand over to actors
		ret += pl.sendInvokeMethod
	}
	
	return newGasCharge("OnMethodInvocation", ret, 0).WithExtra(extra)
}

计算相关常量
const (
	GasStorageMulti = 1000
	GasComputeMulti = 1
)

var prices = map[abi.ChainEpoch]Pricelist{
	abi.ChainEpoch(0): &pricelistV0{
		onChainMessageComputeBase:    38863,
		onChainMessageStorageBase:    36,
		onChainMessageStoragePerByte: 1,

		onChainReturnValuePerByte: 1,

		sendBase:                29233,
		sendTransferFunds:       27500,
		sendTransferOnlyPremium: 159672,
		sendInvokeMethod:        -5377,

		ipldGetBase:    75242,
		ipldPutBase:    84070,
		ipldPutPerByte: 1,

		createActorCompute: 1108454,
		createActorStorage: 36 + 40,
		deleteActor:        -(36 + 40), // -createActorStorage

		verifySignature: map[crypto.SigType]int64{
			crypto.SigTypeBLS:       16598605,
			crypto.SigTypeSecp256k1: 1637292,
		},

		hashingBase:                  31355,
		computeUnsealedSectorCidBase: 98647,
		verifySealBase:               2000, // TODO gas , it VerifySeal syscall is not used
		verifyPostLookup: map[abi.RegisteredPoStProof]scalingCost{
			abi.RegisteredPoStProof_StackedDrgWindow512MiBV1: {
				flat:  123861062,
				scale: 9226981,
			},
			abi.RegisteredPoStProof_StackedDrgWindow32GiBV1: {
				flat:  748593537,
				scale: 85639,
			},
			abi.RegisteredPoStProof_StackedDrgWindow64GiBV1: {
				flat:  748593537,
				scale: 85639,
			},
		},
		verifyConsensusFault: 495422,
	},
}

计算结果与日志比对

    当前操作是转0.001 FIL,对应的消息size是142
	实际获取到的结果 433268  (打印日志获取)

	GasStorageMulti = 1000
	GasComputeMulti = 1
	计算方式: (上面代码注释中的 计算2)
	ret := pl.sendBase
	ret += pl.sendTransferFunds
	ret += pl.sendTransferOnlyPremium//如果仅仅只是转账,需要该笔
	gas = newGasCharge("OnMethodInvocation", ret, 0)

	计算ret = 29233 + 27500 + 159672 = 216405

	toUse := g.ComputeGas*GasComputeMulti + g.StorageGas*GasStorageMulti
	 = 216405 * 1 + 0 =  216405
	
	还有一次计算 (上面代码注释中的 计算1)
	newGasCharge("OnChainMessage", pl.onChainMessageComputeBase,
		pl.onChainMessageStorageBase+pl.onChainMessageStoragePerByte*int64(msgSize))
	转换对应数值,msgSize是sm *SignedMessage 序列化的结果,
	打印的size是 142
	newGasCharge("OnChainMessage", 38863,
		36+142)
	gas = newGasCharge("OnChainMessage", ret, 0)
	38863 * 1 + (36+142)*1000 = 216,863

	216405 + 216,863 = 433268
	
	计算成功
	
	注意:
	在得出这个值后, 还需要 * 1.25
	433268 * 1.25 = 541585
	

GasEstimateGasPremium

大概是最近的块的消息取均值,使用方式参考发送交易
nblocksincl 传2,取最近n*2个块的所有消息小费均值
具体算法没太理解,可自行查看源码…

func (a *GasAPI) GasEstimateGasPremium(ctx context.Context, nblocksincl uint64,
	sender address.Address, gaslimit int64, _ types.TipSetKey) (types.BigInt, error) {
	//略
	}
	//内部计算大概是取当前往前推N(=nblocksincl*2)个块的所有消息的GasPremium,GasLimit,算法没看懂
	//应该是取均值之类的
	
	//nblocksincl 大概是块数量,包括当前块的意思,内部使用时 *2
	//gaslimit 内部未使用到该参数,忽略 
	

//1、当前gas计算代码, 参数:nblocksincl写死2
        a.GasEstimateGasPremium(ctx, 2, msg.From, msg.GasLimit, types.TipSetKey{})
//2、 rpc获取GasPremium
		nb := []int{1, 2, 3, 5, 10, 20, 50, 100, 300}
		for _, nblocks := range nb {
		    //这个TODO提示使用真实账号....
			addr := builtin.SystemActorAddr // TODO: make real when used in GasEstimateGasPremium
            //另外这个nb是列表 也没提示为啥这么用,也是取均值?
			est, err := api.GasEstimateGasPremium(ctx, uint64(nblocks), addr, 10000, types.EmptyTSK)
			if err != nil {
				return err
			}

			fmt.Printf("%d blocks: %s (%s)\n", nblocks, est, types.FIL(est))
		}
		










GasEstimateFeeCap

代码

func (a *GasAPI) GasEstimateFeeCap(ctx context.Context, msg *types.Message, maxqueueblks int64,
	tsk types.TipSetKey) (types.BigInt, error) {
	ts := a.Chain.GetHeaviestTipSet()//获取最新块

	var act types.Actor//获取账号状态
	err := a.Stmgr.WithParentState(ts, a.Stmgr.WithActor(msg.From, stmgr.GetActor(&act)))
	if err != nil {
		return types.NewInt(0), xerrors.Errorf("getting actor: %w", err)
	}
	//获取父区块的最低手续费
	parentBaseFee := ts.Blocks()[0].ParentBaseFee
	//增加因子 3.247321025468409
	increaseFactor := math.Pow(1.+1./float64(build.BaseFeeMaxChangeDenom), float64(maxqueueblks))

	//fee在原来的基础上*parentBaseFee
	feeInFuture := types.BigMul(parentBaseFee, types.NewInt(uint64(increaseFactor*(1<<8))))
	feeInFuture = types.BigDiv(feeInFuture, types.NewInt(1<<8))

	gasLimitBig := types.NewInt(uint64(msg.GasLimit))
	maxAccepted := types.BigDiv(act.Balance, types.NewInt(MaxSpendOnFeeDenom))//
	expectedFee := types.BigMul(feeInFuture, gasLimitBig)//预期手续费base * gasLimit

	out := feeInFuture
	if types.BigCmp(expectedFee, maxAccepted) > 0 {
		//如果预期手续费大于余额的1%,提示手续费太高
		log.Warnf("Expected fee for message higher than tolerance: %s > %s, setting to tolerance",
			types.FIL(expectedFee), types.FIL(maxAccepted))
		//改成
		out = types.BigDiv(maxAccepted, gasLimitBig)
	}

	return out, nil
}

简化版

    //简化版,具体查看后面的代码
    curBase = parentBaseFee * 3.247321025468409  
    expectedFee = curBase * gasLimit   
    maxAccepted = balance / 100  
    if expectedFee > maxAccepted {  
           out = maxAccepted / gasLimit   
    }
    
    
    然后再加上小费
    msg.GasFeeCap = big.Add(feeCap, msg.GasPremium)

区块浏览器中展现的值

飞狐浏览器
https://filfox.info/zh/
官方浏览器没展示这么清楚


0.772703973308509788 FIL	销毁手续费 = Base Fee Burn + Over Estimation Burn

版本 (API)	0
Nonce		26
Gas Fee Cap	1,712.955555402 nanoFIL //下面有根据日志计算得来的 
Gas Premium	1 attoFIL // 小费
Gas 限额	542,835 //通过gasLimit * 1.25
Gas 使用量	435,268 //计算得来的gasLimit
Base Fee Burn	0.745594738688717736 FIL // FeeCap * 使用量 (两种情况,具体看下面计算方式)
Over Estimation Burn	0.027109234619792052 FIL // 通过 feeCap/gas限额/使用量计算的来,具体看下面
Miner Tip	0 FIL


Gas Fee Cap 计算时日志,当前余额92.985222891635558 FIL
日志  

打印上个块base 10089621013943253
Expected fee for message higher than tolerance: 17778.85359609112888176 FIL > 0.92985222891635558 FIL, setting to tolerance  
计算得来的GasFeeCap 1712955555401 
再加上小费的GasFeeCap  1712955555402


按照这日志, 估计得余额170w+ fil的才能走基于parentBaseFee计算(另外一次日志是19274.3FIL,更高...  190W+)  
否则都是balance / 100 / gasLimit

92985222891635558000 / 100 / 542835 + 1(这个1是premium)
得出1712955555402 转成nanoFIL 除于9个0



Base Fee Burn 和 Over Estimation Burn 计算方式

路径 chain/vm/burn.go

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 {//如果baseFee 大于feeCap
		baseFeeToPay = feeCap //
		out.MinerPenalty = big.Mul(big.Sub(baseFee, feeCap), gasUsedBig)
	}
	//计算结果,当前日志baseFee是大于feeCap得,所以结果是 feeCap * gasUsed
	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))
    //计算OverestimationBurn
    //当前日志传参是 (435268,542835)
	out.GasRefund, out.GasBurned = ComputeGasOverestimationBurn(gasUsed, gasLimit)

	if out.GasBurned != 0 {
		gasBurnedBig := big.NewInt(out.GasBurned)
		//这里计算结果feeCapp * out.GasBurned
		//27109234619792052
		//转换得 0.027109234619792052 ; 和浏览器中一致
		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
}

ComputeGasOverestimationBurn(gasUsed, gasLimit)

const (
	gasOveruseNum   = 11
	gasOveruseDenom = 10
)
func ComputeGasOverestimationBurn(gasUsed, gasLimit int64) (int64, int64) {
	if gasUsed == 0 {
		return 0, gasLimit
	}

	// over = gasLimit/gasUsed - 1 - 0.1
	// over = min(over, 1)
	// gasToBurn = (gasLimit - gasUsed) * over

	// so to factor out division from `over`
	// over*gasUsed = min(gasLimit - (11*gasUsed)/10, gasUsed)
	// gasToBurn = ((gasLimit - gasUsed)*over*gasUsed) / gasUsed
	over := gasLimit - (gasOveruseNum*gasUsed)/gasOveruseDenom
	if over < 0 {
		return gasLimit - gasUsed, 0
	}

	// if we want sharper scaling it goes here:
	// over *= 2

	if over > gasUsed {
		over = gasUsed
	}

	// needs bigint, as it overflows in pathological case gasLimit > 2^32 gasUsed = gasLimit / 2
	gasToBurn := big.NewInt(gasLimit - gasUsed)
	gasToBurn = big.Mul(gasToBurn, big.NewInt(over))
	gasToBurn = big.Div(gasToBurn, big.NewInt(gasUsed))

	return gasLimit - gasUsed - gasToBurn.Int64(), gasToBurn.Int64()
}

你可能感兴趣的:(filecoin-lotus,区块链,filecoin,lotus)