Merge remote-tracking branch 'origin/testnet/3' into feat/new-workers

This commit is contained in:
Łukasz Magiera 2020-03-20 23:42:46 +01:00
commit e6aa01653a
17 changed files with 754 additions and 60 deletions

3
.gitmodules vendored
View File

@ -2,3 +2,6 @@
path = extern/filecoin-ffi
url = https://github.com/filecoin-project/filecoin-ffi.git
branch = master
[submodule "extern/serialization-vectors"]
path = extern/serialization-vectors
url = https://github.com/filecoin-project/serialization-vectors

View File

@ -64,7 +64,7 @@ const MaxSealLookback = SealRandomnessLookbackLimit + 2000
// Mining
// Epochs
const EcRandomnessLookback = 300
const EcRandomnessLookback = 1
// /////
// Devnet settings

View File

@ -3,8 +3,8 @@ package gen
import (
"bytes"
"context"
"crypto/sha256"
"fmt"
"github.com/minio/blake2b-simd"
"io/ioutil"
"sync/atomic"
@ -276,8 +276,12 @@ func CarWalkFunc(nd format.Node) (out []*format.Link, err error) {
func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m address.Address, round int64) (*types.EPostProof, *types.Ticket, error) {
mc := &mca{w: cg.w, sm: cg.sm}
// TODO: REVIEW: Am I doing this correctly?
ticketRand, err := mc.ChainGetRandomness(ctx, pts.Key(), crypto.DomainSeparationTag_TicketProduction, pts.Height(), m.Bytes())
buf := new(bytes.Buffer)
if err := m.MarshalCBOR(buf); err != nil {
return nil, nil, xerrors.Errorf("failed to cbor marshal address: %w", err)
}
ticketRand, err := mc.ChainGetRandomness(ctx, pts.Key(), crypto.DomainSeparationTag_TicketProduction, abi.ChainEpoch(round-build.EcRandomnessLookback), buf.Bytes())
if err != nil {
return nil, nil, err
}
@ -549,7 +553,11 @@ type ProofInput struct {
}
func IsRoundWinner(ctx context.Context, ts *types.TipSet, round int64, miner address.Address, epp ElectionPoStProver, a MiningCheckAPI) (*ProofInput, error) {
epostRand, err := a.ChainGetRandomness(ctx, ts.Key(), crypto.DomainSeparationTag_ElectionPoStChallengeSeed, abi.ChainEpoch(round-build.EcRandomnessLookback), miner.Bytes())
buf := new(bytes.Buffer)
if err := miner.MarshalCBOR(buf); err != nil {
return nil, xerrors.Errorf("failed to cbor marshal address: %w")
}
epostRand, err := a.ChainGetRandomness(ctx, ts.Key(), crypto.DomainSeparationTag_ElectionPoStChallengeSeed, abi.ChainEpoch(round-build.EcRandomnessLookback), buf.Bytes())
if err != nil {
return nil, xerrors.Errorf("chain get randomness: %w", err)
}
@ -584,7 +592,7 @@ func IsRoundWinner(ctx context.Context, ts *types.TipSet, round int64, miner add
})
}
hvrf := sha256.Sum256(vrfout)
hvrf := blake2b.Sum256(vrfout)
candidates, err := epp.GenerateCandidates(ctx, sinfos, hvrf[:])
if err != nil {
return nil, xerrors.Errorf("failed to generate electionPoSt candidates: %w", err)

View File

@ -2,12 +2,14 @@ package state
import (
"context"
"fmt"
"testing"
"github.com/filecoin-project/specs-actors/actors/builtin"
address "github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/types"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
)
@ -227,3 +229,45 @@ func assertNotHas(t *testing.T, st *StateTree, addr address.Address) {
t.Fatal("shouldnt have found actor", addr)
}
}
func TestStateTreeConsistency(t *testing.T) {
cst := cbor.NewMemCborStore()
st, err := NewStateTree(cst)
if err != nil {
t.Fatal(err)
}
var addrs []address.Address
for i := 100; i < 150; i++ {
a, err := address.NewIDAddress(uint64(i))
if err != nil {
t.Fatal(err)
}
addrs = append(addrs, a)
}
randomCid, err := cid.Decode("bafy2bzacecu7n7wbtogznrtuuvf73dsz7wasgyneqasksdblxupnyovmtwxxu")
if err != nil {
t.Fatal(err)
}
for i, a := range addrs {
st.SetActor(a, &types.Actor{
Code: randomCid,
Head: randomCid,
Balance: types.NewInt(uint64(10000 + i)),
Nonce: uint64(1000 - i),
})
}
root, err := st.Flush(context.TODO())
if err != nil {
t.Fatal(err)
}
fmt.Println("root is: ", root)
if root.String() != "bafy2bzacec6igwshty4qqexix6iffzdawp5e4ke7mamfn35g3ga6rc3dyhgnc" {
t.Fatal("MISMATCH!")
}
}

View File

@ -1,10 +1,11 @@
package chain
import (
"bytes"
"context"
"crypto/sha256"
"errors"
"fmt"
"github.com/minio/blake2b-simd"
"sync"
"time"
@ -617,7 +618,11 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
})
tktsCheck := async.Err(func() error {
vrfBase, err := syncer.sm.ChainStore().GetRandomness(ctx, baseTs.Cids(), crypto.DomainSeparationTag_TicketProduction, int64(baseTs.Height()), h.Miner.Bytes())
buf := new(bytes.Buffer)
if err := h.Miner.MarshalCBOR(buf); err != nil {
return xerrors.Errorf("failed to marshal miner address to cbor: %w", err)
}
vrfBase, err := syncer.sm.ChainStore().GetRandomness(ctx, baseTs.Cids(), crypto.DomainSeparationTag_TicketProduction, int64(baseTs.Height()), buf.Bytes())
if err != nil {
return xerrors.Errorf("failed to get randomness for verifying election proof: %w", err)
}
@ -656,7 +661,11 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
}
func (syncer *Syncer) VerifyElectionPoStProof(ctx context.Context, h *types.BlockHeader, baseTs *types.TipSet, waddr address.Address) error {
rand, err := syncer.sm.ChainStore().GetRandomness(ctx, baseTs.Cids(), crypto.DomainSeparationTag_ElectionPoStChallengeSeed, int64(h.Height-build.EcRandomnessLookback), h.Miner.Bytes())
buf := new(bytes.Buffer)
if err := h.Miner.MarshalCBOR(buf); err != nil {
return xerrors.Errorf("failed to marshal miner to cbor: %w", err)
}
rand, err := syncer.sm.ChainStore().GetRandomness(ctx, baseTs.Cids(), crypto.DomainSeparationTag_ElectionPoStChallengeSeed, int64(h.Height-build.EcRandomnessLookback), buf.Bytes())
if err != nil {
return xerrors.Errorf("failed to get randomness for verifying election proof: %w", err)
}
@ -725,7 +734,7 @@ func (syncer *Syncer) VerifyElectionPoStProof(ctx context.Context, h *types.Bloc
// TODO: why do we need this here?
challengeCount := sectorbuilder.ElectionPostChallengeCount(uint64(len(sectorInfo)), 0)
hvrf := sha256.Sum256(h.EPostProof.PostRand)
hvrf := blake2b.Sum256(h.EPostProof.PostRand)
pvi := abi.PoStVerifyInfo{
Randomness: hvrf[:],
Candidates: candidates,

197
chain/vectors/gen/main.go Normal file
View File

@ -0,0 +1,197 @@
package main
import (
"encoding/json"
"fmt"
"math/rand"
"os"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/gen"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/mock"
"github.com/filecoin-project/lotus/chain/vectors"
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/abi/big"
"github.com/filecoin-project/specs-actors/actors/builtin/power"
"github.com/filecoin-project/specs-actors/actors/crypto"
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
)
func init() {
power.ConsensusMinerMinPower = big.NewInt(2048)
}
func MakeHeaderVectors() []vectors.HeaderVector {
cg, err := gen.NewGenerator()
if err != nil {
panic(err)
}
var out []vectors.HeaderVector
for i := 0; i < 5; i++ {
nts, err := cg.NextTipSet()
if err != nil {
panic(err)
}
h := nts.TipSet.Blocks[0].Header
data, err := h.Serialize()
if err != nil {
panic(err)
}
out = append(out, vectors.HeaderVector{
Block: h,
Cid: h.Cid().String(),
CborHex: fmt.Sprintf("%x", data),
})
}
return out
}
func MakeMessageSigningVectors() []vectors.MessageSigningVector {
w, err := wallet.NewWallet(wallet.NewMemKeyStore())
if err != nil {
panic(err)
}
blsk, err := w.GenerateKey(crypto.SigTypeBLS)
if err != nil {
panic(err)
}
bki, err := w.Export(blsk)
if err != nil {
panic(err)
}
to, err := address.NewIDAddress(99999)
if err != nil {
panic(err)
}
bmsg := mock.MkMessage(blsk, to, 55, w)
blsmsv := vectors.MessageSigningVector{
Unsigned: &bmsg.Message,
Cid: bmsg.Message.Cid().String(),
CidHexBytes: fmt.Sprintf("%x", bmsg.Message.Cid().Bytes()),
PrivateKey: bki.PrivateKey,
Signature: &bmsg.Signature,
}
secpk, err := w.GenerateKey(crypto.SigTypeBLS)
if err != nil {
panic(err)
}
ski, err := w.Export(secpk)
if err != nil {
panic(err)
}
smsg := mock.MkMessage(secpk, to, 55, w)
smsv := vectors.MessageSigningVector{
Unsigned: &smsg.Message,
Cid: smsg.Message.Cid().String(),
CidHexBytes: fmt.Sprintf("%x", smsg.Message.Cid().Bytes()),
PrivateKey: ski.PrivateKey,
Signature: &smsg.Signature,
}
return []vectors.MessageSigningVector{blsmsv, smsv}
}
func MakeUnsignedMessageVectors() []vectors.UnsignedMessageVector {
froms := []string{
"t2ch7krq7l35i74rebqbjdsp3ucl47t24e3juxjfa",
"t1pyfq7dg6sq65acyomqvzvbgwni4zllglqffw5dy",
"t1cyg66djxytxhzdq7ynoqfxk7xinp6xsejbeufli",
"t16n7vrq5humzoqll7zg4yw6dta645tuakcoalp6y",
"t1awsiuji4wpbxpzslg36f3wnfxzi4o5gq67tz2mi",
"t14mb3j32uuwajy5b2mliz63isp6zl5xkppzyuhfy",
"t1dzdmyzzdy6q5elobj63eokzv2xnwsp4vm5l6aka",
"t1svd45rkcfpsyqedvvhuv77yvllvu5ygmygjlvka",
"t1mrret5liwh46qde6qhaxrmcwil7jawjeqdijwfq",
"t1ly3ynedw74p4q3ytdnb4stjdkiodrl54moeyxea",
"t1uqexvn66gj4lxkbvmrgposwrlxbyd655o2nayyi",
"t1dwwjod7vw62jzw2eva7gtxohaidjhgh6w2rofui",
"t1slswisymmkfulmvl3jynrnwqi27tkvmsgzhztvy",
"t1e3vymxcdqfkqwz6e6wnxxx6ayuml3vxi5gef4xa",
"t1bgqopgk64ywpprka4citgi62aldclyaegvwvx6y",
"t1aizqgl2klzkzffwu35rufyuzefke2i6ndbewuhi",
"t1mzposcnsd2tc66yu5i3kajtrh5pvwohdjvitcey",
"t1x7xvs6oorrrlefyzn6wlbvaibzj3a2fyt4hsmvq",
"t1ez743nvc4j7qfirwnmxbh4qdqwha3iyalnq4rya",
"t17dvtgkop7cqgi6myjne5kzvrnsbg5wnowjphhwy",
"t1kvar5z3q7dwrfxjqsnuqpq5qsd7mvh2xypblwta",
}
var out []vectors.UnsignedMessageVector
for _, a := range froms {
from, err := address.NewFromString(a)
if err != nil {
panic(err)
}
to, err := address.NewIDAddress(rand.Uint64())
if err != nil {
panic(err)
}
params := make([]byte, 32)
rand.Read(params)
msg := &types.Message{
To: to,
From: from,
Value: types.NewInt(rand.Uint64()),
Method: abi.MethodNum(rand.Uint64()),
GasPrice: types.NewInt(rand.Uint64()),
GasLimit: rand.Int63(),
Nonce: rand.Uint64(),
Params: params,
}
ser, err := msg.Serialize()
if err != nil {
panic(err)
}
out = append(out, vectors.UnsignedMessageVector{
Message: msg,
HexCbor: fmt.Sprintf("%x", ser),
})
}
return out
}
func WriteJsonToFile(fname string, obj interface{}) error {
fi, err := os.Create(fname)
if err != nil {
return err
}
defer fi.Close()
out, err := json.MarshalIndent(obj, "", " ")
if err != nil {
return err
}
fi.Write(out)
return nil
}
func main() {
if err := WriteJsonToFile("block_headers.json", MakeHeaderVectors()); err != nil {
panic(err)
}
if err := WriteJsonToFile("message_signing.json", MakeMessageSigningVectors()); err != nil {
panic(err)
}
if err := WriteJsonToFile("unsigned_messages.json", MakeUnsignedMessageVectors()); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,25 @@
package vectors
import (
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/specs-actors/actors/crypto"
)
type HeaderVector struct {
Block *types.BlockHeader `json:"block"`
CborHex string `json:"cbor_hex"`
Cid string `json:"cid"`
}
type MessageSigningVector struct {
Unsigned *types.Message
Cid string
CidHexBytes string
PrivateKey []byte
Signature *crypto.Signature
}
type UnsignedMessageVector struct {
Message *types.Message `json:"message"`
HexCbor string `json:"hex_cbor"`
}

View File

@ -0,0 +1,45 @@
package vectors
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"testing"
)
func LoadVector(t *testing.T, f string, out interface{}) {
p := filepath.Join("../../extern/serialization-vectors", f)
fi, err := os.Open(p)
if err != nil {
t.Fatal(err)
}
if err := json.NewDecoder(fi).Decode(out); err != nil {
t.Fatal(err)
}
}
func TestBlockHeaderVectors(t *testing.T) {
var headers []HeaderVector
LoadVector(t, "block_headers.json", &headers)
for i, hv := range headers {
if hv.Block.Cid().String() != hv.Cid {
t.Fatalf("CID mismatch in test vector %d", i)
}
data, err := hv.Block.Serialize()
if err != nil {
t.Fatal(err)
}
if fmt.Sprintf("%x", data) != hv.CborHex {
t.Fatalf("serialized data mismatched for test vector %d", i)
}
}
}
func TestMessageSigningVectors(t *testing.T) {
// TODO:
}

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
@ -44,8 +44,7 @@ type Runtime struct {
originNonce uint64
internalExecutions []*ExecutionResult
// the first internal call has a value of 1 for this field
internalCallCounter int64
numActorsCreated uint64
}
func (rs *Runtime) ResolveAddress(address address.Address) (ret address.Address, ok bool) {
@ -78,13 +77,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)
}
}()
@ -168,7 +165,7 @@ func (rt *Runtime) NewActorAddress() address.Address {
if err := binary.Write(&b, binary.BigEndian, rt.originNonce); err != nil {
rt.Abortf(exitcode.ErrSerialization, "writing nonce address into a buffer: %v", err)
}
if err := binary.Write(&b, binary.BigEndian, rt.internalCallCounter); err != nil { // TODO: expose on vm
if err := binary.Write(&b, binary.BigEndian, rt.numActorsCreated); err != nil { // TODO: expose on vm
rt.Abortf(exitcode.ErrSerialization, "writing callSeqNum address into a buffer: %v", err)
}
addr, err := address.NewActorAddress(b.Bytes())
@ -176,10 +173,12 @@ func (rt *Runtime) NewActorAddress() address.Address {
rt.Abortf(exitcode.ErrSerialization, "create actor address: %v", err)
}
rt.incrementNumActorsCreated()
return addr
}
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 {
@ -339,7 +340,7 @@ func (rt *Runtime) internalSend(to address.Address, method abi.MethodNum, value
if subrt != nil {
er.Subcalls = subrt.internalExecutions
rt.internalCallCounter = subrt.internalCallCounter
rt.numActorsCreated = subrt.numActorsCreated
}
rt.internalExecutions = append(rt.internalExecutions, &er)
return ret, errSend
@ -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,24 @@ 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
}
func (rt *Runtime) incrementNumActorsCreated() {
rt.numActorsCreated++
}

View File

@ -4,6 +4,7 @@ import (
"fmt"
"reflect"
"runtime"
"strings"
"testing"
suites "github.com/filecoin-project/chain-validation/suites"
@ -37,6 +38,9 @@ func init() {
/* tests to skip go here */
tipset.TestInternalMessageApplicationFailure,
tipset.TestInvalidSenderAddress,
tipset.TestBlockMessageDeduplication,
tipset.TestMinerSubmitFallbackPoSt,
tipset.TestMinerMissPoStChallengeWindow,
}}
}
@ -46,7 +50,9 @@ func TestChainValidationMessageSuite(t *testing.T) {
if TestSuiteSkipper.Skip(testCase) {
continue
}
testCase(t, f)
t.Run(caseName(testCase), func(t *testing.T) {
testCase(t, f)
})
}
}
@ -56,6 +62,14 @@ func TestChainValidationTipSetSuite(t *testing.T) {
if TestSuiteSkipper.Skip(testCase) {
continue
}
testCase(t, f)
t.Run(caseName(testCase), func(t *testing.T) {
testCase(t, f)
})
}
}
func caseName(testCase suites.TestCase) string {
fqName := runtime.FuncForPC(reflect.ValueOf(testCase).Pointer()).Name()
toks := strings.Split(fqName, ".")
return toks[len(toks)-1]
}

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")
@ -115,7 +115,7 @@ func (bs *gasChargingBlocks) Put(blk block.Block) error {
return nil
}
func (vm *VM) makeRuntime(ctx context.Context, msg *types.Message, origin address.Address, originNonce uint64, usedGas int64, icc int64) *Runtime {
func (vm *VM) makeRuntime(ctx context.Context, msg *types.Message, origin address.Address, originNonce uint64, usedGas int64, nac uint64) *Runtime {
rt := &Runtime{
ctx: ctx,
vm: vm,
@ -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,
gasUsed: usedGas,
gasAvailable: msg.GasLimit,
numActorsCreated: nac,
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,31 +208,34 @@ 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
var nac uint64 = 0
if parent != nil {
gasUsed = parent.gasUsed + gasUsed
origin = parent.origin
on = parent.originNonce
icc = parent.internalCallCounter + 1
nac = parent.numActorsCreated
}
rt := vm.makeRuntime(ctx, msg, origin, on, gasUsed, icc)
rt := vm.makeRuntime(ctx, msg, origin, on, gasUsed, nac)
if parent != nil {
defer func() {
parent.gasUsed = rt.gasUsed
}()
}
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{
@ -288,10 +300,6 @@ func (vm *VM) ApplyMessage(ctx context.Context, msg *types.Message) (*ApplyRet,
}
st := vm.cstate
if err := st.Snapshot(ctx); err != nil {
return nil, xerrors.Errorf("snapshot failed: %w", err)
}
defer st.ClearSnapshot()
fromActor, err := st.GetActor(msg.From)
if err != nil {
@ -310,7 +318,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, msg *types.Message) (*ApplyRet,
return &ApplyRet{
MessageReceipt: types.MessageReceipt{
ExitCode: exitcode.SysErrInvalidCallSeqNum,
GasUsed: msg.GasLimit,
GasUsed: 0,
},
}, nil
}
@ -333,8 +341,21 @@ func (vm *VM) ApplyMessage(ctx context.Context, msg *types.Message) (*ApplyRet,
fromActor.Nonce++
if err := st.Snapshot(ctx); err != nil {
return nil, xerrors.Errorf("snapshot failed: %w", err)
}
defer st.ClearSnapshot()
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 +374,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 +571,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

1
extern/serialization-vectors vendored Submodule

@ -0,0 +1 @@
Subproject commit 1e778d5bd77f758e83a18c41d10c2649b0e70fef

2
go.mod
View File

@ -12,7 +12,7 @@ require (
github.com/coreos/go-systemd/v22 v22.0.0
github.com/docker/go-units v0.4.0
github.com/elastic/go-sysinfo v1.3.0
github.com/filecoin-project/chain-validation v0.0.6-0.20200318065243-0ccb5ec3afc5
github.com/filecoin-project/chain-validation v0.0.6-0.20200320210432-2793319e9867
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

@ -102,8 +102,8 @@ github.com/elastic/go-windows v1.0.0 h1:qLURgZFkkrYyTTkvYpsZIgf83AUsdIHfvlJaqaZ7
github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU=
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.20200320210432-2793319e9867 h1:6+9Khz+vidBfWG7xQ6a2uRpLnd3RhMVcOwJxu3XhvgI=
github.com/filecoin-project/chain-validation v0.0.6-0.20200320210432-2793319e9867/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=
@ -141,8 +141,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=

View File

@ -1,12 +1,13 @@
package miner
import (
"bytes"
"context"
"fmt"
"sync"
"time"
"github.com/filecoin-project/go-address"
address "github.com/filecoin-project/go-address"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/crypto"
lru "github.com/hashicorp/golang-lru"
@ -348,7 +349,12 @@ func (m *Miner) computeTicket(ctx context.Context, addr address.Address, base *M
return nil, err
}
input, err := m.api.ChainGetRandomness(ctx, base.ts.Key(), crypto.DomainSeparationTag_TicketProduction, base.ts.Height(), addr.Bytes())
buf := new(bytes.Buffer)
if err := addr.MarshalCBOR(buf); err != nil {
return nil, xerrors.Errorf("failed to marshal address to cbor: %w", err)
}
input, err := m.api.ChainGetRandomness(ctx, base.ts.Key(), crypto.DomainSeparationTag_TicketProduction, base.ts.Height(), buf.Bytes())
if err != nil {
return nil, err
}