Rework gas charging

This commit is contained in:
Jakub Sztandera 2020-03-19 17:27:42 +01:00 committed by Aayush Rajasekaran
parent 825a3e1d5c
commit ae40654907
6 changed files with 367 additions and 30 deletions

141
chain/vm/gas.go Normal file
View File

@ -0,0 +1,141 @@
package vm
import (
"fmt"
addr "github.com/filecoin-project/go-address"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/crypto"
"github.com/filecoin-project/specs-actors/actors/runtime"
vmr "github.com/filecoin-project/specs-actors/actors/runtime"
"github.com/ipfs/go-cid"
)
// 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
// OnChainReturnValue returns the gas used for storing the response of a message in the chain.
OnChainReturnValue(dataSize int) int64
// OnMethodInvocation returns the gas used when invoking a method.
OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) int64
// OnIpldGet returns the gas used for storing an object
OnIpldGet(dataSize int) int64
// OnIpldPut returns the gas used for storing an object
OnIpldPut(dataSize int) int64
// OnCreateActor returns the gas used for creating an actor
OnCreateActor() int64
// OnDeleteActor returns the gas used for deleting an actor
OnDeleteActor() int64
OnVerifySignature(sigType crypto.SigType, planTextSize int) int64
OnHashing(dataSize int) int64
OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) int64
OnVerifySeal(info abi.SealVerifyInfo) int64
OnVerifyPost(info abi.PoStVerifyInfo) int64
OnVerifyConsensusFault() int64
}
var prices = map[abi.ChainEpoch]Pricelist{
abi.ChainEpoch(0): &pricelistV0{
onChainMessageBase: 0,
onChainMessagePerByte: 2,
onChainReturnValuePerByte: 8,
sendBase: 5,
sendTransferFunds: 5,
sendInvokeMethod: 10,
ipldGetBase: 10,
ipldGetPerByte: 1,
ipldPutBase: 20,
ipldPutPerByte: 2,
createActorBase: 40, // IPLD put + 20
createActorExtra: 500,
deleteActor: -500, // -createActorExtra
// Dragons: this cost is not persistable, create a LinearCost{a,b} struct that has a `.Cost(x) -> ax + b`
verifySignature: map[crypto.SigType]func(int64) int64{
crypto.SigTypeBLS: func(x int64) int64 { return 3*x + 2 },
crypto.SigTypeSecp256k1: func(x int64) int64 { return 3*x + 2 },
},
hashingBase: 5,
hashingPerByte: 2,
computeUnsealedSectorCidBase: 100,
verifySealBase: 2000,
verifyPostBase: 700,
verifyConsensusFault: 10,
},
}
// PricelistByEpoch finds the latest prices for the given epoch
func PricelistByEpoch(epoch abi.ChainEpoch) Pricelist {
// since we are storing the prices as map or epoch to price
// we need to get the price with the highest epoch that is lower or equal to the `epoch` arg
bestEpoch := abi.ChainEpoch(0)
bestPrice := prices[bestEpoch]
for e, pl := range prices {
// if `e` happened after `bestEpoch` and `e` is earlier or equal to the target `epoch`
if e > bestEpoch && e <= epoch {
bestEpoch = e
bestPrice = pl
}
}
if bestPrice == nil {
panic(fmt.Sprintf("bad setup: no gas prices available for epoch %d", epoch))
}
return bestPrice
}
type pricedSyscalls struct {
under vmr.Syscalls
pl Pricelist
chargeGas func(int64)
}
// Verifies that a signature is valid for an address and plaintext.
func (ps pricedSyscalls) VerifySignature(signature crypto.Signature, signer addr.Address, plaintext []byte) error {
ps.chargeGas(ps.pl.OnVerifySignature(signature.Type, len(plaintext)))
return ps.under.VerifySignature(signature, signer, plaintext)
}
// Hashes input data using blake2b with 256 bit output.
func (ps pricedSyscalls) HashBlake2b(data []byte) [32]byte {
ps.chargeGas(ps.pl.OnHashing(len(data)))
return ps.under.HashBlake2b(data)
}
// Computes an unsealed sector CID (CommD) from its constituent piece CIDs (CommPs) and sizes.
func (ps pricedSyscalls) ComputeUnsealedSectorCID(reg abi.RegisteredProof, pieces []abi.PieceInfo) (cid.Cid, error) {
ps.chargeGas(ps.pl.OnComputeUnsealedSectorCid(reg, pieces))
return ps.under.ComputeUnsealedSectorCID(reg, pieces)
}
// Verifies a sector seal proof.
func (ps pricedSyscalls) VerifySeal(vi abi.SealVerifyInfo) error {
ps.chargeGas(ps.pl.OnVerifySeal(vi))
return ps.under.VerifySeal(vi)
}
// Verifies a proof of spacetime.
func (ps pricedSyscalls) VerifyPoSt(vi abi.PoStVerifyInfo) error {
ps.chargeGas(ps.pl.OnVerifyPost(vi))
return ps.under.VerifyPoSt(vi)
}
// Verifies that two block headers provide proof of a consensus fault:
// - both headers mined by the same actor
// - headers are different
// - first header is of the same or lower epoch as the second
// - at least one of the headers appears in the current chain at or after epoch `earliest`
// - the headers provide evidence of a fault (see the spec for the different fault types).
// The parameters are all serialized block headers. The third "extra" parameter is consulted only for
// the "parent grinding fault", in which case it must be the sibling of h1 (same parent tipset) and one of the
// blocks in the parent of h2 (i.e. h2's grandparent).
// Returns nil and an error if the headers don't prove a fault.
func (ps pricedSyscalls) VerifyConsensusFault(h1 []byte, h2 []byte, extra []byte, earliest abi.ChainEpoch) (*runtime.ConsensusFault, error) {
ps.chargeGas(ps.pl.OnVerifyConsensusFault())
return ps.under.VerifyConsensusFault(h1, h2, extra, earliest)
}

165
chain/vm/gas_v0.go Normal file
View File

@ -0,0 +1,165 @@
package vm
import (
"github.com/filecoin-project/lotus/chain/actors/aerrors"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/crypto"
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
)
type pricelistV0 struct {
///////////////////////////////////////////////////////////////////////////
// System operations
///////////////////////////////////////////////////////////////////////////
// Gas cost charged to the originator of an on-chain message (regardless of
// whether it succeeds or fails in application) is given by:
// OnChainMessageBase + len(serialized message)*OnChainMessagePerByte
// Together, these account for the cost of message propagation and validation,
// up to but excluding any actual processing by the VM.
// This is the cost a block producer burns when including an invalid message.
onChainMessageBase int64
onChainMessagePerByte int64
// Gas cost charged to the originator of a non-nil return value produced
// by an on-chain message is given by:
// len(return value)*OnChainReturnValuePerByte
onChainReturnValuePerByte int64
// Gas cost for any message send execution(including the top-level one
// initiated by an on-chain message).
// This accounts for the cost of loading sender and receiver actors and
// (for top-level messages) incrementing the sender's sequence number.
// Load and store of actor sub-state is charged separately.
sendBase int64
// Gas cost charged, in addition to SendBase, if a message send
// is accompanied by any nonzero currency amount.
// Accounts for writing receiver's new balance (the sender's state is
// already accounted for).
sendTransferFunds int64
// Gas cost charged, in addition to SendBase, if a message invokes
// a method on the receiver.
// Accounts for the cost of loading receiver code and method dispatch.
sendInvokeMethod int64
// Gas cost (Base + len*PerByte) for any Get operation to the IPLD store
// in the runtime VM context.
ipldGetBase int64
ipldGetPerByte int64
// Gas cost (Base + len*PerByte) for any Put operation to the IPLD store
// in the runtime VM context.
//
// Note: these costs should be significantly higher than the costs for Get
// operations, since they reflect not only serialization/deserialization
// but also persistent storage of chain data.
ipldPutBase int64
ipldPutPerByte int64
// Gas cost for creating a new actor (via InitActor's Exec method).
//
// Note: this costs assume that the extra will be partially or totally refunded while
// the base is covering for the put.
createActorBase int64
createActorExtra int64
// Gas cost for deleting an actor.
//
// Note: this partially refunds the create cost to incentivise the deletion of the actors.
deleteActor int64
verifySignature map[crypto.SigType]func(len int64) int64
hashingBase int64
hashingPerByte int64
computeUnsealedSectorCidBase int64
verifySealBase int64
verifyPostBase int64
verifyConsensusFault int64
}
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)
}
// 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
}
// OnMethodInvocation returns the gas used when invoking a method.
func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) int64 {
ret := pl.sendBase
if value != abi.NewTokenAmount(0) {
ret += pl.sendTransferFunds
}
if methodNum != builtin.MethodSend {
ret += pl.sendInvokeMethod
}
return ret
}
// OnIpldGet returns the gas used for storing an object
func (pl *pricelistV0) OnIpldGet(dataSize int) int64 {
return pl.ipldGetBase + int64(dataSize)*pl.ipldGetPerByte
}
// OnIpldPut returns the gas used for storing an object
func (pl *pricelistV0) OnIpldPut(dataSize int) int64 {
return 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
}
// OnDeleteActor returns the gas used for deleting an actor
func (pl *pricelistV0) OnDeleteActor() int64 {
return pl.deleteActor
}
// OnVerifySignature
func (pl *pricelistV0) OnVerifySignature(sigType crypto.SigType, planTextSize int) int64 {
costFn, ok := pl.verifySignature[sigType]
if !ok {
// TODO: fix retcode to be int64
panic(aerrors.Newf(uint8(exitcode.SysErrInternal&0xff), "Cost function for signature type %d not supported", sigType))
}
return costFn(int64(planTextSize))
}
// OnHashing
func (pl *pricelistV0) OnHashing(dataSize int) int64 {
return pl.hashingBase + int64(dataSize)*pl.hashingPerByte
}
// OnComputeUnsealedSectorCid
func (pl *pricelistV0) OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) int64 {
// TODO: this needs more cost tunning, check with @lotus
return pl.computeUnsealedSectorCidBase
}
// OnVerifySeal
func (pl *pricelistV0) OnVerifySeal(info abi.SealVerifyInfo) int64 {
// TODO: this needs more cost tunning, check with @lotus
return pl.verifySealBase
}
// OnVerifyPost
func (pl *pricelistV0) OnVerifyPost(info abi.PoStVerifyInfo) int64 {
// TODO: this needs more cost tunning, check with @lotus
return pl.verifyPostBase
}
// OnVerifyConsensusFault
func (pl *pricelistV0) OnVerifyConsensusFault() int64 {
return pl.verifyConsensusFault
}

View File

@ -4,7 +4,6 @@ import (
"bytes"
"context"
"encoding/binary"
"runtime/debug"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/specs-actors/actors/abi"
@ -28,11 +27,12 @@ import (
type Runtime struct {
ctx context.Context
vm *VM
state *state.StateTree
msg *types.Message
height abi.ChainEpoch
cst cbor.IpldStore
vm *VM
state *state.StateTree
msg *types.Message
height abi.ChainEpoch
cst cbor.IpldStore
pricelist Pricelist
gasAvailable int64
gasUsed int64
@ -78,13 +78,11 @@ func (rs *Runtime) shimCall(f func() interface{}) (rval []byte, aerr aerrors.Act
defer func() {
if r := recover(); r != nil {
if ar, ok := r.(aerrors.ActorError); ok {
log.Warn("VM.Call failure: ", ar)
debug.PrintStack()
log.Errorf("VM.Call failure: %+v", ar)
aerr = ar
return
}
debug.PrintStack()
log.Errorf("ERROR")
log.Errorf("spec actors failure: %s", r)
aerr = aerrors.Newf(1, "spec actors failure: %s", r)
}
}()
@ -180,6 +178,7 @@ func (rt *Runtime) NewActorAddress() address.Address {
}
func (rt *Runtime) CreateActor(codeId cid.Cid, address address.Address) {
rt.ChargeGas(rt.Pricelist().OnCreateActor())
var err error
err = rt.state.SetActor(address, &types.Actor{
@ -194,6 +193,7 @@ func (rt *Runtime) CreateActor(codeId cid.Cid, address address.Address) {
}
func (rt *Runtime) DeleteActor() {
rt.ChargeGas(rt.Pricelist().OnDeleteActor())
act, err := rt.state.GetActor(rt.Message().Receiver())
if err != nil {
rt.Abortf(exitcode.SysErrInternal, "failed to load actor in delete actor: %s", err)
@ -314,6 +314,7 @@ func (rt *Runtime) internalSend(to address.Address, method abi.MethodNum, value
return nil, aerrors.Fatalf("snapshot failed: %s", err)
}
defer st.ClearSnapshot()
rt.ChargeGas(rt.Pricelist().OnMethodInvocation(value, method))
ret, errSend, subrt := rt.vm.send(ctx, msg, rt, 0)
if errSend != nil {
@ -400,8 +401,6 @@ func (rt *Runtime) GetBalance(a address.Address) (types.BigInt, aerrors.ActorErr
}
func (rt *Runtime) stateCommit(oldh, newh cid.Cid) aerrors.ActorError {
rt.ChargeGas(gasCommit)
// TODO: we can make this more efficient in the future...
act, err := rt.state.GetActor(rt.Message().Receiver())
if err != nil {
@ -422,8 +421,20 @@ func (rt *Runtime) stateCommit(oldh, newh cid.Cid) aerrors.ActorError {
}
func (rt *Runtime) ChargeGas(toUse int64) {
rt.gasUsed = rt.gasUsed + toUse
if rt.gasUsed > rt.gasAvailable {
rt.Abortf(exitcode.SysErrOutOfGas, "not enough gas: used=%d, available=%d", rt.gasUsed, rt.gasAvailable)
err := rt.chargeGasSafe(toUse)
if err != nil {
panic(err)
}
}
func (rt *Runtime) chargeGasSafe(toUse int64) aerrors.ActorError {
rt.gasUsed += toUse
if rt.gasUsed > rt.gasAvailable {
return aerrors.Newf(uint8(exitcode.SysErrOutOfGas), "not enough gas: used=%d, available=%d", rt.gasUsed, rt.gasAvailable)
}
return nil
}
func (rt *Runtime) Pricelist() Pricelist {
return rt.pricelist
}

View File

@ -92,22 +92,22 @@ var _ cbor.IpldBlockstore = (*gasChargingBlocks)(nil)
type gasChargingBlocks struct {
chargeGas func(int64)
pricelist Pricelist
under cbor.IpldBlockstore
}
func (bs *gasChargingBlocks) Get(c cid.Cid) (block.Block, error) {
bs.chargeGas(gasGetObj)
blk, err := bs.under.Get(c)
if err != nil {
return nil, aerrors.Escalate(err, "failed to get block from blockstore")
}
bs.chargeGas(int64(len(blk.RawData())) * gasGetPerByte)
bs.chargeGas(bs.pricelist.OnIpldGet(len(blk.RawData())))
return blk, nil
}
func (bs *gasChargingBlocks) Put(blk block.Block) error {
bs.chargeGas(gasPutObj + int64(len(blk.RawData()))*gasPutPerByte)
bs.chargeGas(bs.pricelist.OnIpldPut(len(blk.RawData())))
if err := bs.under.Put(blk); err != nil {
return aerrors.Escalate(err, "failed to write data to disk")
@ -124,16 +124,21 @@ func (vm *VM) makeRuntime(ctx context.Context, msg *types.Message, origin addres
origin: origin,
originNonce: originNonce,
height: vm.blockHeight,
sys: vm.Syscalls,
gasUsed: usedGas,
gasAvailable: msg.GasLimit,
internalCallCounter: icc,
pricelist: PricelistByEpoch(vm.blockHeight),
}
rt.cst = &cbor.BasicIpldStore{
Blocks: &gasChargingBlocks{rt.ChargeGas, vm.cst.Blocks},
Blocks: &gasChargingBlocks{rt.ChargeGas, rt.pricelist, vm.cst.Blocks},
Atlas: vm.cst.Atlas,
}
rt.sys = pricedSyscalls{
under: vm.Syscalls,
chargeGas: rt.ChargeGas,
pl: rt.pricelist,
}
return rt
}
@ -193,6 +198,8 @@ func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
return nil, aerrors.Absorb(err, 1, "could not find source actor"), nil
}
gasUsed := gasCharge
toActor, err := st.GetActor(msg.To)
if err != nil {
if xerrors.Is(err, init_.ErrAddressNotFound) {
@ -201,12 +208,12 @@ func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
return nil, aerrors.Absorb(err, 1, "could not create account"), nil
}
toActor = a
gasUsed += PricelistByEpoch(vm.blockHeight).OnCreateActor()
} else {
return nil, aerrors.Escalate(err, "getting actor"), nil
}
}
gasUsed := gasCharge
origin := msg.From
on := msg.Nonce
var icc int64 = 0
@ -223,9 +230,12 @@ func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
}()
}
if types.BigCmp(msg.Value, types.NewInt(0)) != 0 {
rt.ChargeGas(gasFundTransfer)
aerr := rt.chargeGasSafe(rt.Pricelist().OnMethodInvocation(msg.Value, msg.Method))
if aerr != nil {
return nil, aerr, rt
}
if types.BigCmp(msg.Value, types.NewInt(0)) != 0 {
if err := Transfer(fromActor, toActor, msg.Value); err != nil {
return nil, aerrors.Absorb(err, 1, "failed to transfer funds"), nil
}
@ -273,11 +283,13 @@ func (vm *VM) ApplyMessage(ctx context.Context, msg *types.Message) (*ApplyRet,
return nil, err
}
pl := PricelistByEpoch(vm.blockHeight)
serMsg, err := msg.Serialize()
if err != nil {
return nil, xerrors.Errorf("could not serialize message: %w", err)
}
msgGasCost := int64(len(serMsg)) * gasPerMessageByte
msgGasCost := pl.OnChainMessage(len(serMsg))
// TODO: charge miner??
if msgGasCost > msg.GasLimit {
return &ApplyRet{
MessageReceipt: types.MessageReceipt{
@ -335,6 +347,14 @@ func (vm *VM) ApplyMessage(ctx context.Context, msg *types.Message) (*ApplyRet,
ret, actorErr, rt := vm.send(ctx, msg, nil, msgGasCost)
{
actorErr2 := rt.chargeGasSafe(rt.Pricelist().OnChainReturnValue(len(ret)))
if actorErr == nil {
//TODO: Ambigous what to do in this case
actorErr = actorErr2
}
}
if aerrors.IsFatal(actorErr) {
return nil, xerrors.Errorf("[from=%s,to=%s,n=%d,m=%d,h=%d] fatal error: %w", msg.From, msg.To, msg.Nonce, msg.Method, vm.blockHeight, actorErr)
}
@ -353,6 +373,9 @@ func (vm *VM) ApplyMessage(ctx context.Context, msg *types.Message) (*ApplyRet,
}
} else {
gasUsed = rt.gasUsed
if gasUsed < 0 {
gasUsed = 0
}
// refund unused gas
refund := types.BigMul(types.NewInt(uint64(msg.GasLimit-gasUsed)), msg.GasPrice)
if err := Transfer(gasHolder, fromActor, refund); err != nil {
@ -547,7 +570,6 @@ func (vm *VM) Invoke(act *types.Actor, rt *Runtime, method abi.MethodNum, params
defer func() {
rt.ctx = oldCtx
}()
rt.ChargeGas(gasInvoke)
ret, err := vm.inv.Invoke(act, rt, method, params)
if err != nil {
return nil, err

2
go.mod
View File

@ -11,7 +11,7 @@ require (
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
github.com/coreos/go-systemd/v22 v22.0.0
github.com/docker/go-units v0.4.0
github.com/filecoin-project/chain-validation v0.0.6-0.20200318065243-0ccb5ec3afc5
github.com/filecoin-project/chain-validation v0.0.6-0.20200320065847-72b36edba0fd
github.com/filecoin-project/filecoin-ffi v0.0.0-20200304181354-4446ff8a1bb9
github.com/filecoin-project/go-address v0.0.2-0.20200218010043-eb9bb40ed5be
github.com/filecoin-project/go-amt-ipld/v2 v2.0.1-0.20200131012142-05d80eeccc5e

6
go.sum
View File

@ -98,8 +98,8 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
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/filecoin-project/chain-validation v0.0.6-0.20200318065243-0ccb5ec3afc5 h1:cr9+8iX+u9fDV53MWqqZw820EyeWVX+h/HCz56JUWb0=
github.com/filecoin-project/chain-validation v0.0.6-0.20200318065243-0ccb5ec3afc5/go.mod h1:7HoEkq8OWN3vGcCZ4SRGxAPeL/mLckS+PNV3F0XmrCs=
github.com/filecoin-project/chain-validation v0.0.6-0.20200320065847-72b36edba0fd h1:4sf6dbvA/ZmCcU834shF9hEuHeC+CXoEnT+EIiZ95/0=
github.com/filecoin-project/chain-validation v0.0.6-0.20200320065847-72b36edba0fd/go.mod h1:YTLxUr6gOZpkUaXzLe7OZ4s1dpfJGp2FY/J2/K5DJqc=
github.com/filecoin-project/go-address v0.0.0-20200107215422-da8eea2842b5 h1:/MmWluswvDIbuPvBct4q6HeQgVm62O2DzWYTB38kt4A=
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 h1:TooKBwR/g8jG0hZ3lqe9S5sy2vTUcLOZLlz3M5wGn2E=
@ -133,8 +133,6 @@ github.com/filecoin-project/specs-actors v0.0.0-20200210130641-2d1fbd8672cf/go.m
github.com/filecoin-project/specs-actors v0.0.0-20200226200336-94c9b92b2775/go.mod h1:0HAWYrvajFHDgRaKbF0rl+IybVLZL5z4gQ8koCMPhoU=
github.com/filecoin-project/specs-actors v0.0.0-20200302223606-0eaf97b10aaf/go.mod h1:0HAWYrvajFHDgRaKbF0rl+IybVLZL5z4gQ8koCMPhoU=
github.com/filecoin-project/specs-actors v0.0.0-20200306000749-99e98e61e2a0/go.mod h1:0HAWYrvajFHDgRaKbF0rl+IybVLZL5z4gQ8koCMPhoU=
github.com/filecoin-project/specs-actors v0.0.0-20200311215506-e95895452888 h1:VCrkpFmZuQRyHrUpFTS3K/09cCQDMi/ZJUQ6c4zr1g4=
github.com/filecoin-project/specs-actors v0.0.0-20200311215506-e95895452888/go.mod h1:5WngRgTN5Eo4+0SjCBqLzEr2l6Mj45DrP2606gBhqI0=
github.com/filecoin-project/specs-actors v0.0.0-20200312030511-3f5510bf6130 h1:atiWEDtI/gzSm89fL+NyneLN3eHfBd1QPgOZyXPjA5M=
github.com/filecoin-project/specs-actors v0.0.0-20200312030511-3f5510bf6130/go.mod h1:5WngRgTN5Eo4+0SjCBqLzEr2l6Mj45DrP2606gBhqI0=
github.com/filecoin-project/specs-storage v0.0.0-20200303233430-1a5a408f7513 h1:okBx3lPomwDxlPmRvyP078BwivDfdxNUlpCDhDD0ia8=