2020-07-01 16:29:09 +00:00
|
|
|
package testkit
|
2020-06-30 22:02:01 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
2020-07-06 14:47:17 +00:00
|
|
|
"fmt"
|
2020-07-07 21:02:29 +00:00
|
|
|
mbig "math/big"
|
2020-06-30 22:02:01 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/lotus/build"
|
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
|
|
"github.com/filecoin-project/lotus/genesis"
|
|
|
|
"github.com/filecoin-project/lotus/node"
|
|
|
|
"github.com/filecoin-project/lotus/node/modules"
|
|
|
|
modtest "github.com/filecoin-project/lotus/node/modules/testing"
|
|
|
|
"github.com/filecoin-project/lotus/node/repo"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
|
|
|
|
|
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
|
|
ma "github.com/multiformats/go-multiaddr"
|
|
|
|
)
|
|
|
|
|
2020-07-01 16:29:09 +00:00
|
|
|
// Bootstrapper is a special kind of process that produces a genesis block with
|
|
|
|
// the initial wallet balances and preseals for all enlisted miners and clients.
|
|
|
|
type Bootstrapper struct {
|
|
|
|
*LotusNode
|
2020-06-30 22:02:01 +00:00
|
|
|
|
2020-07-01 16:29:09 +00:00
|
|
|
t *TestEnvironment
|
2020-06-30 22:02:01 +00:00
|
|
|
}
|
|
|
|
|
2020-07-01 16:29:09 +00:00
|
|
|
func PrepareBootstrapper(t *TestEnvironment) (*Bootstrapper, error) {
|
|
|
|
var (
|
|
|
|
clients = t.IntParam("clients")
|
|
|
|
miners = t.IntParam("miners")
|
|
|
|
nodes = clients + miners
|
|
|
|
)
|
|
|
|
|
2020-06-30 22:02:01 +00:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), PrepareNodeTimeout)
|
|
|
|
defer cancel()
|
|
|
|
|
2020-07-01 16:29:09 +00:00
|
|
|
pubsubTracerMaddr, err := GetPubsubTracerMaddr(ctx, t)
|
2020-06-30 22:02:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-07-01 16:29:09 +00:00
|
|
|
randomBeaconOpt, err := GetRandomBeaconOpts(ctx, t)
|
2020-06-30 22:02:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// the first duty of the boostrapper is to construct the genesis block
|
|
|
|
// first collect all client and miner balances to assign initial funds
|
2020-07-01 16:29:09 +00:00
|
|
|
balances, err := WaitForBalances(t, ctx, nodes)
|
2020-06-30 22:02:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-07-07 12:58:09 +00:00
|
|
|
totalBalance := big.Zero()
|
|
|
|
for _, b := range balances {
|
2020-07-07 21:02:29 +00:00
|
|
|
totalBalance = big.Add(attoFil(b.Balance), totalBalance)
|
2020-07-07 12:58:09 +00:00
|
|
|
}
|
|
|
|
|
2020-07-07 21:02:29 +00:00
|
|
|
t.RecordMessage("TOTAL BALANCE: %s AttoFIL (%f FIL)", totalBalance, fractionalFil(totalBalance))
|
2020-07-07 12:58:09 +00:00
|
|
|
if max := types.TotalFilecoinInt; totalBalance.GreaterThanEqual(max) {
|
|
|
|
panic(fmt.Sprintf("total sum of balances is greater than max Filecoin ever; sum=%s, max=%s", totalBalance, max))
|
|
|
|
}
|
|
|
|
|
2020-06-30 22:02:01 +00:00
|
|
|
// then collect all preseals from miners
|
2020-07-01 16:29:09 +00:00
|
|
|
preseals, err := CollectPreseals(t, ctx, miners)
|
2020-06-30 22:02:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// now construct the genesis block
|
|
|
|
var genesisActors []genesis.Actor
|
|
|
|
var genesisMiners []genesis.Miner
|
|
|
|
|
|
|
|
for _, bm := range balances {
|
2020-07-07 21:02:29 +00:00
|
|
|
balance := attoFil(bm.Balance)
|
2020-07-07 12:58:09 +00:00
|
|
|
t.RecordMessage("balance assigned to actor %s: %s AttoFIL", bm.Addr, balance)
|
2020-06-30 22:02:01 +00:00
|
|
|
genesisActors = append(genesisActors,
|
|
|
|
genesis.Actor{
|
|
|
|
Type: genesis.TAccount,
|
2020-07-07 12:58:09 +00:00
|
|
|
Balance: balance,
|
2020-06-30 22:02:01 +00:00
|
|
|
Meta: (&genesis.AccountMeta{Owner: bm.Addr}).ActorMeta(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, pm := range preseals {
|
|
|
|
genesisMiners = append(genesisMiners, pm.Miner)
|
|
|
|
}
|
|
|
|
|
|
|
|
genesisTemplate := genesis.Template{
|
|
|
|
Accounts: genesisActors,
|
|
|
|
Miners: genesisMiners,
|
|
|
|
Timestamp: uint64(time.Now().Unix()) - uint64(t.IntParam("genesis_timestamp_offset")), // 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()
|
|
|
|
|
2020-07-01 16:29:09 +00:00
|
|
|
n := &LotusNode{}
|
2020-06-30 22:02:01 +00:00
|
|
|
stop, err := node.New(context.Background(),
|
2020-07-01 16:29:09 +00:00
|
|
|
node.FullAPI(&n.FullApi),
|
2020-06-30 22:02:01 +00:00
|
|
|
node.Online(),
|
|
|
|
node.Repo(repo.NewMemory(nil)),
|
|
|
|
node.Override(new(modules.Genesis), modtest.MakeGenesisMem(&genesisBuffer, genesisTemplate)),
|
2020-07-06 14:47:17 +00:00
|
|
|
withApiEndpoint(fmt.Sprintf("/ip4/0.0.0.0/tcp/%s", t.PortNumber("node_rpc", "0"))),
|
2020-06-30 22:02:01 +00:00
|
|
|
withListenAddress(bootstrapperIP),
|
|
|
|
withBootstrapper(nil),
|
2020-07-01 16:29:09 +00:00
|
|
|
withPubsubConfig(true, pubsubTracerMaddr),
|
|
|
|
randomBeaconOpt,
|
2020-06-30 22:02:01 +00:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-07-01 16:29:09 +00:00
|
|
|
n.StopFn = stop
|
2020-06-30 22:02:01 +00:00
|
|
|
|
|
|
|
var bootstrapperAddr ma.Multiaddr
|
|
|
|
|
2020-07-01 16:29:09 +00:00
|
|
|
bootstrapperAddrs, err := n.FullApi.NetAddrsListen(ctx)
|
2020-06-30 22:02:01 +00:00
|
|
|
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(),
|
|
|
|
}
|
2020-07-01 16:29:09 +00:00
|
|
|
t.SyncClient.MustPublish(ctx, GenesisTopic, genesisMsg)
|
2020-06-30 22:02:01 +00:00
|
|
|
|
|
|
|
t.RecordMessage("waiting for all nodes to be ready")
|
2020-07-01 16:29:09 +00:00
|
|
|
t.SyncClient.MustSignalAndWait(ctx, StateReady, t.TestInstanceCount)
|
|
|
|
|
|
|
|
return &Bootstrapper{n, t}, nil
|
|
|
|
}
|
2020-06-30 22:02:01 +00:00
|
|
|
|
2020-07-01 16:29:09 +00:00
|
|
|
// RunDefault runs a default bootstrapper.
|
|
|
|
func (b *Bootstrapper) RunDefault() error {
|
|
|
|
b.t.RecordMessage("running bootstrapper")
|
|
|
|
ctx := context.Background()
|
|
|
|
b.t.SyncClient.MustSignalAndWait(ctx, StateDone, b.t.TestInstanceCount)
|
|
|
|
return nil
|
2020-06-30 22:02:01 +00:00
|
|
|
}
|
2020-07-07 21:02:29 +00:00
|
|
|
|
|
|
|
// attoFil converts a fractional filecoin value into AttoFIL, rounding if necessary
|
|
|
|
func attoFil(f float64) big.Int {
|
|
|
|
a := mbig.NewFloat(f)
|
|
|
|
a.Mul(a, mbig.NewFloat(float64(build.FilecoinPrecision)))
|
|
|
|
i, _ := a.Int(nil)
|
|
|
|
return big.Int{Int: i}
|
|
|
|
}
|
|
|
|
|
|
|
|
// fractionalFil converts from AttoFIL to a fractional Fil value
|
|
|
|
// possibly losing some precision due to floating point gremlins
|
|
|
|
func fractionalFil(atto big.Int) float64 {
|
|
|
|
f := mbig.NewFloat(0)
|
|
|
|
f.SetInt(atto.Int)
|
|
|
|
f.Quo(f, mbig.NewFloat(float64(build.FilecoinPrecision)))
|
|
|
|
val, _ := f.Float64()
|
|
|
|
return val
|
|
|
|
}
|