从cli命令send中, 有两种转账方式
路径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 对应加密方式签名
路径
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
}
命令
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)
}
需先了解EIP1559的概念
路径 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
}
// 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
大概是最近的块的消息取均值,使用方式参考发送交易
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))
}
代码
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
路径 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
}
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()
}