Split gas internally into compute gas and storage gas

Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
This commit is contained in:
Jakub Sztandera 2020-06-11 20:37:14 +02:00
parent c8046f4597
commit 6acc9a62f8
No known key found for this signature in database
GPG Key ID: 9A9AF56F8B3879BA
4 changed files with 75 additions and 49 deletions

View File

@ -12,34 +12,57 @@ import (
"github.com/ipfs/go-cid" "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. // Pricelist provides prices for operations in the VM.
// //
// Note: this interface should be APPEND ONLY since last chain checkpoint // Note: this interface should be APPEND ONLY since last chain checkpoint
type Pricelist interface { type Pricelist interface {
// OnChainMessage returns the gas used for storing a message of a given size in the chain. // 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 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 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 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 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 returns the gas used for creating an actor
OnCreateActor() int64 OnCreateActor() GasCharge
// OnDeleteActor returns the gas used for deleting an actor // OnDeleteActor returns the gas used for deleting an actor
OnDeleteActor() int64 OnDeleteActor() GasCharge
OnVerifySignature(sigType crypto.SigType, planTextSize int) (int64, error) OnVerifySignature(sigType crypto.SigType, planTextSize int) (GasCharge, error)
OnHashing(dataSize int) int64 OnHashing(dataSize int) GasCharge
OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) int64 OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) GasCharge
OnVerifySeal(info abi.SealVerifyInfo) int64 OnVerifySeal(info abi.SealVerifyInfo) GasCharge
OnVerifyPost(info abi.WindowPoStVerifyInfo) int64 OnVerifyPost(info abi.WindowPoStVerifyInfo) GasCharge
OnVerifyConsensusFault() int64 OnVerifyConsensusFault() GasCharge
} }
var prices = map[abi.ChainEpoch]Pricelist{ var prices = map[abi.ChainEpoch]Pricelist{
@ -93,7 +116,7 @@ func PricelistByEpoch(epoch abi.ChainEpoch) Pricelist {
type pricedSyscalls struct { type pricedSyscalls struct {
under vmr.Syscalls under vmr.Syscalls
pl Pricelist pl Pricelist
chargeGas func(int64) chargeGas func(GasCharge)
} }
// Verifies that a signature is valid for an address and plaintext. // 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) { 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) return ps.under.BatchVerifySeals(inp)
} }

View File

@ -2,6 +2,7 @@ package vm
import ( import (
"fmt" "fmt"
"github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin" "github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/crypto" "github.com/filecoin-project/specs-actors/actors/crypto"
@ -84,17 +85,17 @@ type pricelistV0 struct {
var _ Pricelist = (*pricelistV0)(nil) var _ Pricelist = (*pricelistV0)(nil)
// OnChainMessage returns the gas used for storing a message of a given size in the chain. // OnChainMessage returns the gas used for storing a message of a given size in the chain.
func (pl *pricelistV0) OnChainMessage(msgSize int) int64 { func (pl *pricelistV0) OnChainMessage(msgSize int) GasCharge {
return pl.onChainMessageBase + pl.onChainMessagePerByte*int64(msgSize) 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. // OnChainReturnValue returns the gas used for storing the response of a message in the chain.
func (pl *pricelistV0) OnChainReturnValue(dataSize int) int64 { func (pl *pricelistV0) OnChainReturnValue(dataSize int) GasCharge {
return int64(dataSize) * pl.onChainReturnValuePerByte return newGasCharge("OnChainReturnValue", 0, int64(dataSize)*pl.onChainReturnValuePerByte)
} }
// OnMethodInvocation returns the gas used when invoking a method. // 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 ret := pl.sendBase
if value != abi.NewTokenAmount(0) { if value != abi.NewTokenAmount(0) {
ret += pl.sendTransferFunds ret += pl.sendTransferFunds
@ -102,62 +103,63 @@ func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.M
if methodNum != builtin.MethodSend { if methodNum != builtin.MethodSend {
ret += pl.sendInvokeMethod ret += pl.sendInvokeMethod
} }
return ret return newGasCharge("OnMethodInvocation", ret, 0)
} }
// OnIpldGet returns the gas used for storing an object // OnIpldGet returns the gas used for storing an object
func (pl *pricelistV0) OnIpldGet(dataSize int) int64 { func (pl *pricelistV0) OnIpldGet(dataSize int) GasCharge {
return pl.ipldGetBase + int64(dataSize)*pl.ipldGetPerByte return newGasCharge("OnIpldGet", pl.ipldGetBase+int64(dataSize)*pl.ipldGetPerByte, 0)
} }
// OnIpldPut returns the gas used for storing an object // OnIpldPut returns the gas used for storing an object
func (pl *pricelistV0) OnIpldPut(dataSize int) int64 { func (pl *pricelistV0) OnIpldPut(dataSize int) GasCharge {
return pl.ipldPutBase + int64(dataSize)*pl.ipldPutPerByte return newGasCharge("OnIpldPut", pl.ipldPutBase, int64(dataSize)*pl.ipldPutPerByte)
} }
// OnCreateActor returns the gas used for creating an actor // OnCreateActor returns the gas used for creating an actor
func (pl *pricelistV0) OnCreateActor() int64 { func (pl *pricelistV0) OnCreateActor() GasCharge {
return pl.createActorBase + pl.createActorExtra return newGasCharge("OnCreateActor", pl.createActorBase, pl.createActorExtra)
} }
// OnDeleteActor returns the gas used for deleting an actor // OnDeleteActor returns the gas used for deleting an actor
func (pl *pricelistV0) OnDeleteActor() int64 { func (pl *pricelistV0) OnDeleteActor() GasCharge {
return pl.deleteActor return newGasCharge("OnDeleteActor", 0, pl.deleteActor)
} }
// OnVerifySignature // 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] costFn, ok := pl.verifySignature[sigType]
if !ok { 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 // OnHashing
func (pl *pricelistV0) OnHashing(dataSize int) int64 { func (pl *pricelistV0) OnHashing(dataSize int) GasCharge {
return pl.hashingBase + int64(dataSize)*pl.hashingPerByte return newGasCharge("OnHashing", pl.hashingBase+int64(dataSize)*pl.hashingPerByte, 0)
} }
// OnComputeUnsealedSectorCid // 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 // TODO: this needs more cost tunning, check with @lotus
return pl.computeUnsealedSectorCidBase return newGasCharge("OnComputeUnsealedSectorCid", pl.computeUnsealedSectorCidBase, 0)
} }
// OnVerifySeal // 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 // TODO: this needs more cost tunning, check with @lotus
return pl.verifySealBase return newGasCharge("OnVerifySeal", pl.verifySealBase, 0)
} }
// OnVerifyPost // 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 // TODO: this needs more cost tunning, check with @lotus
return pl.verifyPostBase return newGasCharge("OnVerifyPost", pl.verifyPostBase, 0)
} }
// OnVerifyConsensusFault // OnVerifyConsensusFault
func (pl *pricelistV0) OnVerifyConsensusFault() int64 { func (pl *pricelistV0) OnVerifyConsensusFault() GasCharge {
return pl.verifyConsensusFault return newGasCharge("OnVerifyConsensusFault", pl.verifyConsensusFault, 0)
} }

View File

@ -484,14 +484,15 @@ func (rt *Runtime) stateCommit(oldh, newh cid.Cid) aerrors.ActorError {
return nil return nil
} }
func (rt *Runtime) ChargeGas(toUse int64) { func (rt *Runtime) ChargeGas(gas GasCharge) {
err := rt.chargeGasInternal(toUse) err := rt.chargeGasInternal(gas)
if err != nil { if err != nil {
panic(err) 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 { if rt.gasUsed+toUse > rt.gasAvailable {
rt.gasUsed = rt.gasAvailable rt.gasUsed = rt.gasAvailable
return aerrors.Newf(exitcode.SysErrOutOfGas, "not enough gas: used=%d, available=%d", 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 return nil
} }
func (rt *Runtime) chargeGasSafe(toUse int64) aerrors.ActorError { func (rt *Runtime) chargeGasSafe(gas GasCharge) aerrors.ActorError {
return rt.chargeGasInternal(toUse) return rt.chargeGasInternal(gas)
} }
func (rt *Runtime) Pricelist() Pricelist { func (rt *Runtime) Pricelist() Pricelist {

View File

@ -62,7 +62,7 @@ func ResolveToKeyAddr(state types.StateTree, cst cbor.IpldStore, addr address.Ad
var _ cbor.IpldBlockstore = (*gasChargingBlocks)(nil) var _ cbor.IpldBlockstore = (*gasChargingBlocks)(nil)
type gasChargingBlocks struct { type gasChargingBlocks struct {
chargeGas func(int64) chargeGas func(GasCharge)
pricelist Pricelist pricelist Pricelist
under cbor.IpldBlockstore under cbor.IpldBlockstore
} }
@ -294,7 +294,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
pl := PricelistByEpoch(vm.blockHeight) 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 // this should never happen, but is currently still exercised by some tests
if msgGasCost > msg.GasLimit { if msgGasCost > msg.GasLimit {
return &ApplyRet{ return &ApplyRet{