Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: conformance & tvx: support ReportConsensusFault messages #8302

Merged
merged 5 commits into from
Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions cmd/tvx/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (
)

const (
PrecursorSelectAll = "all"
PrecursorSelectSender = "sender"
PrecursorSelectAll = "all"
PrecursorSelectParticipants = "participants"
)

type extractOpts struct {
Expand Down Expand Up @@ -86,12 +86,12 @@ var extractCmd = &cli.Command{
},
&cli.StringFlag{
Name: "precursor-select",
Usage: "precursors to apply; values: 'all', 'sender'; 'all' selects all preceding " +
"messages in the canonicalised tipset, 'sender' selects only preceding messages from the same " +
"sender. Usually, 'sender' is a good tradeoff and gives you sufficient accuracy. If the receipt sanity " +
Usage: "precursors to apply; values: 'all', 'participants'; 'all' selects all preceding " +
"messages in the canonicalised tipset, 'participants' selects only preceding messages from the same " +
"participants. Usually, 'participants' is a good tradeoff and gives you sufficient accuracy. If the receipt sanity " +
"check fails due to gas reasons, switch to 'all', as previous messages in the tipset may have " +
"affected state in a disruptive way",
Value: "sender",
Value: "participants",
Destination: &extractFlags.precursor,
},
&cli.BoolFlag{
Expand Down
9 changes: 5 additions & 4 deletions cmd/tvx/extract_many.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import (
"github.com/fatih/color"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/lotus/chain/consensus/filcns"
"github.com/hashicorp/go-multierror"
"github.com/ipfs/go-cid"
"github.com/multiformats/go-multihash"
"github.com/urfave/cli/v2"

"github.com/filecoin-project/lotus/chain/consensus/filcns"
)

var extractManyFlags struct {
Expand Down Expand Up @@ -176,7 +177,7 @@ func runExtractMany(c *cli.Context) error {
// Vector filename, using a base of outdir.
file := filepath.Join(outdir, actorcodename, methodname, exitcodename, id) + ".json"

log.Println(color.YellowString("processing message cid with 'sender' precursor mode: %s", id))
log.Println(color.YellowString("processing message cid with 'participants' precursor mode: %s", id))

opts := extractOpts{
id: id,
Expand All @@ -185,7 +186,7 @@ func runExtractMany(c *cli.Context) error {
cid: mcid,
file: file,
retain: "accessed-cids",
precursor: PrecursorSelectSender,
precursor: PrecursorSelectParticipants,
}

if err := doExtractMessage(opts); err != nil {
Expand All @@ -199,7 +200,7 @@ func runExtractMany(c *cli.Context) error {
generated = append(generated, file)
}

log.Printf("extractions to try with canonical precursor selection mode: %d", len(retry))
log.Printf("extractions to try with 'all' precursor selection mode: %d", len(retry))

for _, r := range retry {
log.Printf("retrying %s: %s", r.cid, r.id)
Expand Down
53 changes: 39 additions & 14 deletions cmd/tvx/extract_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func doExtractMessage(opts extractOpts) error {
return fmt.Errorf("failed to fetch messages in canonical order from inclusion tipset: %w", err)
}

related, found, err := findMsgAndPrecursors(opts.precursor, mcid, msg.From, msgs)
related, found, err := findMsgAndPrecursors(ctx, opts.precursor, mcid, msg.From, msg.To, msgs)
if err != nil {
return fmt.Errorf("failed while finding message and precursors: %w", err)
}
Expand Down Expand Up @@ -114,7 +114,7 @@ func doExtractMessage(opts extractOpts) error {
log.Printf("applying precursor %d, cid: %s", i, m.Cid())
_, root, err = driver.ExecuteMessage(pst.Blockstore, conformance.ExecuteMessageParams{
Preroot: root,
Epoch: execTs.Height(),
Epoch: incTs.Height(),
Message: m,
CircSupply: circSupplyDetail.FilCirculating,
BaseFee: basefee,
Expand All @@ -139,6 +139,7 @@ func doExtractMessage(opts extractOpts) error {
)

log.Printf("using state retention strategy: %s", retention)
log.Printf("now applying requested message: %s", msg.Cid())
switch retention {
case "accessed-cids":
tbs, ok := pst.Blockstore.(TracingBlockstore)
Expand All @@ -151,7 +152,7 @@ func doExtractMessage(opts extractOpts) error {
preroot = root
applyret, postroot, err = driver.ExecuteMessage(pst.Blockstore, conformance.ExecuteMessageParams{
Preroot: preroot,
Epoch: execTs.Height(),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The execution epoch was wrong, it should be the inclusion tipset.

Epoch: incTs.Height(),
Message: msg,
CircSupply: circSupplyDetail.FilCirculating,
BaseFee: basefee,
Expand Down Expand Up @@ -184,7 +185,7 @@ func doExtractMessage(opts extractOpts) error {
}
applyret, postroot, err = driver.ExecuteMessage(pst.Blockstore, conformance.ExecuteMessageParams{
Preroot: preroot,
Epoch: execTs.Height(),
Epoch: incTs.Height(),
Message: msg,
CircSupply: circSupplyDetail.FilCirculating,
BaseFee: basefee,
Expand Down Expand Up @@ -299,7 +300,7 @@ func doExtractMessage(opts extractOpts) error {
CAR: out.Bytes(),
Pre: &schema.Preconditions{
Variants: []schema.Variant{
{ID: codename, Epoch: int64(execTs.Height()), NetworkVersion: uint(nv)},
{ID: codename, Epoch: int64(incTs.Height()), NetworkVersion: uint(nv)},
},
CircSupply: circSupply.Int,
BaseFee: basefee.Int,
Expand Down Expand Up @@ -368,13 +369,13 @@ func resolveFromChain(ctx context.Context, api v0api.FullNode, mcid cid.Cid, blo
// types.EmptyTSK hints to use the HEAD.
execTs, err = api.ChainGetTipSetByHeight(ctx, blk.Height+1, types.EmptyTSK)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to get message execution tipset: %w", err)
return nil, nil, nil, fmt.Errorf("failed to get message execution tipset (%d) : %w", blk.Height+1, err)
}

// walk back from the execTs instead of HEAD, to save time.
incTs, err = api.ChainGetTipSetByHeight(ctx, blk.Height, execTs.Key())
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to get message inclusion tipset: %w", err)
return nil, nil, nil, fmt.Errorf("failed to get message inclusion tipset (%d): %w", blk.Height, err)
}

return msg, execTs, incTs, nil
Expand Down Expand Up @@ -403,19 +404,29 @@ func fetchThisAndPrevTipset(ctx context.Context, api v0api.FullNode, target type
// findMsgAndPrecursors ranges through the canonical messages slice, locating
// the target message and returning precursors in accordance to the supplied
// mode.
func findMsgAndPrecursors(mode string, msgCid cid.Cid, sender address.Address, msgs []api.Message) (related []*types.Message, found bool, err error) {
// Range through canonicalised messages, selecting only the precursors based
// on selection mode.
for _, other := range msgs {
func findMsgAndPrecursors(ctx context.Context, mode string, msgCid cid.Cid, sender address.Address, recipient address.Address, msgs []api.Message) (related []*types.Message, found bool, err error) {
// Resolve addresses to IDs for canonicality.
senderID := mustResolveAddr(ctx, sender)
recipientID := mustResolveAddr(ctx, recipient)

// Range through messages, selecting only the precursors based on selection mode.
for _, m := range msgs {
msgSenderID := mustResolveAddr(ctx, m.Message.From)
msgRecipientID := mustResolveAddr(ctx, m.Message.To)

switch {
case mode == PrecursorSelectAll:
fallthrough
case mode == PrecursorSelectSender && other.Message.From == sender:
related = append(related, other.Message)
case mode == PrecursorSelectParticipants &&
msgSenderID == senderID ||
msgRecipientID == recipientID ||
msgSenderID == recipientID ||
msgRecipientID == senderID:
Comment on lines +420 to +424
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A parentheses seems to be missing around the OR parts.

related = append(related, m.Message)
Comment on lines +420 to +425
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now find precursors by matching on all participants involved.

}

// this message is the target; we're done.
if other.Cid == msgCid {
if m.Cid == msgCid {
return related, true, nil
}
}
Expand All @@ -425,3 +436,17 @@ func findMsgAndPrecursors(mode string, msgCid cid.Cid, sender address.Address, m
// target).
return related, false, nil
}

var addressCache = make(map[address.Address]address.Address)

func mustResolveAddr(ctx context.Context, addr address.Address) address.Address {
if resolved, ok := addressCache[addr]; ok {
return resolved
}
id, err := FullAPI.StateLookupID(ctx, addr, types.EmptyTSK)
if err != nil {
panic(fmt.Errorf("failed to resolve addr: %w", err))
}
addressCache[addr] = id
return id
}
16 changes: 16 additions & 0 deletions conformance/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"

"github.com/filecoin-project/go-state-types/network"
cbor "github.com/ipfs/go-ipld-cbor"

"github.com/filecoin-project/lotus/blockstore"
"github.com/filecoin-project/lotus/chain/consensus/filcns"
Expand Down Expand Up @@ -199,6 +200,9 @@ type ExecuteMessageParams struct {
// 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.
Rand vm.Rand

// Lookback is the LookbackStateGetter; returns the state tree at a given epoch.
Lookback vm.LookbackStateGetter
}

// ExecuteMessage executes a conformance test vector message in a temporary VM.
Expand All @@ -213,6 +217,17 @@ func (d *Driver) ExecuteMessage(bs blockstore.Blockstore, params ExecuteMessageP
params.Rand = NewFixedRand()
}

// TODO: This lookback state returns the supplied precondition state tree, unconditionally.
// This is obviously not correct, but the lookback state tree is only used to validate the
// worker key when verifying a consensus fault. If the worker key hasn't changed in the
// current finality window, this workaround is enough.
// The correct solutions are documented in https://github.com/filecoin-project/ref-fvm/issues/381,
// but they're much harder to implement, and the tradeoffs aren't clear.
var lookback vm.LookbackStateGetter = func(ctx context.Context, epoch abi.ChainEpoch) (*state.StateTree, error) {
cst := cbor.NewCborStore(bs)
return state.LoadStateTree(cst, params.Preroot)
}

vmOpts := &vm.VMOpts{
StateBase: params.Preroot,
Epoch: params.Epoch,
Expand All @@ -224,6 +239,7 @@ func (d *Driver) ExecuteMessage(bs blockstore.Blockstore, params ExecuteMessageP
Rand: params.Rand,
BaseFee: params.BaseFee,
NetworkVersion: params.NetworkVersion,
LookbackState: lookback,
}

lvm, err := vm.NewVM(context.TODO(), vmOpts)
Expand Down