initial version of the new itest kit.
Still need to migrate all integration tests, add godocs, and probably zap bugs.
This commit is contained in:
parent
f6a7f21def
commit
2bcedcf55f
@ -8,33 +8,30 @@ import (
|
|||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-state-types/big"
|
"github.com/filecoin-project/go-state-types/big"
|
||||||
|
"github.com/filecoin-project/go-state-types/exitcode"
|
||||||
lapi "github.com/filecoin-project/lotus/api"
|
lapi "github.com/filecoin-project/lotus/api"
|
||||||
"github.com/filecoin-project/lotus/build"
|
"github.com/filecoin-project/lotus/build"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
"github.com/filecoin-project/lotus/itests/kit"
|
"github.com/filecoin-project/lotus/itests/kit"
|
||||||
"github.com/filecoin-project/lotus/node/impl"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAPI(t *testing.T) {
|
func TestAPI(t *testing.T) {
|
||||||
t.Run("direct", func(t *testing.T) {
|
t.Run("direct", func(t *testing.T) {
|
||||||
runAPITest(t, kit.Builder)
|
runAPITest(t)
|
||||||
})
|
})
|
||||||
t.Run("rpc", func(t *testing.T) {
|
t.Run("rpc", func(t *testing.T) {
|
||||||
runAPITest(t, kit.RPCBuilder)
|
runAPITest(t, kit.ThroughRPC())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type apiSuite struct {
|
type apiSuite struct {
|
||||||
makeNodes kit.APIBuilder
|
opts []kit.NodeOpt
|
||||||
}
|
}
|
||||||
|
|
||||||
// runAPITest is the entry point to API test suite
|
// runAPITest is the entry point to API test suite
|
||||||
func runAPITest(t *testing.T, b kit.APIBuilder) {
|
func runAPITest(t *testing.T, opts ...kit.NodeOpt) {
|
||||||
ts := apiSuite{
|
ts := apiSuite{opts: opts}
|
||||||
makeNodes: b,
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("version", ts.testVersion)
|
t.Run("version", ts.testVersion)
|
||||||
t.Run("id", ts.testID)
|
t.Run("id", ts.testID)
|
||||||
@ -51,145 +48,114 @@ func (ts *apiSuite) testVersion(t *testing.T) {
|
|||||||
lapi.RunningNodeType = lapi.NodeUnknown
|
lapi.RunningNodeType = lapi.NodeUnknown
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx := context.Background()
|
full, _, _ := kit.EnsembleMinimum(t, ts.opts...)
|
||||||
apis, _ := ts.makeNodes(t, kit.OneFull, kit.OneMiner)
|
|
||||||
napi := apis[0]
|
v, err := full.Version(context.Background())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
v, err := napi.Version(ctx)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
versions := strings.Split(v.Version, "+")
|
versions := strings.Split(v.Version, "+")
|
||||||
if len(versions) <= 0 {
|
require.NotZero(t, len(versions), "empty version")
|
||||||
t.Fatal("empty version")
|
|
||||||
}
|
|
||||||
require.Equal(t, versions[0], build.BuildVersion)
|
require.Equal(t, versions[0], build.BuildVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *apiSuite) testSearchMsg(t *testing.T) {
|
func (ts *apiSuite) testID(t *testing.T) {
|
||||||
apis, miners := ts.makeNodes(t, kit.OneFull, kit.OneMiner)
|
ctx := context.Background()
|
||||||
|
|
||||||
api := apis[0]
|
full, _, _ := kit.EnsembleMinimum(t, ts.opts...)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
id, err := full.ID(ctx)
|
||||||
senderAddr, err := api.WalletDefaultAddress(ctx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
require.Regexp(t, "^12", id.Pretty())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts *apiSuite) testConnectTwo(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
one, two, _, ens := kit.EnsembleTwo(t, ts.opts...)
|
||||||
|
|
||||||
|
p, err := one.NetPeers(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Empty(t, p, "node one has peers")
|
||||||
|
|
||||||
|
p, err = two.NetPeers(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Empty(t, p, "node two has peers")
|
||||||
|
|
||||||
|
ens.InterconnectAll()
|
||||||
|
|
||||||
|
peers, err := one.NetPeers(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Lenf(t, peers, 1, "node one doesn't have 1 peer")
|
||||||
|
|
||||||
|
peers, err = two.NetPeers(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Lenf(t, peers, 1, "node two doesn't have 1 peer")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts *apiSuite) testSearchMsg(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
full, _, ens := kit.EnsembleMinimum(t, ts.opts...)
|
||||||
|
|
||||||
|
senderAddr, err := full.WalletDefaultAddress(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
msg := &types.Message{
|
msg := &types.Message{
|
||||||
From: senderAddr,
|
From: senderAddr,
|
||||||
To: senderAddr,
|
To: senderAddr,
|
||||||
Value: big.Zero(),
|
Value: big.Zero(),
|
||||||
}
|
}
|
||||||
bm := kit.NewBlockMiner(t, miners[0])
|
|
||||||
bm.MineBlocks(ctx, 100*time.Millisecond)
|
|
||||||
defer bm.Stop()
|
|
||||||
|
|
||||||
sm, err := api.MpoolPushMessage(ctx, msg, nil)
|
ens.BeginMining(100 * time.Millisecond)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
res, err := api.StateWaitMsg(ctx, sm.Cid(), 1, lapi.LookbackNoLimit, true)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if res.Receipt.ExitCode != 0 {
|
|
||||||
t.Fatal("did not successfully send message")
|
|
||||||
}
|
|
||||||
|
|
||||||
searchRes, err := api.StateSearchMsg(ctx, types.EmptyTSK, sm.Cid(), lapi.LookbackNoLimit, true)
|
sm, err := full.MpoolPushMessage(ctx, msg, nil)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if searchRes.TipSet != res.TipSet {
|
res, err := full.StateWaitMsg(ctx, sm.Cid(), 1, lapi.LookbackNoLimit, true)
|
||||||
t.Fatalf("search ts: %s, different from wait ts: %s", searchRes.TipSet, res.TipSet)
|
require.NoError(t, err)
|
||||||
}
|
|
||||||
|
|
||||||
}
|
require.Equal(t, exitcode.Ok, res.Receipt.ExitCode, "message not successful")
|
||||||
|
|
||||||
func (ts *apiSuite) testID(t *testing.T) {
|
searchRes, err := full.StateSearchMsg(ctx, types.EmptyTSK, sm.Cid(), lapi.LookbackNoLimit, true)
|
||||||
ctx := context.Background()
|
require.NoError(t, err)
|
||||||
apis, _ := ts.makeNodes(t, kit.OneFull, kit.OneMiner)
|
|
||||||
api := apis[0]
|
|
||||||
|
|
||||||
id, err := api.ID(ctx)
|
require.Equalf(t, res.TipSet, searchRes.TipSet, "search ts: %s, different from wait ts: %s", searchRes.TipSet, res.TipSet)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
assert.Regexp(t, "^12", id.Pretty())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts *apiSuite) testConnectTwo(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
apis, _ := ts.makeNodes(t, kit.TwoFull, kit.OneMiner)
|
|
||||||
|
|
||||||
p, err := apis[0].NetPeers(ctx)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(p) != 0 {
|
|
||||||
t.Error("Node 0 has a peer")
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err = apis[1].NetPeers(ctx)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(p) != 0 {
|
|
||||||
t.Error("Node 1 has a peer")
|
|
||||||
}
|
|
||||||
|
|
||||||
addrs, err := apis[1].NetAddrsListen(ctx)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := apis[0].NetConnect(ctx, addrs); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err = apis[0].NetPeers(ctx)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(p) != 1 {
|
|
||||||
t.Error("Node 0 doesn't have 1 peer")
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err = apis[1].NetPeers(ctx)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(p) != 1 {
|
|
||||||
t.Error("Node 0 doesn't have 1 peer")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *apiSuite) testMining(t *testing.T) {
|
func (ts *apiSuite) testMining(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
fulls, miners := ts.makeNodes(t, kit.OneFull, kit.OneMiner)
|
|
||||||
api := fulls[0]
|
|
||||||
|
|
||||||
newHeads, err := api.ChainNotify(ctx)
|
full, miner, _ := kit.EnsembleMinimum(t, ts.opts...)
|
||||||
|
|
||||||
|
newHeads, err := full.ChainNotify(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
initHead := (<-newHeads)[0]
|
initHead := (<-newHeads)[0]
|
||||||
baseHeight := initHead.Val.Height()
|
baseHeight := initHead.Val.Height()
|
||||||
|
|
||||||
h1, err := api.ChainHead(ctx)
|
h1, err := full.ChainHead(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, int64(h1.Height()), int64(baseHeight))
|
require.Equal(t, int64(h1.Height()), int64(baseHeight))
|
||||||
|
|
||||||
bm := kit.NewBlockMiner(t, miners[0])
|
bm := kit.NewBlockMiner(t, miner)
|
||||||
bm.MineUntilBlock(ctx, fulls[0], nil)
|
bm.MineUntilBlock(ctx, full, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
<-newHeads
|
<-newHeads
|
||||||
|
|
||||||
h2, err := api.ChainHead(ctx)
|
h2, err := full.ChainHead(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Greater(t, int64(h2.Height()), int64(h1.Height()))
|
require.Greater(t, int64(h2.Height()), int64(h1.Height()))
|
||||||
|
|
||||||
|
bm.MineUntilBlock(ctx, full, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
<-newHeads
|
||||||
|
|
||||||
|
h3, err := full.ChainHead(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Greater(t, int64(h3.Height()), int64(h2.Height()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *apiSuite) testMiningReal(t *testing.T) {
|
func (ts *apiSuite) testMiningReal(t *testing.T) {
|
||||||
@ -198,66 +164,26 @@ func (ts *apiSuite) testMiningReal(t *testing.T) {
|
|||||||
build.InsecurePoStValidation = true
|
build.InsecurePoStValidation = true
|
||||||
}()
|
}()
|
||||||
|
|
||||||
ctx := context.Background()
|
ts.testMining(t)
|
||||||
fulls, miners := ts.makeNodes(t, kit.OneFull, kit.OneMiner)
|
|
||||||
api := fulls[0]
|
|
||||||
|
|
||||||
newHeads, err := api.ChainNotify(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
at := (<-newHeads)[0].Val.Height()
|
|
||||||
|
|
||||||
h1, err := api.ChainHead(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, int64(at), int64(h1.Height()))
|
|
||||||
|
|
||||||
bm := kit.NewBlockMiner(t, miners[0])
|
|
||||||
|
|
||||||
bm.MineUntilBlock(ctx, fulls[0], nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
<-newHeads
|
|
||||||
|
|
||||||
h2, err := api.ChainHead(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Greater(t, int64(h2.Height()), int64(h1.Height()))
|
|
||||||
|
|
||||||
bm.MineUntilBlock(ctx, fulls[0], nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
<-newHeads
|
|
||||||
|
|
||||||
h3, err := api.ChainHead(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Greater(t, int64(h3.Height()), int64(h2.Height()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *apiSuite) testNonGenesisMiner(t *testing.T) {
|
func (ts *apiSuite) testNonGenesisMiner(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
n, sn := ts.makeNodes(t,
|
|
||||||
[]kit.FullNodeOpts{kit.FullNodeWithLatestActorsAt(-1)},
|
|
||||||
[]kit.StorageMiner{{Full: 0, Preseal: kit.PresealGenesis}},
|
|
||||||
)
|
|
||||||
|
|
||||||
full, ok := n[0].FullNode.(*impl.FullNodeAPI)
|
full, genesisMiner, ens := kit.EnsembleMinimum(t, ts.opts...)
|
||||||
if !ok {
|
|
||||||
t.Skip("not testing with a full node")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
genesisMiner := sn[0]
|
|
||||||
|
|
||||||
bm := kit.NewBlockMiner(t, genesisMiner)
|
ens.BeginMining(4 * time.Millisecond)
|
||||||
bm.MineBlocks(ctx, 4*time.Millisecond)
|
|
||||||
t.Cleanup(bm.Stop)
|
|
||||||
|
|
||||||
gaa, err := genesisMiner.ActorAddress(ctx)
|
gaa, err := genesisMiner.ActorAddress(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
gmi, err := full.StateMinerInfo(ctx, gaa, types.EmptyTSK)
|
_, err = full.StateMinerInfo(ctx, gaa, types.EmptyTSK)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
testm := n[0].Stb(ctx, t, kit.TestSpt, gmi.Owner)
|
var newMiner kit.TestMiner
|
||||||
|
ens.Miner(&newMiner, full, kit.OwnerAddr(full.DefaultKey)).Start()
|
||||||
|
|
||||||
ta, err := testm.ActorAddress(ctx)
|
ta, err := newMiner.ActorAddress(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tid, err := address.IDFromAddress(ta)
|
tid, err := address.IDFromAddress(ta)
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/itests/kit"
|
"github.com/filecoin-project/lotus/itests/kit"
|
||||||
|
"github.com/filecoin-project/lotus/itests/kit2"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/filecoin-project/lotus/miner"
|
"github.com/filecoin-project/lotus/miner"
|
||||||
"github.com/filecoin-project/lotus/node"
|
"github.com/filecoin-project/lotus/node"
|
||||||
"github.com/filecoin-project/lotus/node/impl"
|
"github.com/filecoin-project/lotus/node/impl"
|
||||||
|
"github.com/filecoin-project/lotus/node/impl/client"
|
||||||
market2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/market"
|
market2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/market"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@ -66,15 +67,15 @@ func TestAPIDealFlowReal(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("basic", func(t *testing.T) {
|
t.Run("basic", func(t *testing.T) {
|
||||||
runFullDealCycles(t, 1, kit.Builder, time.Second, false, false, 0)
|
runFullDealCycles(t, 1, kit.FullNodeBuilder, time.Second, false, false, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("fast-retrieval", func(t *testing.T) {
|
t.Run("fast-retrieval", func(t *testing.T) {
|
||||||
runFullDealCycles(t, 1, kit.Builder, time.Second, false, true, 0)
|
runFullDealCycles(t, 1, kit.FullNodeBuilder, time.Second, false, true, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("retrieval-second", func(t *testing.T) {
|
t.Run("retrieval-second", func(t *testing.T) {
|
||||||
runSecondDealRetrievalTest(t, kit.Builder, time.Second)
|
runSecondDealRetrievalTest(t, kit.FullNodeBuilder, time.Second)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,6 +310,7 @@ func TestDealMining(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runFullDealCycles(t *testing.T, n int, b kit.APIBuilder, blocktime time.Duration, carExport, fastRet bool, startEpoch abi.ChainEpoch) {
|
func runFullDealCycles(t *testing.T, n int, b kit.APIBuilder, blocktime time.Duration, carExport, fastRet bool, startEpoch abi.ChainEpoch) {
|
||||||
|
|
||||||
fulls, miners := b(t, kit.OneFull, kit.OneMiner)
|
fulls, miners := b(t, kit.OneFull, kit.OneMiner)
|
||||||
client, miner := fulls[0].FullNode.(*impl.FullNodeAPI), miners[0]
|
client, miner := fulls[0].FullNode.(*impl.FullNodeAPI), miners[0]
|
||||||
|
|
||||||
@ -325,21 +327,23 @@ func runFullDealCycles(t *testing.T, n int, b kit.APIBuilder, blocktime time.Dur
|
|||||||
func runFastRetrievalDealFlowT(t *testing.T, b kit.APIBuilder, blocktime time.Duration, startEpoch abi.ChainEpoch) {
|
func runFastRetrievalDealFlowT(t *testing.T, b kit.APIBuilder, blocktime time.Duration, startEpoch abi.ChainEpoch) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
fulls, miners := b(t, kit.OneFull, kit.OneMiner)
|
var (
|
||||||
client, miner := fulls[0].FullNode.(*impl.FullNodeAPI), miners[0]
|
nb = kit.NewNodeBuilder(t)
|
||||||
|
full = nb.FullNode()
|
||||||
|
miner = nb.Miner(full)
|
||||||
|
)
|
||||||
|
|
||||||
kit.ConnectAndStartMining(t, blocktime, miner, client)
|
nb.Create()
|
||||||
|
|
||||||
dh := kit.NewDealHarness(t, client, miner)
|
kit.ConnectAndStartMining(t, blocktime, miner, full)
|
||||||
|
|
||||||
|
dh := kit.NewDealHarness(t, full, miner)
|
||||||
data := make([]byte, 1600)
|
data := make([]byte, 1600)
|
||||||
rand.New(rand.NewSource(int64(8))).Read(data)
|
rand.New(rand.NewSource(int64(8))).Read(data)
|
||||||
|
|
||||||
r := bytes.NewReader(data)
|
r := bytes.NewReader(data)
|
||||||
fcid, err := client.ClientImportLocal(ctx, r)
|
fcid, err := full.FullNode.(*impl.FullNodeAPI).ClientImportLocal(ctx, r)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("FILE CID: ", fcid)
|
fmt.Println("FILE CID: ", fcid)
|
||||||
|
|
||||||
@ -349,7 +353,7 @@ func runFastRetrievalDealFlowT(t *testing.T, b kit.APIBuilder, blocktime time.Du
|
|||||||
fmt.Println("deal published, retrieving")
|
fmt.Println("deal published, retrieving")
|
||||||
|
|
||||||
// Retrieval
|
// Retrieval
|
||||||
info, err := client.ClientGetDealInfo(ctx, *deal)
|
info, err := full.ClientGetDealInfo(ctx, *deal)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
dh.TestRetrieval(ctx, fcid, &info.PieceCID, false, data)
|
dh.TestRetrieval(ctx, fcid, &info.PieceCID, false, data)
|
@ -291,7 +291,7 @@ func startNodes(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
n, sn := kit.RPCMockMinerBuilder(t, opts, kit.OneMiner)
|
n, sn := kit.MinerRPCMockMinerBuilder(t, opts, kit.OneMiner)
|
||||||
|
|
||||||
full := n[0]
|
full := n[0]
|
||||||
lite := n[1]
|
lite := n[1]
|
@ -15,14 +15,14 @@ import (
|
|||||||
// BlockMiner is a utility that makes a test miner Mine blocks on a timer.
|
// BlockMiner is a utility that makes a test miner Mine blocks on a timer.
|
||||||
type BlockMiner struct {
|
type BlockMiner struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
miner TestMiner
|
miner *TestMiner
|
||||||
|
|
||||||
nextNulls int64
|
nextNulls int64
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBlockMiner(t *testing.T, miner TestMiner) *BlockMiner {
|
func NewBlockMiner(t *testing.T, miner *TestMiner) *BlockMiner {
|
||||||
return &BlockMiner{
|
return &BlockMiner{
|
||||||
t: t,
|
t: t,
|
||||||
miner: miner,
|
miner: miner,
|
||||||
@ -69,7 +69,7 @@ func (bm *BlockMiner) InjectNulls(rounds abi.ChainEpoch) {
|
|||||||
atomic.AddInt64(&bm.nextNulls, int64(rounds))
|
atomic.AddInt64(&bm.nextNulls, int64(rounds))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bm *BlockMiner) MineUntilBlock(ctx context.Context, fn TestFullNode, cb func(abi.ChainEpoch)) {
|
func (bm *BlockMiner) MineUntilBlock(ctx context.Context, fn *TestFullNode, cb func(abi.ChainEpoch)) {
|
||||||
for i := 0; i < 1000; i++ {
|
for i := 0; i < 1000; i++ {
|
||||||
var (
|
var (
|
||||||
success bool
|
success bool
|
||||||
|
@ -31,11 +31,11 @@ import (
|
|||||||
type DealHarness struct {
|
type DealHarness struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
client api.FullNode
|
client api.FullNode
|
||||||
miner TestMiner
|
miner *TestMiner
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDealHarness creates a test harness that contains testing utilities for deals.
|
// NewDealHarness creates a test harness that contains testing utilities for deals.
|
||||||
func NewDealHarness(t *testing.T, client api.FullNode, miner TestMiner) *DealHarness {
|
func NewDealHarness(t *testing.T, client api.FullNode, miner *TestMiner) *DealHarness {
|
||||||
return &DealHarness{
|
return &DealHarness{
|
||||||
t: t,
|
t: t,
|
||||||
client: client,
|
client: client,
|
||||||
@ -252,27 +252,6 @@ type DealsScaffold struct {
|
|||||||
BlockMiner *BlockMiner
|
BlockMiner *BlockMiner
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConnectAndStartMining(t *testing.T, blocktime time.Duration, miner TestMiner, clients ...api.FullNode) *BlockMiner {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
for _, c := range clients {
|
|
||||||
addrinfo, err := c.NetAddrsListen(ctx)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := miner.NetConnect(ctx, addrinfo); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
blockMiner := NewBlockMiner(t, miner)
|
|
||||||
blockMiner.MineBlocks(ctx, blocktime)
|
|
||||||
|
|
||||||
return blockMiner
|
|
||||||
}
|
|
||||||
|
|
||||||
type TestDealState int
|
type TestDealState int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
708
itests/kit/ensemble.go
Normal file
708
itests/kit/ensemble.go
Normal file
@ -0,0 +1,708 @@
|
|||||||
|
package kit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/go-state-types/big"
|
||||||
|
"github.com/filecoin-project/go-state-types/exitcode"
|
||||||
|
"github.com/filecoin-project/go-state-types/network"
|
||||||
|
"github.com/filecoin-project/go-storedcounter"
|
||||||
|
"github.com/filecoin-project/lotus/api"
|
||||||
|
"github.com/filecoin-project/lotus/api/client"
|
||||||
|
"github.com/filecoin-project/lotus/api/v1api"
|
||||||
|
"github.com/filecoin-project/lotus/build"
|
||||||
|
"github.com/filecoin-project/lotus/chain"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
|
||||||
|
"github.com/filecoin-project/lotus/chain/gen"
|
||||||
|
genesis2 "github.com/filecoin-project/lotus/chain/gen/genesis"
|
||||||
|
"github.com/filecoin-project/lotus/chain/messagepool"
|
||||||
|
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/chain/wallet"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-seed/seed"
|
||||||
|
sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage"
|
||||||
|
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
|
||||||
|
"github.com/filecoin-project/lotus/extern/sector-storage/mock"
|
||||||
|
"github.com/filecoin-project/lotus/genesis"
|
||||||
|
lotusminer "github.com/filecoin-project/lotus/miner"
|
||||||
|
"github.com/filecoin-project/lotus/node"
|
||||||
|
"github.com/filecoin-project/lotus/node/modules"
|
||||||
|
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||||
|
testing2 "github.com/filecoin-project/lotus/node/modules/testing"
|
||||||
|
"github.com/filecoin-project/lotus/node/repo"
|
||||||
|
"github.com/filecoin-project/lotus/storage/mockstorage"
|
||||||
|
miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner"
|
||||||
|
power2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/power"
|
||||||
|
"github.com/ipfs/go-datastore"
|
||||||
|
libp2pcrypto "github.com/libp2p/go-libp2p-core/crypto"
|
||||||
|
"github.com/libp2p/go-libp2p-core/peer"
|
||||||
|
mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
|
||||||
|
"github.com/multiformats/go-multiaddr"
|
||||||
|
manet "github.com/multiformats/go-multiaddr/net"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
chain.BootstrapPeerThreshold = 1
|
||||||
|
messagepool.HeadChangeCoalesceMinDelay = time.Microsecond
|
||||||
|
messagepool.HeadChangeCoalesceMaxDelay = 2 * time.Microsecond
|
||||||
|
messagepool.HeadChangeCoalesceMergeInterval = 100 * time.Nanosecond
|
||||||
|
}
|
||||||
|
|
||||||
|
type BuilderOpt func(opts *BuilderOpts) error
|
||||||
|
|
||||||
|
type BuilderOpts struct {
|
||||||
|
pastOffset time.Duration
|
||||||
|
spt abi.RegisteredSealProof
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultBuilderOpts = BuilderOpts{
|
||||||
|
pastOffset: 10000 * time.Second,
|
||||||
|
spt: abi.RegisteredSealProof_StackedDrg2KiBV1,
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProofType(proofType abi.RegisteredSealProof) BuilderOpt {
|
||||||
|
return func(opts *BuilderOpts) error {
|
||||||
|
opts.spt = proofType
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensemble is a collection of nodes instantiated within a test. Ensemble
|
||||||
|
// supports building full nodes and miners.
|
||||||
|
type Ensemble struct {
|
||||||
|
t *testing.T
|
||||||
|
bootstrapped bool
|
||||||
|
genesisBlock bytes.Buffer
|
||||||
|
mn mocknet.Mocknet
|
||||||
|
options *BuilderOpts
|
||||||
|
|
||||||
|
inactive struct {
|
||||||
|
fullnodes []*TestFullNode
|
||||||
|
miners []*TestMiner
|
||||||
|
}
|
||||||
|
active struct {
|
||||||
|
fullnodes []*TestFullNode
|
||||||
|
miners []*TestMiner
|
||||||
|
}
|
||||||
|
genesis struct {
|
||||||
|
miners []genesis.Miner
|
||||||
|
accounts []genesis.Actor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEnsemble
|
||||||
|
func NewEnsemble(t *testing.T, opts ...BuilderOpt) *Ensemble {
|
||||||
|
options := DefaultBuilderOpts
|
||||||
|
for _, o := range opts {
|
||||||
|
err := o(&options)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
return &Ensemble{t: t, options: &options}
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeOpts struct {
|
||||||
|
balance abi.TokenAmount
|
||||||
|
lite bool
|
||||||
|
sectors int
|
||||||
|
mockProofs bool
|
||||||
|
rpc bool
|
||||||
|
ownerKey *wallet.Key
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultNodeOpts = NodeOpts{
|
||||||
|
balance: big.Mul(big.NewInt(100000000), types.NewInt(build.FilecoinPrecision)),
|
||||||
|
sectors: 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeOpt func(opts *NodeOpts) error
|
||||||
|
|
||||||
|
// OwnerBalance specifies the balance to be attributed to a miner's owner account.
|
||||||
|
//
|
||||||
|
// Only used when creating a miner.
|
||||||
|
func OwnerBalance(balance abi.TokenAmount) NodeOpt {
|
||||||
|
return func(opts *NodeOpts) error {
|
||||||
|
opts.balance = balance
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LiteNode specifies that this node will be a lite node.
|
||||||
|
//
|
||||||
|
// Only used when creating a fullnode.
|
||||||
|
func LiteNode() NodeOpt {
|
||||||
|
return func(opts *NodeOpts) error {
|
||||||
|
opts.lite = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PresealSectors specifies the amount of preseal sectors to give to a miner
|
||||||
|
// at genesis.
|
||||||
|
//
|
||||||
|
// Only used when creating a miner.
|
||||||
|
func PresealSectors(sectors int) NodeOpt {
|
||||||
|
return func(opts *NodeOpts) error {
|
||||||
|
opts.sectors = sectors
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockProofs activates mock proofs for the entire ensemble.
|
||||||
|
func MockProofs() NodeOpt {
|
||||||
|
return func(opts *NodeOpts) error {
|
||||||
|
opts.mockProofs = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ThroughRPC() NodeOpt {
|
||||||
|
return func(opts *NodeOpts) error {
|
||||||
|
opts.rpc = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OwnerAddr(wk *wallet.Key) NodeOpt {
|
||||||
|
return func(opts *NodeOpts) error {
|
||||||
|
opts.ownerKey = wk
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FullNode enrolls a new full node.
|
||||||
|
func (n *Ensemble) FullNode(full *TestFullNode, opts ...NodeOpt) *Ensemble {
|
||||||
|
options := DefaultNodeOpts
|
||||||
|
for _, o := range opts {
|
||||||
|
err := o(&options)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var key *wallet.Key
|
||||||
|
if !n.bootstrapped && !options.balance.IsZero() {
|
||||||
|
// create a key+ddress, and assign it some FIL.
|
||||||
|
// this will be set as the default wallet.
|
||||||
|
var err error
|
||||||
|
key, err = wallet.GenerateKey(types.KTBLS)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
genacc := genesis.Actor{
|
||||||
|
Type: genesis.TAccount,
|
||||||
|
Balance: options.balance,
|
||||||
|
Meta: (&genesis.AccountMeta{Owner: key.Address}).ActorMeta(),
|
||||||
|
}
|
||||||
|
|
||||||
|
n.genesis.accounts = append(n.genesis.accounts, genacc)
|
||||||
|
}
|
||||||
|
|
||||||
|
*full = TestFullNode{options: options, DefaultKey: key}
|
||||||
|
n.inactive.fullnodes = append(n.inactive.fullnodes, full)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Miner enrolls a new miner, using the provided full node for chain
|
||||||
|
// interactions.
|
||||||
|
func (n *Ensemble) Miner(miner *TestMiner, full *TestFullNode, opts ...NodeOpt) *Ensemble {
|
||||||
|
require.NotNil(n.t, full, "full node required when instantiating miner")
|
||||||
|
|
||||||
|
options := DefaultNodeOpts
|
||||||
|
for _, o := range opts {
|
||||||
|
err := o(&options)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
privkey, _, err := libp2pcrypto.GenerateEd25519Key(rand.Reader)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
peerId, err := peer.IDFromPrivateKey(privkey)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
tdir, err := ioutil.TempDir("", "preseal-memgen")
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
minerCnt := len(n.inactive.miners) + len(n.active.miners)
|
||||||
|
|
||||||
|
actorAddr, err := address.NewIDAddress(genesis2.MinerStart + uint64(minerCnt))
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
ownerKey := options.ownerKey
|
||||||
|
if !n.bootstrapped {
|
||||||
|
var (
|
||||||
|
sectors = options.sectors
|
||||||
|
k *types.KeyInfo
|
||||||
|
genm *genesis.Miner
|
||||||
|
)
|
||||||
|
|
||||||
|
// create the preseal commitment.
|
||||||
|
if options.mockProofs {
|
||||||
|
genm, k, err = mockstorage.PreSeal(abi.RegisteredSealProof_StackedDrg2KiBV1, actorAddr, sectors)
|
||||||
|
} else {
|
||||||
|
genm, k, err = seed.PreSeal(actorAddr, abi.RegisteredSealProof_StackedDrg2KiBV1, 0, sectors, tdir, []byte("make genesis mem random"), nil, true)
|
||||||
|
}
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
genm.PeerId = peerId
|
||||||
|
|
||||||
|
// create an owner key, and assign it some FIL.
|
||||||
|
ownerKey, err = wallet.NewKey(*k)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
genacc := genesis.Actor{
|
||||||
|
Type: genesis.TAccount,
|
||||||
|
Balance: options.balance,
|
||||||
|
Meta: (&genesis.AccountMeta{Owner: ownerKey.Address}).ActorMeta(),
|
||||||
|
}
|
||||||
|
|
||||||
|
n.genesis.miners = append(n.genesis.miners, *genm)
|
||||||
|
n.genesis.accounts = append(n.genesis.accounts, genacc)
|
||||||
|
} else {
|
||||||
|
require.NotNil(n.t, ownerKey, "worker key can't be null if initializing a miner after genesis")
|
||||||
|
}
|
||||||
|
|
||||||
|
*miner = TestMiner{
|
||||||
|
ActorAddr: actorAddr,
|
||||||
|
OwnerKey: ownerKey,
|
||||||
|
FullNode: full,
|
||||||
|
PresealDir: tdir,
|
||||||
|
options: options,
|
||||||
|
}
|
||||||
|
|
||||||
|
miner.Libp2p.PeerID = peerId
|
||||||
|
miner.Libp2p.PrivKey = privkey
|
||||||
|
|
||||||
|
n.inactive.miners = append(n.inactive.miners, miner)
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start starts all enrolled nodes.
|
||||||
|
func (n *Ensemble) Start() *Ensemble {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
n.t.Cleanup(cancel)
|
||||||
|
|
||||||
|
var gtempl *genesis.Template
|
||||||
|
if !n.bootstrapped {
|
||||||
|
// We haven't been bootstrapped yet, we need to generate genesis and
|
||||||
|
// create the networking backbone.
|
||||||
|
gtempl = n.generateGenesis()
|
||||||
|
n.mn = mocknet.New(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create all inactive full nodes.
|
||||||
|
for i, full := range n.inactive.fullnodes {
|
||||||
|
opts := []node.Option{
|
||||||
|
node.FullAPI(&full.FullNode, node.Lite(full.options.lite)),
|
||||||
|
node.Online(),
|
||||||
|
node.Repo(repo.NewMemory(nil)),
|
||||||
|
node.MockHost(n.mn),
|
||||||
|
node.Test(),
|
||||||
|
|
||||||
|
// so that we subscribe to pubsub topics immediately
|
||||||
|
node.Override(new(dtypes.Bootstrapper), dtypes.Bootstrapper(true)),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Either generate the genesis or inject it.
|
||||||
|
if i == 0 && !n.bootstrapped {
|
||||||
|
opts = append(opts, node.Override(new(modules.Genesis), testing2.MakeGenesisMem(&n.genesisBlock, *gtempl)))
|
||||||
|
} else {
|
||||||
|
opts = append(opts, node.Override(new(modules.Genesis), modules.LoadGenesis(n.genesisBlock.Bytes())))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Are we mocking proofs?
|
||||||
|
if full.options.mockProofs {
|
||||||
|
opts = append(opts, node.Override(new(ffiwrapper.Verifier), mock.MockVerifier))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the full node.
|
||||||
|
stop, err := node.New(ctx, opts...)
|
||||||
|
|
||||||
|
// fullOpts[i].Opts(fulls),
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
addr, err := full.WalletImport(context.Background(), &full.DefaultKey.KeyInfo)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
err = full.WalletSetDefault(context.Background(), addr)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
// Are we hitting this node through its RPC?
|
||||||
|
if full.options.rpc {
|
||||||
|
withRPC := fullRpc(n.t, full)
|
||||||
|
n.inactive.fullnodes[i] = withRPC
|
||||||
|
}
|
||||||
|
|
||||||
|
n.t.Cleanup(func() { _ = stop(context.Background()) })
|
||||||
|
|
||||||
|
n.active.fullnodes = append(n.active.fullnodes, full)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are here, we have processed all inactive fullnodes and moved them
|
||||||
|
// to active, so clear the slice.
|
||||||
|
n.inactive.fullnodes = n.inactive.fullnodes[:0]
|
||||||
|
|
||||||
|
// Link all the nodes.
|
||||||
|
err := n.mn.LinkAll()
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
// Create all inactive miners.
|
||||||
|
for i, m := range n.inactive.miners {
|
||||||
|
if n.bootstrapped {
|
||||||
|
// this is a miner created after genesis, so it won't have a preseal.
|
||||||
|
// we need to create it on chain.
|
||||||
|
params, aerr := actors.SerializeParams(&power2.CreateMinerParams{
|
||||||
|
Owner: m.OwnerKey.Address,
|
||||||
|
Worker: m.OwnerKey.Address,
|
||||||
|
SealProofType: n.options.spt,
|
||||||
|
Peer: abi.PeerID(m.Libp2p.PeerID),
|
||||||
|
})
|
||||||
|
require.NoError(n.t, aerr)
|
||||||
|
|
||||||
|
createStorageMinerMsg := &types.Message{
|
||||||
|
From: m.OwnerKey.Address,
|
||||||
|
To: power.Address,
|
||||||
|
Value: big.Zero(),
|
||||||
|
|
||||||
|
Method: power.Methods.CreateMiner,
|
||||||
|
Params: params,
|
||||||
|
|
||||||
|
GasLimit: 0,
|
||||||
|
GasPremium: big.NewInt(5252),
|
||||||
|
}
|
||||||
|
signed, err := m.FullNode.FullNode.MpoolPushMessage(ctx, createStorageMinerMsg, nil)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
mw, err := m.FullNode.FullNode.StateWaitMsg(ctx, signed.Cid(), build.MessageConfidence, api.LookbackNoLimit, true)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
require.Equal(n.t, exitcode.Ok, mw.Receipt.ExitCode)
|
||||||
|
|
||||||
|
var retval power2.CreateMinerReturn
|
||||||
|
err = retval.UnmarshalCBOR(bytes.NewReader(mw.Receipt.Return))
|
||||||
|
require.NoError(n.t, err, "failed to create miner")
|
||||||
|
|
||||||
|
m.ActorAddr = retval.IDAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
has, err := m.FullNode.WalletHas(ctx, m.OwnerKey.Address)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
// Only import the owner's full key into our companion full node, if we
|
||||||
|
// don't have it still.
|
||||||
|
if !has {
|
||||||
|
_, err = m.FullNode.WalletImport(ctx, &m.OwnerKey.KeyInfo)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Set it as the default address.
|
||||||
|
// err = m.FullNode.WalletSetDefault(ctx, m.OwnerAddr.Address)
|
||||||
|
// require.NoError(n.t, err)
|
||||||
|
|
||||||
|
r := repo.NewMemory(nil)
|
||||||
|
|
||||||
|
lr, err := r.Lock(repo.StorageMiner)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
ks, err := lr.KeyStore()
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
pk, err := m.Libp2p.PrivKey.Bytes()
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
err = ks.Put("libp2p-host", types.KeyInfo{
|
||||||
|
Type: "libp2p-host",
|
||||||
|
PrivateKey: pk,
|
||||||
|
})
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
ds, err := lr.Datastore(context.TODO(), "/metadata")
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
err = ds.Put(datastore.NewKey("miner-address"), m.ActorAddr.Bytes())
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
nic := storedcounter.New(ds, datastore.NewKey(modules.StorageCounterDSPrefix))
|
||||||
|
for i := 0; i < m.options.sectors; i++ {
|
||||||
|
_, err := nic.Next()
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
}
|
||||||
|
_, err = nic.Next()
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
err = lr.Close()
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
enc, err := actors.SerializeParams(&miner2.ChangePeerIDParams{NewID: abi.PeerID(m.Libp2p.PeerID)})
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
msg := &types.Message{
|
||||||
|
From: m.OwnerKey.Address,
|
||||||
|
To: m.ActorAddr,
|
||||||
|
Method: miner.Methods.ChangePeerID,
|
||||||
|
Params: enc,
|
||||||
|
Value: types.NewInt(0),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = m.FullNode.MpoolPushMessage(ctx, msg, nil)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
var mineBlock = make(chan lotusminer.MineReq)
|
||||||
|
opts := []node.Option{
|
||||||
|
node.StorageMiner(&m.StorageMiner),
|
||||||
|
node.Online(),
|
||||||
|
node.Repo(r),
|
||||||
|
node.Test(),
|
||||||
|
|
||||||
|
node.MockHost(n.mn),
|
||||||
|
|
||||||
|
node.Override(new(v1api.FullNode), m.FullNode),
|
||||||
|
node.Override(new(*lotusminer.Miner), lotusminer.NewTestMiner(mineBlock, m.ActorAddr)),
|
||||||
|
}
|
||||||
|
|
||||||
|
idAddr, err := address.IDFromAddress(m.ActorAddr)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
if !n.bootstrapped && m.options.mockProofs {
|
||||||
|
s := n.genesis.miners[i].Sectors
|
||||||
|
sectors := make([]abi.SectorID, len(s))
|
||||||
|
for i, sector := range s {
|
||||||
|
sectors[i] = abi.SectorID{
|
||||||
|
Miner: abi.ActorID(idAddr),
|
||||||
|
Number: sector.SectorID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opts = append(opts,
|
||||||
|
node.Override(new(sectorstorage.SectorManager), func() (sectorstorage.SectorManager, error) {
|
||||||
|
return mock.NewMockSectorMgr(sectors), nil
|
||||||
|
}),
|
||||||
|
node.Override(new(ffiwrapper.Verifier), mock.MockVerifier),
|
||||||
|
node.Unset(new(*sectorstorage.Manager)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// start node
|
||||||
|
stop, err := node.New(ctx, opts...)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
// using real proofs, therefore need real sectors.
|
||||||
|
if !n.bootstrapped && !m.options.mockProofs {
|
||||||
|
err := m.StorageAddLocal(ctx, m.PresealDir)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n.t.Cleanup(func() { _ = stop(context.Background()) })
|
||||||
|
|
||||||
|
// Are we hitting this node through its RPC?
|
||||||
|
if m.options.rpc {
|
||||||
|
withRPC := minerRpc(n.t, m)
|
||||||
|
n.inactive.miners[i] = withRPC
|
||||||
|
}
|
||||||
|
|
||||||
|
mineOne := func(ctx context.Context, req lotusminer.MineReq) error {
|
||||||
|
select {
|
||||||
|
case mineBlock <- req:
|
||||||
|
return nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.MineOne = mineOne
|
||||||
|
m.Stop = stop
|
||||||
|
|
||||||
|
n.active.miners = append(n.active.miners, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are here, we have processed all inactive miners and moved them
|
||||||
|
// to active, so clear the slice.
|
||||||
|
n.inactive.miners = n.inactive.miners[:0]
|
||||||
|
|
||||||
|
// Link all the nodes.
|
||||||
|
err = n.mn.LinkAll()
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
if !n.bootstrapped && len(n.active.miners) > 0 {
|
||||||
|
// We have *just* bootstrapped, so
|
||||||
|
// mine 2 blocks to setup some CE stuff
|
||||||
|
// in some actors
|
||||||
|
var wait sync.Mutex
|
||||||
|
wait.Lock()
|
||||||
|
|
||||||
|
observer := n.active.fullnodes[0]
|
||||||
|
|
||||||
|
bm := NewBlockMiner(n.t, n.active.miners[0])
|
||||||
|
n.t.Cleanup(bm.Stop)
|
||||||
|
|
||||||
|
bm.MineUntilBlock(ctx, observer, func(epoch abi.ChainEpoch) {
|
||||||
|
wait.Unlock()
|
||||||
|
})
|
||||||
|
wait.Lock()
|
||||||
|
bm.MineUntilBlock(ctx, observer, func(epoch abi.ChainEpoch) {
|
||||||
|
wait.Unlock()
|
||||||
|
})
|
||||||
|
wait.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
n.bootstrapped = true
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterconnectAll connects all full nodes one to another. We do not need to
|
||||||
|
// take action with miners, because miners only stay connected to their full
|
||||||
|
// nodes over JSON-RPC.
|
||||||
|
func (n *Ensemble) InterconnectAll() *Ensemble {
|
||||||
|
last := len(n.active.fullnodes) - 1
|
||||||
|
for i, from := range n.active.fullnodes {
|
||||||
|
if i == last {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
n.Connect(from, n.active.fullnodes[i+1:]...)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect connects one full node to the provided full nodes.
|
||||||
|
func (n *Ensemble) Connect(from *TestFullNode, to ...*TestFullNode) *Ensemble {
|
||||||
|
addr, err := from.NetAddrsListen(context.Background())
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
|
||||||
|
for _, other := range to {
|
||||||
|
err = other.NetConnect(context.Background(), addr)
|
||||||
|
require.NoError(n.t, err)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeginMining kicks off mining for the specified miners. If nil or 0-length,
|
||||||
|
// it will kick off mining for all enrolled and active miners.
|
||||||
|
func (n *Ensemble) BeginMining(blocktime time.Duration, miners ...*TestMiner) []*BlockMiner {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// wait one second to make sure that nodes are connected and have handshaken.
|
||||||
|
// TODO make this deterministic by listening to identify events on the
|
||||||
|
// libp2p eventbus instead (or something else).
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
var bms []*BlockMiner
|
||||||
|
if len(miners) == 0 {
|
||||||
|
miners = n.active.miners
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range miners {
|
||||||
|
bm := NewBlockMiner(n.t, m)
|
||||||
|
bm.MineBlocks(ctx, blocktime)
|
||||||
|
bms = append(bms, bm)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bms
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Ensemble) generateGenesis() *genesis.Template {
|
||||||
|
templ := &genesis.Template{
|
||||||
|
Accounts: n.genesis.accounts,
|
||||||
|
Miners: n.genesis.miners,
|
||||||
|
NetworkName: "test",
|
||||||
|
Timestamp: uint64(time.Now().Unix() - 10000), // some time sufficiently far in the past
|
||||||
|
VerifregRootKey: gen.DefaultVerifregRootkeyActor,
|
||||||
|
RemainderAccount: gen.DefaultRemainderAccountActor,
|
||||||
|
}
|
||||||
|
|
||||||
|
return templ
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateRPCServer(t *testing.T, handler http.Handler) (*httptest.Server, multiaddr.Multiaddr) {
|
||||||
|
testServ := httptest.NewServer(handler)
|
||||||
|
t.Cleanup(testServ.Close)
|
||||||
|
t.Cleanup(testServ.CloseClientConnections)
|
||||||
|
|
||||||
|
addr := testServ.Listener.Addr()
|
||||||
|
maddr, err := manet.FromNetAddr(addr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return testServ, maddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func fullRpc(t *testing.T, f *TestFullNode) *TestFullNode {
|
||||||
|
tok, err := f.FullNode.AuthNew(context.Background(), []auth.Permission{"admin", "read", "write", "sign"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
handler, err := node.FullNodeHandler(f.FullNode)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
srv, maddr := CreateRPCServer(t, handler)
|
||||||
|
|
||||||
|
cl, stop, err := client.NewFullNodeRPCV1(context.Background(), "ws://"+srv.Listener.Addr().String()+"/rpc/v1", map[string][]string{
|
||||||
|
"Authorization": {"Bearer " + string(tok)},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(stop)
|
||||||
|
f.ListenAddr, f.FullNode = maddr, cl
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func minerRpc(t *testing.T, m *TestMiner) *TestMiner {
|
||||||
|
tok, err := m.StorageMiner.AuthNew(context.Background(), []auth.Permission{"admin", "read", "write"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
handler, err := node.MinerHandler(m.StorageMiner)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
srv, maddr := CreateRPCServer(t, handler)
|
||||||
|
|
||||||
|
cl, stop, err := client.NewStorageMinerRPCV0(context.Background(), "ws://"+srv.Listener.Addr().String()+"/rpc/v0", map[string][]string{
|
||||||
|
"Authorization": {"Bearer " + string(tok)},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(stop)
|
||||||
|
|
||||||
|
m.ListenAddr, m.StorageMiner = maddr, cl
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatestActorsAt(upgradeHeight abi.ChainEpoch) node.Option {
|
||||||
|
if upgradeHeight == -1 {
|
||||||
|
upgradeHeight = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
return node.Override(new(stmgr.UpgradeSchedule), stmgr.UpgradeSchedule{{
|
||||||
|
// prepare for upgrade.
|
||||||
|
Network: network.Version9,
|
||||||
|
Height: 1,
|
||||||
|
Migration: stmgr.UpgradeActorsV2,
|
||||||
|
}, {
|
||||||
|
Network: network.Version10,
|
||||||
|
Height: 2,
|
||||||
|
Migration: stmgr.UpgradeActorsV3,
|
||||||
|
}, {
|
||||||
|
Network: network.Version12,
|
||||||
|
Height: upgradeHeight,
|
||||||
|
Migration: stmgr.UpgradeActorsV4,
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func SDRUpgradeAt(calico, persian abi.ChainEpoch) node.Option {
|
||||||
|
return node.Override(new(stmgr.UpgradeSchedule), stmgr.UpgradeSchedule{{
|
||||||
|
Network: network.Version6,
|
||||||
|
Height: 1,
|
||||||
|
Migration: stmgr.UpgradeActorsV2,
|
||||||
|
}, {
|
||||||
|
Network: network.Version7,
|
||||||
|
Height: calico,
|
||||||
|
Migration: stmgr.UpgradeCalico,
|
||||||
|
}, {
|
||||||
|
Network: network.Version8,
|
||||||
|
Height: persian,
|
||||||
|
}})
|
||||||
|
|
||||||
|
}
|
21
itests/kit/ensemble_presets.go
Normal file
21
itests/kit/ensemble_presets.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package kit
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func EnsembleMinimum(t *testing.T, opts ...NodeOpt) (*TestFullNode, *TestMiner, *Ensemble) {
|
||||||
|
var (
|
||||||
|
full TestFullNode
|
||||||
|
miner TestMiner
|
||||||
|
)
|
||||||
|
ensemble := NewEnsemble(t).FullNode(&full, opts...).Miner(&miner, &full, opts...).Start()
|
||||||
|
return &full, &miner, ensemble
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnsembleTwo(t *testing.T, opts ...NodeOpt) (*TestFullNode, *TestFullNode, *TestMiner, *Ensemble) {
|
||||||
|
var (
|
||||||
|
one, two TestFullNode
|
||||||
|
miner TestMiner
|
||||||
|
)
|
||||||
|
ensemble := NewEnsemble(t).FullNode(&one, opts...).FullNode(&two, opts...).Miner(&miner, &one, opts...).Start()
|
||||||
|
return &one, &two, &miner, ensemble
|
||||||
|
}
|
@ -5,6 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
@ -15,9 +16,7 @@ import (
|
|||||||
// to the recipient address.
|
// to the recipient address.
|
||||||
func SendFunds(ctx context.Context, t *testing.T, sender TestFullNode, recipient address.Address, amount abi.TokenAmount) {
|
func SendFunds(ctx context.Context, t *testing.T, sender TestFullNode, recipient address.Address, amount abi.TokenAmount) {
|
||||||
senderAddr, err := sender.WalletDefaultAddress(ctx)
|
senderAddr, err := sender.WalletDefaultAddress(ctx)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := &types.Message{
|
msg := &types.Message{
|
||||||
From: senderAddr,
|
From: senderAddr,
|
||||||
@ -26,14 +25,10 @@ func SendFunds(ctx context.Context, t *testing.T, sender TestFullNode, recipient
|
|||||||
}
|
}
|
||||||
|
|
||||||
sm, err := sender.MpoolPushMessage(ctx, msg, nil)
|
sm, err := sender.MpoolPushMessage(ctx, msg, nil)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
res, err := sender.StateWaitMsg(ctx, sm.Cid(), 3, api.LookbackNoLimit, true)
|
res, err := sender.StateWaitMsg(ctx, sm.Cid(), 3, api.LookbackNoLimit, true)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
require.Equal(t, 0, res.Receipt.ExitCode, "did not successfully send funds")
|
||||||
if res.Receipt.ExitCode != 0 {
|
|
||||||
t.Fatal("did not successfully send money")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
25
itests/kit/init.go
Normal file
25
itests/kit/init.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package kit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/lotus/build"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/policy"
|
||||||
|
logging "github.com/ipfs/go-log/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
_ = logging.SetLogLevel("*", "INFO")
|
||||||
|
|
||||||
|
policy.SetConsensusMinerMinPower(abi.NewStoragePower(2048))
|
||||||
|
policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1)
|
||||||
|
policy.SetMinVerifiedDealSize(abi.NewStoragePower(256))
|
||||||
|
|
||||||
|
err := os.Setenv("BELLMAN_NO_GPU", "1")
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("failed to set BELLMAN_NO_GPU env variable: %s", err))
|
||||||
|
}
|
||||||
|
build.InsecurePoStValidation = true
|
||||||
|
}
|
@ -1,87 +1,42 @@
|
|||||||
package kit
|
package kit
|
||||||
|
|
||||||
import (
|
//
|
||||||
"context"
|
// func StartTwoNodesOneMiner(ctx context.Context, t *testing.T, blocktime time.Duration) ([]TestFullNode, []address.Address) {
|
||||||
"testing"
|
// n, sn := MinerRPCMockMinerBuilder(t, TwoFull, OneMiner)
|
||||||
"time"
|
//
|
||||||
|
// fullNode1 := n[0]
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
// fullNode2 := n[1]
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
// miner := sn[0]
|
||||||
|
//
|
||||||
"github.com/filecoin-project/go-address"
|
// // Get everyone connected
|
||||||
)
|
// addrs, err := fullNode1.NetAddrsListen(ctx)
|
||||||
|
// if err != nil {
|
||||||
func StartOneNodeOneMiner(ctx context.Context, t *testing.T, blocktime time.Duration) (TestFullNode, address.Address) {
|
// t.Fatal(err)
|
||||||
n, sn := RPCMockMinerBuilder(t, OneFull, OneMiner)
|
// }
|
||||||
|
//
|
||||||
full := n[0]
|
// if err := fullNode2.NetConnect(ctx, addrs); err != nil {
|
||||||
miner := sn[0]
|
// t.Fatal(err)
|
||||||
|
// }
|
||||||
// Get everyone connected
|
//
|
||||||
addrs, err := full.NetAddrsListen(ctx)
|
// // Start mining blocks
|
||||||
if err != nil {
|
// bm := NewBlockMiner(t, miner)
|
||||||
t.Fatal(err)
|
// bm.MineBlocks(ctx, blocktime)
|
||||||
}
|
// t.Cleanup(bm.Stop)
|
||||||
|
//
|
||||||
if err := miner.NetConnect(ctx, addrs); err != nil {
|
// // Send some funds to register the second node
|
||||||
t.Fatal(err)
|
// fullNodeAddr2, err := fullNode2.WalletNew(ctx, types.KTSecp256k1)
|
||||||
}
|
// if err != nil {
|
||||||
|
// t.Fatal(err)
|
||||||
// Start mining blocks
|
// }
|
||||||
bm := NewBlockMiner(t, miner)
|
//
|
||||||
bm.MineBlocks(ctx, blocktime)
|
// SendFunds(ctx, t, fullNode1, fullNodeAddr2, abi.NewTokenAmount(1e18))
|
||||||
t.Cleanup(bm.Stop)
|
//
|
||||||
|
// // Get the first node's address
|
||||||
// Get the full node's wallet address
|
// fullNodeAddr1, err := fullNode1.WalletDefaultAddress(ctx)
|
||||||
fullAddr, err := full.WalletDefaultAddress(ctx)
|
// if err != nil {
|
||||||
if err != nil {
|
// t.Fatal(err)
|
||||||
t.Fatal(err)
|
// }
|
||||||
}
|
//
|
||||||
|
// // Create mock CLI
|
||||||
// Create mock CLI
|
// return n, []address.Address{fullNodeAddr1, fullNodeAddr2}
|
||||||
return full, fullAddr
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
func StartTwoNodesOneMiner(ctx context.Context, t *testing.T, blocktime time.Duration) ([]TestFullNode, []address.Address) {
|
|
||||||
n, sn := RPCMockMinerBuilder(t, TwoFull, OneMiner)
|
|
||||||
|
|
||||||
fullNode1 := n[0]
|
|
||||||
fullNode2 := n[1]
|
|
||||||
miner := sn[0]
|
|
||||||
|
|
||||||
// Get everyone connected
|
|
||||||
addrs, err := fullNode1.NetAddrsListen(ctx)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fullNode2.NetConnect(ctx, addrs); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := miner.NetConnect(ctx, addrs); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start mining blocks
|
|
||||||
bm := NewBlockMiner(t, miner)
|
|
||||||
bm.MineBlocks(ctx, blocktime)
|
|
||||||
t.Cleanup(bm.Stop)
|
|
||||||
|
|
||||||
// Send some funds to register the second node
|
|
||||||
fullNodeAddr2, err := fullNode2.WalletNew(ctx, types.KTSecp256k1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
SendFunds(ctx, t, fullNode1, fullNodeAddr2, abi.NewTokenAmount(1e18))
|
|
||||||
|
|
||||||
// Get the first node's address
|
|
||||||
fullNodeAddr1, err := fullNode1.WalletDefaultAddress(ctx)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create mock CLI
|
|
||||||
return n, []address.Address{fullNodeAddr1, fullNodeAddr2}
|
|
||||||
}
|
|
||||||
|
@ -1,606 +0,0 @@
|
|||||||
package kit
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"crypto/rand"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
|
||||||
"github.com/filecoin-project/go-state-types/big"
|
|
||||||
"github.com/filecoin-project/go-state-types/exitcode"
|
|
||||||
"github.com/filecoin-project/go-storedcounter"
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
|
||||||
"github.com/filecoin-project/lotus/api/client"
|
|
||||||
"github.com/filecoin-project/lotus/api/v1api"
|
|
||||||
"github.com/filecoin-project/lotus/build"
|
|
||||||
"github.com/filecoin-project/lotus/chain"
|
|
||||||
"github.com/filecoin-project/lotus/chain/actors"
|
|
||||||
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
|
||||||
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
|
|
||||||
"github.com/filecoin-project/lotus/chain/gen"
|
|
||||||
genesis2 "github.com/filecoin-project/lotus/chain/gen/genesis"
|
|
||||||
"github.com/filecoin-project/lotus/chain/messagepool"
|
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
|
||||||
"github.com/filecoin-project/lotus/chain/wallet"
|
|
||||||
"github.com/filecoin-project/lotus/cmd/lotus-seed/seed"
|
|
||||||
sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage"
|
|
||||||
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
|
|
||||||
"github.com/filecoin-project/lotus/extern/sector-storage/mock"
|
|
||||||
"github.com/filecoin-project/lotus/genesis"
|
|
||||||
lotusminer "github.com/filecoin-project/lotus/miner"
|
|
||||||
"github.com/filecoin-project/lotus/node"
|
|
||||||
"github.com/filecoin-project/lotus/node/modules"
|
|
||||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
|
||||||
testing2 "github.com/filecoin-project/lotus/node/modules/testing"
|
|
||||||
"github.com/filecoin-project/lotus/node/repo"
|
|
||||||
"github.com/filecoin-project/lotus/storage/mockstorage"
|
|
||||||
miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner"
|
|
||||||
power2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/power"
|
|
||||||
"github.com/ipfs/go-datastore"
|
|
||||||
"github.com/libp2p/go-libp2p-core/crypto"
|
|
||||||
"github.com/libp2p/go-libp2p-core/peer"
|
|
||||||
mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
|
|
||||||
"github.com/multiformats/go-multiaddr"
|
|
||||||
manet "github.com/multiformats/go-multiaddr/net"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
chain.BootstrapPeerThreshold = 1
|
|
||||||
messagepool.HeadChangeCoalesceMinDelay = time.Microsecond
|
|
||||||
messagepool.HeadChangeCoalesceMaxDelay = 2 * time.Microsecond
|
|
||||||
messagepool.HeadChangeCoalesceMergeInterval = 100 * time.Nanosecond
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateTestStorageNode(ctx context.Context, t *testing.T, waddr address.Address, act address.Address, pk crypto.PrivKey, tnd TestFullNode, mn mocknet.Mocknet, opts node.Option) TestMiner {
|
|
||||||
r := repo.NewMemory(nil)
|
|
||||||
|
|
||||||
lr, err := r.Lock(repo.StorageMiner)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
ks, err := lr.KeyStore()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
kbytes, err := pk.Bytes()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
err = ks.Put("libp2p-host", types.KeyInfo{
|
|
||||||
Type: "libp2p-host",
|
|
||||||
PrivateKey: kbytes,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
ds, err := lr.Datastore(context.TODO(), "/metadata")
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = ds.Put(datastore.NewKey("miner-address"), act.Bytes())
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
nic := storedcounter.New(ds, datastore.NewKey(modules.StorageCounterDSPrefix))
|
|
||||||
for i := 0; i < GenesisPreseals; i++ {
|
|
||||||
_, err := nic.Next()
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
_, err = nic.Next()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
err = lr.Close()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
peerid, err := peer.IDFromPrivateKey(pk)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
enc, err := actors.SerializeParams(&miner2.ChangePeerIDParams{NewID: abi.PeerID(peerid)})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
msg := &types.Message{
|
|
||||||
To: act,
|
|
||||||
From: waddr,
|
|
||||||
Method: miner.Methods.ChangePeerID,
|
|
||||||
Params: enc,
|
|
||||||
Value: types.NewInt(0),
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = tnd.MpoolPushMessage(ctx, msg, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// start node
|
|
||||||
var minerapi api.StorageMiner
|
|
||||||
|
|
||||||
mineBlock := make(chan lotusminer.MineReq)
|
|
||||||
stop, err := node.New(ctx,
|
|
||||||
node.StorageMiner(&minerapi),
|
|
||||||
node.Online(),
|
|
||||||
node.Repo(r),
|
|
||||||
node.Test(),
|
|
||||||
|
|
||||||
node.MockHost(mn),
|
|
||||||
|
|
||||||
node.Override(new(v1api.FullNode), tnd),
|
|
||||||
node.Override(new(*lotusminer.Miner), lotusminer.NewTestMiner(mineBlock, act)),
|
|
||||||
|
|
||||||
opts,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to construct node: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Cleanup(func() { _ = stop(context.Background()) })
|
|
||||||
|
|
||||||
/*// Bootstrap with full node
|
|
||||||
remoteAddrs, err := tnd.NetAddrsListen(Ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
err = minerapi.NetConnect(Ctx, remoteAddrs)
|
|
||||||
require.NoError(t, err)*/
|
|
||||||
mineOne := func(ctx context.Context, req lotusminer.MineReq) error {
|
|
||||||
select {
|
|
||||||
case mineBlock <- req:
|
|
||||||
return nil
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TestMiner{StorageMiner: minerapi, MineOne: mineOne, Stop: stop}
|
|
||||||
}
|
|
||||||
|
|
||||||
func storageBuilder(parentNode TestFullNode, mn mocknet.Mocknet, opts node.Option) MinerBuilder {
|
|
||||||
return func(ctx context.Context, t *testing.T, spt abi.RegisteredSealProof, owner address.Address) TestMiner {
|
|
||||||
pk, _, err := crypto.GenerateEd25519Key(rand.Reader)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
minerPid, err := peer.IDFromPrivateKey(pk)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
params, serr := actors.SerializeParams(&power2.CreateMinerParams{
|
|
||||||
Owner: owner,
|
|
||||||
Worker: owner,
|
|
||||||
SealProofType: spt,
|
|
||||||
Peer: abi.PeerID(minerPid),
|
|
||||||
})
|
|
||||||
require.NoError(t, serr)
|
|
||||||
|
|
||||||
createStorageMinerMsg := &types.Message{
|
|
||||||
To: power.Address,
|
|
||||||
From: owner,
|
|
||||||
Value: big.Zero(),
|
|
||||||
|
|
||||||
Method: power.Methods.CreateMiner,
|
|
||||||
Params: params,
|
|
||||||
|
|
||||||
GasLimit: 0,
|
|
||||||
GasPremium: big.NewInt(5252),
|
|
||||||
}
|
|
||||||
|
|
||||||
signed, err := parentNode.MpoolPushMessage(ctx, createStorageMinerMsg, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
mw, err := parentNode.StateWaitMsg(ctx, signed.Cid(), build.MessageConfidence, api.LookbackNoLimit, true)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, exitcode.Ok, mw.Receipt.ExitCode)
|
|
||||||
|
|
||||||
var retval power2.CreateMinerReturn
|
|
||||||
err = retval.UnmarshalCBOR(bytes.NewReader(mw.Receipt.Return))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
return CreateTestStorageNode(ctx, t, owner, retval.IDAddress, pk, parentNode, mn, opts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Builder(t *testing.T, fullOpts []FullNodeOpts, storage []StorageMiner) ([]TestFullNode, []TestMiner) {
|
|
||||||
return mockBuilderOpts(t, fullOpts, storage, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func RPCBuilder(t *testing.T, fullOpts []FullNodeOpts, storage []StorageMiner) ([]TestFullNode, []TestMiner) {
|
|
||||||
return mockBuilderOpts(t, fullOpts, storage, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MockMinerBuilder(t *testing.T, fullOpts []FullNodeOpts, storage []StorageMiner) ([]TestFullNode, []TestMiner) {
|
|
||||||
return mockMinerBuilderOpts(t, fullOpts, storage, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func RPCMockMinerBuilder(t *testing.T, fullOpts []FullNodeOpts, storage []StorageMiner) ([]TestFullNode, []TestMiner) {
|
|
||||||
return mockMinerBuilderOpts(t, fullOpts, storage, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func mockBuilderOpts(t *testing.T, fullOpts []FullNodeOpts, storage []StorageMiner, rpc bool) ([]TestFullNode, []TestMiner) {
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
t.Cleanup(cancel)
|
|
||||||
|
|
||||||
mn := mocknet.New(ctx)
|
|
||||||
|
|
||||||
fulls := make([]TestFullNode, len(fullOpts))
|
|
||||||
miners := make([]TestMiner, len(storage))
|
|
||||||
|
|
||||||
// *****
|
|
||||||
pk, _, err := crypto.GenerateEd25519Key(rand.Reader)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
minerPid, err := peer.IDFromPrivateKey(pk)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var genbuf bytes.Buffer
|
|
||||||
|
|
||||||
if len(storage) > 1 {
|
|
||||||
panic("need more peer IDs")
|
|
||||||
}
|
|
||||||
// *****
|
|
||||||
|
|
||||||
// PRESEAL SECTION, TRY TO REPLACE WITH BETTER IN THE FUTURE
|
|
||||||
// TODO: would be great if there was a better way to fake the preseals
|
|
||||||
|
|
||||||
var (
|
|
||||||
genms []genesis.Miner
|
|
||||||
maddrs []address.Address
|
|
||||||
genaccs []genesis.Actor
|
|
||||||
keys []*wallet.Key
|
|
||||||
)
|
|
||||||
|
|
||||||
var presealDirs []string
|
|
||||||
for i := 0; i < len(storage); i++ {
|
|
||||||
maddr, err := address.NewIDAddress(genesis2.MinerStart + uint64(i))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
tdir, err := ioutil.TempDir("", "preseal-memgen")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
genm, k, err := seed.PreSeal(maddr, abi.RegisteredSealProof_StackedDrg2KiBV1, 0, GenesisPreseals, tdir, []byte("make genesis mem random"), nil, true)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
genm.PeerId = minerPid
|
|
||||||
|
|
||||||
wk, err := wallet.NewKey(*k)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
genaccs = append(genaccs, genesis.Actor{
|
|
||||||
Type: genesis.TAccount,
|
|
||||||
Balance: big.Mul(big.NewInt(400000000), types.NewInt(build.FilecoinPrecision)),
|
|
||||||
Meta: (&genesis.AccountMeta{Owner: wk.Address}).ActorMeta(),
|
|
||||||
})
|
|
||||||
|
|
||||||
keys = append(keys, wk)
|
|
||||||
presealDirs = append(presealDirs, tdir)
|
|
||||||
maddrs = append(maddrs, maddr)
|
|
||||||
genms = append(genms, *genm)
|
|
||||||
}
|
|
||||||
templ := &genesis.Template{
|
|
||||||
Accounts: genaccs,
|
|
||||||
Miners: genms,
|
|
||||||
NetworkName: "test",
|
|
||||||
Timestamp: uint64(time.Now().Unix() - 10000), // some time sufficiently far in the past
|
|
||||||
VerifregRootKey: gen.DefaultVerifregRootkeyActor,
|
|
||||||
RemainderAccount: gen.DefaultRemainderAccountActor,
|
|
||||||
}
|
|
||||||
|
|
||||||
// END PRESEAL SECTION
|
|
||||||
|
|
||||||
for i := 0; i < len(fullOpts); i++ {
|
|
||||||
var genesis node.Option
|
|
||||||
if i == 0 {
|
|
||||||
genesis = node.Override(new(modules.Genesis), testing2.MakeGenesisMem(&genbuf, *templ))
|
|
||||||
} else {
|
|
||||||
genesis = node.Override(new(modules.Genesis), modules.LoadGenesis(genbuf.Bytes()))
|
|
||||||
}
|
|
||||||
|
|
||||||
stop, err := node.New(ctx,
|
|
||||||
node.FullAPI(&fulls[i].FullNode, node.Lite(fullOpts[i].Lite)),
|
|
||||||
node.Online(),
|
|
||||||
node.Repo(repo.NewMemory(nil)),
|
|
||||||
node.MockHost(mn),
|
|
||||||
node.Test(),
|
|
||||||
|
|
||||||
genesis,
|
|
||||||
|
|
||||||
fullOpts[i].Opts(fulls),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Cleanup(func() { _ = stop(context.Background()) })
|
|
||||||
|
|
||||||
if rpc {
|
|
||||||
fulls[i] = fullRpc(t, fulls[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
fulls[i].Stb = storageBuilder(fulls[i], mn, node.Options())
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, def := range storage {
|
|
||||||
// TODO: support non-bootstrap miners
|
|
||||||
if i != 0 {
|
|
||||||
t.Fatal("only one storage node supported")
|
|
||||||
}
|
|
||||||
if def.Full != 0 {
|
|
||||||
t.Fatal("storage nodes only supported on the first full node")
|
|
||||||
}
|
|
||||||
|
|
||||||
f := fulls[def.Full]
|
|
||||||
if _, err := f.FullNode.WalletImport(ctx, &keys[i].KeyInfo); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := f.FullNode.WalletSetDefault(ctx, keys[i].Address); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
genMiner := maddrs[i]
|
|
||||||
wa := genms[i].Worker
|
|
||||||
|
|
||||||
opts := def.Opts
|
|
||||||
if opts == nil {
|
|
||||||
opts = node.Options()
|
|
||||||
}
|
|
||||||
miners[i] = CreateTestStorageNode(ctx, t, wa, genMiner, pk, f, mn, opts)
|
|
||||||
if err := miners[i].StorageAddLocal(ctx, presealDirs[i]); err != nil {
|
|
||||||
t.Fatalf("%+v", err)
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
sma := miners[i].StorageMiner.(*impl.StorageMinerAPI)
|
|
||||||
|
|
||||||
psd := presealDirs[i]
|
|
||||||
*/
|
|
||||||
if rpc {
|
|
||||||
miners[i] = storerRpc(t, miners[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mn.LinkAll(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(miners) > 0 {
|
|
||||||
// Mine 2 blocks to setup some CE stuff in some actors
|
|
||||||
var wait sync.Mutex
|
|
||||||
wait.Lock()
|
|
||||||
|
|
||||||
bm := NewBlockMiner(t, miners[0])
|
|
||||||
t.Cleanup(bm.Stop)
|
|
||||||
|
|
||||||
bm.MineUntilBlock(ctx, fulls[0], func(epoch abi.ChainEpoch) {
|
|
||||||
wait.Unlock()
|
|
||||||
})
|
|
||||||
|
|
||||||
wait.Lock()
|
|
||||||
bm.MineUntilBlock(ctx, fulls[0], func(epoch abi.ChainEpoch) {
|
|
||||||
wait.Unlock()
|
|
||||||
})
|
|
||||||
wait.Lock()
|
|
||||||
}
|
|
||||||
|
|
||||||
return fulls, miners
|
|
||||||
}
|
|
||||||
|
|
||||||
func mockMinerBuilderOpts(t *testing.T, fullOpts []FullNodeOpts, storage []StorageMiner, rpc bool) ([]TestFullNode, []TestMiner) {
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
t.Cleanup(cancel)
|
|
||||||
|
|
||||||
mn := mocknet.New(ctx)
|
|
||||||
|
|
||||||
fulls := make([]TestFullNode, len(fullOpts))
|
|
||||||
miners := make([]TestMiner, len(storage))
|
|
||||||
|
|
||||||
var genbuf bytes.Buffer
|
|
||||||
|
|
||||||
// PRESEAL SECTION, TRY TO REPLACE WITH BETTER IN THE FUTURE
|
|
||||||
// TODO: would be great if there was a better way to fake the preseals
|
|
||||||
|
|
||||||
var (
|
|
||||||
genms []genesis.Miner
|
|
||||||
genaccs []genesis.Actor
|
|
||||||
maddrs []address.Address
|
|
||||||
keys []*wallet.Key
|
|
||||||
pidKeys []crypto.PrivKey
|
|
||||||
)
|
|
||||||
for i := 0; i < len(storage); i++ {
|
|
||||||
maddr, err := address.NewIDAddress(genesis2.MinerStart + uint64(i))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
preseals := storage[i].Preseal
|
|
||||||
if preseals == PresealGenesis {
|
|
||||||
preseals = GenesisPreseals
|
|
||||||
}
|
|
||||||
|
|
||||||
genm, k, err := mockstorage.PreSeal(abi.RegisteredSealProof_StackedDrg2KiBV1, maddr, preseals)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pk, _, err := crypto.GenerateEd25519Key(rand.Reader)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
minerPid, err := peer.IDFromPrivateKey(pk)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
genm.PeerId = minerPid
|
|
||||||
|
|
||||||
wk, err := wallet.NewKey(*k)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
genaccs = append(genaccs, genesis.Actor{
|
|
||||||
Type: genesis.TAccount,
|
|
||||||
Balance: big.Mul(big.NewInt(400000000), types.NewInt(build.FilecoinPrecision)),
|
|
||||||
Meta: (&genesis.AccountMeta{Owner: wk.Address}).ActorMeta(),
|
|
||||||
})
|
|
||||||
|
|
||||||
keys = append(keys, wk)
|
|
||||||
pidKeys = append(pidKeys, pk)
|
|
||||||
maddrs = append(maddrs, maddr)
|
|
||||||
genms = append(genms, *genm)
|
|
||||||
}
|
|
||||||
templ := &genesis.Template{
|
|
||||||
Accounts: genaccs,
|
|
||||||
Miners: genms,
|
|
||||||
NetworkName: "test",
|
|
||||||
Timestamp: uint64(time.Now().Unix()) - (build.BlockDelaySecs * 20000),
|
|
||||||
VerifregRootKey: gen.DefaultVerifregRootkeyActor,
|
|
||||||
RemainderAccount: gen.DefaultRemainderAccountActor,
|
|
||||||
}
|
|
||||||
|
|
||||||
// END PRESEAL SECTION
|
|
||||||
|
|
||||||
for i := 0; i < len(fullOpts); i++ {
|
|
||||||
var genesis node.Option
|
|
||||||
if i == 0 {
|
|
||||||
genesis = node.Override(new(modules.Genesis), testing2.MakeGenesisMem(&genbuf, *templ))
|
|
||||||
} else {
|
|
||||||
genesis = node.Override(new(modules.Genesis), modules.LoadGenesis(genbuf.Bytes()))
|
|
||||||
}
|
|
||||||
|
|
||||||
stop, err := node.New(ctx,
|
|
||||||
node.FullAPI(&fulls[i].FullNode, node.Lite(fullOpts[i].Lite)),
|
|
||||||
node.Online(),
|
|
||||||
node.Repo(repo.NewMemory(nil)),
|
|
||||||
node.MockHost(mn),
|
|
||||||
node.Test(),
|
|
||||||
|
|
||||||
node.Override(new(ffiwrapper.Verifier), mock.MockVerifier),
|
|
||||||
|
|
||||||
// so that we subscribe to pubsub topics immediately
|
|
||||||
node.Override(new(dtypes.Bootstrapper), dtypes.Bootstrapper(true)),
|
|
||||||
|
|
||||||
genesis,
|
|
||||||
|
|
||||||
fullOpts[i].Opts(fulls),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%+v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Cleanup(func() { _ = stop(context.Background()) })
|
|
||||||
|
|
||||||
if rpc {
|
|
||||||
fulls[i] = fullRpc(t, fulls[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
fulls[i].Stb = storageBuilder(fulls[i], mn, node.Options(
|
|
||||||
node.Override(new(sectorstorage.SectorManager), func() (sectorstorage.SectorManager, error) {
|
|
||||||
return mock.NewMockSectorMgr(nil), nil
|
|
||||||
}),
|
|
||||||
node.Override(new(ffiwrapper.Verifier), mock.MockVerifier),
|
|
||||||
node.Unset(new(*sectorstorage.Manager)),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, def := range storage {
|
|
||||||
// TODO: support non-bootstrap miners
|
|
||||||
|
|
||||||
minerID := abi.ActorID(genesis2.MinerStart + uint64(i))
|
|
||||||
|
|
||||||
if def.Full != 0 {
|
|
||||||
t.Fatal("storage nodes only supported on the first full node")
|
|
||||||
}
|
|
||||||
|
|
||||||
f := fulls[def.Full]
|
|
||||||
if _, err := f.FullNode.WalletImport(ctx, &keys[i].KeyInfo); err != nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
if err := f.FullNode.WalletSetDefault(ctx, keys[i].Address); err != nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
sectors := make([]abi.SectorID, len(genms[i].Sectors))
|
|
||||||
for i, sector := range genms[i].Sectors {
|
|
||||||
sectors[i] = abi.SectorID{
|
|
||||||
Miner: minerID,
|
|
||||||
Number: sector.SectorID,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := def.Opts
|
|
||||||
if opts == nil {
|
|
||||||
opts = node.Options()
|
|
||||||
}
|
|
||||||
miners[i] = CreateTestStorageNode(ctx, t, genms[i].Worker, maddrs[i], pidKeys[i], f, mn, node.Options(
|
|
||||||
node.Override(new(sectorstorage.SectorManager), func() (sectorstorage.SectorManager, error) {
|
|
||||||
return mock.NewMockSectorMgr(sectors), nil
|
|
||||||
}),
|
|
||||||
node.Override(new(ffiwrapper.Verifier), mock.MockVerifier),
|
|
||||||
node.Unset(new(*sectorstorage.Manager)),
|
|
||||||
opts,
|
|
||||||
))
|
|
||||||
|
|
||||||
if rpc {
|
|
||||||
miners[i] = storerRpc(t, miners[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mn.LinkAll(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bm := NewBlockMiner(t, miners[0])
|
|
||||||
|
|
||||||
if len(miners) > 0 {
|
|
||||||
// Mine 2 blocks to setup some CE stuff in some actors
|
|
||||||
var wait sync.Mutex
|
|
||||||
wait.Lock()
|
|
||||||
|
|
||||||
bm.MineUntilBlock(ctx, fulls[0], func(abi.ChainEpoch) {
|
|
||||||
wait.Unlock()
|
|
||||||
})
|
|
||||||
wait.Lock()
|
|
||||||
bm.MineUntilBlock(ctx, fulls[0], func(abi.ChainEpoch) {
|
|
||||||
wait.Unlock()
|
|
||||||
})
|
|
||||||
wait.Lock()
|
|
||||||
}
|
|
||||||
|
|
||||||
return fulls, miners
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateRPCServer(t *testing.T, handler http.Handler) (*httptest.Server, multiaddr.Multiaddr) {
|
|
||||||
testServ := httptest.NewServer(handler)
|
|
||||||
t.Cleanup(testServ.Close)
|
|
||||||
t.Cleanup(testServ.CloseClientConnections)
|
|
||||||
|
|
||||||
addr := testServ.Listener.Addr()
|
|
||||||
maddr, err := manet.FromNetAddr(addr)
|
|
||||||
require.NoError(t, err)
|
|
||||||
return testServ, maddr
|
|
||||||
}
|
|
||||||
|
|
||||||
func fullRpc(t *testing.T, nd TestFullNode) TestFullNode {
|
|
||||||
handler, err := node.FullNodeHandler(nd.FullNode)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
srv, maddr := CreateRPCServer(t, handler)
|
|
||||||
|
|
||||||
var ret TestFullNode
|
|
||||||
cl, stop, err := client.NewFullNodeRPCV1(context.Background(), srv.Listener.Addr().String()+"/rpc/v1", nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
t.Cleanup(stop)
|
|
||||||
ret.ListenAddr, ret.FullNode = maddr, cl
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func storerRpc(t *testing.T, nd TestMiner) TestMiner {
|
|
||||||
handler, err := node.MinerHandler(nd.StorageMiner)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
srv, maddr := CreateRPCServer(t, handler)
|
|
||||||
|
|
||||||
var ret TestMiner
|
|
||||||
cl, stop, err := client.NewStorageMinerRPCV0(context.Background(), srv.Listener.Addr().String()+"/rpc/v0", nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
t.Cleanup(stop)
|
|
||||||
|
|
||||||
ret.ListenAddr, ret.StorageMiner, ret.MineOne = maddr, cl, nd.MineOne
|
|
||||||
return ret
|
|
||||||
}
|
|
22
itests/kit/node_full.go
Normal file
22
itests/kit/node_full.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package kit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/api/v1api"
|
||||||
|
"github.com/filecoin-project/lotus/chain/wallet"
|
||||||
|
"github.com/multiformats/go-multiaddr"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestFullNode struct {
|
||||||
|
v1api.FullNode
|
||||||
|
|
||||||
|
t *testing.T
|
||||||
|
|
||||||
|
// ListenAddr is the address on which an API server is listening, if an
|
||||||
|
// API server is created for this Node.
|
||||||
|
ListenAddr multiaddr.Multiaddr
|
||||||
|
DefaultKey *wallet.Key
|
||||||
|
|
||||||
|
options NodeOpts
|
||||||
|
}
|
97
itests/kit/node_miner.go
Normal file
97
itests/kit/node_miner.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package kit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
lapi "github.com/filecoin-project/lotus/api"
|
||||||
|
"github.com/filecoin-project/lotus/build"
|
||||||
|
"github.com/filecoin-project/lotus/chain/wallet"
|
||||||
|
sealing "github.com/filecoin-project/lotus/extern/storage-sealing"
|
||||||
|
"github.com/filecoin-project/lotus/miner"
|
||||||
|
libp2pcrypto "github.com/libp2p/go-libp2p-core/crypto"
|
||||||
|
"github.com/libp2p/go-libp2p-core/peer"
|
||||||
|
"github.com/multiformats/go-multiaddr"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestMiner struct {
|
||||||
|
lapi.StorageMiner
|
||||||
|
|
||||||
|
t *testing.T
|
||||||
|
|
||||||
|
// ListenAddr is the address on which an API server is listening, if an
|
||||||
|
// API server is created for this Node
|
||||||
|
ListenAddr multiaddr.Multiaddr
|
||||||
|
|
||||||
|
ActorAddr address.Address
|
||||||
|
OwnerKey *wallet.Key
|
||||||
|
MineOne func(context.Context, miner.MineReq) error
|
||||||
|
Stop func(context.Context) error
|
||||||
|
|
||||||
|
FullNode *TestFullNode
|
||||||
|
PresealDir string
|
||||||
|
|
||||||
|
Libp2p struct {
|
||||||
|
PeerID peer.ID
|
||||||
|
PrivKey libp2pcrypto.PrivKey
|
||||||
|
}
|
||||||
|
|
||||||
|
options NodeOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
var MineNext = miner.MineReq{
|
||||||
|
InjectNulls: 0,
|
||||||
|
Done: func(bool, abi.ChainEpoch, error) {},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *TestMiner) PledgeSectors(ctx context.Context, n, existing int, blockNotif <-chan struct{}) { //nolint:golint
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if i%3 == 0 && blockNotif != nil {
|
||||||
|
<-blockNotif
|
||||||
|
tm.t.Log("WAIT")
|
||||||
|
}
|
||||||
|
tm.t.Logf("PLEDGING %d", i)
|
||||||
|
_, err := tm.PledgeSector(ctx)
|
||||||
|
require.NoError(tm.t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
s, err := tm.SectorsList(ctx) // Note - the test builder doesn't import genesis sectors into FSM
|
||||||
|
require.NoError(tm.t, err)
|
||||||
|
fmt.Printf("Sectors: %d\n", len(s))
|
||||||
|
if len(s) >= n+existing {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
build.Clock.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("All sectors is fsm\n")
|
||||||
|
|
||||||
|
s, err := tm.SectorsList(ctx)
|
||||||
|
require.NoError(tm.t, err)
|
||||||
|
|
||||||
|
toCheck := map[abi.SectorNumber]struct{}{}
|
||||||
|
for _, number := range s {
|
||||||
|
toCheck[number] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for len(toCheck) > 0 {
|
||||||
|
for n := range toCheck {
|
||||||
|
st, err := tm.SectorsStatus(ctx, n, false)
|
||||||
|
require.NoError(tm.t, err)
|
||||||
|
if st.State == lapi.SectorState(sealing.Proving) {
|
||||||
|
delete(toCheck, n)
|
||||||
|
}
|
||||||
|
require.NotContains(tm.t, string(st.State), "Fail", "sector in a failed state")
|
||||||
|
}
|
||||||
|
|
||||||
|
build.Clock.Sleep(100 * time.Millisecond)
|
||||||
|
fmt.Printf("WaitSeal: %d\n", len(s))
|
||||||
|
}
|
||||||
|
}
|
@ -1,133 +0,0 @@
|
|||||||
package kit
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/multiformats/go-multiaddr"
|
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
|
||||||
"github.com/filecoin-project/go-state-types/network"
|
|
||||||
|
|
||||||
lapi "github.com/filecoin-project/lotus/api"
|
|
||||||
"github.com/filecoin-project/lotus/api/v1api"
|
|
||||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
|
||||||
"github.com/filecoin-project/lotus/miner"
|
|
||||||
"github.com/filecoin-project/lotus/node"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MinerBuilder func(context.Context, *testing.T, abi.RegisteredSealProof, address.Address) TestMiner
|
|
||||||
|
|
||||||
type TestFullNode struct {
|
|
||||||
v1api.FullNode
|
|
||||||
// ListenAddr is the address on which an API server is listening, if an
|
|
||||||
// API server is created for this Node
|
|
||||||
ListenAddr multiaddr.Multiaddr
|
|
||||||
|
|
||||||
Stb MinerBuilder
|
|
||||||
}
|
|
||||||
|
|
||||||
type TestMiner struct {
|
|
||||||
lapi.StorageMiner
|
|
||||||
// ListenAddr is the address on which an API server is listening, if an
|
|
||||||
// API server is created for this Node
|
|
||||||
ListenAddr multiaddr.Multiaddr
|
|
||||||
|
|
||||||
MineOne func(context.Context, miner.MineReq) error
|
|
||||||
Stop func(context.Context) error
|
|
||||||
}
|
|
||||||
|
|
||||||
var PresealGenesis = -1
|
|
||||||
|
|
||||||
const GenesisPreseals = 2
|
|
||||||
|
|
||||||
const TestSpt = abi.RegisteredSealProof_StackedDrg2KiBV1_1
|
|
||||||
|
|
||||||
// Options for setting up a mock storage Miner
|
|
||||||
type StorageMiner struct {
|
|
||||||
Full int
|
|
||||||
Opts node.Option
|
|
||||||
Preseal int
|
|
||||||
}
|
|
||||||
|
|
||||||
type OptionGenerator func([]TestFullNode) node.Option
|
|
||||||
|
|
||||||
// Options for setting up a mock full node
|
|
||||||
type FullNodeOpts struct {
|
|
||||||
Lite bool // run node in "lite" mode
|
|
||||||
Opts OptionGenerator // generate dependency injection options
|
|
||||||
}
|
|
||||||
|
|
||||||
// APIBuilder is a function which is invoked in test suite to provide
|
|
||||||
// test nodes and networks
|
|
||||||
//
|
|
||||||
// fullOpts array defines options for each full node
|
|
||||||
// storage array defines storage nodes, numbers in the array specify full node
|
|
||||||
// index the storage node 'belongs' to
|
|
||||||
type APIBuilder func(t *testing.T, full []FullNodeOpts, storage []StorageMiner) ([]TestFullNode, []TestMiner)
|
|
||||||
|
|
||||||
func DefaultFullOpts(nFull int) []FullNodeOpts {
|
|
||||||
full := make([]FullNodeOpts, nFull)
|
|
||||||
for i := range full {
|
|
||||||
full[i] = FullNodeOpts{
|
|
||||||
Opts: func(nodes []TestFullNode) node.Option {
|
|
||||||
return node.Options()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return full
|
|
||||||
}
|
|
||||||
|
|
||||||
var OneMiner = []StorageMiner{{Full: 0, Preseal: PresealGenesis}}
|
|
||||||
var OneFull = DefaultFullOpts(1)
|
|
||||||
var TwoFull = DefaultFullOpts(2)
|
|
||||||
|
|
||||||
var FullNodeWithLatestActorsAt = func(upgradeHeight abi.ChainEpoch) FullNodeOpts {
|
|
||||||
if upgradeHeight == -1 {
|
|
||||||
upgradeHeight = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
return FullNodeOpts{
|
|
||||||
Opts: func(nodes []TestFullNode) node.Option {
|
|
||||||
return node.Override(new(stmgr.UpgradeSchedule), stmgr.UpgradeSchedule{{
|
|
||||||
// prepare for upgrade.
|
|
||||||
Network: network.Version9,
|
|
||||||
Height: 1,
|
|
||||||
Migration: stmgr.UpgradeActorsV2,
|
|
||||||
}, {
|
|
||||||
Network: network.Version10,
|
|
||||||
Height: 2,
|
|
||||||
Migration: stmgr.UpgradeActorsV3,
|
|
||||||
}, {
|
|
||||||
Network: network.Version12,
|
|
||||||
Height: upgradeHeight,
|
|
||||||
Migration: stmgr.UpgradeActorsV4,
|
|
||||||
}})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var FullNodeWithSDRAt = func(calico, persian abi.ChainEpoch) FullNodeOpts {
|
|
||||||
return FullNodeOpts{
|
|
||||||
Opts: func(nodes []TestFullNode) node.Option {
|
|
||||||
return node.Override(new(stmgr.UpgradeSchedule), stmgr.UpgradeSchedule{{
|
|
||||||
Network: network.Version6,
|
|
||||||
Height: 1,
|
|
||||||
Migration: stmgr.UpgradeActorsV2,
|
|
||||||
}, {
|
|
||||||
Network: network.Version7,
|
|
||||||
Height: calico,
|
|
||||||
Migration: stmgr.UpgradeCalico,
|
|
||||||
}, {
|
|
||||||
Network: network.Version8,
|
|
||||||
Height: persian,
|
|
||||||
}})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var MineNext = miner.MineReq{
|
|
||||||
InjectNulls: 0,
|
|
||||||
Done: func(bool, abi.ChainEpoch, error) {},
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
package kit
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
|
||||||
"github.com/filecoin-project/lotus/build"
|
|
||||||
sealing "github.com/filecoin-project/lotus/extern/storage-sealing"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func PledgeSectors(t *testing.T, ctx context.Context, miner TestMiner, n, existing int, blockNotif <-chan struct{}) { //nolint:golint
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
if i%3 == 0 && blockNotif != nil {
|
|
||||||
<-blockNotif
|
|
||||||
t.Log("WAIT")
|
|
||||||
}
|
|
||||||
t.Logf("PLEDGING %d", i)
|
|
||||||
_, err := miner.PledgeSector(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
s, err := miner.SectorsList(ctx) // Note - the test builder doesn't import genesis sectors into FSM
|
|
||||||
require.NoError(t, err)
|
|
||||||
fmt.Printf("Sectors: %d\n", len(s))
|
|
||||||
if len(s) >= n+existing {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
build.Clock.Sleep(100 * time.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("All sectors is fsm\n")
|
|
||||||
|
|
||||||
s, err := miner.SectorsList(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
toCheck := map[abi.SectorNumber]struct{}{}
|
|
||||||
for _, number := range s {
|
|
||||||
toCheck[number] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
for len(toCheck) > 0 {
|
|
||||||
for n := range toCheck {
|
|
||||||
st, err := miner.SectorsStatus(ctx, n, false)
|
|
||||||
require.NoError(t, err)
|
|
||||||
if st.State == api.SectorState(sealing.Proving) {
|
|
||||||
delete(toCheck, n)
|
|
||||||
}
|
|
||||||
if strings.Contains(string(st.State), "Fail") {
|
|
||||||
t.Fatal("sector in a failed state", st.State)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
build.Clock.Sleep(100 * time.Millisecond)
|
|
||||||
fmt.Printf("WaitSeal: %d\n", len(s))
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user