5733c71c50
We were ignoring quite a few error cases, and had one case where we weren't actually updating state where we wanted to. Unfortunately, if the linter doesn't pass, nobody has any reason to actually check lint failures in CI. There are three remaining XXXs marked in the code for lint.
460 lines
11 KiB
Go
460 lines
11 KiB
Go
package mock
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"math/rand"
|
|
"sync"
|
|
|
|
"github.com/filecoin-project/go-bitfield"
|
|
commcid "github.com/filecoin-project/go-fil-commcid"
|
|
"github.com/filecoin-project/specs-actors/actors/abi"
|
|
"github.com/filecoin-project/specs-storage/storage"
|
|
"github.com/ipfs/go-cid"
|
|
logging "github.com/ipfs/go-log"
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
|
|
"github.com/filecoin-project/lotus/extern/sector-storage/storiface"
|
|
)
|
|
|
|
var log = logging.Logger("sbmock")
|
|
|
|
type SectorMgr struct {
|
|
sectors map[abi.SectorID]*sectorState
|
|
pieces map[cid.Cid][]byte
|
|
sectorSize abi.SectorSize
|
|
nextSectorID abi.SectorNumber
|
|
proofType abi.RegisteredSealProof
|
|
|
|
lk sync.Mutex
|
|
}
|
|
|
|
type mockVerif struct{}
|
|
|
|
func NewMockSectorMgr(ssize abi.SectorSize, genesisSectors []abi.SectorID) *SectorMgr {
|
|
rt, err := ffiwrapper.SealProofTypeFromSectorSize(ssize)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
sectors := make(map[abi.SectorID]*sectorState)
|
|
for _, sid := range genesisSectors {
|
|
sectors[sid] = §orState{
|
|
failed: false,
|
|
state: stateCommit,
|
|
}
|
|
}
|
|
|
|
return &SectorMgr{
|
|
sectors: sectors,
|
|
pieces: map[cid.Cid][]byte{},
|
|
sectorSize: ssize,
|
|
nextSectorID: 5,
|
|
proofType: rt,
|
|
}
|
|
}
|
|
|
|
const (
|
|
statePacking = iota
|
|
statePreCommit
|
|
stateCommit // nolint
|
|
)
|
|
|
|
type sectorState struct {
|
|
pieces []cid.Cid
|
|
failed bool
|
|
|
|
state int
|
|
|
|
lk sync.Mutex
|
|
}
|
|
|
|
func (mgr *SectorMgr) NewSector(ctx context.Context, sector abi.SectorID) error {
|
|
return nil
|
|
}
|
|
|
|
func (mgr *SectorMgr) AddPiece(ctx context.Context, sectorID abi.SectorID, existingPieces []abi.UnpaddedPieceSize, size abi.UnpaddedPieceSize, r io.Reader) (abi.PieceInfo, error) {
|
|
log.Warn("Add piece: ", sectorID, size, mgr.proofType)
|
|
|
|
var b bytes.Buffer
|
|
tr := io.TeeReader(r, &b)
|
|
|
|
c, err := ffiwrapper.GeneratePieceCIDFromFile(mgr.proofType, tr, size)
|
|
if err != nil {
|
|
return abi.PieceInfo{}, xerrors.Errorf("failed to generate piece cid: %w", err)
|
|
}
|
|
|
|
log.Warn("Generated Piece CID: ", c)
|
|
|
|
mgr.lk.Lock()
|
|
mgr.pieces[c] = b.Bytes()
|
|
|
|
ss, ok := mgr.sectors[sectorID]
|
|
if !ok {
|
|
ss = §orState{
|
|
state: statePacking,
|
|
}
|
|
mgr.sectors[sectorID] = ss
|
|
}
|
|
mgr.lk.Unlock()
|
|
|
|
ss.lk.Lock()
|
|
ss.pieces = append(ss.pieces, c)
|
|
ss.lk.Unlock()
|
|
|
|
return abi.PieceInfo{
|
|
|
|
Size: size.Padded(),
|
|
PieceCID: c,
|
|
}, nil
|
|
}
|
|
|
|
func (mgr *SectorMgr) SectorSize() abi.SectorSize {
|
|
return mgr.sectorSize
|
|
}
|
|
|
|
func (mgr *SectorMgr) AcquireSectorNumber() (abi.SectorNumber, error) {
|
|
mgr.lk.Lock()
|
|
defer mgr.lk.Unlock()
|
|
id := mgr.nextSectorID
|
|
mgr.nextSectorID++
|
|
return id, nil
|
|
}
|
|
|
|
func (mgr *SectorMgr) SealPreCommit1(ctx context.Context, sid abi.SectorID, ticket abi.SealRandomness, pieces []abi.PieceInfo) (out storage.PreCommit1Out, err error) {
|
|
mgr.lk.Lock()
|
|
ss, ok := mgr.sectors[sid]
|
|
mgr.lk.Unlock()
|
|
if !ok {
|
|
return nil, xerrors.Errorf("no sector with id %d in storage", sid)
|
|
}
|
|
|
|
ss.lk.Lock()
|
|
defer ss.lk.Unlock()
|
|
|
|
ussize := abi.PaddedPieceSize(mgr.sectorSize).Unpadded()
|
|
|
|
// TODO: verify pieces in sinfo.pieces match passed in pieces
|
|
|
|
var sum abi.UnpaddedPieceSize
|
|
for _, p := range pieces {
|
|
sum += p.Size.Unpadded()
|
|
}
|
|
|
|
if sum != ussize {
|
|
return nil, xerrors.Errorf("aggregated piece sizes don't match up: %d != %d", sum, ussize)
|
|
}
|
|
|
|
if ss.state != statePacking {
|
|
return nil, xerrors.Errorf("cannot call pre-seal on sector not in 'packing' state")
|
|
}
|
|
|
|
opFinishWait(ctx)
|
|
|
|
ss.state = statePreCommit
|
|
|
|
pis := make([]abi.PieceInfo, len(ss.pieces))
|
|
for i, piece := range ss.pieces {
|
|
pis[i] = abi.PieceInfo{
|
|
Size: pieces[i].Size,
|
|
PieceCID: piece,
|
|
}
|
|
}
|
|
|
|
commd, err := MockVerifier.GenerateDataCommitment(mgr.proofType, pis)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_, _, cc, err := commcid.CIDToCommitment(commd)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
cc[0] ^= 'd'
|
|
|
|
return cc, nil
|
|
}
|
|
|
|
func (mgr *SectorMgr) SealPreCommit2(ctx context.Context, sid abi.SectorID, phase1Out storage.PreCommit1Out) (cids storage.SectorCids, err error) {
|
|
db := []byte(string(phase1Out))
|
|
db[0] ^= 'd'
|
|
|
|
d, _ := commcid.DataCommitmentV1ToCID(db)
|
|
|
|
commr := make([]byte, 32)
|
|
for i := range db {
|
|
commr[32-(i+1)] = db[i]
|
|
}
|
|
|
|
commR, _ := commcid.ReplicaCommitmentV1ToCID(commr)
|
|
|
|
return storage.SectorCids{
|
|
Unsealed: d,
|
|
Sealed: commR,
|
|
}, nil
|
|
}
|
|
|
|
func (mgr *SectorMgr) SealCommit1(ctx context.Context, sid abi.SectorID, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness, pieces []abi.PieceInfo, cids storage.SectorCids) (output storage.Commit1Out, err error) {
|
|
mgr.lk.Lock()
|
|
ss, ok := mgr.sectors[sid]
|
|
mgr.lk.Unlock()
|
|
if !ok {
|
|
return nil, xerrors.Errorf("no such sector %d", sid)
|
|
}
|
|
ss.lk.Lock()
|
|
defer ss.lk.Unlock()
|
|
|
|
if ss.failed {
|
|
return nil, xerrors.Errorf("[mock] cannot commit failed sector %d", sid)
|
|
}
|
|
|
|
if ss.state != statePreCommit {
|
|
return nil, xerrors.Errorf("cannot commit sector that has not been precommitted")
|
|
}
|
|
|
|
opFinishWait(ctx)
|
|
|
|
var out [32]byte
|
|
for i := range out {
|
|
out[i] = cids.Unsealed.Bytes()[i] + cids.Sealed.Bytes()[31-i] - ticket[i]*seed[i] ^ byte(sid.Number&0xff)
|
|
}
|
|
|
|
return out[:], nil
|
|
}
|
|
|
|
func (mgr *SectorMgr) SealCommit2(ctx context.Context, sid abi.SectorID, phase1Out storage.Commit1Out) (proof storage.Proof, err error) {
|
|
var out [32]byte
|
|
for i := range out {
|
|
out[i] = phase1Out[i] ^ byte(sid.Number&0xff)
|
|
}
|
|
|
|
return out[:], nil
|
|
}
|
|
|
|
// Test Instrumentation Methods
|
|
|
|
func (mgr *SectorMgr) MarkFailed(sid abi.SectorID, failed bool) error {
|
|
mgr.lk.Lock()
|
|
defer mgr.lk.Unlock()
|
|
ss, ok := mgr.sectors[sid]
|
|
if !ok {
|
|
return fmt.Errorf("no such sector in storage")
|
|
}
|
|
|
|
ss.failed = failed
|
|
return nil
|
|
}
|
|
|
|
func opFinishWait(ctx context.Context) {
|
|
val, ok := ctx.Value("opfinish").(chan struct{})
|
|
if !ok {
|
|
return
|
|
}
|
|
<-val
|
|
}
|
|
|
|
func AddOpFinish(ctx context.Context) (context.Context, func()) {
|
|
done := make(chan struct{})
|
|
|
|
return context.WithValue(ctx, "opfinish", done), func() { // nolint
|
|
close(done)
|
|
}
|
|
}
|
|
|
|
func (mgr *SectorMgr) GenerateWinningPoSt(ctx context.Context, minerID abi.ActorID, sectorInfo []abi.SectorInfo, randomness abi.PoStRandomness) ([]abi.PoStProof, error) {
|
|
return generateFakePoSt(sectorInfo, abi.RegisteredSealProof.RegisteredWinningPoStProof, randomness), nil
|
|
}
|
|
|
|
func (mgr *SectorMgr) GenerateWindowPoSt(ctx context.Context, minerID abi.ActorID, sectorInfo []abi.SectorInfo, randomness abi.PoStRandomness) ([]abi.PoStProof, []abi.SectorID, error) {
|
|
si := make([]abi.SectorInfo, 0, len(sectorInfo))
|
|
var skipped []abi.SectorID
|
|
|
|
for _, info := range sectorInfo {
|
|
sid := abi.SectorID{
|
|
Miner: minerID,
|
|
Number: info.SectorNumber,
|
|
}
|
|
|
|
_, found := mgr.sectors[sid]
|
|
|
|
if found && !mgr.sectors[sid].failed {
|
|
si = append(si, info)
|
|
} else {
|
|
skipped = append(skipped, sid)
|
|
}
|
|
}
|
|
|
|
return generateFakePoSt(si, abi.RegisteredSealProof.RegisteredWindowPoStProof, randomness), skipped, nil
|
|
}
|
|
|
|
func generateFakePoSt(sectorInfo []abi.SectorInfo, rpt func(abi.RegisteredSealProof) (abi.RegisteredPoStProof, error), randomness abi.PoStRandomness) []abi.PoStProof {
|
|
sectors := bitfield.New()
|
|
for _, info := range sectorInfo {
|
|
sectors.Set(uint64(info.SectorNumber))
|
|
}
|
|
|
|
wp, err := rpt(sectorInfo[0].SealProof)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
var proofBuf bytes.Buffer
|
|
|
|
_, err = proofBuf.Write(randomness)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := sectors.MarshalCBOR(&proofBuf); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return []abi.PoStProof{
|
|
{
|
|
PoStProof: wp,
|
|
ProofBytes: proofBuf.Bytes(),
|
|
},
|
|
}
|
|
}
|
|
|
|
func (mgr *SectorMgr) ReadPiece(ctx context.Context, w io.Writer, sectorID abi.SectorID, offset storiface.UnpaddedByteIndex, size abi.UnpaddedPieceSize, randomness abi.SealRandomness, c cid.Cid) error {
|
|
if len(mgr.sectors[sectorID].pieces) > 1 || offset != 0 {
|
|
panic("implme")
|
|
}
|
|
|
|
_, err := io.CopyN(w, bytes.NewReader(mgr.pieces[mgr.sectors[sectorID].pieces[0]]), int64(size))
|
|
return err
|
|
}
|
|
|
|
func (mgr *SectorMgr) StageFakeData(mid abi.ActorID) (abi.SectorID, []abi.PieceInfo, error) {
|
|
usize := abi.PaddedPieceSize(mgr.sectorSize).Unpadded()
|
|
sid, err := mgr.AcquireSectorNumber()
|
|
if err != nil {
|
|
return abi.SectorID{}, nil, err
|
|
}
|
|
|
|
buf := make([]byte, usize)
|
|
_, _ = rand.Read(buf) // nolint:gosec
|
|
|
|
id := abi.SectorID{
|
|
Miner: mid,
|
|
Number: sid,
|
|
}
|
|
|
|
pi, err := mgr.AddPiece(context.TODO(), id, nil, usize, bytes.NewReader(buf))
|
|
if err != nil {
|
|
return abi.SectorID{}, nil, err
|
|
}
|
|
|
|
return id, []abi.PieceInfo{pi}, nil
|
|
}
|
|
|
|
func (mgr *SectorMgr) FinalizeSector(context.Context, abi.SectorID, []storage.Range) error {
|
|
return nil
|
|
}
|
|
|
|
func (mgr *SectorMgr) ReleaseUnsealed(ctx context.Context, sector abi.SectorID, safeToFree []storage.Range) error {
|
|
return nil
|
|
}
|
|
|
|
func (mgr *SectorMgr) Remove(ctx context.Context, sector abi.SectorID) error {
|
|
mgr.lk.Lock()
|
|
defer mgr.lk.Unlock()
|
|
|
|
if _, has := mgr.sectors[sector]; !has {
|
|
return xerrors.Errorf("sector not found")
|
|
}
|
|
|
|
delete(mgr.sectors, sector)
|
|
return nil
|
|
}
|
|
|
|
func (mgr *SectorMgr) CheckProvable(ctx context.Context, spt abi.RegisteredSealProof, ids []abi.SectorID) ([]abi.SectorID, error) {
|
|
var bad []abi.SectorID
|
|
|
|
for _, sid := range ids {
|
|
_, found := mgr.sectors[sid]
|
|
|
|
if !found || mgr.sectors[sid].failed {
|
|
bad = append(bad, sid)
|
|
}
|
|
}
|
|
|
|
return bad, nil
|
|
}
|
|
|
|
func (m mockVerif) VerifySeal(svi abi.SealVerifyInfo) (bool, error) {
|
|
if len(svi.Proof) != 32 { // Real ones are longer, but this should be fine
|
|
return false, nil
|
|
}
|
|
|
|
for i, b := range svi.Proof {
|
|
if b != svi.UnsealedCID.Bytes()[i]+svi.SealedCID.Bytes()[31-i]-svi.InteractiveRandomness[i]*svi.Randomness[i] {
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (m mockVerif) VerifyWinningPoSt(ctx context.Context, info abi.WinningPoStVerifyInfo) (bool, error) {
|
|
return true, nil
|
|
}
|
|
|
|
func (m mockVerif) VerifyWindowPoSt(ctx context.Context, info abi.WindowPoStVerifyInfo) (bool, error) {
|
|
if len(info.Proofs) != 1 {
|
|
return false, xerrors.Errorf("expected 1 proof entry")
|
|
}
|
|
|
|
proof := info.Proofs[0]
|
|
|
|
if !bytes.Equal(proof.ProofBytes[:len(info.Randomness)], info.Randomness) {
|
|
return false, xerrors.Errorf("bad randomness")
|
|
}
|
|
|
|
sectors := bitfield.New()
|
|
if err := sectors.UnmarshalCBOR(bytes.NewReader(proof.ProofBytes[len(info.Randomness):])); err != nil {
|
|
return false, xerrors.Errorf("unmarshaling sectors bitfield from \"proof\": %w", err)
|
|
}
|
|
|
|
challenged := bitfield.New()
|
|
for _, sector := range info.ChallengedSectors {
|
|
challenged.Set(uint64(sector.SectorNumber))
|
|
}
|
|
|
|
{
|
|
b1, err := sectors.MarshalJSON()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
b2, err := challenged.MarshalJSON()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if !bytes.Equal(b1, b2) {
|
|
return false, xerrors.Errorf("proven and challenged sector sets didn't match: %s != %s", string(b1), string(b2))
|
|
}
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (m mockVerif) GenerateDataCommitment(pt abi.RegisteredSealProof, pieces []abi.PieceInfo) (cid.Cid, error) {
|
|
return ffiwrapper.GenerateUnsealedCID(pt, pieces)
|
|
}
|
|
|
|
func (m mockVerif) GenerateWinningPoStSectorChallenge(ctx context.Context, proofType abi.RegisteredPoStProof, minerID abi.ActorID, randomness abi.PoStRandomness, eligibleSectorCount uint64) ([]uint64, error) {
|
|
return []uint64{0}, nil
|
|
}
|
|
|
|
var MockVerifier = mockVerif{}
|
|
|
|
var _ storage.Sealer = &SectorMgr{}
|
|
var _ ffiwrapper.Verifier = MockVerifier
|