13 KiB
Creating a storage miner
This document explains the code flow of the storage miner creation process. It describes the flow on two dimensions: at the network level, from the local node to the rest of the Filecoin network, and at the VM level, from the CLI commands to the new chain state. It assumes the reader is already familiar with the general Lotus architecture, relying especially on the descriptions of the CLI/API interface and the VM chain state.
Note we are not following the Storage Mining user documentation where the miner is created along with the owner through the faucet, but we do those in separate stages try to exercise what will be the closest code path to Mainnet, using the createStorageMiner()
call in the CLI (when --actor
is not provided).
FIXME: Check that all the information it assumes throughout this document is actually present in the main architecture doc.
Topics with new information covered here (at different levels of detail) are:
- Addresses
- Wallet, key store
- Account actor
- Filecoin Message
- Storage node type
- Miner actor, owner and worker addresses
- Message generation and message pool
- Message execution in the VM
- Power actor
- PubSub/GossipSub
Wallet
We start by creating a new wallet which will be associated with the miner. A "wallet" is just an abstraction over a Filecoin address associated to an account actor: an actor (object) in the VM that represents a user in the network.
lotus wallet new
# t1ildao7vmywh67lolnd7esq7bqkfkhnalxsj74wq
The returned (random-looking) string is the address associated with the new wallet. The first part of the string encodes its Protocol
, in this case type 1 (t1
), which corresponds to a public-key address generated from the secp256k1
elliptic curve (default option in the wallet
command, alternatively it can also generate the BLS signature scheme).
At this point the address still hasn't been transmitted to the network, it is just reflected on the key store of our local node. The key store (by default in ~/.lotus/keystore/
) contains all the keys generated by this node, including this new key which will be set as the default and will be used by other commands (like the miner creation one). (See the WalletNew()
API for more information about key generation and storage.)
It is important to note that the key store also holds the private key associated with the public one (that represent a Filecoin address) but this one is never transmitted over the network, it is just used locally to sign messages and prove this node is in possession of it and hence represents the account actor associated to the address/wallet.
Account actor
There is no explicit Filecoin message or CLI command that creates an account actor but rather it is implicit in the public-key address reflected by the wallet. The first time the VM sees it as the receiving address of a Filecoin message it will automatically create the associated account actor.
We will trigger its creation then by sending funds to it, needed for the creation of the storage miner (note this document reflects the Filecoin Testnet, where we actually don't need funds to create a miner but will do the full procedure regardless to show the full cycle). In the Testnet we can send funds to an account through the Faucet, inserting the address returned by the wallet new
command.
FIXME: Is there an easy way to visualize the message generated by the Faucet?
Storage miner node
The lotus-storage-miner
command provides a set of tools to manage the miners associated with the local storage miner node. At this point it is important to note the different node types, in the previous document we always referred to a single local node, FullNode
, which handled the sync process and any other communication with the Filecoin network (the term full stands for full validation of the consensus protocol, there are no light clients at the moment that do not do the full validation). We now create a new node of type StorageMiner
, with its own repo (each node is always associated to its own repo), by default in ~/.lotusstorage
. The difference between the two nodes lies in the services they run (see build options in the main architecture document).
The lotus-storage-miner init
command option creates a new storage miner node. We will only be able to run the command once the chain has been synced by the full node (which needs to be running) and it will also require the download of the proof parameters (of several GBs, so it may take some time).
The main options that define a miner are the owner and worker addresses associated to it (stored in MinerInfo
, a substructure of the Miner Actor State) and its peer ID. We use default values for all of these options in the command and briefly described them here:
-
Owner: address of the account actor that manages this miner (receives its payments, can change its information, etc.). By default this option is set to the default wallet address (and associated account actor), in the case of this example it will be the address just created with the
wallet new
command. -
Worker: address of an account actor that signs blocks and submits proofs associated with this miner, it can be the same as the owner address. By default the command will create a new wallet address for it, of type BLS (the only one accepted for worker addresses).
-
Peer ID: a network ID (belonging to the
libp2p
stack) used to contact the miner directly off-chain (e.g., to make a storage deal). Note the difference with the rest of the communication in the Filecoin network that happens largely inside the chain itself: when we "send" messages to the different actors that is actually a VM abstraction meaning we execute the method in the VM itself run by logic of the targeted actor, physically (at the network TCP/IP level) we broadcast the message to all of our peers to be included in a Filecoin block.
With the miner information filled the command constructs a Filecoin message to broadcast to the network and be included in a Filecoin block by a miner (see createStorageMiner()
). We will wait for that block to be synced to the chain (by the full node) before returning the miner ID address. The ID address is another way to refer to the miner through a unique ID in the chain, it has a type 0 and it is the address that is normally seen in chain visualization tools, e.g., t01475
(since, in contrast with the public-key types of addresses, it is easily readable by humans).
The Filecoin message constructed will be targeted to the Power Actor (StoragePowerActorAddr
), which tracks the amount of power (storage capacity) every miner has, and it will have the method number of the CreateMiner
constant.
Message broadcast
At the network level, the message (to be included in a block) is sent through the message pool to the miners. The message pool is an abstraction over libp2p
's PubSub protocol, a communication pattern where peers subscribe to a topic (an identifying string) and other peers publish to it (broadcasting to all the subscribed peers it has registered). (Particularly in Lotus we use the GossipSub implementation of that protocol.) In this case the topic we publish to is /fil/msgs
, to which the miners will be subscribed and will receive, validate and process the message (see HandleIncomingMessages
in the miner node construction for more details).
It is of interest to note that the GossipSub protocol is the same that miners themselves use to publish mined blocks in the /fil/blocks
topic, to which the full node is subscribed to, used in the sync process (along with the hello
protocol described in the architecture doc).
Back to the CLI command, the MpoolPushMessage
API in the full node takes care of populating the rest of the Filecoin message (e.g., inserting the correct nonce) and serializing it before actually publishing to the GossipSub network. After that, it explicitly waits for the message to be included by a miner in a block and then for that block to reach the chain where the full node will receive it as part of the sync process (see StateWaitMsg
), checking its return code for success and its return value for the miner ID.
VM: message execution
We describe here the code flow inside the VM when it executes the CreateMiner
method (of the message sent by the lotus-storage-miner
command included by a miner in a block). This execution will be the same seen by all participants in the Filecoin protocol, the miner including the message in the block, the full node syncing to it, and any other peer receiving also this message.
There is a one-to-one mapping between the pair of actor and method number (To:
/Method:
fields) in a message in the VM, and the Go function in an actor's exported methods list that implement it. In this case, for the Power Actor list of method numbers defined in MethodsPower
, the CreateMiner
method number 2 will correspond to the Go function with the same index in the list of methods returned by Exports()
(and normally also the same name, here (Actor).CreateMiner()
).
The Power Actor in CreateMiner()
will do two things:
-
Send another message,
Exec
, to the Init Actor to instruct it to create the miner actor with the information provided bylotus-storage-miner
and receive its ID address (this ID is the one returned to the CLI command). -
Generate an entry in its list of power claims (
State.Claims
) for the newly created ID address of the miner.
Init Actor: create a new actor
The Init
actor is the only way to create actors (besides the initial actors included in the genesis block). It has a single method (besides its own constructor), Exec
. An actor is nothing more than an entry in the Init
's State.AddressMap
, a HAMT that maps an (Actor
) address to an ID one. The Actor
address is a unique address provided by the Runtime
(NewActorAddress()
) derived from the public key of the message's origin address, in this case the --owner
defined in the CLI command.
Miner Actor: constructor
The Init actor will also call the constructor for the type of actor it created, in this case a Miner. The constructor is always the method numbered 1 for any actor.
FIXME: Do we want to say anything about the constructor? The setting of the proving period? Everything else seems like default values.