From a2bcc1fec29f3e81bae84b6110ea3052921d2e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 13 Jan 2020 21:47:27 +0100 Subject: [PATCH] Mostly functional mock sectorbuilder --- api/test/deals.go | 2 +- chain/actors/actor_miner.go | 14 +--- chain/actors/actor_miner_test.go | 3 +- chain/actors/actor_storagemarket.go | 2 +- chain/actors/actors_test.go | 4 +- chain/actors/harness2_test.go | 5 +- chain/gen/gen.go | 7 +- chain/gen/utils.go | 6 +- chain/stmgr/call.go | 2 +- chain/stmgr/stmgr.go | 2 +- chain/store/store.go | 10 ++- chain/store/store_test.go | 2 +- chain/store/weight.go | 2 +- chain/sync.go | 2 +- chain/types/vmcontext.go | 13 +++- chain/validation/applier.go | 3 +- chain/vm/syscalls.go | 20 ++++- chain/vm/vm.go | 6 +- cmd/lotus-bench/main.go | 6 +- go.mod | 4 + node/builder.go | 6 +- node/impl/storminer.go | 2 +- node/modules/chain.go | 4 +- node/modules/storageminer.go | 2 +- node/modules/testing/genesis.go | 12 +-- node/node_test.go | 108 +++++++++++++++++++++++++- storage/fpost_sched.go | 4 +- storage/garbage.go | 2 +- storage/miner.go | 28 ++----- storage/sbmock/preseal.go | 52 +++++++++++++ storage/sbmock/sbmock.go | 116 ++++++++++++++++++++++++---- storage/sbmock/util.go | 28 +++++++ storage/sectorblocks/blocks.go | 6 +- 33 files changed, 390 insertions(+), 95 deletions(-) create mode 100644 storage/sbmock/preseal.go create mode 100644 storage/sbmock/util.go diff --git a/api/test/deals.go b/api/test/deals.go index 76a739605..30faee4eb 100644 --- a/api/test/deals.go +++ b/api/test/deals.go @@ -64,7 +64,7 @@ func TestDealFlow(t *testing.T, b APIBuilder) { go func() { defer close(done) for mine { - time.Sleep(time.Second) + time.Sleep(100 * time.Millisecond) fmt.Println("mining a block now") if err := sn[0].MineOne(ctx); err != nil { t.Error(err) diff --git a/chain/actors/actor_miner.go b/chain/actors/actor_miner.go index c6bb7ae6d..62bfd3ab0 100644 --- a/chain/actors/actor_miner.go +++ b/chain/actors/actor_miner.go @@ -19,7 +19,6 @@ import ( cbor "github.com/ipfs/go-ipld-cbor" "github.com/libp2p/go-libp2p-core/peer" cbg "github.com/whyrusleeping/cbor-gen" - "go.opencensus.io/trace" "golang.org/x/xerrors" ) @@ -526,7 +525,7 @@ func (sma StorageMinerActor) SubmitFallbackPoSt(act *types.Actor, vmctx types.VM }) } - if ok, lerr := sectorbuilder.VerifyFallbackPost(vmctx.Context(), mi.SectorSize, + if ok, lerr := vmctx.Sys().VerifyFallbackPost(vmctx.Context(), mi.SectorSize, sectorbuilder.NewSortedPublicSectorInfo(sectorInfos), seed[:], params.Proof, candidates, proverID, activeFaults); !ok || lerr != nil { if lerr != nil { // TODO: study PoST errors @@ -641,17 +640,6 @@ func RemoveFromSectorSet(ctx context.Context, s types.Storage, ss cid.Cid, ids [ return ncid, nil } -func ValidatePoRep(ctx context.Context, maddr address.Address, ssize uint64, commD, commR, ticket, proof, seed []byte, sectorID uint64) (bool, ActorError) { - _, span := trace.StartSpan(ctx, "ValidatePoRep") - defer span.End() - ok, err := sectorbuilder.VerifySeal(ssize, commR, commD, maddr, ticket, seed, sectorID, proof) - if err != nil { - return false, aerrors.Absorb(err, 25, "verify seal failed") - } - - return ok, nil -} - func CollateralForPower(power types.BigInt) types.BigInt { return types.BigMul(power, types.NewInt(10)) /* TODO: this diff --git a/chain/actors/actor_miner_test.go b/chain/actors/actor_miner_test.go index f76509373..410dfcdc3 100644 --- a/chain/actors/actor_miner_test.go +++ b/chain/actors/actor_miner_test.go @@ -254,7 +254,7 @@ func getMinerSectorSet(ctx context.Context, st types.StateTree, bs blockstore.Bl func (h *Harness) makeFakeDeal(t *testing.T, miner, worker, client address.Address, size uint64) *actors.StorageDealProposal { data := make([]byte, size) rand.Read(data) - commP, err := sectorbuilder.GeneratePieceCommitment(bytes.NewReader(data), size) + commP, err := (§orbuilder.SectorBuilder{}).GeneratePieceCommitment(bytes.NewReader(data), size) if err != nil { t.Fatal(err) } @@ -262,7 +262,6 @@ func (h *Harness) makeFakeDeal(t *testing.T, miner, worker, client address.Addre prop := actors.StorageDealProposal{ PieceRef: commP[:], PieceSize: size, - //PieceSerialization SerializationMode // Needs to be here as it tells how data in the sector maps to PieceRef cid Client: client, Provider: miner, diff --git a/chain/actors/actor_storagemarket.go b/chain/actors/actor_storagemarket.go index a3fb269bb..022baa1b8 100644 --- a/chain/actors/actor_storagemarket.go +++ b/chain/actors/actor_storagemarket.go @@ -649,7 +649,7 @@ func (sma StorageMarketActor) ComputeDataCommitment(act *types.Actor, vmctx type }) } - commd, err := sectorbuilder.GenerateDataCommitment(params.SectorSize, pieces) + commd, err := vmctx.Sys().GenerateDataCommitment(params.SectorSize, pieces) if err != nil { return nil, aerrors.Absorb(err, 6, "failed to generate data commitment from pieces") } diff --git a/chain/actors/actors_test.go b/chain/actors/actors_test.go index 64207b422..d0f8fba86 100644 --- a/chain/actors/actors_test.go +++ b/chain/actors/actors_test.go @@ -50,10 +50,10 @@ func setupVMTestEnv(t *testing.T) (*vm.VM, []address.Address, bstore.Blockstore) t.Fatal(err) } - cs := store.NewChainStore(bs, nil) + cs := store.NewChainStore(bs, nil, nil) // TODO: should probabaly mock out the randomness bit, nil works for now - vm, err := vm.NewVM(stateroot, 1, nil, maddr, cs.Blockstore()) + vm, err := vm.NewVM(stateroot, 1, nil, maddr, cs.Blockstore(), cs.VMSys()) if err != nil { t.Fatal(err) } diff --git a/chain/actors/harness2_test.go b/chain/actors/harness2_test.go index 2739d0cc8..213d1d48e 100644 --- a/chain/actors/harness2_test.go +++ b/chain/actors/harness2_test.go @@ -3,6 +3,7 @@ package actors_test import ( "bytes" "context" + "github.com/filecoin-project/go-sectorbuilder" "math/rand" "testing" @@ -194,8 +195,8 @@ func NewHarness(t *testing.T, options ...HarnessOpt) *Harness { t.Fatal(err) } - h.cs = store.NewChainStore(h.bs, nil) - h.vm, err = vm.NewVM(stateroot, 1, h.Rand, h.HI.Miner, h.cs.Blockstore()) + h.cs = store.NewChainStore(h.bs, nil, vm.Syscalls(sectorbuilder.ProofVerifier)) + h.vm, err = vm.NewVM(stateroot, 1, h.Rand, h.HI.Miner, h.cs.Blockstore(), h.cs.VMSys()) if err != nil { t.Fatal(err) } diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 0b396ad06..bf0c7a2b9 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -6,6 +6,7 @@ import ( "crypto/sha256" "encoding/binary" "fmt" + "github.com/filecoin-project/lotus/chain/vm" "io/ioutil" "sync/atomic" @@ -170,7 +171,9 @@ func NewGenerator() (*ChainGen, error) { MinerAddrs: []address.Address{maddr1, maddr2}, } - genb, err := MakeGenesisBlock(bs, map[address.Address]types.BigInt{ + sys := vm.Syscalls(sectorbuilder.ProofVerifier) + + genb, err := MakeGenesisBlock(bs, sys, map[address.Address]types.BigInt{ mk1: types.FromFil(40000), mk2: types.FromFil(40000), banker: types.FromFil(50000), @@ -179,7 +182,7 @@ func NewGenerator() (*ChainGen, error) { return nil, xerrors.Errorf("make genesis block failed: %w", err) } - cs := store.NewChainStore(bs, ds) + cs := store.NewChainStore(bs, ds, sys) genfb := &types.FullBlock{Header: genb.Genesis} gents := store.NewFullTipSet([]*types.FullBlock{genfb}) diff --git a/chain/gen/utils.go b/chain/gen/utils.go index 3f528075e..8b53e7c7a 100644 --- a/chain/gen/utils.go +++ b/chain/gen/utils.go @@ -275,7 +275,7 @@ func mustEnc(i cbg.CBORMarshaler) []byte { } func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid, gmcfg *GenMinerCfg) (cid.Cid, []actors.StorageDealProposal, error) { - vm, err := vm.NewVM(sroot, 0, nil, actors.NetworkAddress, cs.Blockstore()) + vm, err := vm.NewVM(sroot, 0, nil, actors.NetworkAddress, cs.Blockstore(), cs.VMSys()) if err != nil { return cid.Undef, nil, xerrors.Errorf("failed to create NewVM: %w", err) } @@ -555,7 +555,7 @@ func doExecValue(ctx context.Context, vm *vm.VM, to, from address.Address, value return ret.Return, nil } -func MakeGenesisBlock(bs bstore.Blockstore, balances map[address.Address]types.BigInt, gmcfg *GenMinerCfg, ts uint64) (*GenesisBootstrap, error) { +func MakeGenesisBlock(bs bstore.Blockstore, sys *types.VMSyscalls, balances map[address.Address]types.BigInt, gmcfg *GenMinerCfg, ts uint64) (*GenesisBootstrap, error) { ctx := context.Background() state, err := MakeInitialStateTree(bs, balances) @@ -569,7 +569,7 @@ func MakeGenesisBlock(bs bstore.Blockstore, balances map[address.Address]types.B } // temp chainstore - cs := store.NewChainStore(bs, datastore.NewMapDatastore()) + cs := store.NewChainStore(bs, datastore.NewMapDatastore(), sys) stateroot, deals, err := SetupStorageMiners(ctx, cs, stateroot, gmcfg) if err != nil { return nil, xerrors.Errorf("setup storage miners failed: %w", err) diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index cfbe729c0..bc4a2f9fc 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -18,7 +18,7 @@ func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate ctx, span := trace.StartSpan(ctx, "statemanager.CallRaw") defer span.End() - vmi, err := vm.NewVM(bstate, bheight, r, actors.NetworkAddress, sm.cs.Blockstore()) + vmi, err := vm.NewVM(bstate, bheight, r, actors.NetworkAddress, sm.cs.Blockstore(), sm.cs.VMSys()) if err != nil { return nil, xerrors.Errorf("failed to set up vm: %w", err) } diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 34204fb3e..310f0a9e9 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -139,7 +139,7 @@ func (sm *StateManager) computeTipSetState(ctx context.Context, blks []*types.Bl r := store.NewChainRand(sm.cs, cids, blks[0].Height) - vmi, err := vm.NewVM(pstate, blks[0].Height, r, address.Undef, sm.cs.Blockstore()) + vmi, err := vm.NewVM(pstate, blks[0].Height, r, address.Undef, sm.cs.Blockstore(), sm.cs.VMSys()) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("instantiating VM failed: %w", err) } diff --git a/chain/store/store.go b/chain/store/store.go index 07d1c692c..c75f1ffa0 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -51,9 +51,11 @@ type ChainStore struct { mmCache *lru.ARCCache tsCache *lru.ARCCache + + vmcalls *types.VMSyscalls } -func NewChainStore(bs bstore.Blockstore, ds dstore.Batching) *ChainStore { +func NewChainStore(bs bstore.Blockstore, ds dstore.Batching, vmcalls *types.VMSyscalls) *ChainStore { c, _ := lru.NewARC(2048) tsc, _ := lru.NewARC(4096) cs := &ChainStore{ @@ -63,6 +65,7 @@ func NewChainStore(bs bstore.Blockstore, ds dstore.Batching) *ChainStore { tipsets: make(map[uint64][]cid.Cid), mmCache: c, tsCache: tsc, + vmcalls: vmcalls, } cs.reorgCh = cs.reorgWorker(context.TODO()) @@ -793,6 +796,11 @@ func (cs *ChainStore) Blockstore() bstore.Blockstore { return cs.bs } +func (cs *ChainStore) VMSys() *types.VMSyscalls { + return cs.vmcalls +} + + func (cs *ChainStore) TryFillTipSet(ts *types.TipSet) (*FullTipSet, error) { var out []*types.FullBlock diff --git a/chain/store/store_test.go b/chain/store/store_test.go index 4d6d11c1b..c3c7a2ef4 100644 --- a/chain/store/store_test.go +++ b/chain/store/store_test.go @@ -55,7 +55,7 @@ func BenchmarkGetRandomness(b *testing.B) { bs := blockstore.NewBlockstore(bds) - cs := store.NewChainStore(bs, mds) + cs := store.NewChainStore(bs, mds, nil) b.ResetTimer() diff --git a/chain/store/weight.go b/chain/store/weight.go index 901356728..49ef9ef26 100644 --- a/chain/store/weight.go +++ b/chain/store/weight.go @@ -60,7 +60,7 @@ func (cs *ChainStore) call(ctx context.Context, msg *types.Message, ts *types.Ti r := NewChainRand(cs, ts.Cids(), ts.Height()) - vmi, err := vm.NewVM(bstate, ts.Height(), r, actors.NetworkAddress, cs.bs) + vmi, err := vm.NewVM(bstate, ts.Height(), r, actors.NetworkAddress, cs.bs, cs.vmcalls) if err != nil { return nil, xerrors.Errorf("failed to set up vm: %w", err) } diff --git a/chain/sync.go b/chain/sync.go index e5a79133c..7c8e13b36 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -690,7 +690,7 @@ func (syncer *Syncer) VerifyElectionPoStProof(ctx context.Context, h *types.Bloc } hvrf := sha256.Sum256(h.EPostProof.PostRand) - ok, err := sectorbuilder.VerifyElectionPost(ctx, ssize, *sectorInfo, hvrf[:], h.EPostProof.Proof, winners, h.Miner) + ok, err := sectorbuilder.ProofVerifier.VerifyElectionPost(ctx, ssize, *sectorInfo, hvrf[:], h.EPostProof.Proof, winners, h.Miner) if err != nil { return xerrors.Errorf("failed to verify election post: %w", err) } diff --git a/chain/types/vmcontext.go b/chain/types/vmcontext.go index 1af2aa3da..609044d3c 100644 --- a/chain/types/vmcontext.go +++ b/chain/types/vmcontext.go @@ -2,6 +2,7 @@ package types import ( "context" + "github.com/filecoin-project/go-sectorbuilder" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-amt-ipld" @@ -46,7 +47,17 @@ type VMContext interface { } type VMSyscalls struct { - ValidatePoRep func(context.Context, address.Address, uint64, []byte, []byte, []byte, []byte, []byte, uint64) (bool, aerrors.ActorError) + ValidatePoRep func(context.Context, address.Address, uint64, []byte, []byte, []byte, []byte, []byte, uint64) (bool, aerrors.ActorError) + VerifyFallbackPost func(ctx context.Context, + sectorSize uint64, + sectorInfo sectorbuilder.SortedPublicSectorInfo, + challengeSeed []byte, + proof []byte, + candidates []sectorbuilder.EPostCandidate, + proverID address.Address, + faults uint64) (bool, error) + + GenerateDataCommitment func(ssize uint64, pieces []sectorbuilder.PublicPieceInfo) ([sectorbuilder.CommLen]byte, error) } type storageWrapper struct { diff --git a/chain/validation/applier.go b/chain/validation/applier.go index 4e21f1228..8caf1b2dd 100644 --- a/chain/validation/applier.go +++ b/chain/validation/applier.go @@ -2,6 +2,7 @@ package validation import ( "context" + "github.com/filecoin-project/go-sectorbuilder" vchain "github.com/filecoin-project/chain-validation/pkg/chain" vstate "github.com/filecoin-project/chain-validation/pkg/state" @@ -32,7 +33,7 @@ func (a *Applier) ApplyMessage(eCtx *vchain.ExecutionContext, state vstate.Wrapp if err != nil { return vchain.MessageReceipt{}, err } - lotusVM, err := vm.NewVM(base, eCtx.Epoch, randSrc, minerAddr, st.bs) + lotusVM, err := vm.NewVM(base, eCtx.Epoch, randSrc, minerAddr, st.bs, vm.Syscalls(sectorbuilder.ProofVerifier)) if err != nil { return vchain.MessageReceipt{}, err } diff --git a/chain/vm/syscalls.go b/chain/vm/syscalls.go index 92f9ea269..5c83c654f 100644 --- a/chain/vm/syscalls.go +++ b/chain/vm/syscalls.go @@ -1,14 +1,30 @@ package vm import ( + "context" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-sectorbuilder" "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/aerrors" "github.com/filecoin-project/lotus/chain/types" + "go.opencensus.io/trace" ) // Actual type is defined in chain/types/vmcontext.go because the VMContext interface is there -func DefaultSyscalls() *types.VMSyscalls { +func Syscalls(verifier sectorbuilder.Verifier) *types.VMSyscalls { return &types.VMSyscalls{ - ValidatePoRep: actors.ValidatePoRep, + ValidatePoRep: func(ctx context.Context, maddr address.Address, ssize uint64, commD, commR, ticket, proof, seed []byte, sectorID uint64) (bool, actors.ActorError) { + _, span := trace.StartSpan(ctx, "ValidatePoRep") + defer span.End() + ok, err := verifier.VerifySeal(ssize, commR, commD, maddr, ticket, seed, sectorID, proof) + if err != nil { + return false, aerrors.Absorb(err, 25, "verify seal failed") + } + + return ok, nil + }, + VerifyFallbackPost: verifier.VerifyFallbackPost, + GenerateDataCommitment: verifier.GenerateDataCommitment, } } diff --git a/chain/vm/vm.go b/chain/vm/vm.go index a038211b1..a0ab66999 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -312,7 +312,7 @@ type VM struct { Syscalls *types.VMSyscalls } -func NewVM(base cid.Cid, height uint64, r Rand, maddr address.Address, cbs blockstore.Blockstore) (*VM, error) { +func NewVM(base cid.Cid, height uint64, r Rand, maddr address.Address, cbs blockstore.Blockstore, syscalls *types.VMSyscalls) (*VM, error) { buf := bufbstore.NewBufferedBstore(cbs) cst := hamt.CSTFromBstore(buf) state, err := state.LoadStateTree(cst, base) @@ -328,8 +328,8 @@ func NewVM(base cid.Cid, height uint64, r Rand, maddr address.Address, cbs block blockHeight: height, blockMiner: maddr, inv: newInvoker(), - rand: r, - Syscalls: DefaultSyscalls(), + rand: r, // TODO: Probably should be a syscall + Syscalls: syscalls, }, nil } diff --git a/cmd/lotus-bench/main.go b/cmd/lotus-bench/main.go index 6bd212c78..2cd059a2c 100644 --- a/cmd/lotus-bench/main.go +++ b/cmd/lotus-bench/main.go @@ -213,7 +213,7 @@ func main() { sealcommit := time.Now() commD := pi.CommP - ok, err := sectorbuilder.VerifySeal(sectorSize, pco.CommR[:], commD[:], maddr, ticket.TicketBytes[:], seed.TicketBytes[:], i, proof) + ok, err := sectorbuilder.ProofVerifier.VerifySeal(sectorSize, pco.CommR[:], commD[:], maddr, ticket.TicketBytes[:], seed.TicketBytes[:], i, proof) if err != nil { return err } @@ -307,7 +307,7 @@ func main() { 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) + ok, err := sectorbuilder.ProofVerifier.VerifyElectionPost(context.TODO(), sectorSize, sinfos, challenge[:], proof1, candidates[:1], maddr) if err != nil { return err } @@ -317,7 +317,7 @@ func main() { verifypost1 := time.Now() - ok, err = sectorbuilder.VerifyElectionPost(context.TODO(), sectorSize, sinfos, challenge[:], proof2, candidates[:1], maddr) + ok, err = sectorbuilder.ProofVerifier.VerifyElectionPost(context.TODO(), sectorSize, sinfos, challenge[:], proof2, candidates[:1], maddr) if err != nil { return err } diff --git a/go.mod b/go.mod index afbe0c0a9..55657aff6 100644 --- a/go.mod +++ b/go.mod @@ -109,3 +109,7 @@ require ( replace github.com/golangci/golangci-lint => github.com/golangci/golangci-lint v1.18.0 replace github.com/filecoin-project/filecoin-ffi => ./extern/filecoin-ffi + +replace github.com/filecoin-project/go-sectorbuilder => /home/magik6k/gohack/github.com/filecoin-project/go-sectorbuilder + +replace github.com/filecoin-project/go-fil-markets => /home/magik6k/gohack/github.com/filecoin-project/go-fil-markets diff --git a/node/builder.go b/node/builder.go index dd0af277d..e2563c81e 100644 --- a/node/builder.go +++ b/node/builder.go @@ -23,6 +23,7 @@ import ( "github.com/filecoin-project/go-fil-markets/retrievalmarket/discovery" "github.com/filecoin-project/go-fil-markets/storagemarket" deals "github.com/filecoin-project/go-fil-markets/storagemarket/impl" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain" "github.com/filecoin-project/lotus/chain/blocksync" @@ -33,6 +34,7 @@ import ( "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/chain/vm" "github.com/filecoin-project/lotus/chain/wallet" "github.com/filecoin-project/lotus/markets/storageadapter" "github.com/filecoin-project/lotus/miner" @@ -193,6 +195,8 @@ func Online() Option { Override(HandleIncomingMessagesKey, modules.HandleIncomingMessages), + Override(new(sectorbuilder.Verifier), sectorbuilder.ProofVerifier), + Override(new(*types.VMSyscalls), vm.Syscalls), Override(new(*store.ChainStore), modules.ChainStore), Override(new(*stmgr.StateManager), stmgr.NewStateManager), Override(new(*wallet.Wallet), wallet.NewWallet), @@ -239,7 +243,7 @@ func Online() Option { // Storage miner ApplyIf(func(s *Settings) bool { return s.nodeType == repo.StorageMiner }, - Override(new(storage.SectorBuilder), modules.SectorBuilder), + Override(new(sectorbuilder.Interface), modules.SectorBuilder), Override(new(*sectorblocks.SectorBlocks), sectorblocks.NewSectorBlocks), Override(new(storage.TicketFn), modules.SealTicketGen), Override(new(*storage.Miner), modules.StorageMiner), diff --git a/node/impl/storminer.go b/node/impl/storminer.go index ac149b64b..9bd5b4c73 100644 --- a/node/impl/storminer.go +++ b/node/impl/storminer.go @@ -26,7 +26,7 @@ type StorageMinerAPI struct { CommonAPI SectorBuilderConfig *sectorbuilder.Config - SectorBuilder storage.SectorBuilder + SectorBuilder sectorbuilder.Interface SectorBlocks *sectorblocks.SectorBlocks Miner *storage.Miner diff --git a/node/modules/chain.go b/node/modules/chain.go index bc0ff3ce2..b70cb6892 100644 --- a/node/modules/chain.go +++ b/node/modules/chain.go @@ -73,8 +73,8 @@ func ChainBlockservice(bs dtypes.ChainBlockstore, rem dtypes.ChainExchange) dtyp return blockservice.New(bs, rem) } -func ChainStore(lc fx.Lifecycle, bs dtypes.ChainBlockstore, ds dtypes.MetadataDS) *store.ChainStore { - chain := store.NewChainStore(bs, ds) +func ChainStore(lc fx.Lifecycle, bs dtypes.ChainBlockstore, ds dtypes.MetadataDS, syscalls *types.VMSyscalls) *store.ChainStore { + chain := store.NewChainStore(bs, ds, syscalls) if err := chain.Load(); err != nil { log.Warnf("loading chain state from disk: %s", err) diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index 822b70baa..ae9780560 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -98,7 +98,7 @@ func SectorBuilderConfig(storagePath string, threads uint, noprecommit, nocommit } } -func StorageMiner(mctx helpers.MetricsCtx, lc fx.Lifecycle, api api.FullNode, h host.Host, ds dtypes.MetadataDS, sb storage.SectorBuilder, tktFn storage.TicketFn) (*storage.Miner, error) { +func StorageMiner(mctx helpers.MetricsCtx, lc fx.Lifecycle, api api.FullNode, h host.Host, ds dtypes.MetadataDS, sb sectorbuilder.Interface, tktFn storage.TicketFn) (*storage.Miner, error) { maddr, err := minerAddrFromDS(ds) if err != nil { return nil, err diff --git a/node/modules/testing/genesis.go b/node/modules/testing/genesis.go index bfb6e26c5..a5cc2e68b 100644 --- a/node/modules/testing/genesis.go +++ b/node/modules/testing/genesis.go @@ -30,8 +30,8 @@ import ( var glog = logging.Logger("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 { +func MakeGenesisMem(out io.Writer, gmc *gen.GenMinerCfg) func(bs dtypes.ChainBlockstore, w *wallet.Wallet, syscalls *types.VMSyscalls) modules.Genesis { + return func(bs dtypes.ChainBlockstore, w *wallet.Wallet, syscalls *types.VMSyscalls) 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") defk, err := w.GenerateKey(types.KTBLS) @@ -51,7 +51,7 @@ func MakeGenesisMem(out io.Writer, gmc *gen.GenMinerCfg) func(bs dtypes.ChainBlo alloc[waddr] = types.FromFil(10000) } - b, err := gen.MakeGenesisBlock(bs, alloc, gmc, 100000) + b, err := gen.MakeGenesisBlock(bs, syscalls, alloc, gmc, 100000) if err != nil { return nil, err } @@ -68,8 +68,8 @@ func MakeGenesisMem(out io.Writer, gmc *gen.GenMinerCfg) func(bs dtypes.ChainBlo } } -func MakeGenesis(outFile, presealInfo, timestamp string) func(bs dtypes.ChainBlockstore, w *wallet.Wallet) modules.Genesis { - return func(bs dtypes.ChainBlockstore, w *wallet.Wallet) modules.Genesis { +func MakeGenesis(outFile, presealInfo, timestamp string) func(bs dtypes.ChainBlockstore, w *wallet.Wallet, syscalls *types.VMSyscalls) modules.Genesis { + return func(bs dtypes.ChainBlockstore, w *wallet.Wallet, syscalls *types.VMSyscalls) 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") presealInfo, err := homedir.Expand(presealInfo) @@ -130,7 +130,7 @@ func MakeGenesis(outFile, presealInfo, timestamp string) func(bs dtypes.ChainBlo ts = uint64(t.Unix()) } - b, err := gen.MakeGenesisBlock(bs, addrs, gmc, ts) + b, err := gen.MakeGenesisBlock(bs, syscalls, addrs, gmc, ts) if err != nil { return nil, err } diff --git a/node/node_test.go b/node/node_test.go index eb063ff58..e813c39b5 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -36,6 +36,7 @@ import ( "github.com/filecoin-project/lotus/node/modules" modtest "github.com/filecoin-project/lotus/node/modules/testing" "github.com/filecoin-project/lotus/node/repo" + "github.com/filecoin-project/lotus/storage/sbmock" ) func init() { @@ -102,6 +103,7 @@ func testStorageNode(ctx context.Context, t *testing.T, waddr address.Address, a node.Test(), node.MockHost(mn), + node.Override(new(sectorbuilder.Interface), sbmock.NewMockSectorBuilder(5, build.SectorSizes[0])), node.Override(new(api.FullNode), tnd), node.Override(new(*miner.Miner), miner.NewTestMiner(mineBlock, act)), @@ -247,6 +249,110 @@ func builder(t *testing.T, nFull int, storage []int) ([]test.TestNode, []test.Te return fulls, storers } +func mockSbBuilder(t *testing.T, nFull int, storage []int) ([]test.TestNode, []test.TestStorageNode) { + ctx := context.Background() + mn := mocknet.New(ctx) + + fulls := make([]test.TestNode, nFull) + storers := make([]test.TestStorageNode, len(storage)) + + pk, _, err := crypto.GenerateEd25519Key(rand.Reader) + require.NoError(t, err) + + minerPid, err := peer.IDFromPrivateKey(pk) + require.NoError(t, err) + + 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{}, + } + + var presealDirs []string + 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 := sbmock.PreSeal(1024, maddr, 1) + if err != nil { + t.Fatal(err) + } + + presealDirs = append(presealDirs, tdir) + 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, gmc)) + } else { + genesis = node.Override(new(modules.Genesis), modules.LoadGenesis(genbuf.Bytes())) + } + + var err error + // TODO: Don't ignore stop + _, err = node.New(ctx, + node.FullAPI(&fulls[i].FullNode), + node.Online(), + node.Repo(repo.NewMemory(nil)), + node.MockHost(mn), + node.Test(), + + node.Override(new(sectorbuilder.Verifier), sbmock.MockVerifier), + + genesis, + ) + if err != nil { + t.Fatal(err) + } + + } + + for i, full := range storage { + // TODO: support non-bootstrap miners + if i != 0 { + t.Fatal("only one storage node supported") + } + if full != 0 { + t.Fatal("storage nodes only supported on the first full node") + } + + f := fulls[full] + + genMiner := gmc.MinerAddrs[i] + wa := gmc.PreSeals[genMiner.String()].Worker + + storers[i] = testStorageNode(ctx, t, wa, genMiner, pk, f, mn) + + /*if err := sma.SectorBuilder.ImportFrom(osb, false); err != nil { + t.Fatal(err) + }*/ + + } + + if err := mn.LinkAll(); err != nil { + t.Fatal(err) + } + + return fulls, storers +} + + func TestAPI(t *testing.T) { test.TestApis(t, builder) } @@ -292,5 +398,5 @@ func TestAPIDealFlow(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode") } - test.TestDealFlow(t, builder) + test.TestDealFlow(t, mockSbBuilder) } diff --git a/storage/fpost_sched.go b/storage/fpost_sched.go index 30dadbd56..cafb212b6 100644 --- a/storage/fpost_sched.go +++ b/storage/fpost_sched.go @@ -8,6 +8,8 @@ import ( "golang.org/x/xerrors" "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-sectorbuilder" + "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" @@ -19,7 +21,7 @@ const StartConfidence = 4 // TODO: config type fpostScheduler struct { api storageMinerApi - sb SectorBuilder + sb sectorbuilder.Interface actor address.Address worker address.Address diff --git a/storage/garbage.go b/storage/garbage.go index a5470cdaa..5da1771b2 100644 --- a/storage/garbage.go +++ b/storage/garbage.go @@ -22,7 +22,7 @@ func (m *Miner) pledgeSector(ctx context.Context, sectorID uint64, existingPiece deals := make([]actors.StorageDealProposal, len(sizes)) for i, size := range sizes { release := m.sb.RateLimit() - commP, err := sectorbuilder.GeneratePieceCommitment(io.LimitReader(rand.New(rand.NewSource(42)), int64(size)), size) + commP, err := m.sb.GeneratePieceCommitment(io.LimitReader(rand.New(rand.NewSource(42)), int64(size)), size) release() if err != nil { diff --git a/storage/miner.go b/storage/miner.go index 90c4d9fac..02022d03f 100644 --- a/storage/miner.go +++ b/storage/miner.go @@ -3,7 +3,6 @@ package storage import ( "context" "errors" - "io" "time" "github.com/ipfs/go-cid" @@ -37,7 +36,7 @@ type Miner struct { worker address.Address // Sealing - sb SectorBuilder + sb sectorbuilder.Interface sectors *statestore.StateStore tktFn TicketFn @@ -72,24 +71,7 @@ type storageMinerApi interface { WalletHas(context.Context, address.Address) (bool, error) } -type SectorBuilder interface { - RateLimit() func() - AddPiece(uint64, uint64, io.Reader, []uint64) (sectorbuilder.PublicPieceInfo, error) - SectorSize() uint64 - AcquireSectorId() (uint64, error) - Scrub(sectorbuilder.SortedPublicSectorInfo) []*sectorbuilder.Fault - GenerateFallbackPoSt(sectorbuilder.SortedPublicSectorInfo, [sectorbuilder.CommLen]byte, []uint64) ([]sectorbuilder.EPostCandidate, []byte, error) - SealPreCommit(context.Context, uint64, sectorbuilder.SealTicket, []sectorbuilder.PublicPieceInfo) (sectorbuilder.RawSealPreCommitOutput, error) - SealCommit(context.Context, uint64, sectorbuilder.SealTicket, sectorbuilder.SealSeed, []sectorbuilder.PublicPieceInfo, sectorbuilder.RawSealPreCommitOutput) ([]byte, error) - - // Not so sure about these being on the interface - GetPath(string, string) (string, error) - WorkerStats() sectorbuilder.WorkerStats - AddWorker(context.Context, sectorbuilder.WorkerCfg) (<-chan sectorbuilder.WorkerTask, error) - TaskDone(context.Context, uint64, sectorbuilder.SealRes) error -} - -func NewMiner(api storageMinerApi, addr address.Address, h host.Host, ds datastore.Batching, sb SectorBuilder, tktFn TicketFn) (*Miner, error) { +func NewMiner(api storageMinerApi, addr address.Address, h host.Host, ds datastore.Batching, sb sectorbuilder.Interface, tktFn TicketFn) (*Miner, error) { return &Miner{ api: api, @@ -162,11 +144,11 @@ func (m *Miner) runPreflightChecks(ctx context.Context) error { } type SectorBuilderEpp struct { - sb *sectorbuilder.SectorBuilder + sb sectorbuilder.Interface } -func NewElectionPoStProver(sb SectorBuilder) *SectorBuilderEpp { - return &SectorBuilderEpp{sb.(*sectorbuilder.SectorBuilder)} +func NewElectionPoStProver(sb sectorbuilder.Interface) *SectorBuilderEpp { + return &SectorBuilderEpp{sb} } var _ gen.ElectionPoStProver = (*SectorBuilderEpp)(nil) diff --git a/storage/sbmock/preseal.go b/storage/sbmock/preseal.go new file mode 100644 index 000000000..ca498a134 --- /dev/null +++ b/storage/sbmock/preseal.go @@ -0,0 +1,52 @@ +package sbmock + +import ( + "math" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-sectorbuilder" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/wallet" + "github.com/filecoin-project/lotus/genesis" +) + +func PreSeal(ssize uint64, maddr address.Address, sectors int) (*genesis.GenesisMiner, error) { + k, err := wallet.GenerateKey(types.KTBLS) + if err != nil { + return nil, err + } + + genm := &genesis.GenesisMiner{ + Owner: k.Address, + Worker: k.Address, + SectorSize: ssize, + Sectors: make([]*genesis.PreSeal, sectors), + Key: k.KeyInfo, + } + + for i := range genm.Sectors { + preseal := &genesis.PreSeal{} + sdata := randB(sectorbuilder.UserBytesForSectorSize(ssize)) + + preseal.CommD = commD(sdata) + preseal.CommR = commDR(preseal.CommD[:]) + preseal.SectorID = uint64(i + 1) + preseal.Deal = actors.StorageDealProposal{ + PieceRef: preseal.CommD[:], + PieceSize: sectorbuilder.UserBytesForSectorSize(ssize), + Client: maddr, + Provider: maddr, + ProposalExpiration: math.MaxUint64, + Duration: math.MaxUint64, + StoragePricePerEpoch: types.NewInt(0), + StorageCollateral: types.NewInt(0), + ProposerSignature: nil, + } + + genm.Sectors[i] = preseal + } + + return genm, nil +} \ No newline at end of file diff --git a/storage/sbmock/sbmock.go b/storage/sbmock/sbmock.go index 73905db28..191751c2d 100644 --- a/storage/sbmock/sbmock.go +++ b/storage/sbmock/sbmock.go @@ -6,19 +6,16 @@ import ( "fmt" "io" "io/ioutil" + "math/big" "math/rand" "sync" + ffi "github.com/filecoin-project/filecoin-ffi" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-sectorbuilder" "golang.org/x/xerrors" ) -func randComm() [sectorbuilder.CommLen]byte { - var out [sectorbuilder.CommLen]byte - rand.Read(out[:]) - return out -} - type SBMock struct { sectors map[uint64]*sectorState sectorSize uint64 @@ -28,11 +25,13 @@ type SBMock struct { lk sync.Mutex } +type mockVerif struct {} + func NewMockSectorBuilder(threads int, ssize uint64) *SBMock { return &SBMock{ sectors: make(map[uint64]*sectorState), sectorSize: ssize, - nextSectorID: 0, + nextSectorID: 5, rateLimit: make(chan struct{}, threads), } } @@ -82,7 +81,7 @@ func (sb *SBMock) AddPiece(size uint64, sectorId uint64, r io.Reader, existingPi ss.pieces = append(ss.pieces, b) return sectorbuilder.PublicPieceInfo{ Size: size, - // TODO: should we compute a commP? maybe do it when we need it + CommP: commD(b), }, nil } @@ -158,9 +157,22 @@ func (sb *SBMock) SealPreCommit(ctx context.Context, sid uint64, ticket sectorbu ss.state = statePreCommit + pis := make([]ffi.PublicPieceInfo, len(ss.pieces)) + for i, piece := range ss.pieces { + pis[i] = ffi.PublicPieceInfo{ + Size: uint64(len(piece)), + CommP: commD(piece), + } + } + + commd, err := MockVerifier.GenerateDataCommitment(sb.sectorSize, pis) + if err != nil { + return sectorbuilder.RawSealPreCommitOutput{}, err + } + return sectorbuilder.RawSealPreCommitOutput{ - CommD: randComm(), - CommR: randComm(), + CommD: commd, + CommR: commDR(commd[:]), }, nil } @@ -184,9 +196,11 @@ func (sb *SBMock) SealCommit(ctx context.Context, sid uint64, ticket sectorbuild opFinishWait(ctx) - buf := make([]byte, 32) - rand.Read(buf) - return buf, nil + var out [32]byte + for i := range out { + out[i] = precommit.CommD[i] + precommit.CommR[31 - i] - ticket.TicketBytes[i] * seed.TicketBytes[i] + } + return out[:], nil } func (sb *SBMock) GetPath(string, string) (string, error) { @@ -235,6 +249,45 @@ func AddOpFinish(ctx context.Context) (context.Context, func()) { } } +func (sb *SBMock) ComputeElectionPoSt(sectorInfo sectorbuilder.SortedPublicSectorInfo, challengeSeed []byte, winners []sectorbuilder.EPostCandidate) ([]byte, error) { + panic("implement me") +} + +func (sb *SBMock) GenerateEPostCandidates(sectorInfo sectorbuilder.SortedPublicSectorInfo, challengeSeed [sectorbuilder.CommLen]byte, faults []uint64) ([]sectorbuilder.EPostCandidate, error) { + if len(faults) > 0 { + panic("todo") + } + + n := sectorbuilder.ElectionPostChallengeCount(uint64(len(sectorInfo.Values())), uint64(len(faults))) + if n > uint64(len(sectorInfo.Values())) { + n = uint64(len(sectorInfo.Values())) + } + + out := make([]sectorbuilder.EPostCandidate, len(sectorInfo.Values())) + + seed := big.NewInt(0).SetBytes(challengeSeed[:]) + start := seed.Mod(seed, big.NewInt(int64(len(sectorInfo.Values())))).Int64() + + for i := 0; uint64(i) < n; i++ { + out[i] = sectorbuilder.EPostCandidate{ + SectorID: uint64((int(start) + i) % len(sectorInfo.Values())), + PartialTicket: challengeSeed, + Ticket: commDR(challengeSeed[:]), + SectorChallengeIndex: 0, + } + } + + return out, nil +} + +func (sb *SBMock) GeneratePieceCommitment(piece io.Reader, pieceSize uint64) (commP [sectorbuilder.CommLen]byte, err error) { + panic("implement me") +} + +func (sb *SBMock) ReadPieceFromSealedSector(sectorID uint64, offset uint64, size uint64, ticket []byte, commD []byte) (io.ReadCloser, error) { + panic("implement me") +} + func (sb *SBMock) StageFakeData() (uint64, []sectorbuilder.PublicPieceInfo, error) { usize := sectorbuilder.UserBytesForSectorSize(sb.sectorSize) sid, err := sb.AcquireSectorId() @@ -252,3 +305,40 @@ func (sb *SBMock) StageFakeData() (uint64, []sectorbuilder.PublicPieceInfo, erro return sid, []sectorbuilder.PublicPieceInfo{pi}, nil } + +func (m mockVerif) VerifyElectionPost(ctx context.Context, sectorSize uint64, sectorInfo sectorbuilder.SortedPublicSectorInfo, challengeSeed []byte, proof []byte, candidates []sectorbuilder.EPostCandidate, proverID address.Address) (bool, error) { + panic("implement me") +} + +func (m mockVerif) VerifyFallbackPost(ctx context.Context, sectorSize uint64, sectorInfo sectorbuilder.SortedPublicSectorInfo, challengeSeed []byte, proof []byte, candidates []sectorbuilder.EPostCandidate, proverID address.Address, faults uint64) (bool, error) { + panic("implement me") +} + +func (m mockVerif) VerifySeal(sectorSize uint64, commR, commD []byte, proverID address.Address, ticket []byte, seed []byte, sectorID uint64, proof []byte) (bool, error) { + if len(proof) != 32 { // Real ones are longer, but this should be fine + return false, nil + } + + for i, b := range proof { + if b != commD[i] + commR[31 - i] - ticket[i] * seed[i] { + return false, nil + } + } + + return true, nil +} + +func (m mockVerif) GenerateDataCommitment(ssize uint64, pieces []ffi.PublicPieceInfo) ([sectorbuilder.CommLen]byte, error) { + if len(pieces) != 1 { + panic("todo") + } + if pieces[0].Size != sectorbuilder.UserBytesForSectorSize(ssize) { + panic("todo") + } + return pieces[0].CommP, nil +} + +var MockVerifier = mockVerif{} + +var _ sectorbuilder.Verifier = MockVerifier +var _ sectorbuilder.Interface = &SBMock{} diff --git a/storage/sbmock/util.go b/storage/sbmock/util.go new file mode 100644 index 000000000..30a60ea32 --- /dev/null +++ b/storage/sbmock/util.go @@ -0,0 +1,28 @@ +package sbmock + +import ( + "crypto/rand" + "crypto/sha256" + "io" + "io/ioutil" +) + +func randB(n uint64) []byte { + b, err := ioutil.ReadAll(io.LimitReader(rand.Reader, int64(n))) + if err != nil { + panic(err) + } + return b +} + +func commDR(in []byte) (out [32]byte) { + for i, b := range in { + out[i] = ^b + } + + return out +} + +func commD(b []byte) [32]byte { + return sha256.Sum256(b) +} diff --git a/storage/sectorblocks/blocks.go b/storage/sectorblocks/blocks.go index 4b80d0fb6..ed3a718df 100644 --- a/storage/sectorblocks/blocks.go +++ b/storage/sectorblocks/blocks.go @@ -39,7 +39,7 @@ var ErrNotFound = errors.New("not found") type SectorBlocks struct { *storage.Miner - sb *sectorbuilder.SectorBuilder + sb sectorbuilder.Interface intermediate blockstore.Blockstore // holds intermediate nodes TODO: consider combining with the staging blockstore @@ -47,10 +47,10 @@ type SectorBlocks struct { keyLk sync.Mutex } -func NewSectorBlocks(miner *storage.Miner, ds dtypes.MetadataDS, sb storage.SectorBuilder) *SectorBlocks { +func NewSectorBlocks(miner *storage.Miner, ds dtypes.MetadataDS, sb sectorbuilder.Interface) *SectorBlocks { sbc := &SectorBlocks{ Miner: miner, - sb: sb.(*sectorbuilder.SectorBuilder), + sb: sb, intermediate: blockstore.NewBlockstore(namespace.Wrap(ds, imBlocksPrefix)),