diff --git a/commands/chain.go b/commands/chain.go index 69d3e5087e..6c47e0c398 100644 --- a/commands/chain.go +++ b/commands/chain.go @@ -19,8 +19,9 @@ var chainCmd = &cmds.Command{ Tagline: "Inspect the filecoin blockchain", }, Subcommands: map[string]*cmds.Command{ - "head": chainHeadCmd, - "ls": chainLsCmd, + "head": chainHeadCmd, + "ls": chainLsCmd, + "block": chainBlockCmd, }, } @@ -56,12 +57,19 @@ var chainLsCmd = &cmds.Command{ }, Options: []cmdkit.Option{ cmdkit.BoolOption("long", "l", "List blocks in long format, including CID, Miner, StateRoot, block height and message count respectively"), + cmdkit.Uint64Option("begin", "b", "The block height of smallest height, default for genesis"), + cmdkit.Uint64Option("end", "e", "The block height of largest height, default for head"), }, Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error { iter, err := GetPorcelainAPI(env).ChainLs(req.Context) if err != nil { return err } + beginHeight, _ := req.Options["begin"].(uint64) + endHeight, _ := req.Options["end"].(uint64) + + fmt.Println("beginHeight", beginHeight, "endHeight", endHeight) + for ; !iter.Complete(); err = iter.Next() { if err != nil { return err @@ -69,6 +77,13 @@ var chainLsCmd = &cmds.Command{ if !iter.Value().Defined() { panic("tipsets from this iterator should have at least one member") } + height, _ := iter.Value().Height() + if height < beginHeight { + return nil + } + if endHeight != 0 && height > endHeight { + continue + } if err := re.Emit(iter.Value().ToSlice()); err != nil { return err } @@ -108,3 +123,63 @@ var chainLsCmd = &cmds.Command{ }), }, } + +var chainBlockCmd = &cmds.Command{ + Helptext: cmdkit.HelpText{ + Tagline: "Show a filecoin block by its CID", + ShortDescription: `Prints the miner, parent weight, height, +and nonce of a given block. If JSON encoding is specified with the --enc flag, +all other block properties will be included as well.`, + }, + Arguments: []cmdkit.Argument{ + cmdkit.StringArg("cid", true, false, "CID of block to show"), + }, + Options: []cmdkit.Option{ + cmdkit.BoolOption("messages", "m", "show messages in block"), + }, + Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error { + cid, err := cid.Decode(req.Arguments[0]) + if err != nil { + return err + } + + block, err := GetPorcelainAPI(env).ChainGetBlock(req.Context, cid) + if err != nil { + return err + } + + return re.Emit(block) + }, + Type: types.Block{}, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, block *types.Block) error { + wStr, err := types.FixedStr(uint64(block.ParentWeight)) + if err != nil { + return err + } + + _, err = fmt.Fprintf(w, `Block Details +Miner: %s +Weight: %s +Height: %s +Nonce: %s +Timestamp: %s +`, + block.Miner, + wStr, + strconv.FormatUint(uint64(block.Height), 10), + strconv.FormatUint(uint64(block.Nonce), 10), + strconv.FormatUint(uint64(block.Timestamp), 10), + ) + if err != nil { + return err + } + + showMessages, _ := req.Options["messages"].(bool) + if showMessages == true { + _, err = fmt.Fprintf(w, `Messages: %s`+"\n", block.Messages) + } + return err + }), + }, +} diff --git a/commands/chain_daemon_test.go b/commands/chain_daemon_test.go index 1050a5488c..2dade579e0 100644 --- a/commands/chain_daemon_test.go +++ b/commands/chain_daemon_test.go @@ -127,4 +127,83 @@ func TestChainLs(t *testing.T) { assert.Contains(t, chainLsResult, `"height":"1"`) assert.Contains(t, chainLsResult, `"nonce":"0"`) }) + + t.Run("chain ls --begin --end with height range", func(t *testing.T) { + daemon := makeTestDaemonWithMinerAndStart(t) + defer daemon.ShutdownSuccess() + + daemon.RunSuccess("mining", "once", "--enc", "text") + daemon.RunSuccess("mining", "once", "--enc", "text") + daemon.RunSuccess("mining", "once", "--enc", "text") + daemon.RunSuccess("mining", "once", "--enc", "text") + chainLsResult := daemon.RunSuccess("chain", "ls", "--long", "-b", "1", "-e", "3", "--enc", "json").ReadStdoutTrimNewlines() + assert.Contains(t, chainLsResult, `"height":"1"`) + assert.Contains(t, chainLsResult, `"height":"2"`) + assert.Contains(t, chainLsResult, `"height":"3"`) + assert.NotContains(t, chainLsResult, `"height":"0"`) + assert.NotContains(t, chainLsResult, `"height":"4"`) + assert.Contains(t, chainLsResult, `"nonce":"0"`) + }) +} + +func TestBlockDaemon(t *testing.T) { + tf.IntegrationTest(t) + + t.Run("chain block returns human readable output for the filecoin block", func(t *testing.T) { + d := makeTestDaemonWithMinerAndStart(t) + defer d.ShutdownSuccess() + + // mine a block and get its CID + minedBlockCidStr := th.RunSuccessFirstLine(d, "mining", "once") + + // get the mined block by its CID + output := d.RunSuccess("chain", "block", minedBlockCidStr).ReadStdoutTrimNewlines() + + assert.Contains(t, output, "Block Details") + assert.Contains(t, output, "Weight: 0") + assert.Contains(t, output, "Height: 1") + assert.Contains(t, output, "Nonce: 0") + assert.Contains(t, output, "Timestamp: ") + }) + + t.Run("chain block --messages returns human readable output for the filecoin block including messages", func(t *testing.T) { + d := makeTestDaemonWithMinerAndStart(t) + defer d.ShutdownSuccess() + + // mine a block and get its CID + minedBlockCidStr := th.RunSuccessFirstLine(d, "mining", "once") + + // get the mined block by its CID + output := d.RunSuccess("chain", "block", "--messages", minedBlockCidStr).ReadStdoutTrimNewlines() + + assert.Contains(t, output, "Block Details") + assert.Contains(t, output, "Weight: 0") + assert.Contains(t, output, "Height: 1") + assert.Contains(t, output, "Nonce: 0") + assert.Contains(t, output, "Timestamp: ") + assert.Contains(t, output, "Messages: ") + }) + + t.Run("chain block --enc json returns JSON for a filecoin block", func(t *testing.T) { + d := th.NewDaemon(t, + th.KeyFile(fixtures.KeyFilePaths()[0]), + th.WithMiner(fixtures.TestMiners[0])).Start() + defer d.ShutdownSuccess() + + // mine a block and get its CID + minedBlockCidStr := th.RunSuccessFirstLine(d, "mining", "once") + + // get the mined block by its CID + blockGetLine := th.RunSuccessFirstLine(d, "chain", "block", minedBlockCidStr, "--enc", "json") + var blockGetBlock types.Block + require.NoError(t, json.Unmarshal([]byte(blockGetLine), &blockGetBlock)) + + // ensure that we were returned the correct block + + require.Equal(t, minedBlockCidStr, blockGetBlock.Cid().String()) + + // ensure that the JSON we received from block get conforms to schema + + requireSchemaConformance(t, []byte(blockGetLine), "filecoin_block") + }) } diff --git a/commands/main.go b/commands/main.go index 9ca66d07d2..2715b0cf1c 100644 --- a/commands/main.go +++ b/commands/main.go @@ -107,7 +107,6 @@ VIEW DATA STRUCTURES go-filecoin chain - Inspect the filecoin blockchain go-filecoin dag - Interact with IPLD DAG objects go-filecoin deals - Manage deals made by or with this node - go-filecoin show - Get human-readable representations of filecoin objects NETWORK COMMANDS go-filecoin bitswap - Explore libp2p bitswap @@ -179,7 +178,6 @@ var rootSubcmdsDaemon = map[string]*cmds.Command{ "ping": pingCmd, "protocol": protocolCmd, "retrieval-client": retrievalClientCmd, - "show": showCmd, "stats": statsCmd, "swarm": swarmCmd, "wallet": walletCmd, diff --git a/commands/message_daemon_test.go b/commands/message_daemon_test.go index c7583c7a16..7a429f123e 100644 --- a/commands/message_daemon_test.go +++ b/commands/message_daemon_test.go @@ -142,7 +142,7 @@ func TestMessageSendBlockGasLimit(t *testing.T) { blockCid := d.RunSuccess("mining", "once").ReadStdoutTrimNewlines() - blockInfo := d.RunSuccess("show", "block", blockCid, "--enc", "json").ReadStdoutTrimNewlines() + blockInfo := d.RunSuccess("chain", "block", blockCid, "--enc", "json").ReadStdoutTrimNewlines() require.NoError(t, json.Unmarshal([]byte(blockInfo), &result)) assert.NotEmpty(t, result.Messages, "msg under the block gas limit passes validation and is run in the block") diff --git a/commands/show.go b/commands/show.go deleted file mode 100644 index e1a63e241a..0000000000 --- a/commands/show.go +++ /dev/null @@ -1,82 +0,0 @@ -package commands - -import ( - "fmt" - "io" - "strconv" - - "github.com/ipfs/go-cid" - "github.com/ipfs/go-ipfs-cmdkit" - "github.com/ipfs/go-ipfs-cmds" - - "github.com/filecoin-project/go-filecoin/types" -) - -var showCmd = &cmds.Command{ - Helptext: cmdkit.HelpText{ - Tagline: "Get human-readable representations of filecoin objects", - }, - Subcommands: map[string]*cmds.Command{ - "block": showBlockCmd, - }, -} - -var showBlockCmd = &cmds.Command{ - Helptext: cmdkit.HelpText{ - Tagline: "Show a filecoin block by its CID", - ShortDescription: `Prints the miner, parent weight, height, -and nonce of a given block. If JSON encoding is specified with the --enc flag, -all other block properties will be included as well.`, - }, - Arguments: []cmdkit.Argument{ - cmdkit.StringArg("cid", true, false, "CID of block to show"), - }, - Options: []cmdkit.Option{ - cmdkit.BoolOption("messages", "m", "show messages in block"), - }, - Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error { - cid, err := cid.Decode(req.Arguments[0]) - if err != nil { - return err - } - - block, err := GetPorcelainAPI(env).ChainGetBlock(req.Context, cid) - if err != nil { - return err - } - - return re.Emit(block) - }, - Type: types.Block{}, - Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, block *types.Block) error { - wStr, err := types.FixedStr(uint64(block.ParentWeight)) - if err != nil { - return err - } - - _, err = fmt.Fprintf(w, `Block Details -Miner: %s -Weight: %s -Height: %s -Nonce: %s -Timestamp: %s -`, - block.Miner, - wStr, - strconv.FormatUint(uint64(block.Height), 10), - strconv.FormatUint(uint64(block.Nonce), 10), - strconv.FormatUint(uint64(block.Timestamp), 10), - ) - if err != nil { - return err - } - - showMessages, _ := req.Options["messages"].(bool) - if showMessages == true { - _, err = fmt.Fprintf(w, `Messages: %s`+"\n", block.Messages) - } - return err - }), - }, -} diff --git a/commands/show_test.go b/commands/show_test.go deleted file mode 100644 index 83a8fe7676..0000000000 --- a/commands/show_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package commands_test - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/filecoin-project/go-filecoin/fixtures" - th "github.com/filecoin-project/go-filecoin/testhelpers" - tf "github.com/filecoin-project/go-filecoin/testhelpers/testflags" - "github.com/filecoin-project/go-filecoin/types" -) - -func TestBlockDaemon(t *testing.T) { - tf.IntegrationTest(t) - - t.Run("show block returns human readable output for the filecoin block", func(t *testing.T) { - d := makeTestDaemonWithMinerAndStart(t) - defer d.ShutdownSuccess() - - // mine a block and get its CID - minedBlockCidStr := th.RunSuccessFirstLine(d, "mining", "once") - - // get the mined block by its CID - output := d.RunSuccess("show", "block", minedBlockCidStr).ReadStdoutTrimNewlines() - - assert.Contains(t, output, "Block Details") - assert.Contains(t, output, "Weight: 0") - assert.Contains(t, output, "Height: 1") - assert.Contains(t, output, "Nonce: 0") - assert.Contains(t, output, "Timestamp: ") - }) - - t.Run("show block --messages returns human readable output for the filecoin block including messages", func(t *testing.T) { - d := makeTestDaemonWithMinerAndStart(t) - defer d.ShutdownSuccess() - - // mine a block and get its CID - minedBlockCidStr := th.RunSuccessFirstLine(d, "mining", "once") - - // get the mined block by its CID - output := d.RunSuccess("show", "block", "--messages", minedBlockCidStr).ReadStdoutTrimNewlines() - - assert.Contains(t, output, "Block Details") - assert.Contains(t, output, "Weight: 0") - assert.Contains(t, output, "Height: 1") - assert.Contains(t, output, "Nonce: 0") - assert.Contains(t, output, "Timestamp: ") - assert.Contains(t, output, "Messages: ") - }) - - t.Run("show block --enc json returns JSON for a filecoin block", func(t *testing.T) { - d := th.NewDaemon(t, - th.KeyFile(fixtures.KeyFilePaths()[0]), - th.WithMiner(fixtures.TestMiners[0])).Start() - defer d.ShutdownSuccess() - - // mine a block and get its CID - minedBlockCidStr := th.RunSuccessFirstLine(d, "mining", "once") - - // get the mined block by its CID - blockGetLine := th.RunSuccessFirstLine(d, "show", "block", minedBlockCidStr, "--enc", "json") - var blockGetBlock types.Block - require.NoError(t, json.Unmarshal([]byte(blockGetLine), &blockGetBlock)) - - // ensure that we were returned the correct block - - require.Equal(t, minedBlockCidStr, blockGetBlock.Cid().String()) - - // ensure that the JSON we received from block get conforms to schema - - requireSchemaConformance(t, []byte(blockGetLine), "filecoin_block") - }) -} diff --git a/tools/fast/action_chain.go b/tools/fast/action_chain.go index 7952684c6b..530c333e83 100644 --- a/tools/fast/action_chain.go +++ b/tools/fast/action_chain.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" + "github.com/filecoin-project/go-filecoin/types" "github.com/ipfs/go-cid" ) @@ -21,3 +22,16 @@ func (f *Filecoin) ChainHead(ctx context.Context) ([]cid.Cid, error) { func (f *Filecoin) ChainLs(ctx context.Context) (*json.Decoder, error) { return f.RunCmdLDJSONWithStdin(ctx, nil, "go-filecoin", "chain", "ls") } + +// ChainBlock runs the `chain block` command against the filecoin process +func (f *Filecoin) ChainBlock(ctx context.Context, ref cid.Cid) (*types.Block, error) { + var out types.Block + + sRef := ref.String() + + if err := f.RunCmdJSONWithStdin(ctx, nil, &out, "go-filecoin", "chain", "block", sRef); err != nil { + return nil, err + } + + return &out, nil +} diff --git a/tools/fast/action_show.go b/tools/fast/action_show.go deleted file mode 100644 index 437cb15504..0000000000 --- a/tools/fast/action_show.go +++ /dev/null @@ -1,22 +0,0 @@ -package fast - -import ( - "context" - - "github.com/ipfs/go-cid" - - "github.com/filecoin-project/go-filecoin/types" -) - -// ShowBlock runs the `show block` command against the filecoin process -func (f *Filecoin) ShowBlock(ctx context.Context, ref cid.Cid) (*types.Block, error) { - var out types.Block - - sRef := ref.String() - - if err := f.RunCmdJSONWithStdin(ctx, nil, &out, "go-filecoin", "show", "block", sRef); err != nil { - return nil, err - } - - return &out, nil -} diff --git a/tools/fast/series/get_head_block_height.go b/tools/fast/series/get_head_block_height.go index 9d7a8b2b4f..db7eb418b0 100644 --- a/tools/fast/series/get_head_block_height.go +++ b/tools/fast/series/get_head_block_height.go @@ -14,7 +14,7 @@ func GetHeadBlockHeight(ctx context.Context, client *fast.Filecoin) (*types.Bloc return nil, err } - block, err := client.ShowBlock(ctx, tipset[0]) + block, err := client.ChainBlock(ctx, tipset[0]) if err != nil { return nil, err }