@@ -2,9 +2,11 @@ package spcli
22
33import (
44 "bytes"
5+ "context"
56 "fmt"
67 "strconv"
78
9+ "github.com/docker/go-units"
810 cbor "github.com/ipfs/go-ipld-cbor"
911 "github.com/libp2p/go-libp2p/core/peer"
1012 ma "github.com/multiformats/go-multiaddr"
@@ -19,16 +21,20 @@ import (
1921 "github.com/filecoin-project/go-state-types/builtin"
2022 "github.com/filecoin-project/go-state-types/builtin/v9/miner"
2123 "github.com/filecoin-project/go-state-types/network"
24+ power2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/power"
25+ power6 "github.com/filecoin-project/specs-actors/v6/actors/builtin/power"
2226
23- "github.com/filecoin-project/lotus/api"
2427 lapi "github.com/filecoin-project/lotus/api"
28+ "github.com/filecoin-project/lotus/api/v1api"
2529 "github.com/filecoin-project/lotus/blockstore"
2630 "github.com/filecoin-project/lotus/build"
2731 "github.com/filecoin-project/lotus/chain/actors"
2832 "github.com/filecoin-project/lotus/chain/actors/adt"
2933 lminer "github.com/filecoin-project/lotus/chain/actors/builtin/miner"
34+ "github.com/filecoin-project/lotus/chain/actors/builtin/power"
3035 "github.com/filecoin-project/lotus/chain/types"
3136 lcli "github.com/filecoin-project/lotus/cli"
37+ cliutil "github.com/filecoin-project/lotus/cli/util"
3238 "github.com/filecoin-project/lotus/node/impl"
3339)
3440
@@ -1225,7 +1231,7 @@ func ActorCompactAllocatedCmd(getActor ActorAddressGetter) *cli.Command {
12251231 }
12261232}
12271233
1228- func isController (mi api .MinerInfo , addr address.Address ) bool {
1234+ func isController (mi lapi .MinerInfo , addr address.Address ) bool {
12291235 if addr == mi .Owner || addr == mi .Worker {
12301236 return true
12311237 }
@@ -1238,3 +1244,190 @@ func isController(mi api.MinerInfo, addr address.Address) bool {
12381244
12391245 return false
12401246}
1247+
1248+ var ActorNewMinerCmd = & cli.Command {
1249+ Name : "new-miner" ,
1250+ Usage : "Initializes a new miner actor" ,
1251+ Flags : []cli.Flag {
1252+ & cli.StringFlag {
1253+ Name : "worker" ,
1254+ Aliases : []string {"w" },
1255+ Usage : "worker key to use for new miner initialisation" ,
1256+ },
1257+ & cli.StringFlag {
1258+ Name : "owner" ,
1259+ Aliases : []string {"o" },
1260+ Usage : "owner key to use for new miner initialisation" ,
1261+ },
1262+ & cli.StringFlag {
1263+ Name : "from" ,
1264+ Aliases : []string {"f" },
1265+ Usage : "address to send actor(miner) creation message from" ,
1266+ },
1267+ & cli.StringFlag {
1268+ Name : "sector-size" ,
1269+ Usage : "specify sector size to use for new miner initialisation" ,
1270+ },
1271+ },
1272+ Action : func (cctx * cli.Context ) error {
1273+ ctx := cctx .Context
1274+
1275+ full , closer , err := cliutil .GetFullNodeAPIV1 (cctx )
1276+ if err != nil {
1277+ return xerrors .Errorf ("connecting to full node: %w" , err )
1278+ }
1279+ defer closer ()
1280+
1281+ var owner address.Address
1282+ if cctx .String ("owner" ) == "" {
1283+ return xerrors .Errorf ("must provide a owner address" )
1284+ }
1285+ owner , err = address .NewFromString (cctx .String ("owner" ))
1286+
1287+ if err != nil {
1288+ return err
1289+ }
1290+
1291+ worker := owner
1292+ if cctx .String ("worker" ) != "" {
1293+ worker , err = address .NewFromString (cctx .String ("worker" ))
1294+ if err != nil {
1295+ return xerrors .Errorf ("could not parse worker address: %w" , err )
1296+ }
1297+ }
1298+
1299+ sender := owner
1300+ if fromstr := cctx .String ("from" ); fromstr != "" {
1301+ faddr , err := address .NewFromString (fromstr )
1302+ if err != nil {
1303+ return xerrors .Errorf ("could not parse from address: %w" , err )
1304+ }
1305+ sender = faddr
1306+ }
1307+
1308+ if ! cctx .IsSet ("sector-size" ) {
1309+ return xerrors .Errorf ("must define sector size" )
1310+ }
1311+
1312+ sectorSizeInt , err := units .RAMInBytes (cctx .String ("sector-size" ))
1313+ if err != nil {
1314+ return err
1315+ }
1316+ ssize := abi .SectorSize (sectorSizeInt )
1317+
1318+ _ , err = CreateStorageMiner (ctx , full , owner , worker , sender , ssize , cctx .Uint64 ("confidence" ))
1319+ if err != nil {
1320+ return err
1321+ }
1322+ return nil
1323+ },
1324+ }
1325+
1326+ func CreateStorageMiner (ctx context.Context , fullNode v1api.FullNode , owner , worker , sender address.Address , ssize abi.SectorSize , confidence uint64 ) (address.Address , error ) {
1327+ // make sure the sender account exists on chain
1328+ _ , err := fullNode .StateLookupID (ctx , owner , types .EmptyTSK )
1329+ if err != nil {
1330+ return address .Undef , xerrors .Errorf ("sender must exist on chain: %w" , err )
1331+ }
1332+
1333+ // make sure the worker account exists on chain
1334+ _ , err = fullNode .StateLookupID (ctx , worker , types .EmptyTSK )
1335+ if err != nil {
1336+ signed , err := fullNode .MpoolPushMessage (ctx , & types.Message {
1337+ From : sender ,
1338+ To : worker ,
1339+ Value : types .NewInt (0 ),
1340+ }, nil )
1341+ if err != nil {
1342+ return address .Undef , xerrors .Errorf ("push worker init: %w" , err )
1343+ }
1344+
1345+ fmt .Printf ("Initializing worker account %s, message: %s\n " , worker , signed .Cid ())
1346+ fmt .Println ("Waiting for confirmation" )
1347+
1348+ mw , err := fullNode .StateWaitMsg (ctx , signed .Cid (), confidence , 2000 , true )
1349+ if err != nil {
1350+ return address .Undef , xerrors .Errorf ("waiting for worker init: %w" , err )
1351+ }
1352+ if mw .Receipt .ExitCode != 0 {
1353+ return address .Undef , xerrors .Errorf ("initializing worker account failed: exit code %d" , mw .Receipt .ExitCode )
1354+ }
1355+ }
1356+
1357+ // make sure the owner account exists on chain
1358+ _ , err = fullNode .StateLookupID (ctx , owner , types .EmptyTSK )
1359+ if err != nil {
1360+ signed , err := fullNode .MpoolPushMessage (ctx , & types.Message {
1361+ From : sender ,
1362+ To : owner ,
1363+ Value : types .NewInt (0 ),
1364+ }, nil )
1365+ if err != nil {
1366+ return address .Undef , xerrors .Errorf ("push owner init: %w" , err )
1367+ }
1368+
1369+ fmt .Printf ("Initializing owner account %s, message: %s\n " , worker , signed .Cid ())
1370+ fmt .Println ("Waiting for confirmation" )
1371+
1372+ mw , err := fullNode .StateWaitMsg (ctx , signed .Cid (), confidence , 2000 , true )
1373+ if err != nil {
1374+ return address .Undef , xerrors .Errorf ("waiting for owner init: %w" , err )
1375+ }
1376+ if mw .Receipt .ExitCode != 0 {
1377+ return address .Undef , xerrors .Errorf ("initializing owner account failed: exit code %d" , mw .Receipt .ExitCode )
1378+ }
1379+ }
1380+
1381+ // Note: the correct thing to do would be to call SealProofTypeFromSectorSize if actors version is v3 or later, but this still works
1382+ nv , err := fullNode .StateNetworkVersion (ctx , types .EmptyTSK )
1383+ if err != nil {
1384+ return address .Undef , xerrors .Errorf ("failed to get network version: %w" , err )
1385+ }
1386+ spt , err := lminer .WindowPoStProofTypeFromSectorSize (ssize , nv )
1387+ if err != nil {
1388+ return address .Undef , xerrors .Errorf ("getting post proof type: %w" , err )
1389+ }
1390+
1391+ params , err := actors .SerializeParams (& power6.CreateMinerParams {
1392+ Owner : owner ,
1393+ Worker : worker ,
1394+ WindowPoStProofType : spt ,
1395+ })
1396+ if err != nil {
1397+ return address .Undef , err
1398+ }
1399+
1400+ createStorageMinerMsg := & types.Message {
1401+ To : power .Address ,
1402+ From : sender ,
1403+ Value : big .Zero (),
1404+
1405+ Method : power .Methods .CreateMiner ,
1406+ Params : params ,
1407+ }
1408+
1409+ signed , err := fullNode .MpoolPushMessage (ctx , createStorageMinerMsg , nil )
1410+ if err != nil {
1411+ return address .Undef , xerrors .Errorf ("pushing createMiner message: %w" , err )
1412+ }
1413+
1414+ fmt .Printf ("Pushed CreateMiner message: %s\n " , signed .Cid ())
1415+ fmt .Println ("Waiting for confirmation" )
1416+
1417+ mw , err := fullNode .StateWaitMsg (ctx , signed .Cid (), confidence , 2000 , true )
1418+ if err != nil {
1419+ return address .Undef , xerrors .Errorf ("waiting for createMiner message: %w" , err )
1420+ }
1421+
1422+ if mw .Receipt .ExitCode != 0 {
1423+ return address .Undef , xerrors .Errorf ("create miner failed: exit code %d" , mw .Receipt .ExitCode )
1424+ }
1425+
1426+ var retval power2.CreateMinerReturn
1427+ if err := retval .UnmarshalCBOR (bytes .NewReader (mw .Receipt .Return )); err != nil {
1428+ return address .Undef , err
1429+ }
1430+
1431+ fmt .Printf ("New miners address is: %s (%s)\n " , retval .IDAddress , retval .RobustAddress )
1432+ return retval .IDAddress , nil
1433+ }
0 commit comments