diff --git a/api/api.go b/api/api.go index d5ba98f64..d99cfab0a 100644 --- a/api/api.go +++ b/api/api.go @@ -74,6 +74,9 @@ type FullNode interface { ChainGetBlock(context.Context, cid.Cid) (*types.BlockHeader, error) ChainGetBlockMessages(context.Context, cid.Cid) ([]*types.SignedMessage, error) + // if tipset is nil, we'll use heaviest + ChainCall(context.Context, *types.Message, *types.TipSet) (*types.MessageReceipt, error) + // messages MpoolPending(context.Context, *types.TipSet) ([]*types.SignedMessage, error) diff --git a/api/struct.go b/api/struct.go index 82379f278..321376376 100644 --- a/api/struct.go +++ b/api/struct.go @@ -39,13 +39,14 @@ type FullNodeStruct struct { CommonStruct Internal struct { - ChainNotify func(context.Context) (<-chan *store.HeadChange, error) `perm:"read"` - ChainSubmitBlock func(ctx context.Context, blk *chain.BlockMsg) error `perm:"write"` - ChainHead func(context.Context) (*types.TipSet, error) `perm:"read"` - ChainGetRandomness func(context.Context, *types.TipSet) ([]byte, error) `perm:"read"` - ChainWaitMsg func(context.Context, cid.Cid) (*MsgWait, error) `perm:"read"` - ChainGetBlock func(context.Context, cid.Cid) (*types.BlockHeader, error) `perm:"read"` - ChainGetBlockMessages func(context.Context, cid.Cid) ([]*types.SignedMessage, error) `perm:"read"` + ChainNotify func(context.Context) (<-chan *store.HeadChange, error) `perm:"read"` + ChainSubmitBlock func(ctx context.Context, blk *chain.BlockMsg) error `perm:"write"` + ChainHead func(context.Context) (*types.TipSet, error) `perm:"read"` + ChainGetRandomness func(context.Context, *types.TipSet) ([]byte, error) `perm:"read"` + ChainWaitMsg func(context.Context, cid.Cid) (*MsgWait, error) `perm:"read"` + ChainGetBlock func(context.Context, cid.Cid) (*types.BlockHeader, error) `perm:"read"` + ChainGetBlockMessages func(context.Context, cid.Cid) ([]*types.SignedMessage, error) `perm:"read"` + ChainCall func(context.Context, *types.Message, *types.TipSet) (*types.MessageReceipt, error) `perm:"read"` MpoolPending func(context.Context, *types.TipSet) ([]*types.SignedMessage, error) `perm:"read"` MpoolPush func(context.Context, *types.SignedMessage) error `perm:"write"` @@ -155,6 +156,10 @@ func (c *FullNodeStruct) ChainWaitMsg(ctx context.Context, msgc cid.Cid) (*MsgWa return c.Internal.ChainWaitMsg(ctx, msgc) } +func (c *FullNodeStruct) ChainCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) { + return c.Internal.ChainCall(ctx, msg, ts) +} + func (c *FullNodeStruct) WalletNew(ctx context.Context, typ string) (address.Address, error) { return c.Internal.WalletNew(ctx, typ) } diff --git a/chain/actors/actor_miner.go b/chain/actors/actor_miner.go index 34d023048..f40f8bb40 100644 --- a/chain/actors/actor_miner.go +++ b/chain/actors/actor_miner.go @@ -118,7 +118,7 @@ func (sma StorageMinerActor) Exports() []interface{} { //6: sma.ArbitrateDeal, //7: sma.DePledge, //8: sma.GetOwner, - //9: sma.GetWorkerAddr, + 9: sma.GetWorkerAddr, 10: sma.GetPower, //11: sma.GetPeerID, //12: sma.GetSectorSize, @@ -275,3 +275,12 @@ func CollateralForPower(power types.BigInt) types.BigInt { return collateralRequired */ } + +func (sma StorageMinerActor) GetWorkerAddr(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) { + var self StorageMinerActorState + state := vmctx.Storage().GetHead() + if err := vmctx.Storage().Get(state, &self); err != nil { + return nil, err + } + return self.Worker.Bytes(), nil +} diff --git a/chain/address/address.go b/chain/address/address.go index 362477148..2ebec4fd6 100644 --- a/chain/address/address.go +++ b/chain/address/address.go @@ -62,10 +62,15 @@ const ( Actor // BLS represents the address BLS protocol. BLS + + Unknown = Protocol(255) ) // Protocol returns the protocol used by the address. func (a Address) Protocol() Protocol { + if len(a.str) == 0 { + return Unknown + } return a.str[0] } diff --git a/chain/state/statetree.go b/chain/state/statetree.go index 462918120..7239fc282 100644 --- a/chain/state/statetree.go +++ b/chain/state/statetree.go @@ -2,6 +2,7 @@ package state import ( "context" + "fmt" "github.com/ipfs/go-cid" hamt "github.com/ipfs/go-hamt-ipld" @@ -79,6 +80,10 @@ func (st *StateTree) lookupID(addr address.Address) (address.Address, error) { } func (st *StateTree) GetActor(addr address.Address) (*types.Actor, error) { + if addr == address.Undef { + return nil, fmt.Errorf("GetActor called on undefined address") + } + if addr.Protocol() != address.ID { iaddr, err := st.lookupID(addr) if err != nil { diff --git a/chain/types/bigint.go b/chain/types/bigint.go index 5e5b54dc5..23027c4ab 100644 --- a/chain/types/bigint.go +++ b/chain/types/bigint.go @@ -26,6 +26,8 @@ func init() { Complete()) } +var EmptyInt = BigInt{} + type BigInt struct { *big.Int } diff --git a/chain/wallet/wallet.go b/chain/wallet/wallet.go index c6cb3fc18..a94ba69ef 100644 --- a/chain/wallet/wallet.go +++ b/chain/wallet/wallet.go @@ -5,6 +5,8 @@ import ( "strings" "github.com/filecoin-project/go-bls-sigs" + "github.com/filecoin-project/go-lotus/node/repo" + "github.com/minio/blake2b-simd" "golang.org/x/xerrors" @@ -71,6 +73,9 @@ func (w *Wallet) findKey(addr address.Address) (*Key, error) { } ki, err := w.keystore.Get(KNamePrefix + addr.String()) if err != nil { + if xerrors.Is(err, repo.ErrKeyNotFound) { + return nil, nil + } return nil, xerrors.Errorf("getting from keystore: %w", err) } k, err = NewKey(ki) @@ -153,6 +158,14 @@ func (w *Wallet) GenerateKey(typ string) (address.Address, error) { return k.Address, nil } +func (w *Wallet) HasKey(addr address.Address) (bool, error) { + k, err := w.findKey(addr) + if err != nil { + return false, err + } + return k != nil, nil +} + type Key struct { types.KeyInfo diff --git a/cmd/lotus-storage-miner/init.go b/cmd/lotus-storage-miner/init.go index 0b15bb611..59e4a7ada 100644 --- a/cmd/lotus-storage-miner/init.go +++ b/cmd/lotus-storage-miner/init.go @@ -1,6 +1,7 @@ package main import ( + "github.com/ipfs/go-datastore" "github.com/libp2p/go-libp2p-core/peer" "golang.org/x/xerrors" "gopkg.in/urfave/cli.v2" @@ -166,9 +167,16 @@ var initCmd = &cli.Command{ return err } - // TODO: persist this address in the storage-miner repo log.Infof("New storage miners address is: %s", addr) + ds, err := lr.Datastore("/metadata") + if err != nil { + return err + } + if err := ds.Put(datastore.NewKey("miner-address"), addr.Bytes()); err != nil { + return err + } + // TODO: Point to setting storage price, maybe do it interactively or something log.Info("Storage miner successfully created, you can now start it with 'lotus-storage-miner run'") diff --git a/cmd/lotus-storage-miner/run.go b/cmd/lotus-storage-miner/run.go index 80df5015b..0e3d06932 100644 --- a/cmd/lotus-storage-miner/run.go +++ b/cmd/lotus-storage-miner/run.go @@ -74,6 +74,7 @@ var runCmd = &cli.Command{ return lr.SetAPIEndpoint(apima) }), node.Override(new(*sectorbuilder.SectorBuilderConfig), modules.SectorBuilderConfig(storageRepoPath)), + node.Override(new(api.FullNode), nodeApi), ) if err != nil { return err diff --git a/go.mod b/go.mod index d4ea9604b..454dc74d4 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,8 @@ go 1.12 require ( contrib.go.opencensus.io/exporter/jaeger v0.1.0 github.com/BurntSushi/toml v0.3.1 + github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect + github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 // indirect github.com/filecoin-project/go-bls-sigs v0.0.0-20190718224239-4bc4b8a7bbf8 github.com/filecoin-project/go-leb128 v0.0.0-20190212224330-8d79a5489543 github.com/filecoin-project/go-sectorbuilder v0.0.0-00010101000000-000000000000 @@ -60,6 +62,7 @@ require ( github.com/multiformats/go-multihash v0.0.6 github.com/pkg/errors v0.8.1 github.com/polydawn/refmt v0.0.0-20190408063855-01bf1e26dd14 + github.com/prometheus/common v0.6.0 github.com/smartystreets/assertions v1.0.1 // indirect github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect github.com/stretchr/testify v1.3.0 diff --git a/go.sum b/go.sum index 55488b54b..0f87ce619 100644 --- a/go.sum +++ b/go.sum @@ -14,11 +14,16 @@ github.com/Stebalien/go-bitfield v0.0.1 h1:X3kbSSPUaJK60wV2hjOPZwmpljr6VGCqdq4cB github.com/Stebalien/go-bitfield v0.0.1/go.mod h1:GNjFpasyUVkHMsfEOk8EFLJ9syQ6SI+XWrX9Wf2XH0s= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/apache/thrift v0.12.0 h1:pODnxUFNcjP9UTLZGTdeh+j16A8lJbRvD3rOtrk/7bs= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50 h1:4i3KsuVA0o0KoBxAC5x+MY7RbteiMK1V7gf/G08NGIQ= @@ -72,6 +77,7 @@ github.com/go-check/check v0.0.0-20180628173108-788fd7840127 h1:0gkP6mzaMqkmpcJY github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -221,6 +227,7 @@ github.com/jbenet/goprocess v0.1.3 h1:YKyIEECS/XvcfHtBzxtjBBbWK+MbvA6dG8ASiqwvr1 github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -228,6 +235,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b h1:wxtKgYHEncAU00muMD06dzLiahtGM1eouRNOzVV7tdQ= github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= @@ -402,6 +410,8 @@ github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2 h1:ZEw4I2EgPKDJ2iEw0cNmLB3ROrEmkOtXIkaG7wZg+78= @@ -453,11 +463,17 @@ github.com/polydawn/refmt v0.0.0-20190408063855-01bf1e26dd14 h1:2m16U/rLwVaRdz7A github.com/polydawn/refmt v0.0.0-20190408063855-01bf1e26dd14/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= @@ -575,6 +591,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= diff --git a/lib/sectorbuilder/poll.go b/lib/sectorbuilder/poll.go new file mode 100644 index 000000000..0e93efc16 --- /dev/null +++ b/lib/sectorbuilder/poll.go @@ -0,0 +1,57 @@ +package sectorbuilder + +import ( + "context" + "time" + + "github.com/prometheus/common/log" +) + +// TODO: really need to get a callbacks API from the rust-sectorbuilder +func (sb *SectorBuilder) pollForSealedSectors(ctx context.Context) { + watching := make(map[uint64]bool) + + staged, err := sb.GetAllStagedSectors() + if err != nil { + // TODO: this is probably worth shutting the miner down over until we + // have better recovery mechanisms + log.Errorf("failed to get staged sectors: %s", err) + } + for _, s := range staged { + watching[s.SectorID] = true + } + + tick := time.Tick(time.Second * 30) + for { + select { + case <-tick: + log.Info("polling for sealed sectors...") + + // add new staged sectors to watch list + staged, err := sb.GetAllStagedSectors() + if err != nil { + log.Errorf("in loop: failed to get staged sectors: %s", err) + continue + } + for _, s := range staged { + watching[s.SectorID] = true + } + + for s := range watching { + status, err := sb.SealStatus(s) + if err != nil { + log.Errorf("getting seal status: %s", err) + continue + } + + if status.SealStatusCode == 0 { // constant pls, zero implies the last step? + delete(watching, s) + sb.sschan <- status + } + } + case <-ctx.Done(): + close(sb.sschan) + return + } + } +} diff --git a/lib/sectorbuilder/sectorbuilder.go b/lib/sectorbuilder/sectorbuilder.go index 20bc8fb27..81d036e6e 100644 --- a/lib/sectorbuilder/sectorbuilder.go +++ b/lib/sectorbuilder/sectorbuilder.go @@ -1,6 +1,7 @@ package sectorbuilder import ( + "context" "unsafe" "github.com/filecoin-project/go-lotus/chain/address" @@ -15,6 +16,8 @@ const CommLen = sectorbuilder.CommitmentBytesLen type SectorBuilder struct { handle unsafe.Pointer + + sschan chan SectorSealingStatus } type SectorBuilderConfig struct { @@ -39,6 +42,10 @@ func New(cfg *SectorBuilderConfig) (*SectorBuilder, error) { }, nil } +func (sb *SectorBuilder) Run(ctx context.Context) { + go sb.pollForSealedSectors(ctx) +} + func (sb *SectorBuilder) Destroy() { sectorbuilder.DestroySectorBuilder(sb.handle) } @@ -70,6 +77,11 @@ func (sb *SectorBuilder) GeneratePoSt(sortedCommRs [][CommLen]byte, challengeSee return sectorbuilder.GeneratePoSt(sb.handle, sortedCommRs, challengeSeed) } +func (sb *SectorBuilder) SealedSectorChan() <-chan SectorSealingStatus { + // is this ever going to be multi-consumer? If so, switch to using pubsub/eventbus + return sb.sschan +} + var UserBytesForSectorSize = sectorbuilder.GetMaxUserBytesPerStagedSector func VerifySeal(sectorSize uint64, commR, commD, commRStar [CommLen]byte, proverID address.Address, sectorID uint64, proof []byte) (bool, error) { diff --git a/node/builder.go b/node/builder.go index 3167d1d5b..c41ea1e31 100644 --- a/node/builder.go +++ b/node/builder.go @@ -37,6 +37,7 @@ import ( "github.com/filecoin-project/go-lotus/node/modules/lp2p" "github.com/filecoin-project/go-lotus/node/modules/testing" "github.com/filecoin-project/go-lotus/node/repo" + "github.com/filecoin-project/go-lotus/storage" ) // special is a type used to give keys to modules which @@ -180,6 +181,9 @@ func Online() Option { libp2p(), + // common + Override(new(*wallet.Wallet), wallet.NewWallet), + // Full node ApplyIf(func(s *Settings) bool { return s.nodeType == nodeFull }, @@ -198,7 +202,6 @@ func Online() Option { // Filecoin services Override(new(*chain.Syncer), chain.NewSyncer), Override(new(*chain.BlockSync), chain.NewBlockSyncClient), - Override(new(*wallet.Wallet), wallet.NewWallet), Override(new(*chain.MessagePool), chain.NewMessagePool), Override(new(modules.Genesis), modules.ErrorGenesis), @@ -214,6 +217,7 @@ func Online() Option { // Storage miner ApplyIf(func(s *Settings) bool { return s.nodeType == nodeStorageMiner }, Override(new(*sectorbuilder.SectorBuilder), sectorbuilder.New), + Override(new(*storage.Miner), modules.StorageMiner), ), ) } diff --git a/node/impl/full.go b/node/impl/full.go index e08233248..9afd68e0e 100644 --- a/node/impl/full.go +++ b/node/impl/full.go @@ -11,6 +11,7 @@ import ( "github.com/filecoin-project/go-lotus/chain/gen" "github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/types" + "github.com/filecoin-project/go-lotus/chain/vm" "github.com/filecoin-project/go-lotus/chain/wallet" "github.com/filecoin-project/go-lotus/miner" "github.com/filecoin-project/go-lotus/node/client" @@ -85,6 +86,34 @@ func (a *FullNodeAPI) ChainGetBlockMessages(ctx context.Context, msg cid.Cid) ([ return a.Chain.MessagesForBlock(b) } +func (a *FullNodeAPI) ChainCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) { + if ts == nil { + ts = a.Chain.GetHeaviestTipSet() + } + state, err := a.Chain.TipSetState(ts.Cids()) + if err != nil { + return nil, err + } + + vmi, err := vm.NewVM(state, ts.Height(), ts.Blocks()[0].Miner, a.Chain) + if err != nil { + return nil, xerrors.Errorf("failed to set up vm: %w", err) + } + + if msg.GasLimit == types.EmptyInt { + msg.GasLimit = types.NewInt(10000000000) + } + if msg.GasPrice == types.EmptyInt { + msg.GasPrice = types.NewInt(0) + } + if msg.Value == types.EmptyInt { + msg.Value = types.NewInt(0) + } + + // TODO: maybe just use the invoker directly? + return vmi.ApplyMessage(ctx, msg) +} + func (a *FullNodeAPI) MpoolPending(ctx context.Context, ts *types.TipSet) ([]*types.SignedMessage, error) { // TODO: need to make sure we don't return messages that were already included in the referenced chain // also need to accept ts == nil just fine, assume nil == chain.Head() diff --git a/node/impl/storminer.go b/node/impl/storminer.go index 013d04f04..3219f9943 100644 --- a/node/impl/storminer.go +++ b/node/impl/storminer.go @@ -8,12 +8,15 @@ import ( "github.com/filecoin-project/go-lotus/api" "github.com/filecoin-project/go-lotus/lib/sectorbuilder" + "github.com/filecoin-project/go-lotus/storage" ) type StorageMinerAPI struct { CommonAPI SectorBuilder *sectorbuilder.SectorBuilder + + Miner *storage.Miner } func (sm *StorageMinerAPI) StoreGarbageData(ctx context.Context) (uint64, error) { diff --git a/node/modules/core.go b/node/modules/core.go index 96accfb74..0ddae1224 100644 --- a/node/modules/core.go +++ b/node/modules/core.go @@ -34,9 +34,11 @@ import ( "github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/types" + "github.com/filecoin-project/go-lotus/chain/wallet" "github.com/filecoin-project/go-lotus/lib/sectorbuilder" "github.com/filecoin-project/go-lotus/node/modules/helpers" "github.com/filecoin-project/go-lotus/node/repo" + "github.com/filecoin-project/go-lotus/storage" ) var log = logging.Logger("modules") @@ -237,12 +239,59 @@ func SectorBuilderConfig(storagePath string) func() (*sectorbuilder.SectorBuilde return nil, err } - return §orbuilder.SectorBuilderConfig{ + sb := §orbuilder.SectorBuilderConfig{ Miner: minerAddr, SectorSize: 1024, MetadataDir: metadata, SealedDir: sealed, StagedDir: staging, - }, nil + } + + return sb, nil } } + +func SectorBuilder(mctx helpers.MetricsCtx, lc fx.Lifecycle, sbc *sectorbuilder.SectorBuilderConfig) (*sectorbuilder.SectorBuilder, error) { + sb, err := sectorbuilder.New(sbc) + if err != nil { + return nil, err + } + + ctx := helpers.LifecycleCtx(mctx, lc) + + lc.Append(fx.Hook{ + OnStart: func(context.Context) error { + sb.Run(ctx) + return nil + }, + }) + + return sb, nil +} + +func StorageMiner(mctx helpers.MetricsCtx, lc fx.Lifecycle, api api.FullNode, h host.Host, ds datastore.Batching, sb *sectorbuilder.SectorBuilder, w *wallet.Wallet) (*storage.Miner, error) { + maddrb, err := ds.Get(datastore.NewKey("miner-address")) + if err != nil { + return nil, err + } + + maddr, err := address.NewFromBytes(maddrb) + if err != nil { + return nil, err + } + + sm, err := storage.NewMiner(api, maddr, h, ds, sb, w) + if err != nil { + return nil, err + } + + ctx := helpers.LifecycleCtx(mctx, lc) + + lc.Append(fx.Hook{ + OnStart: func(context.Context) error { + return sm.Run(ctx) + }, + }) + + return sm, nil +} diff --git a/storage/miner.go b/storage/miner.go new file mode 100644 index 000000000..2d99afa17 --- /dev/null +++ b/storage/miner.go @@ -0,0 +1,200 @@ +package storage + +import ( + "context" + "fmt" + + "github.com/filecoin-project/go-lotus/api" + "github.com/filecoin-project/go-lotus/chain/actors" + "github.com/filecoin-project/go-lotus/chain/address" + "github.com/filecoin-project/go-lotus/chain/store" + "github.com/filecoin-project/go-lotus/chain/types" + "github.com/filecoin-project/go-lotus/chain/wallet" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-datastore" + logging "github.com/ipfs/go-log" + host "github.com/libp2p/go-libp2p-core/host" + "github.com/pkg/errors" + + "github.com/filecoin-project/go-lotus/lib/sectorbuilder" +) + +var log = logging.Logger("storageminer") + +type Miner struct { + api storageMinerApi + + sb *sectorbuilder.SectorBuilder + + w *wallet.Wallet + + maddr address.Address + + worker address.Address + + h host.Host + + ds datastore.Batching +} + +type storageMinerApi interface { + // I think I want this... but this is tricky + //ReadState(ctx context.Context, addr address.Address) (????, error) + + // Call a read only method on actors (no interaction with the chain required) + ChainCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) + + MpoolPush(context.Context, *types.SignedMessage) error + MpoolGetNonce(context.Context, address.Address) (uint64, error) + + ChainWaitMsg(context.Context, cid.Cid) (*api.MsgWait, error) + ChainNotify(context.Context) (<-chan *store.HeadChange, error) +} + +func NewMiner(api storageMinerApi, addr address.Address, h host.Host, ds datastore.Batching, sb *sectorbuilder.SectorBuilder, w *wallet.Wallet) (*Miner, error) { + return &Miner{ + api: api, + maddr: addr, + h: h, + ds: ds, + sb: sb, + w: w, + }, nil +} + +func (m *Miner) Run(ctx context.Context) error { + if err := m.runPreflightChecks(ctx); err != nil { + return errors.Wrap(err, "miner preflight checks failed") + } + + go m.handlePostingSealedSectors(ctx) + go m.runPoSt(ctx) + return nil +} + +func (m *Miner) handlePostingSealedSectors(ctx context.Context) { + for { + select { + case sinfo, ok := <-m.sb.SealedSectorChan(): + if !ok { + // TODO: set some state variable so that this state can be + // visible via some status command + log.Warning("sealed sector channel closed, aborting process") + return + } + + if err := m.commitSector(ctx, sinfo); err != nil { + log.Errorf("failed to commit sector: %s", err) + continue + } + + case <-ctx.Done(): + log.Warning("exiting seal posting routine") + return + } + } +} + +func (m *Miner) commitSector(ctx context.Context, sinfo sectorbuilder.SectorSealingStatus) error { + params := &actors.CommitSectorParams{ + SectorId: types.NewInt(sinfo.SectorID), + CommD: sinfo.CommD[:], + CommR: sinfo.CommR[:], + CommRStar: sinfo.CommRStar[:], + Proof: sinfo.Proof, + } + enc, aerr := actors.SerializeParams(params) + if aerr != nil { + return errors.Wrap(aerr, "could not serialize commit sector parameters") + } + + msg := types.Message{ + To: m.maddr, + From: m.worker, + Method: actors.MAMethods.CommitSector, + Params: enc, + Value: types.NewInt(0), // TODO: need to ensure sufficient collateral + GasLimit: types.NewInt(10000 /* i dont know help */), + GasPrice: types.NewInt(1), + } + + nonce, err := m.api.MpoolGetNonce(ctx, m.worker) + if err != nil { + return errors.Wrap(err, "failed to get nonce") + } + + msg.Nonce = nonce + + data, err := msg.Serialize() + if err != nil { + return errors.Wrap(err, "serializing commit sector message") + } + + sig, err := m.w.Sign(m.worker, data) + if err != nil { + return errors.Wrap(err, "signing commit sector message") + } + + smsg := &types.SignedMessage{ + Message: msg, + Signature: *sig, + } + + if err := m.api.MpoolPush(ctx, smsg); err != nil { + return errors.Wrap(err, "pushing commit sector message to mpool") + } + + m.trackCommitSectorMessage(smsg) + return nil +} + +// make sure the message gets included in the chain successfully +func (m *Miner) trackCommitSectorMessage(smsg *types.SignedMessage) { + log.Warning("not currently tracking commit sector messages") +} + +func (m *Miner) runPoSt(ctx context.Context) { + log.Warning("dont care about posts yet") +} + +func (m *Miner) runPreflightChecks(ctx context.Context) error { + worker, err := m.getWorkerAddr(ctx) + if err != nil { + return err + } + + m.worker = worker + + // try signing something with that key to make sure we can + has, err := m.w.HasKey(worker) + if err != nil { + return errors.Wrap(err, "failed to check wallet for worker key") + } + + if !has { + return errors.New("key for worker not found in local wallet") + } + + log.Infof("starting up miner %s, worker addr %s", m.maddr, m.worker) + return nil +} + +func (m *Miner) getWorkerAddr(ctx context.Context) (address.Address, error) { + msg := &types.Message{ + To: m.maddr, + From: m.maddr, // it doesnt like it if we dont give it a from... probably should fix that + Method: actors.MAMethods.GetWorkerAddr, + Params: actors.EmptyStructCBOR, + } + + recpt, err := m.api.ChainCall(ctx, msg, nil) + if err != nil { + return address.Undef, errors.Wrapf(err, "calling getWorker(%s)", m.maddr) + } + + if recpt.ExitCode != 0 { + return address.Undef, fmt.Errorf("failed to call getWorker(%s): return %d", m.maddr, recpt.ExitCode) + } + + return address.NewFromBytes(recpt.Return) +}