Merge pull request #7881 from filecoin-project/raulk/fix/test-vector-runner

test vector runner fixes for v6 vectors
This commit is contained in:
Łukasz Magiera 2022-01-04 22:46:10 +01:00 committed by GitHub
commit 6f316cddbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 98 additions and 35 deletions

View File

@ -10,8 +10,9 @@ import (
addr "github.com/filecoin-project/go-address" addr "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/build"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/filecoin-project/lotus/build"
) )
type GasCharge struct { type GasCharge struct {
@ -81,7 +82,9 @@ type Pricelist interface {
OnVerifyConsensusFault() GasCharge OnVerifyConsensusFault() GasCharge
} }
var prices = map[abi.ChainEpoch]Pricelist{ // Prices are the price lists per starting epoch. Public for testing purposes
// (concretely to allow the test vector runner to rebase prices).
var Prices = map[abi.ChainEpoch]Pricelist{
abi.ChainEpoch(0): &pricelistV0{ abi.ChainEpoch(0): &pricelistV0{
computeGasMulti: 1, computeGasMulti: 1,
storageGasMulti: 1000, storageGasMulti: 1000,
@ -214,8 +217,8 @@ func PricelistByEpoch(epoch abi.ChainEpoch) Pricelist {
// since we are storing the prices as map or epoch to price // 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 // we need to get the price with the highest epoch that is lower or equal to the `epoch` arg
bestEpoch := abi.ChainEpoch(0) bestEpoch := abi.ChainEpoch(0)
bestPrice := prices[bestEpoch] bestPrice := Prices[bestEpoch]
for e, pl := range prices { for e, pl := range Prices {
// if `e` happened after `bestEpoch` and `e` is earlier or equal to the target `epoch` // if `e` happened after `bestEpoch` and `e` is earlier or equal to the target `epoch`
if e > bestEpoch && e <= epoch { if e > bestEpoch && e <= epoch {
bestEpoch = e bestEpoch = e

View File

@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/fs"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
@ -136,25 +137,31 @@ func processTipsetOpts() error {
} }
func execVectorDir(path string, outdir string) error { func execVectorDir(path string, outdir string) error {
files, err := filepath.Glob(filepath.Join(path, "*")) return filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
if err != nil { if err != nil {
return fmt.Errorf("failed to glob input directory %s: %w", path, err) return fmt.Errorf("failed while visiting path %s: %w", path, err)
} }
for _, f := range files { if d.IsDir() || !strings.HasSuffix(path, "json") {
outfile := strings.TrimSuffix(filepath.Base(f), filepath.Ext(f)) + ".out" return nil
}
// Create an output file to capture the output from the run of the vector.
outfile := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)) + ".out"
outpath := filepath.Join(outdir, outfile) outpath := filepath.Join(outdir, outfile)
outw, err := os.Create(outpath) outw, err := os.Create(outpath)
if err != nil { if err != nil {
return fmt.Errorf("failed to create file %s: %w", outpath, err) return fmt.Errorf("failed to create file %s: %w", outpath, err)
} }
log.Printf("processing vector %s; sending output to %s", f, outpath) log.Printf("processing vector %s; sending output to %s", path, outpath)
// Actually run the vector.
log.SetOutput(io.MultiWriter(os.Stderr, outw)) // tee the output. log.SetOutput(io.MultiWriter(os.Stderr, outw)) // tee the output.
_, _ = execVectorFile(new(conformance.LogReporter), f) _, _ = execVectorFile(new(conformance.LogReporter), path)
log.SetOutput(os.Stderr) log.SetOutput(os.Stderr)
_ = outw.Close() _ = outw.Close()
}
return nil return nil
})
} }
func execVectorsStdin() error { func execVectorsStdin() error {

View File

@ -5,6 +5,8 @@ import (
gobig "math/big" gobig "math/big"
"os" "os"
"github.com/filecoin-project/go-state-types/network"
"github.com/filecoin-project/lotus/blockstore" "github.com/filecoin-project/lotus/blockstore"
"github.com/filecoin-project/lotus/chain/consensus/filcns" "github.com/filecoin-project/lotus/chain/consensus/filcns"
"github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/state"
@ -187,11 +189,12 @@ func (d *Driver) ExecuteTipset(bs blockstore.Blockstore, ds ds.Batching, params
} }
type ExecuteMessageParams struct { type ExecuteMessageParams struct {
Preroot cid.Cid Preroot cid.Cid
Epoch abi.ChainEpoch Epoch abi.ChainEpoch
Message *types.Message Message *types.Message
CircSupply abi.TokenAmount CircSupply abi.TokenAmount
BaseFee abi.TokenAmount BaseFee abi.TokenAmount
NetworkVersion network.Version
// Rand is an optional vm.Rand implementation to use. If nil, the driver // Rand is an optional vm.Rand implementation to use. If nil, the driver
// will use a vm.Rand that returns a fixed value for all calls. // will use a vm.Rand that returns a fixed value for all calls.
@ -210,13 +213,6 @@ func (d *Driver) ExecuteMessage(bs blockstore.Blockstore, params ExecuteMessageP
params.Rand = NewFixedRand() params.Rand = NewFixedRand()
} }
// dummy state manager; only to reference the GetNetworkVersion method,
// which does not depend on state.
sm, err := stmgr.NewStateManager(nil, filcns.NewTipSetExecutor(), nil, filcns.DefaultUpgradeSchedule(), nil)
if err != nil {
return nil, cid.Cid{}, err
}
vmOpts := &vm.VMOpts{ vmOpts := &vm.VMOpts{
StateBase: params.Preroot, StateBase: params.Preroot,
Epoch: params.Epoch, Epoch: params.Epoch,
@ -227,7 +223,7 @@ func (d *Driver) ExecuteMessage(bs blockstore.Blockstore, params ExecuteMessageP
}, },
Rand: params.Rand, Rand: params.Rand,
BaseFee: params.BaseFee, BaseFee: params.BaseFee,
NetworkVersion: sm.GetNetworkVersion(context.Background(), params.Epoch), NetworkVersion: params.NetworkVersion,
} }
lvm, err := vm.NewVM(context.TODO(), vmOpts) lvm, err := vm.NewVM(context.TODO(), vmOpts)

View File

@ -7,6 +7,7 @@ import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math"
"os" "os"
"os/exec" "os/exec"
"strconv" "strconv"
@ -14,6 +15,7 @@ import (
"github.com/fatih/color" "github.com/fatih/color"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/go-state-types/network"
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
blocks "github.com/ipfs/go-block-format" blocks "github.com/ipfs/go-block-format"
"github.com/ipfs/go-blockservice" "github.com/ipfs/go-blockservice"
@ -27,6 +29,7 @@ import (
"github.com/filecoin-project/test-vectors/schema" "github.com/filecoin-project/test-vectors/schema"
"github.com/filecoin-project/lotus/blockstore" "github.com/filecoin-project/lotus/blockstore"
"github.com/filecoin-project/lotus/chain/consensus/filcns"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm" "github.com/filecoin-project/lotus/chain/vm"
) )
@ -50,11 +53,58 @@ var TipsetVectorOpts struct {
OnTipsetApplied []func(bs blockstore.Blockstore, params *ExecuteTipsetParams, res *ExecuteTipsetResult) OnTipsetApplied []func(bs blockstore.Blockstore, params *ExecuteTipsetParams, res *ExecuteTipsetResult)
} }
type GasPricingRestoreFn func()
// adjustGasPricing adjusts the global gas price mapping to make sure that the
// gas pricelist for vector's network version is used at the vector's epoch.
// Because it manipulates a global, it returns a function that reverts the
// change. The caller MUST invoke this function or the test vector runner will
// become invalid.
func adjustGasPricing(vectorEpoch abi.ChainEpoch, vectorNv network.Version) GasPricingRestoreFn {
// Stash the current pricing mapping.
// Ok to take a reference instead of a copy, because we override the map
// with a new one below.
var old = vm.Prices
// Resolve the epoch at which the vector network version kicks in.
var epoch abi.ChainEpoch = math.MaxInt64
if vectorNv == network.Version0 {
// genesis is not an upgrade.
epoch = 0
} else {
for _, u := range filcns.DefaultUpgradeSchedule() {
if u.Network == vectorNv {
epoch = u.Height
break
}
}
}
if epoch == math.MaxInt64 {
panic(fmt.Sprintf("could not resolve network version %d to height", vectorNv))
}
// Find the right pricelist for this network version.
pricelist := vm.PricelistByEpoch(epoch)
// Override the pricing mapping by setting the relevant pricelist for the
// network version at the epoch where the vector runs.
vm.Prices = map[abi.ChainEpoch]vm.Pricelist{
vectorEpoch: pricelist,
}
// Return a function to restore the original mapping.
return func() {
vm.Prices = old
}
}
// ExecuteMessageVector executes a message-class test vector. // ExecuteMessageVector executes a message-class test vector.
func ExecuteMessageVector(r Reporter, vector *schema.TestVector, variant *schema.Variant) (diffs []string, err error) { func ExecuteMessageVector(r Reporter, vector *schema.TestVector, variant *schema.Variant) (diffs []string, err error) {
var ( var (
ctx = context.Background() ctx = context.Background()
baseEpoch = variant.Epoch baseEpoch = abi.ChainEpoch(variant.Epoch)
nv = network.Version(variant.NetworkVersion)
root = vector.Pre.StateTree.RootCID root = vector.Pre.StateTree.RootCID
) )
@ -67,6 +117,10 @@ func ExecuteMessageVector(r Reporter, vector *schema.TestVector, variant *schema
// Create a new Driver. // Create a new Driver.
driver := NewDriver(ctx, vector.Selector, DriverOpts{DisableVMFlush: true}) driver := NewDriver(ctx, vector.Selector, DriverOpts{DisableVMFlush: true})
// Monkey patch the gas pricing.
revertFn := adjustGasPricing(baseEpoch, nv)
defer revertFn()
// Apply every message. // Apply every message.
for i, m := range vector.ApplyMessages { for i, m := range vector.ApplyMessages {
msg, err := types.DecodeMessage(m.Bytes) msg, err := types.DecodeMessage(m.Bytes)
@ -76,18 +130,19 @@ func ExecuteMessageVector(r Reporter, vector *schema.TestVector, variant *schema
// add the epoch offset if one is set. // add the epoch offset if one is set.
if m.EpochOffset != nil { if m.EpochOffset != nil {
baseEpoch += *m.EpochOffset baseEpoch += abi.ChainEpoch(*m.EpochOffset)
} }
// Execute the message. // Execute the message.
var ret *vm.ApplyRet var ret *vm.ApplyRet
ret, root, err = driver.ExecuteMessage(bs, ExecuteMessageParams{ ret, root, err = driver.ExecuteMessage(bs, ExecuteMessageParams{
Preroot: root, Preroot: root,
Epoch: abi.ChainEpoch(baseEpoch), Epoch: baseEpoch,
Message: msg, Message: msg,
BaseFee: BaseFeeOrDefault(vector.Pre.BaseFee), BaseFee: BaseFeeOrDefault(vector.Pre.BaseFee),
CircSupply: CircSupplyOrDefault(vector.Pre.CircSupply), CircSupply: CircSupplyOrDefault(vector.Pre.CircSupply),
Rand: NewReplayingRand(r, vector.Randomness), Rand: NewReplayingRand(r, vector.Randomness),
NetworkVersion: nv,
}) })
if err != nil { if err != nil {
r.Fatalf("fatal failure when executing message: %s", err) r.Fatalf("fatal failure when executing message: %s", err)
@ -184,8 +239,10 @@ func ExecuteTipsetVector(r Reporter, vector *schema.TestVector, variant *schema.
func AssertMsgResult(r Reporter, expected *schema.Receipt, actual *vm.ApplyRet, label string) { func AssertMsgResult(r Reporter, expected *schema.Receipt, actual *vm.ApplyRet, label string) {
r.Helper() r.Helper()
applyret := actual
if expected, actual := exitcode.ExitCode(expected.ExitCode), actual.ExitCode; expected != actual { if expected, actual := exitcode.ExitCode(expected.ExitCode), actual.ExitCode; expected != actual {
r.Errorf("exit code of msg %s did not match; expected: %s, got: %s", label, expected, actual) r.Errorf("exit code of msg %s did not match; expected: %s, got: %s", label, expected, actual)
r.Errorf("\t\\==> actor error: %s", applyret.ActorErr)
} }
if expected, actual := expected.GasUsed, actual.GasUsed; expected != actual { if expected, actual := expected.GasUsed, actual.GasUsed; expected != actual {
r.Errorf("gas used of msg %s did not match; expected: %d, got: %d", label, expected, actual) r.Errorf("gas used of msg %s did not match; expected: %d, got: %d", label, expected, actual)