2020-02-11 20:48:03 +00:00
|
|
|
package genesis
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-08-24 18:33:49 +00:00
|
|
|
"crypto/rand"
|
2020-02-12 00:58:55 +00:00
|
|
|
"encoding/json"
|
2020-08-18 21:30:49 +00:00
|
|
|
"fmt"
|
2020-07-23 02:05:11 +00:00
|
|
|
|
2020-02-11 20:48:03 +00:00
|
|
|
"github.com/ipfs/go-cid"
|
|
|
|
"github.com/ipfs/go-datastore"
|
|
|
|
cbor "github.com/ipfs/go-ipld-cbor"
|
|
|
|
logging "github.com/ipfs/go-log/v2"
|
|
|
|
"golang.org/x/xerrors"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/go-address"
|
2020-09-07 03:49:10 +00:00
|
|
|
"github.com/filecoin-project/go-state-types/abi"
|
2022-09-06 15:49:29 +00:00
|
|
|
actorstypes "github.com/filecoin-project/go-state-types/actors"
|
2020-09-07 03:49:10 +00:00
|
|
|
"github.com/filecoin-project/go-state-types/big"
|
|
|
|
"github.com/filecoin-project/go-state-types/crypto"
|
2022-06-14 15:00:51 +00:00
|
|
|
"github.com/filecoin-project/go-state-types/network"
|
|
|
|
builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
|
|
|
|
verifreg0 "github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
|
|
|
adt0 "github.com/filecoin-project/specs-actors/actors/util/adt"
|
2020-02-11 20:48:03 +00:00
|
|
|
|
2021-01-29 20:01:00 +00:00
|
|
|
bstore "github.com/filecoin-project/lotus/blockstore"
|
2020-08-06 17:09:03 +00:00
|
|
|
"github.com/filecoin-project/lotus/build"
|
2022-06-14 15:00:51 +00:00
|
|
|
"github.com/filecoin-project/lotus/chain/actors"
|
|
|
|
"github.com/filecoin-project/lotus/chain/actors/adt"
|
|
|
|
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
|
|
|
"github.com/filecoin-project/lotus/chain/actors/builtin/account"
|
|
|
|
"github.com/filecoin-project/lotus/chain/actors/builtin/cron"
|
2022-11-16 01:57:23 +00:00
|
|
|
"github.com/filecoin-project/lotus/chain/actors/builtin/datacap"
|
2022-06-14 15:00:51 +00:00
|
|
|
init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init"
|
|
|
|
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
|
|
|
|
"github.com/filecoin-project/lotus/chain/actors/builtin/multisig"
|
|
|
|
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
|
|
|
|
"github.com/filecoin-project/lotus/chain/actors/builtin/reward"
|
|
|
|
"github.com/filecoin-project/lotus/chain/actors/builtin/system"
|
|
|
|
"github.com/filecoin-project/lotus/chain/actors/builtin/verifreg"
|
|
|
|
"github.com/filecoin-project/lotus/chain/consensus/filcns"
|
2020-02-11 20:48:03 +00:00
|
|
|
"github.com/filecoin-project/lotus/chain/state"
|
|
|
|
"github.com/filecoin-project/lotus/chain/store"
|
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
2020-05-14 02:32:04 +00:00
|
|
|
"github.com/filecoin-project/lotus/chain/vm"
|
2020-02-12 00:58:55 +00:00
|
|
|
"github.com/filecoin-project/lotus/genesis"
|
2022-06-14 15:00:51 +00:00
|
|
|
"github.com/filecoin-project/lotus/journal"
|
2020-07-24 09:34:48 +00:00
|
|
|
"github.com/filecoin-project/lotus/lib/sigs"
|
2022-06-14 15:00:51 +00:00
|
|
|
"github.com/filecoin-project/lotus/node/bundle"
|
2020-02-11 20:48:03 +00:00
|
|
|
)
|
|
|
|
|
2020-02-12 00:58:55 +00:00
|
|
|
const AccountStart = 100
|
|
|
|
const MinerStart = 1000
|
|
|
|
const MaxAccounts = MinerStart - AccountStart
|
|
|
|
|
2020-02-11 20:48:03 +00:00
|
|
|
var log = logging.Logger("genesis")
|
|
|
|
|
|
|
|
type GenesisBootstrap struct {
|
|
|
|
Genesis *types.BlockHeader
|
|
|
|
}
|
2020-04-21 21:38:26 +00:00
|
|
|
|
2020-02-12 00:58:55 +00:00
|
|
|
/*
|
|
|
|
From a list of parameters, create a genesis block / initial state
|
|
|
|
|
|
|
|
The process:
|
2020-02-12 01:15:02 +00:00
|
|
|
- Bootstrap state (MakeInitialStateTree)
|
2020-02-12 22:12:11 +00:00
|
|
|
- Create empty state
|
2020-02-14 21:38:18 +00:00
|
|
|
- Create system actor
|
2020-02-12 22:12:11 +00:00
|
|
|
- Make init actor
|
2020-02-12 00:58:55 +00:00
|
|
|
- Create accounts mappings
|
|
|
|
- Set NextID to MinerStart
|
|
|
|
- Setup Reward (1.4B fil)
|
|
|
|
- Setup Cron
|
2020-02-12 22:12:11 +00:00
|
|
|
- Create empty power actor
|
2020-02-12 00:58:55 +00:00
|
|
|
- Create empty market
|
2020-04-21 18:25:43 +00:00
|
|
|
- Create verified registry
|
2020-02-12 00:58:55 +00:00
|
|
|
- Setup burnt fund address
|
2020-02-12 22:12:11 +00:00
|
|
|
- Initialize account / msig balances
|
2020-02-12 00:58:55 +00:00
|
|
|
- Instantiate early vm with genesis syscalls
|
|
|
|
- Create miners
|
2020-02-12 22:12:11 +00:00
|
|
|
- Each:
|
|
|
|
- power.CreateMiner, set msg value to PowerBalance
|
2020-02-12 00:58:55 +00:00
|
|
|
- market.AddFunds with correct value
|
2020-02-12 02:13:00 +00:00
|
|
|
- market.PublishDeals for related sectors
|
2020-06-26 13:23:52 +00:00
|
|
|
- Set network power in the power actor to what we'll have after genesis creation
|
2020-07-20 15:59:05 +00:00
|
|
|
- Recreate reward actor state with the right power
|
2020-06-26 13:23:52 +00:00
|
|
|
- For each precommitted sector
|
|
|
|
- Get deal weight
|
|
|
|
- Calculate QA Power
|
|
|
|
- Remove fake power from the power actor
|
|
|
|
- Calculate pledge
|
|
|
|
- Precommit
|
|
|
|
- Confirm valid
|
2020-02-12 00:58:55 +00:00
|
|
|
|
|
|
|
Data Types:
|
|
|
|
|
|
|
|
PreSeal :{
|
2020-02-12 22:12:11 +00:00
|
|
|
CommR CID
|
|
|
|
CommD CID
|
|
|
|
SectorID SectorNumber
|
|
|
|
Deal market.DealProposal # Start at 0, self-deal!
|
2020-02-12 00:58:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Genesis: {
|
|
|
|
Accounts: [ # non-miner, non-singleton actors, max len = MaxAccounts
|
|
|
|
{
|
|
|
|
Type: "account" / "multisig",
|
|
|
|
Value: "attofil",
|
|
|
|
[Meta: {msig settings, account key..}]
|
|
|
|
},...
|
|
|
|
],
|
|
|
|
Miners: [
|
|
|
|
{
|
|
|
|
Owner, Worker Addr # ID
|
|
|
|
MarketBalance, PowerBalance TokenAmount
|
|
|
|
SectorSize uint64
|
|
|
|
PreSeals []PreSeal
|
|
|
|
},...
|
|
|
|
],
|
|
|
|
}
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2020-07-28 17:51:47 +00:00
|
|
|
func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template genesis.Template) (*state.StateTree, map[address.Address]address.Address, error) {
|
2020-02-12 00:58:55 +00:00
|
|
|
// Create empty state tree
|
|
|
|
|
|
|
|
cst := cbor.NewCborStore(bs)
|
2020-02-17 17:19:06 +00:00
|
|
|
_, err := cst.Put(context.TODO(), []struct{}{})
|
|
|
|
if err != nil {
|
2020-07-28 17:51:47 +00:00
|
|
|
return nil, nil, xerrors.Errorf("putting empty object: %w", err)
|
2020-02-17 17:19:06 +00:00
|
|
|
}
|
|
|
|
|
2021-05-15 01:11:23 +00:00
|
|
|
sv, err := state.VersionForNetwork(template.NetworkVersion)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, xerrors.Errorf("getting state tree version: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
state, err := state.NewStateTree(cst, sv)
|
2020-02-12 00:58:55 +00:00
|
|
|
if err != nil {
|
2020-07-28 17:51:47 +00:00
|
|
|
return nil, nil, xerrors.Errorf("making new state tree: %w", err)
|
2020-02-12 00:58:55 +00:00
|
|
|
}
|
|
|
|
|
2022-09-06 15:49:29 +00:00
|
|
|
av, err := actorstypes.VersionForNetwork(template.NetworkVersion)
|
2021-08-10 17:07:30 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, xerrors.Errorf("getting network version: %w", err)
|
|
|
|
}
|
2021-05-15 01:11:23 +00:00
|
|
|
|
feat: refactor: actor bundling system (#8838)
1. Include the builtin-actors in the lotus source tree.
2. Embed the bundle on build instead of downloading at runtime.
3. Avoid reading the bundle whenever possible by including bundle
metadata (the bundle CID, the actor CIDs, etc.).
4. Remove everything related to dependency injection.
1. We're no longer downloading the bundle, so doing anything ahead
of time doesn't really help.
2. We register the manifests on init because, unfortunately, they're
global.
3. We explicitly load the current actors bundle in the genesis
state-tree method.
4. For testing, we just change the in-use bundle with a bit of a
hack. It's not great, but using dependency injection doesn't make
any sense either because, again, the manifest information is
global.
5. Remove the bundle.toml file. Bundles may be overridden by
specifying an override path in the parameters file, or an
environment variable.
fixes #8701
2022-06-13 17:15:00 +00:00
|
|
|
if err := bundle.LoadBundles(ctx, bs, av); err != nil {
|
|
|
|
return nil, nil, xerrors.Errorf("loading actors for genesis block: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-02-14 21:38:18 +00:00
|
|
|
// Create system actor
|
|
|
|
|
2021-05-15 01:11:23 +00:00
|
|
|
sysact, err := SetupSystemActor(ctx, bs, av)
|
2020-02-14 21:38:18 +00:00
|
|
|
if err != nil {
|
2021-05-15 01:11:23 +00:00
|
|
|
return nil, nil, xerrors.Errorf("setup system actor: %w", err)
|
2020-02-14 21:38:18 +00:00
|
|
|
}
|
2021-05-15 01:11:23 +00:00
|
|
|
if err := state.SetActor(system.Address, sysact); err != nil {
|
|
|
|
return nil, nil, xerrors.Errorf("set system actor: %w", err)
|
2020-02-14 21:38:18 +00:00
|
|
|
}
|
|
|
|
|
2020-02-12 00:58:55 +00:00
|
|
|
// Create init actor
|
|
|
|
|
2021-05-15 01:11:23 +00:00
|
|
|
idStart, initact, keyIDs, err := SetupInitActor(ctx, bs, template.NetworkName, template.Accounts, template.VerifregRootKey, template.RemainderAccount, av)
|
2020-02-12 00:58:55 +00:00
|
|
|
if err != nil {
|
2020-07-28 17:51:47 +00:00
|
|
|
return nil, nil, xerrors.Errorf("setup init actor: %w", err)
|
2020-02-12 00:58:55 +00:00
|
|
|
}
|
2021-05-15 01:11:23 +00:00
|
|
|
if err := state.SetActor(init_.Address, initact); err != nil {
|
2020-07-28 17:51:47 +00:00
|
|
|
return nil, nil, xerrors.Errorf("set init actor: %w", err)
|
2020-02-12 00:58:55 +00:00
|
|
|
}
|
|
|
|
|
2020-02-12 01:15:02 +00:00
|
|
|
// Setup reward
|
2021-05-15 01:11:23 +00:00
|
|
|
// RewardActor's state is overwritten by SetupStorageMiners, but needs to exist for miner creation messages
|
|
|
|
rewact, err := SetupRewardActor(ctx, bs, big.Zero(), av)
|
2020-03-04 21:21:24 +00:00
|
|
|
if err != nil {
|
2021-05-15 01:11:23 +00:00
|
|
|
return nil, nil, xerrors.Errorf("setup reward actor: %w", err)
|
2020-03-04 21:21:24 +00:00
|
|
|
}
|
|
|
|
|
2021-05-15 01:11:23 +00:00
|
|
|
err = state.SetActor(reward.Address, rewact)
|
2020-02-12 01:15:02 +00:00
|
|
|
if err != nil {
|
2021-05-15 01:11:23 +00:00
|
|
|
return nil, nil, xerrors.Errorf("set reward actor: %w", err)
|
2020-02-12 01:15:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Setup cron
|
2021-05-15 01:11:23 +00:00
|
|
|
cronact, err := SetupCronActor(ctx, bs, av)
|
2020-02-12 01:15:02 +00:00
|
|
|
if err != nil {
|
2020-07-28 17:51:47 +00:00
|
|
|
return nil, nil, xerrors.Errorf("setup cron actor: %w", err)
|
2020-02-12 01:15:02 +00:00
|
|
|
}
|
2021-05-15 01:11:23 +00:00
|
|
|
if err := state.SetActor(cron.Address, cronact); err != nil {
|
2020-07-28 17:51:47 +00:00
|
|
|
return nil, nil, xerrors.Errorf("set cron actor: %w", err)
|
2020-02-12 01:15:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create empty power actor
|
2021-05-15 01:11:23 +00:00
|
|
|
spact, err := SetupStoragePowerActor(ctx, bs, av)
|
2020-02-12 01:15:02 +00:00
|
|
|
if err != nil {
|
2021-05-15 01:11:23 +00:00
|
|
|
return nil, nil, xerrors.Errorf("setup storage power actor: %w", err)
|
2020-02-12 01:15:02 +00:00
|
|
|
}
|
2021-05-15 01:11:23 +00:00
|
|
|
if err := state.SetActor(power.Address, spact); err != nil {
|
|
|
|
return nil, nil, xerrors.Errorf("set storage power actor: %w", err)
|
2020-02-12 01:15:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create empty market actor
|
2021-05-15 01:11:23 +00:00
|
|
|
marketact, err := SetupStorageMarketActor(ctx, bs, av)
|
2020-02-12 01:15:02 +00:00
|
|
|
if err != nil {
|
2020-07-28 17:51:47 +00:00
|
|
|
return nil, nil, xerrors.Errorf("setup storage market actor: %w", err)
|
2020-02-12 01:15:02 +00:00
|
|
|
}
|
2021-05-15 01:11:23 +00:00
|
|
|
if err := state.SetActor(market.Address, marketact); err != nil {
|
|
|
|
return nil, nil, xerrors.Errorf("set storage market actor: %w", err)
|
2020-02-12 01:15:02 +00:00
|
|
|
}
|
|
|
|
|
2020-04-21 18:25:43 +00:00
|
|
|
// Create verified registry
|
2021-05-15 01:11:23 +00:00
|
|
|
verifact, err := SetupVerifiedRegistryActor(ctx, bs, av)
|
2020-04-21 18:25:43 +00:00
|
|
|
if err != nil {
|
2021-05-15 01:11:23 +00:00
|
|
|
return nil, nil, xerrors.Errorf("setup verified registry market actor: %w", err)
|
2020-04-21 18:25:43 +00:00
|
|
|
}
|
2021-05-15 01:11:23 +00:00
|
|
|
if err := state.SetActor(verifreg.Address, verifact); err != nil {
|
|
|
|
return nil, nil, xerrors.Errorf("set verified registry actor: %w", err)
|
2020-04-21 18:25:43 +00:00
|
|
|
}
|
|
|
|
|
2022-11-16 01:57:23 +00:00
|
|
|
// Create datacap actor
|
|
|
|
if av >= 9 {
|
|
|
|
dcapact, err := SetupDatacapActor(ctx, bs, av)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, xerrors.Errorf("setup datacap actor: %w", err)
|
|
|
|
}
|
|
|
|
if err := state.SetActor(datacap.Address, dcapact); err != nil {
|
|
|
|
return nil, nil, xerrors.Errorf("set datacap actor: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-02 16:07:23 +00:00
|
|
|
bact, err := MakeAccountActor(ctx, cst, av, builtin.BurntFundsActorAddr, big.Zero())
|
2020-08-23 01:53:44 +00:00
|
|
|
if err != nil {
|
2021-05-15 01:11:23 +00:00
|
|
|
return nil, nil, xerrors.Errorf("setup burnt funds actor state: %w", err)
|
2020-08-23 01:53:44 +00:00
|
|
|
}
|
2021-05-15 01:11:23 +00:00
|
|
|
if err := state.SetActor(builtin.BurntFundsActorAddr, bact); err != nil {
|
|
|
|
return nil, nil, xerrors.Errorf("set burnt funds actor: %w", err)
|
2020-02-12 01:15:02 +00:00
|
|
|
}
|
2020-02-12 00:58:55 +00:00
|
|
|
|
2020-02-12 01:15:02 +00:00
|
|
|
// Create accounts
|
2020-08-19 19:54:33 +00:00
|
|
|
for _, info := range template.Accounts {
|
2020-02-12 00:58:55 +00:00
|
|
|
|
2020-08-19 19:54:33 +00:00
|
|
|
switch info.Type {
|
|
|
|
case genesis.TAccount:
|
2021-09-02 16:07:23 +00:00
|
|
|
if err := CreateAccountActor(ctx, cst, state, info, keyIDs, av); err != nil {
|
2020-08-19 19:54:33 +00:00
|
|
|
return nil, nil, xerrors.Errorf("failed to create account actor: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
case genesis.TMultisig:
|
|
|
|
|
|
|
|
ida, err := address.NewIDAddress(uint64(idStart))
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
idStart++
|
2020-02-12 00:58:55 +00:00
|
|
|
|
2021-09-02 16:07:23 +00:00
|
|
|
if err := CreateMultisigAccount(ctx, cst, state, ida, info, keyIDs, av); err != nil {
|
2020-08-19 19:54:33 +00:00
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return nil, nil, xerrors.New("unsupported account type")
|
2020-02-12 00:58:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-03-06 06:26:51 +00:00
|
|
|
switch template.VerifregRootKey.Type {
|
|
|
|
case genesis.TAccount:
|
|
|
|
var ainfo genesis.AccountMeta
|
|
|
|
if err := json.Unmarshal(template.VerifregRootKey.Meta, &ainfo); err != nil {
|
|
|
|
return nil, nil, xerrors.Errorf("unmarshaling account meta: %w", err)
|
|
|
|
}
|
2020-05-14 02:32:04 +00:00
|
|
|
|
2021-03-06 06:26:51 +00:00
|
|
|
_, ok := keyIDs[ainfo.Owner]
|
|
|
|
if ok {
|
|
|
|
return nil, nil, fmt.Errorf("rootkey account has already been declared, cannot be assigned 80: %s", ainfo.Owner)
|
|
|
|
}
|
2020-05-14 02:32:04 +00:00
|
|
|
|
2021-09-02 16:07:23 +00:00
|
|
|
vact, err := MakeAccountActor(ctx, cst, av, ainfo.Owner, template.VerifregRootKey.Balance)
|
2021-03-06 06:26:51 +00:00
|
|
|
if err != nil {
|
2021-05-15 01:11:23 +00:00
|
|
|
return nil, nil, xerrors.Errorf("setup verifreg rootkey account state: %w", err)
|
|
|
|
}
|
|
|
|
if err = state.SetActor(builtin.RootVerifierAddress, vact); err != nil {
|
|
|
|
return nil, nil, xerrors.Errorf("set verifreg rootkey account actor: %w", err)
|
2021-03-06 06:26:51 +00:00
|
|
|
}
|
|
|
|
case genesis.TMultisig:
|
2021-09-02 16:07:23 +00:00
|
|
|
if err = CreateMultisigAccount(ctx, cst, state, builtin.RootVerifierAddress, template.VerifregRootKey, keyIDs, av); err != nil {
|
2021-03-06 06:26:51 +00:00
|
|
|
return nil, nil, xerrors.Errorf("failed to set up verified registry signer: %w", err)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return nil, nil, xerrors.Errorf("unknown account type for verifreg rootkey: %w", err)
|
2020-05-14 02:32:04 +00:00
|
|
|
}
|
|
|
|
|
2020-07-23 20:44:09 +00:00
|
|
|
// Setup the first verifier as ID-address 81
|
|
|
|
// TODO: remove this
|
|
|
|
skBytes, err := sigs.Generate(crypto.SigTypeBLS)
|
|
|
|
if err != nil {
|
2020-07-28 17:51:47 +00:00
|
|
|
return nil, nil, xerrors.Errorf("creating random verifier secret key: %w", err)
|
2020-07-23 20:44:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
verifierPk, err := sigs.ToPublic(crypto.SigTypeBLS, skBytes)
|
|
|
|
if err != nil {
|
2020-07-28 17:51:47 +00:00
|
|
|
return nil, nil, xerrors.Errorf("creating random verifier public key: %w", err)
|
2020-07-23 20:44:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
verifierAd, err := address.NewBLSAddress(verifierPk)
|
|
|
|
if err != nil {
|
2020-07-28 17:51:47 +00:00
|
|
|
return nil, nil, xerrors.Errorf("creating random verifier address: %w", err)
|
2020-07-23 20:44:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
verifierId, err := address.NewIDAddress(81)
|
|
|
|
if err != nil {
|
2020-07-28 17:51:47 +00:00
|
|
|
return nil, nil, err
|
2020-07-23 20:44:09 +00:00
|
|
|
}
|
|
|
|
|
2021-09-02 16:07:23 +00:00
|
|
|
verifierAct, err := MakeAccountActor(ctx, cst, av, verifierAd, big.Zero())
|
2020-07-23 20:44:09 +00:00
|
|
|
if err != nil {
|
2021-05-15 01:11:23 +00:00
|
|
|
return nil, nil, xerrors.Errorf("setup first verifier state: %w", err)
|
2020-07-23 20:44:09 +00:00
|
|
|
}
|
|
|
|
|
2021-05-15 01:11:23 +00:00
|
|
|
if err = state.SetActor(verifierId, verifierAct); err != nil {
|
|
|
|
return nil, nil, xerrors.Errorf("set first verifier actor: %w", err)
|
2020-07-23 20:44:09 +00:00
|
|
|
}
|
|
|
|
|
2020-08-18 06:38:58 +00:00
|
|
|
totalFilAllocated := big.Zero()
|
2020-08-18 20:45:43 +00:00
|
|
|
|
2020-08-18 06:38:58 +00:00
|
|
|
err = state.ForEach(func(addr address.Address, act *types.Actor) error {
|
2021-06-18 21:20:48 +00:00
|
|
|
if act.Balance.Nil() {
|
|
|
|
panic(fmt.Sprintf("actor %s (%s) has nil balance", addr, builtin.ActorNameByCode(act.Code)))
|
|
|
|
}
|
2020-08-18 06:38:58 +00:00
|
|
|
totalFilAllocated = big.Add(totalFilAllocated, act.Balance)
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, xerrors.Errorf("summing account balances in state tree: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
totalFil := big.Mul(big.NewInt(int64(build.FilBase)), big.NewInt(int64(build.FilecoinPrecision)))
|
|
|
|
remainingFil := big.Sub(totalFil, totalFilAllocated)
|
2020-08-18 17:56:54 +00:00
|
|
|
if remainingFil.Sign() < 0 {
|
|
|
|
return nil, nil, xerrors.Errorf("somehow overallocated filecoin (allocated = %s)", types.FIL(totalFilAllocated))
|
|
|
|
}
|
2020-08-18 06:38:58 +00:00
|
|
|
|
2020-08-21 01:11:34 +00:00
|
|
|
template.RemainderAccount.Balance = remainingFil
|
|
|
|
|
2021-03-06 06:31:43 +00:00
|
|
|
switch template.RemainderAccount.Type {
|
|
|
|
case genesis.TAccount:
|
|
|
|
var ainfo genesis.AccountMeta
|
|
|
|
if err := json.Unmarshal(template.RemainderAccount.Meta, &ainfo); err != nil {
|
|
|
|
return nil, nil, xerrors.Errorf("unmarshaling account meta: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, ok := keyIDs[ainfo.Owner]
|
|
|
|
if ok {
|
|
|
|
return nil, nil, fmt.Errorf("remainder account has already been declared, cannot be assigned 90: %s", ainfo.Owner)
|
|
|
|
}
|
|
|
|
|
2021-03-12 21:58:43 +00:00
|
|
|
keyIDs[ainfo.Owner] = builtin.ReserveAddress
|
2021-09-02 16:07:23 +00:00
|
|
|
err = CreateAccountActor(ctx, cst, state, template.RemainderAccount, keyIDs, av)
|
2021-03-06 06:31:43 +00:00
|
|
|
if err != nil {
|
2021-03-12 21:58:43 +00:00
|
|
|
return nil, nil, xerrors.Errorf("creating remainder acct: %w", err)
|
2021-03-06 06:31:43 +00:00
|
|
|
}
|
2021-03-12 21:58:43 +00:00
|
|
|
|
2021-03-06 06:31:43 +00:00
|
|
|
case genesis.TMultisig:
|
2021-09-02 16:07:23 +00:00
|
|
|
if err = CreateMultisigAccount(ctx, cst, state, builtin.ReserveAddress, template.RemainderAccount, keyIDs, av); err != nil {
|
2021-03-06 06:31:43 +00:00
|
|
|
return nil, nil, xerrors.Errorf("failed to set up remainder: %w", err)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return nil, nil, xerrors.Errorf("unknown account type for remainder: %w", err)
|
2020-08-18 21:30:49 +00:00
|
|
|
}
|
2020-08-19 19:54:33 +00:00
|
|
|
|
|
|
|
return state, keyIDs, nil
|
|
|
|
}
|
|
|
|
|
2022-09-06 15:49:29 +00:00
|
|
|
func MakeAccountActor(ctx context.Context, cst cbor.IpldStore, av actorstypes.Version, addr address.Address, bal types.BigInt) (*types.Actor, error) {
|
2021-05-15 01:11:23 +00:00
|
|
|
ast, err := account.MakeState(adt.WrapStore(ctx, cst), av, addr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
statecid, err := cst.Put(ctx, ast.GetState())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-06-29 16:54:14 +00:00
|
|
|
actcid, ok := actors.GetActorCodeID(av, actors.AccountKey)
|
|
|
|
if !ok {
|
|
|
|
return nil, xerrors.Errorf("failed to get account actor code ID for actors version %d", av)
|
2021-05-15 01:11:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
act := &types.Actor{
|
|
|
|
Code: actcid,
|
|
|
|
Head: statecid,
|
|
|
|
Balance: bal,
|
|
|
|
}
|
|
|
|
|
|
|
|
return act, nil
|
|
|
|
}
|
|
|
|
|
2022-09-06 15:49:29 +00:00
|
|
|
func CreateAccountActor(ctx context.Context, cst cbor.IpldStore, state *state.StateTree, info genesis.Actor, keyIDs map[address.Address]address.Address, av actorstypes.Version) error {
|
2020-08-19 19:54:33 +00:00
|
|
|
var ainfo genesis.AccountMeta
|
|
|
|
if err := json.Unmarshal(info.Meta, &ainfo); err != nil {
|
|
|
|
return xerrors.Errorf("unmarshaling account meta: %w", err)
|
|
|
|
}
|
2021-05-15 01:11:23 +00:00
|
|
|
|
2021-09-02 16:07:23 +00:00
|
|
|
aa, err := MakeAccountActor(ctx, cst, av, ainfo.Owner, info.Balance)
|
2020-08-19 19:54:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
ida, ok := keyIDs[ainfo.Owner]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("no registered ID for account actor: %s", ainfo.Owner)
|
|
|
|
}
|
|
|
|
|
2021-05-15 01:11:23 +00:00
|
|
|
err = state.SetActor(ida, aa)
|
2020-08-18 20:45:43 +00:00
|
|
|
if err != nil {
|
2020-08-19 19:54:33 +00:00
|
|
|
return xerrors.Errorf("setting account from actmap: %w", err)
|
2020-08-18 06:38:58 +00:00
|
|
|
}
|
2020-08-19 19:54:33 +00:00
|
|
|
return nil
|
2020-02-12 00:58:55 +00:00
|
|
|
}
|
|
|
|
|
2022-09-06 15:49:29 +00:00
|
|
|
func CreateMultisigAccount(ctx context.Context, cst cbor.IpldStore, state *state.StateTree, ida address.Address, info genesis.Actor, keyIDs map[address.Address]address.Address, av actorstypes.Version) error {
|
2020-08-19 19:54:33 +00:00
|
|
|
if info.Type != genesis.TMultisig {
|
2021-09-02 16:07:23 +00:00
|
|
|
return fmt.Errorf("can only call CreateMultisigAccount with multisig Actor info")
|
2020-08-19 19:54:33 +00:00
|
|
|
}
|
|
|
|
var ainfo genesis.MultisigMeta
|
|
|
|
if err := json.Unmarshal(info.Meta, &ainfo); err != nil {
|
|
|
|
return xerrors.Errorf("unmarshaling account meta: %w", err)
|
|
|
|
}
|
2020-07-24 09:34:48 +00:00
|
|
|
|
2020-08-19 19:54:33 +00:00
|
|
|
var signers []address.Address
|
2020-08-18 10:33:11 +00:00
|
|
|
|
2020-08-19 19:54:33 +00:00
|
|
|
for _, e := range ainfo.Signers {
|
|
|
|
idAddress, ok := keyIDs[e]
|
2020-08-19 20:32:53 +00:00
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("no registered key ID for signer: %s", e)
|
|
|
|
}
|
2020-08-19 19:54:33 +00:00
|
|
|
|
|
|
|
// Check if actor already exists
|
|
|
|
_, err := state.GetActor(e)
|
|
|
|
if err == nil {
|
2020-08-18 10:33:11 +00:00
|
|
|
signers = append(signers, idAddress)
|
2020-08-19 19:54:33 +00:00
|
|
|
continue
|
2020-08-18 10:23:28 +00:00
|
|
|
}
|
|
|
|
|
2021-09-02 16:07:23 +00:00
|
|
|
aa, err := MakeAccountActor(ctx, cst, av, e, big.Zero())
|
2020-07-20 15:03:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-05-15 01:11:23 +00:00
|
|
|
|
|
|
|
if err = state.SetActor(idAddress, aa); err != nil {
|
2020-07-20 15:03:27 +00:00
|
|
|
return xerrors.Errorf("setting account from actmap: %w", err)
|
|
|
|
}
|
2020-08-19 19:54:33 +00:00
|
|
|
signers = append(signers, idAddress)
|
2020-05-14 02:32:04 +00:00
|
|
|
}
|
|
|
|
|
2021-05-15 01:11:23 +00:00
|
|
|
mst, err := multisig.MakeState(adt.WrapStore(ctx, cst), av, signers, uint64(ainfo.Threshold), abi.ChainEpoch(ainfo.VestingStart), abi.ChainEpoch(ainfo.VestingDuration), info.Balance)
|
2021-05-15 01:11:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-05-15 01:11:23 +00:00
|
|
|
|
|
|
|
statecid, err := cst.Put(ctx, mst.GetState())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-06-29 16:54:14 +00:00
|
|
|
actcid, ok := actors.GetActorCodeID(av, actors.MultisigKey)
|
|
|
|
if !ok {
|
|
|
|
return xerrors.Errorf("failed to get multisig code ID for actors version %d", av)
|
2021-05-15 01:11:23 +00:00
|
|
|
}
|
|
|
|
|
2020-08-19 19:54:33 +00:00
|
|
|
err = state.SetActor(ida, &types.Actor{
|
2021-05-15 01:11:23 +00:00
|
|
|
Code: actcid,
|
2020-08-19 19:54:33 +00:00
|
|
|
Balance: info.Balance,
|
2021-05-15 01:11:23 +00:00
|
|
|
Head: statecid,
|
2020-08-19 19:54:33 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("setting account from actmap: %w", err)
|
|
|
|
}
|
2021-05-15 01:11:23 +00:00
|
|
|
|
2020-08-19 19:54:33 +00:00
|
|
|
return nil
|
2020-02-12 00:58:55 +00:00
|
|
|
}
|
|
|
|
|
2021-07-27 13:30:23 +00:00
|
|
|
func VerifyPreSealedData(ctx context.Context, cs *store.ChainStore, sys vm.SyscallBuilder, stateroot cid.Cid, template genesis.Template, keyIDs map[address.Address]address.Address, nv network.Version) (cid.Cid, error) {
|
2020-05-14 02:32:04 +00:00
|
|
|
verifNeeds := make(map[address.Address]abi.PaddedPieceSize)
|
|
|
|
var sum abi.PaddedPieceSize
|
2020-07-24 00:34:27 +00:00
|
|
|
|
2021-12-17 04:59:26 +00:00
|
|
|
csc := func(context.Context, abi.ChainEpoch, *state.StateTree) (abi.TokenAmount, error) {
|
|
|
|
return big.Zero(), nil
|
|
|
|
}
|
|
|
|
|
2020-08-06 17:09:03 +00:00
|
|
|
vmopt := vm.VMOpts{
|
2020-08-09 22:49:38 +00:00
|
|
|
StateBase: stateroot,
|
|
|
|
Epoch: 0,
|
|
|
|
Rand: &fakeRand{},
|
2021-02-28 22:48:36 +00:00
|
|
|
Bstore: cs.StateBlockstore(),
|
2021-09-02 16:07:23 +00:00
|
|
|
Actors: filcns.NewActorRegistry(),
|
2021-07-27 13:30:23 +00:00
|
|
|
Syscalls: mkFakedSigSyscalls(sys),
|
2021-12-17 04:59:26 +00:00
|
|
|
CircSupplyCalc: csc,
|
2021-12-18 00:05:59 +00:00
|
|
|
NetworkVersion: nv,
|
2022-02-23 23:17:05 +00:00
|
|
|
BaseFee: big.Zero(),
|
2020-08-06 17:09:03 +00:00
|
|
|
}
|
2022-05-15 19:26:52 +00:00
|
|
|
vm, err := vm.NewVM(ctx, &vmopt)
|
2020-07-24 00:34:27 +00:00
|
|
|
if err != nil {
|
2022-06-10 11:47:19 +00:00
|
|
|
return cid.Undef, xerrors.Errorf("failed to create VM: %w", err)
|
2020-07-24 00:34:27 +00:00
|
|
|
}
|
|
|
|
|
2020-08-19 19:54:33 +00:00
|
|
|
for mi, m := range template.Miners {
|
|
|
|
for si, s := range m.Sectors {
|
|
|
|
if s.Deal.Provider != m.ID {
|
|
|
|
return cid.Undef, xerrors.Errorf("Sector %d in miner %d in template had mismatch in provider and miner ID: %s != %s", si, mi, s.Deal.Provider, m.ID)
|
|
|
|
}
|
2020-07-24 00:34:27 +00:00
|
|
|
|
2020-05-14 04:00:32 +00:00
|
|
|
amt := s.Deal.PieceSize
|
2020-07-28 17:51:47 +00:00
|
|
|
verifNeeds[keyIDs[s.Deal.Client]] += amt
|
2020-05-14 02:32:04 +00:00
|
|
|
sum += amt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-24 09:34:48 +00:00
|
|
|
verifregRoot, err := address.NewIDAddress(80)
|
2020-05-14 02:32:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
2020-07-23 20:44:09 +00:00
|
|
|
verifier, err := address.NewIDAddress(81)
|
2020-05-14 02:32:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
2021-05-15 01:11:23 +00:00
|
|
|
// Note: This is brittle, if the methodNum / param changes, it could break things
|
|
|
|
_, err = doExecValue(ctx, vm, verifreg.Address, verifregRoot, types.NewInt(0), builtin0.MethodsVerifiedRegistry.AddVerifier, mustEnc(&verifreg0.AddVerifierParams{
|
2020-07-28 09:53:00 +00:00
|
|
|
|
2020-05-14 02:32:04 +00:00
|
|
|
Address: verifier,
|
2020-05-14 04:00:32 +00:00
|
|
|
Allowance: abi.NewStoragePower(int64(sum)), // eh, close enough
|
2020-05-14 02:32:04 +00:00
|
|
|
|
|
|
|
}))
|
|
|
|
if err != nil {
|
2020-07-20 15:03:27 +00:00
|
|
|
return cid.Undef, xerrors.Errorf("failed to create verifier: %w", err)
|
2020-05-14 02:32:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for c, amt := range verifNeeds {
|
2021-05-15 01:11:23 +00:00
|
|
|
// Note: This is brittle, if the methodNum / param changes, it could break things
|
|
|
|
_, err := doExecValue(ctx, vm, verifreg.Address, verifier, types.NewInt(0), builtin0.MethodsVerifiedRegistry.AddVerifiedClient, mustEnc(&verifreg0.AddVerifiedClientParams{
|
2020-05-14 02:32:04 +00:00
|
|
|
Address: c,
|
2020-05-14 04:00:32 +00:00
|
|
|
Allowance: abi.NewStoragePower(int64(amt)),
|
2020-05-14 02:32:04 +00:00
|
|
|
}))
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, xerrors.Errorf("failed to add verified client: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-14 11:45:45 +00:00
|
|
|
st, err := vm.Flush(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return cid.Cid{}, xerrors.Errorf("vm flush: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return st, nil
|
2020-05-14 02:32:04 +00:00
|
|
|
}
|
|
|
|
|
2020-10-09 19:52:04 +00:00
|
|
|
func MakeGenesisBlock(ctx context.Context, j journal.Journal, bs bstore.Blockstore, sys vm.SyscallBuilder, template genesis.Template) (*GenesisBootstrap, error) {
|
|
|
|
if j == nil {
|
|
|
|
j = journal.NilJournal()
|
|
|
|
}
|
2020-07-28 17:51:47 +00:00
|
|
|
st, keyIDs, err := MakeInitialStateTree(ctx, bs, template)
|
2020-02-11 20:48:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("make initial state tree failed: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-02-12 01:15:02 +00:00
|
|
|
stateroot, err := st.Flush(ctx)
|
2020-02-11 20:48:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("flush state tree failed: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// temp chainstore
|
2021-09-02 16:07:23 +00:00
|
|
|
cs := store.NewChainStore(bs, bs, datastore.NewMapDatastore(), nil, j)
|
2020-05-14 02:32:04 +00:00
|
|
|
|
|
|
|
// Verify PreSealed Data
|
2021-07-27 13:30:23 +00:00
|
|
|
stateroot, err = VerifyPreSealedData(ctx, cs, sys, stateroot, template, keyIDs, template.NetworkVersion)
|
2020-05-14 02:32:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("failed to verify presealed data: %w", err)
|
|
|
|
}
|
|
|
|
|
2021-07-27 13:30:23 +00:00
|
|
|
stateroot, err = SetupStorageMiners(ctx, cs, sys, stateroot, template.Miners, template.NetworkVersion)
|
2020-02-11 20:48:03 +00:00
|
|
|
if err != nil {
|
2020-07-11 08:55:13 +00:00
|
|
|
return nil, xerrors.Errorf("setup miners failed: %w", err)
|
2020-02-11 20:48:03 +00:00
|
|
|
}
|
|
|
|
|
2021-05-15 01:11:23 +00:00
|
|
|
store := adt.WrapStore(ctx, cbor.NewCborStore(bs))
|
2020-09-18 21:59:27 +00:00
|
|
|
emptyroot, err := adt0.MakeEmptyArray(store).Root()
|
2020-02-11 20:48:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("amt build failed: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
mm := &types.MsgMeta{
|
|
|
|
BlsMessages: emptyroot,
|
|
|
|
SecpkMessages: emptyroot,
|
|
|
|
}
|
|
|
|
mmb, err := mm.ToStorageBlock()
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("serializing msgmeta failed: %w", err)
|
|
|
|
}
|
2021-12-11 21:03:00 +00:00
|
|
|
if err := bs.Put(ctx, mmb); err != nil {
|
2020-02-11 20:48:03 +00:00
|
|
|
return nil, xerrors.Errorf("putting msgmeta block to blockstore: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Infof("Empty Genesis root: %s", emptyroot)
|
|
|
|
|
2020-08-24 18:33:49 +00:00
|
|
|
tickBuf := make([]byte, 32)
|
|
|
|
_, _ = rand.Read(tickBuf)
|
2020-02-11 20:48:03 +00:00
|
|
|
genesisticket := &types.Ticket{
|
2020-08-24 18:33:49 +00:00
|
|
|
VRFProof: tickBuf,
|
2020-02-11 20:48:03 +00:00
|
|
|
}
|
|
|
|
|
2020-08-18 01:54:49 +00:00
|
|
|
filecoinGenesisCid, err := cid.Decode("bafyreiaqpwbbyjo4a42saasj36kkrpv4tsherf2e7bvezkert2a7dhonoi")
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("failed to decode filecoin genesis block CID: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-08-18 13:04:31 +00:00
|
|
|
if !expectedCid().Equals(filecoinGenesisCid) {
|
|
|
|
return nil, xerrors.Errorf("expectedCid != filecoinGenesisCid")
|
|
|
|
}
|
|
|
|
|
2020-08-18 01:54:49 +00:00
|
|
|
gblk, err := getGenesisBlock()
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("failed to construct filecoin genesis block: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-08-18 13:04:31 +00:00
|
|
|
if !filecoinGenesisCid.Equals(gblk.Cid()) {
|
|
|
|
return nil, xerrors.Errorf("filecoinGenesisCid != gblk.Cid")
|
|
|
|
}
|
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
if err := bs.Put(ctx, gblk); err != nil {
|
2020-08-18 01:54:49 +00:00
|
|
|
return nil, xerrors.Errorf("failed writing filecoin genesis block to blockstore: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-02-11 20:48:03 +00:00
|
|
|
b := &types.BlockHeader{
|
2021-05-15 01:11:23 +00:00
|
|
|
Miner: system.Address,
|
2020-04-08 19:06:41 +00:00
|
|
|
Ticket: genesisticket,
|
2020-08-18 01:54:49 +00:00
|
|
|
Parents: []cid.Cid{filecoinGenesisCid},
|
2020-02-11 20:48:03 +00:00
|
|
|
Height: 0,
|
|
|
|
ParentWeight: types.NewInt(0),
|
|
|
|
ParentStateRoot: stateroot,
|
|
|
|
Messages: mmb.Cid(),
|
|
|
|
ParentMessageReceipts: emptyroot,
|
2020-03-21 22:25:00 +00:00
|
|
|
BLSAggregate: nil,
|
|
|
|
BlockSig: nil,
|
2020-02-12 01:15:02 +00:00
|
|
|
Timestamp: template.Timestamp,
|
2020-05-08 17:59:18 +00:00
|
|
|
ElectionProof: new(types.ElectionProof),
|
2020-04-08 15:11:42 +00:00
|
|
|
BeaconEntries: []types.BeaconEntry{
|
2020-04-08 19:37:04 +00:00
|
|
|
{
|
2020-04-08 15:11:42 +00:00
|
|
|
Round: 0,
|
|
|
|
Data: make([]byte, 32),
|
|
|
|
},
|
|
|
|
},
|
2020-08-06 17:09:03 +00:00
|
|
|
ParentBaseFee: abi.NewTokenAmount(build.InitialBaseFee),
|
2020-02-11 20:48:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sb, err := b.ToStorageBlock()
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("serializing block header failed: %w", err)
|
|
|
|
}
|
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
if err := bs.Put(ctx, sb); err != nil {
|
2020-02-11 20:48:03 +00:00
|
|
|
return nil, xerrors.Errorf("putting header to blockstore: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &GenesisBootstrap{
|
|
|
|
Genesis: b,
|
|
|
|
}, nil
|
|
|
|
}
|