tvx extract: more tipset extraction goodness.

- ability to extract a tipset range into individual vectors.
- ability to extract a tipset range and squash into a single multi-tipset vector.
- mark statediff output deterministically, so it can be extracted by tooling.
- ability to execute callbacks between tipsets in the driver.
- implement save-balances callback.
This commit is contained in:
Raúl Kripalani
2020-12-27 18:58:35 +00:00
parent bb5a92e2f4
commit cd032d5418
10 changed files with 487 additions and 192 deletions
+2 -2
View File
@@ -11,7 +11,7 @@ import (
"github.com/filecoin-project/test-vectors/schema"
)
var invokees = map[schema.Class]func(Reporter, *schema.TestVector, *schema.Variant){
var invokees = map[schema.Class]func(Reporter, *schema.TestVector, *schema.Variant) ([]string, error){
schema.ClassMessage: ExecuteMessageVector,
schema.ClassTipset: ExecuteTipsetVector,
}
@@ -133,7 +133,7 @@ func TestConformance(t *testing.T) {
for _, variant := range vector.Pre.Variants {
variant := variant
t.Run(variant.ID, func(t *testing.T) {
invokee(t, &vector, &variant)
_, _ = invokee(t, &vector, &variant) //nolint:errcheck
})
}
})
+3
View File
@@ -71,6 +71,9 @@ type ExecuteTipsetResult struct {
AppliedMessages []*types.Message
// AppliedResults stores the results of AppliedMessages, in the same order.
AppliedResults []*vm.ApplyRet
// PostBaseFee returns the basefee after applying this tipset.
PostBaseFee abi.TokenAmount
}
type ExecuteTipsetParams struct {
+60 -18
View File
@@ -14,6 +14,7 @@ import (
"github.com/fatih/color"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/hashicorp/go-multierror"
blocks "github.com/ipfs/go-block-format"
"github.com/ipfs/go-blockservice"
"github.com/ipfs/go-cid"
@@ -38,8 +39,19 @@ var FallbackBlockstoreGetter interface {
ChainReadObj(context.Context, cid.Cid) ([]byte, error)
}
var TipsetVectorOpts struct {
// PipelineBaseFee pipelines the basefee in multi-tipset vectors from one
// tipset to another. Basefees in the vector are ignored, except for that of
// the first tipset. UNUSED.
PipelineBaseFee bool
// OnTipsetApplied contains callback functions called after a tipset has been
// applied.
OnTipsetApplied []func(bs blockstore.Blockstore, params *ExecuteTipsetParams, res *ExecuteTipsetResult)
}
// ExecuteMessageVector executes a message-class test vector.
func ExecuteMessageVector(r Reporter, vector *schema.TestVector, variant *schema.Variant) {
func ExecuteMessageVector(r Reporter, vector *schema.TestVector, variant *schema.Variant) (diffs []string, err error) {
var (
ctx = context.Background()
baseEpoch = variant.Epoch
@@ -88,14 +100,16 @@ func ExecuteMessageVector(r Reporter, vector *schema.TestVector, variant *schema
// Once all messages are applied, assert that the final state root matches
// the expected postcondition root.
if expected, actual := vector.Post.StateTree.RootCID, root; expected != actual {
r.Errorf("wrong post root cid; expected %v, but got %v", expected, actual)
dumpThreeWayStateDiff(r, vector, bs, root)
r.FailNow()
ierr := fmt.Errorf("wrong post root cid; expected %v, but got %v", expected, actual)
r.Errorf(ierr.Error())
err = multierror.Append(err, ierr)
diffs = dumpThreeWayStateDiff(r, vector, bs, root)
}
return diffs, err
}
// ExecuteTipsetVector executes a tipset-class test vector.
func ExecuteTipsetVector(r Reporter, vector *schema.TestVector, variant *schema.Variant) {
func ExecuteTipsetVector(r Reporter, vector *schema.TestVector, variant *schema.Variant) (diffs []string, err error) {
var (
ctx = context.Background()
baseEpoch = abi.ChainEpoch(variant.Epoch)
@@ -107,6 +121,7 @@ func ExecuteTipsetVector(r Reporter, vector *schema.TestVector, variant *schema.
bs, err := LoadBlockstore(vector.CAR)
if err != nil {
r.Fatalf("failed to load the vector CAR: %w", err)
return nil, err
}
// Create a new Driver.
@@ -118,15 +133,22 @@ func ExecuteTipsetVector(r Reporter, vector *schema.TestVector, variant *schema.
for i, ts := range vector.ApplyTipsets {
ts := ts // capture
execEpoch := baseEpoch + abi.ChainEpoch(ts.EpochOffset)
ret, err := driver.ExecuteTipset(bs, tmpds, ExecuteTipsetParams{
params := ExecuteTipsetParams{
Preroot: root,
ParentEpoch: prevEpoch,
Tipset: &ts,
ExecEpoch: execEpoch,
Rand: NewReplayingRand(r, vector.Randomness),
})
}
ret, err := driver.ExecuteTipset(bs, tmpds, params)
if err != nil {
r.Fatalf("failed to apply tipset %d: %s", i, err)
return nil, err
}
// invoke callbacks.
for _, cb := range TipsetVectorOpts.OnTipsetApplied {
cb(bs, &params, ret)
}
for j, v := range ret.AppliedResults {
@@ -136,7 +158,9 @@ func ExecuteTipsetVector(r Reporter, vector *schema.TestVector, variant *schema.
// Compare the receipts root.
if expected, actual := vector.Post.ReceiptsRoots[i], ret.ReceiptsRoot; expected != actual {
r.Errorf("post receipts root doesn't match; expected: %s, was: %s", expected, actual)
ierr := fmt.Errorf("post receipts root doesn't match; expected: %s, was: %s", expected, actual)
r.Errorf(ierr.Error())
err = multierror.Append(err, ierr)
}
prevEpoch = execEpoch
@@ -146,10 +170,12 @@ func ExecuteTipsetVector(r Reporter, vector *schema.TestVector, variant *schema.
// Once all messages are applied, assert that the final state root matches
// the expected postcondition root.
if expected, actual := vector.Post.StateTree.RootCID, root; expected != actual {
r.Errorf("wrong post root cid; expected %v, but got %v", expected, actual)
dumpThreeWayStateDiff(r, vector, bs, root)
r.FailNow()
ierr := fmt.Errorf("wrong post root cid; expected %v, but got %v", expected, actual)
r.Errorf(ierr.Error())
err = multierror.Append(err, ierr)
diffs = dumpThreeWayStateDiff(r, vector, bs, root)
}
return diffs, err
}
// AssertMsgResult compares a message result. It takes the expected receipt
@@ -169,7 +195,7 @@ func AssertMsgResult(r Reporter, expected *schema.Receipt, actual *vm.ApplyRet,
}
}
func dumpThreeWayStateDiff(r Reporter, vector *schema.TestVector, bs blockstore.Blockstore, actual cid.Cid) {
func dumpThreeWayStateDiff(r Reporter, vector *schema.TestVector, bs blockstore.Blockstore, actual cid.Cid) []string {
// check if statediff exists; if not, skip.
if err := exec.Command("statediff", "--help").Run(); err != nil {
r.Log("could not dump 3-way state tree diff upon test failure: statediff command not found")
@@ -178,7 +204,7 @@ func dumpThreeWayStateDiff(r Reporter, vector *schema.TestVector, bs blockstore.
r.Log("$ cd statediff")
r.Log("$ go generate ./...")
r.Log("$ go install ./cmd/statediff")
return
return nil
}
tmpCar, err := writeStateToTempCAR(bs,
@@ -188,6 +214,7 @@ func dumpThreeWayStateDiff(r Reporter, vector *schema.TestVector, bs blockstore.
)
if err != nil {
r.Fatalf("failed to write temporary state CAR: %s", err)
return nil
}
defer os.RemoveAll(tmpCar) //nolint:errcheck
@@ -202,28 +229,43 @@ func dumpThreeWayStateDiff(r Reporter, vector *schema.TestVector, bs blockstore.
d3 = color.New(color.FgGreen, color.Bold).Sprint("[Δ3]")
)
printDiff := func(left, right cid.Cid) {
diff := func(left, right cid.Cid) string {
cmd := exec.Command("statediff", "car", "--file", tmpCar, left.String(), right.String())
b, err := cmd.CombinedOutput()
if err != nil {
r.Fatalf("statediff failed: %s", err)
}
r.Log(string(b))
return string(b)
}
bold := color.New(color.Bold).SprintfFunc()
r.Log(bold("-----BEGIN STATEDIFF-----"))
// run state diffs.
r.Log(bold("=== dumping 3-way diffs between %s, %s, %s ===", a, b, c))
r.Log(bold("--- %s left: %s; right: %s ---", d1, a, b))
printDiff(vector.Post.StateTree.RootCID, actual)
diffA := diff(vector.Post.StateTree.RootCID, actual)
r.Log(bold("----------BEGIN STATEDIFF A----------"))
r.Log(diffA)
r.Log(bold("----------END STATEDIFF A----------"))
r.Log(bold("--- %s left: %s; right: %s ---", d2, c, b))
printDiff(vector.Pre.StateTree.RootCID, actual)
diffB := diff(vector.Pre.StateTree.RootCID, actual)
r.Log(bold("----------BEGIN STATEDIFF B----------"))
r.Log(diffB)
r.Log(bold("----------END STATEDIFF B----------"))
r.Log(bold("--- %s left: %s; right: %s ---", d3, c, a))
printDiff(vector.Pre.StateTree.RootCID, vector.Post.StateTree.RootCID)
diffC := diff(vector.Pre.StateTree.RootCID, vector.Post.StateTree.RootCID)
r.Log(bold("----------BEGIN STATEDIFF C----------"))
r.Log(diffC)
r.Log(bold("----------END STATEDIFF C----------"))
r.Log(bold("-----END STATEDIFF-----"))
return []string{diffA, diffB, diffC}
}
// writeStateToTempCAR writes the provided roots to a temporary CAR that'll be