From 6acc9a62f83450eb51e22c5062b82730d482f41b Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 11 Jun 2020 20:37:14 +0200 Subject: [PATCH] Split gas internally into compute gas and storage gas Signed-off-by: Jakub Sztandera --- chain/vm/gas.go | 53 ++++++++++++++++++++++++++++++------------ chain/vm/gas_v0.go | 56 +++++++++++++++++++++++---------------------- chain/vm/runtime.go | 11 +++++---- chain/vm/vm.go | 4 ++-- 4 files changed, 75 insertions(+), 49 deletions(-) diff --git a/chain/vm/gas.go b/chain/vm/gas.go index f1cf89845..c030621f6 100644 --- a/chain/vm/gas.go +++ b/chain/vm/gas.go @@ -12,34 +12,57 @@ import ( "github.com/ipfs/go-cid" ) +const ( + GasStorageMulti = 1 + GasComputeMulti = 1 +) + +type GasCharge struct { + Name string + ComputeGas int64 + StorageGas int64 +} + +func (g GasCharge) Total() int64 { + return g.ComputeGas*GasComputeMulti + g.StorageGas*GasStorageMulti +} + +func newGasCharge(name string, computeGas int64, storageGas int64) GasCharge { + return GasCharge{ + Name: name, + ComputeGas: computeGas, + StorageGas: storageGas, + } +} + // Pricelist provides prices for operations in the VM. // // Note: this interface should be APPEND ONLY since last chain checkpoint type Pricelist interface { // OnChainMessage returns the gas used for storing a message of a given size in the chain. - OnChainMessage(msgSize int) int64 + OnChainMessage(msgSize int) GasCharge // OnChainReturnValue returns the gas used for storing the response of a message in the chain. - OnChainReturnValue(dataSize int) int64 + OnChainReturnValue(dataSize int) GasCharge // OnMethodInvocation returns the gas used when invoking a method. - OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) int64 + OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) GasCharge // OnIpldGet returns the gas used for storing an object - OnIpldGet(dataSize int) int64 + OnIpldGet(dataSize int) GasCharge // OnIpldPut returns the gas used for storing an object - OnIpldPut(dataSize int) int64 + OnIpldPut(dataSize int) GasCharge // OnCreateActor returns the gas used for creating an actor - OnCreateActor() int64 + OnCreateActor() GasCharge // OnDeleteActor returns the gas used for deleting an actor - OnDeleteActor() int64 + OnDeleteActor() GasCharge - OnVerifySignature(sigType crypto.SigType, planTextSize int) (int64, error) - OnHashing(dataSize int) int64 - OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) int64 - OnVerifySeal(info abi.SealVerifyInfo) int64 - OnVerifyPost(info abi.WindowPoStVerifyInfo) int64 - OnVerifyConsensusFault() int64 + OnVerifySignature(sigType crypto.SigType, planTextSize int) (GasCharge, error) + OnHashing(dataSize int) GasCharge + OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) GasCharge + OnVerifySeal(info abi.SealVerifyInfo) GasCharge + OnVerifyPost(info abi.WindowPoStVerifyInfo) GasCharge + OnVerifyConsensusFault() GasCharge } var prices = map[abi.ChainEpoch]Pricelist{ @@ -93,7 +116,7 @@ func PricelistByEpoch(epoch abi.ChainEpoch) Pricelist { type pricedSyscalls struct { under vmr.Syscalls pl Pricelist - chargeGas func(int64) + chargeGas func(GasCharge) } // Verifies that a signature is valid for an address and plaintext. @@ -146,6 +169,6 @@ func (ps pricedSyscalls) VerifyConsensusFault(h1 []byte, h2 []byte, extra []byte } func (ps pricedSyscalls) BatchVerifySeals(inp map[address.Address][]abi.SealVerifyInfo) (map[address.Address][]bool, error) { - ps.chargeGas(0) // TODO: this is only called by the cron actor. Should we even charge gas? + ps.chargeGas(newGasCharge("BatchVerifySeals", 0, 0)) // TODO: this is only called by the cron actor. Should we even charge gas? return ps.under.BatchVerifySeals(inp) } diff --git a/chain/vm/gas_v0.go b/chain/vm/gas_v0.go index a20ac927d..0b6196377 100644 --- a/chain/vm/gas_v0.go +++ b/chain/vm/gas_v0.go @@ -2,6 +2,7 @@ package vm import ( "fmt" + "github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/builtin" "github.com/filecoin-project/specs-actors/actors/crypto" @@ -84,17 +85,17 @@ type pricelistV0 struct { var _ Pricelist = (*pricelistV0)(nil) // OnChainMessage returns the gas used for storing a message of a given size in the chain. -func (pl *pricelistV0) OnChainMessage(msgSize int) int64 { - return pl.onChainMessageBase + pl.onChainMessagePerByte*int64(msgSize) +func (pl *pricelistV0) OnChainMessage(msgSize int) GasCharge { + return newGasCharge("OnChainMessage", 0, pl.onChainMessageBase+pl.onChainMessagePerByte*int64(msgSize)) } // OnChainReturnValue returns the gas used for storing the response of a message in the chain. -func (pl *pricelistV0) OnChainReturnValue(dataSize int) int64 { - return int64(dataSize) * pl.onChainReturnValuePerByte +func (pl *pricelistV0) OnChainReturnValue(dataSize int) GasCharge { + return newGasCharge("OnChainReturnValue", 0, int64(dataSize)*pl.onChainReturnValuePerByte) } // OnMethodInvocation returns the gas used when invoking a method. -func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) int64 { +func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) GasCharge { ret := pl.sendBase if value != abi.NewTokenAmount(0) { ret += pl.sendTransferFunds @@ -102,62 +103,63 @@ func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.M if methodNum != builtin.MethodSend { ret += pl.sendInvokeMethod } - return ret + return newGasCharge("OnMethodInvocation", ret, 0) } // OnIpldGet returns the gas used for storing an object -func (pl *pricelistV0) OnIpldGet(dataSize int) int64 { - return pl.ipldGetBase + int64(dataSize)*pl.ipldGetPerByte +func (pl *pricelistV0) OnIpldGet(dataSize int) GasCharge { + return newGasCharge("OnIpldGet", pl.ipldGetBase+int64(dataSize)*pl.ipldGetPerByte, 0) } // OnIpldPut returns the gas used for storing an object -func (pl *pricelistV0) OnIpldPut(dataSize int) int64 { - return pl.ipldPutBase + int64(dataSize)*pl.ipldPutPerByte +func (pl *pricelistV0) OnIpldPut(dataSize int) GasCharge { + return newGasCharge("OnIpldPut", pl.ipldPutBase, int64(dataSize)*pl.ipldPutPerByte) } // OnCreateActor returns the gas used for creating an actor -func (pl *pricelistV0) OnCreateActor() int64 { - return pl.createActorBase + pl.createActorExtra +func (pl *pricelistV0) OnCreateActor() GasCharge { + return newGasCharge("OnCreateActor", pl.createActorBase, pl.createActorExtra) } // OnDeleteActor returns the gas used for deleting an actor -func (pl *pricelistV0) OnDeleteActor() int64 { - return pl.deleteActor +func (pl *pricelistV0) OnDeleteActor() GasCharge { + return newGasCharge("OnDeleteActor", 0, pl.deleteActor) } // OnVerifySignature -func (pl *pricelistV0) OnVerifySignature(sigType crypto.SigType, planTextSize int) (int64, error) { +func (pl *pricelistV0) OnVerifySignature(sigType crypto.SigType, planTextSize int) (GasCharge, error) { costFn, ok := pl.verifySignature[sigType] if !ok { - return 0, fmt.Errorf("cost function for signature type %d not supported", sigType) + return GasCharge{}, fmt.Errorf("cost function for signature type %d not supported", sigType) } - return costFn(int64(planTextSize)), nil + sigName, _ := sigType.Name() + return newGasCharge("OnVerifySignature/"+sigName, costFn(int64(planTextSize)), 0), nil } // OnHashing -func (pl *pricelistV0) OnHashing(dataSize int) int64 { - return pl.hashingBase + int64(dataSize)*pl.hashingPerByte +func (pl *pricelistV0) OnHashing(dataSize int) GasCharge { + return newGasCharge("OnHashing", pl.hashingBase+int64(dataSize)*pl.hashingPerByte, 0) } // OnComputeUnsealedSectorCid -func (pl *pricelistV0) OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) int64 { +func (pl *pricelistV0) OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) GasCharge { // TODO: this needs more cost tunning, check with @lotus - return pl.computeUnsealedSectorCidBase + return newGasCharge("OnComputeUnsealedSectorCid", pl.computeUnsealedSectorCidBase, 0) } // OnVerifySeal -func (pl *pricelistV0) OnVerifySeal(info abi.SealVerifyInfo) int64 { +func (pl *pricelistV0) OnVerifySeal(info abi.SealVerifyInfo) GasCharge { // TODO: this needs more cost tunning, check with @lotus - return pl.verifySealBase + return newGasCharge("OnVerifySeal", pl.verifySealBase, 0) } // OnVerifyPost -func (pl *pricelistV0) OnVerifyPost(info abi.WindowPoStVerifyInfo) int64 { +func (pl *pricelistV0) OnVerifyPost(info abi.WindowPoStVerifyInfo) GasCharge { // TODO: this needs more cost tunning, check with @lotus - return pl.verifyPostBase + return newGasCharge("OnVerifyPost", pl.verifyPostBase, 0) } // OnVerifyConsensusFault -func (pl *pricelistV0) OnVerifyConsensusFault() int64 { - return pl.verifyConsensusFault +func (pl *pricelistV0) OnVerifyConsensusFault() GasCharge { + return newGasCharge("OnVerifyConsensusFault", pl.verifyConsensusFault, 0) } diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index 70583e47f..ae3a60616 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -484,14 +484,15 @@ func (rt *Runtime) stateCommit(oldh, newh cid.Cid) aerrors.ActorError { return nil } -func (rt *Runtime) ChargeGas(toUse int64) { - err := rt.chargeGasInternal(toUse) +func (rt *Runtime) ChargeGas(gas GasCharge) { + err := rt.chargeGasInternal(gas) if err != nil { panic(err) } } -func (rt *Runtime) chargeGasInternal(toUse int64) aerrors.ActorError { +func (rt *Runtime) chargeGasInternal(gas GasCharge) aerrors.ActorError { + toUse := gas.Total() if rt.gasUsed+toUse > rt.gasAvailable { rt.gasUsed = rt.gasAvailable return aerrors.Newf(exitcode.SysErrOutOfGas, "not enough gas: used=%d, available=%d", rt.gasUsed, rt.gasAvailable) @@ -500,8 +501,8 @@ func (rt *Runtime) chargeGasInternal(toUse int64) aerrors.ActorError { return nil } -func (rt *Runtime) chargeGasSafe(toUse int64) aerrors.ActorError { - return rt.chargeGasInternal(toUse) +func (rt *Runtime) chargeGasSafe(gas GasCharge) aerrors.ActorError { + return rt.chargeGasInternal(gas) } func (rt *Runtime) Pricelist() Pricelist { diff --git a/chain/vm/vm.go b/chain/vm/vm.go index 42d6d373e..11db0d419 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -62,7 +62,7 @@ func ResolveToKeyAddr(state types.StateTree, cst cbor.IpldStore, addr address.Ad var _ cbor.IpldBlockstore = (*gasChargingBlocks)(nil) type gasChargingBlocks struct { - chargeGas func(int64) + chargeGas func(GasCharge) pricelist Pricelist under cbor.IpldBlockstore } @@ -294,7 +294,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, pl := PricelistByEpoch(vm.blockHeight) - msgGasCost := pl.OnChainMessage(cmsg.ChainLength()) + msgGasCost := pl.OnChainMessage(cmsg.ChainLength()).Total() // this should never happen, but is currently still exercised by some tests if msgGasCost > msg.GasLimit { return &ApplyRet{