bd10bdf99a
* build: Bump version to v1.17.3-dev * build: set version to v1.18.0-dev * chore: actors: Allow builtin-actors to return a map of methods (#9342) * Allow builtin-actors to return a map of methods * go mod * Fix tests * Fix tests, check carefully please * Delete lotus-pond (#9352) * feat: add StateNetworkVersion to mpool API * chore: refactor: rename NewestNetworkVersion * feat: actors: Integrate datacap actor into lotus (#9348) * Integrate datacap actor * Implement datacap actor in chain/builtin * feat: support typed errors over RPC * chore: deps: update to go-jsonrpc 0.1.8 * remove duplicate import * fix: itest: check for closed connection * chore: refactor: move retry test to API * address magik supernit * Add ability to only have single partition per msg for partitions with recovery sectors * doc gen * Address comments * Return beneficiary info from miner state Info() * Update builtin-actors to dev/20220922-v9 which includes FIP-0045 changes in progress * Integrate verifreg changes to lotus * Setup datacap actor * Update builtin-actors to dev/20220922-v9-1 * Update datacap actor to query datacap instead of verifreg * update gst * update markets * update actors with hamt fix * update gst * Update datacap to parse tokens * Update bundles * datacap and verifreg actors use ID addresses without protocol byte * update builtin-actors to rc1 * update go-fil-markets * Update bundles to rc2 * Integrate the v9 migration * Add api for getting allocation * Add upgrade epoch for butterfly * Tweak PreSeal struct to be infra-friendly * docsgen * More tweaking of PreSeal for genesis * review fixes * Use fake cid for test * add butterfly artifacts for oct 5 upgrade * check datacaps for v8 verifreg match v9 datacap actor * Remove print statements * Update to go-state-types master * Update to go-state-types v0.9.0-rc1 * review fixes * use go-fil-markets v1.24.0-v17 * Add accessors for allocations and claims maps * fix: missing permissions tag * butterfly * update butterfly artifacts * sealing pipeline: Prepare deal assigning logic for FIP-45 * sealing pipeline: Get allocationId with StateApi * use NoAllocationID instead of nil AllocationId * address review * Add datacap actor to registry.go * Add cli for listing allocations and removing expired allocations * Update to go-state-types master * deps: upgrade go-merkledag to 0.8.0 * shark params * Update cli/filplus.go Co-authored-by: Aayush Rajasekaran <arajasek94@gmail.com> * revert change to verifreg util * docsgen-cli * miss the stuff * Update FFI * Update go-state-types to v0.9.0 * Update builtin-actors to v9.0.0 * add calib upgrade epcoh * update the upgrade envvar * kill shark * Remove fvm splash banner from nv17 upgrade * check invariance for pending deals and allocations * check pending verified deal proposal migrated to allocation * Add check for unsealed CID in precommit sectors * Fix counting of allocations in nv17 migration test * make gen * pass state trees as pointers * Add assertion that migrations with & without cache are the same * compare allocation to verified deal proposal * Fix miner state precommit info * fix migration test tool * add changelog * Update to go-state-types v0.9.1 * Integrate builtin-actors v9.0.1 * chore: ver: bump version for rc3 (#9512) * Bump version to 1.18.0-rc3 * Update CHANGELOG.md * Update CHANGELOG.md Co-authored-by: Aayush Rajasekaran <arajasek94@gmail.com> * Update CHANGELOG.md Co-authored-by: Aayush Rajasekaran <arajasek94@gmail.com> Co-authored-by: Jiaying Wang <42981373+jennijuju@users.noreply.github.com> Co-authored-by: Aayush Rajasekaran <arajasek94@gmail.com> * Migration: Use autobatch bs * Fix autobatch Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai> * Invoker: Use MethodMeta from go-state-types * Add a second premigration for nv17 * Add more shed tools for migration checking * address review * Lotus release v1.18.0-rc4 * fix: ci: fix app-image build on ci (#9527) * Remove old go version first * Add GO_VERSION file * Use GO_VERSION to set / verify go version * mv GO_VERSION GO_VERSION_MIN * Use GO_VERSION_MIN in Makefile check Co-authored-by: Ian Davis <jungziege@gmail.com> * Update to latest go-state-types for migration fixes * go mod tidy * fix: use api.ErrActorNotFound instead of types.ErrActorNotFound * fix: add fields to ForkUpgradeParams * docs: update actors_version_checklist.md * chore: fix lint * update to go state type v0.9.6 with market migration fix (#9545) * update go-state-types to v-0.9.7 * Add invariant checks to migration * fix invariant check: number of entries in datacap actor should include verifreg * Invariant checks: Only include not-activated deals * test: nv17 migration * Address review * add lotus-shed invariance method * Migration cli takes a stateroot cid and a height * make gen * Update to builtin-actors v9.0.2 * Failing test that shows that notaries can remove datacap from the verifreg actor * Test that should pass when the problem is solved * make gen * Review fixes * statemanager call function will return call information even if call errors * update go-state-types * update builtin-actors * bubble up errors properly from ApplyImplicitMessage * bump to rc5 * set new upgrade heights for calibnet * set new upgrade height for butterfly * tweak calibnet upgrade schedule * clarify changelog note about calibnet * butterfly * update calibnet artifacts * Allow setting local bundles for Debug FVM for av 9+ * fix: autobatch: remove potential deadlock when a block is missing Check the _underlying_ blockstore instead of recursing. Also, drop the lock before we do that. * fix imports * build: set shark mainnet epoch (#9640) * chore: build: Lotus release v1.18.0 (#9641) * Lotus release v1.18.0 * add changelog * address review * changelog improvement Co-authored-by: Jennifer Wang <jiayingw703@gmail.com> Co-authored-by: Jiaying Wang <42981373+jennijuju@users.noreply.github.com> Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai> Co-authored-by: Łukasz Magiera <magik6k@gmail.com> Co-authored-by: Łukasz Magiera <magik6k@users.noreply.github.com> Co-authored-by: Aayush <arajasek94@gmail.com> Co-authored-by: Geoff Stuart <geoff.vball@gmail.com> Co-authored-by: Shrenuj Bansal <shrenuj.bansal@protocol.ai> Co-authored-by: simlecode <69969590+simlecode@users.noreply.github.com> Co-authored-by: Rod Vagg <rod@vagg.org> Co-authored-by: Jakub Sztandera <kubuxu@protocol.ai> Co-authored-by: Ian Davis <jungziege@gmail.com> Co-authored-by: zenground0 <ZenGround0@users.noreply.github.com> Co-authored-by: Steven Allen <steven@stebalien.com>
560 lines
16 KiB
Go
560 lines
16 KiB
Go
// stm: #unit
|
|
package cli
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/golang/mock/gomock"
|
|
"github.com/ipfs/go-cid"
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/filecoin-project/go-state-types/abi"
|
|
"github.com/filecoin-project/go-state-types/big"
|
|
"github.com/filecoin-project/specs-actors/v7/actors/builtin"
|
|
|
|
"github.com/filecoin-project/lotus/api"
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
"github.com/filecoin-project/lotus/chain/types/mock"
|
|
)
|
|
|
|
func TestChainHead(t *testing.T) {
|
|
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("chain", ChainHeadCmd))
|
|
defer done()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
ts := mock.TipSet(mock.MkBlock(nil, 0, 0))
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainHead(ctx).Return(ts, nil),
|
|
)
|
|
|
|
//stm: @CLI_CHAIN_HEAD_001
|
|
err := app.Run([]string{"chain", "head"})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Regexp(t, regexp.MustCompile(ts.Cids()[0].String()), buf.String())
|
|
}
|
|
|
|
func TestGetBlock(t *testing.T) {
|
|
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("chain", ChainGetBlock))
|
|
defer done()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
block := mock.MkBlock(nil, 0, 0)
|
|
blockMsgs := api.BlockMessages{}
|
|
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainGetBlock(ctx, block.Cid()).Return(block, nil),
|
|
mockApi.EXPECT().ChainGetBlockMessages(ctx, block.Cid()).Return(&blockMsgs, nil),
|
|
mockApi.EXPECT().ChainGetParentMessages(ctx, block.Cid()).Return([]api.Message{}, nil),
|
|
mockApi.EXPECT().ChainGetParentReceipts(ctx, block.Cid()).Return([]*types.MessageReceipt{}, nil),
|
|
)
|
|
|
|
//stm: @CLI_CHAIN_GET_BLOCK_001
|
|
err := app.Run([]string{"chain", "getblock", block.Cid().String()})
|
|
assert.NoError(t, err)
|
|
|
|
// expected output format
|
|
out := struct {
|
|
types.BlockHeader
|
|
BlsMessages []*types.Message
|
|
SecpkMessages []*types.SignedMessage
|
|
ParentReceipts []*types.MessageReceipt
|
|
ParentMessages []cid.Cid
|
|
}{}
|
|
|
|
err = json.Unmarshal(buf.Bytes(), &out)
|
|
assert.NoError(t, err)
|
|
|
|
assert.True(t, block.Cid().Equals(out.Cid()))
|
|
}
|
|
|
|
func TestReadOjb(t *testing.T) {
|
|
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("chain", ChainReadObjCmd))
|
|
defer done()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
block := mock.MkBlock(nil, 0, 0)
|
|
obj := new(bytes.Buffer)
|
|
err := block.MarshalCBOR(obj)
|
|
assert.NoError(t, err)
|
|
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainReadObj(ctx, block.Cid()).Return(obj.Bytes(), nil),
|
|
)
|
|
|
|
//stm: @CLI_CHAIN_READ_OBJECT_001
|
|
err = app.Run([]string{"chain", "read-obj", block.Cid().String()})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, buf.String(), fmt.Sprintf("%x\n", obj.Bytes()))
|
|
}
|
|
|
|
func TestChainDeleteObj(t *testing.T) {
|
|
cmd := WithCategory("chain", ChainDeleteObjCmd)
|
|
block := mock.MkBlock(nil, 0, 0)
|
|
|
|
// given no force flag, it should return an error and no API calls should be made
|
|
t.Run("no-really-do-it", func(t *testing.T) {
|
|
app, _, _, done := NewMockAppWithFullAPI(t, cmd)
|
|
defer done()
|
|
|
|
//stm: @CLI_CHAIN_DELETE_OBJECT_002
|
|
err := app.Run([]string{"chain", "delete-obj", block.Cid().String()})
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
// given a force flag, it calls API delete
|
|
t.Run("really-do-it", func(t *testing.T) {
|
|
app, mockApi, buf, done := NewMockAppWithFullAPI(t, cmd)
|
|
defer done()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainDeleteObj(ctx, block.Cid()).Return(nil),
|
|
)
|
|
|
|
//stm: @CLI_CHAIN_DELETE_OBJECT_001
|
|
err := app.Run([]string{"chain", "delete-obj", "--really-do-it=true", block.Cid().String()})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Contains(t, buf.String(), block.Cid().String())
|
|
})
|
|
}
|
|
|
|
func TestChainStatObj(t *testing.T) {
|
|
cmd := WithCategory("chain", ChainStatObjCmd)
|
|
block := mock.MkBlock(nil, 0, 0)
|
|
stat := api.ObjStat{Size: 123, Links: 321}
|
|
|
|
checkOutput := func(buf *bytes.Buffer) {
|
|
out := buf.String()
|
|
outSplit := strings.Split(out, "\n")
|
|
|
|
assert.Contains(t, outSplit[0], fmt.Sprintf("%d", stat.Links))
|
|
assert.Contains(t, outSplit[1], fmt.Sprintf("%d", stat.Size))
|
|
}
|
|
|
|
// given no --base flag, it calls ChainStatObj with base=cid.Undef
|
|
t.Run("no-base", func(t *testing.T) {
|
|
app, mockApi, buf, done := NewMockAppWithFullAPI(t, cmd)
|
|
defer done()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainStatObj(ctx, block.Cid(), cid.Undef).Return(stat, nil),
|
|
)
|
|
|
|
//stm: @CLI_CHAIN_STAT_OBJECT_001
|
|
err := app.Run([]string{"chain", "stat-obj", block.Cid().String()})
|
|
assert.NoError(t, err)
|
|
|
|
checkOutput(buf)
|
|
})
|
|
|
|
// given a --base flag, it calls ChainStatObj with that base
|
|
t.Run("base", func(t *testing.T) {
|
|
app, mockApi, buf, done := NewMockAppWithFullAPI(t, cmd)
|
|
defer done()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainStatObj(ctx, block.Cid(), block.Cid()).Return(stat, nil),
|
|
)
|
|
|
|
//stm: @CLI_CHAIN_STAT_OBJECT_002
|
|
err := app.Run([]string{"chain", "stat-obj", fmt.Sprintf("-base=%s", block.Cid().String()), block.Cid().String()})
|
|
assert.NoError(t, err)
|
|
|
|
checkOutput(buf)
|
|
})
|
|
}
|
|
|
|
func TestChainGetMsg(t *testing.T) {
|
|
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("chain", ChainGetMsgCmd))
|
|
defer done()
|
|
|
|
addrs, err := mock.RandomActorAddresses(12345, 2)
|
|
assert.NoError(t, err)
|
|
|
|
from := addrs[0]
|
|
to := addrs[1]
|
|
|
|
msg := mock.UnsignedMessage(*from, *to, 0)
|
|
|
|
obj := new(bytes.Buffer)
|
|
err = msg.MarshalCBOR(obj)
|
|
assert.NoError(t, err)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainReadObj(ctx, msg.Cid()).Return(obj.Bytes(), nil),
|
|
)
|
|
|
|
//stm: @CLI_CHAIN_GET_MESSAGE_001
|
|
err = app.Run([]string{"chain", "getmessage", msg.Cid().String()})
|
|
assert.NoError(t, err)
|
|
|
|
var out types.Message
|
|
err = json.Unmarshal(buf.Bytes(), &out)
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, *msg, out)
|
|
}
|
|
|
|
func TestSetHead(t *testing.T) {
|
|
cmd := WithCategory("chain", ChainSetHeadCmd)
|
|
genesis := mock.TipSet(mock.MkBlock(nil, 0, 0))
|
|
ts := mock.TipSet(mock.MkBlock(genesis, 1, 0))
|
|
epoch := abi.ChainEpoch(uint64(0))
|
|
|
|
// given the -genesis flag, resets head to genesis ignoring the provided ts positional argument
|
|
t.Run("genesis", func(t *testing.T) {
|
|
app, mockApi, _, done := NewMockAppWithFullAPI(t, cmd)
|
|
defer done()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainGetGenesis(ctx).Return(genesis, nil),
|
|
mockApi.EXPECT().ChainSetHead(ctx, genesis.Key()).Return(nil),
|
|
)
|
|
|
|
//stm: @CLI_CHAIN_SET_HEAD_003
|
|
err := app.Run([]string{"chain", "sethead", "-genesis=true", ts.Key().String()})
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
// given the -epoch flag, resets head to given epoch, ignoring the provided ts positional argument
|
|
t.Run("epoch", func(t *testing.T) {
|
|
app, mockApi, _, done := NewMockAppWithFullAPI(t, cmd)
|
|
defer done()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainGetTipSetByHeight(ctx, epoch, types.EmptyTSK).Return(genesis, nil),
|
|
mockApi.EXPECT().ChainSetHead(ctx, genesis.Key()).Return(nil),
|
|
)
|
|
|
|
//stm: @CLI_CHAIN_SET_HEAD_002
|
|
err := app.Run([]string{"chain", "sethead", fmt.Sprintf("-epoch=%s", epoch), ts.Key().String()})
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
// given no flag, resets the head to given tipset key
|
|
t.Run("default", func(t *testing.T) {
|
|
app, mockApi, _, done := NewMockAppWithFullAPI(t, cmd)
|
|
defer done()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainGetBlock(ctx, ts.Key().Cids()[0]).Return(ts.Blocks()[0], nil),
|
|
mockApi.EXPECT().ChainSetHead(ctx, ts.Key()).Return(nil),
|
|
)
|
|
|
|
//stm: @CLI_CHAIN_SET_HEAD_001
|
|
err := app.Run([]string{"chain", "sethead", ts.Key().Cids()[0].String()})
|
|
assert.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestInspectUsage(t *testing.T) {
|
|
cmd := WithCategory("chain", ChainInspectUsage)
|
|
ts := mock.TipSet(mock.MkBlock(nil, 0, 0))
|
|
|
|
addrs, err := mock.RandomActorAddresses(12345, 2)
|
|
assert.NoError(t, err)
|
|
|
|
from := addrs[0]
|
|
to := addrs[1]
|
|
|
|
msg := mock.UnsignedMessage(*from, *to, 0)
|
|
msgs := []api.Message{{Cid: msg.Cid(), Message: msg}}
|
|
|
|
actor := &types.Actor{
|
|
Code: builtin.StorageMarketActorCodeID,
|
|
Nonce: 0,
|
|
Balance: big.NewInt(1000000000),
|
|
}
|
|
|
|
t.Run("default", func(t *testing.T) {
|
|
app, mockApi, buf, done := NewMockAppWithFullAPI(t, cmd)
|
|
defer done()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainHead(ctx).Return(ts, nil),
|
|
mockApi.EXPECT().ChainGetParentMessages(ctx, ts.Blocks()[0].Cid()).Return(msgs, nil),
|
|
mockApi.EXPECT().ChainGetTipSet(ctx, ts.Parents()).Return(nil, nil),
|
|
mockApi.EXPECT().StateGetActor(ctx, *to, ts.Key()).Return(actor, nil),
|
|
)
|
|
|
|
//stm: @CLI_CHAIN_INSPECT_USAGE_001
|
|
err := app.Run([]string{"chain", "inspect-usage"})
|
|
assert.NoError(t, err)
|
|
|
|
out := buf.String()
|
|
|
|
// output is plaintext, had to do string matching
|
|
assert.Contains(t, out, from.String())
|
|
assert.Contains(t, out, to.String())
|
|
// check for gas by sender
|
|
assert.Contains(t, out, "By Sender")
|
|
// check for gas by method
|
|
assert.Contains(t, out, "By Method:\nSend")
|
|
})
|
|
}
|
|
|
|
func TestChainList(t *testing.T) {
|
|
cmd := WithCategory("chain", ChainListCmd)
|
|
genesis := mock.TipSet(mock.MkBlock(nil, 0, 0))
|
|
blk := mock.MkBlock(genesis, 0, 0)
|
|
blk.Height = 1
|
|
head := mock.TipSet(blk)
|
|
|
|
addrs, err := mock.RandomActorAddresses(12345, 2)
|
|
assert.NoError(t, err)
|
|
|
|
from := addrs[0]
|
|
to := addrs[1]
|
|
|
|
msg := mock.UnsignedMessage(*from, *to, 0)
|
|
msgs := []api.Message{{Cid: msg.Cid(), Message: msg}}
|
|
blockMsgs := &api.BlockMessages{}
|
|
receipts := []*types.MessageReceipt{}
|
|
|
|
t.Run("default", func(t *testing.T) {
|
|
app, mockApi, buf, done := NewMockAppWithFullAPI(t, cmd)
|
|
defer done()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
// same method gets called mocked multiple times bcs it's called in a for loop for all tipsets (2 in this case)
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainHead(ctx).Return(head, nil),
|
|
mockApi.EXPECT().ChainGetTipSet(ctx, head.Parents()).Return(genesis, nil),
|
|
mockApi.EXPECT().ChainGetBlockMessages(ctx, genesis.Blocks()[0].Cid()).Return(blockMsgs, nil),
|
|
mockApi.EXPECT().ChainGetParentMessages(ctx, head.Blocks()[0].Cid()).Return(msgs, nil),
|
|
mockApi.EXPECT().ChainGetParentReceipts(ctx, head.Blocks()[0].Cid()).Return(receipts, nil),
|
|
mockApi.EXPECT().ChainGetBlockMessages(ctx, head.Blocks()[0].Cid()).Return(blockMsgs, nil),
|
|
)
|
|
|
|
//stm: CLI_CHAIN_LIST_001
|
|
err := app.Run([]string{"chain", "love", "--gas-stats=true"}) // chain is love ❤️
|
|
assert.NoError(t, err)
|
|
|
|
out := buf.String()
|
|
|
|
// should print out 2 blocks, indexed with 0: and 1:
|
|
assert.Contains(t, out, "0:")
|
|
assert.Contains(t, out, "1:")
|
|
})
|
|
}
|
|
|
|
func TestChainGet(t *testing.T) {
|
|
blk := mock.MkBlock(nil, 0, 0)
|
|
ts := mock.TipSet(blk)
|
|
cmd := WithCategory("chain", ChainGetCmd)
|
|
|
|
// given no -as-type flag & ipfs prefix, should print object as JSON if it's marshalable
|
|
t.Run("ipfs", func(t *testing.T) {
|
|
path := fmt.Sprintf("/ipfs/%s", blk.Cid().String())
|
|
|
|
app, mockApi, buf, done := NewMockAppWithFullAPI(t, cmd)
|
|
defer done()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainGetNode(ctx, path).Return(&api.IpldObject{Cid: blk.Cid(), Obj: blk}, nil),
|
|
)
|
|
|
|
//stm: @CLI_CHAIN_GET_001
|
|
err := app.Run([]string{"chain", "get", path})
|
|
assert.NoError(t, err)
|
|
|
|
var out types.BlockHeader
|
|
err = json.Unmarshal(buf.Bytes(), &out)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, *blk, out)
|
|
})
|
|
|
|
// given no -as-type flag & ipfs prefix, should traverse from head.ParentStateRoot and print JSON if it's marshalable
|
|
t.Run("pstate", func(t *testing.T) {
|
|
p1 := "/pstate"
|
|
p2 := fmt.Sprintf("/ipfs/%s", ts.ParentState().String())
|
|
|
|
app, mockApi, buf, done := NewMockAppWithFullAPI(t, cmd)
|
|
defer done()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainHead(ctx).Return(ts, nil),
|
|
mockApi.EXPECT().ChainGetNode(ctx, p2).Return(&api.IpldObject{Cid: blk.Cid(), Obj: blk}, nil),
|
|
)
|
|
|
|
//stm: @CLI_CHAIN_GET_002
|
|
err := app.Run([]string{"chain", "get", p1})
|
|
assert.NoError(t, err)
|
|
|
|
var out types.BlockHeader
|
|
err = json.Unmarshal(buf.Bytes(), &out)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, *blk, out)
|
|
})
|
|
|
|
// given an unknown -as-type value, return an error
|
|
t.Run("unknown-type", func(t *testing.T) {
|
|
app, mockApi, _, done := NewMockAppWithFullAPI(t, cmd)
|
|
defer done()
|
|
|
|
path := fmt.Sprintf("/ipfs/%s", blk.Cid().String())
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainGetNode(ctx, path).Return(&api.IpldObject{Cid: blk.Cid(), Obj: blk}, nil),
|
|
)
|
|
|
|
//stm: @CLI_CHAIN_GET_004
|
|
err := app.Run([]string{"chain", "get", "-as-type=foo", path})
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestChainBisect(t *testing.T) {
|
|
blk1 := mock.MkBlock(nil, 0, 0)
|
|
blk1.Height = 0
|
|
ts1 := mock.TipSet(blk1)
|
|
|
|
blk2 := mock.MkBlock(ts1, 0, 0)
|
|
blk2.Height = 1
|
|
ts2 := mock.TipSet(blk2)
|
|
|
|
subpath := "whatever/its/mocked"
|
|
minHeight := uint64(0)
|
|
maxHeight := uint64(1)
|
|
shell := "echo"
|
|
|
|
path := fmt.Sprintf("/ipld/%s/%s", ts2.ParentState(), subpath)
|
|
|
|
cmd := WithCategory("chain", ChainBisectCmd)
|
|
|
|
app, mockApi, buf, done := NewMockAppWithFullAPI(t, cmd)
|
|
defer done()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainGetTipSetByHeight(ctx, abi.ChainEpoch(maxHeight), types.EmptyTSK).Return(ts2, nil),
|
|
mockApi.EXPECT().ChainGetTipSetByHeight(ctx, abi.ChainEpoch(maxHeight), ts2.Key()).Return(ts2, nil),
|
|
mockApi.EXPECT().ChainGetNode(ctx, path).Return(&api.IpldObject{Cid: blk2.Cid(), Obj: blk2}, nil),
|
|
)
|
|
|
|
//stm: @CLI_CHAIN_BISECT_001
|
|
err := app.Run([]string{"chain", "bisect", fmt.Sprintf("%d", minHeight), fmt.Sprintf("%d", maxHeight), subpath, shell})
|
|
assert.NoError(t, err)
|
|
|
|
out := buf.String()
|
|
assert.Contains(t, out, path)
|
|
}
|
|
|
|
func TestChainExport(t *testing.T) {
|
|
app, mockApi, _, done := NewMockAppWithFullAPI(t, WithCategory("chain", ChainExportCmd))
|
|
defer done()
|
|
|
|
// export writes to a file, I mocked it so there are no side-effects
|
|
mockFile := mockExportFile{new(bytes.Buffer)}
|
|
app.Metadata["export-file"] = mockFile
|
|
|
|
blk := mock.MkBlock(nil, 0, 0)
|
|
ts := mock.TipSet(blk)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
export := make(chan []byte, 2)
|
|
expBytes := []byte("whatever")
|
|
export <- expBytes
|
|
export <- []byte{} // empty slice means export is complete
|
|
close(export)
|
|
|
|
gomock.InOrder(
|
|
mockApi.EXPECT().ChainHead(ctx).Return(ts, nil),
|
|
mockApi.EXPECT().ChainExport(ctx, abi.ChainEpoch(0), false, ts.Key()).Return(export, nil),
|
|
)
|
|
|
|
//stm: @CLI_CHAIN_EXPORT_001
|
|
err := app.Run([]string{"chain", "export", "whatever.car"})
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, expBytes, mockFile.Bytes())
|
|
}
|
|
|
|
func TestChainGasPrice(t *testing.T) {
|
|
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("chain", ChainGasPriceCmd))
|
|
defer done()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
// estimate gas is called with various num blocks in implementation,
|
|
// so we mock and count how many times it's called, and we expect that many results printed
|
|
calls := 0
|
|
mockApi.
|
|
EXPECT().
|
|
GasEstimateGasPremium(ctx, gomock.Any(), builtin.SystemActorAddr, int64(10000), types.EmptyTSK).
|
|
Return(big.NewInt(0), nil).
|
|
AnyTimes().
|
|
Do(func(a, b, c, d, e interface{}) { // looks funny, but we don't care about args here, just counting
|
|
calls++
|
|
})
|
|
|
|
//stm: @CLI_CHAIN_GAS_PRICE_001
|
|
err := app.Run([]string{"chain", "gas-price"})
|
|
assert.NoError(t, err)
|
|
|
|
lines := strings.Split(strings.Trim(buf.String(), "\n"), "\n")
|
|
assert.Equal(t, calls, len(lines))
|
|
}
|
|
|
|
type mockExportFile struct {
|
|
*bytes.Buffer
|
|
}
|
|
|
|
func (mef mockExportFile) Close() error {
|
|
return nil
|
|
}
|