Merge pull request #6311 from filecoin-project/raulk/itests
consolidate integration tests into `itests` package; create test kit; cleanup
This commit is contained in:
+6
-4
@@ -5,7 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/big"
|
||||
"github.com/ipfs/go-cid"
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/libp2p/go-libp2p-core/host"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
protocol "github.com/libp2p/go-libp2p-core/protocol"
|
||||
"github.com/libp2p/go-libp2p-core/protocol"
|
||||
|
||||
cborutil "github.com/filecoin-project/go-cbor-util"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
"github.com/filecoin-project/lotus/lib/peermgr"
|
||||
)
|
||||
|
||||
// TODO(TEST): missing test coverage.
|
||||
|
||||
const ProtocolID = "/fil/hello/1.0.0"
|
||||
|
||||
var log = logging.Logger("hello")
|
||||
@@ -33,12 +35,14 @@ type HelloMessage struct {
|
||||
HeaviestTipSetWeight big.Int
|
||||
GenesisHash cid.Cid
|
||||
}
|
||||
|
||||
type LatencyMessage struct {
|
||||
TArrival int64
|
||||
TSent int64
|
||||
}
|
||||
|
||||
type NewStreamFunc func(context.Context, peer.ID, ...protocol.ID) (inet.Stream, error)
|
||||
|
||||
type Service struct {
|
||||
h host.Host
|
||||
|
||||
@@ -62,7 +66,6 @@ func NewHelloService(h host.Host, cs *store.ChainStore, syncer *chain.Syncer, pm
|
||||
}
|
||||
|
||||
func (hs *Service) HandleStream(s inet.Stream) {
|
||||
|
||||
var hmsg HelloMessage
|
||||
if err := cborutil.ReadCborRPC(s, &hmsg); err != nil {
|
||||
log.Infow("failed to read hello message, disconnecting", "error", err)
|
||||
@@ -121,7 +124,6 @@ func (hs *Service) HandleStream(s inet.Stream) {
|
||||
log.Debugf("Got new tipset through Hello: %s from %s", ts.Cids(), s.Conn().RemotePeer())
|
||||
hs.syncer.InformNewHead(s.Conn().RemotePeer(), ts)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (hs *Service) SayHello(ctx context.Context, pid peer.ID) error {
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
package node_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/lotus/api/test"
|
||||
"github.com/filecoin-project/lotus/chain/actors/policy"
|
||||
"github.com/filecoin-project/lotus/lib/lotuslog"
|
||||
builder "github.com/filecoin-project/lotus/node/test"
|
||||
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))
|
||||
}
|
||||
|
||||
func TestAPI(t *testing.T) {
|
||||
test.TestApis(t, builder.Builder)
|
||||
}
|
||||
|
||||
func TestAPIRPC(t *testing.T) {
|
||||
test.TestApis(t, builder.RPCBuilder)
|
||||
}
|
||||
|
||||
func TestAPIDealFlow(t *testing.T) {
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
blockTime := 10 * time.Millisecond
|
||||
|
||||
// For these tests where the block time is artificially short, just use
|
||||
// a deal start epoch that is guaranteed to be far enough in the future
|
||||
// so that the deal starts sealing in time
|
||||
dealStartEpoch := abi.ChainEpoch(2 << 12)
|
||||
|
||||
t.Run("TestDealFlow", func(t *testing.T) {
|
||||
test.TestDealFlow(t, builder.MockSbBuilder, blockTime, false, false, dealStartEpoch)
|
||||
})
|
||||
t.Run("WithExportedCAR", func(t *testing.T) {
|
||||
test.TestDealFlow(t, builder.MockSbBuilder, blockTime, true, false, dealStartEpoch)
|
||||
})
|
||||
t.Run("TestDoubleDealFlow", func(t *testing.T) {
|
||||
test.TestDoubleDealFlow(t, builder.MockSbBuilder, blockTime, dealStartEpoch)
|
||||
})
|
||||
t.Run("TestFastRetrievalDealFlow", func(t *testing.T) {
|
||||
test.TestFastRetrievalDealFlow(t, builder.MockSbBuilder, blockTime, dealStartEpoch)
|
||||
})
|
||||
t.Run("TestPublishDealsBatching", func(t *testing.T) {
|
||||
test.TestPublishDealsBatching(t, builder.MockSbBuilder, blockTime, dealStartEpoch)
|
||||
})
|
||||
t.Run("TestOfflineDealFlow", func(t *testing.T) {
|
||||
test.TestOfflineDealFlow(t, builder.MockSbBuilder, blockTime, dealStartEpoch, false)
|
||||
})
|
||||
t.Run("TestOfflineDealFlowFastRetrieval", func(t *testing.T) {
|
||||
test.TestOfflineDealFlow(t, builder.MockSbBuilder, blockTime, dealStartEpoch, true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBatchDealInput(t *testing.T) {
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
logging.SetLogLevel("sectors", "DEBUG")
|
||||
|
||||
blockTime := 10 * time.Millisecond
|
||||
|
||||
// For these tests where the block time is artificially short, just use
|
||||
// a deal start epoch that is guaranteed to be far enough in the future
|
||||
// so that the deal starts sealing in time
|
||||
dealStartEpoch := abi.ChainEpoch(2 << 12)
|
||||
|
||||
test.TestBatchDealInput(t, builder.MockSbBuilder, blockTime, dealStartEpoch)
|
||||
}
|
||||
|
||||
func TestAPIDealFlowReal(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode")
|
||||
}
|
||||
lotuslog.SetupLogLevels()
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
// TODO: just set this globally?
|
||||
oldDelay := policy.GetPreCommitChallengeDelay()
|
||||
policy.SetPreCommitChallengeDelay(5)
|
||||
t.Cleanup(func() {
|
||||
policy.SetPreCommitChallengeDelay(oldDelay)
|
||||
})
|
||||
|
||||
t.Run("basic", func(t *testing.T) {
|
||||
test.TestDealFlow(t, builder.Builder, time.Second, false, false, 0)
|
||||
})
|
||||
|
||||
t.Run("fast-retrieval", func(t *testing.T) {
|
||||
test.TestDealFlow(t, builder.Builder, time.Second, false, true, 0)
|
||||
})
|
||||
|
||||
t.Run("retrieval-second", func(t *testing.T) {
|
||||
test.TestSecondDealRetrieval(t, builder.Builder, time.Second)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDealMining(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode")
|
||||
}
|
||||
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
test.TestDealMining(t, builder.MockSbBuilder, 50*time.Millisecond, false)
|
||||
}
|
||||
|
||||
func TestSDRUpgrade(t *testing.T) {
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
oldDelay := policy.GetPreCommitChallengeDelay()
|
||||
policy.SetPreCommitChallengeDelay(5)
|
||||
t.Cleanup(func() {
|
||||
policy.SetPreCommitChallengeDelay(oldDelay)
|
||||
})
|
||||
|
||||
test.TestSDRUpgrade(t, builder.MockSbBuilder, 50*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestPledgeSectors(t *testing.T) {
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
t.Run("1", func(t *testing.T) {
|
||||
test.TestPledgeSector(t, builder.MockSbBuilder, 50*time.Millisecond, 1)
|
||||
})
|
||||
|
||||
t.Run("100", func(t *testing.T) {
|
||||
test.TestPledgeSector(t, builder.MockSbBuilder, 50*time.Millisecond, 100)
|
||||
})
|
||||
|
||||
t.Run("1000", func(t *testing.T) {
|
||||
if testing.Short() { // takes ~16s
|
||||
t.Skip("skipping test in short mode")
|
||||
}
|
||||
|
||||
test.TestPledgeSector(t, builder.MockSbBuilder, 50*time.Millisecond, 1000)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPledgeBatching(t *testing.T) {
|
||||
t.Run("100", func(t *testing.T) {
|
||||
test.TestPledgeBatching(t, builder.MockSbBuilder, 50*time.Millisecond, 100)
|
||||
})
|
||||
t.Run("100-before-nv13", func(t *testing.T) {
|
||||
test.TestPledgeBeforeNv13(t, builder.MockSbBuilder, 50*time.Millisecond, 100)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTapeFix(t *testing.T) {
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
test.TestTapeFix(t, builder.MockSbBuilder, 2*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestWindowedPost(t *testing.T) {
|
||||
if os.Getenv("LOTUS_TEST_WINDOW_POST") != "1" {
|
||||
t.Skip("this takes a few minutes, set LOTUS_TEST_WINDOW_POST=1 to run")
|
||||
}
|
||||
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("gen", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
test.TestWindowPost(t, builder.MockSbBuilder, 2*time.Millisecond, 10)
|
||||
}
|
||||
|
||||
func TestTerminate(t *testing.T) {
|
||||
if os.Getenv("LOTUS_TEST_WINDOW_POST") != "1" {
|
||||
t.Skip("this takes a few minutes, set LOTUS_TEST_WINDOW_POST=1 to run")
|
||||
}
|
||||
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
test.TestTerminate(t, builder.MockSbBuilder, 2*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestCCUpgrade(t *testing.T) {
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
test.TestCCUpgrade(t, builder.MockSbBuilder, 5*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestPaymentChannels(t *testing.T) {
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("pubsub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
test.TestPaymentChannels(t, builder.MockSbBuilder, 5*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestWindowPostDispute(t *testing.T) {
|
||||
if os.Getenv("LOTUS_TEST_WINDOW_POST") != "1" {
|
||||
t.Skip("this takes a few minutes, set LOTUS_TEST_WINDOW_POST=1 to run")
|
||||
}
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("gen", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
test.TestWindowPostDispute(t, builder.MockSbBuilder, 2*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestWindowPostDisputeFails(t *testing.T) {
|
||||
if os.Getenv("LOTUS_TEST_WINDOW_POST") != "1" {
|
||||
t.Skip("this takes a few minutes, set LOTUS_TEST_WINDOW_POST=1 to run")
|
||||
}
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("gen", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
test.TestWindowPostDisputeFails(t, builder.MockSbBuilder, 2*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestWindowPostBaseFeeNoBurn(t *testing.T) {
|
||||
if os.Getenv("LOTUS_TEST_WINDOW_POST") != "1" {
|
||||
t.Skip("this takes a few minutes, set LOTUS_TEST_WINDOW_POST=1 to run")
|
||||
}
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("gen", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
test.TestWindowPostBaseFeeNoBurn(t, builder.MockSbBuilder, 2*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestWindowPostBaseFeeBurn(t *testing.T) {
|
||||
if os.Getenv("LOTUS_TEST_WINDOW_POST") != "1" {
|
||||
t.Skip("this takes a few minutes, set LOTUS_TEST_WINDOW_POST=1 to run")
|
||||
}
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("gen", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
test.TestWindowPostBaseFeeBurn(t, builder.MockSbBuilder, 2*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestDeadlineToggling(t *testing.T) {
|
||||
if os.Getenv("LOTUS_TEST_DEADLINE_TOGGLING") != "1" {
|
||||
t.Skip("this takes a few minutes, set LOTUS_TEST_DEADLINE_TOGGLING=1 to run")
|
||||
}
|
||||
logging.SetLogLevel("miner", "ERROR")
|
||||
logging.SetLogLevel("gen", "ERROR")
|
||||
logging.SetLogLevel("chainstore", "ERROR")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "FATAL")
|
||||
|
||||
test.TestDeadlineToggling(t, builder.MockSbBuilder, 2*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestVerifiedClientTopUp(t *testing.T) {
|
||||
logging.SetLogLevel("storageminer", "FATAL")
|
||||
logging.SetLogLevel("chain", "ERROR")
|
||||
test.AddVerifiedClient(t, builder.MockSbBuilder)
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
"go.opencensus.io/tag"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/api/v0api"
|
||||
"github.com/filecoin-project/lotus/api/v1api"
|
||||
"github.com/filecoin-project/lotus/metrics"
|
||||
"github.com/filecoin-project/lotus/node/impl"
|
||||
)
|
||||
|
||||
var rpclog = logging.Logger("rpc")
|
||||
|
||||
// ServeRPC serves an HTTP handler over the supplied listen multiaddr.
|
||||
//
|
||||
// This function spawns a goroutine to run the server, and returns immediately.
|
||||
// It returns the stop function to be called to terminate the endpoint.
|
||||
//
|
||||
// The supplied ID is used in tracing, by inserting a tag in the context.
|
||||
func ServeRPC(h http.Handler, id string, addr multiaddr.Multiaddr) (StopFunc, error) {
|
||||
// Start listening to the addr; if invalid or occupied, we will fail early.
|
||||
lst, err := manet.Listen(addr)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("could not listen: %w", err)
|
||||
}
|
||||
|
||||
// Instantiate the server and start listening.
|
||||
srv := &http.Server{
|
||||
Handler: h,
|
||||
BaseContext: func(listener net.Listener) context.Context {
|
||||
ctx, _ := tag.New(context.Background(), tag.Upsert(metrics.APIInterface, id))
|
||||
return ctx
|
||||
},
|
||||
}
|
||||
|
||||
go func() {
|
||||
err = srv.Serve(manet.NetListener(lst))
|
||||
if err != http.ErrServerClosed {
|
||||
rpclog.Warnf("rpc server failed: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return srv.Shutdown, err
|
||||
}
|
||||
|
||||
// FullNodeHandler returns a full node handler, to be mounted as-is on the server.
|
||||
func FullNodeHandler(a v1api.FullNode, permissioned bool, opts ...jsonrpc.ServerOption) (http.Handler, error) {
|
||||
m := mux.NewRouter()
|
||||
|
||||
serveRpc := func(path string, hnd interface{}) {
|
||||
rpcServer := jsonrpc.NewServer(opts...)
|
||||
rpcServer.Register("Filecoin", hnd)
|
||||
|
||||
var handler http.Handler = rpcServer
|
||||
if permissioned {
|
||||
handler = &auth.Handler{Verify: a.AuthVerify, Next: rpcServer.ServeHTTP}
|
||||
}
|
||||
|
||||
m.Handle(path, handler)
|
||||
}
|
||||
|
||||
fnapi := metrics.MetricedFullAPI(a)
|
||||
if permissioned {
|
||||
fnapi = api.PermissionedFullAPI(fnapi)
|
||||
}
|
||||
|
||||
serveRpc("/rpc/v1", fnapi)
|
||||
serveRpc("/rpc/v0", &v0api.WrapperV1Full{FullNode: fnapi})
|
||||
|
||||
// Import handler
|
||||
handleImportFunc := handleImport(a.(*impl.FullNodeAPI))
|
||||
if permissioned {
|
||||
importAH := &auth.Handler{
|
||||
Verify: a.AuthVerify,
|
||||
Next: handleImportFunc,
|
||||
}
|
||||
m.Handle("/rest/v0/import", importAH)
|
||||
} else {
|
||||
m.HandleFunc("/rest/v0/import", handleImportFunc)
|
||||
}
|
||||
|
||||
// debugging
|
||||
m.Handle("/debug/metrics", metrics.Exporter())
|
||||
m.Handle("/debug/pprof-set/block", handleFractionOpt("BlockProfileRate", runtime.SetBlockProfileRate))
|
||||
m.Handle("/debug/pprof-set/mutex", handleFractionOpt("MutexProfileFraction", func(x int) {
|
||||
runtime.SetMutexProfileFraction(x)
|
||||
}))
|
||||
m.PathPrefix("/").Handler(http.DefaultServeMux) // pprof
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// MinerHandler returns a miner handler, to be mounted as-is on the server.
|
||||
func MinerHandler(a api.StorageMiner, permissioned bool) (http.Handler, error) {
|
||||
m := mux.NewRouter()
|
||||
|
||||
mapi := metrics.MetricedStorMinerAPI(a)
|
||||
if permissioned {
|
||||
mapi = api.PermissionedStorMinerAPI(mapi)
|
||||
}
|
||||
|
||||
rpcServer := jsonrpc.NewServer()
|
||||
rpcServer.Register("Filecoin", mapi)
|
||||
|
||||
m.Handle("/rpc/v0", rpcServer)
|
||||
m.PathPrefix("/remote").HandlerFunc(a.(*impl.StorageMinerAPI).ServeRemote)
|
||||
|
||||
// debugging
|
||||
m.Handle("/debug/metrics", metrics.Exporter())
|
||||
m.PathPrefix("/").Handler(http.DefaultServeMux) // pprof
|
||||
|
||||
if !permissioned {
|
||||
return rpcServer, nil
|
||||
}
|
||||
|
||||
ah := &auth.Handler{
|
||||
Verify: a.AuthVerify,
|
||||
Next: m.ServeHTTP,
|
||||
}
|
||||
return ah, nil
|
||||
}
|
||||
|
||||
func handleImport(a *impl.FullNodeAPI) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "PUT" {
|
||||
w.WriteHeader(404)
|
||||
return
|
||||
}
|
||||
if !auth.HasPerm(r.Context(), nil, api.PermWrite) {
|
||||
w.WriteHeader(401)
|
||||
_ = json.NewEncoder(w).Encode(struct{ Error string }{"unauthorized: missing write permission"})
|
||||
return
|
||||
}
|
||||
|
||||
c, err := a.ClientImportLocal(r.Context(), r.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
_ = json.NewEncoder(w).Encode(struct{ Error string }{err.Error()})
|
||||
return
|
||||
}
|
||||
w.WriteHeader(200)
|
||||
err = json.NewEncoder(w).Encode(struct{ Cid cid.Cid }{c})
|
||||
if err != nil {
|
||||
rpclog.Errorf("/rest/v0/import: Writing response failed: %+v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleFractionOpt(name string, setter func(int)) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(rw, "only POST allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
asfr := r.Form.Get("x")
|
||||
if len(asfr) == 0 {
|
||||
http.Error(rw, "parameter 'x' must be set", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
fr, err := strconv.Atoi(asfr)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
rpclog.Infof("setting %s to %d", name, fr)
|
||||
setter(fr)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type ShutdownHandler struct {
|
||||
Component string
|
||||
StopFunc StopFunc
|
||||
}
|
||||
|
||||
// MonitorShutdown manages shutdown requests, by watching signals and invoking
|
||||
// the supplied handlers in order.
|
||||
//
|
||||
// It watches SIGTERM and SIGINT OS signals, as well as the trigger channel.
|
||||
// When any of them fire, it calls the supplied handlers in order. If any of
|
||||
// them errors, it merely logs the error.
|
||||
//
|
||||
// Once the shutdown has completed, it closes the returned channel. The caller
|
||||
// can watch this channel
|
||||
func MonitorShutdown(triggerCh <-chan struct{}, handlers ...ShutdownHandler) <-chan struct{} {
|
||||
sigCh := make(chan os.Signal, 2)
|
||||
out := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case sig := <-sigCh:
|
||||
log.Warnw("received shutdown", "signal", sig)
|
||||
case <-triggerCh:
|
||||
log.Warn("received shutdown")
|
||||
}
|
||||
|
||||
log.Warn("Shutting down...")
|
||||
|
||||
// Call all the handlers, logging on failure and success.
|
||||
for _, h := range handlers {
|
||||
if err := h.StopFunc(context.TODO()); err != nil {
|
||||
log.Errorf("shutting down %s failed: %s", h.Component, err)
|
||||
continue
|
||||
}
|
||||
log.Infof("%s shut down successfully ", h.Component)
|
||||
}
|
||||
|
||||
log.Warn("Graceful shutdown successful")
|
||||
|
||||
// Sync all loggers.
|
||||
_ = log.Sync() //nolint:errcheck
|
||||
close(out)
|
||||
}()
|
||||
|
||||
signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT)
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMonitorShutdown(t *testing.T) {
|
||||
signalCh := make(chan struct{})
|
||||
|
||||
// Three shutdown handlers.
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(3)
|
||||
h := ShutdownHandler{
|
||||
Component: "handler",
|
||||
StopFunc: func(_ context.Context) error {
|
||||
wg.Done()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
finishCh := MonitorShutdown(signalCh, h, h, h)
|
||||
|
||||
// Nothing here after 10ms.
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
require.Len(t, finishCh, 0)
|
||||
|
||||
// Now trigger the shutdown.
|
||||
close(signalCh)
|
||||
wg.Wait()
|
||||
<-finishCh
|
||||
}
|
||||
@@ -1,690 +0,0 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/network"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
"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/test"
|
||||
"github.com/filecoin-project/lotus/api/v0api"
|
||||
"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"
|
||||
"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 test.TestNode, mn mocknet.Mocknet, opts node.Option) test.TestStorageNode {
|
||||
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 < test.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 test.TestStorageNode{StorageMiner: minerapi, MineOne: mineOne, Stop: stop}
|
||||
}
|
||||
|
||||
func storageBuilder(parentNode test.TestNode, mn mocknet.Mocknet, opts node.Option) test.StorageBuilder {
|
||||
return func(ctx context.Context, t *testing.T, spt abi.RegisteredSealProof, owner address.Address) test.TestStorageNode {
|
||||
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 []test.FullNodeOpts, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) {
|
||||
return mockBuilderOpts(t, fullOpts, storage, false)
|
||||
}
|
||||
|
||||
func MockSbBuilder(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) {
|
||||
return mockSbBuilderOpts(t, fullOpts, storage, false)
|
||||
}
|
||||
|
||||
func RPCBuilder(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) {
|
||||
return mockBuilderOpts(t, fullOpts, storage, true)
|
||||
}
|
||||
|
||||
func RPCMockSbBuilder(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) {
|
||||
return mockSbBuilderOpts(t, fullOpts, storage, true)
|
||||
}
|
||||
|
||||
func mockBuilderOpts(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner, rpc bool) ([]test.TestNode, []test.TestStorageNode) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
|
||||
mn := mocknet.New(ctx)
|
||||
|
||||
fulls := make([]test.TestNode, len(fullOpts))
|
||||
storers := make([]test.TestStorageNode, 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
|
||||
var maddrs []address.Address
|
||||
var genaccs []genesis.Actor
|
||||
var 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, test.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)
|
||||
}
|
||||
|
||||
rkhKey, err := wallet.GenerateKey(types.KTSecp256k1)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
vrk := genesis.Actor{
|
||||
Type: genesis.TAccount,
|
||||
Balance: big.Mul(big.Div(big.NewInt(int64(build.FilBase)), big.NewInt(100)), big.NewInt(int64(build.FilecoinPrecision))),
|
||||
Meta: (&genesis.AccountMeta{Owner: rkhKey.Address}).ActorMeta(),
|
||||
}
|
||||
keys = append(keys, rkhKey)
|
||||
|
||||
templ := &genesis.Template{
|
||||
NetworkVersion: network.Version0,
|
||||
Accounts: genaccs,
|
||||
Miners: genms,
|
||||
NetworkName: "test",
|
||||
Timestamp: uint64(time.Now().Unix() - 10000), // some time sufficiently far in the past
|
||||
VerifregRootKey: vrk,
|
||||
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())
|
||||
}
|
||||
|
||||
if _, err := fulls[0].FullNode.WalletImport(ctx, &rkhKey.KeyInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
storers[i] = CreateTestStorageNode(ctx, t, wa, genMiner, pk, f, mn, opts)
|
||||
if err := storers[i].StorageAddLocal(ctx, presealDirs[i]); err != nil {
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
/*
|
||||
sma := storers[i].StorageMiner.(*impl.StorageMinerAPI)
|
||||
|
||||
psd := presealDirs[i]
|
||||
*/
|
||||
if rpc {
|
||||
storers[i] = storerRpc(t, storers[i])
|
||||
}
|
||||
}
|
||||
|
||||
if err := mn.LinkAll(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(storers) > 0 {
|
||||
// Mine 2 blocks to setup some CE stuff in some actors
|
||||
var wait sync.Mutex
|
||||
wait.Lock()
|
||||
|
||||
test.MineUntilBlock(ctx, t, fulls[0], storers[0], func(epoch abi.ChainEpoch) {
|
||||
wait.Unlock()
|
||||
})
|
||||
|
||||
wait.Lock()
|
||||
test.MineUntilBlock(ctx, t, fulls[0], storers[0], func(epoch abi.ChainEpoch) {
|
||||
wait.Unlock()
|
||||
})
|
||||
wait.Lock()
|
||||
}
|
||||
|
||||
return fulls, storers
|
||||
}
|
||||
|
||||
func mockSbBuilderOpts(t *testing.T, fullOpts []test.FullNodeOpts, storage []test.StorageMiner, rpc bool) ([]test.TestNode, []test.TestStorageNode) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
|
||||
mn := mocknet.New(ctx)
|
||||
|
||||
fulls := make([]test.TestNode, len(fullOpts))
|
||||
storers := make([]test.TestStorageNode, 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
|
||||
var genaccs []genesis.Actor
|
||||
var maddrs []address.Address
|
||||
var keys []*wallet.Key
|
||||
var 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 == test.PresealGenesis {
|
||||
preseals = test.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)
|
||||
}
|
||||
|
||||
rkhKey, err := wallet.GenerateKey(types.KTSecp256k1)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
vrk := genesis.Actor{
|
||||
Type: genesis.TAccount,
|
||||
Balance: big.Mul(big.Div(big.NewInt(int64(build.FilBase)), big.NewInt(100)), big.NewInt(int64(build.FilecoinPrecision))),
|
||||
Meta: (&genesis.AccountMeta{Owner: rkhKey.Address}).ActorMeta(),
|
||||
}
|
||||
keys = append(keys, rkhKey)
|
||||
|
||||
templ := &genesis.Template{
|
||||
NetworkVersion: network.Version0,
|
||||
Accounts: genaccs,
|
||||
Miners: genms,
|
||||
NetworkName: "test",
|
||||
Timestamp: uint64(time.Now().Unix()) - (build.BlockDelaySecs * 20000),
|
||||
VerifregRootKey: vrk,
|
||||
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),
|
||||
node.Override(new(ffiwrapper.Prover), mock.MockProver),
|
||||
|
||||
// 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(*mock.SectorMgr), func() (*mock.SectorMgr, error) {
|
||||
return mock.NewMockSectorMgr(nil), nil
|
||||
}),
|
||||
|
||||
node.Override(new(sectorstorage.SectorManager), node.From(new(*mock.SectorMgr))),
|
||||
node.Override(new(sectorstorage.Unsealer), node.From(new(*mock.SectorMgr))),
|
||||
node.Override(new(sectorstorage.PieceProvider), node.From(new(*mock.SectorMgr))),
|
||||
|
||||
node.Override(new(ffiwrapper.Verifier), mock.MockVerifier),
|
||||
node.Override(new(ffiwrapper.Prover), mock.MockProver),
|
||||
node.Unset(new(*sectorstorage.Manager)),
|
||||
))
|
||||
}
|
||||
|
||||
if _, err := fulls[0].FullNode.WalletImport(ctx, &rkhKey.KeyInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
storers[i] = CreateTestStorageNode(ctx, t, genms[i].Worker, maddrs[i], pidKeys[i], f, mn, node.Options(
|
||||
node.Override(new(*mock.SectorMgr), func() (*mock.SectorMgr, error) {
|
||||
return mock.NewMockSectorMgr(sectors), nil
|
||||
}),
|
||||
|
||||
node.Override(new(sectorstorage.SectorManager), node.From(new(*mock.SectorMgr))),
|
||||
node.Override(new(sectorstorage.Unsealer), node.From(new(*mock.SectorMgr))),
|
||||
node.Override(new(sectorstorage.PieceProvider), node.From(new(*mock.SectorMgr))),
|
||||
|
||||
node.Override(new(ffiwrapper.Verifier), mock.MockVerifier),
|
||||
node.Override(new(ffiwrapper.Prover), mock.MockProver),
|
||||
node.Unset(new(*sectorstorage.Manager)),
|
||||
opts,
|
||||
))
|
||||
|
||||
if rpc {
|
||||
storers[i] = storerRpc(t, storers[i])
|
||||
}
|
||||
}
|
||||
|
||||
if err := mn.LinkAll(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(storers) > 0 {
|
||||
// Mine 2 blocks to setup some CE stuff in some actors
|
||||
var wait sync.Mutex
|
||||
wait.Lock()
|
||||
|
||||
test.MineUntilBlock(ctx, t, fulls[0], storers[0], func(abi.ChainEpoch) {
|
||||
wait.Unlock()
|
||||
})
|
||||
wait.Lock()
|
||||
test.MineUntilBlock(ctx, t, fulls[0], storers[0], func(abi.ChainEpoch) {
|
||||
wait.Unlock()
|
||||
})
|
||||
wait.Lock()
|
||||
}
|
||||
|
||||
return fulls, storers
|
||||
}
|
||||
|
||||
func fullRpc(t *testing.T, nd test.TestNode) test.TestNode {
|
||||
ma, listenAddr, err := CreateRPCServer(t, map[string]interface{}{
|
||||
"/rpc/v1": nd,
|
||||
"/rpc/v0": &v0api.WrapperV1Full{FullNode: nd},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
var stop func()
|
||||
var full test.TestNode
|
||||
full.FullNode, stop, err = client.NewFullNodeRPCV1(context.Background(), listenAddr+"/rpc/v1", nil)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(stop)
|
||||
|
||||
full.ListenAddr = ma
|
||||
return full
|
||||
}
|
||||
|
||||
func storerRpc(t *testing.T, nd test.TestStorageNode) test.TestStorageNode {
|
||||
ma, listenAddr, err := CreateRPCServer(t, map[string]interface{}{
|
||||
"/rpc/v0": nd,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
var stop func()
|
||||
var storer test.TestStorageNode
|
||||
storer.StorageMiner, stop, err = client.NewStorageMinerRPCV0(context.Background(), listenAddr+"/rpc/v0", nil)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(stop)
|
||||
|
||||
storer.ListenAddr = ma
|
||||
storer.MineOne = nd.MineOne
|
||||
return storer
|
||||
}
|
||||
|
||||
func CreateRPCServer(t *testing.T, handlers map[string]interface{}) (multiaddr.Multiaddr, string, error) {
|
||||
m := mux.NewRouter()
|
||||
for path, handler := range handlers {
|
||||
rpcServer := jsonrpc.NewServer()
|
||||
rpcServer.Register("Filecoin", handler)
|
||||
m.Handle(path, rpcServer)
|
||||
}
|
||||
testServ := httptest.NewServer(m) // todo: close
|
||||
t.Cleanup(testServ.Close)
|
||||
t.Cleanup(testServ.CloseClientConnections)
|
||||
|
||||
addr := testServ.Listener.Addr()
|
||||
listenAddr := "ws://" + addr.String()
|
||||
ma, err := parseWSMultiAddr(addr)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return ma, listenAddr, err
|
||||
}
|
||||
|
||||
func parseWSMultiAddr(addr net.Addr) (multiaddr.Multiaddr, error) {
|
||||
host, port, err := net.SplitHostPort(addr.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ma, err := multiaddr.NewMultiaddr("/ip4/" + host + "/" + addr.Network() + "/" + port + "/ws")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ma, nil
|
||||
}
|
||||
|
||||
func WSMultiAddrToString(addr multiaddr.Multiaddr) (string, error) {
|
||||
parts := strings.Split(addr.String(), "/")
|
||||
if len(parts) != 6 || parts[0] != "" {
|
||||
return "", xerrors.Errorf("Malformed ws multiaddr %s", addr)
|
||||
}
|
||||
|
||||
host := parts[2]
|
||||
port := parts[4]
|
||||
proto := parts[5]
|
||||
|
||||
return proto + "://" + host + ":" + port + "/rpc/v0", nil
|
||||
}
|
||||
Reference in New Issue
Block a user