Merge pull request #1184 from filecoin-project/feat/chain-bisect

chain: bisect cmd
This commit is contained in:
Łukasz Magiera 2020-02-04 04:43:31 +01:00 committed by GitHub
commit f837cb5513
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 122 additions and 13 deletions

View File

@ -34,31 +34,31 @@ type EPostProof struct {
}
type BlockHeader struct {
Miner address.Address
Miner address.Address // 0
Ticket *Ticket
Ticket *Ticket // 1
EPostProof EPostProof
EPostProof EPostProof // 2
Parents []cid.Cid
Parents []cid.Cid // 3
ParentWeight BigInt
ParentWeight BigInt // 4
Height uint64
Height uint64 // 5
ParentStateRoot cid.Cid
ParentStateRoot cid.Cid // 6
ParentMessageReceipts cid.Cid
ParentMessageReceipts cid.Cid // 7
Messages cid.Cid
Messages cid.Cid // 8
BLSAggregate Signature
BLSAggregate Signature // 9
Timestamp uint64
Timestamp uint64 // 10
BlockSig *Signature
BlockSig *Signature // 11
ForkSignaling uint64
ForkSignaling uint64 // 12
}
func (b *BlockHeader) ToStorageBlock() (block.Block, error) {

View File

@ -1,10 +1,13 @@
package cli
import (
"bytes"
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"time"
@ -28,6 +31,7 @@ var chainCmd = &cli.Command{
chainSetHeadCmd,
chainListCmd,
chainGetCmd,
chainBisectCmd,
chainExportCmd,
slashConsensusFault,
},
@ -402,6 +406,111 @@ func printTipSet(format string, ts *types.TipSet) {
fmt.Println(format)
}
var chainBisectCmd = &cli.Command{
Name: "bisect",
Usage: "bisect chain for an event",
Description: `Bisect the chain state tree:
lotus chain bisect [min height] [max height] '1/2/3/state/path' 'shell command' 'args'
Returns the first tipset in which condition is true
v
[start] FFFFFFFTTT [end]
Example: find height at which deal ID 100 000 appeared
- lotus chain bisect 1 32000 '@Ha:t03/1' jq -e '.[2] > 100000'
For special path elements see 'chain get' help
`,
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := ReqContext(cctx)
if cctx.Args().Len() < 4 {
return xerrors.New("need at least 4 args")
}
start, err := strconv.ParseUint(cctx.Args().Get(0), 10, 64)
if err != nil {
return err
}
end, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64)
if err != nil {
return err
}
subPath := cctx.Args().Get(2)
highest, err := api.ChainGetTipSetByHeight(ctx, end, nil)
if err != nil {
return err
}
prev := highest.Height()
for {
mid := (start + end) / 2
if end - start == 1 {
mid = end
start = end
}
midTs, err := api.ChainGetTipSetByHeight(ctx, mid, highest)
if err != nil {
return err
}
path := "/ipld/" + midTs.ParentState().String() + "/" + subPath
fmt.Printf("* Testing %d (%d - %d) (%s): ", mid, start, end, path)
nd, err := api.ChainGetNode(ctx, path)
if err != nil {
return err
}
b, err := json.MarshalIndent(nd, "", "\t")
if err != nil {
return err
}
cmd := exec.CommandContext(ctx, cctx.Args().Get(3), cctx.Args().Slice()[4:]...)
cmd.Stdin = bytes.NewReader(b)
var out bytes.Buffer
cmd.Stdout = &out
switch cmd.Run().(type) {
case nil:
// it's lower
end = mid
highest = midTs
fmt.Println("true")
case *exec.ExitError:
start = mid
fmt.Println("false")
default:
return err
}
if start == end {
if strings.TrimSpace(out.String()) == "true" {
fmt.Println(midTs.Height())
} else {
fmt.Println(prev)
}
return nil
}
prev = mid
}
},
}
var chainExportCmd = &cli.Command{
Name: "export",
Usage: "export chain to a car file",