generic test plan scaffolding, with baseline plan (#39)
* test plan scaffolding * generify the testplan role dispatch * manifest.toml * initial go.mod and go.sum * correct name * gomod: update from build * node construction in scaffolding * fix test runner return type * remove offending comments * add initial composition, and fix context bug * debug lines * check errors from node construction * specify Repo after Online option * add power/proof type initialization code * fix baseline composition * use new docker-images (build/run) introduced in the #48 PR * upgrade go-sdk to master (#51) * fix types for run.InvokeMap * fix miner actor sequence address * explictly specify listen address for nodes on the data network * make a separate full node for the miner * initialize the wallet for the full node before creating the storage node * go mod tidy * also set the listen address for the miner node * circleci to build the soup testplan * extract topics * test runner: pass the role map to doRun for generic runner * use a wrapper TestEnvironment to encapsulate the runenv and initCtx * embed RunEnv and InitContext into TestEnvironment for better ergonomics * remove empty import * extract stateReady Co-authored-by: Anton Evangelatov <anton.evangelatov@gmail.com>
This commit is contained in:
parent
1907fe2a91
commit
448bbf3710
@ -51,3 +51,7 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: "build lotus-testground"
|
name: "build lotus-testground"
|
||||||
command: pushd lotus-testground && go build .
|
command: pushd lotus-testground && go build .
|
||||||
|
- run:
|
||||||
|
name: "build lotus-soup"
|
||||||
|
command: pushd lotus-soup && go build .
|
||||||
|
|
||||||
|
1
lotus-soup/.gitignore
vendored
Normal file
1
lotus-soup/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
lotus-soup
|
64
lotus-soup/baseline.go
Normal file
64
lotus-soup/baseline.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// This is the basline test; Filecoin 101.
|
||||||
|
//
|
||||||
|
// A network with a bootstrapper, a number of miners, and a number of clients/full nodes
|
||||||
|
// is constructed and connected through the bootstrapper.
|
||||||
|
// Some funds are allocated to each node and a number of sectors are presealed in the genesis block.
|
||||||
|
//
|
||||||
|
// The test plan:
|
||||||
|
// One or more clients store content to one or more miners, testing storage deals.
|
||||||
|
// The plan ensures that the storage deals hit the blockchain and measure the time it took.
|
||||||
|
// Verification: one or more clients retrieve and verify the hashes of stored content.
|
||||||
|
// The plan ensures that all (previously) published content can be correctly retrieved
|
||||||
|
// and measures the time it took.
|
||||||
|
//
|
||||||
|
// Preparation of the genesis block: this is the responsibility of the bootstrapper.
|
||||||
|
// In order to compute the genesis block, we need to collect identities and presealed
|
||||||
|
// sectors from each node.
|
||||||
|
// The we create a genesis block that allocates some funds to each node and collects
|
||||||
|
// the presealed sectors.
|
||||||
|
var baselineRoles = map[string]func(*TestEnvironment) error{
|
||||||
|
"bootstrapper": runBaselineBootstrapper,
|
||||||
|
"miner": runBaselineMiner,
|
||||||
|
"client": runBaselineClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runBaselineBootstrapper(t *TestEnvironment) error {
|
||||||
|
t.RecordMessage("running bootstrapper")
|
||||||
|
_, err := prepareBootstrapper(t)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO just wait until completion of test, nothing else to do
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runBaselineMiner(t *TestEnvironment) error {
|
||||||
|
t.RecordMessage("running miner")
|
||||||
|
_, err := prepareMiner(t)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO wait a bit for network to bootstrap
|
||||||
|
// TODO just wait until completion of test, serving requests -- the client does all the job
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runBaselineClient(t *TestEnvironment) error {
|
||||||
|
t.RecordMessage("running client")
|
||||||
|
_, err := prepareClient(t)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO generate a number of random "files" and publish them to one or more miners
|
||||||
|
// TODO broadcast published content CIDs to other clients
|
||||||
|
// TODO select a random piece of content published by some other client and retreieve it
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
58
lotus-soup/compositions/composition.toml
Normal file
58
lotus-soup/compositions/composition.toml
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
[metadata]
|
||||||
|
name = "lotus-soup"
|
||||||
|
author = ""
|
||||||
|
|
||||||
|
[global]
|
||||||
|
plan = "lotus-soup"
|
||||||
|
case = "lotus-baseline"
|
||||||
|
total_instances = 3
|
||||||
|
builder = "docker:go"
|
||||||
|
runner = "local:docker"
|
||||||
|
|
||||||
|
[[groups]]
|
||||||
|
id = "bootstrapper"
|
||||||
|
[groups.resources]
|
||||||
|
memory = "120Mi"
|
||||||
|
cpu = "10m"
|
||||||
|
[groups.instances]
|
||||||
|
count = 1
|
||||||
|
percentage = 0.0
|
||||||
|
[groups.run]
|
||||||
|
[groups.run.test_params]
|
||||||
|
role = "bootstrapper"
|
||||||
|
clients = "1"
|
||||||
|
miners = "1"
|
||||||
|
balance = "2000"
|
||||||
|
sectors = "10"
|
||||||
|
|
||||||
|
[[groups]]
|
||||||
|
id = "miners"
|
||||||
|
[groups.resources]
|
||||||
|
memory = "120Mi"
|
||||||
|
cpu = "10m"
|
||||||
|
[groups.instances]
|
||||||
|
count = 1
|
||||||
|
percentage = 0.0
|
||||||
|
[groups.run]
|
||||||
|
[groups.run.test_params]
|
||||||
|
role = "miner"
|
||||||
|
clients = "1"
|
||||||
|
miners = "1"
|
||||||
|
balance = "2000"
|
||||||
|
sectors = "10"
|
||||||
|
|
||||||
|
[[groups]]
|
||||||
|
id = "clients"
|
||||||
|
[groups.resources]
|
||||||
|
memory = "120Mi"
|
||||||
|
cpu = "10m"
|
||||||
|
[groups.instances]
|
||||||
|
count = 1
|
||||||
|
percentage = 0.0
|
||||||
|
[groups.run]
|
||||||
|
[groups.run.test_params]
|
||||||
|
role = "client"
|
||||||
|
clients = "1"
|
||||||
|
miners = "1"
|
||||||
|
balance = "2000"
|
||||||
|
sectors = "10"
|
20
lotus-soup/go.mod
Normal file
20
lotus-soup/go.mod
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
module github.com/filecoin-project/oni/lotus-soup
|
||||||
|
|
||||||
|
go 1.14
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/filecoin-project/go-address v0.0.2-0.20200504173055-8b6f2fb2b3ef
|
||||||
|
github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b
|
||||||
|
github.com/filecoin-project/lotus v0.4.1-0.20200623104442-68d38eff33e4
|
||||||
|
github.com/filecoin-project/specs-actors v0.6.2-0.20200617175406-de392ca14121
|
||||||
|
github.com/ipfs/go-datastore v0.4.4
|
||||||
|
github.com/ipfs/go-log/v2 v2.1.2-0.20200609205458-f8d20c392cb7
|
||||||
|
github.com/libp2p/go-libp2p-core v0.6.0
|
||||||
|
github.com/multiformats/go-multiaddr v0.2.2
|
||||||
|
github.com/testground/sdk-go v0.2.3-0.20200617132925-2e4d69f9ba38
|
||||||
|
)
|
||||||
|
|
||||||
|
// This will work in all build modes: docker:go, exec:go, and local go build.
|
||||||
|
// On docker:go and exec:go, it maps to /extra/filecoin-ffi, as it's picked up
|
||||||
|
// as an "extra source" in the manifest.
|
||||||
|
replace github.com/filecoin-project/filecoin-ffi => ../extra/filecoin-ffi
|
1865
lotus-soup/go.sum
Normal file
1865
lotus-soup/go.sum
Normal file
File diff suppressed because it is too large
Load Diff
27
lotus-soup/main.go
Normal file
27
lotus-soup/main.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/testground/sdk-go/run"
|
||||||
|
"github.com/testground/sdk-go/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testplans = map[string]interface{}{
|
||||||
|
"lotus-baseline": doRun(baselineRoles),
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
run.InvokeMap(testplans)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doRun(roles map[string]func(*TestEnvironment) error) run.InitializedTestCaseFn {
|
||||||
|
return func(runenv *runtime.RunEnv, initCtx *run.InitContext) error {
|
||||||
|
role := runenv.StringParam("role")
|
||||||
|
proc, ok := baselineRoles[role]
|
||||||
|
if ok {
|
||||||
|
return proc(&TestEnvironment{RunEnv: runenv, InitContext: initCtx})
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Unknown role: %s", role)
|
||||||
|
}
|
||||||
|
}
|
30
lotus-soup/manifest.toml
Normal file
30
lotus-soup/manifest.toml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
name = "lotus-soup"
|
||||||
|
extra_sources = { "exec:go" = ["../extra/filecoin-ffi"] }
|
||||||
|
|
||||||
|
[defaults]
|
||||||
|
builder = "docker:go"
|
||||||
|
runner = "local:docker"
|
||||||
|
|
||||||
|
[builders."docker:go"]
|
||||||
|
enabled = true
|
||||||
|
build_base_image = "iptestground/oni-buildbase:v1"
|
||||||
|
runtime_image = "iptestground/oni-runtime:v1"
|
||||||
|
enable_go_build_cache = true
|
||||||
|
skip_runtime_image = false
|
||||||
|
|
||||||
|
[runners."local:docker"]
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
[runners."cluster:k8s"]
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
[[testcases]]
|
||||||
|
name = "lotus-baseline"
|
||||||
|
instances = { min = 1, max = 100, default = 5 }
|
||||||
|
|
||||||
|
[testcases.params]
|
||||||
|
clients = { type = "int", default = 1 }
|
||||||
|
miners = { type = "int", default = 1 }
|
||||||
|
balance = { type = "int", default = 1 }
|
||||||
|
sectors = { type = "int", default = 1 }
|
||||||
|
role = { type = "string" }
|
518
lotus-soup/node.go
Normal file
518
lotus-soup/node.go
Normal file
@ -0,0 +1,518 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
|
||||||
|
//"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/testground/sdk-go/run"
|
||||||
|
"github.com/testground/sdk-go/runtime"
|
||||||
|
"github.com/testground/sdk-go/sync"
|
||||||
|
|
||||||
|
logging "github.com/ipfs/go-log/v2"
|
||||||
|
libp2p_crypto "github.com/libp2p/go-libp2p-core/crypto"
|
||||||
|
"github.com/libp2p/go-libp2p-core/peer"
|
||||||
|
ma "github.com/multiformats/go-multiaddr"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-datastore"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/go-storedcounter"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/api"
|
||||||
|
"github.com/filecoin-project/lotus/build"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors"
|
||||||
|
genesis_chain "github.com/filecoin-project/lotus/chain/gen/genesis"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/chain/wallet"
|
||||||
|
"github.com/filecoin-project/lotus/cmd/lotus-seed/seed"
|
||||||
|
"github.com/filecoin-project/lotus/genesis"
|
||||||
|
"github.com/filecoin-project/lotus/node"
|
||||||
|
"github.com/filecoin-project/lotus/node/config"
|
||||||
|
"github.com/filecoin-project/lotus/node/modules"
|
||||||
|
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||||
|
"github.com/filecoin-project/lotus/node/modules/lp2p"
|
||||||
|
modtest "github.com/filecoin-project/lotus/node/modules/testing"
|
||||||
|
"github.com/filecoin-project/lotus/node/repo"
|
||||||
|
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||||
|
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||||
|
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||||
|
saminer "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||||
|
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||||
|
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
PrepareNodeTimeout = time.Minute
|
||||||
|
|
||||||
|
genesisTopic = sync.NewTopic("genesis", &GenesisMsg{})
|
||||||
|
balanceTopic = sync.NewTopic("balance", &InitialBalanceMsg{})
|
||||||
|
presealTopic = sync.NewTopic("preseal", &PresealMsg{})
|
||||||
|
|
||||||
|
stateReady = sync.State("ready")
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestEnvironment struct {
|
||||||
|
*runtime.RunEnv
|
||||||
|
*run.InitContext
|
||||||
|
}
|
||||||
|
|
||||||
|
type Node struct {
|
||||||
|
fullApi api.FullNode
|
||||||
|
minerApi api.StorageMiner
|
||||||
|
stop node.StopFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
type InitialBalanceMsg struct {
|
||||||
|
Addr address.Address
|
||||||
|
Balance int
|
||||||
|
}
|
||||||
|
|
||||||
|
type PresealMsg struct {
|
||||||
|
Miner genesis.Miner
|
||||||
|
}
|
||||||
|
|
||||||
|
type GenesisMsg struct {
|
||||||
|
Genesis []byte
|
||||||
|
Bootstrapper []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
logging.SetLogLevel("vm", "WARN")
|
||||||
|
|
||||||
|
build.DisableBuiltinAssets = true
|
||||||
|
|
||||||
|
// Note: I don't understand the significance of this, but the node test does it.
|
||||||
|
power.ConsensusMinerMinPower = big.NewInt(2048)
|
||||||
|
saminer.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{
|
||||||
|
abi.RegisteredSealProof_StackedDrg2KiBV1: {},
|
||||||
|
}
|
||||||
|
verifreg.MinVerifiedDealSize = big.NewInt(256)
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareBootstrapper(t *TestEnvironment) (*Node, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), PrepareNodeTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
clients := t.IntParam("clients")
|
||||||
|
miners := t.IntParam("miners")
|
||||||
|
nodes := clients + miners
|
||||||
|
|
||||||
|
// the first duty of the boostrapper is to construct the genesis block
|
||||||
|
// first collect all client and miner balances to assign initial funds
|
||||||
|
balanceMsgs := make([]*InitialBalanceMsg, 0, nodes)
|
||||||
|
balanceCh := make(chan *InitialBalanceMsg)
|
||||||
|
|
||||||
|
t.SyncClient.MustSubscribe(ctx, balanceTopic, balanceCh)
|
||||||
|
for i := 0; i < nodes; i++ {
|
||||||
|
m := <-balanceCh
|
||||||
|
balanceMsgs = append(balanceMsgs, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// then collect all preseals from miners
|
||||||
|
presealMsgs := make([]*PresealMsg, 0, miners)
|
||||||
|
presealCh := make(chan *PresealMsg)
|
||||||
|
|
||||||
|
t.SyncClient.MustSubscribe(ctx, presealTopic, presealCh)
|
||||||
|
for i := 0; i < miners; i++ {
|
||||||
|
m := <-presealCh
|
||||||
|
presealMsgs = append(presealMsgs, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// now construct the genesis block
|
||||||
|
var genesisActors []genesis.Actor
|
||||||
|
var genesisMiners []genesis.Miner
|
||||||
|
|
||||||
|
for _, bm := range balanceMsgs {
|
||||||
|
genesisActors = append(genesisActors,
|
||||||
|
genesis.Actor{
|
||||||
|
Type: genesis.TAccount,
|
||||||
|
Balance: big.Mul(big.NewInt(int64(bm.Balance)), types.NewInt(build.FilecoinPrecision)),
|
||||||
|
Meta: (&genesis.AccountMeta{Owner: bm.Addr}).ActorMeta(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pm := range presealMsgs {
|
||||||
|
genesisMiners = append(genesisMiners, pm.Miner)
|
||||||
|
}
|
||||||
|
|
||||||
|
genesisTemplate := genesis.Template{
|
||||||
|
Accounts: genesisActors,
|
||||||
|
Miners: genesisMiners,
|
||||||
|
Timestamp: uint64(time.Now().Unix() - 1000), // this needs to be in the past
|
||||||
|
}
|
||||||
|
|
||||||
|
// dump the genesis block
|
||||||
|
// var jsonBuf bytes.Buffer
|
||||||
|
// jsonEnc := json.NewEncoder(&jsonBuf)
|
||||||
|
// err := jsonEnc.Encode(genesisTemplate)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// runenv.RecordMessage(fmt.Sprintf("Genesis template: %s", string(jsonBuf.Bytes())))
|
||||||
|
|
||||||
|
// this is horrendously disgusting, we use this contraption to side effect the construction
|
||||||
|
// of the genesis block in the buffer -- yes, a side effect of dependency injection.
|
||||||
|
// I remember when software was straightforward...
|
||||||
|
var genesisBuffer bytes.Buffer
|
||||||
|
|
||||||
|
bootstrapperIP := t.NetClient.MustGetDataNetworkIP().String()
|
||||||
|
|
||||||
|
n := &Node{}
|
||||||
|
stop, err := node.New(context.Background(),
|
||||||
|
node.FullAPI(&n.fullApi),
|
||||||
|
node.Online(),
|
||||||
|
node.Repo(repo.NewMemory(nil)),
|
||||||
|
node.Override(new(modules.Genesis), modtest.MakeGenesisMem(&genesisBuffer, genesisTemplate)),
|
||||||
|
withListenAddress(bootstrapperIP),
|
||||||
|
withBootstrapper(nil),
|
||||||
|
withPubsubConfig(true),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
n.stop = stop
|
||||||
|
|
||||||
|
// this dance to construct the bootstrapper multiaddr is quite vexing.
|
||||||
|
var bootstrapperAddr ma.Multiaddr
|
||||||
|
|
||||||
|
bootstrapperAddrs, err := n.fullApi.NetAddrsListen(ctx)
|
||||||
|
if err != nil {
|
||||||
|
stop(context.TODO())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, a := range bootstrapperAddrs.Addrs {
|
||||||
|
ip, err := a.ValueForProtocol(ma.P_IP4)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ip != bootstrapperIP {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
addrs, err := peer.AddrInfoToP2pAddrs(&peer.AddrInfo{
|
||||||
|
ID: bootstrapperAddrs.ID,
|
||||||
|
Addrs: []ma.Multiaddr{a},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
bootstrapperAddr = addrs[0]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if bootstrapperAddr == nil {
|
||||||
|
panic("failed to determine bootstrapper address")
|
||||||
|
}
|
||||||
|
|
||||||
|
genesisMsg := &GenesisMsg{
|
||||||
|
Genesis: genesisBuffer.Bytes(),
|
||||||
|
Bootstrapper: bootstrapperAddr.Bytes(),
|
||||||
|
}
|
||||||
|
t.SyncClient.MustPublish(ctx, genesisTopic, genesisMsg)
|
||||||
|
|
||||||
|
// we are ready; wait for all nodes to be ready
|
||||||
|
t.SyncClient.MustBarrier(ctx, stateReady, t.TestInstanceCount)
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareMiner(t *TestEnvironment) (*Node, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), PrepareNodeTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// first create a wallet
|
||||||
|
walletKey, err := wallet.GenerateKey(crypto.SigTypeBLS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// publish the account ID/balance
|
||||||
|
balance := t.IntParam("balance")
|
||||||
|
balanceMsg := &InitialBalanceMsg{Addr: walletKey.Address, Balance: balance}
|
||||||
|
t.SyncClient.Publish(ctx, balanceTopic, balanceMsg)
|
||||||
|
|
||||||
|
// create and publish the preseal commitment
|
||||||
|
priv, _, err := libp2p_crypto.GenerateEd25519Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
minerID, err := peer.IDFromPrivateKey(priv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
minerAddr, err := address.NewIDAddress(genesis_chain.MinerStart + uint64(t.GroupSeq-1))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
presealDir, err := ioutil.TempDir("", "preseal")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sectors := t.IntParam("sectors")
|
||||||
|
genMiner, _, err := seed.PreSeal(minerAddr, abi.RegisteredSealProof_StackedDrg2KiBV1, 0, sectors, presealDir, []byte("TODO: randomize this"), &walletKey.KeyInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
genMiner.PeerId = minerID
|
||||||
|
|
||||||
|
t.RecordMessage("Miner Info: Owner: %s Worker: %s", genMiner.Owner, genMiner.Worker)
|
||||||
|
|
||||||
|
presealMsg := &PresealMsg{Miner: *genMiner}
|
||||||
|
t.SyncClient.Publish(ctx, presealTopic, presealMsg)
|
||||||
|
|
||||||
|
// then collect the genesis block and bootstrapper address
|
||||||
|
genesisCh := make(chan *GenesisMsg)
|
||||||
|
t.SyncClient.MustSubscribe(ctx, genesisTopic, genesisCh)
|
||||||
|
genesisMsg := <-genesisCh
|
||||||
|
|
||||||
|
// prepare the repo
|
||||||
|
minerRepo := repo.NewMemory(nil)
|
||||||
|
|
||||||
|
// V00D00 People DaNC3!
|
||||||
|
lr, err := minerRepo.Lock(repo.StorageMiner)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ks, err := lr.KeyStore()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
kbytes, err := priv.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ks.Put("libp2p-host", types.KeyInfo{
|
||||||
|
Type: "libp2p-host",
|
||||||
|
PrivateKey: kbytes,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ds, err := lr.Datastore("/metadata")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ds.Put(datastore.NewKey("miner-address"), minerAddr.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nic := storedcounter.New(ds, datastore.NewKey(modules.StorageCounterDSPrefix))
|
||||||
|
for i := 0; i < (sectors + 1); i++ {
|
||||||
|
_, err = nic.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = lr.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
minerIP := t.NetClient.MustGetDataNetworkIP().String()
|
||||||
|
|
||||||
|
// create the node
|
||||||
|
// we need both a full node _and_ and storage miner node
|
||||||
|
n := &Node{}
|
||||||
|
|
||||||
|
stop1, err := node.New(context.Background(),
|
||||||
|
node.FullAPI(&n.fullApi),
|
||||||
|
node.Online(),
|
||||||
|
node.Repo(repo.NewMemory(nil)),
|
||||||
|
withGenesis(genesisMsg.Genesis),
|
||||||
|
withListenAddress(minerIP),
|
||||||
|
withBootstrapper(genesisMsg.Bootstrapper),
|
||||||
|
withPubsubConfig(false),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the wallet
|
||||||
|
err = n.setWallet(ctx, walletKey)
|
||||||
|
if err != nil {
|
||||||
|
stop1(context.TODO())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stop2, err := node.New(context.Background(),
|
||||||
|
node.StorageMiner(&n.minerApi),
|
||||||
|
node.Online(),
|
||||||
|
node.Repo(minerRepo),
|
||||||
|
node.Override(new(api.FullNode), n.fullApi),
|
||||||
|
withMinerListenAddress(minerIP),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
stop1(context.TODO())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
n.stop = func(ctx context.Context) error {
|
||||||
|
// TODO use a multierror for this
|
||||||
|
err2 := stop2(ctx)
|
||||||
|
err1 := stop1(ctx)
|
||||||
|
if err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
|
||||||
|
// add local storage for presealed sectors
|
||||||
|
err = n.minerApi.StorageAddLocal(ctx, presealDir)
|
||||||
|
if err != nil {
|
||||||
|
n.stop(context.TODO())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the miner PeerID
|
||||||
|
minerIDEncoded, err := actors.SerializeParams(&saminer.ChangePeerIDParams{NewID: abi.PeerID(minerID)})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
changeMinerID := &types.Message{
|
||||||
|
To: minerAddr,
|
||||||
|
From: genMiner.Worker,
|
||||||
|
Method: builtin.MethodsMiner.ChangePeerID,
|
||||||
|
Params: minerIDEncoded,
|
||||||
|
Value: types.NewInt(0),
|
||||||
|
GasPrice: types.NewInt(0),
|
||||||
|
GasLimit: 1000000,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = n.fullApi.MpoolPushMessage(ctx, changeMinerID)
|
||||||
|
if err != nil {
|
||||||
|
n.stop(context.TODO())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// we are ready; wait for all nodes to be ready
|
||||||
|
t.RecordMessage("waiting for all nodes to be ready")
|
||||||
|
t.SyncClient.MustBarrier(ctx, stateReady, t.TestInstanceCount)
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareClient(t *TestEnvironment) (*Node, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), PrepareNodeTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// first create a wallet
|
||||||
|
walletKey, err := wallet.GenerateKey(crypto.SigTypeBLS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// publish the account ID/balance
|
||||||
|
balance := t.IntParam("balance")
|
||||||
|
balanceMsg := &InitialBalanceMsg{Addr: walletKey.Address, Balance: balance}
|
||||||
|
t.SyncClient.Publish(ctx, balanceTopic, balanceMsg)
|
||||||
|
|
||||||
|
// then collect the genesis block and bootstrapper address
|
||||||
|
genesisCh := make(chan *GenesisMsg)
|
||||||
|
t.SyncClient.MustSubscribe(ctx, genesisTopic, genesisCh)
|
||||||
|
genesisMsg := <-genesisCh
|
||||||
|
|
||||||
|
clientIP := t.NetClient.MustGetDataNetworkIP().String()
|
||||||
|
|
||||||
|
// create the node
|
||||||
|
n := &Node{}
|
||||||
|
stop, err := node.New(context.Background(),
|
||||||
|
node.FullAPI(&n.fullApi),
|
||||||
|
node.Online(),
|
||||||
|
node.Repo(repo.NewMemory(nil)),
|
||||||
|
withGenesis(genesisMsg.Genesis),
|
||||||
|
withListenAddress(clientIP),
|
||||||
|
withBootstrapper(genesisMsg.Bootstrapper),
|
||||||
|
withPubsubConfig(false),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
n.stop = stop
|
||||||
|
|
||||||
|
// set the wallet
|
||||||
|
err = n.setWallet(ctx, walletKey)
|
||||||
|
if err != nil {
|
||||||
|
stop(context.TODO())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.RecordMessage("waiting for all nodes to be ready")
|
||||||
|
// we are ready; wait for all nodes to be ready
|
||||||
|
t.SyncClient.MustBarrier(ctx, stateReady, t.TestInstanceCount)
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) setWallet(ctx context.Context, walletKey *wallet.Key) error {
|
||||||
|
_, err := n.fullApi.WalletImport(ctx, &walletKey.KeyInfo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = n.fullApi.WalletSetDefault(ctx, walletKey.Address)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func withGenesis(gb []byte) node.Option {
|
||||||
|
return node.Override(new(modules.Genesis), modules.LoadGenesis(gb))
|
||||||
|
}
|
||||||
|
|
||||||
|
func withBootstrapper(ab []byte) node.Option {
|
||||||
|
return node.Override(new(dtypes.BootstrapPeers),
|
||||||
|
func() (dtypes.BootstrapPeers, error) {
|
||||||
|
if ab == nil {
|
||||||
|
return dtypes.BootstrapPeers{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
a, err := ma.NewMultiaddrBytes(ab)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ai, err := peer.AddrInfoFromP2pAddr(a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dtypes.BootstrapPeers{*ai}, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func withPubsubConfig(bootstrapper bool) node.Option {
|
||||||
|
return node.Override(new(*config.Pubsub), func() *config.Pubsub {
|
||||||
|
return &config.Pubsub{
|
||||||
|
Bootstrapper: bootstrapper,
|
||||||
|
RemoteTracer: "",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func withListenAddress(ip string) node.Option {
|
||||||
|
addrs := []string{fmt.Sprintf("/ip4/%s/tcp/4001", ip)}
|
||||||
|
return node.Override(node.StartListeningKey, lp2p.StartListening(addrs))
|
||||||
|
}
|
||||||
|
|
||||||
|
func withMinerListenAddress(ip string) node.Option {
|
||||||
|
addrs := []string{fmt.Sprintf("/ip4/%s/tcp/4002", ip)}
|
||||||
|
return node.Override(node.StartListeningKey, lp2p.StartListening(addrs))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user