diff --git a/.circleci/config.yml b/.circleci/config.yml index 10f5cd782..827c9f77b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -46,6 +46,15 @@ jobs: - go/mod-download - go/mod-tidy-check + build-all: + executor: golang + steps: + - install-deps + - prepare + - go/mod-download + - run: + command: make buildall + test: description: | Run tests with gotestsum. @@ -166,3 +175,4 @@ workflows: - test: codecov-upload: true - mod-tidy-check + - build-all \ No newline at end of file diff --git a/.gitignore b/.gitignore index b4d957e5c..a3ca66522 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,13 @@ /lotus /lotus-storage-miner /lotus-seal-worker +/lotus-seed /pond /townhall /fountain +/stats +/bench +/bench.json /lotuspond/front/node_modules /lotuspond/front/build /cmd/lotus-townhall/townhall/node_modules diff --git a/.gitmodules b/.gitmodules index d8e0ccfa6..a655f05b9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,4 @@ -[submodule "extern/go-bls-sigs"] - path = extern/go-bls-sigs - url = https://github.com/filecoin-project/go-bls-sigs.git -[submodule "extern/go-sectorbuilder"] - path = extern/go-sectorbuilder - url = https://github.com/filecoin-project/go-sectorbuilder +[submodule "extern/filecoin-ffi"] + path = extern/filecoin-ffi + url = https://github.com/filecoin-project/filecoin-ffi.git + branch = master diff --git a/Makefile b/Makefile index b2884b0ac..f2dc69c24 100644 --- a/Makefile +++ b/Makefile @@ -12,37 +12,21 @@ MODULES:= CLEAN:= -## BLS +## FFI -BLS_PATH:=extern/go-bls-sigs/ -BLS_DEPS:=libbls_signatures.a libbls_signatures.pc libbls_signatures.h -BLS_DEPS:=$(addprefix $(BLS_PATH),$(BLS_DEPS)) +FFI_PATH:=extern/filecoin-ffi/ +FFI_DEPS:=libfilecoin.a filecoin.pc filecoin.h +FFI_DEPS:=$(addprefix $(FFI_PATH),$(FFI_DEPS)) -$(BLS_DEPS): build/.bls-install ; +$(FFI_DEPS): build/.filecoin-install ; -build/.bls-install: $(BLS_PATH) - $(MAKE) -C $(BLS_PATH) $(BLS_DEPS:$(BLS_PATH)%=%) +build/.filecoin-install: $(FFI_PATH) + $(MAKE) -C $(FFI_PATH) $(FFI_DEPS:$(FFI_PATH)%=%) @touch $@ -MODULES+=$(BLS_PATH) -BUILD_DEPS+=build/.bls-install -CLEAN+=build/.bls-install - -## SECTOR BUILDER - -SECTOR_BUILDER_PATH:=extern/go-sectorbuilder/ -SECTOR_BUILDER_DEPS:=libsector_builder_ffi.a sector_builder_ffi.pc sector_builder_ffi.h -SECTOR_BUILDER_DEPS:=$(addprefix $(SECTOR_BUILDER_PATH),$(SECTOR_BUILDER_DEPS)) - -$(SECTOR_BUILDER_DEPS): build/.sector-builder-install ; - -build/.sector-builder-install: $(SECTOR_BUILDER_PATH) - $(MAKE) -C $(SECTOR_BUILDER_PATH) $(SECTOR_BUILDER_DEPS:$(SECTOR_BUILDER_PATH)%=%) - @touch $@ - -MODULES+=$(SECTOR_BUILDER_PATH) -BUILD_DEPS+=build/.sector-builder-install -CLEAN+=build/.sector-builder-install +MODULES+=$(FFI_PATH) +BUILD_DEPS+=build/.filecoin-install +CLEAN+=build/.filecoin-install $(MODULES): build/.update-modules ; @@ -53,43 +37,55 @@ build/.update-modules: # end git modules -## PROOFS +## MAIN BINARIES CLEAN+=build/.update-modules deps: $(BUILD_DEPS) .PHONY: deps +debug: GOFLAGS=-tags=debug +debug: lotus lotus-storage-miner + lotus: $(BUILD_DEPS) rm -f lotus - go build -o lotus ./cmd/lotus + go build $(GOFLAGS) -o lotus ./cmd/lotus go run github.com/GeertJohan/go.rice/rice append --exec lotus -i ./build .PHONY: lotus -CLEAN+=lotus +BINS+=lotus lotus-storage-miner: $(BUILD_DEPS) rm -f lotus-storage-miner - go build -o lotus-storage-miner ./cmd/lotus-storage-miner + go build $(GOFLAGS) -o lotus-storage-miner ./cmd/lotus-storage-miner go run github.com/GeertJohan/go.rice/rice append --exec lotus-storage-miner -i ./build .PHONY: lotus-storage-miner +BINS+=lotus-storage-miner lotus-seal-worker: $(BUILD_DEPS) rm -f lotus-seal-worker go build -o lotus-seal-worker ./cmd/lotus-seal-worker go run github.com/GeertJohan/go.rice/rice append --exec lotus-seal-worker -i ./build .PHONY: lotus-seal-worker - -CLEAN+=lotus-storage-miner +BINS+=lotus-seal-worker build: lotus lotus-storage-miner - .PHONY: build install: install -C ./lotus /usr/local/bin/lotus install -C ./lotus-storage-miner /usr/local/bin/lotus-storage-miner +# TOOLS + +lotus-seed: $(BUILD_DEPS) + rm -f lotus-seed + go build -o lotus-seed ./cmd/lotus-seed + go run github.com/GeertJohan/go.rice/rice append --exec lotus-seed -i ./build + +.PHONY: lotus-seed +BINS+=lotus-seed + benchmarks: go run github.com/whyrusleeping/bencher ./... > bench.json @echo Submitting results @@ -100,6 +96,7 @@ pond: build go build -o pond ./lotuspond (cd lotuspond/front && npm i && npm run build) .PHONY: pond +BINS+=pond townhall: rm -f townhall @@ -107,28 +104,42 @@ townhall: (cd ./cmd/lotus-townhall/townhall && npm i && npm run build) go run github.com/GeertJohan/go.rice/rice append --exec townhall -i ./cmd/lotus-townhall -i ./build .PHONY: townhall +BINS+=townhall fountain: rm -f fountain go build -o fountain ./cmd/lotus-fountain go run github.com/GeertJohan/go.rice/rice append --exec fountain -i ./cmd/lotus-fountain .PHONY: fountain +BINS+=fountain chainwatch: rm -f chainwatch go build -o chainwatch ./cmd/lotus-chainwatch go run github.com/GeertJohan/go.rice/rice append --exec chainwatch -i ./cmd/lotus-chainwatch .PHONY: chainwatch +BINS+=chainwatch + +bench: + rm -f bench + go build -o bench ./cmd/lotus-bench + go run github.com/GeertJohan/go.rice/rice append --exec bench -i ./build +.PHONY: bench +BINS+=bench stats: rm -f stats go build -o stats ./tools/stats .PHONY: stats +BINS+=stats + +# MISC + +buildall: $(BINS) clean: - rm -rf $(CLEAN) - -$(MAKE) -C $(BLS_PATH) clean - -$(MAKE) -C $(SECTOR_BUILDER_PATH) clean + rm -rf $(CLEAN) $(BINS) + -$(MAKE) -C $(FFI_PATH) clean .PHONY: clean dist-clean: diff --git a/api/api_full.go b/api/api_full.go index 1afd74fd9..2e0b81d1a 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -23,7 +23,7 @@ type FullNode interface { // First message is guaranteed to be of len == 1, and type == 'current' ChainNotify(context.Context) (<-chan []*store.HeadChange, error) ChainHead(context.Context) (*types.TipSet, error) - ChainGetRandomness(context.Context, types.TipSetKey, []*types.Ticket, int) ([]byte, error) + ChainGetRandomness(context.Context, types.TipSetKey, int64) ([]byte, error) ChainGetBlock(context.Context, cid.Cid) (*types.BlockHeader, error) ChainGetTipSet(context.Context, types.TipSetKey) (*types.TipSet, error) ChainGetBlockMessages(context.Context, cid.Cid) (*BlockMessages, error) @@ -51,10 +51,7 @@ type FullNode interface { // miner - MinerRegister(context.Context, address.Address) error - MinerUnregister(context.Context, address.Address) error - MinerAddresses(context.Context) ([]address.Address, error) - MinerCreateBlock(context.Context, address.Address, *types.TipSet, []*types.Ticket, types.ElectionProof, []*types.SignedMessage, uint64) (*types.BlockMsg, error) + MinerCreateBlock(context.Context, address.Address, *types.TipSet, *types.Ticket, *types.EPostProof, []*types.SignedMessage, uint64, uint64) (*types.BlockMsg, error) // // UX ? @@ -102,7 +99,7 @@ type FullNode interface { StateMinerPower(context.Context, address.Address, *types.TipSet) (MinerPower, error) StateMinerWorker(context.Context, address.Address, *types.TipSet) (address.Address, error) StateMinerPeerID(ctx context.Context, m address.Address, ts *types.TipSet) (peer.ID, error) - StateMinerProvingPeriodEnd(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) + StateMinerElectionPeriodStart(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) StateMinerSectorSize(context.Context, address.Address, *types.TipSet) (uint64, error) StatePledgeCollateral(context.Context, *types.TipSet) (types.BigInt, error) StateWaitMsg(context.Context, cid.Cid) (*MsgWait, error) diff --git a/api/struct.go b/api/struct.go index d36fd1698..3c91518bb 100644 --- a/api/struct.go +++ b/api/struct.go @@ -38,19 +38,19 @@ type FullNodeStruct struct { CommonStruct Internal struct { - ChainNotify func(context.Context) (<-chan []*store.HeadChange, error) `perm:"read"` - ChainHead func(context.Context) (*types.TipSet, error) `perm:"read"` - ChainGetRandomness func(context.Context, types.TipSetKey, []*types.Ticket, int) ([]byte, error) `perm:"read"` - ChainGetBlock func(context.Context, cid.Cid) (*types.BlockHeader, error) `perm:"read"` - ChainGetTipSet func(context.Context, types.TipSetKey) (*types.TipSet, error) `perm:"read"` - ChainGetBlockMessages func(context.Context, cid.Cid) (*BlockMessages, error) `perm:"read"` - ChainGetParentReceipts func(context.Context, cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"` - ChainGetParentMessages func(context.Context, cid.Cid) ([]Message, error) `perm:"read"` - ChainGetTipSetByHeight func(context.Context, uint64, *types.TipSet) (*types.TipSet, error) `perm:"read"` - ChainReadObj func(context.Context, cid.Cid) ([]byte, error) `perm:"read"` - ChainSetHead func(context.Context, *types.TipSet) error `perm:"admin"` - ChainGetGenesis func(context.Context) (*types.TipSet, error) `perm:"read"` - ChainTipSetWeight func(context.Context, *types.TipSet) (types.BigInt, error) `perm:"read"` + ChainNotify func(context.Context) (<-chan []*store.HeadChange, error) `perm:"read"` + ChainHead func(context.Context) (*types.TipSet, error) `perm:"read"` + ChainGetRandomness func(context.Context, types.TipSetKey, int64) ([]byte, error) `perm:"read"` + ChainGetBlock func(context.Context, cid.Cid) (*types.BlockHeader, error) `perm:"read"` + ChainGetTipSet func(context.Context, types.TipSetKey) (*types.TipSet, error) `perm:"read"` + ChainGetBlockMessages func(context.Context, cid.Cid) (*BlockMessages, error) `perm:"read"` + ChainGetParentReceipts func(context.Context, cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"` + ChainGetParentMessages func(context.Context, cid.Cid) ([]Message, error) `perm:"read"` + ChainGetTipSetByHeight func(context.Context, uint64, *types.TipSet) (*types.TipSet, error) `perm:"read"` + ChainReadObj func(context.Context, cid.Cid) ([]byte, error) `perm:"read"` + ChainSetHead func(context.Context, *types.TipSet) error `perm:"admin"` + ChainGetGenesis func(context.Context) (*types.TipSet, error) `perm:"read"` + ChainTipSetWeight func(context.Context, *types.TipSet) (types.BigInt, error) `perm:"read"` SyncState func(context.Context) (*SyncState, error) `perm:"read"` SyncSubmitBlock func(ctx context.Context, blk *types.BlockMsg) error `perm:"write"` @@ -62,10 +62,7 @@ type FullNodeStruct struct { MpoolGetNonce func(context.Context, address.Address) (uint64, error) `perm:"read"` MpoolSub func(context.Context) (<-chan MpoolUpdate, error) `perm:"read"` - MinerRegister func(context.Context, address.Address) error `perm:"admin"` - MinerUnregister func(context.Context, address.Address) error `perm:"admin"` - MinerAddresses func(context.Context) ([]address.Address, error) `perm:"write"` - MinerCreateBlock func(context.Context, address.Address, *types.TipSet, []*types.Ticket, types.ElectionProof, []*types.SignedMessage, uint64) (*types.BlockMsg, error) `perm:"write"` + MinerCreateBlock func(context.Context, address.Address, *types.TipSet, *types.Ticket, *types.EPostProof, []*types.SignedMessage, uint64, uint64) (*types.BlockMsg, error) `perm:"write"` WalletNew func(context.Context, string) (address.Address, error) `perm:"write"` WalletHas func(context.Context, address.Address) (bool, error) `perm:"write"` @@ -88,28 +85,28 @@ type FullNodeStruct struct { ClientRetrieve func(ctx context.Context, order RetrievalOrder, path string) error `perm:"admin"` ClientQueryAsk func(ctx context.Context, p peer.ID, miner address.Address) (*types.SignedStorageAsk, error) `perm:"read"` - StateMinerSectors func(context.Context, address.Address, *types.TipSet) ([]*ChainSectorInfo, error) `perm:"read"` - StateMinerProvingSet func(context.Context, address.Address, *types.TipSet) ([]*ChainSectorInfo, error) `perm:"read"` - StateMinerPower func(context.Context, address.Address, *types.TipSet) (MinerPower, error) `perm:"read"` - StateMinerWorker func(context.Context, address.Address, *types.TipSet) (address.Address, error) `perm:"read"` - StateMinerPeerID func(ctx context.Context, m address.Address, ts *types.TipSet) (peer.ID, error) `perm:"read"` - StateMinerProvingPeriodEnd func(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) `perm:"read"` - StateMinerSectorSize func(context.Context, address.Address, *types.TipSet) (uint64, error) `perm:"read"` - StateCall func(context.Context, *types.Message, *types.TipSet) (*types.MessageReceipt, error) `perm:"read"` - StateReplay func(context.Context, *types.TipSet, cid.Cid) (*ReplayResults, error) `perm:"read"` - StateGetActor func(context.Context, address.Address, *types.TipSet) (*types.Actor, error) `perm:"read"` - StateReadState func(context.Context, *types.Actor, *types.TipSet) (*ActorState, error) `perm:"read"` - StatePledgeCollateral func(context.Context, *types.TipSet) (types.BigInt, error) `perm:"read"` - StateWaitMsg func(context.Context, cid.Cid) (*MsgWait, error) `perm:"read"` - StateListMiners func(context.Context, *types.TipSet) ([]address.Address, error) `perm:"read"` - StateListActors func(context.Context, *types.TipSet) ([]address.Address, error) `perm:"read"` - StateMarketBalance func(context.Context, address.Address, *types.TipSet) (actors.StorageParticipantBalance, error) `perm:"read"` - StateMarketParticipants func(context.Context, *types.TipSet) (map[string]actors.StorageParticipantBalance, error) `perm:"read"` - StateMarketDeals func(context.Context, *types.TipSet) (map[string]actors.OnChainDeal, error) `perm:"read"` - StateMarketStorageDeal func(context.Context, uint64, *types.TipSet) (*actors.OnChainDeal, error) `perm:"read"` - StateLookupID func(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) `perm:"read"` - StateChangedActors func(context.Context, cid.Cid, cid.Cid) (map[string]types.Actor, error) `perm:"read"` - StateGetReceipt func(context.Context, cid.Cid, *types.TipSet) (*types.MessageReceipt, error) `perm:"read"` + StateMinerSectors func(context.Context, address.Address, *types.TipSet) ([]*ChainSectorInfo, error) `perm:"read"` + StateMinerProvingSet func(context.Context, address.Address, *types.TipSet) ([]*ChainSectorInfo, error) `perm:"read"` + StateMinerPower func(context.Context, address.Address, *types.TipSet) (MinerPower, error) `perm:"read"` + StateMinerWorker func(context.Context, address.Address, *types.TipSet) (address.Address, error) `perm:"read"` + StateMinerPeerID func(ctx context.Context, m address.Address, ts *types.TipSet) (peer.ID, error) `perm:"read"` + StateMinerElectionPeriodStart func(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) `perm:"read"` + StateMinerSectorSize func(context.Context, address.Address, *types.TipSet) (uint64, error) `perm:"read"` + StateCall func(context.Context, *types.Message, *types.TipSet) (*types.MessageReceipt, error) `perm:"read"` + StateReplay func(context.Context, *types.TipSet, cid.Cid) (*ReplayResults, error) `perm:"read"` + StateGetActor func(context.Context, address.Address, *types.TipSet) (*types.Actor, error) `perm:"read"` + StateReadState func(context.Context, *types.Actor, *types.TipSet) (*ActorState, error) `perm:"read"` + StatePledgeCollateral func(context.Context, *types.TipSet) (types.BigInt, error) `perm:"read"` + StateWaitMsg func(context.Context, cid.Cid) (*MsgWait, error) `perm:"read"` + StateListMiners func(context.Context, *types.TipSet) ([]address.Address, error) `perm:"read"` + StateListActors func(context.Context, *types.TipSet) ([]address.Address, error) `perm:"read"` + StateMarketBalance func(context.Context, address.Address, *types.TipSet) (actors.StorageParticipantBalance, error) `perm:"read"` + StateMarketParticipants func(context.Context, *types.TipSet) (map[string]actors.StorageParticipantBalance, error) `perm:"read"` + StateMarketDeals func(context.Context, *types.TipSet) (map[string]actors.OnChainDeal, error) `perm:"read"` + StateMarketStorageDeal func(context.Context, uint64, *types.TipSet) (*actors.OnChainDeal, error) `perm:"read"` + StateLookupID func(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) `perm:"read"` + StateChangedActors func(context.Context, cid.Cid, cid.Cid) (map[string]types.Actor, error) `perm:"read"` + StateGetReceipt func(context.Context, cid.Cid, *types.TipSet) (*types.MessageReceipt, error) `perm:"read"` MarketEnsureAvailable func(context.Context, address.Address, types.BigInt) error `perm:"sign"` @@ -238,28 +235,16 @@ func (c *FullNodeStruct) MpoolSub(ctx context.Context) (<-chan MpoolUpdate, erro return c.Internal.MpoolSub(ctx) } -func (c *FullNodeStruct) MinerRegister(ctx context.Context, addr address.Address) error { - return c.Internal.MinerRegister(ctx, addr) -} - -func (c *FullNodeStruct) MinerUnregister(ctx context.Context, addr address.Address) error { - return c.Internal.MinerUnregister(ctx, addr) -} - -func (c *FullNodeStruct) MinerAddresses(ctx context.Context) ([]address.Address, error) { - return c.Internal.MinerAddresses(ctx) -} - -func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, addr address.Address, base *types.TipSet, tickets []*types.Ticket, eproof types.ElectionProof, msgs []*types.SignedMessage, ts uint64) (*types.BlockMsg, error) { - return c.Internal.MinerCreateBlock(ctx, addr, base, tickets, eproof, msgs, ts) +func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, addr address.Address, base *types.TipSet, ticket *types.Ticket, eproof *types.EPostProof, msgs []*types.SignedMessage, height, ts uint64) (*types.BlockMsg, error) { + return c.Internal.MinerCreateBlock(ctx, addr, base, ticket, eproof, msgs, height, ts) } func (c *FullNodeStruct) ChainHead(ctx context.Context) (*types.TipSet, error) { return c.Internal.ChainHead(ctx) } -func (c *FullNodeStruct) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, ticks []*types.Ticket, lb int) ([]byte, error) { - return c.Internal.ChainGetRandomness(ctx, pts, ticks, lb) +func (c *FullNodeStruct) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, round int64) ([]byte, error) { + return c.Internal.ChainGetRandomness(ctx, pts, round) } func (c *FullNodeStruct) ChainGetTipSetByHeight(ctx context.Context, h uint64, ts *types.TipSet) (*types.TipSet, error) { @@ -382,8 +367,8 @@ func (c *FullNodeStruct) StateMinerPeerID(ctx context.Context, m address.Address return c.Internal.StateMinerPeerID(ctx, m, ts) } -func (c *FullNodeStruct) StateMinerProvingPeriodEnd(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) { - return c.Internal.StateMinerProvingPeriodEnd(ctx, actor, ts) +func (c *FullNodeStruct) StateMinerElectionPeriodStart(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) { + return c.Internal.StateMinerElectionPeriodStart(ctx, actor, ts) } func (c *FullNodeStruct) StateMinerSectorSize(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) { diff --git a/api/test/deals.go b/api/test/deals.go index cb3002459..8dbde5a78 100644 --- a/api/test/deals.go +++ b/api/test/deals.go @@ -42,7 +42,7 @@ func TestDealFlow(t *testing.T, b APIBuilder) { t.Fatal(err) } - maddr, err := address.NewFromString("t0101") + maddr, err := address.NewFromString("t0102") if err != nil { t.Fatal(err) } diff --git a/build/bootstrap.go b/build/bootstrap.go index 9f8dd6883..9d9c3e9f1 100644 --- a/build/bootstrap.go +++ b/build/bootstrap.go @@ -2,10 +2,11 @@ package build import ( "context" - "github.com/filecoin-project/lotus/lib/addrutil" "os" "strings" + "github.com/filecoin-project/lotus/lib/addrutil" + rice "github.com/GeertJohan/go.rice" "github.com/libp2p/go-libp2p-core/peer" ) diff --git a/build/bootstrap/bootstrappers.pi b/build/bootstrap/bootstrappers.pi deleted file mode 100644 index 27abfeb3f..000000000 --- a/build/bootstrap/bootstrappers.pi +++ /dev/null @@ -1 +0,0 @@ -/ip4/147.75.80.17/tcp/1347/p2p/12D3KooWFCkQdiJEMBVA6RrWq22ZXVFfM41YX8soQ5QVvNFjMJT8 diff --git a/build/bootstrap/root.pi b/build/bootstrap/root.pi deleted file mode 100644 index af18e8d88..000000000 --- a/build/bootstrap/root.pi +++ /dev/null @@ -1 +0,0 @@ -/ip4/147.75.80.29/tcp/1347/p2p/12D3KooWSw9h3e6YrYZfRWDcir8qMV7ctZG9VmtXwSaP2ntsKXYf diff --git a/build/paramfetch.go b/build/paramfetch.go index 21d590f2c..b167f1ca5 100644 --- a/build/paramfetch.go +++ b/build/paramfetch.go @@ -156,6 +156,8 @@ func doFetch(out string, info paramFile) error { if err != nil { return err } + log.Infof("GET %s", url) + req := http.Request{ Method: "GET", URL: url, diff --git a/build/params_debug.go b/build/params_debug.go new file mode 100644 index 000000000..7186ee478 --- /dev/null +++ b/build/params_debug.go @@ -0,0 +1,27 @@ +// +build debug + +package build + +import "os" + +// Seconds +const BlockDelay = 6 + +// FallbackPoStDelay is the number of epochs the miner needs to wait after +// ElectionPeriodStart before starting fallback post computation +// +// Epochs +const FallbackPoStDelay = 10 + +// SlashablePowerDelay is the number of epochs after ElectionPeriodStart, after +// which the miner is slashed +// +// Epochs +const SlashablePowerDelay = 20 + +// Epochs +const InteractivePoRepDelay = 2 + +func init() { + os.Setenv("TRUST_PARAMS", "1") +} diff --git a/build/params_devnet.go b/build/params_devnet.go new file mode 100644 index 000000000..2a18b9372 --- /dev/null +++ b/build/params_devnet.go @@ -0,0 +1,21 @@ +// +build !debug + +package build + +// Seconds +const BlockDelay = 12 + +// FallbackPoStDelay is the number of epochs the miner needs to wait after +// ElectionPeriodStart before starting fallback post computation +// +// Epochs +const FallbackPoStDelay = 1000 + +// SlashablePowerDelay is the number of epochs after ElectionPeriodStart, after +// which the miner is slashed +// +// Epochs +const SlashablePowerDelay = 2000 + +// Epochs +const InteractivePoRepDelay = 10 diff --git a/build/params.go b/build/params_shared.go similarity index 70% rename from build/params.go rename to build/params_shared.go index eb2d2a167..0134084bf 100644 --- a/build/params.go +++ b/build/params_shared.go @@ -13,6 +13,7 @@ const UnixfsChunkSize uint64 = 1 << 20 const UnixfsLinksPerLevel = 1024 var SectorSizes = []uint64{ + 1 << 10, 16 << 20, 256 << 20, 1 << 30, @@ -30,25 +31,22 @@ func SupportedSectorSize(ssize uint64) bool { // ///// // Payments -// Blocks +// Epochs const PaymentChannelClosingDelay = 6 * 60 * 2 // six hours // ///// // Consensus / Network -// Seconds -const BlockDelay = 2 - // Seconds const AllowableClockDrift = BlockDelay * 2 -// Blocks +// Epochs const ForkLengthThreshold = Finality // Blocks (e) const BlocksPerEpoch = 5 -// Blocks +// Epochs const Finality = 500 // constants for Weight calculation @@ -59,39 +57,27 @@ const WRatioDen = 2 // ///// // Proofs -// Blocks -const ProvingPeriodDuration uint64 = 30 - -// PoStChallangeTime sets the window in which post computation should happen -// Blocks -const PoStChallangeTime = ProvingPeriodDuration - 6 - -// PoStRandomnessLookback is additional randomness lookback for PoSt computation -// To compute randomness epoch in a given proving period: -// RandH = PPE - PoStChallangeTime - PoStRandomnessLookback -// -// Blocks -const PoStRandomnessLookback = 1 - -// Blocks +// Epochs const SealRandomnessLookback = Finality -// Blocks +// Epochs const SealRandomnessLookbackLimit = SealRandomnessLookback + 2000 +// 1 / n +const SectorChallengeRatioDiv = 25 + +const MaxFallbackPostChallengeCount = 10 + // ///// // Mining -// Blocks +// Epochs const EcRandomnessLookback = 300 const PowerCollateralProportion = 5 const PerCapitaCollateralProportion = 1 const CollateralPrecision = 1000 -// Blocks -const InteractivePoRepDelay = 10 - // ///// // Devnet settings @@ -105,8 +91,8 @@ var InitialReward *big.Int const FilecoinPrecision = 1_000_000_000_000_000_000 // six years -// Blocks -const HalvingPeriodBlocks = 6 * 365 * 24 * 60 * 2 +// Epochs +const HalvingPeriodEpochs = 6 * 365 * 24 * 60 * 2 // TODO: Move other important consts here @@ -124,6 +110,6 @@ func init() { // Sync const BadBlockCacheSize = 1 << 15 -// assuming 4000 blocks per round, this lets us not lose any messages across a +// assuming 4000 messages per round, this lets us not lose any messages across a // 10 block reorg. const BlsSignatureCacheSize = 40000 diff --git a/build/proof-params/parameters.json b/build/proof-params/parameters.json index e12686127..0fc8abffb 100644 --- a/build/proof-params/parameters.json +++ b/build/proof-params/parameters.json @@ -1,82 +1,82 @@ { - "v15-proof-of-spacetime-rational-535d1050e3adca2a0dfe6c3c0c4fa12097c9a7835fb969042f82a507b13310e0.params": { - "cid": "QmT22f1Np1GpW29NXD7Zrv3Ae4poMYhmkDjyscqL8QrJXY", - "digest": "989fd8d989e0f7f1fe21bb010cf1b231", - "sector_size": 16777216 - }, - "v15-proof-of-spacetime-rational-535d1050e3adca2a0dfe6c3c0c4fa12097c9a7835fb969042f82a507b13310e0.vk": { - "cid": "QmVqSdc23to4UwduCCb25223rpSccvtcgPMfRKY1qjucDc", - "digest": "c6d258c37243b8544238a98100e3e399", - "sector_size": 16777216 - }, - "v15-proof-of-spacetime-rational-b99f15d0bdaaf4ffb68b2ca72b69ea8d915f66a2a56f667430ad69d87aa5febd.params": { - "cid": "QmRTCqgokEGTMfWVaSr7qFXTNotmpd2QBEi8RsvSQKmPLz", - "digest": "ff77a5e270afc6e1c7fbc19e48348fac", - "sector_size": 1073741824 - }, - "v15-proof-of-spacetime-rational-b99f15d0bdaaf4ffb68b2ca72b69ea8d915f66a2a56f667430ad69d87aa5febd.vk": { - "cid": "QmRssVAXRN3xp9VdSpTq1pNjkob3QiikoFZiM5hqrmh1VU", - "digest": "b41f35ac26224258e366327716a835a4", - "sector_size": 1073741824 - }, - "v15-proof-of-spacetime-rational-ba14a058a9dea194f68596f8ecf6537074f038a15c8d1a8550e10e31d4728912.params": { - "cid": "QmYNVRVzjXkuxJfnHTU5vmEcUBQf8dabXZ4m53SzqMkBv5", - "digest": "d156b685e4a1fe3a1f7230b6a39b5ad4", - "sector_size": 1024 - }, - "v15-proof-of-spacetime-rational-ba14a058a9dea194f68596f8ecf6537074f038a15c8d1a8550e10e31d4728912.vk": { - "cid": "QmaCEcsCFVuepMKdC5WURbr5ucEyLMNGxQaB7HqSnr2KGh", - "digest": "06ff067ac78cdab5d7bbc82170882241", - "sector_size": 1024 - }, - "v15-proof-of-spacetime-rational-c2ae2b440e693ee69fd6da9e85c4294c5c70c1a46d5785ca5f2a676d6cd4c8de.params": { - "cid": "QmVuabRvJ797NwLisGKwRURASGxopBBgg4rfNsbZoSYzAc", - "digest": "0e1ceb79a459a60508f480e5b1fed7ac", + "v19-proof-of-spacetime-election-09ae025de08399327e14f0cb6b4c907b6fe1e8b77046e31de8921bde588de900.params": { + "cid": "QmZEKhzKbC7SPngjL85ghyuxabPmiEuUh4fpkC7CDK1J5q", + "digest": "1c81338b8afeaae514fd5d6c08c9c6e5", "sector_size": 268435456 }, - "v15-proof-of-spacetime-rational-c2ae2b440e693ee69fd6da9e85c4294c5c70c1a46d5785ca5f2a676d6cd4c8de.vk": { - "cid": "QmdWENZBAbuUty1vVNn9vmvj1XbJ5UC8qzpcVD35s5AJxG", - "digest": "1b755c74b9d6823c014f6a7ef76249f2", + "v19-proof-of-spacetime-election-09ae025de08399327e14f0cb6b4c907b6fe1e8b77046e31de8921bde588de900.vk": { + "cid": "Qmf5XQuM58jNmxudXeZMuZtGGiNzcd56Fiyn9h76wpX5dN", + "digest": "bb0f07b6071cd28e9348223c6a9c46d1", "sector_size": 268435456 }, - "v15-stacked-proof-of-replication-0c0b444c6f31d11c8e98003cc99a3b938db26b77a296d4253cda8945c234266d.params": { - "cid": "QmZDVpWTw5Eti5pE7N5z1Cmqsw8hPXhUcvG3cQuceK56LH", - "digest": "6aa80306018ea1328f2d6faf8c080734", - "sector_size": 16777216 - }, - "v15-stacked-proof-of-replication-0c0b444c6f31d11c8e98003cc99a3b938db26b77a296d4253cda8945c234266d.vk": { - "cid": "QmaoXV7iVSJcfZ5qubYy7NBcXDSdnTzxH85d7M4bdDtfGZ", - "digest": "f6832eb736faf2960e920d32e9780b12", - "sector_size": 16777216 - }, - "v15-stacked-proof-of-replication-967b11bb59be11b7dc6f2b627520ba450a3aa50846dbbf886cb8b735fe25c4e7.params": { - "cid": "QmbUW3a3q5DHBb7Ufk8iSbnSCZgbwpe3serqfwKmcTd11w", - "digest": "64024e461b07c869df2463d33dd28035", - "sector_size": 268435456 - }, - "v15-stacked-proof-of-replication-967b11bb59be11b7dc6f2b627520ba450a3aa50846dbbf886cb8b735fe25c4e7.vk": { - "cid": "Qme3QgBBE7hUgrK7G9ZfJhzkbvViN5HALFpFduYs5K1piv", - "digest": "32496f4dc434b0ed9ef49cb62497a7d1", - "sector_size": 268435456 - }, - "v15-stacked-proof-of-replication-d01cd22091627b721c60a3375b5219af653fb9f6928c70aa7400587d396bc07a.params": { - "cid": "QmZzgJmb8WXYDKxS22HDgnoBYcZzXDC7s2c2zsV7kouNZ9", - "digest": "cd91f7ccb2ff57a06f3375946dcbdc68", - "sector_size": 1073741824 - }, - "v15-stacked-proof-of-replication-d01cd22091627b721c60a3375b5219af653fb9f6928c70aa7400587d396bc07a.vk": { - "cid": "QmRUMVzFnENbvyNb6aN2AJ2dnnewr1ESGA1UQLMVZZdsJM", - "digest": "92fc84b76dbe69c731518aebcb82ac82", - "sector_size": 1073741824 - }, - "v15-stacked-proof-of-replication-f464b92d805d03de6e2c20e2530135b2c8ec96045ec58f342d6feb90282bff8a.params": { - "cid": "QmSixsGkxJXTAuFfWKy5aEXEDJEnpcb1GkdQVF8TCPWoHy", - "digest": "f8339ae93478ded3840d0bc7efa19953", + "v19-proof-of-spacetime-election-4a2342062706429612fac099694f77294e355c6c9265b80feaff12a0268b0a92.params": { + "cid": "QmaHs5CHcSD6QhaGAp4ysJP4HTZaxPxybCGmUVDk3TNocA", + "digest": "727fb2896a5668d04ba6e0ce71eb50d7", "sector_size": 1024 }, - "v15-stacked-proof-of-replication-f464b92d805d03de6e2c20e2530135b2c8ec96045ec58f342d6feb90282bff8a.vk": { - "cid": "QmTMC8hdZ2TkZ9BFuzHzRLM9SuR2PQdUrSAwABeCuHyV2f", - "digest": "f27f08ce1246ee6612c250bb12803ef1", + "v19-proof-of-spacetime-election-4a2342062706429612fac099694f77294e355c6c9265b80feaff12a0268b0a92.vk": { + "cid": "QmVg8mUXMb6MiZQseAyCmHzEgNkPbV72xQoRmdFr1yJA4w", + "digest": "3fdf4e65a7baf1a2bab5b8a717f3379a", "sector_size": 1024 + }, + "v19-proof-of-spacetime-election-512f5e6dc00a37fa13c8b0e468188f85957b7bf1ab36d17fb9fe9ed49ae8d657.params": { + "cid": "QmYf1532WoeXhy8AoduWNxpBuwn5DEkpU6YFDGCXh1mqBX", + "digest": "983e641f9df01799bc33d5fb3c3020b2", + "sector_size": 1073741824 + }, + "v19-proof-of-spacetime-election-512f5e6dc00a37fa13c8b0e468188f85957b7bf1ab36d17fb9fe9ed49ae8d657.vk": { + "cid": "QmVig7LUpNSXUcfjkSxUKsFaxqYxEZdaEARWvi14sbihJ4", + "digest": "e1f7a46b60217f1dddf56671dd86e6a7", + "sector_size": 1073741824 + }, + "v19-proof-of-spacetime-election-6c7cbfe7eed40b6c0b23a213a70648770aed65d9ca03ae85451573c18532304b.params": { + "cid": "QmSTF7C6vQbV6qjEQknXpBDuixBkxYeMshX25NonjJxjbi", + "digest": "b434ece6a37e588ca11ed117f1c14325", + "sector_size": 16777216 + }, + "v19-proof-of-spacetime-election-6c7cbfe7eed40b6c0b23a213a70648770aed65d9ca03ae85451573c18532304b.vk": { + "cid": "QmaFV9n5scuYxKc9QpJnbBUf4sjhkUzpZ7QkoTqL8XsNQz", + "digest": "9c2e40b6635470d3674324b01c9a3976", + "sector_size": 16777216 + }, + "v19-stacked-proof-of-replication-5a438611c880423c4f5199787cd531b197846ef46af40af69222467ab9073226.params": { + "cid": "QmQb5mc3ksZh2K5GsNwJrkwuHoWXh8adBdJV3qTbFaRvGe", + "digest": "a187287b1d03cd7ec4f1fccd57f3f3d1", + "sector_size": 1024 + }, + "v19-stacked-proof-of-replication-5a438611c880423c4f5199787cd531b197846ef46af40af69222467ab9073226.vk": { + "cid": "QmYUjFHzeX22dfzv9wQxF4Qn8wS67bqBwr1Wcz2rga9rAZ", + "digest": "747792363f08d7b53be1a2f51f91582a", + "sector_size": 1024 + }, + "v19-stacked-proof-of-replication-6ae8ae8998ef393ffd171487bc5141fa3642e9fd39e3a7dbada4f6e7bacffb9b.params": { + "cid": "QmRUZqCwYdcVfQ49Z97g2xkJnaSh4b9SHWwfB3kgZiPo9L", + "digest": "b9494e0ae432a0ebde9c8c877c914583", + "sector_size": 16777216 + }, + "v19-stacked-proof-of-replication-6ae8ae8998ef393ffd171487bc5141fa3642e9fd39e3a7dbada4f6e7bacffb9b.vk": { + "cid": "QmXngrBy74h8LYhYrbBpefXsXQFWLU3WX3LTXnVQnu1Sdc", + "digest": "af2c0d6834fa581b6f507f8266244dfb", + "sector_size": 16777216 + }, + "v19-stacked-proof-of-replication-d2ca0f634aebcecba88904612ff82f2349b080b1290879f3fba73c1d9a13d84e.params": { + "cid": "QmfNstuJFKnBt4yJHsNfoKahn1LafBdpJju23U5UNZd9Xy", + "digest": "3911d84ca2b86f491bc7c6372d7d9285", + "sector_size": 268435456 + }, + "v19-stacked-proof-of-replication-d2ca0f634aebcecba88904612ff82f2349b080b1290879f3fba73c1d9a13d84e.vk": { + "cid": "QmQgSRQBbp7udHDp5pNA3GSCjSyXktUHBw15wx9meL4wgc", + "digest": "ecd2a9bdd178b0ebc9110f568fd70d07", + "sector_size": 268435456 + }, + "v19-stacked-proof-of-replication-f7b95455d6b7a5e967388a97c2ddc01807eff4c1736e84be4554853bf7783105.params": { + "cid": "QmV5mAkhohUHPRWoNtS3Uo4yvmF23CR2u8JxeeCqthMCdX", + "digest": "9306d91c3518b87016d5efc19428b25e", + "sector_size": 1073741824 + }, + "v19-stacked-proof-of-replication-f7b95455d6b7a5e967388a97c2ddc01807eff4c1736e84be4554853bf7783105.vk": { + "cid": "QmPjwPdUQJmqp2rQuJX2dy57AC9YCwYtMHdpZzW5BG9JWh", + "digest": "a2004fca043da423df51f6c4bb65788c", + "sector_size": 1073741824 } } \ No newline at end of file diff --git a/build/testing_flags.go b/build/testing_flags.go new file mode 100644 index 000000000..1f26121e7 --- /dev/null +++ b/build/testing_flags.go @@ -0,0 +1,3 @@ +package build + +var InsecurePoStValidation = false diff --git a/chain/actors/actor_init.go b/chain/actors/actor_init.go index 66d9d98fa..ffcd5fd94 100644 --- a/chain/actors/actor_init.go +++ b/chain/actors/actor_init.go @@ -40,7 +40,6 @@ func init() { type InitActor struct{} type InitActorState struct { - // TODO: this needs to be a HAMT, its a dumb map for now AddressMap cid.Cid NextID uint64 diff --git a/chain/actors/actor_miner.go b/chain/actors/actor_miner.go index 2f2a1df33..6d0d7eb49 100644 --- a/chain/actors/actor_miner.go +++ b/chain/actors/actor_miner.go @@ -6,6 +6,8 @@ import ( "encoding/binary" "fmt" + ffi "github.com/filecoin-project/filecoin-ffi" + "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/aerrors" "github.com/filecoin-project/lotus/chain/address" @@ -29,12 +31,16 @@ type StorageMinerActorState struct { PreCommittedSectors map[string]*PreCommittedSector // All sectors this miner has committed. + // + // AMT[sectorID]ffi.PublicSectorInfo Sectors cid.Cid // TODO: Spec says 'StagedCommittedSectors', which one is it? // Sectors this miner is currently mining. It is only updated // when a PoSt is submitted (not as each new sector commitment is added). + // + // AMT[sectorID]ffi.PublicSectorInfo ProvingSet cid.Cid // TODO: these: @@ -54,12 +60,6 @@ type StorageMinerActorState struct { // These become the currentFaultSet when a PoSt is submitted. NextFaultSet types.BitField - // Sectors reported during the last PoSt submission as being 'done'. - // The collateral for them is still being held until - // the next PoSt submission in case early sector - // removal penalization is needed. - NextDoneSet types.BitField - // Amount of power this miner has. Power types.BigInt @@ -69,7 +69,7 @@ type StorageMinerActorState struct { // The height at which this miner was slashed at. SlashedAt uint64 - ProvingPeriodEnd uint64 + ElectionPeriodStart uint64 } type MinerInfo struct { @@ -117,7 +117,7 @@ type maMethods struct { Constructor uint64 PreCommitSector uint64 ProveCommitSector uint64 - SubmitPoSt uint64 + SubmitFallbackPoSt uint64 SlashStorageFault uint64 GetCurrentProvingSet uint64 ArbitrateDeal uint64 @@ -133,16 +133,17 @@ type maMethods struct { CheckMiner uint64 DeclareFaults uint64 SlashConsensusFault uint64 + SubmitElectionPoSt uint64 } -var MAMethods = maMethods{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19} +var MAMethods = maMethods{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} func (sma StorageMinerActor) Exports() []interface{} { return []interface{}{ 1: sma.StorageMinerConstructor, 2: sma.PreCommitSector, 3: sma.ProveCommitSector, - 4: sma.SubmitPoSt, + 4: sma.SubmitFallbackPoSt, //5: sma.SlashStorageFault, //6: sma.GetCurrentProvingSet, //7: sma.ArbitrateDeal, @@ -158,6 +159,7 @@ func (sma StorageMinerActor) Exports() []interface{} { 17: sma.CheckMiner, 18: sma.DeclareFaults, 19: sma.SlashConsensusFault, + 20: sma.SubmitElectionPoSt, } } @@ -345,7 +347,7 @@ func (sma StorageMinerActor) ProveCommitSector(act *types.Actor, vmctx types.VMC // Note: There must exist a unique index in the miner's sector set for each // sector ID. The `faults`, `recovered`, and `done` parameters of the // SubmitPoSt method express indices into this sector set. - nssroot, err := AddToSectorSet(ctx, vmctx.Storage(), self.Sectors, params.SectorID, us.Info.CommR, commD) + nssroot, err := AddToSectorSet(ctx, types.WrapStorage(vmctx.Storage()), self.Sectors, params.SectorID, us.Info.CommR, commD) if err != nil { return nil, err } @@ -366,7 +368,9 @@ func (sma StorageMinerActor) ProveCommitSector(act *types.Actor, vmctx types.VMC if pss.Count == 0 { self.ProvingSet = self.Sectors - self.ProvingPeriodEnd = vmctx.BlockHeight() + build.ProvingPeriodDuration + // TODO: probably want to wait until the miner is above a certain + // threshold before starting this + self.ElectionPeriodStart = vmctx.BlockHeight() } nstate, err := vmctx.Storage().Put(self) @@ -388,23 +392,12 @@ func (sma StorageMinerActor) ProveCommitSector(act *types.Actor, vmctx types.VMC return nil, err } -type SubmitPoStParams struct { - Proof []byte - DoneSet types.BitField - // TODO: once the spec changes finish, we have more work to do here... +type SubmitFallbackPoStParams struct { + Proof []byte + Candidates []types.EPostTicket } -func ProvingPeriodEnd(setPeriodEnd, height uint64) (uint64, uint64) { - offset := setPeriodEnd % build.ProvingPeriodDuration - period := ((height - offset - 1) / build.ProvingPeriodDuration) + 1 - end := (period * build.ProvingPeriodDuration) + offset - - return end, period -} - -// TODO: this is a dummy method that allows us to plumb in other parts of the -// system for now. -func (sma StorageMinerActor) SubmitPoSt(act *types.Actor, vmctx types.VMContext, params *SubmitPoStParams) ([]byte, ActorError) { +func (sma StorageMinerActor) SubmitFallbackPoSt(act *types.Actor, vmctx types.VMContext, params *SubmitFallbackPoStParams) ([]byte, ActorError) { oldstate, self, err := loadState(vmctx) if err != nil { return nil, err @@ -419,36 +412,28 @@ func (sma StorageMinerActor) SubmitPoSt(act *types.Actor, vmctx types.VMContext, return nil, aerrors.New(1, "not authorized to submit post for miner") } - currentProvingPeriodEnd, _ := ProvingPeriodEnd(self.ProvingPeriodEnd, vmctx.BlockHeight()) - - feesRequired := types.NewInt(0) - - if currentProvingPeriodEnd > self.ProvingPeriodEnd { - //TODO late fee calc - feesRequired = types.BigAdd(feesRequired, types.NewInt(1000)) - } - - //TODO temporary sector failure fees - - msgVal := vmctx.Message().Value - if msgVal.LessThan(feesRequired) { - return nil, aerrors.New(2, "not enough funds to pay post submission fees") - } - - if msgVal.GreaterThan(feesRequired) { - _, err := vmctx.Send(vmctx.Message().From, 0, - types.BigSub(msgVal, feesRequired), nil) - if err != nil { - return nil, aerrors.Wrap(err, "could not refund excess fees") + /* + // TODO: handle fees + msgVal := vmctx.Message().Value + if msgVal.LessThan(feesRequired) { + return nil, aerrors.New(2, "not enough funds to pay post submission fees") } - } + + if msgVal.GreaterThan(feesRequired) { + _, err := vmctx.Send(vmctx.Message().From, 0, + types.BigSub(msgVal, feesRequired), nil) + if err != nil { + return nil, aerrors.Wrap(err, "could not refund excess fees") + } + } + */ var seed [sectorbuilder.CommLen]byte { - randHeight := currentProvingPeriodEnd - build.PoStChallangeTime - build.PoStRandomnessLookback + randHeight := self.ElectionPeriodStart + build.FallbackPoStDelay if vmctx.BlockHeight() <= randHeight { // TODO: spec, retcode - return nil, aerrors.Newf(1, "submit PoSt called outside submission window (%d < %d)", vmctx.BlockHeight(), randHeight) + return nil, aerrors.Newf(1, "submit fallback PoSt called too early (%d < %d)", vmctx.BlockHeight(), randHeight) } rand, err := vmctx.GetRandomness(randHeight) @@ -468,13 +453,13 @@ func (sma StorageMinerActor) SubmitPoSt(act *types.Actor, vmctx types.VMContext, return nil, aerrors.HandleExternalError(lerr, "could not load proving set node") } - var sectorInfos []sectorbuilder.SectorInfo + var sectorInfos []ffi.PublicSectorInfo if err := pss.ForEach(func(id uint64, v *cbg.Deferred) error { var comms [][]byte if err := cbor.DecodeInto(v.Raw, &comms); err != nil { return xerrors.New("could not decode comms") } - si := sectorbuilder.SectorInfo{ + si := ffi.PublicSectorInfo{ SectorID: id, } commR := comms[0] @@ -491,10 +476,23 @@ func (sma StorageMinerActor) SubmitPoSt(act *types.Actor, vmctx types.VMContext, } faults := self.CurrentFaultSet.All() + _ = faults - if ok, lerr := sectorbuilder.VerifyPost(vmctx.Context(), mi.SectorSize, - sectorbuilder.NewSortedSectorInfo(sectorInfos), seed, params.Proof, - faults); !ok || lerr != nil { + proverID := vmctx.Message().To // TODO: normalize to ID address + + var candidates []sectorbuilder.EPostCandidate + for _, t := range params.Candidates { + var partial [32]byte + copy(partial[:], t.Partial) + candidates = append(candidates, sectorbuilder.EPostCandidate{ + PartialTicket: partial, + SectorID: t.SectorID, + SectorChallengeIndex: t.ChallengeIndex, + }) + } + + if ok, lerr := sectorbuilder.VerifyFallbackPost(vmctx.Context(), mi.SectorSize, + sectorbuilder.NewSortedPublicSectorInfo(sectorInfos), seed[:], params.Proof, candidates, proverID); !ok || lerr != nil { if lerr != nil { // TODO: study PoST errors return nil, aerrors.Absorb(lerr, 4, "PoST error") @@ -503,60 +501,12 @@ func (sma StorageMinerActor) SubmitPoSt(act *types.Actor, vmctx types.VMContext, return nil, aerrors.New(4, "PoST invalid") } } - self.CurrentFaultSet = self.NextFaultSet - self.NextFaultSet = types.NewBitField() - ss, lerr := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingSet) - if lerr != nil { - return nil, aerrors.HandleExternalError(lerr, "could not load proving set node") - } - - if err := ss.BatchDelete(params.DoneSet.All()); err != nil { - // TODO: this could fail for system reasons (block not found) or for - // bad user input reasons (e.g. bad doneset). The latter should be a - // non-fatal error - return nil, aerrors.HandleExternalError(err, "failed to delete sectors in done set") - } - - self.ProvingSet, lerr = ss.Flush() - if lerr != nil { - return nil, aerrors.HandleExternalError(lerr, "could not flush AMT") - } - - oldPower := self.Power - self.Power = types.BigMul(types.NewInt(pss.Count-uint64(len(faults))), - types.NewInt(mi.SectorSize)) - - delta := types.BigSub(self.Power, oldPower) - if self.SlashedAt != 0 { - self.SlashedAt = 0 - delta = self.Power - } - - prevPE := self.ProvingPeriodEnd - if !self.Active { - self.Active = true - prevPE = 0 - } - - enc, err := SerializeParams(&UpdateStorageParams{ - Delta: delta, - NextProvingPeriodEnd: currentProvingPeriodEnd + build.ProvingPeriodDuration, - PreviousProvingPeriodEnd: prevPE, - }) - if err != nil { + // Post submission is successful! + if err := onSuccessfulPoSt(self, vmctx); err != nil { return nil, err } - _, err = vmctx.Send(StoragePowerAddress, SPAMethods.UpdateStorage, types.NewInt(0), enc) - if err != nil { - return nil, err - } - - self.ProvingSet = self.Sectors - self.ProvingPeriodEnd = currentProvingPeriodEnd + build.ProvingPeriodDuration - self.NextDoneSet = params.DoneSet - c, err := vmctx.Storage().Put(self) if err != nil { return nil, err @@ -586,8 +536,8 @@ func SectorIsUnique(ctx context.Context, s types.Storage, sroot cid.Cid, sid uin return !found, nil } -func AddToSectorSet(ctx context.Context, s types.Storage, ss cid.Cid, sectorID uint64, commR, commD []byte) (cid.Cid, ActorError) { - ssr, err := amt.LoadAMT(types.WrapStorage(s), ss) +func AddToSectorSet(ctx context.Context, blks amt.Blocks, ss cid.Cid, sectorID uint64, commR, commD []byte) (cid.Cid, ActorError) { + ssr, err := amt.LoadAMT(blks, ss) if err != nil { return cid.Undef, aerrors.HandleExternalError(err, "could not load sector set node") } @@ -749,7 +699,7 @@ func (sma StorageMinerActor) GetSectorSize(act *types.Actor, vmctx types.VMConte } func isLate(height uint64, self *StorageMinerActorState) bool { - return self.ProvingPeriodEnd > 0 && height >= self.ProvingPeriodEnd // TODO: review: maybe > ? + return self.ElectionPeriodStart > 0 && height >= self.ElectionPeriodStart+build.SlashablePowerDelay } func (sma StorageMinerActor) IsSlashed(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) { @@ -817,32 +767,34 @@ type DeclareFaultsParams struct { } func (sma StorageMinerActor) DeclareFaults(act *types.Actor, vmctx types.VMContext, params *DeclareFaultsParams) ([]byte, ActorError) { - oldstate, self, aerr := loadState(vmctx) - if aerr != nil { - return nil, aerr - } - - challengeHeight := self.ProvingPeriodEnd - build.PoStChallangeTime - - if vmctx.BlockHeight() < challengeHeight { - // TODO: optimized bitfield methods - for _, v := range params.Faults.All() { - self.CurrentFaultSet.Set(v) + /* + oldstate, self, aerr := loadState(vmctx) + if aerr != nil { + return nil, aerr } - } else { - for _, v := range params.Faults.All() { - self.NextFaultSet.Set(v) + + challengeHeight := self.ProvingPeriodEnd - build.PoStChallangeTime + + if vmctx.BlockHeight() < challengeHeight { + // TODO: optimized bitfield methods + for _, v := range params.Faults.All() { + self.CurrentFaultSet.Set(v) + } + } else { + for _, v := range params.Faults.All() { + self.NextFaultSet.Set(v) + } } - } - nstate, err := vmctx.Storage().Put(self) - if err != nil { - return nil, err - } - if err := vmctx.Storage().Commit(oldstate, nstate); err != nil { - return nil, err - } + nstate, err := vmctx.Storage().Put(self) + if err != nil { + return nil, err + } + if err := vmctx.Storage().Commit(oldstate, nstate); err != nil { + return nil, err + } + */ return nil, nil } @@ -892,6 +844,88 @@ func (sma StorageMinerActor) SlashConsensusFault(act *types.Actor, vmctx types.V return nil, nil } +func (sma StorageMinerActor) SubmitElectionPoSt(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, aerrors.ActorError) { + if vmctx.Message().From != NetworkAddress { + return nil, aerrors.Newf(1, "submit election post can only be called by the storage power actor") + } + + oldstate, self, aerr := loadState(vmctx) + if aerr != nil { + return nil, aerr + } + + if self.SlashedAt != 0 { + return nil, aerrors.New(1, "slashed miners can't perform election PoSt") + } + + if err := onSuccessfulPoSt(self, vmctx); err != nil { + return nil, err + } + + ncid, err := vmctx.Storage().Put(self) + if err != nil { + return nil, err + } + if err := vmctx.Storage().Commit(oldstate, ncid); err != nil { + return nil, err + } + + return nil, nil +} + +func onSuccessfulPoSt(self *StorageMinerActorState, vmctx types.VMContext) aerrors.ActorError { + // TODO: some sector upkeep stuff that is very haphazard and unclear in the spec + + var mi MinerInfo + if err := vmctx.Storage().Get(self.Info, &mi); err != nil { + return err + } + + pss, nerr := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingSet) + if nerr != nil { + return aerrors.HandleExternalError(nerr, "failed to load proving set") + } + + self.CurrentFaultSet = self.NextFaultSet + self.NextFaultSet = types.NewBitField() + + faults := []uint64{} // TODO + + oldPower := self.Power + self.Power = types.BigMul(types.NewInt(pss.Count-uint64(len(faults))), + types.NewInt(mi.SectorSize)) + + delta := types.BigSub(self.Power, oldPower) + if self.SlashedAt != 0 { + self.SlashedAt = 0 + delta = self.Power + } + + prevSlashingDeadline := self.ElectionPeriodStart + build.SlashablePowerDelay + if !self.Active { + self.Active = true + prevSlashingDeadline = 0 + } + + enc, err := SerializeParams(&UpdateStorageParams{ + Delta: delta, + NextProvingPeriodEnd: vmctx.BlockHeight() + build.SlashablePowerDelay, + PreviousProvingPeriodEnd: prevSlashingDeadline, + }) + if err != nil { + return err + } + + _, err = vmctx.Send(StoragePowerAddress, SPAMethods.UpdateStorage, types.NewInt(0), enc) + if err != nil { + return err + } + + self.ProvingSet = self.Sectors + self.ElectionPeriodStart = vmctx.BlockHeight() + return nil +} + func slasherShare(total types.BigInt, elapsed uint64) types.BigInt { // [int(pow(1.26, n) * 10) for n in range(30)] fracs := []uint64{10, 12, 15, 20, 25, 31, 40, 50, 63, 80, 100, 127, 160, 201, 254, 320, 403, 508, 640, 807, 1017, 1281, 1614, 2034, 2563, 3230, 4070, 5128, 6462, 8142} diff --git a/chain/actors/actor_storagemarket.go b/chain/actors/actor_storagemarket.go index 6fb07be3f..9cd8a265a 100644 --- a/chain/actors/actor_storagemarket.go +++ b/chain/actors/actor_storagemarket.go @@ -3,16 +3,17 @@ package actors import ( "bytes" "context" + "golang.org/x/xerrors" "github.com/filecoin-project/go-amt-ipld" "github.com/ipfs/go-cid" "github.com/ipfs/go-hamt-ipld" - "golang.org/x/xerrors" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/aerrors" "github.com/filecoin-project/lotus/chain/address" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/cborutil" "github.com/filecoin-project/lotus/lib/sectorbuilder" ) @@ -110,6 +111,15 @@ func (sdp *StorageDealProposal) Sign(ctx context.Context, sign SignFunc) error { return nil } +func (sdp *StorageDealProposal) Cid() (cid.Cid, error) { + nd, err := cborutil.AsIpld(sdp) + if err != nil { + return cid.Undef, err + } + + return nd.Cid(), nil +} + func (sdp *StorageDealProposal) Verify() error { unsigned := *sdp unsigned.ProposerSignature = nil @@ -538,9 +548,7 @@ func (sma StorageMarketActor) ProcessStorageDealsPayment(act *types.Actor, vmctx return nil, nil } - // todo: check math (written on a plane, also tired) - // TODO: division is hard, this more than likely has some off-by-one issue - toPay := types.BigMul(dealInfo.Deal.Proposal.StoragePricePerEpoch, types.NewInt(build.ProvingPeriodDuration)) + toPay := types.BigMul(dealInfo.Deal.Proposal.StoragePricePerEpoch, types.NewInt(build.SlashablePowerDelay)) b, bnd, aerr := GetMarketBalances(vmctx.Context(), vmctx.Ipld(), self.Balances, dealInfo.Deal.Proposal.Client, providerWorker) if aerr != nil { diff --git a/chain/actors/actor_storagepower.go b/chain/actors/actor_storagepower.go index e76cdd054..85fb2890e 100644 --- a/chain/actors/actor_storagepower.go +++ b/chain/actors/actor_storagepower.go @@ -289,8 +289,8 @@ func (spa StoragePowerActor) UpdateStorage(act *types.Actor, vmctx types.VMConte self.TotalStorage = types.BigAdd(self.TotalStorage, params.Delta) - previousBucket := params.PreviousProvingPeriodEnd % build.ProvingPeriodDuration - nextBucket := params.NextProvingPeriodEnd % build.ProvingPeriodDuration + previousBucket := params.PreviousProvingPeriodEnd % build.SlashablePowerDelay + nextBucket := params.NextProvingPeriodEnd % build.SlashablePowerDelay if previousBucket == nextBucket && params.PreviousProvingPeriodEnd != 0 { nroot, err := vmctx.Storage().Put(&self) @@ -601,7 +601,7 @@ func (spa StoragePowerActor) CheckProofSubmissions(act *types.Actor, vmctx types } func checkProofSubmissionsAtH(vmctx types.VMContext, self *StoragePowerState, height uint64) aerrors.ActorError { - bucketID := height % build.ProvingPeriodDuration + bucketID := height % build.SlashablePowerDelay buckets, eerr := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingBuckets) if eerr != nil { diff --git a/chain/actors/actor_storagepower_test.go b/chain/actors/actor_storagepower_test.go index b5330be5a..20157ada8 100644 --- a/chain/actors/actor_storagepower_test.go +++ b/chain/actors/actor_storagepower_test.go @@ -170,5 +170,5 @@ func signBlock(t *testing.T, w *wallet.Wallet, worker address.Address, blk *type t.Fatal(err) } - blk.BlockSig = *sig + blk.BlockSig = sig } diff --git a/chain/actors/cbor_gen.go b/chain/actors/cbor_gen.go index 53de185e1..115f02744 100644 --- a/chain/actors/cbor_gen.go +++ b/chain/actors/cbor_gen.go @@ -198,7 +198,7 @@ func (t *StorageMinerActorState) MarshalCBOR(w io.Writer) error { _, err := w.Write(cbg.CborNull) return err } - if _, err := w.Write([]byte{139}); err != nil { + if _, err := w.Write([]byte{138}); err != nil { return err } @@ -258,11 +258,6 @@ func (t *StorageMinerActorState) MarshalCBOR(w io.Writer) error { return err } - // t.t.NextDoneSet (types.BitField) (struct) - if err := t.NextDoneSet.MarshalCBOR(w); err != nil { - return err - } - // t.t.Power (types.BigInt) (struct) if err := t.Power.MarshalCBOR(w); err != nil { return err @@ -278,8 +273,8 @@ func (t *StorageMinerActorState) MarshalCBOR(w io.Writer) error { return err } - // t.t.ProvingPeriodEnd (uint64) (uint64) - if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.ProvingPeriodEnd))); err != nil { + // t.t.ElectionPeriodStart (uint64) (uint64) + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.ElectionPeriodStart))); err != nil { return err } return nil @@ -296,7 +291,7 @@ func (t *StorageMinerActorState) UnmarshalCBOR(r io.Reader) error { return fmt.Errorf("cbor input should be of type array") } - if extra != 11 { + if extra != 10 { return fmt.Errorf("cbor input had wrong number of fields") } @@ -406,15 +401,6 @@ func (t *StorageMinerActorState) UnmarshalCBOR(r io.Reader) error { return err } - } - // t.t.NextDoneSet (types.BitField) (struct) - - { - - if err := t.NextDoneSet.UnmarshalCBOR(br); err != nil { - return err - } - } // t.t.Power (types.BigInt) (struct) @@ -452,7 +438,7 @@ func (t *StorageMinerActorState) UnmarshalCBOR(r io.Reader) error { return fmt.Errorf("wrong type for uint64 field") } t.SlashedAt = uint64(extra) - // t.t.ProvingPeriodEnd (uint64) (uint64) + // t.t.ElectionPeriodStart (uint64) (uint64) maj, extra, err = cbg.CborReadHeader(br) if err != nil { @@ -461,7 +447,7 @@ func (t *StorageMinerActorState) UnmarshalCBOR(r io.Reader) error { if maj != cbg.MajUnsignedInt { return fmt.Errorf("wrong type for uint64 field") } - t.ProvingPeriodEnd = uint64(extra) + t.ElectionPeriodStart = uint64(extra) return nil } @@ -827,7 +813,7 @@ func (t *MinerInfo) UnmarshalCBOR(r io.Reader) error { return nil } -func (t *SubmitPoStParams) MarshalCBOR(w io.Writer) error { +func (t *SubmitFallbackPoStParams) MarshalCBOR(w io.Writer) error { if t == nil { _, err := w.Write(cbg.CborNull) return err @@ -844,14 +830,19 @@ func (t *SubmitPoStParams) MarshalCBOR(w io.Writer) error { return err } - // t.t.DoneSet (types.BitField) (struct) - if err := t.DoneSet.MarshalCBOR(w); err != nil { + // t.t.Candidates ([]types.EPostTicket) (slice) + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Candidates)))); err != nil { return err } + for _, v := range t.Candidates { + if err := v.MarshalCBOR(w); err != nil { + return err + } + } return nil } -func (t *SubmitPoStParams) UnmarshalCBOR(r io.Reader) error { +func (t *SubmitFallbackPoStParams) UnmarshalCBOR(r io.Reader) error { br := cbg.GetPeeker(r) maj, extra, err := cbg.CborReadHeader(br) @@ -883,15 +874,32 @@ func (t *SubmitPoStParams) UnmarshalCBOR(r io.Reader) error { if _, err := io.ReadFull(br, t.Proof); err != nil { return err } - // t.t.DoneSet (types.BitField) (struct) + // t.t.Candidates ([]types.EPostTicket) (slice) - { + maj, extra, err = cbg.CborReadHeader(br) + if err != nil { + return err + } + if extra > 8192 { + return fmt.Errorf("t.Candidates: array too large (%d)", extra) + } - if err := t.DoneSet.UnmarshalCBOR(br); err != nil { + if maj != cbg.MajArray { + return fmt.Errorf("expected cbor array") + } + if extra > 0 { + t.Candidates = make([]types.EPostTicket, extra) + } + for i := 0; i < int(extra); i++ { + + var v types.EPostTicket + if err := v.UnmarshalCBOR(br); err != nil { return err } + t.Candidates[i] = v } + return nil } diff --git a/chain/address/address.go b/chain/address/address.go index 07ff47851..3912b8bf6 100644 --- a/chain/address/address.go +++ b/chain/address/address.go @@ -7,7 +7,7 @@ import ( "io" "strconv" - "github.com/filecoin-project/go-bls-sigs" + bls "github.com/filecoin-project/filecoin-ffi" "github.com/filecoin-project/go-leb128" cbor "github.com/ipfs/go-ipld-cbor" "github.com/minio/blake2b-simd" @@ -389,3 +389,11 @@ func (a *Address) UnmarshalCBOR(br io.Reader) error { return nil } + +func IDFromAddress(addr Address) (uint64, error) { + if addr.Protocol() != ID { + return 0, xerrors.Errorf("cannot get id from non id address") + } + + return leb128.ToUInt64(addr.Payload()), nil +} diff --git a/chain/address/address_test.go b/chain/address/address_test.go index 1c666ee7c..48e9fc76f 100644 --- a/chain/address/address_test.go +++ b/chain/address/address_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/filecoin-project/go-bls-sigs" + ffi "github.com/filecoin-project/filecoin-ffi" "github.com/filecoin-project/go-leb128" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -285,7 +285,7 @@ func TestVectorActorAddress(t *testing.T) { func TestRandomBLSAddress(t *testing.T) { assert := assert.New(t) - pk := bls.PrivateKeyPublicKey(bls.PrivateKeyGenerate()) + pk := ffi.PrivateKeyPublicKey(ffi.PrivateKeyGenerate()) addr, err := NewBLSAddress(pk[:]) assert.NoError(err) @@ -410,8 +410,8 @@ func TestInvalidByteAddresses(t *testing.T) { {append([]byte{2}, make([]byte, PayloadHashLength+1)...), ErrInvalidPayload}, // BLS Protocol - {append([]byte{3}, make([]byte, bls.PublicKeyBytes-1)...), ErrInvalidPayload}, - {append([]byte{3}, make([]byte, bls.PrivateKeyBytes+1)...), ErrInvalidPayload}, + {append([]byte{3}, make([]byte, ffi.PublicKeyBytes-1)...), ErrInvalidPayload}, + {append([]byte{3}, make([]byte, ffi.PrivateKeyBytes+1)...), ErrInvalidPayload}, } for _, tc := range testCases { diff --git a/chain/address/constants.go b/chain/address/constants.go index 8fc66c678..9eabb862b 100644 --- a/chain/address/constants.go +++ b/chain/address/constants.go @@ -2,9 +2,9 @@ package address import ( "encoding/base32" + "errors" "github.com/minio/blake2b-simd" - errors "github.com/pkg/errors" ) func init() { diff --git a/chain/blocksync/cbor_gen.go b/chain/blocksync/cbor_gen.go index 579b3f857..4af61a490 100644 --- a/chain/blocksync/cbor_gen.go +++ b/chain/blocksync/cbor_gen.go @@ -5,7 +5,7 @@ import ( "io" "github.com/filecoin-project/lotus/chain/types" - cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" xerrors "golang.org/x/xerrors" ) diff --git a/chain/deals/provider.go b/chain/deals/provider.go index fecaecea2..626fa7c0b 100644 --- a/chain/deals/provider.go +++ b/chain/deals/provider.go @@ -24,6 +24,8 @@ import ( "github.com/filecoin-project/lotus/storage/sectorblocks" ) +var ProviderDsPrefix = "/deals/provider" + type MinerDeal struct { Client peer.ID Proposal actors.StorageDealProposal @@ -110,7 +112,7 @@ func NewProvider(ds dtypes.MetadataDS, sminer *storage.Miner, secb *sectorblocks actor: minerAddress, - deals: statestore.New(namespace.Wrap(ds, datastore.NewKey("/deals/client"))), + deals: statestore.New(namespace.Wrap(ds, datastore.NewKey(ProviderDsPrefix))), ds: ds, } diff --git a/chain/events/events_called.go b/chain/events/events_called.go index e38b01035..00b86bfee 100644 --- a/chain/events/events_called.go +++ b/chain/events/events_called.go @@ -6,7 +6,9 @@ import ( "sync" "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" ) @@ -302,9 +304,10 @@ func (e *calledEvents) Called(check CheckFunc, hnd CalledHandler, rev RevertHand e.lk.Lock() defer e.lk.Unlock() - done, more, err := check(e.tsc.best()) + ts := e.tsc.best() + done, more, err := check(ts) if err != nil { - return err + return xerrors.Errorf("called check error (h: %d): %w", ts.Height(), err) } if done { timeout = NoTimeout @@ -335,6 +338,6 @@ func (e *calledEvents) Called(check CheckFunc, hnd CalledHandler, rev RevertHand return nil } -func (e *calledEvents) CalledMsg(ctx context.Context, hnd CalledHandler, rev RevertHandler, confidence int, timeout uint64, msg *types.Message) error { - return e.Called(e.CheckMsg(ctx, msg, hnd), hnd, rev, confidence, timeout, e.MatchMsg(msg)) +func (e *calledEvents) CalledMsg(ctx context.Context, hnd CalledHandler, rev RevertHandler, confidence int, timeout uint64, msg store.ChainMsg) error { + return e.Called(e.CheckMsg(ctx, msg, hnd), hnd, rev, confidence, timeout, e.MatchMsg(msg.VMMessage())) } diff --git a/chain/events/events_height.go b/chain/events/events_height.go index a05a3e1be..e7a81d735 100644 --- a/chain/events/events_height.go +++ b/chain/events/events_height.go @@ -96,7 +96,7 @@ func (e *heightEvents) headChangeAt(rev, app []*types.TipSet) error { span.End() if err != nil { - log.Errorf("chain trigger (@H %d, called @ %d) failed: %s", triggerH, ts.Height(), err) + log.Errorf("chain trigger (@H %d, called @ %d) failed: %+v", triggerH, ts.Height(), err) } } return nil diff --git a/chain/events/events_test.go b/chain/events/events_test.go index fa3d116af..927653357 100644 --- a/chain/events/events_test.go +++ b/chain/events/events_test.go @@ -60,26 +60,26 @@ func makeTs(t *testing.T, h uint64, msgcid cid.Cid) *types.TipSet { Height: h, Miner: a, - Tickets: []*types.Ticket{{[]byte{byte(h % 2)}}}, + Ticket: &types.Ticket{[]byte{byte(h % 2)}}, ParentStateRoot: dummyCid, Messages: msgcid, ParentMessageReceipts: dummyCid, - BlockSig: types.Signature{Type: types.KTBLS}, + BlockSig: &types.Signature{Type: types.KTBLS}, BLSAggregate: types.Signature{Type: types.KTBLS}, }, { Height: h, Miner: b, - Tickets: []*types.Ticket{{[]byte{byte((h + 1) % 2)}}}, + Ticket: &types.Ticket{[]byte{byte((h + 1) % 2)}}, ParentStateRoot: dummyCid, Messages: msgcid, ParentMessageReceipts: dummyCid, - BlockSig: types.Signature{Type: types.KTBLS}, + BlockSig: &types.Signature{Type: types.KTBLS}, BLSAggregate: types.Signature{Type: types.KTBLS}, }, }) diff --git a/chain/events/tscache_test.go b/chain/events/tscache_test.go index 7d715aea0..9c7c203bb 100644 --- a/chain/events/tscache_test.go +++ b/chain/events/tscache_test.go @@ -27,7 +27,7 @@ func TestTsCache(t *testing.T) { ParentStateRoot: dummyCid, Messages: dummyCid, ParentMessageReceipts: dummyCid, - BlockSig: types.Signature{Type: types.KTBLS}, + BlockSig: &types.Signature{Type: types.KTBLS}, BLSAggregate: types.Signature{Type: types.KTBLS}, }}) if err != nil { @@ -69,7 +69,7 @@ func TestTsCacheNulls(t *testing.T) { ParentStateRoot: dummyCid, Messages: dummyCid, ParentMessageReceipts: dummyCid, - BlockSig: types.Signature{Type: types.KTBLS}, + BlockSig: &types.Signature{Type: types.KTBLS}, BLSAggregate: types.Signature{Type: types.KTBLS}, }}) if err != nil { diff --git a/chain/events/utils.go b/chain/events/utils.go index 630158b13..ba8083f9b 100644 --- a/chain/events/utils.go +++ b/chain/events/utils.go @@ -5,28 +5,32 @@ import ( "golang.org/x/xerrors" + "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" ) -func (e *calledEvents) CheckMsg(ctx context.Context, msg *types.Message, hnd CalledHandler) CheckFunc { +func (e *calledEvents) CheckMsg(ctx context.Context, smsg store.ChainMsg, hnd CalledHandler) CheckFunc { + msg := smsg.VMMessage() + return func(ts *types.TipSet) (done bool, more bool, err error) { fa, err := e.cs.StateGetActor(ctx, msg.From, ts) if err != nil { return false, true, err } - // TODO: probably want to look at the chain to make sure it's - // the right message, but this is probably good enough for now - done = fa.Nonce >= msg.Nonce + // >= because actor nonce is actually the next nonce that is expected to appear on chain + if msg.Nonce >= fa.Nonce { + return false, true, nil + } - rec, err := e.cs.StateGetReceipt(ctx, msg.Cid(), ts) + rec, err := e.cs.StateGetReceipt(ctx, smsg.VMMessage().Cid(), ts) if err != nil { - return false, true, err + return false, true, xerrors.Errorf("getting receipt in CheckMsg: %w", err) } more, err = hnd(msg, rec, ts, ts.Height()) - return done, more, err + return true, more, err } } diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 1fa00f47f..35891f000 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -3,14 +3,20 @@ package gen import ( "bytes" "context" + "crypto/sha256" + "encoding/binary" "fmt" + "io/ioutil" "sync/atomic" + ffi "github.com/filecoin-project/filecoin-ffi" + "github.com/ipfs/go-blockservice" "github.com/ipfs/go-car" offline "github.com/ipfs/go-ipfs-exchange-offline" "github.com/ipfs/go-merkledag" peer "github.com/libp2p/go-libp2p-peer" + "go.opencensus.io/trace" "golang.org/x/xerrors" "github.com/filecoin-project/lotus/api" @@ -20,6 +26,9 @@ import ( "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/wallet" + "github.com/filecoin-project/lotus/cmd/lotus-seed/seed" + "github.com/filecoin-project/lotus/genesis" + "github.com/filecoin-project/lotus/lib/sectorbuilder" "github.com/filecoin-project/lotus/node/repo" block "github.com/ipfs/go-block-format" @@ -46,10 +55,11 @@ type ChainGen struct { genesis *types.BlockHeader CurTipset *store.FullTipSet - Timestamper func(*types.TipSet, int) uint64 + Timestamper func(*types.TipSet, uint64) uint64 w *wallet.Wallet + eppProvs map[address.Address]ElectionPoStProver Miners []address.Address mworkers []address.Address receivers []address.Address @@ -102,16 +112,6 @@ func NewGenerator() (*ChainGen, error) { return nil, xerrors.Errorf("creating memrepo wallet failed: %w", err) } - worker1, err := w.GenerateKey(types.KTBLS) - if err != nil { - return nil, xerrors.Errorf("failed to generate worker key: %w", err) - } - - worker2, err := w.GenerateKey(types.KTBLS) - if err != nil { - return nil, xerrors.Errorf("failed to generate worker key: %w", err) - } - banker, err := w.GenerateKey(types.KTSecp256k1) if err != nil { return nil, xerrors.Errorf("failed to generate banker key: %w", err) @@ -125,16 +125,60 @@ func NewGenerator() (*ChainGen, error) { } } + // TODO: this is really weird, we have to guess the miner addresses that + // will be created in order to preseal data for them + maddr1, err := address.NewFromString("t0300") + if err != nil { + return nil, err + } + + m1temp, err := ioutil.TempDir("", "preseal") + if err != nil { + return nil, err + } + + genm1, err := seed.PreSeal(maddr1, 1024, 1, m1temp, []byte("some randomness")) + if err != nil { + return nil, err + } + + maddr2, err := address.NewFromString("t0301") + if err != nil { + return nil, err + } + + m2temp, err := ioutil.TempDir("", "preseal") + if err != nil { + return nil, err + } + + genm2, err := seed.PreSeal(maddr2, 1024, 1, m2temp, []byte("some randomness")) + if err != nil { + return nil, err + } + + mk1, err := w.Import(&genm1.Key) + if err != nil { + return nil, err + } + mk2, err := w.Import(&genm2.Key) + if err != nil { + return nil, err + } + minercfg := &GenMinerCfg{ - Workers: []address.Address{worker1, worker2}, - Owners: []address.Address{worker1, worker2}, PeerIDs: []peer.ID{"peerID1", "peerID2"}, + PreSeals: map[string]genesis.GenesisMiner{ + maddr1.String(): *genm1, + maddr2.String(): *genm2, + }, + MinerAddrs: []address.Address{maddr1, maddr2}, } genb, err := MakeGenesisBlock(bs, map[address.Address]types.BigInt{ - worker1: types.FromFil(40000), - worker2: types.FromFil(40000), - banker: types.FromFil(50000), + mk1: types.FromFil(40000), + mk2: types.FromFil(40000), + banker: types.FromFil(50000), }, minercfg, 100000) if err != nil { return nil, xerrors.Errorf("make genesis block failed: %w", err) @@ -153,6 +197,11 @@ func NewGenerator() (*ChainGen, error) { return nil, xerrors.Errorf("MakeGenesisBlock failed to set miner address") } + mgen := make(map[address.Address]ElectionPoStProver) + for _, m := range minercfg.MinerAddrs { + mgen[m] = &eppProvider{} + } + sm := stmgr.NewStateManager(cs) gen := &ChainGen{ @@ -163,8 +212,9 @@ func NewGenerator() (*ChainGen, error) { genesis: genb.Genesis, w: w, - Miners: minercfg.MinerAddrs, - mworkers: minercfg.Workers, + Miners: minercfg.MinerAddrs, + eppProvs: mgen, + //mworkers: minercfg.Workers, banker: banker, receivers: receievers, @@ -189,20 +239,15 @@ func (cg *ChainGen) GenesisCar() ([]byte, error) { out := new(bytes.Buffer) if err := car.WriteCar(context.TODO(), dserv, []cid.Cid{cg.Genesis().Cid()}, out); err != nil { - return nil, err + return nil, xerrors.Errorf("genesis car write car failed: %w", err) } return out.Bytes(), nil } -func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m address.Address, ticks []*types.Ticket) (types.ElectionProof, *types.Ticket, error) { +func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m address.Address, round int64) (*types.EPostProof, *types.Ticket, error) { - var lastTicket *types.Ticket - if len(ticks) == 0 { - lastTicket = pts.MinTicket() - } else { - lastTicket = ticks[len(ticks)-1] - } + lastTicket := pts.MinTicket() st := pts.ParentState() @@ -211,7 +256,8 @@ func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m add return nil, nil, xerrors.Errorf("get miner worker: %w", err) } - vrfout, err := ComputeVRF(ctx, cg.w.Sign, worker, lastTicket.VRFProof) + log.Warnf("compute VRF ROUND: %d %s %s", round, m, worker) + vrfout, err := ComputeVRF(ctx, cg.w.Sign, worker, m, DSepTicket, lastTicket.VRFProof) if err != nil { return nil, nil, xerrors.Errorf("compute VRF: %w", err) } @@ -220,7 +266,7 @@ func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m add VRFProof: vrfout, } - win, eproof, err := IsRoundWinner(ctx, pts, append(ticks, tick), m, &mca{w: cg.w, sm: cg.sm}) + win, eproof, err := IsRoundWinner(ctx, pts, round, m, cg.eppProvs[m], &mca{w: cg.w, sm: cg.sm}) if err != nil { return nil, nil, xerrors.Errorf("checking round winner failed: %w", err) } @@ -248,23 +294,22 @@ func (cg *ChainGen) NextTipSet() (*MinedTipSet, error) { func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Address) (*MinedTipSet, error) { var blks []*types.FullBlock - ticketSets := make([][]*types.Ticket, len(miners)) msgs, err := cg.getRandomMessages() if err != nil { return nil, xerrors.Errorf("get random messages: %w", err) } - for len(blks) == 0 { - for i, m := range miners { - proof, t, err := cg.nextBlockProof(context.TODO(), base, m, ticketSets[i]) + for round := int64(base.Height() + 1); len(blks) == 0; round++ { + for _, m := range miners { + proof, t, err := cg.nextBlockProof(context.TODO(), base, m, round) if err != nil { return nil, xerrors.Errorf("next block proof: %w", err) } - ticketSets[i] = append(ticketSets[i], t) if proof != nil { - fblk, err := cg.makeBlock(base, m, proof, ticketSets[i], msgs) + log.Warn("making block, ticket: ", t.VRFProof) + fblk, err := cg.makeBlock(base, m, proof, t, uint64(round), msgs) if err != nil { return nil, xerrors.Errorf("making a block for next tipset failed: %w", err) } @@ -286,16 +331,16 @@ func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Ad }, nil } -func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, eproof types.ElectionProof, tickets []*types.Ticket, msgs []*types.SignedMessage) (*types.FullBlock, error) { +func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, eproof *types.EPostProof, ticket *types.Ticket, height uint64, msgs []*types.SignedMessage) (*types.FullBlock, error) { var ts uint64 if cg.Timestamper != nil { - ts = cg.Timestamper(parents, len(tickets)) + ts = cg.Timestamper(parents, height-parents.Height()) } else { - ts = parents.MinTimestamp() + (uint64(len(tickets)) * build.BlockDelay) + ts = parents.MinTimestamp() + ((height - parents.Height()) * build.BlockDelay) } - fblk, err := MinerCreateBlock(context.TODO(), cg.sm, cg.w, m, parents, tickets, eproof, msgs, ts) + fblk, err := MinerCreateBlock(context.TODO(), cg.sm, cg.w, m, parents, ticket, eproof, msgs, height, ts) if err != nil { return nil, err } @@ -354,12 +399,16 @@ func (cg *ChainGen) YieldRepo() (repo.Repo, error) { } type MiningCheckAPI interface { - ChainGetRandomness(context.Context, types.TipSetKey, []*types.Ticket, int) ([]byte, error) + ChainGetRandomness(context.Context, types.TipSetKey, int64) ([]byte, error) StateMinerPower(context.Context, address.Address, *types.TipSet) (api.MinerPower, error) StateMinerWorker(context.Context, address.Address, *types.TipSet) (address.Address, error) + StateMinerSectorSize(context.Context, address.Address, *types.TipSet) (uint64, error) + + StateMinerProvingSet(context.Context, address.Address, *types.TipSet) ([]*api.ChainSectorInfo, error) + WalletSign(context.Context, address.Address, []byte) (*types.Signature, error) } @@ -368,8 +417,8 @@ type mca struct { sm *stmgr.StateManager } -func (mca mca) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, ticks []*types.Ticket, lb int) ([]byte, error) { - return mca.sm.ChainStore().GetRandomness(ctx, pts.Cids(), ticks, int64(lb)) +func (mca mca) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, lb int64) ([]byte, error) { + return mca.sm.ChainStore().GetRandomness(ctx, pts.Cids(), int64(lb)) } func (mca mca) StateMinerPower(ctx context.Context, maddr address.Address, ts *types.TipSet) (api.MinerPower, error) { @@ -388,12 +437,45 @@ func (mca mca) StateMinerWorker(ctx context.Context, maddr address.Address, ts * return stmgr.GetMinerWorkerRaw(ctx, mca.sm, ts.ParentState(), maddr) } +func (mca mca) StateMinerSectorSize(ctx context.Context, maddr address.Address, ts *types.TipSet) (uint64, error) { + return stmgr.GetMinerSectorSize(ctx, mca.sm, ts, maddr) +} + +func (mca mca) StateMinerProvingSet(ctx context.Context, maddr address.Address, ts *types.TipSet) ([]*api.ChainSectorInfo, error) { + return stmgr.GetMinerProvingSet(ctx, mca.sm, ts, maddr) +} + func (mca mca) WalletSign(ctx context.Context, a address.Address, v []byte) (*types.Signature, error) { return mca.w.Sign(ctx, a, v) } -func IsRoundWinner(ctx context.Context, ts *types.TipSet, ticks []*types.Ticket, miner address.Address, a MiningCheckAPI) (bool, types.ElectionProof, error) { - r, err := a.ChainGetRandomness(ctx, ts.Key(), ticks, build.EcRandomnessLookback) +type ElectionPoStProver interface { + GenerateCandidates(context.Context, sectorbuilder.SortedPublicSectorInfo, []byte) ([]sectorbuilder.EPostCandidate, error) + ComputeProof(context.Context, sectorbuilder.SortedPublicSectorInfo, []byte, []sectorbuilder.EPostCandidate) ([]byte, error) +} + +type eppProvider struct { + sectors []ffi.PublicSectorInfo +} + +func (epp *eppProvider) GenerateCandidates(ctx context.Context, _ sectorbuilder.SortedPublicSectorInfo, eprand []byte) ([]sectorbuilder.EPostCandidate, error) { + return []sectorbuilder.EPostCandidate{ + sectorbuilder.EPostCandidate{ + SectorID: 1, + PartialTicket: [32]byte{}, + Ticket: [32]byte{}, + SectorChallengeIndex: 1, + }, + }, nil +} + +func (epp *eppProvider) ComputeProof(ctx context.Context, _ sectorbuilder.SortedPublicSectorInfo, eprand []byte, winners []sectorbuilder.EPostCandidate) ([]byte, error) { + + return []byte("valid proof"), nil +} + +func IsRoundWinner(ctx context.Context, ts *types.TipSet, round int64, miner address.Address, epp ElectionPoStProver, a MiningCheckAPI) (bool, *types.EPostProof, error) { + r, err := a.ChainGetRandomness(ctx, ts.Key(), round-build.EcRandomnessLookback) if err != nil { return false, nil, xerrors.Errorf("chain get randomness: %w", err) } @@ -403,30 +485,153 @@ func IsRoundWinner(ctx context.Context, ts *types.TipSet, ticks []*types.Ticket, return false, nil, xerrors.Errorf("failed to get miner worker: %w", err) } - vrfout, err := ComputeVRF(ctx, a.WalletSign, mworker, r) + vrfout, err := ComputeVRF(ctx, a.WalletSign, mworker, miner, DSepElectionPost, r) if err != nil { return false, nil, xerrors.Errorf("failed to compute VRF: %w", err) } + pset, err := a.StateMinerProvingSet(ctx, miner, ts) + if err != nil { + return false, nil, xerrors.Errorf("failed to load proving set for miner: %w", err) + } + if len(pset) == 0 { + return false, nil, nil + } + + var sinfos []ffi.PublicSectorInfo + for _, s := range pset { + var commRa [32]byte + copy(commRa[:], s.CommR) + sinfos = append(sinfos, ffi.PublicSectorInfo{ + SectorID: s.SectorID, + CommR: commRa, + }) + } + sectors := sectorbuilder.NewSortedPublicSectorInfo(sinfos) + + hvrf := sha256.Sum256(vrfout) + log.Info("Replicas: ", sectors) + candidates, err := epp.GenerateCandidates(ctx, sectors, hvrf[:]) + if err != nil { + return false, nil, xerrors.Errorf("failed to generate electionPoSt candidates: %w", err) + } + pow, err := a.StateMinerPower(ctx, miner, ts) if err != nil { return false, nil, xerrors.Errorf("failed to check power: %w", err) } - return types.PowerCmp(vrfout, pow.MinerPower, pow.TotalPower), vrfout, nil + ssize, err := a.StateMinerSectorSize(ctx, miner, ts) + if err != nil { + return false, nil, xerrors.Errorf("failed to look up miners sector size: %w", err) + } + + var winners []sectorbuilder.EPostCandidate + for _, c := range candidates { + if types.IsTicketWinner(c.PartialTicket[:], ssize, pow.TotalPower, 1) { + winners = append(winners, c) + } + } + + // no winners, sad + if len(winners) == 0 { + return false, nil, nil + } + + proof, err := epp.ComputeProof(ctx, sectors, hvrf[:], winners) + if err != nil { + return false, nil, xerrors.Errorf("failed to compute snark for election proof: %w", err) + } + + ept := types.EPostProof{ + Proof: proof, + PostRand: vrfout, + } + for _, win := range winners { + ept.Candidates = append(ept.Candidates, types.EPostTicket{ + Partial: win.PartialTicket[:], + SectorID: win.SectorID, + ChallengeIndex: win.SectorChallengeIndex, + }) + } + + return true, &ept, nil } type SignFunc func(context.Context, address.Address, []byte) (*types.Signature, error) -func ComputeVRF(ctx context.Context, sign SignFunc, w address.Address, input []byte) ([]byte, error) { - sig, err := sign(ctx, w, input) +const ( + DSepTicket = 1 + DSepElectionPost = 2 +) + +func hashVRFBase(personalization uint64, miner address.Address, input []byte) ([]byte, error) { + if miner.Protocol() != address.ID { + return nil, xerrors.Errorf("miner address for compute VRF must be an ID address") + } + + var persbuf [8]byte + binary.LittleEndian.PutUint64(persbuf[:], personalization) + + h := sha256.New() + h.Write(persbuf[:]) + h.Write([]byte{0}) + h.Write(input) + h.Write([]byte{0}) + h.Write(miner.Bytes()) + + return h.Sum(nil), nil +} + +func VerifyVRF(ctx context.Context, worker, miner address.Address, p uint64, input, vrfproof []byte) error { + ctx, span := trace.StartSpan(ctx, "VerifyVRF") + defer span.End() + + vrfBase, err := hashVRFBase(p, miner, input) + if err != nil { + return xerrors.Errorf("computing vrf base failed: %w", err) + } + + sig := &types.Signature{ + Type: types.KTBLS, + Data: vrfproof, + } + + if err := sig.Verify(worker, vrfBase); err != nil { + return xerrors.Errorf("vrf was invalid: %w", err) + } + + return nil +} + +func ComputeVRF(ctx context.Context, sign SignFunc, worker, miner address.Address, p uint64, input []byte) ([]byte, error) { + sigInput, err := hashVRFBase(p, miner, input) if err != nil { return nil, err } + sig, err := sign(ctx, worker, sigInput) + if err != nil { + return nil, err + } + log.Warnf("making ticket: %x %s %s %x %x", sig.Data, worker, miner, input, sigInput) + if sig.Type != types.KTBLS { return nil, fmt.Errorf("miner worker address was not a BLS key") } return sig.Data, nil } + +func TicketHash(t *types.Ticket, addr address.Address) []byte { + h := sha256.New() + + h.Write(t.VRFProof) + + // Field Delimeter + h.Write([]byte{0}) + + h.Write(addr.Bytes()) + + return h.Sum(nil) +} diff --git a/chain/gen/gen_test.go b/chain/gen/gen_test.go index b4f2456b4..399742f5f 100644 --- a/chain/gen/gen_test.go +++ b/chain/gen/gen_test.go @@ -12,18 +12,12 @@ func testGeneration(t testing.TB, n int, msgs int) { g.msgsPerBlock = msgs - var height int for i := 0; i < n; i++ { mts, err := g.NextTipSet() if err != nil { t.Fatalf("error at H:%d, %s", i, err) } - - ts := mts.TipSet.TipSet() - if ts.Height() != uint64(height+len(ts.Blocks()[0].Tickets)) { - t.Fatal("wrong height", ts.Height(), i, len(ts.Blocks()[0].Tickets), len(ts.Blocks())) - } - height += len(ts.Blocks()[0].Tickets) + _ = mts } } diff --git a/chain/gen/mining.go b/chain/gen/mining.go index 5cfc3a694..e9aeaef7d 100644 --- a/chain/gen/mining.go +++ b/chain/gen/mining.go @@ -3,8 +3,8 @@ package gen import ( "context" + bls "github.com/filecoin-project/filecoin-ffi" amt "github.com/filecoin-project/go-amt-ipld" - bls "github.com/filecoin-project/go-bls-sigs" cid "github.com/ipfs/go-cid" hamt "github.com/ipfs/go-hamt-ipld" cbg "github.com/whyrusleeping/cbor-gen" @@ -18,14 +18,12 @@ import ( "github.com/filecoin-project/lotus/chain/wallet" ) -func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wallet, miner address.Address, parents *types.TipSet, tickets []*types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage, timestamp uint64) (*types.FullBlock, error) { +func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wallet, miner address.Address, parents *types.TipSet, ticket *types.Ticket, proof *types.EPostProof, msgs []*types.SignedMessage, height, timestamp uint64) (*types.FullBlock, error) { st, recpts, err := sm.TipSetState(ctx, parents) if err != nil { return nil, xerrors.Errorf("failed to load tipset state: %w", err) } - height := parents.Height() + uint64(len(tickets)) - worker, err := stmgr.GetMinerWorkerRaw(ctx, sm, st, miner) if err != nil { return nil, xerrors.Errorf("failed to get miner worker: %w", err) @@ -34,10 +32,10 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wal next := &types.BlockHeader{ Miner: miner, Parents: parents.Cids(), - Tickets: tickets, + Ticket: ticket, Height: height, Timestamp: timestamp, - ElectionProof: proof, + EPostProof: *proof, ParentStateRoot: st, ParentMessageReceipts: recpts, } @@ -122,7 +120,7 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wal return nil, xerrors.Errorf("failed to sign new block: %w", err) } - next.BlockSig = *sig + next.BlockSig = sig fullBlock := &types.FullBlock{ Header: next, diff --git a/chain/gen/utils.go b/chain/gen/utils.go index f60da262d..e16a78813 100644 --- a/chain/gen/utils.go +++ b/chain/gen/utils.go @@ -1,6 +1,7 @@ package gen import ( + "bytes" "context" "fmt" @@ -8,6 +9,7 @@ import ( "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" hamt "github.com/ipfs/go-hamt-ipld" + blockstore "github.com/ipfs/go-ipfs-blockstore" bstore "github.com/ipfs/go-ipfs-blockstore" peer "github.com/libp2p/go-libp2p-peer" cbg "github.com/whyrusleeping/cbor-gen" @@ -20,8 +22,17 @@ import ( "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/vm" + "github.com/filecoin-project/lotus/genesis" ) +var validSsizes = map[uint64]struct{}{} + +func init() { + for _, size := range build.SectorSizes { + validSsizes[size] = struct{}{} + } +} + type GenesisBootstrap struct { Genesis *types.BlockHeader } @@ -98,15 +109,6 @@ func MakeInitialStateTree(bs bstore.Blockstore, actmap map[address.Address]types return nil, xerrors.Errorf("set storage market actor: %w", err) } - smact, err := SetupStorageMarketActor(bs) - if err != nil { - return nil, xerrors.Errorf("setup storage market actor: %w", err) - } - - if err := state.SetActor(actors.StorageMarketAddress, smact); err != nil { - return nil, xerrors.Errorf("set storage market actor: %w", err) - } - netAmt := types.FromFil(build.TotalFilecoin) for _, amt := range actmap { netAmt = types.BigSub(netAmt, amt) @@ -177,46 +179,61 @@ func SetupStoragePowerActor(bs bstore.Blockstore) (*types.Actor, error) { }, nil } -func SetupStorageMarketActor(bs bstore.Blockstore) (*types.Actor, error) { +func SetupStorageMarketActor(bs bstore.Blockstore, sroot cid.Cid, deals []actors.StorageDeal) (cid.Cid, error) { cst := hamt.CSTFromBstore(bs) nd := hamt.NewNode(cst) emptyHAMT, err := cst.Put(context.TODO(), nd) if err != nil { - return nil, err + return cid.Undef, err } blks := amt.WrapBlockstore(bs) - emptyAMT, err := amt.FromArray(blks, nil) + cdeals := make([]cbg.CBORMarshaler, len(deals)) + for i, deal := range deals { + cdeals[i] = &actors.OnChainDeal{ + Deal: deal, + ActivationEpoch: 1, + } + } + + dealAmt, err := amt.FromArray(blks, cdeals) if err != nil { - return nil, xerrors.Errorf("amt build failed: %w", err) + return cid.Undef, xerrors.Errorf("amt build failed: %w", err) } sms := &actors.StorageMarketState{ Balances: emptyHAMT, - Deals: emptyAMT, + Deals: dealAmt, NextDealID: 0, } stcid, err := cst.Put(context.TODO(), sms) if err != nil { - return nil, err + return cid.Undef, err } - return &types.Actor{ + act := &types.Actor{ Code: actors.StorageMarketCodeCid, Head: stcid, Nonce: 0, Balance: types.NewInt(0), - }, nil + } + + state, err := state.LoadStateTree(cst, sroot) + if err != nil { + return cid.Undef, xerrors.Errorf("making new state tree: %w", err) + } + + if err := state.SetActor(actors.StorageMarketAddress, act); err != nil { + return cid.Undef, xerrors.Errorf("set storage market actor: %w", err) + } + + return state.Flush() } type GenMinerCfg struct { - Owners []address.Address - Workers []address.Address - - // not quite generating real sectors yet, but this will be necessary - //SectorDir string + PreSeals map[string]genesis.GenesisMiner // The addresses of the created miner, this is set by the genesis setup MinerAddrs []address.Address @@ -232,78 +249,250 @@ func mustEnc(i cbg.CBORMarshaler) []byte { return enc } -func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid, gmcfg *GenMinerCfg) (cid.Cid, error) { +func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid, gmcfg *GenMinerCfg) (cid.Cid, []actors.StorageDeal, error) { vm, err := vm.NewVM(sroot, 0, nil, actors.NetworkAddress, cs.Blockstore()) if err != nil { - return cid.Undef, xerrors.Errorf("failed to create NewVM: %w", err) + return cid.Undef, nil, xerrors.Errorf("failed to create NewVM: %w", err) } - for i := 0; i < len(gmcfg.Workers); i++ { - owner := gmcfg.Owners[i] - worker := gmcfg.Workers[i] - pid := gmcfg.PeerIDs[i] + if len(gmcfg.MinerAddrs) != len(gmcfg.PreSeals) { + return cid.Undef, nil, xerrors.Errorf("miner address list, and preseal count doesn't match (%d != %d)", len(gmcfg.MinerAddrs), len(gmcfg.PreSeals)) + } - params := mustEnc(&actors.CreateStorageMinerParams{ - Owner: owner, - Worker: worker, - SectorSize: build.SectorSizes[0], - PeerID: pid, - }) + var deals []actors.StorageDeal - // TODO: hardcoding 7000000 here is a little fragile, it changes any + for i, maddr := range gmcfg.MinerAddrs { + ps, psok := gmcfg.PreSeals[maddr.String()] + if !psok { + return cid.Undef, nil, xerrors.Errorf("no preseal for miner %s", maddr) + } + + minerParams := &actors.CreateStorageMinerParams{ + Owner: ps.Owner, + Worker: ps.Worker, + SectorSize: ps.SectorSize, + PeerID: gmcfg.PeerIDs[i], // TODO: grab from preseal too + } + + params := mustEnc(minerParams) + + // TODO: hardcoding 6500 here is a little fragile, it changes any // time anyone changes the initial account allocations - rval, err := doExecValue(ctx, vm, actors.StoragePowerAddress, owner, types.FromFil(6500), actors.SPAMethods.CreateStorageMiner, params) + rval, err := doExecValue(ctx, vm, actors.StoragePowerAddress, ps.Worker, types.FromFil(6500), actors.SPAMethods.CreateStorageMiner, params) if err != nil { - return cid.Undef, xerrors.Errorf("failed to create genesis miner: %w", err) + return cid.Undef, nil, xerrors.Errorf("failed to create genesis miner: %w", err) } - maddr, err := address.NewFromBytes(rval) + maddrret, err := address.NewFromBytes(rval) if err != nil { - return cid.Undef, err + return cid.Undef, nil, err } - gmcfg.MinerAddrs = append(gmcfg.MinerAddrs, maddr) + _, err = vm.Flush(ctx) + if err != nil { + return cid.Undef, nil, err + } - params = mustEnc(&actors.UpdateStorageParams{Delta: types.NewInt(5000)}) + cst := hamt.CSTFromBstore(cs.Blockstore()) + if err := reassignMinerActorAddress(vm, cst, maddrret, maddr); err != nil { + return cid.Undef, nil, err + } + + power := types.BigMul(types.NewInt(minerParams.SectorSize), types.NewInt(uint64(len(ps.Sectors)))) + + params = mustEnc(&actors.UpdateStorageParams{Delta: power}) _, err = doExec(ctx, vm, actors.StoragePowerAddress, maddr, actors.SPAMethods.UpdateStorage, params) if err != nil { - return cid.Undef, xerrors.Errorf("failed to update total storage: %w", err) + return cid.Undef, nil, xerrors.Errorf("failed to update total storage: %w", err) } - // UGLY HACKY MODIFICATION OF MINER POWER - // we have to flush the vm here because it buffers stuff internally for perf reasons if _, err := vm.Flush(ctx); err != nil { - return cid.Undef, xerrors.Errorf("vm.Flush failed: %w", err) + return cid.Undef, nil, xerrors.Errorf("vm.Flush failed: %w", err) } st := vm.StateTree() mact, err := st.GetActor(maddr) if err != nil { - return cid.Undef, xerrors.Errorf("get miner actor failed: %w", err) + return cid.Undef, nil, xerrors.Errorf("get miner actor failed: %w", err) } - cst := hamt.CSTFromBstore(cs.Blockstore()) var mstate actors.StorageMinerActorState if err := cst.Get(ctx, mact.Head, &mstate); err != nil { - return cid.Undef, xerrors.Errorf("getting miner actor state failed: %w", err) + return cid.Undef, nil, xerrors.Errorf("getting miner actor state failed: %w", err) + } + mstate.Power = types.NewInt(build.SectorSizes[0]) + + blks := amt.WrapBlockstore(cs.Blockstore()) + + for _, s := range ps.Sectors { + nssroot, err := actors.AddToSectorSet(ctx, blks, mstate.Sectors, s.SectorID, s.CommR[:], s.CommD[:]) + if err != nil { + return cid.Undef, nil, xerrors.Errorf("failed to add fake sector to sector set: %w", err) + } + mstate.Sectors = nssroot + mstate.ProvingSet = nssroot + + deals = append(deals, s.Deal) } - mstate.Power = types.NewInt(5000) nstate, err := cst.Put(ctx, &mstate) if err != nil { - return cid.Undef, err + return cid.Undef, nil, err } mact.Head = nstate if err := st.SetActor(maddr, mact); err != nil { - return cid.Undef, err + return cid.Undef, nil, err } - // End of super haxx } - return vm.Flush(ctx) + c, err := vm.Flush(ctx) + return c, deals, err +} + +func reassignMinerActorAddress(vm *vm.VM, cst *hamt.CborIpldStore, from, to address.Address) error { + if from == to { + return nil + } + act, err := vm.StateTree().GetActor(from) + if err != nil { + return xerrors.Errorf("reassign: failed to get 'from' actor: %w", err) + } + + _, err = vm.StateTree().GetActor(to) + if err == nil { + return xerrors.Errorf("cannot reassign actor, target address taken") + } + if err := vm.StateTree().SetActor(to, act); err != nil { + return xerrors.Errorf("failed to reassign actor: %w", err) + } + + if err := adjustStorageMarketTracking(vm, cst, from, to); err != nil { + return xerrors.Errorf("adjusting storage market tracking: %w", err) + } + + // Now, adjust the tracking in the init actor + return initActorReassign(vm, cst, from, to) +} + +func adjustStorageMarketTracking(vm *vm.VM, cst *hamt.CborIpldStore, from, to address.Address) error { + ctx := context.TODO() + act, err := vm.StateTree().GetActor(actors.StoragePowerAddress) + if err != nil { + return xerrors.Errorf("loading storage power actor: %w", err) + } + + var spst actors.StoragePowerState + if err := cst.Get(ctx, act.Head, &spst); err != nil { + return xerrors.Errorf("loading storage power actor state: %w", err) + } + + miners, err := hamt.LoadNode(ctx, cst, spst.Miners) + if err != nil { + return xerrors.Errorf("loading miner set: %w", err) + } + + if err := miners.Delete(ctx, string(from.Bytes())); err != nil { + return xerrors.Errorf("deleting from spa set: %w", err) + } + + if err := miners.Set(ctx, string(to.Bytes()), uint64(1)); err != nil { + return xerrors.Errorf("failed setting miner: %w", err) + } + + if err := miners.Flush(ctx); err != nil { + return err + } + + nminerscid, err := cst.Put(ctx, miners) + if err != nil { + return err + } + spst.Miners = nminerscid + + nhead, err := cst.Put(ctx, &spst) + if err != nil { + return err + } + + act.Head = nhead + + return nil +} + +func initActorReassign(vm *vm.VM, cst *hamt.CborIpldStore, from, to address.Address) error { + ctx := context.TODO() + initact, err := vm.StateTree().GetActor(actors.InitAddress) + if err != nil { + return xerrors.Errorf("couldnt get init actor: %w", err) + } + + var st actors.InitActorState + if err := cst.Get(ctx, initact.Head, &st); err != nil { + return xerrors.Errorf("reassign loading init actor state: %w", err) + } + + amap, err := hamt.LoadNode(ctx, cst, st.AddressMap) + if err != nil { + return xerrors.Errorf("failed to load init actor map: %w", err) + } + + target, err := address.IDFromAddress(from) + if err != nil { + return xerrors.Errorf("failed to extract ID: %w", err) + } + + var out string + halt := xerrors.Errorf("halt") + err = amap.ForEach(ctx, func(k string, v interface{}) error { + _, val, err := cbg.CborReadHeader(bytes.NewReader(v.(*cbg.Deferred).Raw)) + if err != nil { + return xerrors.Errorf("parsing int in map failed: %w", err) + } + + if val == target { + out = k + return halt + } + return nil + }) + + if err == nil { + return xerrors.Errorf("could not find from address in init ID map") + } + if !xerrors.Is(err, halt) { + return xerrors.Errorf("finding address in ID map failed: %w", err) + } + + if err := amap.Delete(ctx, out); err != nil { + return xerrors.Errorf("deleting 'from' entry in amap: %w", err) + } + + if err := amap.Set(ctx, out, target); err != nil { + return xerrors.Errorf("setting 'to' entry in amap: %w", err) + } + + if err := amap.Flush(ctx); err != nil { + return xerrors.Errorf("failed to flush amap: %w", err) + } + + ncid, err := cst.Put(ctx, amap) + if err != nil { + return err + } + + st.AddressMap = ncid + + nacthead, err := cst.Put(ctx, &st) + if err != nil { + return err + } + + initact.Head = nacthead + + return nil } func doExec(ctx context.Context, vm *vm.VM, to, from address.Address, method uint64, params []byte) ([]byte, error) { @@ -352,11 +541,21 @@ func MakeGenesisBlock(bs bstore.Blockstore, balances map[address.Address]types.B // temp chainstore cs := store.NewChainStore(bs, datastore.NewMapDatastore()) - stateroot, err = SetupStorageMiners(ctx, cs, stateroot, gmcfg) + stateroot, deals, err := SetupStorageMiners(ctx, cs, stateroot, gmcfg) if err != nil { return nil, xerrors.Errorf("setup storage miners failed: %w", err) } + stateroot, err = SetupStorageMarketActor(bs, stateroot, deals) + if err != nil { + return nil, xerrors.Errorf("setup storage market actor: %w", err) + } + + stateroot, err = AdjustInitActorStartID(ctx, bs, stateroot, 1000) + if err != nil { + return nil, xerrors.Errorf("failed to adjust init actor start ID: %w", err) + } + blks := amt.WrapBlockstore(bs) emptyroot, err := amt.FromArray(blks, nil) @@ -383,9 +582,12 @@ func MakeGenesisBlock(bs bstore.Blockstore, balances map[address.Address]types.B } b := &types.BlockHeader{ - Miner: actors.InitAddress, - Tickets: []*types.Ticket{genesisticket}, - ElectionProof: []byte("the Genesis block"), + Miner: actors.InitAddress, + Ticket: genesisticket, + EPostProof: types.EPostProof{ + Proof: []byte("not a real proof"), + PostRand: []byte("i guess this is kinda random"), + }, Parents: []cid.Cid{}, Height: 0, ParentWeight: types.NewInt(0), @@ -393,7 +595,7 @@ func MakeGenesisBlock(bs bstore.Blockstore, balances map[address.Address]types.B Messages: mmb.Cid(), ParentMessageReceipts: emptyroot, BLSAggregate: types.Signature{Type: types.KTBLS, Data: []byte("signatureeee")}, - BlockSig: types.Signature{Type: types.KTBLS, Data: []byte("block signatureeee")}, + BlockSig: &types.Signature{Type: types.KTBLS, Data: []byte("block signatureeee")}, Timestamp: ts, } @@ -410,3 +612,37 @@ func MakeGenesisBlock(bs bstore.Blockstore, balances map[address.Address]types.B Genesis: b, }, nil } + +func AdjustInitActorStartID(ctx context.Context, bs blockstore.Blockstore, stateroot cid.Cid, val uint64) (cid.Cid, error) { + cst := hamt.CSTFromBstore(bs) + + tree, err := state.LoadStateTree(cst, stateroot) + if err != nil { + return cid.Undef, err + } + + act, err := tree.GetActor(actors.InitAddress) + if err != nil { + return cid.Undef, err + } + + var st actors.InitActorState + if err := cst.Get(ctx, act.Head, &st); err != nil { + return cid.Undef, err + } + + st.NextID = val + + nstate, err := cst.Put(ctx, &st) + if err != nil { + return cid.Undef, err + } + + act.Head = nstate + + if err := tree.SetActor(actors.InitAddress, act); err != nil { + return cid.Undef, err + } + + return tree.Flush() +} diff --git a/chain/messagepool.go b/chain/messagepool.go index 57053c966..f0a3ed383 100644 --- a/chain/messagepool.go +++ b/chain/messagepool.go @@ -1,6 +1,7 @@ package chain import ( + "bytes" "context" "errors" "sort" @@ -8,6 +9,9 @@ import ( "time" lru "github.com/hashicorp/golang-lru" + "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/namespace" + "github.com/ipfs/go-datastore/query" pubsub "github.com/libp2p/go-libp2p-pubsub" lps "github.com/whyrusleeping/pubsub" "go.uber.org/multierr" @@ -18,6 +22,7 @@ import ( "github.com/filecoin-project/lotus/chain/address" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/node/modules/dtypes" ) var ( @@ -35,6 +40,8 @@ var ( const ( msgTopic = "/fil/messages" + localMsgsDs = "/mpool/local" + localUpdates = "update" ) @@ -60,6 +67,8 @@ type MessagePool struct { blsSigCache *lru.TwoQueueCache changes *lps.PubSub + + localMsgs datastore.Datastore } type msgSet struct { @@ -89,7 +98,7 @@ func (ms *msgSet) add(m *types.SignedMessage) error { return nil } -func NewMessagePool(sm *stmgr.StateManager, ps *pubsub.PubSub) *MessagePool { +func NewMessagePool(sm *stmgr.StateManager, ps *pubsub.PubSub, ds dtypes.MetadataDS) (*MessagePool, error) { cache, _ := lru.New2Q(build.BlsSignatureCacheSize) mp := &MessagePool{ closer: make(chan struct{}), @@ -99,10 +108,18 @@ func NewMessagePool(sm *stmgr.StateManager, ps *pubsub.PubSub) *MessagePool { sm: sm, ps: ps, minGasPrice: types.NewInt(0), - maxTxPoolSize: 100000, + maxTxPoolSize: 5000, blsSigCache: cache, changes: lps.New(50), + localMsgs: namespace.Wrap(ds, datastore.NewKey(localMsgsDs)), } + + if err := mp.loadLocal(); err != nil { + return nil, xerrors.Errorf("loading local messages: %w", err) + } + + go mp.repubLocal() + sm.ChainStore().SubscribeHeadChanges(func(rev, app []*types.TipSet) error { err := mp.HeadChange(rev, app) if err != nil { @@ -111,7 +128,7 @@ func NewMessagePool(sm *stmgr.StateManager, ps *pubsub.PubSub) *MessagePool { return err }) - return mp + return mp, nil } func (mp *MessagePool) Close() error { @@ -134,13 +151,13 @@ func (mp *MessagePool) repubLocal() { for _, msg := range msgs { msgb, err := msg.Serialize() if err != nil { - multierr.Append(errout, xerrors.Errorf("could not serialize: %w", err)) + errout = multierr.Append(errout, xerrors.Errorf("could not serialize: %w", err)) continue } err = mp.ps.Publish(msgTopic, msgb) if err != nil { - multierr.Append(errout, xerrors.Errorf("could not publish: %w", err)) + errout = multierr.Append(errout, xerrors.Errorf("could not publish: %w", err)) continue } } @@ -156,8 +173,14 @@ func (mp *MessagePool) repubLocal() { } -func (mp *MessagePool) addLocal(a address.Address) { - mp.localAddrs[a] = struct{}{} +func (mp *MessagePool) addLocal(m *types.SignedMessage, msgb []byte) error { + mp.localAddrs[m.Message.From] = struct{}{} + + if err := mp.localMsgs.Put(datastore.NewKey(string(m.Cid().Bytes())), msgb); err != nil { + return xerrors.Errorf("persisting local message: %w", err) + } + + return nil } func (mp *MessagePool) Push(m *types.SignedMessage) error { @@ -171,7 +194,10 @@ func (mp *MessagePool) Push(m *types.SignedMessage) error { } mp.lk.Lock() - mp.addLocal(m.Message.From) + if err := mp.addLocal(m, msgb); err != nil { + mp.lk.Unlock() + return err + } mp.lk.Unlock() return mp.ps.Publish(msgTopic, msgb) @@ -231,13 +257,20 @@ func (mp *MessagePool) addLocked(m *types.SignedMessage) error { return err } + if _, err := mp.sm.ChainStore().PutMessage(&m.Message); err != nil { + log.Warnf("mpooladd cs.PutMessage failed: %s", err) + return err + } + mset, ok := mp.pending[m.Message.From] if !ok { mset = newMsgSet() mp.pending[m.Message.From] = mset } - mset.add(m) + if err := mset.add(m); err != nil { + log.Error(err) + } mp.changes.Pub(api.MpoolUpdate{ Type: api.MpoolAdd, @@ -254,12 +287,23 @@ func (mp *MessagePool) GetNonce(addr address.Address) (uint64, error) { } func (mp *MessagePool) getNonceLocked(addr address.Address) (uint64, error) { + stateNonce, err := mp.getStateNonce(addr) // sanity check + if err != nil { + return 0, err + } + mset, ok := mp.pending[addr] if ok { + if stateNonce > mset.nextNonce { + log.Errorf("state nonce was larger than mset.nextNonce (%d > %d)", stateNonce, mset.nextNonce) + + return stateNonce, nil + } + return mset.nextNonce, nil } - return mp.getStateNonce(addr) + return stateNonce, nil } func (mp *MessagePool) getStateNonce(addr address.Address) (uint64, error) { @@ -302,7 +346,9 @@ func (mp *MessagePool) PushWithNonce(addr address.Address, cb func(uint64) (*typ if err := mp.addLocked(msg); err != nil { return nil, err } - mp.addLocal(msg.Message.From) + if err := mp.addLocal(msg, msgb); err != nil { + log.Errorf("addLocal failed: %+v", err) + } return msg, mp.ps.Publish(msgTopic, msgb) } @@ -328,8 +374,7 @@ func (mp *MessagePool) Remove(from address.Address, nonce uint64) { delete(mset.msgs, nonce) if len(mset.msgs) == 0 { - // FIXME: This is racy - //delete(mp.pending, from) + delete(mp.pending, from) } else { var max uint64 for nonce := range mset.msgs { @@ -337,6 +382,10 @@ func (mp *MessagePool) Remove(from address.Address, nonce uint64) { max = nonce } } + if max < nonce { + max = nonce // we could have not seen the removed message before + } + mset.nextNonce = max + 1 } } @@ -380,7 +429,7 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet) } for _, msg := range smsgs { if err := mp.Add(msg); err != nil { - return err + log.Error(err) // TODO: probably lots of spam in multi-block tsets } } @@ -388,7 +437,7 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet) smsg := mp.RecoverSig(msg) if smsg != nil { if err := mp.Add(smsg); err != nil { - return err + log.Error(err) // TODO: probably lots of spam in multi-block tsets } } else { log.Warnf("could not recover signature for bls message %s during a reorg revert", msg.Cid()) @@ -423,7 +472,7 @@ func (mp *MessagePool) RecoverSig(msg *types.Message) *types.SignedMessage { } sig, ok := val.(types.Signature) if !ok { - log.Warnf("value in signature cache was not a signature (got %T)", val) + log.Errorf("value in signature cache was not a signature (got %T)", val) return nil } @@ -456,3 +505,31 @@ func (mp *MessagePool) Updates(ctx context.Context) (<-chan api.MpoolUpdate, err return out, nil } + +func (mp *MessagePool) loadLocal() error { + res, err := mp.localMsgs.Query(query.Query{}) + if err != nil { + return xerrors.Errorf("query local messages: %w", err) + } + + for r := range res.Next() { + if r.Error != nil { + return xerrors.Errorf("r.Error: %w", r.Error) + } + + var sm types.SignedMessage + if err := sm.UnmarshalCBOR(bytes.NewReader(r.Value)); err != nil { + return xerrors.Errorf("unmarshaling local message: %w", err) + } + + if err := mp.Add(&sm); err != nil { + if xerrors.Is(err, ErrNonceTooLow) { + continue // todo: drop the message from local cache (if above certain confidence threshold) + } + + return xerrors.Errorf("adding local message: %w", err) + } + } + + return nil +} diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index 731633d26..cfbe729c0 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -68,7 +68,7 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types. state := ts.ParentState() - r := store.NewChainRand(sm.cs, ts.Cids(), ts.Height(), nil) + r := store.NewChainRand(sm.cs, ts.Cids(), ts.Height()) return sm.CallRaw(ctx, msg, state, r, ts.Height()) } diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index eaaa4fc13..3ead45bfa 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -15,7 +15,7 @@ import ( cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" - bls "github.com/filecoin-project/go-bls-sigs" + bls "github.com/filecoin-project/filecoin-ffi" "github.com/ipfs/go-cid" hamt "github.com/ipfs/go-hamt-ipld" logging "github.com/ipfs/go-log" @@ -102,7 +102,7 @@ func (sm *StateManager) computeTipSetState(ctx context.Context, blks []*types.Bl cids[i] = v.Cid() } - r := store.NewChainRand(sm.cs, cids, blks[0].Height, nil) + r := store.NewChainRand(sm.cs, cids, blks[0].Height) vmi, err := vm.NewVM(pstate, blks[0].Height, r, address.Undef, sm.cs.Blockstore()) if err != nil { @@ -113,9 +113,14 @@ func (sm *StateManager) computeTipSetState(ctx context.Context, blks []*types.Bl if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("failed to get network actor: %w", err) } - reward := vm.MiningReward(netact.Balance) for _, b := range blks { + netact, err = vmi.StateTree().GetActor(actors.NetworkAddress) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("failed to get network actor: %w", err) + } + vmi.SetBlockMiner(b.Miner) + owner, err := GetMinerOwner(ctx, sm, pstate, b.Miner) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("failed to get owner for miner %s: %w", b.Miner, err) @@ -130,6 +135,23 @@ func (sm *StateManager) computeTipSetState(ctx context.Context, blks []*types.Bl return cid.Undef, cid.Undef, xerrors.Errorf("failed to deduct funds from network actor: %w", err) } + // all block miners created a valid post, go update the actor state + postSubmitMsg := &types.Message{ + From: actors.NetworkAddress, + Nonce: netact.Nonce, + To: b.Miner, + Method: actors.MAMethods.SubmitElectionPoSt, + GasPrice: types.NewInt(0), + GasLimit: types.NewInt(10000000000), + Value: types.NewInt(0), + } + ret, err := vmi.ApplyMessage(ctx, postSubmitMsg) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("submit election post message invocation failed: %w", err) + } + if ret.ExitCode != 0 { + return cid.Undef, cid.Undef, xerrors.Errorf("submit election post invocation returned nonzero exit code: %d", ret.ExitCode) + } } // TODO: can't use method from chainstore because it doesnt let us know who the block miners were @@ -322,7 +344,12 @@ func (sm *StateManager) GetBlsPublicKey(ctx context.Context, addr address.Addres } func (sm *StateManager) GetReceipt(ctx context.Context, msg cid.Cid, ts *types.TipSet) (*types.MessageReceipt, error) { - r, err := sm.tipsetExecutedMessage(ts, msg) + m, err := sm.cs.GetCMessage(msg) + if err != nil { + return nil, fmt.Errorf("failed to load message: %w", err) + } + + r, err := sm.tipsetExecutedMessage(ts, msg, m.VMMessage()) if err != nil { return nil, err } @@ -331,11 +358,6 @@ func (sm *StateManager) GetReceipt(ctx context.Context, msg cid.Cid, ts *types.T return r, nil } - m, err := sm.cs.GetCMessage(msg) - if err != nil { - return nil, fmt.Errorf("failed to load message: %w", err) - } - _, r, err = sm.searchBackForMsg(ctx, ts, m) if err != nil { return nil, fmt.Errorf("failed to look back through chain for message: %w", err) @@ -368,7 +390,7 @@ func (sm *StateManager) WaitForMessage(ctx context.Context, mcid cid.Cid) (*type return nil, nil, fmt.Errorf("expected current head on SHC stream (got %s)", head[0].Type) } - r, err := sm.tipsetExecutedMessage(head[0].Val, mcid) + r, err := sm.tipsetExecutedMessage(head[0].Val, mcid, msg.VMMessage()) if err != nil { return nil, nil, err } @@ -403,7 +425,7 @@ func (sm *StateManager) WaitForMessage(ctx context.Context, mcid cid.Cid) (*type case store.HCRevert: continue case store.HCApply: - r, err := sm.tipsetExecutedMessage(val.Val, mcid) + r, err := sm.tipsetExecutedMessage(val.Val, mcid, msg.VMMessage()) if err != nil { return nil, nil, err } @@ -454,7 +476,7 @@ func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet return nil, nil, fmt.Errorf("failed to load tipset during msg wait searchback: %w", err) } - r, err := sm.tipsetExecutedMessage(ts, m.Cid()) + r, err := sm.tipsetExecutedMessage(ts, m.Cid(), m.VMMessage()) if err != nil { return nil, nil, fmt.Errorf("checking for message execution during lookback: %w", err) } @@ -467,7 +489,7 @@ func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet } } -func (sm *StateManager) tipsetExecutedMessage(ts *types.TipSet, msg cid.Cid) (*types.MessageReceipt, error) { +func (sm *StateManager) tipsetExecutedMessage(ts *types.TipSet, msg cid.Cid, vmm *types.Message) (*types.MessageReceipt, error) { // The genesis block did not execute any messages if ts.Height() == 0 { return nil, nil @@ -483,9 +505,24 @@ func (sm *StateManager) tipsetExecutedMessage(ts *types.TipSet, msg cid.Cid) (*t return nil, err } - for i, m := range cm { - if m.Cid() == msg { - return sm.cs.GetParentReceipt(ts.Blocks()[0], i) + for ii := range cm { + // iterate in reverse because we going backwards through the chain + i := len(cm) - ii - 1 + m := cm[i] + + if m.VMMessage().From == vmm.From { // cheaper to just check origin first + if m.VMMessage().Nonce == vmm.Nonce { + if m.Cid() == msg { + return sm.cs.GetParentReceipt(ts.Blocks()[0], i) + } + + // this should be that message + return nil, xerrors.Errorf("found message with equal nonce as the one we are looking for (F:%s n %d, TS: %s n%d)", + msg, vmm.Nonce, m.Cid(), m.VMMessage().Nonce) + } + if m.VMMessage().Nonce < vmm.Nonce { + return nil, nil // don't bother looking further + } } } diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index b507fd387..86f66821b 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -3,10 +3,13 @@ package stmgr import ( "context" + ffi "github.com/filecoin-project/filecoin-ffi" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/address" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/sectorbuilder" amt "github.com/filecoin-project/go-amt-ipld" cid "github.com/ipfs/go-cid" @@ -147,21 +150,21 @@ func GetMinerWorker(ctx context.Context, sm *StateManager, ts *types.TipSet, mad return address.NewFromBytes(recp.Return) } -func GetMinerProvingPeriodEnd(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (uint64, error) { +func GetMinerElectionPeriodStart(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (uint64, error) { var mas actors.StorageMinerActorState _, err := sm.LoadActorState(ctx, maddr, &mas, ts) if err != nil { - return 0, xerrors.Errorf("failed to load miner actor state: %w", err) + return 0, xerrors.Errorf("(get eps) failed to load miner actor state: %w", err) } - return mas.ProvingPeriodEnd, nil + return mas.ElectionPeriodStart, nil } func GetMinerProvingSet(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) ([]*api.ChainSectorInfo, error) { var mas actors.StorageMinerActorState _, err := sm.LoadActorState(ctx, maddr, &mas, ts) if err != nil { - return nil, xerrors.Errorf("failed to load miner actor state: %w", err) + return nil, xerrors.Errorf("(get pset) failed to load miner actor state: %w", err) } return LoadSectorsFromSet(ctx, sm.ChainStore().Blockstore(), mas.ProvingSet) @@ -171,17 +174,37 @@ func GetMinerSectorSet(ctx context.Context, sm *StateManager, ts *types.TipSet, var mas actors.StorageMinerActorState _, err := sm.LoadActorState(ctx, maddr, &mas, ts) if err != nil { - return nil, xerrors.Errorf("failed to load miner actor state: %w", err) + return nil, xerrors.Errorf("(get sset) failed to load miner actor state: %w", err) } return LoadSectorsFromSet(ctx, sm.ChainStore().Blockstore(), mas.Sectors) } +func GetSectorsForElectionPost(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (*sectorbuilder.SortedPublicSectorInfo, error) { + sectors, err := GetMinerProvingSet(ctx, sm, ts, maddr) + if err != nil { + return nil, xerrors.Errorf("failed to get sector set for miner: %w", err) + } + + var uselessOtherArray []ffi.PublicSectorInfo + for _, s := range sectors { + var uselessBuffer [32]byte + copy(uselessBuffer[:], s.CommR) + uselessOtherArray = append(uselessOtherArray, ffi.PublicSectorInfo{ + SectorID: s.SectorID, + CommR: uselessBuffer, + }) + } + + ssi := sectorbuilder.NewSortedPublicSectorInfo(uselessOtherArray) + return &ssi, nil +} + func GetMinerSectorSize(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (uint64, error) { var mas actors.StorageMinerActorState _, err := sm.LoadActorState(ctx, maddr, &mas, ts) if err != nil { - return 0, xerrors.Errorf("failed to load miner actor state: %w", err) + return 0, xerrors.Errorf("(get ssize) failed to load miner actor state: %w", err) } cst := hamt.CSTFromBstore(sm.cs.Blockstore()) @@ -197,7 +220,7 @@ func GetMinerSlashed(ctx context.Context, sm *StateManager, ts *types.TipSet, ma var mas actors.StorageMinerActorState _, err := sm.LoadActorState(ctx, maddr, &mas, ts) if err != nil { - return 0, xerrors.Errorf("failed to load miner actor state: %w", err) + return 0, xerrors.Errorf("(get mslash) failed to load miner actor state: %w", err) } return mas.SlashedAt, nil diff --git a/chain/store/store.go b/chain/store/store.go index 00e863e5f..44bdefacf 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -3,8 +3,8 @@ package store import ( "context" "crypto/sha256" + "encoding/binary" "encoding/json" - "fmt" "sync" "github.com/filecoin-project/lotus/build" @@ -12,7 +12,7 @@ import ( "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/vm" "go.opencensus.io/trace" - "go.uber.org/zap" + "go.uber.org/multierr" amt "github.com/filecoin-project/go-amt-ipld" "github.com/filecoin-project/lotus/chain/types" @@ -24,7 +24,6 @@ import ( hamt "github.com/ipfs/go-hamt-ipld" bstore "github.com/ipfs/go-ipfs-blockstore" logging "github.com/ipfs/go-log" - "github.com/pkg/errors" cbg "github.com/whyrusleeping/cbor-gen" pubsub "github.com/whyrusleeping/pubsub" "golang.org/x/xerrors" @@ -100,12 +99,12 @@ func (cs *ChainStore) Load() error { return nil } if err != nil { - return errors.Wrap(err, "failed to load chain state from datastore") + return xerrors.Errorf("failed to load chain state from datastore: %w", err) } var tscids []cid.Cid if err := json.Unmarshal(head, &tscids); err != nil { - return errors.Wrap(err, "failed to unmarshal stored chain head") + return xerrors.Errorf("failed to unmarshal stored chain head: %w", err) } ts, err := cs.LoadTipSet(tscids) @@ -121,11 +120,11 @@ func (cs *ChainStore) Load() error { func (cs *ChainStore) writeHead(ts *types.TipSet) error { data, err := json.Marshal(ts.Cids()) if err != nil { - return errors.Wrap(err, "failed to marshal tipset") + return xerrors.Errorf("failed to marshal tipset: %w", err) } if err := cs.ds.Put(chainHeadKey, data); err != nil { - return errors.Wrap(err, "failed to write chain head to datastore") + return xerrors.Errorf("failed to write chain head to datastore: %w", err) } return nil @@ -209,7 +208,7 @@ func (cs *ChainStore) PutTipSet(ctx context.Context, ts *types.TipSet) error { log.Debugf("expanded %s into %s\n", ts.Cids(), expanded.Cids()) if err := cs.MaybeTakeHeavierTipSet(ctx, expanded); err != nil { - return errors.Wrap(err, "MaybeTakeHeavierTipSet failed in PutTipSet") + return xerrors.Errorf("MaybeTakeHeavierTipSet failed in PutTipSet: %w", err) } return nil } @@ -429,17 +428,32 @@ func (cs *ChainStore) AddToTipSetTracker(b *types.BlockHeader) error { return nil } -func (cs *ChainStore) PersistBlockHeaders(b ...*types.BlockHeader) (err error) { +func (cs *ChainStore) PersistBlockHeaders(b ...*types.BlockHeader) error { sbs := make([]block.Block, len(b)) for i, header := range b { + var err error sbs[i], err = header.ToStorageBlock() if err != nil { return err } } - return cs.bs.PutMany(sbs) + batchSize := 256 + calls := len(b) / batchSize + + var err error + for i := 0; i <= calls; i++ { + start := batchSize * i + end := start + batchSize + if end > len(b) { + end = len(b) + } + + err = multierr.Append(err, cs.bs.PutMany(sbs[start:end])) + } + + return err } type storable interface { @@ -507,7 +521,7 @@ func (cs *ChainStore) AddBlock(ctx context.Context, b *types.BlockHeader) error } if err := cs.MaybeTakeHeavierTipSet(ctx, ts); err != nil { - return errors.Wrap(err, "MaybeTakeHeavierTipSet failed") + return xerrors.Errorf("MaybeTakeHeavierTipSet failed: %w", err) } return nil @@ -537,6 +551,9 @@ func (cs *ChainStore) GetCMessage(c cid.Cid) (ChainMsg, error) { if err == nil { return m, nil } + if err != bstore.ErrNotFound { + log.Warn("GetCMessage: unexpected error getting unsigned message: %s", err) + } return cs.GetSignedMessage(c) } @@ -667,12 +684,12 @@ func (cs *ChainStore) readMsgMetaCids(mmc cid.Cid) ([]cid.Cid, []cid.Cid, error) blscids, err := cs.readAMTCids(msgmeta.BlsMessages) if err != nil { - return nil, nil, errors.Wrap(err, "loading bls message cids for block") + return nil, nil, xerrors.Errorf("loading bls message cids for block: %w", err) } secpkcids, err := cs.readAMTCids(msgmeta.SecpkMessages) if err != nil { - return nil, nil, errors.Wrap(err, "loading secpk message cids for block") + return nil, nil, xerrors.Errorf("loading secpk message cids for block: %w", err) } cs.mmCache.Add(mmc, &mmCids{ @@ -691,12 +708,12 @@ func (cs *ChainStore) MessagesForBlock(b *types.BlockHeader) ([]*types.Message, blsmsgs, err := cs.LoadMessagesFromCids(blscids) if err != nil { - return nil, nil, errors.Wrap(err, "loading bls messages for block") + return nil, nil, xerrors.Errorf("loading bls messages for block: %w", err) } secpkmsgs, err := cs.LoadSignedMessagesFromCids(secpkcids) if err != nil { - return nil, nil, errors.Wrap(err, "loading secpk messages for block") + return nil, nil, xerrors.Errorf("loading secpk messages for block: %w", err) } return blsmsgs, secpkmsgs, nil @@ -706,7 +723,7 @@ func (cs *ChainStore) GetParentReceipt(b *types.BlockHeader, i int) (*types.Mess bs := amt.WrapBlockstore(cs.bs) a, err := amt.LoadAMT(bs, b.ParentMessageReceipts) if err != nil { - return nil, errors.Wrap(err, "amt load") + return nil, xerrors.Errorf("amt load: %w", err) } var r types.MessageReceipt @@ -722,7 +739,7 @@ func (cs *ChainStore) LoadMessagesFromCids(cids []cid.Cid) ([]*types.Message, er for i, c := range cids { m, err := cs.GetMessage(c) if err != nil { - return nil, errors.Wrapf(err, "failed to get message: (%s):%d", c, i) + return nil, xerrors.Errorf("failed to get message: (%s):%d: %w", err, c, i) } msgs = append(msgs, m) @@ -736,7 +753,7 @@ func (cs *ChainStore) LoadSignedMessagesFromCids(cids []cid.Cid) ([]*types.Signe for i, c := range cids { m, err := cs.GetSignedMessage(c) if err != nil { - return nil, errors.Wrapf(err, "failed to get message: (%s):%d", c, i) + return nil, xerrors.Errorf("failed to get message: (%s):%d: %w", err, c, i) } msgs = append(msgs, m) @@ -771,24 +788,21 @@ func (cs *ChainStore) TryFillTipSet(ts *types.TipSet) (*FullTipSet, error) { return NewFullTipSet(out), nil } -func (cs *ChainStore) GetRandomness(ctx context.Context, blks []cid.Cid, tickets []*types.Ticket, lb int64) ([]byte, error) { +func drawRandomness(t *types.Ticket, round int64) []byte { + h := sha256.New() + var buf [8]byte + binary.LittleEndian.PutUint64(buf[:], uint64(round)) + + h.Write(t.VRFProof) + h.Write(buf[:]) + + return h.Sum(nil) +} + +func (cs *ChainStore) GetRandomness(ctx context.Context, blks []cid.Cid, round int64) ([]byte, error) { ctx, span := trace.StartSpan(ctx, "store.GetRandomness") defer span.End() - span.AddAttributes(trace.Int64Attribute("lb", lb)) - - if lb < 0 { - return nil, fmt.Errorf("negative lookback parameters are not valid (got %d)", lb) - } - lt := int64(len(tickets)) - if lb < lt { - log.Desugar().Warn("self sampling randomness. this should be extremely rare, if you see this often it may be a bug", zap.Stack("stacktrace")) - - t := tickets[lt-(1+lb)] - - return t.VRFProof, nil - } - - nv := lb - lt + span.AddAttributes(trace.Int64Attribute("round", round)) for { nts, err := cs.LoadTipSet(blks) @@ -797,26 +811,21 @@ func (cs *ChainStore) GetRandomness(ctx context.Context, blks []cid.Cid, tickets } mtb := nts.MinTicketBlock() - lt := int64(len(mtb.Tickets)) - if nv < lt { - t := mtb.Tickets[lt-(1+nv)] - return t.VRFProof, nil - } - nv -= lt + if int64(nts.Height()) <= round { + return drawRandomness(nts.MinTicketBlock().Ticket, round), nil + } // special case for lookback behind genesis block // TODO(spec): this is not in the spec, need to sync that if mtb.Height == 0 { - t := mtb.Tickets[0] + // round is negative + thash := drawRandomness(mtb.Ticket, round*-1) - rval := t.VRFProof - for i := int64(0); i < nv; i++ { - h := sha256.Sum256(rval) - rval = h[:] - } - return rval, nil + // for negative lookbacks, just use the hash of the positive tickethash value + h := sha256.Sum256(thash) + return h[:], nil } blks = mtb.Parents @@ -837,36 +846,33 @@ func (cs *ChainStore) GetTipsetByHeight(ctx context.Context, h uint64, ts *types } for { - mtb := ts.MinTicketBlock() - if h >= ts.Height()-uint64(len(mtb.Tickets)) { - return ts, nil - } - pts, err := cs.LoadTipSet(ts.Parents()) if err != nil { return nil, err } + + if h > pts.Height() { + return ts, nil + } + ts = pts } } type chainRand struct { - cs *ChainStore - blks []cid.Cid - bh uint64 - tickets []*types.Ticket + cs *ChainStore + blks []cid.Cid + bh uint64 } -func NewChainRand(cs *ChainStore, blks []cid.Cid, bheight uint64, tickets []*types.Ticket) vm.Rand { +func NewChainRand(cs *ChainStore, blks []cid.Cid, bheight uint64) vm.Rand { return &chainRand{ - cs: cs, - blks: blks, - bh: bheight, - tickets: tickets, + cs: cs, + blks: blks, + bh: bheight, } } -func (cr *chainRand) GetRandomness(ctx context.Context, h int64) ([]byte, error) { - lb := (int64(cr.bh) + int64(len(cr.tickets))) - h - return cr.cs.GetRandomness(ctx, cr.blks, cr.tickets, lb) +func (cr *chainRand) GetRandomness(ctx context.Context, round int64) ([]byte, error) { + return cr.cs.GetRandomness(ctx, cr.blks, round) } diff --git a/chain/store/weight.go b/chain/store/weight.go index 53c202845..72811ebec 100644 --- a/chain/store/weight.go +++ b/chain/store/weight.go @@ -2,12 +2,13 @@ package store import ( "context" + "math/big" + "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/vm" "golang.org/x/xerrors" - "math/big" ) var zero = types.NewInt(0) @@ -39,6 +40,7 @@ func (cs *ChainStore) Weight(ctx context.Context, ts *types.TipSet) (types.BigIn log2P = int64(tpow.BitLen() - 1) } else { // Not really expect to be here ... + panic("where are we") return types.EmptyInt, xerrors.Errorf("All power in the net is gone. You network might be disconnected, or the net is dead!") } @@ -57,7 +59,7 @@ func (cs *ChainStore) Weight(ctx context.Context, ts *types.TipSet) (types.BigIn func (cs *ChainStore) call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) { bstate := ts.ParentState() - r := NewChainRand(cs, ts.Cids(), ts.Height(), nil) + r := NewChainRand(cs, ts.Cids(), ts.Height()) vmi, err := vm.NewVM(bstate, ts.Height(), r, actors.NetworkAddress, cs.bs) if err != nil { diff --git a/chain/sync.go b/chain/sync.go index e1736a24d..2519ab71c 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -3,14 +3,15 @@ package chain import ( "bytes" "context" + "crypto/sha256" "errors" "fmt" "sync" "time" "github.com/Gurpartap/async" + bls "github.com/filecoin-project/filecoin-ffi" amt "github.com/filecoin-project/go-amt-ipld" - "github.com/filecoin-project/go-bls-sigs" "github.com/hashicorp/go-multierror" "github.com/ipfs/go-cid" dstore "github.com/ipfs/go-datastore" @@ -28,10 +29,12 @@ import ( "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/address" "github.com/filecoin-project/lotus/chain/blocksync" + "github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/sectorbuilder" ) var log = logging.Logger("chain") @@ -506,33 +509,8 @@ func (syncer *Syncer) minerIsValid(ctx context.Context, maddr address.Address, b return nil } -func (syncer *Syncer) validateTickets(ctx context.Context, mworker address.Address, tickets []*types.Ticket, base *types.TipSet) error { - ctx, span := trace.StartSpan(ctx, "validateTickets") - defer span.End() - span.AddAttributes(trace.Int64Attribute("tickets", int64(len(tickets)))) - - if len(tickets) == 0 { - return xerrors.Errorf("block had no tickets") - } - - cur := base.MinTicket() - for i := 0; i < len(tickets); i++ { - next := tickets[i] - - sig := &types.Signature{ - Type: types.KTBLS, - Data: next.VRFProof, - } - - // TODO: ticket signatures should also include miner address - if err := sig.Verify(mworker, cur.VRFProof); err != nil { - return xerrors.Errorf("invalid ticket, VRFProof invalid: %w", err) - } - - cur = next - } - - return nil +func (syncer *Syncer) validateTicket(ctx context.Context, maddr, mworker address.Address, ticket *types.Ticket, base *types.TipSet) error { + return gen.VerifyVRF(ctx, mworker, maddr, gen.DSepTicket, base.MinTicket().VRFProof, ticket.VRFProof) } var ErrTemporal = errors.New("temporal error") @@ -541,6 +519,9 @@ var ErrTemporal = errors.New("temporal error") func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) error { ctx, span := trace.StartSpan(ctx, "validateBlock") defer span.End() + if build.InsecurePoStValidation { + log.Warn("insecure test validation is enabled, if you see this outside of a test, it is a severe bug!") + } h := b.Header @@ -550,23 +531,34 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err } // fast checks first + if h.BlockSig == nil { + return xerrors.Errorf("block had nil signature") + } + if h.Timestamp > uint64(time.Now().Unix()+build.AllowableClockDrift) { return xerrors.Errorf("block was from the future") } - if h.Timestamp < baseTs.MinTimestamp()+uint64(build.BlockDelay*len(h.Tickets)) { - log.Warn("timestamp funtimes: ", h.Timestamp, baseTs.MinTimestamp(), len(h.Tickets)) - return xerrors.Errorf("block was generated too soon (h.ts:%d < base.mints:%d + BLOCK_DELAY:%d * tkts.len:%d)", h.Timestamp, baseTs.MinTimestamp(), build.BlockDelay, len(h.Tickets)) + if h.Timestamp < baseTs.MinTimestamp()+(build.BlockDelay*(h.Height-baseTs.Height())) { + log.Warn("timestamp funtimes: ", h.Timestamp, baseTs.MinTimestamp(), h.Height, baseTs.Height()) + return xerrors.Errorf("block was generated too soon (h.ts:%d < base.mints:%d + BLOCK_DELAY:%d * deltaH:%d)", h.Timestamp, baseTs.MinTimestamp(), build.BlockDelay, h.Height-baseTs.Height()) } winnerCheck := async.Err(func() error { - mpow, tpow, err := stmgr.GetPower(ctx, syncer.sm, baseTs, h.Miner) + _, tpow, err := stmgr.GetPower(ctx, syncer.sm, baseTs, h.Miner) if err != nil { return xerrors.Errorf("failed getting power: %w", err) } - if !types.PowerCmp(h.ElectionProof, mpow, tpow) { - return xerrors.Errorf("miner created a block but was not a winner") + ssize, err := stmgr.GetMinerSectorSize(ctx, syncer.sm, baseTs, h.Miner) + if err != nil { + return xerrors.Errorf("failed to get sector size for block miner: %w", err) + } + + for _, t := range h.EPostProof.Candidates { + if !types.IsTicketWinner(t.Partial, ssize, tpow, 1) { + return xerrors.Errorf("miner created a block but was not a winner") + } } return nil }) @@ -623,20 +615,20 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err }) tktsCheck := async.Err(func() error { - if err := syncer.validateTickets(ctx, waddr, h.Tickets, baseTs); err != nil { + vrfBase := baseTs.MinTicket().VRFProof + + err := gen.VerifyVRF(ctx, waddr, h.Miner, gen.DSepTicket, vrfBase, h.Ticket.VRFProof) + + if err != nil { + log.Warnf("BAD TICKET: %d %x %x %s %s %x", h.Height, h.Ticket.VRFProof, vrfBase, waddr, h.Miner, baseTs.MinTicket().VRFProof) return xerrors.Errorf("validating block tickets failed: %w", err) } return nil }) eproofCheck := async.Err(func() error { - rand, err := syncer.sm.ChainStore().GetRandomness(ctx, baseTs.Cids(), h.Tickets, build.EcRandomnessLookback) - if err != nil { - return xerrors.Errorf("failed to get randomness for verifying election proof: %w", err) - } - - if err := VerifyElectionProof(ctx, h.ElectionProof, rand, waddr); err != nil { - return xerrors.Errorf("checking eproof failed: %w", err) + if err := syncer.VerifyElectionPoStProof(ctx, h, baseTs, waddr); err != nil { + return xerrors.Errorf("invalid election post: %w", err) } return nil }) @@ -660,6 +652,56 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err return merr } +func (syncer *Syncer) VerifyElectionPoStProof(ctx context.Context, h *types.BlockHeader, baseTs *types.TipSet, waddr address.Address) error { + rand, err := syncer.sm.ChainStore().GetRandomness(ctx, baseTs.Cids(), int64(h.Height-build.EcRandomnessLookback)) + if err != nil { + return xerrors.Errorf("failed to get randomness for verifying election proof: %w", err) + } + + if err := VerifyElectionPoStVRF(ctx, h.EPostProof.PostRand, rand, waddr, h.Miner); err != nil { + return xerrors.Errorf("checking eproof failed: %w", err) + } + + ssize, err := stmgr.GetMinerSectorSize(ctx, syncer.sm, baseTs, h.Miner) + if err != nil { + return xerrors.Errorf("failed to get sector size for miner: %w", err) + } + + var winners []sectorbuilder.EPostCandidate + for _, t := range h.EPostProof.Candidates { + var partial [32]byte + copy(partial[:], t.Partial) + winners = append(winners, sectorbuilder.EPostCandidate{ + PartialTicket: partial, + SectorID: t.SectorID, + SectorChallengeIndex: t.ChallengeIndex, + }) + } + + sectorInfo, err := stmgr.GetSectorsForElectionPost(ctx, syncer.sm, baseTs, h.Miner) + if err != nil { + return xerrors.Errorf("getting election post sector set: %w", err) + } + + if build.InsecurePoStValidation { + if string(h.EPostProof.Proof) == "valid proof" { + return nil + } + return xerrors.Errorf("[TESTING] election post was invalid") + } + hvrf := sha256.Sum256(h.EPostProof.PostRand) + ok, err := sectorbuilder.VerifyElectionPost(ctx, ssize, *sectorInfo, hvrf[:], h.EPostProof.Proof, winners, h.Miner) + if err != nil { + return xerrors.Errorf("failed to verify election post: %w", err) + } + + if !ok { + return xerrors.Errorf("election post was invalid") + } + + return nil +} + func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock, baseTs *types.TipSet) error { nonces := make(map[address.Address]uint64) balances := make(map[address.Address]types.BigInt) @@ -1103,14 +1145,9 @@ func (syncer *Syncer) collectChain(ctx context.Context, ts *types.TipSet) error return nil } -func VerifyElectionProof(ctx context.Context, eproof []byte, rand []byte, worker address.Address) error { - sig := types.Signature{ - Data: eproof, - Type: types.KTBLS, - } - - if err := sig.Verify(worker, rand); err != nil { - return xerrors.Errorf("failed to verify election proof signature: %w", err) +func VerifyElectionPoStVRF(ctx context.Context, evrf []byte, rand []byte, worker, miner address.Address) error { + if err := gen.VerifyVRF(ctx, worker, miner, gen.DSepElectionPost, rand, evrf); err != nil { + return xerrors.Errorf("failed to verify post_randomness vrf: %w", err) } return nil diff --git a/chain/sync_test.go b/chain/sync_test.go index d17b6e853..f161316b9 100644 --- a/chain/sync_test.go +++ b/chain/sync_test.go @@ -23,6 +23,10 @@ import ( "github.com/filecoin-project/lotus/node/repo" ) +func init() { + build.InsecurePoStValidation = true +} + const source = 0 func (tu *syncTestUtil) repoWithChain(t testing.TB, h int) (repo.Repo, []byte, []*store.FullTipSet) { @@ -342,7 +346,7 @@ func (tu *syncTestUtil) waitUntilSyncTarget(to int, target *types.TipSet) { } func TestSyncSimple(t *testing.T) { - H := 50 + H := 2 tu := prepSyncTest(t, H) client := tu.addClientNode() @@ -390,7 +394,7 @@ func TestSyncBadTimestamp(t *testing.T) { tu.waitUntilSync(0, client) base := tu.g.CurTipset - tu.g.Timestamper = func(pts *types.TipSet, tl int) uint64 { + tu.g.Timestamper = func(pts *types.TipSet, tl uint64) uint64 { return pts.MinTimestamp() + (build.BlockDelay / 2) } diff --git a/chain/types/blockheader.go b/chain/types/blockheader.go index aa311db0c..79585f1d2 100644 --- a/chain/types/blockheader.go +++ b/chain/types/blockheader.go @@ -20,14 +20,24 @@ type Ticket struct { VRFProof []byte } -type ElectionProof []byte +type EPostTicket struct { + Partial []byte + SectorID uint64 + ChallengeIndex uint64 +} + +type EPostProof struct { + Proof []byte + PostRand []byte + Candidates []EPostTicket +} type BlockHeader struct { Miner address.Address - Tickets []*Ticket + Ticket *Ticket - ElectionProof []byte + EPostProof EPostProof Parents []cid.Cid @@ -45,7 +55,7 @@ type BlockHeader struct { Timestamp uint64 - BlockSig Signature + BlockSig *Signature } func (b *BlockHeader) ToStorageBlock() (block.Block, error) { @@ -91,12 +101,12 @@ func (blk *BlockHeader) Serialize() ([]byte, error) { } func (blk *BlockHeader) LastTicket() *Ticket { - return blk.Tickets[len(blk.Tickets)-1] + return blk.Ticket } func (blk *BlockHeader) SigningBytes() ([]byte, error) { blkcopy := *blk - blkcopy.BlockSig = Signature{} + blkcopy.BlockSig = nil return blkcopy.Serialize() } @@ -162,29 +172,31 @@ func CidArrsEqual(a, b []cid.Cid) bool { var blocksPerEpoch = NewInt(build.BlocksPerEpoch) -func PowerCmp(eproof ElectionProof, mpow, totpow BigInt) bool { +func IsTicketWinner(partialTicket []byte, ssizeI uint64, totpow BigInt, sampleRate int64) bool { + ssize := NewInt(ssizeI) /* Need to check that - (h(vrfout) + 1) / (max(h) + 1) <= e * minerPower / totalPower + (h(vrfout) + 1) / (max(h) + 1) <= e * sectorSize / totalPower max(h) == 2^256-1 which in terms of integer math means: - (h(vrfout) + 1) * totalPower <= e * minerPower * 2^256 + (h(vrfout) + 1) * totalPower <= e * sectorSize * 2^256 in 2^256 space, it is equivalent to: - h(vrfout) * totalPower < e * minerPower * 2^256 + h(vrfout) * totalPower < e * sectorSize * 2^256 */ - h := sha256.Sum256(eproof) + h := sha256.Sum256(partialTicket) lhs := BigFromBytes(h[:]).Int lhs = lhs.Mul(lhs, totpow.Int) + lhs = lhs.Mul(lhs, big.NewInt(sampleRate)) - // rhs = minerPower * 2^256 - // rhs = minerPower << 256 - rhs := new(big.Int).Lsh(mpow.Int, 256) + // rhs = sectorSize * 2^256 + // rhs = sectorSize << 256 + rhs := new(big.Int).Lsh(ssize.Int, 256) rhs = rhs.Mul(rhs, blocksPerEpoch.Int) - // h(vrfout) * totalPower < e * minerPower * 2^256? + // h(vrfout) * totalPower < e * sectorSize * 2^256? return lhs.Cmp(rhs) == -1 } diff --git a/chain/types/blockheader_test.go b/chain/types/blockheader_test.go index 09ca061b9..54ec1da09 100644 --- a/chain/types/blockheader_test.go +++ b/chain/types/blockheader_test.go @@ -2,6 +2,7 @@ package types import ( "bytes" + "fmt" "reflect" "testing" @@ -23,12 +24,13 @@ func testBlockHeader(t testing.TB) *BlockHeader { } return &BlockHeader{ - Miner: addr, - ElectionProof: []byte("cats won the election"), - Tickets: []*Ticket{ - &Ticket{ - VRFProof: []byte("vrf proof0000000vrf proof0000000"), - }, + Miner: addr, + EPostProof: EPostProof{ + Proof: []byte("pruuf"), + PostRand: []byte("random"), + }, + Ticket: &Ticket{ + VRFProof: []byte("vrf proof0000000vrf proof0000000"), }, Parents: []cid.Cid{c, c}, ParentMessageReceipts: c, @@ -37,7 +39,7 @@ func testBlockHeader(t testing.TB) *BlockHeader { Messages: c, Height: 85919298723, ParentStateRoot: c, - BlockSig: Signature{Type: KTBLS, Data: []byte("boo! im a signature")}, + BlockSig: &Signature{Type: KTBLS, Data: []byte("boo! im a signature")}, } } @@ -55,6 +57,8 @@ func TestBlockHeaderSerialization(t *testing.T) { } if !reflect.DeepEqual(&out, bh) { + fmt.Printf("%#v\n", &out) + fmt.Printf("%#v\n", bh) t.Fatal("not equal") } } diff --git a/chain/types/cbor_gen.go b/chain/types/cbor_gen.go index fa3cddaf4..d2b60f268 100644 --- a/chain/types/cbor_gen.go +++ b/chain/types/cbor_gen.go @@ -5,7 +5,7 @@ import ( "io" "math" - cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" xerrors "golang.org/x/xerrors" ) @@ -28,21 +28,13 @@ func (t *BlockHeader) MarshalCBOR(w io.Writer) error { return err } - // t.t.Tickets ([]*types.Ticket) (slice) - if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Tickets)))); err != nil { + // t.t.Ticket (types.Ticket) (struct) + if err := t.Ticket.MarshalCBOR(w); err != nil { return err } - for _, v := range t.Tickets { - if err := v.MarshalCBOR(w); err != nil { - return err - } - } - // t.t.ElectionProof ([]uint8) (slice) - if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(t.ElectionProof)))); err != nil { - return err - } - if _, err := w.Write(t.ElectionProof); err != nil { + // t.t.EPostProof (types.EPostProof) (struct) + if err := t.EPostProof.MarshalCBOR(w); err != nil { return err } @@ -125,48 +117,35 @@ func (t *BlockHeader) UnmarshalCBOR(r io.Reader) error { } } - // t.t.Tickets ([]*types.Ticket) (slice) + // t.t.Ticket (types.Ticket) (struct) - maj, extra, err = cbg.CborReadHeader(br) - if err != nil { - return err - } - if extra > 8192 { - return fmt.Errorf("t.Tickets: array too large (%d)", extra) - } + { - if maj != cbg.MajArray { - return fmt.Errorf("expected cbor array") - } - if extra > 0 { - t.Tickets = make([]*Ticket, extra) - } - for i := 0; i < int(extra); i++ { + pb, err := br.PeekByte() + if err != nil { + return err + } + if pb == cbg.CborNull[0] { + var nbuf [1]byte + if _, err := br.Read(nbuf[:]); err != nil { + return err + } + } else { + t.Ticket = new(Ticket) + if err := t.Ticket.UnmarshalCBOR(br); err != nil { + return err + } + } - var v Ticket - if err := v.UnmarshalCBOR(br); err != nil { + } + // t.t.EPostProof (types.EPostProof) (struct) + + { + + if err := t.EPostProof.UnmarshalCBOR(br); err != nil { return err } - t.Tickets[i] = &v - } - - // t.t.ElectionProof ([]uint8) (slice) - - maj, extra, err = cbg.CborReadHeader(br) - if err != nil { - return err - } - if extra > 8192 { - return fmt.Errorf("t.ElectionProof: array too large (%d)", extra) - } - - if maj != cbg.MajByteString { - return fmt.Errorf("expected byte array") - } - t.ElectionProof = make([]byte, extra) - if _, err := io.ReadFull(br, t.ElectionProof); err != nil { - return err } // t.t.Parents ([]cid.Cid) (slice) @@ -271,9 +250,21 @@ func (t *BlockHeader) UnmarshalCBOR(r io.Reader) error { { - if err := t.BlockSig.UnmarshalCBOR(br); err != nil { + pb, err := br.PeekByte() + if err != nil { return err } + if pb == cbg.CborNull[0] { + var nbuf [1]byte + if _, err := br.Read(nbuf[:]); err != nil { + return err + } + } else { + t.BlockSig = new(Signature) + if err := t.BlockSig.UnmarshalCBOR(br); err != nil { + return err + } + } } return nil @@ -333,6 +324,205 @@ func (t *Ticket) UnmarshalCBOR(r io.Reader) error { return nil } +func (t *EPostProof) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + if _, err := w.Write([]byte{131}); err != nil { + return err + } + + // t.t.Proof ([]uint8) (slice) + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(t.Proof)))); err != nil { + return err + } + if _, err := w.Write(t.Proof); err != nil { + return err + } + + // t.t.PostRand ([]uint8) (slice) + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(t.PostRand)))); err != nil { + return err + } + if _, err := w.Write(t.PostRand); err != nil { + return err + } + + // t.t.Candidates ([]types.EPostTicket) (slice) + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Candidates)))); err != nil { + return err + } + for _, v := range t.Candidates { + if err := v.MarshalCBOR(w); err != nil { + return err + } + } + return nil +} + +func (t *EPostProof) UnmarshalCBOR(r io.Reader) error { + br := cbg.GetPeeker(r) + + maj, extra, err := cbg.CborReadHeader(br) + if err != nil { + return err + } + if maj != cbg.MajArray { + return fmt.Errorf("cbor input should be of type array") + } + + if extra != 3 { + return fmt.Errorf("cbor input had wrong number of fields") + } + + // t.t.Proof ([]uint8) (slice) + + maj, extra, err = cbg.CborReadHeader(br) + if err != nil { + return err + } + if extra > 8192 { + return fmt.Errorf("t.Proof: array too large (%d)", extra) + } + + if maj != cbg.MajByteString { + return fmt.Errorf("expected byte array") + } + t.Proof = make([]byte, extra) + if _, err := io.ReadFull(br, t.Proof); err != nil { + return err + } + // t.t.PostRand ([]uint8) (slice) + + maj, extra, err = cbg.CborReadHeader(br) + if err != nil { + return err + } + if extra > 8192 { + return fmt.Errorf("t.PostRand: array too large (%d)", extra) + } + + if maj != cbg.MajByteString { + return fmt.Errorf("expected byte array") + } + t.PostRand = make([]byte, extra) + if _, err := io.ReadFull(br, t.PostRand); err != nil { + return err + } + // t.t.Candidates ([]types.EPostTicket) (slice) + + maj, extra, err = cbg.CborReadHeader(br) + if err != nil { + return err + } + if extra > 8192 { + return fmt.Errorf("t.Candidates: array too large (%d)", extra) + } + + if maj != cbg.MajArray { + return fmt.Errorf("expected cbor array") + } + if extra > 0 { + t.Candidates = make([]EPostTicket, extra) + } + for i := 0; i < int(extra); i++ { + + var v EPostTicket + if err := v.UnmarshalCBOR(br); err != nil { + return err + } + + t.Candidates[i] = v + } + + return nil +} + +func (t *EPostTicket) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + if _, err := w.Write([]byte{131}); err != nil { + return err + } + + // t.t.Partial ([]uint8) (slice) + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(t.Partial)))); err != nil { + return err + } + if _, err := w.Write(t.Partial); err != nil { + return err + } + + // t.t.SectorID (uint64) (uint64) + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.SectorID))); err != nil { + return err + } + + // t.t.ChallengeIndex (uint64) (uint64) + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.ChallengeIndex))); err != nil { + return err + } + return nil +} + +func (t *EPostTicket) UnmarshalCBOR(r io.Reader) error { + br := cbg.GetPeeker(r) + + maj, extra, err := cbg.CborReadHeader(br) + if err != nil { + return err + } + if maj != cbg.MajArray { + return fmt.Errorf("cbor input should be of type array") + } + + if extra != 3 { + return fmt.Errorf("cbor input had wrong number of fields") + } + + // t.t.Partial ([]uint8) (slice) + + maj, extra, err = cbg.CborReadHeader(br) + if err != nil { + return err + } + if extra > 8192 { + return fmt.Errorf("t.Partial: array too large (%d)", extra) + } + + if maj != cbg.MajByteString { + return fmt.Errorf("expected byte array") + } + t.Partial = make([]byte, extra) + if _, err := io.ReadFull(br, t.Partial); err != nil { + return err + } + // t.t.SectorID (uint64) (uint64) + + maj, extra, err = cbg.CborReadHeader(br) + if err != nil { + return err + } + if maj != cbg.MajUnsignedInt { + return fmt.Errorf("wrong type for uint64 field") + } + t.SectorID = uint64(extra) + // t.t.ChallengeIndex (uint64) (uint64) + + maj, extra, err = cbg.CborReadHeader(br) + if err != nil { + return err + } + if maj != cbg.MajUnsignedInt { + return fmt.Errorf("wrong type for uint64 field") + } + t.ChallengeIndex = uint64(extra) + return nil +} + func (t *Message) MarshalCBOR(w io.Writer) error { if t == nil { _, err := w.Write(cbg.CborNull) diff --git a/chain/types/mock/chain.go b/chain/types/mock/chain.go index 09b37370a..37d2e8a53 100644 --- a/chain/types/mock/chain.go +++ b/chain/types/mock/chain.go @@ -34,12 +34,12 @@ func MkBlock(parents *types.TipSet, weightInc uint64, ticketNonce uint64) *types } return &types.BlockHeader{ - Miner: addr, - ElectionProof: []byte("cats won the election"), - Tickets: []*types.Ticket{ - { - VRFProof: []byte(fmt.Sprintf("====%d=====", ticketNonce)), - }, + Miner: addr, + EPostProof: types.EPostProof{ + Proof: []byte("election post proof proof"), + }, + Ticket: &types.Ticket{ + VRFProof: []byte(fmt.Sprintf("====%d=====", ticketNonce)), }, Parents: pcids, ParentMessageReceipts: c, @@ -48,7 +48,7 @@ func MkBlock(parents *types.TipSet, weightInc uint64, ticketNonce uint64) *types Messages: c, Height: height, ParentStateRoot: c, - BlockSig: types.Signature{Type: types.KTBLS, Data: []byte("boo! im a signature")}, + BlockSig: &types.Signature{Type: types.KTBLS, Data: []byte("boo! im a signature")}, } } diff --git a/chain/types/signature_cgo.go b/chain/types/signature_cgo.go index 901cab944..4b5a262f6 100644 --- a/chain/types/signature_cgo.go +++ b/chain/types/signature_cgo.go @@ -5,7 +5,7 @@ package types import ( "fmt" - bls "github.com/filecoin-project/go-bls-sigs" + bls "github.com/filecoin-project/filecoin-ffi" "github.com/filecoin-project/lotus/chain/address" "github.com/filecoin-project/lotus/lib/crypto" "github.com/minio/blake2b-simd" diff --git a/chain/types/tipset.go b/chain/types/tipset.go index ca6941e5c..1ed15289e 100644 --- a/chain/types/tipset.go +++ b/chain/types/tipset.go @@ -165,8 +165,7 @@ func (t *Ticket) Less(o *Ticket) bool { } func (ts *TipSet) MinTicket() *Ticket { - b := ts.MinTicketBlock() - return b.Tickets[len(b.Tickets)-1] + return ts.MinTicketBlock().Ticket } func (ts *TipSet) MinTimestamp() uint64 { diff --git a/chain/types/vmcontext.go b/chain/types/vmcontext.go index 388396881..5e2963483 100644 --- a/chain/types/vmcontext.go +++ b/chain/types/vmcontext.go @@ -3,7 +3,7 @@ package types import ( "context" - amt "github.com/filecoin-project/go-amt-ipld" + "github.com/filecoin-project/go-amt-ipld" "github.com/filecoin-project/lotus/chain/actors/aerrors" "github.com/filecoin-project/lotus/chain/address" cid "github.com/ipfs/go-cid" @@ -48,8 +48,8 @@ type storageWrapper struct { s Storage } -func (sw *storageWrapper) Put(i interface{}) (cid.Cid, error) { - c, err := sw.s.Put(i.(cbg.CBORMarshaler)) +func (sw *storageWrapper) Put(i cbg.CBORMarshaler) (cid.Cid, error) { + c, err := sw.s.Put(i) if err != nil { return cid.Undef, err } @@ -57,8 +57,8 @@ func (sw *storageWrapper) Put(i interface{}) (cid.Cid, error) { return c, nil } -func (sw *storageWrapper) Get(c cid.Cid, out interface{}) error { - if err := sw.s.Get(c, out.(cbg.CBORUnmarshaler)); err != nil { +func (sw *storageWrapper) Get(c cid.Cid, out cbg.CBORUnmarshaler) error { + if err := sw.s.Get(c, out); err != nil { return err } diff --git a/chain/validation/message.go b/chain/validation/message.go index 99ffb9b64..f9728708f 100644 --- a/chain/validation/message.go +++ b/chain/validation/message.go @@ -3,11 +3,10 @@ package validation import ( "context" - "github.com/pkg/errors" - "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/address" "github.com/filecoin-project/lotus/chain/types" + "golang.org/x/xerrors" "github.com/filecoin-project/chain-validation/pkg/chain" "github.com/filecoin-project/chain-validation/pkg/state" @@ -43,7 +42,7 @@ func (mf *MessageFactory) MakeMessage(from, to state.Address, method chain.Metho } if int(method) >= len(methods) { - return nil, errors.Errorf("No method name for method %v", method) + return nil, xerrors.Errorf("No method name for method %v", method) } methodId := methods[method] msg := &types.Message{ diff --git a/chain/validation/state.go b/chain/validation/state.go index 9886c43f7..087c05b21 100644 --- a/chain/validation/state.go +++ b/chain/validation/state.go @@ -5,11 +5,10 @@ import ( "fmt" "math/rand" - "github.com/pkg/errors" - "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/vm" + "golang.org/x/xerrors" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" @@ -109,7 +108,7 @@ func (s *StateWrapper) SetActor(addr vstate.Address, code vstate.ActorCodeID, ba // The ID-based address is dropped here, but should be reported back to the caller. _, err = tree.RegisterNewAddress(addrInt, &actr.Actor) if err != nil { - return nil, nil, errors.Wrapf(err, "register new address for actor") + return nil, nil, xerrors.Errorf("register new address for actor: %w", err) } return actr, s.storage, s.flush(tree) } @@ -131,17 +130,24 @@ func (s *StateWrapper) SetSingletonActor(addr vstate.SingletonActorID, balance v return nil, nil, err } if err := tree.SetActor(actors.InitAddress, initact); err != nil { - return nil, nil, errors.Wrapf(err, "set init actor") + return nil, nil, xerrors.Errorf("set init actor: %w", err) } return &actorWrapper{*initact}, s.storage, s.flush(tree) case actors.StorageMarketAddress: - smact, err := gen.SetupStorageMarketActor(s.bs) + nsroot, err := gen.SetupStorageMarketActor(s.bs, s.stateRoot, nil) if err != nil { return nil, nil, err } - if err := tree.SetActor(actors.StorageMarketAddress, smact); err != nil { - return nil, nil, errors.Wrapf(err, "set network storage market actor") + s.stateRoot = nsroot + + tree, err = state.LoadStateTree(s.cst, s.stateRoot) + if err != nil { + return nil, nil, err + } + smact, err := tree.GetActor(actors.StorageMarketAddress) + if err != nil { + return nil, nil, err } return &actorWrapper{*smact}, s.storage, s.flush(tree) case actors.StoragePowerAddress: @@ -150,7 +156,7 @@ func (s *StateWrapper) SetSingletonActor(addr vstate.SingletonActorID, balance v return nil, nil, err } if err := tree.SetActor(actors.StoragePowerAddress, spact); err != nil { - return nil, nil, errors.Wrapf(err, "set network storage market actor") + return nil, nil, xerrors.Errorf("set network storage market actor: %w", err) } return &actorWrapper{*spact}, s.storage, s.flush(tree) case actors.NetworkAddress: @@ -160,7 +166,7 @@ func (s *StateWrapper) SetSingletonActor(addr vstate.SingletonActorID, balance v Head: vm.EmptyObjectCid, } if err := tree.SetActor(actors.NetworkAddress, ntwkact); err != nil { - return nil, nil, errors.Wrapf(err, "set network actor") + return nil, nil, xerrors.Errorf("set network actor: %w", err) } return &actorWrapper{*ntwkact}, s.storage, s.flush(tree) case actors.BurntFundsAddress: @@ -170,11 +176,11 @@ func (s *StateWrapper) SetSingletonActor(addr vstate.SingletonActorID, balance v Head: vm.EmptyObjectCid, } if err := tree.SetActor(actors.BurntFundsAddress, ntwkact); err != nil { - return nil, nil, errors.Wrapf(err, "set network actor") + return nil, nil, xerrors.Errorf("set network actor: %w", err) } return &actorWrapper{*ntwkact}, s.storage, s.flush(tree) default: - return nil, nil, errors.Errorf("%v is not a singleton actor address", addr) + return nil, nil, xerrors.Errorf("%v is not a singleton actor address", addr) } } diff --git a/chain/vm/invoker.go b/chain/vm/invoker.go index 3fa02d4c1..7dca9f96c 100644 --- a/chain/vm/invoker.go +++ b/chain/vm/invoker.go @@ -42,9 +42,13 @@ func newInvoker() *invoker { func (inv *invoker) Invoke(act *types.Actor, vmctx types.VMContext, method uint64, params []byte) ([]byte, aerrors.ActorError) { + if act.Code == actors.AccountCodeCid { + return nil, aerrors.Newf(254, "cannot invoke methods on account actors") + } + code, ok := inv.builtInCode[act.Code] if !ok { - log.Errorf("no code for actor %s", act.Code) + log.Errorf("no code for actor %s (Addr: %s)", act.Code, vmctx.Message().To) return nil, aerrors.Newf(255, "no code for actor %s(%d)(%s)", act.Code, method, hex.EncodeToString(params)) } if method >= uint64(len(code)) || code[method] == nil { @@ -159,7 +163,7 @@ func DumpActorState(code cid.Cid, b []byte) (interface{}, error) { typ, ok := i.builtInState[code] if !ok { - return nil, xerrors.New("state type for actor not found") + return nil, xerrors.Errorf("state type for actor %s not found", code) } rv := reflect.New(typ) diff --git a/chain/vm/vm_test.go b/chain/vm/vm_test.go index 7e4dd3e12..862bb4acf 100644 --- a/chain/vm/vm_test.go +++ b/chain/vm/vm_test.go @@ -12,7 +12,7 @@ import ( func TestBlockReward(t *testing.T) { coffer := types.FromFil(build.MiningRewardTotal).Int sum := new(big.Int) - N := build.HalvingPeriodBlocks + N := build.HalvingPeriodEpochs for i := 0; i < N; i++ { a := MiningReward(types.BigInt{coffer}) sum = sum.Add(sum, a.Int) diff --git a/chain/wallet/wallet.go b/chain/wallet/wallet.go index eeb1af1cb..9ca4d2e0a 100644 --- a/chain/wallet/wallet.go +++ b/chain/wallet/wallet.go @@ -7,7 +7,7 @@ import ( "strings" "sync" - "github.com/filecoin-project/go-bls-sigs" + bls "github.com/filecoin-project/filecoin-ffi" logging "github.com/ipfs/go-log" "github.com/minio/blake2b-simd" @@ -41,6 +41,17 @@ func NewWallet(keystore types.KeyStore) (*Wallet, error) { return w, nil } +func KeyWallet(keys ...*Key) *Wallet { + m := make(map[address.Address]*Key) + for _, key := range keys { + m[key.Address] = key + } + + return &Wallet{ + keys: m, + } +} + func (w *Wallet) Sign(ctx context.Context, addr address.Address, msg []byte) (*types.Signature, error) { ki, err := w.findKey(addr) if err != nil { @@ -85,6 +96,11 @@ func (w *Wallet) findKey(addr address.Address) (*Key, error) { if ok { return k, nil } + if w.keystore == nil { + log.Warn("findKey didn't find the key in in-memory wallet") + return nil, nil + } + ki, err := w.keystore.Get(KNamePrefix + addr.String()) if err != nil { if xerrors.Is(err, types.ErrKeyInfoNotFound) { diff --git a/cli/cmd.go b/cli/cmd.go index 472eb9732..4a3e7efac 100644 --- a/cli/cmd.go +++ b/cli/cmd.go @@ -143,7 +143,6 @@ var Commands = []*cli.Command{ sendCmd, stateCmd, syncCmd, - unregisterMinerCmd, versionCmd, walletCmd, } diff --git a/cli/miner.go b/cli/miner.go deleted file mode 100644 index 074faf962..000000000 --- a/cli/miner.go +++ /dev/null @@ -1,32 +0,0 @@ -package cli - -import ( - "fmt" - - "github.com/filecoin-project/lotus/chain/address" - "gopkg.in/urfave/cli.v2" -) - -var unregisterMinerCmd = &cli.Command{ - Name: "unregister-miner", - Usage: "Manually unregister miner actor", - Action: func(cctx *cli.Context) error { - api, closer, err := GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer closer() - ctx := ReqContext(cctx) - - if !cctx.Args().Present() { - return fmt.Errorf("must pass address of miner to unregister") - } - - maddr, err := address.NewFromString(cctx.Args().First()) - if err != nil { - return err - } - - return api.MinerUnregister(ctx, maddr) - }, -} diff --git a/cli/mpool.go b/cli/mpool.go index f01732884..a2b4324f6 100644 --- a/cli/mpool.go +++ b/cli/mpool.go @@ -4,7 +4,11 @@ import ( "encoding/json" "fmt" + "golang.org/x/xerrors" "gopkg.in/urfave/cli.v2" + + "github.com/filecoin-project/lotus/chain/address" + "github.com/filecoin-project/lotus/chain/types" ) var mpoolCmd = &cli.Command{ @@ -13,6 +17,7 @@ var mpoolCmd = &cli.Command{ Subcommands: []*cli.Command{ mpoolPending, mpoolSub, + mpoolStat, }, } @@ -76,3 +81,75 @@ var mpoolSub = &cli.Command{ } }, } + +type statBucket struct { + msgs map[uint64]*types.SignedMessage +} + +var mpoolStat = &cli.Command{ + Name: "stat", + Usage: "print mempool stats", + Action: func(cctx *cli.Context) error { + api, closer, err := GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + + ctx := ReqContext(cctx) + + ts, err := api.ChainHead(ctx) + if err != nil { + return xerrors.Errorf("getting chain head: %w", err) + } + + msgs, err := api.MpoolPending(ctx, nil) + if err != nil { + return err + } + + buckets := map[address.Address]*statBucket{} + + for _, v := range msgs { + bkt, ok := buckets[v.Message.From] + if !ok { + bkt = &statBucket{ + msgs: map[uint64]*types.SignedMessage{}, + } + buckets[v.Message.From] = bkt + } + + bkt.msgs[v.Message.Nonce] = v + } + for a, bkt := range buckets { + act, err := api.StateGetActor(ctx, a, ts) + if err != nil { + return err + } + + cur := act.Nonce + for { + _, ok := bkt.msgs[cur] + if !ok { + break + } + cur++ + } + + past := 0 + future := 0 + for _, m := range bkt.msgs { + if m.Message.Nonce < act.Nonce { + past++ + } + if m.Message.Nonce > cur { + future++ + } + } + + fmt.Printf("%s, past: %d, cur: %d, future: %d\n", a, past, cur-act.Nonce, future) + } + + return nil + }, +} diff --git a/cmd/lotus-bench/main.go b/cmd/lotus-bench/main.go new file mode 100644 index 000000000..be640ba70 --- /dev/null +++ b/cmd/lotus-bench/main.go @@ -0,0 +1,264 @@ +package main + +import ( + "bytes" + "context" + "crypto/sha256" + "fmt" + "io/ioutil" + "math/rand" + "os" + "path/filepath" + "time" + + ffi "github.com/filecoin-project/filecoin-ffi" + "github.com/ipfs/go-datastore" + logging "github.com/ipfs/go-log" + "github.com/mitchellh/go-homedir" + "golang.org/x/xerrors" + "gopkg.in/urfave/cli.v2" + + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/address" + "github.com/filecoin-project/lotus/lib/sectorbuilder" +) + +var log = logging.Logger("lotus-bench") + +type BenchResults struct { + SectorSize uint64 + + SealingResults []SealingResult + + PostGenerateCandidates time.Duration + PostEProofCold time.Duration + PostEProofHot time.Duration + VerifyEPostCold time.Duration + VerifyEPostHot time.Duration +} + +type SealingResult struct { + AddPiece time.Duration + PreCommit time.Duration + Commit time.Duration + Verify time.Duration +} + +func main() { + logging.SetLogLevel("*", "INFO") + + log.Info("Starting lotus-bench") + + app := &cli.App{ + Name: "lotus-bench", + Usage: "Benchmark performance of lotus on your hardware", + Version: build.Version, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "storage-dir", + Value: "~/.lotus-bench", + Usage: "Path to the storage directory that will store sectors long term", + }, + &cli.Uint64Flag{ + Name: "sector-size", + Value: 1024, + }, + }, + Action: func(c *cli.Context) error { + sdir, err := homedir.Expand(c.String("storage-dir")) + if err != nil { + return err + } + + os.MkdirAll(sdir, 0775) + + tsdir, err := ioutil.TempDir(sdir, "bench") + if err != nil { + return err + } + defer func() { + if err := os.RemoveAll(tsdir); err != nil { + log.Warn("remove all: ", err) + } + }() + + maddr, err := address.NewFromString("t0101") + if err != nil { + return err + } + + sectorSize := c.Uint64("sector-size") + + mds := datastore.NewMapDatastore() + cfg := §orbuilder.Config{ + Miner: maddr, + SectorSize: sectorSize, + WorkerThreads: 2, + CacheDir: filepath.Join(tsdir, "cache"), + SealedDir: filepath.Join(tsdir, "sealed"), + StagedDir: filepath.Join(tsdir, "staged"), + MetadataDir: filepath.Join(tsdir, "meta"), + } + for _, d := range []string{cfg.CacheDir, cfg.SealedDir, cfg.StagedDir, cfg.MetadataDir} { + if err := os.MkdirAll(d, 0775); err != nil { + return err + } + } + + if err := build.GetParams(true, false); err != nil { + return xerrors.Errorf("getting params: %w", err) + } + sb, err := sectorbuilder.New(cfg, mds) + if err != nil { + return err + } + + r := rand.New(rand.NewSource(101)) + size := sectorbuilder.UserBytesForSectorSize(sectorSize) + + var sealTimings []SealingResult + var sealedSectors []ffi.PublicSectorInfo + numSectors := uint64(1) + for i := uint64(1); i <= numSectors; i++ { + start := time.Now() + log.Info("Writing piece into sector...") + pi, err := sb.AddPiece(size, i, r, nil) + if err != nil { + return err + } + + addpiece := time.Now() + + trand := sha256.Sum256([]byte(c.String("ticket-preimage"))) + ticket := sectorbuilder.SealTicket{ + TicketBytes: trand, + } + + log.Info("Running replication...") + pieces := []sectorbuilder.PublicPieceInfo{pi} + pco, err := sb.SealPreCommit(i, ticket, pieces) + if err != nil { + return xerrors.Errorf("commit: %w", err) + } + + precommit := time.Now() + + sealedSectors = append(sealedSectors, ffi.PublicSectorInfo{ + CommR: pco.CommR, + SectorID: i, + }) + + seed := sectorbuilder.SealSeed{ + BlockHeight: 101, + TicketBytes: [32]byte{1, 2, 3, 4, 5}, + } + + log.Info("Generating PoRep for sector") + proof, err := sb.SealCommit(i, ticket, seed, pieces, pco) + if err != nil { + return err + } + + sealcommit := time.Now() + commD := pi.CommP + ok, err := sectorbuilder.VerifySeal(sectorSize, pco.CommR[:], commD[:], maddr, ticket.TicketBytes[:], seed.TicketBytes[:], i, proof) + if err != nil { + return err + } + if !ok { + return xerrors.Errorf("porep proof for sector %d was invalid", i) + } + + verifySeal := time.Now() + + sealTimings = append(sealTimings, SealingResult{ + AddPiece: addpiece.Sub(start), + PreCommit: precommit.Sub(addpiece), + Commit: sealcommit.Sub(precommit), + Verify: verifySeal.Sub(sealcommit), + }) + } + + beforePost := time.Now() + + var challenge [32]byte + rand.Read(challenge[:]) + + log.Info("generating election post candidates") + sinfos := sectorbuilder.NewSortedPublicSectorInfo(sealedSectors) + candidates, err := sb.GenerateEPostCandidates(sinfos, challenge, []uint64{}) + if err != nil { + return err + } + + gencandidates := time.Now() + + log.Info("computing election post snark (cold)") + proof1, err := sb.ComputeElectionPoSt(sinfos, challenge[:], candidates[:1]) + if err != nil { + return err + } + + epost1 := time.Now() + + log.Info("computing election post snark (hot)") + proof2, err := sb.ComputeElectionPoSt(sinfos, challenge[:], candidates[:1]) + if err != nil { + return err + } + + epost2 := time.Now() + + if !bytes.Equal(proof1, proof2) { + log.Warn("separate epost calls returned different proof values (this might be bad)") + } + + ok, err := sectorbuilder.VerifyElectionPost(context.TODO(), sectorSize, sinfos, challenge[:], proof1, candidates[:1], maddr) + if err != nil { + return err + } + if !ok { + log.Error("post verification failed") + } + + verifypost1 := time.Now() + + ok, err = sectorbuilder.VerifyElectionPost(context.TODO(), sectorSize, sinfos, challenge[:], proof2, candidates[:1], maddr) + if err != nil { + return err + } + if !ok { + log.Error("post verification failed") + } + verifypost2 := time.Now() + + benchout := BenchResults{ + SectorSize: cfg.SectorSize, + SealingResults: sealTimings, + + PostGenerateCandidates: gencandidates.Sub(beforePost), + PostEProofCold: epost1.Sub(gencandidates), + PostEProofHot: epost2.Sub(epost1), + VerifyEPostCold: verifypost1.Sub(epost2), + VerifyEPostHot: verifypost2.Sub(verifypost1), + } // TODO: optionally write this as json to a file + + fmt.Println("results") + fmt.Printf("seal: addPiece: %s\n", benchout.SealingResults[0].AddPiece) // TODO: average across multiple sealings + fmt.Printf("seal: preCommit: %s\n", benchout.SealingResults[0].PreCommit) + fmt.Printf("seal: Commit: %s\n", benchout.SealingResults[0].Commit) + fmt.Printf("seal: Verify: %s\n", benchout.SealingResults[0].Verify) + fmt.Printf("generate candidates: %s\n", benchout.PostGenerateCandidates) + fmt.Printf("compute epost proof (cold): %s\n", benchout.PostEProofCold) + fmt.Printf("compute epost proof (hot): %s\n", benchout.PostEProofHot) + fmt.Printf("verify epost proof (cold): %s\n", benchout.VerifyEPostCold) + fmt.Printf("verify epost proof (hot): %s\n", benchout.VerifyEPostHot) + return nil + }, + } + + if err := app.Run(os.Args); err != nil { + log.Warn(err) + return + } +} diff --git a/cmd/lotus-chainwatch/storage.go b/cmd/lotus-chainwatch/storage.go index 26f88640b..35135313f 100644 --- a/cmd/lotus-chainwatch/storage.go +++ b/cmd/lotus-chainwatch/storage.go @@ -252,7 +252,7 @@ func (st *storage) storeMiners(miners map[minerKey]*minerInfo) error { i.info.SectorSize, i.state.Power.String(), i.state.Active, - i.state.ProvingPeriodEnd, + i.state.ElectionPeriodStart, i.state.SlashedAt, ); err != nil { return err diff --git a/cmd/lotus-chainwatch/sync.go b/cmd/lotus-chainwatch/sync.go index 824f06d0b..5c87ea04d 100644 --- a/cmd/lotus-chainwatch/sync.go +++ b/cmd/lotus-chainwatch/sync.go @@ -4,9 +4,10 @@ import ( "bytes" "container/list" "context" + "sync" + actors2 "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/address" - "sync" "github.com/ipfs/go-cid" diff --git a/cmd/lotus-seed/main.go b/cmd/lotus-seed/main.go new file mode 100644 index 000000000..cebe71f32 --- /dev/null +++ b/cmd/lotus-seed/main.go @@ -0,0 +1,89 @@ +package main + +import ( + "os" + + logging "github.com/ipfs/go-log" + "github.com/mitchellh/go-homedir" + "gopkg.in/urfave/cli.v2" + + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/address" + "github.com/filecoin-project/lotus/cmd/lotus-seed/seed" +) + +var log = logging.Logger("lotus-seed") + +func main() { + logging.SetLogLevel("*", "INFO") + + log.Info("Starting seed") + + local := []*cli.Command{ + preSealCmd, + } + + app := &cli.App{ + Name: "lotus-seed", + Usage: "Seal sectors for genesis miner", + Version: build.Version, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "sectorbuilder-dir", + Value: "~/.genesis-sectors", + }, + }, + + Commands: local, + } + + if err := app.Run(os.Args); err != nil { + log.Warn(err) + return + } +} + +var preSealCmd = &cli.Command{ + Name: "pre-seal", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "miner-addr", + Value: "t0101", + Usage: "specify the future address of your miner", + }, + &cli.Uint64Flag{ + Name: "sector-size", + Value: build.SectorSizes[0], + Usage: "specify size of sectors to pre-seal", + }, + &cli.StringFlag{ + Name: "ticket-preimage", + Value: "lotus is fire", + Usage: "set the ticket preimage for sealing randomness", + }, + &cli.Uint64Flag{ + Name: "num-sectors", + Value: 1, + Usage: "select number of sectors to pre-seal", + }, + }, + Action: func(c *cli.Context) error { + sdir := c.String("sectorbuilder-dir") + sbroot, err := homedir.Expand(sdir) + if err != nil { + return err + } + + maddr, err := address.NewFromString(c.String("miner-addr")) + if err != nil { + return err + } + + gm, err := seed.PreSeal(maddr, c.Uint64("sector-size"), c.Uint64("num-sectors"), sbroot, []byte(c.String("ticket-preimage"))) + if err != nil { + return err + } + + return seed.WriteGenesisMiner(maddr, sbroot, gm) + }, +} diff --git a/cmd/lotus-seed/seed/seed.go b/cmd/lotus-seed/seed/seed.go new file mode 100644 index 000000000..f1c2cbbc9 --- /dev/null +++ b/cmd/lotus-seed/seed/seed.go @@ -0,0 +1,167 @@ +package seed + +import ( + "context" + "crypto/sha256" + "encoding/json" + "fmt" + "io/ioutil" + "math/rand" + "os" + "path/filepath" + + badger "github.com/ipfs/go-ds-badger" + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/address" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/wallet" + "github.com/filecoin-project/lotus/genesis" + "github.com/filecoin-project/lotus/lib/sectorbuilder" +) + +func PreSeal(maddr address.Address, ssize uint64, sectors uint64, sbroot string, preimage []byte) (*genesis.GenesisMiner, error) { + cfg := §orbuilder.Config{ + Miner: maddr, + SectorSize: ssize, + CacheDir: filepath.Join(sbroot, "cache"), + SealedDir: filepath.Join(sbroot, "sealed"), + StagedDir: filepath.Join(sbroot, "staging"), + MetadataDir: filepath.Join(sbroot, "meta"), + WorkerThreads: 2, + } + + for _, d := range []string{cfg.CacheDir, cfg.SealedDir, cfg.StagedDir, cfg.MetadataDir} { + if err := os.MkdirAll(d, 0775); err != nil { + return nil, err + } + } + + mds, err := badger.NewDatastore(filepath.Join(sbroot, "badger"), nil) + if err != nil { + return nil, err + } + + if err := build.GetParams(true, false); err != nil { + return nil, xerrors.Errorf("getting params: %w", err) + } + + sb, err := sectorbuilder.New(cfg, mds) + if err != nil { + return nil, err + } + + r := rand.New(rand.NewSource(101)) + size := sectorbuilder.UserBytesForSectorSize(ssize) + + var sealedSectors []*genesis.PreSeal + for i := uint64(1); i <= sectors; i++ { + sid, err := sb.AcquireSectorId() + if err != nil { + return nil, err + } + + pi, err := sb.AddPiece(size, sid, r, nil) + if err != nil { + return nil, err + } + + trand := sha256.Sum256(preimage) + ticket := sectorbuilder.SealTicket{ + TicketBytes: trand, + } + + fmt.Println("Piece info: ", pi) + + pco, err := sb.SealPreCommit(sid, ticket, []sectorbuilder.PublicPieceInfo{pi}) + if err != nil { + return nil, xerrors.Errorf("commit: %w", err) + } + + sealedSectors = append(sealedSectors, &genesis.PreSeal{ + CommR: pco.CommR, + CommD: pco.CommD, + SectorID: sid, + }) + } + + minerAddr, err := wallet.GenerateKey(types.KTBLS) + if err != nil { + return nil, err + } + + miner := &genesis.GenesisMiner{ + Owner: minerAddr.Address, + Worker: minerAddr.Address, + + SectorSize: ssize, + + Sectors: sealedSectors, + + Key: minerAddr.KeyInfo, + } + + if err := createDeals(miner, minerAddr, ssize); err != nil { + return nil, xerrors.Errorf("creating deals: %w", err) + } + + if err := mds.Close(); err != nil { + return nil, xerrors.Errorf("closing datastore: %w", err) + } + + return miner, nil +} + +func WriteGenesisMiner(maddr address.Address, sbroot string, gm *genesis.GenesisMiner) error { + output := map[string]genesis.GenesisMiner{ + maddr.String(): *gm, + } + + out, err := json.MarshalIndent(output, "", " ") + if err != nil { + return err + } + + if err := ioutil.WriteFile(filepath.Join(sbroot, "pre-seal-"+maddr.String()+".json"), out, 0664); err != nil { + return err + } + + return nil +} + +func createDeals(m *genesis.GenesisMiner, k *wallet.Key, ssize uint64) error { + for _, sector := range m.Sectors { + proposal := &actors.StorageDealProposal{ + PieceRef: sector.CommD[:], // just one deal so this == CommP + PieceSize: ssize, + PieceSerialization: actors.SerializationUnixFSv0, + Client: k.Address, + Provider: k.Address, + ProposalExpiration: 9000, // TODO: allow setting + Duration: 9000, + StoragePricePerEpoch: types.NewInt(0), + StorageCollateral: types.NewInt(0), + ProposerSignature: nil, + } + + if err := api.SignWith(context.TODO(), wallet.KeyWallet(k).Sign, k.Address, proposal); err != nil { + return err + } + + deal := &actors.StorageDeal{ + Proposal: *proposal, + CounterSignature: nil, + } + + if err := api.SignWith(context.TODO(), wallet.KeyWallet(k).Sign, k.Address, deal); err != nil { + return err + } + + sector.Deal = *deal + } + + return nil +} diff --git a/cmd/lotus-storage-miner/info.go b/cmd/lotus-storage-miner/info.go index 68757f455..9a3cfac12 100644 --- a/cmd/lotus-storage-miner/info.go +++ b/cmd/lotus-storage-miner/info.go @@ -63,7 +63,7 @@ var infoCmd = &cli.Command{ fmt.Printf("\tLocal: %d / %d (+%d reserved)\n", wstat.LocalTotal-wstat.LocalReserved-wstat.LocalFree, wstat.LocalTotal-wstat.LocalReserved, wstat.LocalReserved) fmt.Printf("\tRemote: %d / %d\n", wstat.RemotesTotal-wstat.RemotesFree, wstat.RemotesTotal) - ppe, err := api.StateMinerProvingPeriodEnd(ctx, maddr, nil) + ppe, err := api.StateMinerElectionPeriodStart(ctx, maddr, nil) if err != nil { return err } diff --git a/cmd/lotus-storage-miner/init.go b/cmd/lotus-storage-miner/init.go index 06500af95..de1164cb5 100644 --- a/cmd/lotus-storage-miner/init.go +++ b/cmd/lotus-storage-miner/init.go @@ -3,22 +3,36 @@ package main import ( "context" "crypto/rand" + "encoding/json" + "fmt" + "io/ioutil" "os" + "path/filepath" + "strconv" "github.com/ipfs/go-datastore" + badger "github.com/ipfs/go-ds-badger" "github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/peer" "github.com/mitchellh/go-homedir" "golang.org/x/xerrors" "gopkg.in/urfave/cli.v2" - "github.com/filecoin-project/lotus/api" + lapi "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/address" + "github.com/filecoin-project/lotus/chain/deals" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/genesis" + "github.com/filecoin-project/lotus/lib/cborutil" + "github.com/filecoin-project/lotus/lib/sectorbuilder" + "github.com/filecoin-project/lotus/miner" + "github.com/filecoin-project/lotus/node/modules" + "github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/lotus/node/repo" + "github.com/filecoin-project/lotus/storage" ) var initCmd = &cli.Command{ @@ -53,6 +67,10 @@ var initCmd = &cli.Command{ Usage: "specify sector size to use", Value: build.SectorSizes[0], }, + &cli.StringFlag{ + Name: "pre-sealed-sectors", + Usage: "specify set of presealed sectors for starting as a genesis miner", + }, }, Action: func(cctx *cli.Context) error { log.Info("Initializing lotus storage miner") @@ -104,6 +122,25 @@ var initCmd = &cli.Command{ return err } + if pssb := cctx.String("pre-sealed-sectors"); pssb != "" { + log.Infof("moving pre-sealed-sectors from %s into newly created storage miner repo", pssb) + lr, err := r.Lock(repo.StorageMiner) + if err != nil { + return err + } + mds, err := lr.Datastore("/metadata") + if err != nil { + return err + } + + if err := migratePreSealedSectors(pssb, repoPath, mds); err != nil { + return err + } + if err := lr.Close(); err != nil { + return xerrors.Errorf("unlocking repo after preseal migration: %w", err) + } + } + if err := storageMinerInit(ctx, cctx, api, r); err != nil { log.Errorf("Failed to initialize lotus-storage-miner: %+v", err) path, err := homedir.Expand(repoPath) @@ -124,7 +161,161 @@ var initCmd = &cli.Command{ }, } -func storageMinerInit(ctx context.Context, cctx *cli.Context, api api.FullNode, r repo.Repo) error { +// TODO: this method should be a lot more robust for mainnet. For testnet, its +// fine if we mess things up a few times +// Also probably makes sense for this method to be in the sectorbuilder package +func migratePreSealedSectors(presealsb string, repoPath string, mds dtypes.MetadataDS) error { + pspath, err := homedir.Expand(presealsb) + if err != nil { + return err + } + + srcds, err := badger.NewDatastore(filepath.Join(pspath, "badger"), nil) + if err != nil { + return xerrors.Errorf("openning presealed sectors datastore: %w", err) + } + + expRepo, err := homedir.Expand(repoPath) + if err != nil { + return err + } + + if stat, err := os.Stat(pspath); err != nil { + return xerrors.Errorf("failed to stat presealed sectors directory: %w", err) + } else if !stat.IsDir() { + return xerrors.Errorf("given presealed sectors path was not a directory: %w", err) + } + + for _, dir := range []string{"meta", "sealed", "staging", "cache"} { + from := filepath.Join(pspath, dir) + to := filepath.Join(expRepo, dir) + if err := os.Rename(from, to); err != nil { + return err + } + } + + val, err := srcds.Get(sectorbuilder.LastSectorIdKey) + if err != nil { + return xerrors.Errorf("getting source last sector ID: %w", err) + } + + if err := mds.Put(sectorbuilder.LastSectorIdKey, val); err != nil { + return xerrors.Errorf("failed to write last sector ID key to target datastore: %w", err) + } + + return nil +} + +func migratePreSealMeta(ctx context.Context, api lapi.FullNode, presealDir string, maddr address.Address, mds dtypes.MetadataDS) error { + b, err := ioutil.ReadFile(filepath.Join(presealDir, "pre-seal-"+maddr.String()+".json")) + if err != nil { + return xerrors.Errorf("reading preseal metadata: %w", err) + } + + preseals := map[string]genesis.GenesisMiner{} + + if err := json.Unmarshal(b, &preseals); err != nil { + return xerrors.Errorf("unmarshaling preseal metadata: %w", err) + } + + meta, ok := preseals[maddr.String()] + if !ok { + return xerrors.New("got wrong preseal info") + } + + for _, sector := range meta.Sectors { + sectorKey := datastore.NewKey(storage.SectorStorePrefix).ChildString(fmt.Sprint(sector.SectorID)) + + dealID, err := findMarketDealID(ctx, api, sector.Deal) + if err != nil { + return xerrors.Errorf("finding storage deal for pre-sealed sector %d: %w", sector.SectorID, err) + } + + info := &storage.SectorInfo{ + State: lapi.Proving, + SectorID: sector.SectorID, + Pieces: []storage.Piece{ + { + DealID: dealID, + Ref: fmt.Sprintf("preseal-%d", sector.SectorID), + Size: meta.SectorSize, + CommP: sector.CommD[:], + }, + }, + CommC: nil, + CommD: sector.CommD[:], + CommR: sector.CommR[:], + CommRLast: nil, + Proof: nil, + Ticket: storage.SealTicket{}, + PreCommitMessage: nil, + Seed: storage.SealSeed{}, + CommitMessage: nil, + } + + b, err := cborutil.Dump(info) + if err != nil { + return err + } + + if err := mds.Put(sectorKey, b); err != nil { + return err + } + + proposalCid, err := sector.Deal.Proposal.Cid() + if err != nil { + return err + } + + dealKey := datastore.NewKey(deals.ProviderDsPrefix).ChildString(proposalCid.String()) + + deal := &deals.MinerDeal{ + Proposal: sector.Deal.Proposal, + ProposalCid: proposalCid, + State: lapi.DealComplete, + Ref: proposalCid, // TODO: This is super wrong, but there + // are no params for CommP CIDs, we can't recover unixfs cid easily, + // and this isn't even used after the deal enters Complete state + DealID: dealID, + SectorID: sector.SectorID, + } + + b, err = cborutil.Dump(deal) + if err != nil { + return err + } + + if err := mds.Put(dealKey, b); err != nil { + return err + } + } + + return nil +} + +func findMarketDealID(ctx context.Context, api lapi.FullNode, deal actors.StorageDeal) (uint64, error) { + // TODO: find a better way + // (this is only used by genesis miners) + + deals, err := api.StateMarketDeals(ctx, nil) + if err != nil { + return 0, xerrors.Errorf("getting market deals: %w", err) + } + + for k, v := range deals { + eq, err := cborutil.Equals(&v.Deal, &deal) + if err != nil { + return 0, err + } + if eq { + return strconv.ParseUint(k, 10, 64) + } + } + + return 0, xerrors.New("deal not found") +} + +func storageMinerInit(ctx context.Context, cctx *cli.Context, api lapi.FullNode, r repo.Repo) error { lr, err := r.Lock(repo.StorageMiner) if err != nil { return err @@ -143,6 +334,11 @@ func storageMinerInit(ctx context.Context, cctx *cli.Context, api api.FullNode, return xerrors.Errorf("peer ID from private key: %w", err) } + mds, err := lr.Datastore("/metadata") + if err != nil { + return err + } + var addr address.Address if act := cctx.String("actor"); act != "" { a, err := address.NewFromString(act) @@ -150,8 +346,51 @@ func storageMinerInit(ctx context.Context, cctx *cli.Context, api api.FullNode, return xerrors.Errorf("failed parsing actor flag value (%q): %w", act, err) } - if err := configureStorageMiner(ctx, api, a, peerid, cctx.Bool("genesis-miner")); err != nil { - return xerrors.Errorf("failed to configure storage miner: %w", err) + if cctx.Bool("genesis-miner") { + if err := mds.Put(datastore.NewKey("miner-address"), a.Bytes()); err != nil { + return err + } + + sbcfg, err := modules.SectorBuilderConfig(lr.Path(), 2)(mds, api) + if err != nil { + return xerrors.Errorf("getting genesis miner sector builder config: %w", err) + } + sb, err := sectorbuilder.New(sbcfg, mds) + if err != nil { + return xerrors.Errorf("failed to set up sectorbuilder for genesis mining: %w", err) + } + epp := storage.NewElectionPoStProver(sb) + + m := miner.NewMiner(api, epp) + { + if err := m.Register(a); err != nil { + return xerrors.Errorf("failed to start up genesis miner: %w", err) + } + + defer func() { + if err := m.Unregister(ctx, a); err != nil { + log.Error("failed to shut down storage miner: ", err) + } + }() + + if err := configureStorageMiner(ctx, api, a, peerid); err != nil { + return xerrors.Errorf("failed to configure storage miner: %w", err) + } + } + + if pssb := cctx.String("pre-sealed-sectors"); pssb != "" { + log.Infof("Importing pre-sealed sector metadata for %s", a) + + if err := migratePreSealMeta(ctx, api, cctx.String("pre-sealed-sectors"), a, mds); err != nil { + return xerrors.Errorf("migrating presealed sector metadata: %w", err) + } + } + + return nil + } else { + if err := configureStorageMiner(ctx, api, a, peerid); err != nil { + return xerrors.Errorf("failed to configure storage miner: %w", err) + } } addr = a @@ -165,12 +404,7 @@ func storageMinerInit(ctx context.Context, cctx *cli.Context, api api.FullNode, } log.Infof("Created new storage miner: %s", addr) - - ds, err := lr.Datastore("/metadata") - if err != nil { - return err - } - if err := ds.Put(datastore.NewKey("miner-address"), addr.Bytes()); err != nil { + if err := mds.Put(datastore.NewKey("miner-address"), addr.Bytes()); err != nil { return err } @@ -203,22 +437,7 @@ func makeHostKey(lr repo.LockedRepo) (crypto.PrivKey, error) { return pk, nil } -func configureStorageMiner(ctx context.Context, api api.FullNode, addr address.Address, peerid peer.ID, genmine bool) error { - if genmine { - log.Warn("Starting genesis mining. This shouldn't happen when connecting to the real network.") - // We may be one of genesis miners, start mining before trying to do any chain operations - // (otherwise our messages won't be mined) - if err := api.MinerRegister(ctx, addr); err != nil { - return err - } - - defer func() { - if err := api.MinerUnregister(ctx, addr); err != nil { - log.Errorf("failed to call api.MinerUnregister: %s", err) - } - }() - } - +func configureStorageMiner(ctx context.Context, api lapi.FullNode, addr address.Address, peerid peer.ID) error { // This really just needs to be an api call at this point... recp, err := api.StateCall(ctx, &types.Message{ To: addr, @@ -271,7 +490,7 @@ func configureStorageMiner(ctx context.Context, api api.FullNode, addr address.A return nil } -func createStorageMiner(ctx context.Context, api api.FullNode, peerid peer.ID, cctx *cli.Context) (addr address.Address, err error) { +func createStorageMiner(ctx context.Context, api lapi.FullNode, peerid peer.ID, cctx *cli.Context) (addr address.Address, err error) { log.Info("Creating StorageMarket.CreateStorageMiner message") var owner address.Address diff --git a/cmd/lotus-storage-miner/main.go b/cmd/lotus-storage-miner/main.go index ca83189b9..26913a17a 100644 --- a/cmd/lotus-storage-miner/main.go +++ b/cmd/lotus-storage-miner/main.go @@ -70,7 +70,7 @@ func main() { } if err := app.Run(os.Args); err != nil { - log.Warn(err) + log.Warnf("%+v", err) os.Exit(1) } } diff --git a/cmd/lotus-storage-miner/run.go b/cmd/lotus-storage-miner/run.go index 2145a90bc..b51d14fb2 100644 --- a/cmd/lotus-storage-miner/run.go +++ b/cmd/lotus-storage-miner/run.go @@ -35,6 +35,7 @@ var runCmd = &cli.Command{ &cli.BoolFlag{ Name: "enable-gpu-proving", Usage: "Enable use of GPU for mining operations", + Value: true, }, }, Action: func(cctx *cli.Context) error { diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index 115970d54..bbf036275 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -21,7 +21,8 @@ import ( ) const ( - makeGenFlag = "lotus-make-random-genesis" + makeGenFlag = "lotus-make-random-genesis" + preSealedSectorsFlag = "genesis-presealed-sectors" ) // DaemonCmd is the `go-lotus daemon` command @@ -38,6 +39,10 @@ var DaemonCmd = &cli.Command{ Value: "", Hidden: true, }, + &cli.StringFlag{ + Name: preSealedSectorsFlag, + Hidden: true, + }, &cli.StringFlag{ Name: "genesis", Usage: "genesis file to use for first node run", @@ -51,11 +56,11 @@ var DaemonCmd = &cli.Command{ ctx := context.Background() r, err := repo.NewFS(cctx.String("repo")) if err != nil { - return err + return xerrors.Errorf("opening fs repo: %w", err) } if err := r.Init(repo.FullNode); err != nil && err != repo.ErrRepoExists { - return err + return xerrors.Errorf("repo init error: %w", err) } if err := build.GetParams(false, false); err != nil { @@ -67,9 +72,8 @@ var DaemonCmd = &cli.Command{ if cctx.String("genesis") != "" { genBytes, err = ioutil.ReadFile(cctx.String("genesis")) if err != nil { - return err + return xerrors.Errorf("reading genesis: %w", err) } - } genesis := node.Options() @@ -77,7 +81,10 @@ var DaemonCmd = &cli.Command{ genesis = node.Override(new(modules.Genesis), modules.LoadGenesis(genBytes)) } if cctx.String(makeGenFlag) != "" { - genesis = node.Override(new(modules.Genesis), testing.MakeGenesis(cctx.String(makeGenFlag))) + if cctx.String(preSealedSectorsFlag) == "" { + return xerrors.Errorf("must also pass file with miner preseal info to `--%s`", preSealedSectorsFlag) + } + genesis = node.Override(new(modules.Genesis), testing.MakeGenesis(cctx.String(makeGenFlag), cctx.String(preSealedSectorsFlag))) } var api api.FullNode @@ -105,12 +112,12 @@ var DaemonCmd = &cli.Command{ ), ) if err != nil { - return err + return xerrors.Errorf("initializing node: %w", err) } endpoint, err := r.APIEndpoint() if err != nil { - return err + return xerrors.Errorf("getting api endpoint: %w", err) } // TODO: properly parse api endpoint (or make it a URL) diff --git a/cmd/lotus/main.go b/cmd/lotus/main.go index cd9a9e727..e2f32c0a0 100644 --- a/cmd/lotus/main.go +++ b/cmd/lotus/main.go @@ -69,7 +69,7 @@ func main() { Code: trace.StatusCodeFailedPrecondition, Message: err.Error(), }) - log.Warn(err) + log.Warnf("%+v", err) os.Exit(1) } return diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi new file mode 160000 index 000000000..0e71b164c --- /dev/null +++ b/extern/filecoin-ffi @@ -0,0 +1 @@ +Subproject commit 0e71b164cf4b2e1c0f53ca25145e3ea57cc53e90 diff --git a/extern/go-bls-sigs b/extern/go-bls-sigs deleted file mode 160000 index 98479d3c7..000000000 --- a/extern/go-bls-sigs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 98479d3c79620f18783da0c2c6a15f8b8eb4fa2e diff --git a/extern/go-sectorbuilder b/extern/go-sectorbuilder deleted file mode 160000 index 40278d4a6..000000000 --- a/extern/go-sectorbuilder +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 40278d4a6623e4c81003e20444871c9362bedd61 diff --git a/gen/main.go b/gen/main.go index c74a10f0f..4aad7622c 100644 --- a/gen/main.go +++ b/gen/main.go @@ -20,6 +20,8 @@ func main() { err := gen.WriteTupleEncodersToFile("./chain/types/cbor_gen.go", "types", types.BlockHeader{}, types.Ticket{}, + types.EPostProof{}, + types.EPostTicket{}, types.Message{}, types.SignedMessage{}, types.MsgMeta{}, @@ -91,7 +93,7 @@ func main() { actors.SectorPreCommitInfo{}, actors.PreCommittedSector{}, actors.MinerInfo{}, - actors.SubmitPoStParams{}, + actors.SubmitFallbackPoStParams{}, actors.PaymentVerifyParams{}, actors.UpdatePeerIDParams{}, actors.MultiSigActorState{}, diff --git a/genesis/types.go b/genesis/types.go new file mode 100644 index 000000000..e8a208452 --- /dev/null +++ b/genesis/types.go @@ -0,0 +1,25 @@ +package genesis + +import ( + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/address" + "github.com/filecoin-project/lotus/chain/types" +) + +type PreSeal struct { + CommR [32]byte + CommD [32]byte + SectorID uint64 + Deal actors.StorageDeal +} + +type GenesisMiner struct { + Owner address.Address + Worker address.Address + + SectorSize uint64 + + Sectors []*PreSeal + + Key types.KeyInfo // TODO: separate file +} diff --git a/go.mod b/go.mod index 1ab3389a3..8764433a8 100644 --- a/go.mod +++ b/go.mod @@ -8,19 +8,17 @@ require ( github.com/GeertJohan/go.rice v1.0.0 github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect - github.com/btcsuite/btcd v0.0.0-20190807005414-4063feeff79a // indirect github.com/fatih/color v1.7.0 // indirect github.com/filecoin-project/chain-validation v0.0.0-20191106200742-11986803c0f7 - github.com/filecoin-project/go-amt-ipld v0.0.0-20190919045431-3650716fff16 - github.com/filecoin-project/go-bls-sigs v0.0.0-20190718224239-4bc4b8a7bbf8 + github.com/filecoin-project/filecoin-ffi v0.0.0-00010101000000-000000000000 + github.com/filecoin-project/go-amt-ipld v0.0.0-20191122035745-59b9dfc0efc7 github.com/filecoin-project/go-leb128 v0.0.0-20190212224330-8d79a5489543 - github.com/filecoin-project/go-sectorbuilder v0.0.0-00010101000000-000000000000 github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1 github.com/go-ole/go-ole v1.2.4 // indirect github.com/google/go-cmp v0.3.1 // indirect github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f // indirect github.com/gorilla/mux v1.7.3 - github.com/gorilla/websocket v1.4.0 + github.com/gorilla/websocket v1.4.1 github.com/hashicorp/go-multierror v1.0.0 github.com/hashicorp/golang-lru v0.5.3 github.com/influxdata/influxdb1-client v0.0.0-20190809212627-fc22c7df067e @@ -44,17 +42,16 @@ require ( github.com/ipfs/go-ipfs-routing v0.1.0 github.com/ipfs/go-ipld-cbor v0.0.3 github.com/ipfs/go-ipld-format v0.0.2 - github.com/ipfs/go-log v0.0.2-0.20190920042044-a609c1ae5144 + github.com/ipfs/go-log v1.0.0 github.com/ipfs/go-merkledag v0.2.4 github.com/ipfs/go-unixfs v0.2.2-0.20190827150610-868af2e9e5cb github.com/ipld/go-ipld-prime v0.0.2-0.20191025154717-8dff1cbec43b github.com/ipsn/go-secp256k1 v0.0.0-20180726113642-9d62b9f0bc52 - github.com/libp2p/go-eventbus v0.1.0 // indirect github.com/libp2p/go-libp2p v0.3.0 github.com/libp2p/go-libp2p-circuit v0.1.1 github.com/libp2p/go-libp2p-connmgr v0.1.0 - github.com/libp2p/go-libp2p-core v0.2.2 - github.com/libp2p/go-libp2p-discovery v0.1.0 + github.com/libp2p/go-libp2p-core v0.2.4 + github.com/libp2p/go-libp2p-discovery v0.2.0 github.com/libp2p/go-libp2p-host v0.1.0 github.com/libp2p/go-libp2p-kad-dht v0.1.1 github.com/libp2p/go-libp2p-mplex v0.2.1 @@ -62,31 +59,30 @@ require ( github.com/libp2p/go-libp2p-peerstore v0.1.3 github.com/libp2p/go-libp2p-pnet v0.1.0 github.com/libp2p/go-libp2p-protocol v0.1.0 - github.com/libp2p/go-libp2p-pubsub v0.1.0 + github.com/libp2p/go-libp2p-pubsub v0.2.3 github.com/libp2p/go-libp2p-quic-transport v0.1.1 github.com/libp2p/go-libp2p-record v0.1.1 github.com/libp2p/go-libp2p-routing-helpers v0.1.0 github.com/libp2p/go-libp2p-secio v0.2.0 - github.com/libp2p/go-libp2p-swarm v0.2.1 // indirect github.com/libp2p/go-libp2p-tls v0.1.0 github.com/libp2p/go-libp2p-yamux v0.2.1 github.com/libp2p/go-maddr-filter v0.0.5 + github.com/libp2p/go-ws-transport v0.1.2 // indirect github.com/mattn/go-isatty v0.0.9 // indirect github.com/mattn/go-runewidth v0.0.4 // indirect github.com/mattn/go-sqlite3 v1.12.0 github.com/miekg/dns v1.1.16 // indirect github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 - github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771 + github.com/minio/sha256-simd v0.1.1 github.com/mitchellh/go-homedir v1.1.0 github.com/multiformats/go-base32 v0.0.3 - github.com/multiformats/go-multiaddr v0.0.4 - github.com/multiformats/go-multiaddr-dns v0.0.3 - github.com/multiformats/go-multiaddr-net v0.0.1 + github.com/multiformats/go-multiaddr v0.1.1 + github.com/multiformats/go-multiaddr-dns v0.2.0 + github.com/multiformats/go-multiaddr-net v0.1.0 github.com/multiformats/go-multihash v0.0.9 github.com/onsi/ginkgo v1.9.0 // indirect github.com/onsi/gomega v1.6.0 // indirect github.com/opentracing/opentracing-go v1.1.0 - github.com/pkg/errors v0.8.1 github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a github.com/smartystreets/assertions v1.0.1 // indirect github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 // indirect @@ -95,16 +91,15 @@ require ( github.com/whyrusleeping/cbor-gen v0.0.0-20191116002219-891f55cd449d github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 github.com/whyrusleeping/pubsub v0.0.0-20131020042734-02de8aa2db3d - go.opencensus.io v0.22.0 + go.opencensus.io v0.22.1 go.uber.org/dig v1.7.0 // indirect go.uber.org/fx v1.9.0 go.uber.org/goleak v0.10.0 // indirect go.uber.org/multierr v1.1.0 go.uber.org/zap v1.10.0 go4.org v0.0.0-20190313082347-94abd6928b1d // indirect - golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 // indirect golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 - golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 + golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 google.golang.org/api v0.9.0 // indirect gopkg.in/cheggaaa/pb.v1 v1.0.28 gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8 @@ -114,6 +109,4 @@ require ( replace github.com/golangci/golangci-lint => github.com/golangci/golangci-lint v1.18.0 -replace github.com/filecoin-project/go-bls-sigs => ./extern/go-bls-sigs - -replace github.com/filecoin-project/go-sectorbuilder => ./extern/go-sectorbuilder +replace github.com/filecoin-project/filecoin-ffi => ./extern/filecoin-ffi diff --git a/go.sum b/go.sum index 6fe298cc5..073050e39 100644 --- a/go.sum +++ b/go.sum @@ -33,8 +33,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 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/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= -github.com/btcsuite/btcd v0.0.0-20190807005414-4063feeff79a h1:We35J+0yvVFrEXbtViYUW8H/wNOhqjIF3PsrW4yYmGw= -github.com/btcsuite/btcd v0.0.0-20190807005414-4063feeff79a/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3 h1:A/EVblehb75cUgXA5njHPn0kLAsykn6mJGz7rnmW5W0= +github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= @@ -78,8 +78,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fd/go-nat v1.0.0/go.mod h1:BTBu/CKvMmOMUPkKVef1pngt2WFH/lg7E6yQnulfp6E= github.com/filecoin-project/chain-validation v0.0.0-20191106200742-11986803c0f7 h1:Ags/z6ZubzKonQ9PsY9fO439yGdVg07qpdxfv/AEUno= github.com/filecoin-project/chain-validation v0.0.0-20191106200742-11986803c0f7/go.mod h1:0/0/QUNqpF/jVzLHFncGeT3NvGPODBhGzQlNgzmoZew= -github.com/filecoin-project/go-amt-ipld v0.0.0-20190919045431-3650716fff16 h1:NzojcJU1VbS6zdLG13JMYis/cQy/MrN3rxmZRq56jKA= -github.com/filecoin-project/go-amt-ipld v0.0.0-20190919045431-3650716fff16/go.mod h1:lKjJYPg2kwbav5f78i5YA8kGccnZn18IySbpneXvaQs= +github.com/filecoin-project/go-amt-ipld v0.0.0-20191122035745-59b9dfc0efc7 h1:lKSMm8Go6qI7+Dk3rWCNIh57wBOqVNJ21re/p7D58gc= +github.com/filecoin-project/go-amt-ipld v0.0.0-20191122035745-59b9dfc0efc7/go.mod h1:lKjJYPg2kwbav5f78i5YA8kGccnZn18IySbpneXvaQs= github.com/filecoin-project/go-leb128 v0.0.0-20190212224330-8d79a5489543 h1:aMJGfgqe1QDhAVwxRg5fjCRF533xHidiKsugk7Vvzug= github.com/filecoin-project/go-leb128 v0.0.0-20190212224330-8d79a5489543/go.mod h1:mjrHv1cDGJWDlGmC0eDc1E5VJr8DmL9XMUcaFwiuKg8= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -97,7 +97,12 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -127,6 +132,8 @@ github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/gxed/pubsub v0.0.0-20180201040156-26ebdf44f824/go.mod h1:OiEWyHgK+CWrmOlVquHaIK1vhpUJydC9m0Je6mhaiNE= @@ -220,8 +227,8 @@ github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dC github.com/ipfs/go-ipld-format v0.0.2 h1:OVAGlyYT6JPZ0pEfGntFPS40lfrDmaDbQwNHEY2G9Zs= github.com/ipfs/go-ipld-format v0.0.2/go.mod h1:4B6+FM2u9OJ9zCV+kSbgFAZlOrv1Hqbf0INGQgiKf9k= github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= -github.com/ipfs/go-log v0.0.2-0.20190920042044-a609c1ae5144 h1:5WM8S1nwquWQ3zEuNhK82NE5Di6Pd41qz9JxxvxTAIA= -github.com/ipfs/go-log v0.0.2-0.20190920042044-a609c1ae5144/go.mod h1:azGN5dH7ailfREknDDNYB0Eq4qZ/4I4Y3gO0ivjJNyM= +github.com/ipfs/go-log v1.0.0 h1:BW3LQIiZzpNyolt84yvKNCd3FU+AK4VDw1hnHR+1aiI= +github.com/ipfs/go-log v1.0.0/go.mod h1:JO7RzlMK6rA+CIxFMLOuB6Wf5b81GDiKElL7UPSIKjA= github.com/ipfs/go-merkledag v0.1.0/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= github.com/ipfs/go-merkledag v0.2.4 h1:ZSHQSe9BENfixUjT+MaLeHEeZGxrZQfgo3KT3SLosF8= @@ -265,6 +272,7 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 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/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -298,6 +306,8 @@ github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3Pt github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= github.com/libp2p/go-libp2p-blankhost v0.1.3 h1:0KycuXvPDhmehw0ASsg+s1o3IfXgCUDqfzAl94KEBOg= github.com/libp2p/go-libp2p-blankhost v0.1.3/go.mod h1:KML1//wiKR8vuuJO0y3LUd1uLv+tlkGTAr3jC0S5cLg= +github.com/libp2p/go-libp2p-blankhost v0.1.4 h1:I96SWjR4rK9irDHcHq3XHN6hawCRTPUADzkJacgZLvk= +github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU= github.com/libp2p/go-libp2p-circuit v0.1.0/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8= github.com/libp2p/go-libp2p-circuit v0.1.1 h1:eopfG9fAg6rEHWQO1TSrLosXDgYbbbu/RTva/tBANus= github.com/libp2p/go-libp2p-circuit v0.1.1/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8= @@ -311,11 +321,15 @@ github.com/libp2p/go-libp2p-core v0.0.6/go.mod h1:0d9xmaYAVY5qmbp/fcgxHT3ZJsLjYe github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI= github.com/libp2p/go-libp2p-core v0.2.2 h1:Sv1ggdoMx9c7v7FOFkR7agraHCnAgqYsXrU1ARSRUMs= github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0= +github.com/libp2p/go-libp2p-core v0.2.4 h1:Et6ykkTwI6PU44tr8qUF9k43vP0aduMNniShAbUJJw8= +github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= github.com/libp2p/go-libp2p-crypto v0.0.1/go.mod h1:yJkNyDmO341d5wwXxDUGO0LykUVT72ImHNUqh5D/dBE= github.com/libp2p/go-libp2p-crypto v0.1.0 h1:k9MFy+o2zGDNGsaoZl0MA3iZ75qXxr9OOoAZF+sD5OQ= github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= github.com/libp2p/go-libp2p-discovery v0.1.0 h1:j+R6cokKcGbnZLf4kcNwpx6mDEUPF3N6SrqMymQhmvs= github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFTGElt8HnoDzwkFZm29g= +github.com/libp2p/go-libp2p-discovery v0.2.0 h1:1p3YSOq7VsgaL+xVHPi8XAmtGyas6D2J6rWBEfz/aiY= +github.com/libp2p/go-libp2p-discovery v0.2.0/go.mod h1:s4VGaxYMbw4+4+tsoQTqh7wfxg97AEdo4GYBt6BadWg= github.com/libp2p/go-libp2p-host v0.0.1/go.mod h1:qWd+H1yuU0m5CwzAkvbSjqKairayEHdR5MMl7Cwa7Go= github.com/libp2p/go-libp2p-host v0.1.0 h1:OZwENiFm6JOK3YR5PZJxkXlJE8a5u8g4YvAUrEV2MjM= github.com/libp2p/go-libp2p-host v0.1.0/go.mod h1:5+fWuLbDn8OxoxPN3CV0vsLe1hAKScSMbT84qRfxum8= @@ -348,8 +362,8 @@ github.com/libp2p/go-libp2p-pnet v0.1.0/go.mod h1:ZkyZw3d0ZFOex71halXRihWf9WH/j3 github.com/libp2p/go-libp2p-protocol v0.0.1/go.mod h1:Af9n4PiruirSDjHycM1QuiMi/1VZNHYcK8cLgFJLZ4s= github.com/libp2p/go-libp2p-protocol v0.1.0 h1:HdqhEyhg0ToCaxgMhnOmUO8snQtt/kQlcjVk3UoJU3c= github.com/libp2p/go-libp2p-protocol v0.1.0/go.mod h1:KQPHpAabB57XQxGrXCNvbL6UEXfQqUgC/1adR2Xtflk= -github.com/libp2p/go-libp2p-pubsub v0.1.0 h1:SmQeMa7IUv5vadh0fYgYsafWCBA1sCy5d/68kIYqGcU= -github.com/libp2p/go-libp2p-pubsub v0.1.0/go.mod h1:ZwlKzRSe1eGvSIdU5bD7+8RZN/Uzw0t1Bp9R1znpR/Q= +github.com/libp2p/go-libp2p-pubsub v0.2.3 h1:qJRnRnM7Z4xnHb4i6EBb3DKQXRPgtFWlKP4AmfJudLQ= +github.com/libp2p/go-libp2p-pubsub v0.2.3/go.mod h1:Jscj3fk23R5mCrOwb625xjVs5ZEyTZcx/OlTwMDqU+g= github.com/libp2p/go-libp2p-quic-transport v0.1.1 h1:MFMJzvsxIEDEVKzO89BnB/FgvMj9WI4GDGUW2ArDPUA= github.com/libp2p/go-libp2p-quic-transport v0.1.1/go.mod h1:wqG/jzhF3Pu2NrhJEvE+IE0NTHNXslOPn9JQzyCAxzU= github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q= @@ -365,8 +379,8 @@ github.com/libp2p/go-libp2p-secio v0.2.0 h1:ywzZBsWEEz2KNTn5RtzauEDq5RFEefPsttXY github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g= github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4= github.com/libp2p/go-libp2p-swarm v0.2.0/go.mod h1:x07b4zkMFo2EvgPV2bMTlNmdQc8i+74Jjio7xGvsTgU= -github.com/libp2p/go-libp2p-swarm v0.2.1 h1:9A8oQqPIZvbaRyrjViHeDYS7fE7fNtP7BRWdJrBHbe8= -github.com/libp2p/go-libp2p-swarm v0.2.1/go.mod h1:x07b4zkMFo2EvgPV2bMTlNmdQc8i+74Jjio7xGvsTgU= +github.com/libp2p/go-libp2p-swarm v0.2.2 h1:T4hUpgEs2r371PweU3DuH7EOmBIdTBCwWs+FLcgx3bQ= +github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU= github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= @@ -394,6 +408,8 @@ github.com/libp2p/go-nat v0.0.3 h1:l6fKV+p0Xa354EqQOQP+d8CivdLM4kl5GxC1hSc/UeI= github.com/libp2p/go-nat v0.0.3/go.mod h1:88nUEt0k0JD45Bk93NIwDqjlhiOwOoV36GchpcVc1yI= github.com/libp2p/go-openssl v0.0.2 h1:9pP2d3Ubaxkv7ZisLjx9BFwgOGnQdQYnfcH29HNY3ls= github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= +github.com/libp2p/go-openssl v0.0.3 h1:wjlG7HvQkt4Fq4cfH33Ivpwp0omaElYEi9z26qaIkIk= +github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-reuseport v0.0.1 h1:7PhkfH73VXfPJYKQ6JwS5I/eVcoyYi9IMNGc6FWpFLw= github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= github.com/libp2p/go-reuseport-transport v0.0.2 h1:WglMwyXyBu61CMkjCCtnmqNqnjib0GIEjMiHTwR/KN4= @@ -403,9 +419,13 @@ github.com/libp2p/go-stream-muxer-multistream v0.2.0 h1:714bRJ4Zy9mdhyTLJ+ZKiROm github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc= github.com/libp2p/go-tcp-transport v0.1.0 h1:IGhowvEqyMFknOar4FWCKSWE0zL36UFKQtiRQD60/8o= github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc= +github.com/libp2p/go-tcp-transport v0.1.1 h1:yGlqURmqgNA2fvzjSgZNlHcsd/IulAnKM8Ncu+vlqnw= +github.com/libp2p/go-tcp-transport v0.1.1/go.mod h1:3HzGvLbx6etZjnFlERyakbaYPdfjg2pWP97dFZworkY= github.com/libp2p/go-testutil v0.1.0/go.mod h1:81b2n5HypcVyrCg/MJx4Wgfp/VHojytjVe/gLzZ2Ehc= github.com/libp2p/go-ws-transport v0.1.0 h1:F+0OvvdmPTDsVc4AjPHjV7L7Pk1B7D5QwtDcKE2oag4= github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw7yT74kj3raBFuo= +github.com/libp2p/go-ws-transport v0.1.2 h1:VnxQcLfSGtqupqPpBNu8fUiCv+IN1RJ2BcVqQEM+z8E= +github.com/libp2p/go-ws-transport v0.1.2/go.mod h1:dsh2Ld8F+XNmzpkaAijmg5Is+e9l6/1tK/6VFOdN69Y= github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.2.3 h1:xX8A36vpXb59frIzWFdEgptLMsOANMFq2K7fPRlunYI= github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= @@ -417,9 +437,8 @@ github.com/marten-seemann/qtls v0.2.3 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNA github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -441,6 +460,8 @@ github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+ github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771 h1:MHkK1uRtFbVqvAgvWxafZe54+5uBxLluGylDiKgdhwo= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= +github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= 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= @@ -454,18 +475,26 @@ github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lg github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.0.4 h1:WgMSI84/eRLdbptXMkMWDXPjPq7SPLIgGUVm2eroyU4= github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.1.1 h1:rVAztJYMhCQ7vEFr8FvxW3mS+HF2eY/oPbOMeS0ZDnE= +github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= -github.com/multiformats/go-multiaddr-dns v0.0.3 h1:P19q/k9jwmtgh+qXFkKfgFM7rCg/9l5AVqh7VNxSXhs= -github.com/multiformats/go-multiaddr-dns v0.0.3/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-dns v0.2.0 h1:YWJoIDwLePniH7OU5hBnDZV6SWuvJqJ0YtN6pLeH9zA= +github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= github.com/multiformats/go-multiaddr-fmt v0.0.1 h1:5YjeOIzbX8OTKVaN72aOzGIYW7PnrZrnkDyOfAWRSMA= github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= +github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= +github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= github.com/multiformats/go-multiaddr-net v0.0.1 h1:76O59E3FavvHqNg7jvzWzsPSW5JSi/ek0E4eiDVbg9g= github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU= +github.com/multiformats/go-multiaddr-net v0.1.0 h1:ZepO8Ezwovd+7b5XPPDhQhayk1yt0AJpzQBpq9fejx4= +github.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= github.com/multiformats/go-multibase v0.0.1 h1:PN9/v21eLywrFWdFNsFKaU04kLJzuYzmrJR+ubhT9qA= github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= +github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.0.9 h1:aoijQXYYl7Xtb2pUUP68R+ys1TlnlR3eX6wmozr0Hp4= github.com/multiformats/go-multihash v0.0.9/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multistream v0.0.1/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= @@ -540,6 +569,7 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= @@ -581,8 +611,8 @@ github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7V github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.1 h1:8dP3SGL7MPB94crU3bEPplMPe83FI4EouesJUeFHv50= +go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/dig v1.7.0 h1:E5/L92iQTNJTjfgJF2KgU+/JpMaiuvK2DHLBj0+kSZk= @@ -611,8 +641,8 @@ golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 h1:Gv7RPwsi3eZ2Fgewe3CBsuOebPwO27PoXzRpJPsvSSM= -golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -632,13 +662,11 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/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-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-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -668,9 +696,8 @@ golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69 h1:rOhMmluY6kLMhdnrivzec6lLgaVbMHMn2ISQXJeJ5EM= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= @@ -681,6 +708,7 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -691,6 +719,8 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -710,6 +740,7 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -724,6 +755,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8 h1:Ggy3mWN4l3PUFPfSG0YB3n5fVYggzysUmiUQ89SnX6Y= gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8/go.mod h1:cKXr3E0k4aosgycml1b5z33BVV6hai1Kh7uDgFOkbcs= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/lib/padreader/padreader.go b/lib/padreader/padreader.go index 135e35b57..179ae305b 100644 --- a/lib/padreader/padreader.go +++ b/lib/padreader/padreader.go @@ -4,7 +4,7 @@ import ( "io" "math/bits" - sectorbuilder "github.com/filecoin-project/go-sectorbuilder" + sectorbuilder "github.com/filecoin-project/filecoin-ffi" ) func PaddedSize(size uint64) uint64 { diff --git a/lib/padreader/padreader_test.go b/lib/padreader/padreader_test.go index 551393b4e..712317e11 100644 --- a/lib/padreader/padreader_test.go +++ b/lib/padreader/padreader_test.go @@ -1,8 +1,9 @@ package padreader import ( - "gotest.tools/assert" "testing" + + "gotest.tools/assert" ) func TestComputePaddedSize(t *testing.T) { diff --git a/lib/sectorbuilder/mock.go b/lib/sectorbuilder/mock.go index e9661ada1..de2009b46 100644 --- a/lib/sectorbuilder/mock.go +++ b/lib/sectorbuilder/mock.go @@ -17,8 +17,6 @@ func TempSectorbuilder(sectorSize uint64, ds dtypes.MetadataDS) (*SectorBuilder, sb, err := TempSectorbuilderDir(dir, sectorSize, ds) return sb, func() { - sb.Destroy() - if err := os.RemoveAll(dir); err != nil { log.Warn("failed to clean up temp sectorbuilder: ", err) } diff --git a/lib/sectorbuilder/post.go b/lib/sectorbuilder/post.go new file mode 100644 index 000000000..6d99fae27 --- /dev/null +++ b/lib/sectorbuilder/post.go @@ -0,0 +1 @@ +package sectorbuilder diff --git a/lib/sectorbuilder/sectorbuilder.go b/lib/sectorbuilder/sectorbuilder.go index 941728e0a..2cbcf8dd4 100644 --- a/lib/sectorbuilder/sectorbuilder.go +++ b/lib/sectorbuilder/sectorbuilder.go @@ -7,13 +7,13 @@ import ( "strconv" "sync" "sync/atomic" - "unsafe" - sectorbuilder "github.com/filecoin-project/go-sectorbuilder" + sectorbuilder "github.com/filecoin-project/filecoin-ffi" "github.com/ipfs/go-datastore" logging "github.com/ipfs/go-log" "golang.org/x/xerrors" + "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/address" "github.com/filecoin-project/lotus/node/modules/dtypes" ) @@ -21,17 +21,12 @@ import ( const PoStReservedWorkers = 1 const PoRepProofPartitions = 2 -var lastSectorIdKey = datastore.NewKey("/sectorbuilder/last") +var LastSectorIdKey = datastore.NewKey("/sectorbuilder/last") var log = logging.Logger("sectorbuilder") -type SectorSealingStatus = sectorbuilder.SectorSealingStatus - -type StagedSectorMetadata = sectorbuilder.StagedSectorMetadata - -type SortedSectorInfo = sectorbuilder.SortedSectorInfo - -type SectorInfo = sectorbuilder.SectorInfo +type SortedPublicSectorInfo = sectorbuilder.SortedPublicSectorInfo +type SortedPrivateSectorInfo = sectorbuilder.SortedPrivateSectorInfo type SealTicket = sectorbuilder.SealTicket @@ -45,14 +40,16 @@ type PublicPieceInfo = sectorbuilder.PublicPieceInfo type RawSealPreCommitOutput sectorbuilder.RawSealPreCommitOutput +type EPostCandidate = sectorbuilder.Candidate + const CommLen = sectorbuilder.CommitmentBytesLen type SectorBuilder struct { - handle unsafe.Pointer - ds dtypes.MetadataDS - idLk sync.Mutex + ds dtypes.MetadataDS + idLk sync.Mutex - ssize uint64 + ssize uint64 + lastID uint64 Miner address.Address @@ -130,8 +127,6 @@ func New(cfg *Config, ds dtypes.MetadataDS) (*SectorBuilder, error) { return nil, xerrors.Errorf("minimum worker threads is %d, specified %d", PoStReservedWorkers, cfg.WorkerThreads) } - proverId := addressToProverID(cfg.Miner) - for _, dir := range []string{cfg.StagedDir, cfg.SealedDir, cfg.CacheDir, cfg.MetadataDir} { if err := os.Mkdir(dir, 0755); err != nil { if os.IsExist(err) { @@ -142,7 +137,7 @@ func New(cfg *Config, ds dtypes.MetadataDS) (*SectorBuilder, error) { } var lastUsedID uint64 - b, err := ds.Get(lastSectorIdKey) + b, err := ds.Get(LastSectorIdKey) switch err { case nil: i, err := strconv.ParseInt(string(b), 10, 64) @@ -155,11 +150,6 @@ func New(cfg *Config, ds dtypes.MetadataDS) (*SectorBuilder, error) { return nil, err } - sbp, err := sectorbuilder.InitSectorBuilder(cfg.SectorSize, PoRepProofPartitions, lastUsedID, cfg.MetadataDir, proverId, cfg.SealedDir, cfg.StagedDir, cfg.CacheDir, 16, cfg.WorkerThreads) - if err != nil { - return nil, err - } - rlimit := cfg.WorkerThreads - PoStReservedWorkers sealLocal := rlimit > 0 @@ -169,10 +159,10 @@ func New(cfg *Config, ds dtypes.MetadataDS) (*SectorBuilder, error) { } sb := &SectorBuilder{ - handle: sbp, - ds: ds, + ds: ds, - ssize: cfg.SectorSize, + ssize: cfg.SectorSize, + lastID: lastUsedID, stagedDir: cfg.StagedDir, sealedDir: cfg.SealedDir, @@ -205,9 +195,10 @@ func NewStandalone(cfg *Config) (*SectorBuilder, error) { } return &SectorBuilder{ - handle: nil, ds: nil, + ssize: cfg.SectorSize, + Miner: cfg.Miner, stagedDir: cfg.StagedDir, sealedDir: cfg.SealedDir, @@ -272,19 +263,14 @@ func addressToProverID(a address.Address) [32]byte { return proverId } -func (sb *SectorBuilder) Destroy() { - sectorbuilder.DestroySectorBuilder(sb.handle) -} - func (sb *SectorBuilder) AcquireSectorId() (uint64, error) { sb.idLk.Lock() defer sb.idLk.Unlock() - id, err := sectorbuilder.AcquireSectorId(sb.handle) - if err != nil { - return 0, err - } - err = sb.ds.Put(lastSectorIdKey, []byte(fmt.Sprint(id))) + sb.lastID++ + id := sb.lastID + + err := sb.ds.Put(LastSectorIdKey, []byte(fmt.Sprint(id))) if err != nil { return 0, err } @@ -305,7 +291,7 @@ func (sb *SectorBuilder) AddPiece(pieceSize uint64, sectorId uint64, file io.Rea return PublicPieceInfo{}, err } - _, _, commP, err := sectorbuilder.StandaloneWriteWithAlignment(f, pieceSize, stagedFile, existingPieceSizes) + _, _, commP, err := sectorbuilder.WriteWithAlignment(f, pieceSize, stagedFile, existingPieceSizes) if err != nil { return PublicPieceInfo{}, err } @@ -329,7 +315,8 @@ func (sb *SectorBuilder) ReadPieceFromSealedSector(pieceKey string) ([]byte, err ret := sb.RateLimit() defer ret() - return sectorbuilder.ReadPieceFromSealedSector(sb.handle, pieceKey) + panic("fixme") + //return sectorbuilder.Unseal(sb.handle, pieceKey) } func (sb *SectorBuilder) sealPreCommitRemote(call workerCall) (RawSealPreCommitOutput, error) { @@ -394,7 +381,7 @@ func (sb *SectorBuilder) SealPreCommit(sectorID uint64, ticket SealTicket, piece stagedPath := sb.StagedSectorPath(sectorID) - rspco, err := sectorbuilder.StandaloneSealPreCommit( + rspco, err := sectorbuilder.SealPreCommit( sb.ssize, PoRepProofPartitions, cacheDir, @@ -431,7 +418,7 @@ func (sb *SectorBuilder) sealCommitLocal(sectorID uint64, ticket SealTicket, see return nil, err } - proof, err = sectorbuilder.StandaloneSealCommit( + proof, err = sectorbuilder.SealCommit( sb.ssize, PoRepProofPartitions, cacheDir, @@ -452,7 +439,7 @@ func (sb *SectorBuilder) sealCommitLocal(sectorID uint64, ticket SealTicket, see return proof, nil } -func (sb *SectorBuilder) SealCommit(sectorID uint64, ticket SealTicket, seed SealSeed, pieces []PublicPieceInfo, pieceKeys []string, rspco RawSealPreCommitOutput) (proof []byte, err error) { +func (sb *SectorBuilder) SealCommit(sectorID uint64, ticket SealTicket, seed SealSeed, pieces []PublicPieceInfo, rspco RawSealPreCommitOutput) (proof []byte, err error) { call := workerCall{ task: WorkerTask{ Type: WorkerCommit, @@ -484,49 +471,91 @@ func (sb *SectorBuilder) SealCommit(sectorID uint64, ticket SealTicket, seed Sea return nil, xerrors.Errorf("commit: %w", err) } - if pieceKeys == nil { - return - } - - pmeta := make([]sectorbuilder.PieceMetadata, len(pieces)) - for i, piece := range pieces { - pmeta[i] = sectorbuilder.PieceMetadata{ - Key: pieceKeys[i], - Size: piece.Size, - CommP: piece.CommP, - } - } - - sealedPath, err := sb.SealedSectorPath(sectorID) - if err != nil { - return nil, err - } - - cacheDir, err := sb.sectorCacheDir(sectorID) - if err != nil { - return nil, err - } - - err = sectorbuilder.ImportSealedSector( - sb.handle, - sectorID, - cacheDir, - sealedPath, - ticket, - seed, - rspco.CommR, - rspco.CommD, - rspco.CommC, - rspco.CommRLast, - proof, - pmeta, - ) - if err != nil { - return nil, xerrors.Errorf("ImportSealedSector: %w", err) - } return proof, nil } +func (sb *SectorBuilder) ComputeElectionPoSt(sectorInfo SortedPublicSectorInfo, challengeSeed []byte, winners []EPostCandidate) ([]byte, error) { + if len(challengeSeed) != CommLen { + return nil, xerrors.Errorf("given challenge seed was the wrong length: %d != %d", len(challengeSeed), CommLen) + } + var cseed [CommLen]byte + copy(cseed[:], challengeSeed) + + privsects, err := sb.pubSectorToPriv(sectorInfo) + if err != nil { + return nil, err + } + + proverID := addressToProverID(sb.Miner) + return sectorbuilder.GeneratePoSt(sb.ssize, proverID, privsects, cseed, winners) +} + +func (sb *SectorBuilder) GenerateEPostCandidates(sectorInfo SortedPublicSectorInfo, challengeSeed [CommLen]byte, faults []uint64) ([]EPostCandidate, error) { + privsectors, err := sb.pubSectorToPriv(sectorInfo) + if err != nil { + return nil, err + } + + challengeCount := electionPostChallengeCount(uint64(len(sectorInfo.Values()))) + + proverID := addressToProverID(sb.Miner) + return sectorbuilder.GenerateCandidates(sb.ssize, proverID, challengeSeed, challengeCount, privsectors) +} + +func (sb *SectorBuilder) pubSectorToPriv(sectorInfo SortedPublicSectorInfo) (SortedPrivateSectorInfo, error) { + var out []sectorbuilder.PrivateSectorInfo + for _, s := range sectorInfo.Values() { + cachePath, err := sb.sectorCacheDir(s.SectorID) + if err != nil { + return SortedPrivateSectorInfo{}, xerrors.Errorf("getting cache path for sector %d: %w", s.SectorID, err) + } + + sealedPath, err := sb.SealedSectorPath(s.SectorID) + if err != nil { + return SortedPrivateSectorInfo{}, xerrors.Errorf("getting sealed path for sector %d: %w", s.SectorID, err) + } + + out = append(out, sectorbuilder.PrivateSectorInfo{ + SectorID: s.SectorID, + CommR: s.CommR, + CacheDirPath: cachePath, + SealedSectorPath: sealedPath, + }) + } + return NewSortedPrivateSectorInfo(out), nil +} + +func (sb *SectorBuilder) GenerateFallbackPoSt(sectorInfo SortedPublicSectorInfo, challengeSeed [CommLen]byte, faults []uint64) ([]EPostCandidate, []byte, error) { + privsectors, err := sb.pubSectorToPriv(sectorInfo) + if err != nil { + return nil, nil, err + } + + challengeCount := fallbackPostChallengeCount(uint64(len(sectorInfo.Values()))) + + proverID := addressToProverID(sb.Miner) + candidates, err := sectorbuilder.GenerateCandidates(sb.ssize, proverID, challengeSeed, challengeCount, privsectors) + if err != nil { + return nil, nil, err + } + + proof, err := sectorbuilder.GeneratePoSt(sb.ssize, proverID, privsectors, challengeSeed, candidates) + return candidates, proof, err +} + func (sb *SectorBuilder) Stop() { close(sb.stopping) } + +func electionPostChallengeCount(sectors uint64) uint64 { + // ceil(sectors / build.SectorChallengeRatioDiv) + return (sectors + build.SectorChallengeRatioDiv - 1) / build.SectorChallengeRatioDiv +} + +func fallbackPostChallengeCount(sectors uint64) uint64 { + challengeCount := electionPostChallengeCount(sectors) + if challengeCount > build.MaxFallbackPostChallengeCount { + return build.MaxFallbackPostChallengeCount + } + return challengeCount +} diff --git a/lib/sectorbuilder/sectorbuilder_test.go b/lib/sectorbuilder/sectorbuilder_test.go index 044c690bf..9ac332ae2 100644 --- a/lib/sectorbuilder/sectorbuilder_test.go +++ b/lib/sectorbuilder/sectorbuilder_test.go @@ -2,6 +2,7 @@ package sectorbuilder_test import ( "context" + "fmt" "io" "io/ioutil" "math/rand" @@ -15,6 +16,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + ffi "github.com/filecoin-project/filecoin-ffi" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/lib/sectorbuilder" ) @@ -59,7 +61,7 @@ func (s *seal) commit(t *testing.T, sb *sectorbuilder.SectorBuilder, done func() TicketBytes: [32]byte{0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7, 6, 45, 3, 2, 1, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9}, } - proof, err := sb.SealCommit(s.sid, s.ticket, seed, []sectorbuilder.PublicPieceInfo{s.ppi}, []string{"foo"}, s.pco) + proof, err := sb.SealCommit(s.sid, s.ticket, seed, []sectorbuilder.PublicPieceInfo{s.ppi}, s.pco) if err != nil { t.Fatalf("%+v", err) } @@ -76,26 +78,44 @@ func (s *seal) commit(t *testing.T, sb *sectorbuilder.SectorBuilder, done func() done() } -func (s *seal) post(t *testing.T, sb *sectorbuilder.SectorBuilder) { +func post(t *testing.T, sb *sectorbuilder.SectorBuilder, seals ...seal) time.Time { cSeed := [32]byte{0, 9, 2, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7, 6, 45, 3, 2, 1, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9} - ssi := sectorbuilder.NewSortedSectorInfo([]sectorbuilder.SectorInfo{{ - SectorID: s.sid, - CommR: s.pco.CommR, - }}) + ppi := make([]ffi.PublicSectorInfo, len(seals)) + for i, s := range seals { + ppi[i] = ffi.PublicSectorInfo{ + SectorID: s.sid, + CommR: s.pco.CommR, + } + } - postProof, err := sb.GeneratePoSt(ssi, cSeed, []uint64{}) + ssi := sectorbuilder.NewSortedPublicSectorInfo(ppi) + + candndates, err := sb.GenerateEPostCandidates(ssi, cSeed, []uint64{}) if err != nil { t.Fatalf("%+v", err) } - ok, err := sectorbuilder.VerifyPost(context.TODO(), sb.SectorSize(), ssi, cSeed, postProof, []uint64{}) + genCandidates := time.Now() + + if len(candndates) != 1 { + t.Fatal("expected 1 candidate") + } + + postProof, err := sb.ComputeElectionPoSt(ssi, cSeed[:], candndates) + if err != nil { + t.Fatalf("%+v", err) + } + + ok, err := sectorbuilder.VerifyElectionPost(context.TODO(), sb.SectorSize(), ssi, cSeed[:], postProof, candndates, sb.Miner) if err != nil { t.Fatalf("%+v", err) } if !ok { t.Fatal("bad post") } + + return genCandidates } func TestSealAndVerify(t *testing.T) { @@ -123,7 +143,10 @@ func TestSealAndVerify(t *testing.T) { t.Fatalf("%+v", err) } cleanup := func() { - sb.Destroy() + if t.Failed() { + fmt.Printf("not removing %s\n", dir) + return + } if err := os.RemoveAll(dir); err != nil { t.Error(err) } @@ -137,20 +160,95 @@ func TestSealAndVerify(t *testing.T) { s := seal{sid: si} + start := time.Now() + s.precommit(t, sb, 1, func() {}) + precommit := time.Now() + s.commit(t, sb, func() {}) - s.post(t, sb) + commit := time.Now() + + genCandidiates := post(t, sb, s) + + epost := time.Now() // Restart sectorbuilder, re-run post - sb.Destroy() sb, err = sectorbuilder.TempSectorbuilderDir(dir, sectorSize, ds) if err != nil { t.Fatalf("%+v", err) } - s.post(t, sb) + post(t, sb, s) + + fmt.Printf("PreCommit: %s\n", precommit.Sub(start).String()) + fmt.Printf("Commit: %s\n", commit.Sub(precommit).String()) + fmt.Printf("GenCandidates: %s\n", genCandidiates.Sub(commit).String()) + fmt.Printf("EPoSt: %s\n", epost.Sub(genCandidiates).String()) +} + +func TestSealPoStNoCommit(t *testing.T) { + if runtime.NumCPU() < 10 && os.Getenv("CI") == "" { // don't bother on slow hardware + t.Skip("this is slow") + } + os.Setenv("BELLMAN_NO_GPU", "1") + os.Setenv("RUST_LOG", "info") + + build.SectorSizes = []uint64{sectorSize} + + if err := build.GetParams(true, true); err != nil { + t.Fatalf("%+v", err) + } + + ds := datastore.NewMapDatastore() + + dir, err := ioutil.TempDir("", "sbtest") + if err != nil { + t.Fatal(err) + } + + sb, err := sectorbuilder.TempSectorbuilderDir(dir, sectorSize, ds) + if err != nil { + t.Fatalf("%+v", err) + } + cleanup := func() { + if t.Failed() { + fmt.Printf("not removing %s\n", dir) + return + } + if err := os.RemoveAll(dir); err != nil { + t.Error(err) + } + } + defer cleanup() + + si, err := sb.AcquireSectorId() + if err != nil { + t.Fatalf("%+v", err) + } + + s := seal{sid: si} + + start := time.Now() + + s.precommit(t, sb, 1, func() {}) + + precommit := time.Now() + + // Restart sectorbuilder, re-run post + sb, err = sectorbuilder.TempSectorbuilderDir(dir, sectorSize, ds) + if err != nil { + t.Fatalf("%+v", err) + } + + genCandidiates := post(t, sb, s) + + epost := time.Now() + + fmt.Printf("PreCommit: %s\n", precommit.Sub(start).String()) + fmt.Printf("GenCandidates: %s\n", genCandidiates.Sub(precommit).String()) + fmt.Printf("EPoSt: %s\n", epost.Sub(genCandidiates).String()) } func TestSealAndVerify2(t *testing.T) { @@ -178,7 +276,6 @@ func TestSealAndVerify2(t *testing.T) { t.Fatalf("%+v", err) } cleanup := func() { - sb.Destroy() if err := os.RemoveAll(dir); err != nil { t.Error(err) } @@ -210,6 +307,8 @@ func TestSealAndVerify2(t *testing.T) { go s1.commit(t, sb, wg.Done) go s2.commit(t, sb, wg.Done) wg.Wait() + + post(t, sb, s1, s2) } func TestAcquireID(t *testing.T) { @@ -235,8 +334,6 @@ func TestAcquireID(t *testing.T) { assertAcquire(2) assertAcquire(3) - sb.Destroy() - sb, err = sectorbuilder.TempSectorbuilderDir(dir, sectorSize, ds) if err != nil { t.Fatalf("%+v", err) @@ -246,7 +343,6 @@ func TestAcquireID(t *testing.T) { assertAcquire(5) assertAcquire(6) - sb.Destroy() if err := os.RemoveAll(dir); err != nil { t.Error(err) } diff --git a/lib/sectorbuilder/simple.go b/lib/sectorbuilder/simple.go index d1ca86e6a..f6a17668a 100644 --- a/lib/sectorbuilder/simple.go +++ b/lib/sectorbuilder/simple.go @@ -4,18 +4,12 @@ import ( "context" "io" - sectorbuilder "github.com/filecoin-project/go-sectorbuilder" + sectorbuilder "github.com/filecoin-project/filecoin-ffi" "go.opencensus.io/trace" "github.com/filecoin-project/lotus/chain/address" ) -func (sb *SectorBuilder) GeneratePoSt(sectorInfo SortedSectorInfo, challengeSeed [CommLen]byte, faults []uint64) ([]byte, error) { - // Wait, this is a blocking method with no way of interrupting it? - // does it checkpoint itself? - return sectorbuilder.GeneratePoSt(sb.handle, sectorInfo, challengeSeed, faults) -} - func (sb *SectorBuilder) SectorSize() uint64 { return sb.ssize } @@ -33,14 +27,37 @@ func VerifySeal(sectorSize uint64, commR, commD []byte, proverID address.Address return sectorbuilder.VerifySeal(sectorSize, commRa, commDa, proverIDa, ticketa, seeda, sectorID, proof) } -func NewSortedSectorInfo(sectors []SectorInfo) SortedSectorInfo { - return sectorbuilder.NewSortedSectorInfo(sectors...) +func NewSortedPrivateSectorInfo(sectors []sectorbuilder.PrivateSectorInfo) SortedPrivateSectorInfo { + return sectorbuilder.NewSortedPrivateSectorInfo(sectors...) } -func VerifyPost(ctx context.Context, sectorSize uint64, sectorInfo SortedSectorInfo, challengeSeed [CommLen]byte, proof []byte, faults []uint64) (bool, error) { +func NewSortedPublicSectorInfo(sectors []sectorbuilder.PublicSectorInfo) SortedPublicSectorInfo { + return sectorbuilder.NewSortedPublicSectorInfo(sectors...) +} + +func VerifyElectionPost(ctx context.Context, sectorSize uint64, sectorInfo SortedPublicSectorInfo, challengeSeed []byte, proof []byte, candidates []EPostCandidate, proverID address.Address) (bool, error) { + challengeCount := electionPostChallengeCount(uint64(len(sectorInfo.Values()))) + return verifyPost(ctx, sectorSize, sectorInfo, challengeCount, challengeSeed, proof, candidates, proverID) +} + +func VerifyFallbackPost(ctx context.Context, sectorSize uint64, sectorInfo SortedPublicSectorInfo, challengeSeed []byte, proof []byte, candidates []EPostCandidate, proverID address.Address) (bool, error) { + challengeCount := fallbackPostChallengeCount(uint64(len(sectorInfo.Values()))) + return verifyPost(ctx, sectorSize, sectorInfo, challengeCount, challengeSeed, proof, candidates, proverID) +} + +func verifyPost(ctx context.Context, sectorSize uint64, sectorInfo SortedPublicSectorInfo, challengeCount uint64, challengeSeed []byte, proof []byte, candidates []EPostCandidate, proverID address.Address) (bool, error) { + if challengeCount != uint64(len(candidates)) { + log.Warnf("verifyPost with wrong candidate count: expected %d, got %d", challengeCount, len(candidates)) + return false, nil // user input, dont't error + } + + var challengeSeeda [CommLen]byte + copy(challengeSeeda[:], challengeSeed) + _, span := trace.StartSpan(ctx, "VerifyPoSt") defer span.End() - return sectorbuilder.VerifyPoSt(sectorSize, sectorInfo, challengeSeed, proof, faults) + prover := addressToProverID(proverID) + return sectorbuilder.VerifyPoSt(sectorSize, sectorInfo, challengeSeeda, challengeCount, proof, candidates, prover) } func GeneratePieceCommitment(piece io.Reader, pieceSize uint64) (commP [CommLen]byte, err error) { @@ -57,6 +74,6 @@ func GeneratePieceCommitment(piece io.Reader, pieceSize uint64) (commP [CommLen] return commP, werr() } -func GenerateDataCommitment(ssize uint64, pieces []PublicPieceInfo) ([CommLen]byte, error) { +func GenerateDataCommitment(ssize uint64, pieces []sectorbuilder.PublicPieceInfo) ([CommLen]byte, error) { return sectorbuilder.GenerateDataCommitment(ssize, pieces) -} +} \ No newline at end of file diff --git a/lotuspond/api.go b/lotuspond/api.go index 2bd87c0b9..ef742b1a2 100644 --- a/lotuspond/api.go +++ b/lotuspond/api.go @@ -2,14 +2,15 @@ package main import ( "crypto/rand" - "github.com/filecoin-project/lotus/lib/jsonrpc" - "github.com/filecoin-project/lotus/node/repo" "io" "io/ioutil" "os" "sync" "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/lib/jsonrpc" + "github.com/filecoin-project/lotus/node/repo" ) type NodeState int diff --git a/lotuspond/front/src/FullNode.js b/lotuspond/front/src/FullNode.js index 874288261..eb530dee3 100644 --- a/lotuspond/front/src/FullNode.js +++ b/lotuspond/front/src/FullNode.js @@ -45,8 +45,6 @@ class FullNode extends React.Component { return this.props.client.call('Filecoin.PaychVoucherList', [paych]) })) - let minerList = await this.props.client.call('Filecoin.MinerAddresses', []) - let mpoolPending = (await this.props.client.call('Filecoin.MpoolPending', [tipset])).length this.setState(() => ({ @@ -62,8 +60,6 @@ class FullNode extends React.Component { vouchers: vouchers, defaultAddr: defaultAddr, - - minerList: minerList, })) } @@ -110,11 +106,6 @@ class FullNode extends React.Component { ) } - let miners = - if(this.state.minerList.length > 0) { - miners = this.state.minerList.map((a, k) =>
) - } - let storageMine = let addresses = this.state.addrs.map((addr) => { @@ -153,7 +144,6 @@ class FullNode extends React.Component {
Balances: [New [Secp256k1] [BLS]]
{addresses}
-
{miners}
{paychannels}
diff --git a/lotuspond/front/src/State.js b/lotuspond/front/src/State.js index 0af240c2e..9932ceb5e 100644 --- a/lotuspond/front/src/State.js +++ b/lotuspond/front/src/State.js @@ -181,7 +181,7 @@ class MinerState extends React.Component {
Worker:
Sector Size: {this.state.sectorSize/1024} KiB
Power: {state.Power} ({state.Power/this.state.networkPower*100}%)
-
Proving Period End: {state.ProvingPeriodEnd}
+
Election Period Start: {state.ElectionPeriodStart}
Slashed: {state.SlashedAt === 0 ? "NO" : state.SlashedAt}
----
diff --git a/lotuspond/front/src/StorageNode.js b/lotuspond/front/src/StorageNode.js index c07abc590..903729d9f 100644 --- a/lotuspond/front/src/StorageNode.js +++ b/lotuspond/front/src/StorageNode.js @@ -120,7 +120,7 @@ class StorageNode extends React.Component {
-  PPE: {this.state.actorState.State.ProvingPeriodEnd} +  EPS: {this.state.actorState.State.ElectionPeriodStart}
{this.state.statusCounts.map((c, i) => {sealCodes[i]}: {c} | )}
diff --git a/lotuspond/outmux.go b/lotuspond/outmux.go index 713338d97..13b8899c0 100644 --- a/lotuspond/outmux.go +++ b/lotuspond/outmux.go @@ -3,12 +3,13 @@ package main import ( "bufio" "fmt" - "github.com/gorilla/websocket" - "github.com/opentracing/opentracing-go/log" "io" "net/http" "strings" "sync" + + "github.com/gorilla/websocket" + "github.com/opentracing/opentracing-go/log" ) type outmux struct { diff --git a/lotuspond/spawn.go b/lotuspond/spawn.go index a4f4c0b36..5eb8ca52d 100644 --- a/lotuspond/spawn.go +++ b/lotuspond/spawn.go @@ -11,6 +11,10 @@ import ( "time" "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/address" + "github.com/filecoin-project/lotus/cmd/lotus-seed/seed" ) func (api *api) Spawn() (nodeInfo, error) { @@ -19,9 +23,29 @@ func (api *api) Spawn() (nodeInfo, error) { return nodeInfo{}, err } + params := []string{"daemon", "--bootstrap=false"} genParam := "--genesis=" + api.genesis + id := atomic.AddInt32(&api.cmds, 1) if id == 1 { + // preseal + + genMiner, err := address.NewIDAddress(101) + if err != nil { + return nodeInfo{}, err + } + + sbroot := filepath.Join(dir, "preseal") + genm, err := seed.PreSeal(genMiner, build.SectorSizes[0], 1, sbroot, []byte("8")) + if err != nil { + return nodeInfo{}, xerrors.Errorf("preseal failed: %w", err) + } + + if err := seed.WriteGenesisMiner(genMiner, sbroot, genm); err != nil { + return nodeInfo{}, xerrors.Errorf("failed to write genminer info: %w", err) + } + params = append(params, "--genesis-presealed-sectors="+filepath.Join(dir, "preseal", "pre-seal-t0101.json")) + // make genesis genf, err := ioutil.TempFile(os.TempDir(), "lotus-genesis-") if err != nil { @@ -54,7 +78,7 @@ func (api *api) Spawn() (nodeInfo, error) { return nodeInfo{}, err } - cmd := exec.Command("./lotus", "daemon", "--bootstrap=false", genParam) + cmd := exec.Command("./lotus", append(params, genParam)...) cmd.Stderr = io.MultiWriter(os.Stderr, errlogfile, mux.errpw) cmd.Stdout = io.MultiWriter(os.Stdout, logfile, mux.outpw) @@ -114,7 +138,7 @@ func (api *api) SpawnStorage(fullNodeRepo string) (nodeInfo, error) { initArgs := []string{"init"} if fullNodeRepo == api.running[1].meta.Repo { - initArgs = []string{"init", "--actor=t0101", "--genesis-miner"} + initArgs = []string{"init", "--actor=t0101", "--genesis-miner", "--pre-sealed-sectors=" + filepath.Join(fullNodeRepo, "preseal")} } id := atomic.AddInt32(&api.cmds, 1) diff --git a/miner/miner.go b/miner/miner.go index 09e904e0c..232910aed 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -5,17 +5,15 @@ import ( "sync" "time" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/address" "github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/node/impl/full" logging "github.com/ipfs/go-log" - "github.com/pkg/errors" "go.opencensus.io/trace" - "go.uber.org/fx" "golang.org/x/xerrors" ) @@ -23,19 +21,10 @@ var log = logging.Logger("miner") type waitFunc func(ctx context.Context) error -type api struct { - fx.In - - full.ChainAPI - full.SyncAPI - full.MpoolAPI - full.WalletAPI - full.StateAPI -} - -func NewMiner(api api) *Miner { +func NewMiner(api api.FullNode, epp gen.ElectionPoStProver) *Miner { return &Miner{ api: api, + epp: epp, waitFunc: func(ctx context.Context) error { // Wait around for half the block time in case other parents come in time.Sleep(build.BlockDelay * time.Second / 2) @@ -45,7 +34,9 @@ func NewMiner(api api) *Miner { } type Miner struct { - api api + api api.FullNode + + epp gen.ElectionPoStProver lk sync.Mutex addresses []address.Address @@ -161,8 +152,8 @@ eventLoop: log.Errorf("failed to get best mining candidate: %s", err) continue } - if base.ts.Equals(lastBase.ts) && len(lastBase.tickets) == len(base.tickets) { - log.Errorf("BestMiningCandidate from the previous round: %s (tkts:%d)", lastBase.ts.Cids(), len(lastBase.tickets)) + if base.ts.Equals(lastBase.ts) && lastBase.nullRounds == base.nullRounds { + log.Errorf("BestMiningCandidate from the previous round: %s (nulls:%d)", lastBase.ts.Cids(), lastBase.nullRounds) time.Sleep(build.BlockDelay * time.Second) continue } @@ -173,7 +164,7 @@ eventLoop: for _, addr := range addrs { b, err := m.mineOne(ctx, addr, base) if err != nil { - log.Errorf("mining block failed: %s", err) + log.Errorf("mining block failed: %+v", err) continue } if b != nil { @@ -205,15 +196,15 @@ eventLoop: } } } else { - nextRound := time.Unix(int64(base.ts.MinTimestamp()+uint64(build.BlockDelay*len(base.tickets))), 0) + nextRound := time.Unix(int64(base.ts.MinTimestamp()+uint64(build.BlockDelay*base.nullRounds)), 0) time.Sleep(time.Until(nextRound)) } } } type MiningBase struct { - ts *types.TipSet - tickets []*types.Ticket + ts *types.TipSet + nullRounds uint64 } func (m *Miner) GetBestMiningCandidate(ctx context.Context) (*MiningBase, error) { @@ -246,35 +237,54 @@ func (m *Miner) GetBestMiningCandidate(ctx context.Context) (*MiningBase, error) }, nil } -func (m *Miner) mineOne(ctx context.Context, addr address.Address, base *MiningBase) (*types.BlockMsg, error) { - log.Debugw("attempting to mine a block", "tipset", types.LogCids(base.ts.Cids())) - ticket, err := m.scratchTicket(ctx, addr, base) +func (m *Miner) isSlashed(ctx context.Context, addr address.Address, ts *types.TipSet) (bool, error) { + power, err := m.api.StateMinerPower(ctx, addr, ts) if err != nil { - return nil, errors.Wrap(err, "scratching ticket failed") + return false, err } - win, proof, err := gen.IsRoundWinner(ctx, base.ts, append(base.tickets, ticket), addr, &m.api) + return power.MinerPower.Equals(types.NewInt(0)), nil +} + +func (m *Miner) mineOne(ctx context.Context, addr address.Address, base *MiningBase) (*types.BlockMsg, error) { + log.Debugw("attempting to mine a block", "tipset", types.LogCids(base.ts.Cids())) + start := time.Now() + + slashed, err := m.isSlashed(ctx, addr, base.ts) if err != nil { - return nil, errors.Wrap(err, "failed to check if we win next round") + return nil, xerrors.Errorf("checking if miner is slashed: %w", err) + } + if slashed { + log.Warnf("Slashed at epoch %d, not attempting to mine a block", base.ts.Height()+base.nullRounds) + base.nullRounds++ + return nil, nil + } + + ticket, err := m.computeTicket(ctx, addr, base) + if err != nil { + return nil, xerrors.Errorf("scratching ticket failed: %w", err) + } + + win, proof, err := gen.IsRoundWinner(ctx, base.ts, int64(base.ts.Height()+base.nullRounds+1), addr, m.epp, m.api) + if err != nil { + return nil, xerrors.Errorf("failed to check if we win next round: %w", err) } if !win { - m.submitNullTicket(base, ticket) + base.nullRounds++ return nil, nil } b, err := m.createBlock(base, addr, ticket, proof) if err != nil { - return nil, errors.Wrap(err, "failed to create block") + return nil, xerrors.Errorf("failed to create block: %w", err) } log.Infow("mined new block", "cid", b.Cid()) - return b, nil -} + dur := time.Now().Sub(start) + log.Infof("Creating block took %s", dur) -func (m *Miner) submitNullTicket(base *MiningBase, ticket *types.Ticket) { - base.tickets = append(base.tickets, ticket) - m.lastWork = base + return b, nil } func (m *Miner) computeVRF(ctx context.Context, addr address.Address, input []byte) ([]byte, error) { @@ -283,7 +293,7 @@ func (m *Miner) computeVRF(ctx context.Context, addr address.Address, input []by return nil, err } - return gen.ComputeVRF(ctx, m.api.WalletSign, w, input) + return gen.ComputeVRF(ctx, m.api.WalletSign, w, addr, gen.DSepTicket, input) } func (m *Miner) getMinerWorker(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { @@ -308,15 +318,11 @@ func (m *Miner) getMinerWorker(ctx context.Context, addr address.Address, ts *ty return w, nil } -func (m *Miner) scratchTicket(ctx context.Context, addr address.Address, base *MiningBase) (*types.Ticket, error) { - var lastTicket *types.Ticket - if len(base.tickets) > 0 { - lastTicket = base.tickets[len(base.tickets)-1] - } else { - lastTicket = base.ts.MinTicket() - } +func (m *Miner) computeTicket(ctx context.Context, addr address.Address, base *MiningBase) (*types.Ticket, error) { - vrfOut, err := m.computeVRF(ctx, addr, lastTicket.VRFProof) + vrfBase := base.ts.MinTicket().VRFProof + + vrfOut, err := m.computeVRF(ctx, addr, vrfBase) if err != nil { return nil, err } @@ -326,11 +332,11 @@ func (m *Miner) scratchTicket(ctx context.Context, addr address.Address, base *M }, nil } -func (m *Miner) createBlock(base *MiningBase, addr address.Address, ticket *types.Ticket, proof types.ElectionProof) (*types.BlockMsg, error) { +func (m *Miner) createBlock(base *MiningBase, addr address.Address, ticket *types.Ticket, proof *types.EPostProof) (*types.BlockMsg, error) { pending, err := m.api.MpoolPending(context.TODO(), base.ts) if err != nil { - return nil, errors.Wrapf(err, "failed to get pending messages") + return nil, xerrors.Errorf("failed to get pending messages: %w", err) } msgs, err := selectMessages(context.TODO(), m.api.StateGetActor, base, pending) @@ -338,10 +344,12 @@ func (m *Miner) createBlock(base *MiningBase, addr address.Address, ticket *type return nil, xerrors.Errorf("message filtering failed: %w", err) } - uts := base.ts.MinTimestamp() + uint64(build.BlockDelay*(len(base.tickets)+1)) + uts := base.ts.MinTimestamp() + uint64(build.BlockDelay*(base.nullRounds+1)) + + nheight := base.ts.Height() + base.nullRounds + 1 // why even return this? that api call could just submit it for us - return m.api.MinerCreateBlock(context.TODO(), addr, base.ts, append(base.tickets, ticket), proof, msgs, uint64(uts)) + return m.api.MinerCreateBlock(context.TODO(), addr, base.ts, ticket, proof, msgs, nheight, uint64(uts)) } type actorLookup func(context.Context, address.Address, *types.TipSet) (*types.Actor, error) @@ -378,7 +386,7 @@ func selectMessages(ctx context.Context, al actorLookup, base *MiningBase, msgs } if msg.Message.Nonce < inclNonces[from] { - log.Warnf("message in mempool has already used nonce (%d < %d) %s", msg.Message.Nonce, inclNonces[from], msg.Cid()) + log.Warnf("message in mempool has already used nonce (%d < %d), from %s, to %s, %s", msg.Message.Nonce, inclNonces[from], msg.Message.From, msg.Message.To, msg.Cid()) continue } diff --git a/miner/testminer.go b/miner/testminer.go index 6adf02266..48fbd92a6 100644 --- a/miner/testminer.go +++ b/miner/testminer.go @@ -2,10 +2,12 @@ package miner import ( "context" + + "github.com/filecoin-project/lotus/api" ) -func NewTestMiner(nextCh <-chan struct{}) func(api api) *Miner { - return func(api api) *Miner { +func NewTestMiner(nextCh <-chan struct{}) func(api api.FullNode) *Miner { + return func(api api.FullNode) *Miner { return &Miner{ api: api, waitFunc: chanWaiter(nextCh), diff --git a/node/builder.go b/node/builder.go index 02082a3e5..412730fab 100644 --- a/node/builder.go +++ b/node/builder.go @@ -22,6 +22,7 @@ import ( "github.com/filecoin-project/lotus/chain" "github.com/filecoin-project/lotus/chain/blocksync" "github.com/filecoin-project/lotus/chain/deals" + "github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/market" "github.com/filecoin-project/lotus/chain/metrics" "github.com/filecoin-project/lotus/chain/stmgr" @@ -92,7 +93,6 @@ const ( HandleDealsKey HandleRetrievalKey RunSectorServiceKey - RegisterMinerKey RegisterProviderValidatorKey // daemon @@ -231,8 +231,6 @@ func Online() Option { Override(new(*paych.Store), paych.NewStore), Override(new(*paych.Manager), paych.NewManager), Override(new(*market.FundMgr), market.NewFundMgr), - - Override(new(*miner.Miner), miner.NewMiner), ), // Storage miner @@ -252,7 +250,8 @@ func Online() Option { Override(RegisterProviderValidatorKey, modules.RegisterProviderValidator), Override(HandleRetrievalKey, modules.HandleRetrieval), Override(HandleDealsKey, modules.HandleDeals), - Override(RegisterMinerKey, modules.RegisterMiner), + Override(new(gen.ElectionPoStProver), storage.NewElectionPoStProver), + Override(new(*miner.Miner), modules.SetupBlockProducer), ), ) } @@ -312,6 +311,9 @@ func ConfigFullNode(c interface{}) Option { return Options( ConfigCommon(&cfg.Common), Override(HeadMetricsKey, metrics.SendHeadNotifs(cfg.Metrics.Nickname)), + If(cfg.Metrics.PubsubTracing, + Override(new(*pubsub.PubSub), lp2p.GossipSub(lp2p.PubsubTracer())), + ), ) } diff --git a/node/config/def.go b/node/config/def.go index d704b428b..9391708a6 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -41,7 +41,8 @@ type Libp2p struct { // // Full Node type Metrics struct { - Nickname string + Nickname string + PubsubTracing bool } // // Storage Miner diff --git a/node/impl/full.go b/node/impl/full.go index 76cdc5de4..2c01674fb 100644 --- a/node/impl/full.go +++ b/node/impl/full.go @@ -1,8 +1,6 @@ package impl import ( - "context" - logging "github.com/ipfs/go-log" "github.com/filecoin-project/lotus/node/impl/client" @@ -10,8 +8,6 @@ import ( "github.com/filecoin-project/lotus/node/impl/paych" "github.com/filecoin-project/lotus/api" - "github.com/filecoin-project/lotus/chain/address" - "github.com/filecoin-project/lotus/miner" "github.com/filecoin-project/lotus/node/impl/full" ) @@ -27,20 +23,6 @@ type FullNodeAPI struct { full.StateAPI full.WalletAPI full.SyncAPI - - Miner *miner.Miner -} - -func (a *FullNodeAPI) MinerAddresses(context.Context) ([]address.Address, error) { - return a.Miner.Addresses() -} - -func (a *FullNodeAPI) MinerRegister(ctx context.Context, addr address.Address) error { - return a.Miner.Register(addr) -} - -func (a *FullNodeAPI) MinerUnregister(ctx context.Context, addr address.Address) error { - return a.Miner.Unregister(ctx, addr) } var _ api.FullNode = &FullNodeAPI{} diff --git a/node/impl/full/chain.go b/node/impl/full/chain.go index 4285682fc..1e39be598 100644 --- a/node/impl/full/chain.go +++ b/node/impl/full/chain.go @@ -28,8 +28,8 @@ func (a *ChainAPI) ChainHead(context.Context) (*types.TipSet, error) { return a.Chain.GetHeaviestTipSet(), nil } -func (a *ChainAPI) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, tickets []*types.Ticket, lb int) ([]byte, error) { - return a.Chain.GetRandomness(ctx, pts.Cids(), tickets, int64(lb)) +func (a *ChainAPI) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, round int64) ([]byte, error) { + return a.Chain.GetRandomness(ctx, pts.Cids(), round) } func (a *ChainAPI) ChainGetBlock(ctx context.Context, msg cid.Cid) (*types.BlockHeader, error) { diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 9724528ca..04037c079 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -76,8 +76,8 @@ func (a *StateAPI) StateMinerPeerID(ctx context.Context, m address.Address, ts * return stmgr.GetMinerPeerID(ctx, a.StateManager, ts, m) } -func (a *StateAPI) StateMinerProvingPeriodEnd(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) { - return stmgr.GetMinerProvingPeriodEnd(ctx, a.StateManager, ts, actor) +func (a *StateAPI) StateMinerElectionPeriodStart(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) { + return stmgr.GetMinerElectionPeriodStart(ctx, a.StateManager, ts, actor) } func (a *StateAPI) StateMinerSectorSize(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) { @@ -186,8 +186,8 @@ func (a *StateAPI) StateReadState(ctx context.Context, act *types.Actor, ts *typ } // This is on StateAPI because miner.Miner requires this, and MinerAPI requires miner.Miner -func (a *StateAPI) MinerCreateBlock(ctx context.Context, addr address.Address, parents *types.TipSet, tickets []*types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage, ts uint64) (*types.BlockMsg, error) { - fblk, err := gen.MinerCreateBlock(ctx, a.StateManager, a.Wallet, addr, parents, tickets, proof, msgs, ts) +func (a *StateAPI) MinerCreateBlock(ctx context.Context, addr address.Address, parents *types.TipSet, ticket *types.Ticket, proof *types.EPostProof, msgs []*types.SignedMessage, height, ts uint64) (*types.BlockMsg, error) { + fblk, err := gen.MinerCreateBlock(ctx, a.StateManager, a.Wallet, addr, parents, ticket, proof, msgs, height, ts) if err != nil { return nil, err } diff --git a/node/impl/storminer.go b/node/impl/storminer.go index 083097827..a4f04cd62 100644 --- a/node/impl/storminer.go +++ b/node/impl/storminer.go @@ -11,6 +11,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/address" "github.com/filecoin-project/lotus/lib/sectorbuilder" + "github.com/filecoin-project/lotus/miner" "github.com/filecoin-project/lotus/storage" "github.com/filecoin-project/lotus/storage/sectorblocks" ) @@ -22,8 +23,9 @@ type StorageMinerAPI struct { SectorBuilder *sectorbuilder.SectorBuilder SectorBlocks *sectorblocks.SectorBlocks - Miner *storage.Miner - Full api.FullNode + Miner *storage.Miner + BlockMiner *miner.Miner + Full api.FullNode } func (sm *StorageMinerAPI) ServeRemote(w http.ResponseWriter, r *http.Request) { diff --git a/node/modules/chain.go b/node/modules/chain.go index 25f71c84a..b6fb29c8c 100644 --- a/node/modules/chain.go +++ b/node/modules/chain.go @@ -41,14 +41,17 @@ func ChainExchange(mctx helpers.MetricsCtx, lc fx.Lifecycle, host host.Host, rt return exch } -func MessagePool(lc fx.Lifecycle, sm *stmgr.StateManager, ps *pubsub.PubSub) *chain.MessagePool { - mp := chain.NewMessagePool(sm, ps) +func MessagePool(lc fx.Lifecycle, sm *stmgr.StateManager, ps *pubsub.PubSub, ds dtypes.MetadataDS) (*chain.MessagePool, error) { + mp, err := chain.NewMessagePool(sm, ps, ds) + if err != nil { + return nil, xerrors.Errorf("constructing mpool: %w", err) + } lc.Append(fx.Hook{ OnStop: func(_ context.Context) error { return mp.Close() }, }) - return mp + return mp, nil } func ChainBlockstore(r repo.LockedRepo) (dtypes.ChainBlockstore, error) { diff --git a/node/modules/core.go b/node/modules/core.go index afc1b682a..5eab490eb 100644 --- a/node/modules/core.go +++ b/node/modules/core.go @@ -3,6 +3,9 @@ package modules import ( "context" "crypto/rand" + "io" + "io/ioutil" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/types" @@ -14,8 +17,6 @@ import ( "github.com/libp2p/go-libp2p-core/peerstore" record "github.com/libp2p/go-libp2p-record" "golang.org/x/xerrors" - "io" - "io/ioutil" ) var log = logging.Logger("modules") diff --git a/node/modules/lp2p/pubsub.go b/node/modules/lp2p/pubsub.go index 3795666c4..657c714f1 100644 --- a/node/modules/lp2p/pubsub.go +++ b/node/modules/lp2p/pubsub.go @@ -1,21 +1,45 @@ package lp2p import ( + "context" + host "github.com/libp2p/go-libp2p-core/host" + peer "github.com/libp2p/go-libp2p-core/peer" pubsub "github.com/libp2p/go-libp2p-pubsub" + ma "github.com/multiformats/go-multiaddr" "go.uber.org/fx" "github.com/filecoin-project/lotus/node/modules/helpers" ) -func FloodSub(pubsubOptions ...pubsub.Option) interface{} { - return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, host host.Host) (service *pubsub.PubSub, err error) { - return pubsub.NewFloodSub(helpers.LifecycleCtx(mctx, lc), host, pubsubOptions...) +type PubsubOpt func(host.Host) pubsub.Option + +func PubsubTracer() PubsubOpt { + return func(host host.Host) pubsub.Option { + pi, err := peer.AddrInfoFromP2pAddr(ma.StringCast("/ip4/147.75.67.199/tcp/4001/p2p/QmTd6UvR47vUidRNZ1ZKXHrAFhqTJAD27rKL9XYghEKgKX")) + if err != nil { + panic(err) + } + + tr, err := pubsub.NewRemoteTracer(context.TODO(), host, *pi) + if err != nil { + panic(err) + } + + return pubsub.WithEventTracer(tr) } } -func GossipSub(pubsubOptions ...pubsub.Option) interface{} { +func GossipSub(pubsubOptions ...PubsubOpt) interface{} { return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, host host.Host) (service *pubsub.PubSub, err error) { - return pubsub.NewGossipSub(helpers.LifecycleCtx(mctx, lc), host, pubsubOptions...) + return pubsub.NewGossipSub(helpers.LifecycleCtx(mctx, lc), host, paresOpts(host, pubsubOptions)...) } } + +func paresOpts(host host.Host, in []PubsubOpt) []pubsub.Option { + out := make([]pubsub.Option, len(in)) + for k, v := range in { + out[k] = v(host) + } + return out +} diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index e5cbe8d62..5be351323 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -2,7 +2,6 @@ package modules import ( "context" - "fmt" "math" "path/filepath" "reflect" @@ -24,9 +23,11 @@ import ( "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/address" "github.com/filecoin-project/lotus/chain/deals" + "github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/datatransfer" "github.com/filecoin-project/lotus/lib/sectorbuilder" "github.com/filecoin-project/lotus/lib/statestore" + "github.com/filecoin-project/lotus/miner" "github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/lotus/node/modules/helpers" "github.com/filecoin-project/lotus/node/repo" @@ -176,41 +177,35 @@ func StagingDAG(mctx helpers.MetricsCtx, lc fx.Lifecycle, r repo.LockedRepo, rt return dag, nil } -func RegisterMiner(lc fx.Lifecycle, ds dtypes.MetadataDS, api api.FullNode) error { +func SetupBlockProducer(lc fx.Lifecycle, ds dtypes.MetadataDS, api api.FullNode, epp gen.ElectionPoStProver) (*miner.Miner, error) { minerAddr, err := minerAddrFromDS(ds) - if err != nil { - return err - } - - lc.Append(fx.Hook{ - OnStart: func(ctx context.Context) error { - log.Infof("Registering miner '%s' with full node", minerAddr) - if err := api.MinerRegister(ctx, minerAddr); err != nil { - return fmt.Errorf("Failed to register miner: %s\nIf you are certain no other storage miner instance is running, try running 'lotus unregister-miner %s' and restarting the storage miner", err, minerAddr) - } - return nil - }, - OnStop: func(ctx context.Context) error { - log.Infof("Unregistering miner '%s' from full node", minerAddr) - return api.MinerUnregister(ctx, minerAddr) - }, - }) - return nil -} - -func SectorBuilder(lc fx.Lifecycle, cfg *sectorbuilder.Config, ds dtypes.MetadataDS) (*sectorbuilder.SectorBuilder, error) { - sb, err := sectorbuilder.New(cfg, ds) if err != nil { return nil, err } + m := miner.NewMiner(api, epp) + lc.Append(fx.Hook{ - OnStop: func(context.Context) error { - sb.Destroy() + OnStart: func(ctx context.Context) error { + if err := m.Register(minerAddr); err != nil { + return err + } return nil }, + OnStop: func(ctx context.Context) error { + return m.Unregister(ctx, minerAddr) + }, }) + return m, nil +} + +func SectorBuilder(cfg *sectorbuilder.Config, ds dtypes.MetadataDS) (*sectorbuilder.SectorBuilder, error) { + sb, err := sectorbuilder.New(cfg, ds) + if err != nil { + return nil, err + } + return sb, nil } @@ -221,7 +216,7 @@ func SealTicketGen(api api.FullNode) storage.TicketFn { return nil, xerrors.Errorf("getting head ts for SealTicket failed: %w", err) } - r, err := api.ChainGetRandomness(ctx, ts.Key(), nil, build.SealRandomnessLookback) + r, err := api.ChainGetRandomness(ctx, ts.Key(), int64(ts.Height())-build.SealRandomnessLookback) if err != nil { return nil, xerrors.Errorf("getting randomness for SealTicket failed: %w", err) } diff --git a/node/modules/testing/genesis.go b/node/modules/testing/genesis.go index 6c81f68ba..6fbc0ccfc 100644 --- a/node/modules/testing/genesis.go +++ b/node/modules/testing/genesis.go @@ -2,11 +2,15 @@ package testing import ( "context" + "encoding/json" "fmt" "io" + "io/ioutil" "os" "time" + "golang.org/x/xerrors" + "github.com/ipfs/go-blockservice" "github.com/ipfs/go-car" "github.com/ipfs/go-cid" @@ -19,29 +23,32 @@ import ( "github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/wallet" + "github.com/filecoin-project/lotus/genesis" "github.com/filecoin-project/lotus/node/modules" "github.com/filecoin-project/lotus/node/modules/dtypes" ) var glog = logging.Logger("genesis") -func MakeGenesisMem(out io.Writer, minerPid peer.ID) func(bs dtypes.ChainBlockstore, w *wallet.Wallet) modules.Genesis { +func MakeGenesisMem(out io.Writer, gmc *gen.GenMinerCfg) func(bs dtypes.ChainBlockstore, w *wallet.Wallet) modules.Genesis { return func(bs dtypes.ChainBlockstore, w *wallet.Wallet) modules.Genesis { return func() (*types.BlockHeader, error) { glog.Warn("Generating new random genesis block, note that this SHOULD NOT happen unless you are setting up new network") - // TODO: make an address allocation - w, err := w.GenerateKey(types.KTBLS) + defk, err := w.GenerateKey(types.KTBLS) if err != nil { return nil, err } - gmc := &gen.GenMinerCfg{ - Owners: []address.Address{w}, - Workers: []address.Address{w}, - PeerIDs: []peer.ID{minerPid}, - } alloc := map[address.Address]types.BigInt{ - w: types.FromFil(10000), + defk: types.FromFil(1000), + } + + for _, genm := range gmc.PreSeals { + waddr, err := w.Import(&genm.Key) + if err != nil { + return nil, err + } + alloc[waddr] = types.FromFil(10000) } b, err := gen.MakeGenesisBlock(bs, alloc, gmc, 100000) @@ -61,23 +68,48 @@ func MakeGenesisMem(out io.Writer, minerPid peer.ID) func(bs dtypes.ChainBlockst } } -func MakeGenesis(outFile string) func(bs dtypes.ChainBlockstore, w *wallet.Wallet) modules.Genesis { +func MakeGenesis(outFile, preseal string) func(bs dtypes.ChainBlockstore, w *wallet.Wallet) modules.Genesis { return func(bs dtypes.ChainBlockstore, w *wallet.Wallet) modules.Genesis { return func() (*types.BlockHeader, error) { glog.Warn("Generating new random genesis block, note that this SHOULD NOT happen unless you are setting up new network") - minerAddr, err := w.GenerateKey(types.KTBLS) + fdata, err := ioutil.ReadFile(preseal) if err != nil { return nil, err } - gmc := &gen.GenMinerCfg{ - Owners: []address.Address{minerAddr}, - Workers: []address.Address{minerAddr}, - PeerIDs: []peer.ID{"peer ID 1"}, + var preseal map[string]genesis.GenesisMiner + if err := json.Unmarshal(fdata, &preseal); err != nil { + return nil, err } - addrs := map[address.Address]types.BigInt{ - minerAddr: types.FromFil(100000), + minerAddresses := make([]address.Address, 0, len(preseal)) + for s := range preseal { + a, err := address.NewFromString(s) + if err != nil { + return nil, err + } + if a.Protocol() != address.ID { + return nil, xerrors.New("expected ID address") + } + minerAddresses = append(minerAddresses, a) + } + + gmc := &gen.GenMinerCfg{ + PeerIDs: []peer.ID{"peer ID 1"}, + PreSeals: preseal, + MinerAddrs: minerAddresses, + } + + addrs := map[address.Address]types.BigInt{} + + for _, miner := range preseal { + if _, err := w.Import(&miner.Key); err != nil { + return nil, xerrors.Errorf("importing miner key: %w", err) + } + + _ = w.SetDefault(miner.Worker) + + addrs[miner.Worker] = types.FromFil(100000) } b, err := gen.MakeGenesisBlock(bs, addrs, gmc, uint64(time.Now().Unix())) diff --git a/node/node_test.go b/node/node_test.go index a1ee38c75..7c9de9365 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "crypto/rand" + "io/ioutil" "net/http/httptest" "testing" @@ -19,7 +20,10 @@ import ( "github.com/filecoin-project/lotus/api/test" "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/address" + "github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/cmd/lotus-seed/seed" + "github.com/filecoin-project/lotus/genesis" "github.com/filecoin-project/lotus/lib/jsonrpc" "github.com/filecoin-project/lotus/miner" "github.com/filecoin-project/lotus/node" @@ -87,7 +91,9 @@ func testStorageNode(ctx context.Context, t *testing.T, waddr address.Address, a node.Override(new(api.FullNode), tnd), ) - require.NoError(t, err) + if err != nil { + t.Fatalf("failed to construct node: %v", err) + } /*// Bootstrap with full node remoteAddrs, err := tnd.NetAddrsListen(ctx) @@ -114,10 +120,38 @@ func builder(t *testing.T, nFull int, storage []int) ([]test.TestNode, []test.Te var genbuf bytes.Buffer + if len(storage) > 1 { + panic("need more peer IDs") + } + // PRESEAL SECTION, TRY TO REPLACE WITH BETTER IN THE FUTURE + // TODO: would be great if there was a better way to fake the preseals + gmc := &gen.GenMinerCfg{ + PeerIDs: []peer.ID{minerPid}, // TODO: if we have more miners, need more peer IDs + PreSeals: map[string]genesis.GenesisMiner{}, + } + for i := 0; i < len(storage); i++ { + maddr, err := address.NewIDAddress(300 + uint64(i)) + if err != nil { + t.Fatal(err) + } + tdir, err := ioutil.TempDir("", "preseal-memgen") + if err != nil { + t.Fatal(err) + } + genm, err := seed.PreSeal(maddr, 1024, 1, tdir, []byte("make genesis mem random")) + if err != nil { + t.Fatal(err) + } + gmc.MinerAddrs = append(gmc.MinerAddrs, maddr) + gmc.PreSeals[maddr.String()] = *genm + } + + // END PRESEAL SECTION + for i := 0; i < nFull; i++ { var genesis node.Option if i == 0 { - genesis = node.Override(new(modules.Genesis), modtest.MakeGenesisMem(&genbuf, minerPid)) + genesis = node.Override(new(modules.Genesis), modtest.MakeGenesisMem(&genbuf, gmc)) } else { genesis = node.Override(new(modules.Genesis), modules.LoadGenesis(genbuf.Bytes())) } @@ -165,7 +199,7 @@ func builder(t *testing.T, nFull int, storage []int) ([]test.TestNode, []test.Te wa, err := f.WalletDefaultAddress(ctx) require.NoError(t, err) - genMiner, err := address.NewFromString("t0101") + genMiner, err := address.NewFromString("t0102") require.NoError(t, err) storers[i] = testStorageNode(ctx, t, wa, genMiner, pk, f, mn) diff --git a/node/options.go b/node/options.go index 9c8a79710..75a57cb9b 100644 --- a/node/options.go +++ b/node/options.go @@ -40,6 +40,12 @@ func ApplyIf(check func(s *Settings) bool, opts ...Option) Option { } } +func If(b bool, opts ...Option) Option { + return ApplyIf(func(s *Settings) bool { + return b + }, opts...) +} + // Override option changes constructor for a given type func Override(typ, constructor interface{}) Option { return func(s *Settings) error { diff --git a/storage/fpost_run.go b/storage/fpost_run.go new file mode 100644 index 000000000..a2cf88810 --- /dev/null +++ b/storage/fpost_run.go @@ -0,0 +1,140 @@ +package storage + +import ( + "context" + "time" + + ffi "github.com/filecoin-project/filecoin-ffi" + "go.opencensus.io/trace" + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/sectorbuilder" +) + +func (s *fpostScheduler) doPost(ctx context.Context, eps uint64, ts *types.TipSet) { + ctx, abort := context.WithCancel(ctx) + + s.abort = abort + s.activeEPS = eps + + go func() { + defer abort() + + ctx, span := trace.StartSpan(ctx, "fpostScheduler.doPost") + defer span.End() + + proof, err := s.runPost(ctx, eps, ts) + if err != nil { + log.Errorf("runPost failed: %+v", err) + return + } + + if err := s.submitPost(ctx, proof); err != nil { + log.Errorf("submitPost failed: %+v", err) + return + } + }() +} + +func (s *fpostScheduler) runPost(ctx context.Context, eps uint64, ts *types.TipSet) (*actors.SubmitFallbackPoStParams, error) { + ctx, span := trace.StartSpan(ctx, "storage.runPost") + defer span.End() + + challengeRound := int64(eps + build.FallbackPoStDelay) + + rand, err := s.api.ChainGetRandomness(ctx, ts.Key(), challengeRound) + if err != nil { + return nil, xerrors.Errorf("failed to get chain randomness for fpost (ts=%d; eps=%d): %w", ts.Height(), eps, err) + } + + ssi, err := s.sortedSectorInfo(ctx, ts) + if err != nil { + return nil, xerrors.Errorf("getting sorted sector info: %w", err) + } + + log.Infow("running fPoSt", "chain-random", rand, "eps", eps, "height", ts.Height()) + + tsStart := time.Now() + var faults []uint64 // TODO + + var seed [32]byte + copy(seed[:], rand) + + scandidates, proof, err := s.sb.GenerateFallbackPoSt(ssi, seed, faults) + if err != nil { + return nil, xerrors.Errorf("running post failed: %w", err) + } + + elapsed := time.Since(tsStart) + log.Infow("submitting PoSt", "pLen", len(proof), "elapsed", elapsed) + + candidates := make([]types.EPostTicket, len(scandidates)) + for i, sc := range scandidates { + candidates[i] = types.EPostTicket{ + Partial: sc.PartialTicket[:], + SectorID: sc.SectorID, + ChallengeIndex: sc.SectorChallengeIndex, + } + } + + return &actors.SubmitFallbackPoStParams{ + Proof: proof, + Candidates: candidates, + }, nil +} + +func (s *fpostScheduler) sortedSectorInfo(ctx context.Context, ts *types.TipSet) (sectorbuilder.SortedPublicSectorInfo, error) { + sset, err := s.api.StateMinerProvingSet(ctx, s.actor, ts) + if err != nil { + return sectorbuilder.SortedPublicSectorInfo{}, xerrors.Errorf("failed to get proving set for miner (tsH: %d): %w", ts.Height(), err) + } + if len(sset) == 0 { + log.Warn("empty proving set! (ts.H: %d)", ts.Height()) + } + + sbsi := make([]ffi.PublicSectorInfo, len(sset)) + for k, sector := range sset { + var commR [sectorbuilder.CommLen]byte + copy(commR[:], sector.CommR) + + sbsi[k] = ffi.PublicSectorInfo{ + SectorID: sector.SectorID, + CommR: commR, + } + } + + return sectorbuilder.NewSortedPublicSectorInfo(sbsi), nil +} + +func (s *fpostScheduler) submitPost(ctx context.Context, proof *actors.SubmitFallbackPoStParams) error { + ctx, span := trace.StartSpan(ctx, "storage.commitPost") + defer span.End() + + enc, aerr := actors.SerializeParams(proof) + if aerr != nil { + return xerrors.Errorf("could not serialize submit post parameters: %w", aerr) + } + + msg := &types.Message{ + To: s.actor, + From: s.worker, + Method: actors.MAMethods.SubmitFallbackPoSt, + Params: enc, + Value: types.NewInt(1000), // currently hard-coded late fee in actor, returned if not late + GasLimit: types.NewInt(10000000), // i dont know help + GasPrice: types.NewInt(1), + } + + // TODO: consider maybe caring about the output + sm, err := s.api.MpoolPushMessage(ctx, msg) + if err != nil { + return xerrors.Errorf("pushing message to mpool: %w", err) + } + + log.Infof("Submitted fallback post: %s", sm.Cid()) + + return nil +} diff --git a/storage/fpost_sched.go b/storage/fpost_sched.go new file mode 100644 index 000000000..9480dc3c2 --- /dev/null +++ b/storage/fpost_sched.go @@ -0,0 +1,141 @@ +package storage + +import ( + "context" + + "go.opencensus.io/trace" + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/address" + "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/sectorbuilder" +) + +const Inactive = 0 + +const StartConfidence = 4 // TODO: config + +type fpostScheduler struct { + api storageMinerApi + sb *sectorbuilder.SectorBuilder + + actor address.Address + worker address.Address + + cur *types.TipSet + + // if a post is in progress, this indicates for which ElectionPeriodStart + activeEPS uint64 + abort context.CancelFunc +} + +func (s *fpostScheduler) run(ctx context.Context) { + notifs, err := s.api.ChainNotify(ctx) + if err != nil { + return + } + + current := <-notifs + if len(current) != 1 { + panic("expected first notif to have len = 1") + } + if current[0].Type != store.HCCurrent { + panic("expected first notif to tell current ts") + } + + if err := s.update(ctx, current[0].Val); err != nil { + panic(err) + } + + // not fine to panic after this point + for { + select { + case changes := <-notifs: + ctx, span := trace.StartSpan(ctx, "fpostScheduler.headChange") + + var lowest, highest *types.TipSet = s.cur, nil + + for _, change := range changes { + switch change.Type { + case store.HCRevert: + lowest = change.Val + case store.HCApply: + highest = change.Val + } + } + + if err := s.revert(ctx, lowest); err != nil { + log.Error("handling head reverts in fallbackPost sched: %+v", err) + } + if err := s.update(ctx, highest); err != nil { + log.Error("handling head updates in fallbackPost sched: %+v", err) + } + + span.End() + } + } +} + +func (s *fpostScheduler) revert(ctx context.Context, newLowest *types.TipSet) error { + if s.cur == newLowest { + return nil + } + s.cur = newLowest + + newEPS, _, err := s.shouldFallbackPost(ctx, newLowest) + if err != nil { + return err + } + + if newEPS != s.activeEPS { + s.abortActivePoSt() + } + + return nil +} + +func (s *fpostScheduler) update(ctx context.Context, new *types.TipSet) error { + newEPS, start, err := s.shouldFallbackPost(ctx, new) + if err != nil { + return err + } + + if newEPS != s.activeEPS { + s.abortActivePoSt() + } + + if newEPS != Inactive && start { + s.doPost(ctx, newEPS, new) + } + + return nil +} + +func (s *fpostScheduler) abortActivePoSt() { + if s.activeEPS == Inactive { + return // noop + } + + if s.abort != nil { + s.abort() + } + + log.Warnf("Aborting Fallback PoSt (EPS: %d)", s.activeEPS) + + s.activeEPS = 0 + s.abort = nil +} + +func (s *fpostScheduler) shouldFallbackPost(ctx context.Context, ts *types.TipSet) (uint64, bool, error) { + eps, err := s.api.StateMinerElectionPeriodStart(ctx, s.actor, ts) + if err != nil { + return 0, false, xerrors.Errorf("getting ElectionPeriodStart: %w", err) + } + + if ts.Height() >= eps+build.FallbackPoStDelay { + return eps, ts.Height() >= eps+build.FallbackPoStDelay+StartConfidence, nil + } + return 0, false, nil +} diff --git a/storage/miner.go b/storage/miner.go index 8c2ebb546..f295504d8 100644 --- a/storage/miner.go +++ b/storage/miner.go @@ -2,6 +2,7 @@ package storage import ( "context" + "errors" "sync" "github.com/ipfs/go-cid" @@ -9,11 +10,12 @@ import ( "github.com/ipfs/go-datastore/namespace" logging "github.com/ipfs/go-log" "github.com/libp2p/go-libp2p-core/host" - "github.com/pkg/errors" + "golang.org/x/xerrors" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/address" "github.com/filecoin-project/lotus/chain/events" + "github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/lib/sectorbuilder" @@ -22,7 +24,7 @@ import ( var log = logging.Logger("storageminer") -const PoStConfidence = 3 +const SectorStorePrefix = "/sectors" type Miner struct { api storageMinerApi @@ -51,7 +53,7 @@ type storageMinerApi interface { // Call a read only method on actors (no interaction with the chain required) StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) StateMinerWorker(context.Context, address.Address, *types.TipSet) (address.Address, error) - StateMinerProvingPeriodEnd(context.Context, address.Address, *types.TipSet) (uint64, error) + StateMinerElectionPeriodStart(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) StateMinerSectors(context.Context, address.Address, *types.TipSet) ([]*api.ChainSectorInfo, error) StateMinerProvingSet(context.Context, address.Address, *types.TipSet) ([]*api.ChainSectorInfo, error) StateMinerSectorSize(context.Context, address.Address, *types.TipSet) (uint64, error) @@ -63,7 +65,7 @@ type storageMinerApi interface { ChainHead(context.Context) (*types.TipSet, error) ChainNotify(context.Context) (<-chan []*store.HeadChange, error) - ChainGetRandomness(context.Context, types.TipSetKey, []*types.Ticket, int) ([]byte, error) + ChainGetRandomness(context.Context, types.TipSetKey, int64) ([]byte, error) ChainGetTipSetByHeight(context.Context, uint64, *types.TipSet) (*types.TipSet, error) ChainGetBlockMessages(context.Context, cid.Cid) (*api.BlockMessages, error) @@ -81,7 +83,7 @@ func NewMiner(api storageMinerApi, addr address.Address, h host.Host, ds datasto sb: sb, tktFn: tktFn, - sectors: statestore.New(namespace.Wrap(ds, datastore.NewKey("/sectors"))), + sectors: statestore.New(namespace.Wrap(ds, datastore.NewKey(SectorStorePrefix))), sectorIncoming: make(chan *SectorInfo), sectorUpdated: make(chan sectorUpdate), @@ -92,12 +94,19 @@ func NewMiner(api storageMinerApi, addr address.Address, h host.Host, ds datasto func (m *Miner) Run(ctx context.Context) error { if err := m.runPreflightChecks(ctx); err != nil { - return errors.Wrap(err, "miner preflight checks failed") + return xerrors.Errorf("miner preflight checks failed: %w", err) } m.events = events.NewEvents(ctx, m.api) - go m.beginPosting(ctx) + fps := &fpostScheduler{ + api: m.api, + sb: m.sb, + actor: m.maddr, + worker: m.worker, + } + + go fps.run(ctx) go m.sectorStateLoop(ctx) return nil } @@ -122,7 +131,7 @@ func (m *Miner) runPreflightChecks(ctx context.Context) error { has, err := m.api.WalletHas(ctx, worker) if err != nil { - return errors.Wrap(err, "failed to check wallet for worker key") + return xerrors.Errorf("failed to check wallet for worker key: %w", err) } if !has { @@ -132,3 +141,25 @@ func (m *Miner) runPreflightChecks(ctx context.Context) error { log.Infof("starting up miner %s, worker addr %s", m.maddr, m.worker) return nil } + +type sectorBuilderEpp struct { + sb *sectorbuilder.SectorBuilder +} + +func NewElectionPoStProver(sb *sectorbuilder.SectorBuilder) *sectorBuilderEpp { + return §orBuilderEpp{sb} +} + +var _ gen.ElectionPoStProver = (*sectorBuilderEpp)(nil) + +func (epp *sectorBuilderEpp) GenerateCandidates(ctx context.Context, ssi sectorbuilder.SortedPublicSectorInfo, rand []byte) ([]sectorbuilder.EPostCandidate, error) { + var faults []uint64 // TODO + + var randbuf [32]byte + copy(randbuf[:], rand) + return epp.sb.GenerateEPostCandidates(ssi, randbuf, faults) +} + +func (epp *sectorBuilderEpp) ComputeProof(ctx context.Context, ssi sectorbuilder.SortedPublicSectorInfo, rand []byte, winners []sectorbuilder.EPostCandidate) ([]byte, error) { + return epp.sb.ComputeElectionPoSt(ssi, rand, winners) +} diff --git a/storage/post.go b/storage/post.go deleted file mode 100644 index 14a61d7c7..000000000 --- a/storage/post.go +++ /dev/null @@ -1,302 +0,0 @@ -package storage - -import ( - "context" - "time" - - "github.com/ipfs/go-cid" - "go.opencensus.io/trace" - "golang.org/x/xerrors" - - "github.com/filecoin-project/lotus/api" - "github.com/filecoin-project/lotus/build" - "github.com/filecoin-project/lotus/chain/actors" - "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/lib/sectorbuilder" -) - -const postMsgTimeout = 20 - -func (m *Miner) beginPosting(ctx context.Context) { - ts, err := m.api.ChainHead(context.TODO()) - if err != nil { - log.Error(err) - return - } - - ppe, err := m.api.StateMinerProvingPeriodEnd(ctx, m.maddr, ts) - if err != nil { - log.Errorf("failed to get proving period end for miner: %s", err) - return - } - - if ppe == 0 { - log.Errorf("Proving period end == 0") - return - } - - m.postLk.Lock() - if m.schedPost > 0 { - log.Warnf("PoSts already running %d", m.schedPost) - m.postLk.Unlock() - return - } - - // height needs to be +1, because otherwise we'd be trying to schedule PoSt - // at current block height - ppe, _ = actors.ProvingPeriodEnd(ppe, ts.Height()+1) - m.schedPost = ppe - - m.postLk.Unlock() - - log.Infof("Scheduling post at height %d", ppe-build.PoStChallangeTime) - err = m.events.ChainAt(m.computePost(m.schedPost), func(ctx context.Context, ts *types.TipSet) error { // Revert - // TODO: Cancel post - log.Errorf("TODO: Cancel PoSt, re-run") - return nil - }, PoStConfidence, ppe-build.PoStChallangeTime) - if err != nil { - // TODO: This is BAD, figure something out - log.Errorf("scheduling PoSt failed: %s", err) - return - } -} - -func (m *Miner) scheduleNextPost(ppe uint64) { - ts, err := m.api.ChainHead(context.TODO()) - if err != nil { - log.Error(err) - // TODO: retry - return - } - - headPPE, provingPeriod := actors.ProvingPeriodEnd(ppe, ts.Height()) - if headPPE > ppe { - log.Warnw("PoSt computation running behind chain", "headPPE", headPPE, "ppe", ppe) - ppe = headPPE - } - - m.postLk.Lock() - if m.schedPost >= ppe { - // this probably can't happen - log.Errorw("PoSt already scheduled", "schedPost", m.schedPost, "ppe", ppe) - m.postLk.Unlock() - return - } - - m.schedPost = ppe - m.postLk.Unlock() - - log.Infow("scheduling PoSt", "post-height", ppe-build.PoStChallangeTime, - "height", ts.Height(), "ppe", ppe, "proving-period", provingPeriod) - err = m.events.ChainAt(m.computePost(ppe), func(ctx context.Context, ts *types.TipSet) error { // Revert - // TODO: Cancel post - log.Errorf("TODO: Cancel PoSt, re-run") - return nil - }, PoStConfidence, ppe-build.PoStChallangeTime) - if err != nil { - // TODO: This is BAD, figure something out - log.Errorf("scheduling PoSt failed: %+v", err) - return - } -} - -type post struct { - m *Miner - - ppe uint64 - ts *types.TipSet - - // prep - sset []*api.ChainSectorInfo - r []byte - - // run - proof []byte - - // commit - msg *types.Message - smsg cid.Cid -} - -func (p *post) doPost(ctx context.Context) error { - ctx, span := trace.StartSpan(ctx, "storage.computePost") - defer span.End() - - if err := p.preparePost(ctx); err != nil { - return err - } - - if err := p.runPost(ctx); err != nil { - return err - } - - if err := p.commitPost(ctx); err != nil { - return err - } - - if err := p.waitCommit(ctx); err != nil { - return err - } - - return nil -} - -func (p *post) preparePost(ctx context.Context) error { - ctx, span := trace.StartSpan(ctx, "storage.preparePost") - defer span.End() - log.Info("preparePost") - - sset, err := p.m.api.StateMinerProvingSet(ctx, p.m.maddr, p.ts) - if err != nil { - return xerrors.Errorf("failed to get proving set for miner: %w", err) - } - if len(sset) == 0 { - log.Warn("empty proving set! (ts.H: %d)", p.ts.Height()) - } - - p.sset = sset - - // Compute how many blocks back we have to look from the given tipset for the PoSt challenge - challengeLookback := int((int64(p.ts.Height()) - int64(p.ppe)) + int64(build.PoStChallangeTime) + int64(build.PoStRandomnessLookback)) - r, err := p.m.api.ChainGetRandomness(ctx, p.ts.Key(), nil, challengeLookback) - if err != nil { - return xerrors.Errorf("failed to get chain randomness for post (ts=%d; ppe=%d): %w", p.ts.Height(), p.ppe, err) - } - p.r = r - - return nil -} - -func (p *post) sortedSectorInfo() sectorbuilder.SortedSectorInfo { - sbsi := make([]sectorbuilder.SectorInfo, len(p.sset)) - for k, sector := range p.sset { - var commR [sectorbuilder.CommLen]byte - copy(commR[:], sector.CommR) - - sbsi[k] = sectorbuilder.SectorInfo{ - SectorID: sector.SectorID, - CommR: commR, - } - } - - return sectorbuilder.NewSortedSectorInfo(sbsi) -} - -func (p *post) runPost(ctx context.Context) error { - ctx, span := trace.StartSpan(ctx, "storage.runPost") - defer span.End() - - log.Infow("running PoSt", "delayed-by", - int64(p.ts.Height())-(int64(p.ppe)-int64(build.PoStChallangeTime)), - "chain-random", p.r, "ppe", p.ppe, "height", p.ts.Height(), "sectors", len(p.sset)) - - tsStart := time.Now() - var faults []uint64 // TODO - - var seed [32]byte - copy(seed[:], p.r) - - proof, err := p.m.sb.GeneratePoSt(p.sortedSectorInfo(), seed, faults) - if err != nil { - return xerrors.Errorf("running post failed: %w", err) - } - elapsed := time.Since(tsStart) - - p.proof = proof - log.Infow("submitting PoSt", "pLen", len(proof), "elapsed", elapsed) - - return nil -} - -func (p *post) commitPost(ctx context.Context) error { - ctx, span := trace.StartSpan(ctx, "storage.commitPost") - defer span.End() - - params := &actors.SubmitPoStParams{ - Proof: p.proof, - DoneSet: types.BitFieldFromSet(nil), - } - - enc, aerr := actors.SerializeParams(params) - if aerr != nil { - return xerrors.Errorf("could not serialize submit post parameters: %w", aerr) - } - - p.msg = &types.Message{ - To: p.m.maddr, - From: p.m.worker, - Method: actors.MAMethods.SubmitPoSt, - Params: enc, - Value: types.NewInt(1000), // currently hard-coded late fee in actor, returned if not late - GasLimit: types.NewInt(1000000 /* i dont know help */), - GasPrice: types.NewInt(1), - } - - log.Info("mpush") - - smsg, err := p.m.api.MpoolPushMessage(ctx, p.msg) - if err != nil { - return xerrors.Errorf("pushing message to mpool: %w", err) - } - p.smsg = smsg.Cid() - - return nil -} - -func (p *post) waitCommit(ctx context.Context) error { - ctx, span := trace.StartSpan(ctx, "storage.waitPost") - defer span.End() - - log.Infof("Waiting for post %s to appear on chain", p.smsg) - - err := p.m.events.CalledMsg(ctx, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH uint64) (more bool, err error) { - if rec.ExitCode != 0 { - log.Warnf("SubmitPoSt EXIT: %d", rec.ExitCode) - } - - log.Infof("Post made it on chain! (height=%d)", ts.Height()) - - return false, nil - }, func(ctx context.Context, ts *types.TipSet) error { - log.Warn("post message reverted") - return nil - }, 3, postMsgTimeout, p.msg) - if err != nil { - return err - } - - return nil -} - -func (m *Miner) computePost(ppe uint64) func(ctx context.Context, ts *types.TipSet, curH uint64) error { - called := 0 - return func(ctx context.Context, ts *types.TipSet, curH uint64) error { - called++ - if called > 1 { - log.Errorw("BUG: computePost callback called again", "ppe", ppe, - "height", ts.Height(), "curH", curH, "called", called-1) - return nil - } - - err := (&post{ - m: m, - ppe: ppe, - ts: ts, - }).doPost(ctx) - if err != nil { - return xerrors.Errorf("doPost: %w", err) - } - - m.scheduleNextPost(ppe + build.ProvingPeriodDuration) - return nil - } -} - -func sectorIdList(si []*api.ChainSectorInfo) []uint64 { - out := make([]uint64, len(si)) - for i, s := range si { - out[i] = s.SectorID - } - return out -} diff --git a/storage/sealing.go b/storage/sealing.go index 3e3c576f5..7b3356fc7 100644 --- a/storage/sealing.go +++ b/storage/sealing.go @@ -125,13 +125,13 @@ func (t *SectorInfo) rspco() sectorbuilder.RawSealPreCommitOutput { } func (m *Miner) sectorStateLoop(ctx context.Context) error { - toRestart, err := m.ListSectors() + trackedSectors, err := m.ListSectors() if err != nil { return err } go func() { - for _, si := range toRestart { + for _, si := range trackedSectors { select { case m.sectorUpdated <- sectorUpdate{ newState: si.State, @@ -146,6 +146,32 @@ func (m *Miner) sectorStateLoop(ctx context.Context) error { } }() + { + // verify on-chain state + trackedByID := map[uint64]*SectorInfo{} + for _, si := range trackedSectors { + trackedByID[si.SectorID] = &si + } + + curTs, err := m.api.ChainHead(ctx) + if err != nil { + return xerrors.Errorf("getting chain head: %w", err) + } + + ps, err := m.api.StateMinerProvingSet(ctx, m.maddr, curTs) + if err != nil { + return err + } + for _, ocs := range ps { + if _, ok := trackedByID[ocs.SectorID]; ok { + continue // TODO: check state + } + + // TODO: attempt recovery + log.Warnf("untracked sector %d found on chain", ocs.SectorID) + } + } + go func() { defer log.Warn("quitting deal provider loop") defer close(m.stopped) @@ -217,15 +243,15 @@ func (m *Miner) onSectorUpdated(ctx context.Context, update sectorUpdate) { switch update.newState { case api.Packing: - m.handle(ctx, sector, m.finishPacking, api.Unsealed) + m.handleSectorUpdate(ctx, sector, m.finishPacking, api.Unsealed) case api.Unsealed: - m.handle(ctx, sector, m.sealPreCommit, api.PreCommitting) + m.handleSectorUpdate(ctx, sector, m.sealPreCommit, api.PreCommitting) case api.PreCommitting: - m.handle(ctx, sector, m.preCommit, api.PreCommitted) + m.handleSectorUpdate(ctx, sector, m.preCommit, api.PreCommitted) case api.PreCommitted: - m.handle(ctx, sector, m.preCommitted, api.SectorNoUpdate) + m.handleSectorUpdate(ctx, sector, m.preCommitted, api.SectorNoUpdate) case api.Committing: - m.handle(ctx, sector, m.committing, api.Proving) + m.handleSectorUpdate(ctx, sector, m.committing, api.Proving) case api.Proving: // TODO: track sector health / expiration log.Infof("Proving sector %d", update.id) diff --git a/storage/sector_states.go b/storage/sector_states.go index 2e69b7ce7..d57f6275d 100644 --- a/storage/sector_states.go +++ b/storage/sector_states.go @@ -3,7 +3,6 @@ package storage import ( "context" - "github.com/pkg/errors" "golang.org/x/xerrors" "github.com/filecoin-project/lotus/api" @@ -15,7 +14,7 @@ import ( type providerHandlerFunc func(ctx context.Context, deal SectorInfo) (func(*SectorInfo), error) -func (m *Miner) handle(ctx context.Context, sector SectorInfo, cb providerHandlerFunc, next api.SectorState) { +func (m *Miner) handleSectorUpdate(ctx context.Context, sector SectorInfo, cb providerHandlerFunc, next api.SectorState) { go func() { mut, err := cb(ctx, sector) @@ -145,7 +144,7 @@ func (m *Miner) preCommitted(ctx context.Context, sector SectorInfo) (func(*Sect log.Infof("precommit for sector %d made it on chain, will start proof computation at height %d", sector.SectorID, randHeight) err = m.events.ChainAt(func(ctx context.Context, ts *types.TipSet, curH uint64) error { - rand, err := m.api.ChainGetRandomness(ctx, ts.Key(), nil, int(ts.Height()-randHeight)) + rand, err := m.api.ChainGetRandomness(ctx, ts.Key(), int64(randHeight)) if err != nil { return xerrors.Errorf("failed to get randomness for computing seal proof: %w", err) } @@ -176,7 +175,7 @@ func (m *Miner) preCommitted(ctx context.Context, sector SectorInfo) (func(*Sect func (m *Miner) committing(ctx context.Context, sector SectorInfo) (func(*SectorInfo), error) { log.Info("scheduling seal proof computation...") - proof, err := m.sb.SealCommit(sector.SectorID, sector.Ticket.SB(), sector.Seed.SB(), sector.pieceInfos(), sector.refs(), sector.rspco()) + proof, err := m.sb.SealCommit(sector.SectorID, sector.Ticket.SB(), sector.Seed.SB(), sector.pieceInfos(), sector.rspco()) if err != nil { return nil, xerrors.Errorf("computing seal proof failed: %w", err) } @@ -206,7 +205,7 @@ func (m *Miner) committing(ctx context.Context, sector SectorInfo) (func(*Sector smsg, err := m.api.MpoolPushMessage(ctx, msg) if err != nil { - log.Error(errors.Wrap(err, "pushing message to mpool")) + log.Error(xerrors.Errorf("pushing message to mpool: %w", err)) } // TODO: Separate state before this wait, so we persist message cid? @@ -217,12 +216,10 @@ func (m *Miner) committing(ctx context.Context, sector SectorInfo) (func(*Sector } if mw.Receipt.ExitCode != 0 { - log.Errorf("UNHANDLED: submitting sector proof failed (t:%x; s:%x(%d); p:%x)", sector.Ticket.TicketBytes, sector.Seed.TicketBytes, sector.Seed.BlockHeight, params.Proof) + log.Errorf("UNHANDLED: submitting sector proof failed (exit=%d, msg=%s) (t:%x; s:%x(%d); p:%x)", mw.Receipt.ExitCode, smsg.Cid(), sector.Ticket.TicketBytes, sector.Seed.TicketBytes, sector.Seed.BlockHeight, params.Proof) return nil, xerrors.New("UNHANDLED: submitting sector proof failed") } - m.beginPosting(ctx) - return func(info *SectorInfo) { mcid := smsg.Cid() info.CommitMessage = &mcid diff --git a/storage/sectorblocks/blockstore.go b/storage/sectorblocks/blockstore.go index e28e750d0..afa0fcbdf 100644 --- a/storage/sectorblocks/blockstore.go +++ b/storage/sectorblocks/blockstore.go @@ -2,7 +2,8 @@ package sectorblocks import ( "context" - "github.com/ipfs/go-block-format" + + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" blockstore "github.com/ipfs/go-ipfs-blockstore" ) diff --git a/tools/stats/rpc.go b/tools/stats/rpc.go index c2131b480..af0f5690c 100644 --- a/tools/stats/rpc.go +++ b/tools/stats/rpc.go @@ -134,12 +134,8 @@ func loadTipsets(ctx context.Context, api api.FullNode, curr *types.TipSet, lowe log.Printf("Walking back { height:%d }", curr.Height()) tipsets = append(tipsets, curr) - ph := ParentTipsetHeight(curr) - if ph == 0 { - break - } - - prev, err := api.ChainGetTipSetByHeight(ctx, ph, curr) + tsk := types.NewTipSetKey(curr.Parents()...) + prev, err := api.ChainGetTipSet(ctx, tsk) if err != nil { return tipsets, err } @@ -154,11 +150,6 @@ func loadTipsets(ctx context.Context, api api.FullNode, curr *types.TipSet, lowe return tipsets, nil } -func ParentTipsetHeight(tipset *types.TipSet) uint64 { - mtb := tipset.MinTicketBlock() - return tipset.Height() - uint64(len(mtb.Tickets)) - 1 -} - func GetFullNodeAPI(repo string) (api.FullNode, jsonrpc.ClientCloser, error) { addr, headers, err := getAPI(repo) if err != nil {