diff --git a/build/bootstrap/bootstrappers.pi b/build/bootstrap/bootstrappers.pi index 4d803d299..27abfeb3f 100644 --- a/build/bootstrap/bootstrappers.pi +++ b/build/bootstrap/bootstrappers.pi @@ -1 +1 @@ -/ip4/147.75.80.17/tcp/1347/p2p/12D3KooWLM9S94b9mnHD2fbWvQzXudoapHjbBbFA1T4QVft65Kym +/ip4/147.75.80.17/tcp/1347/p2p/12D3KooWFCkQdiJEMBVA6RrWq22ZXVFfM41YX8soQ5QVvNFjMJT8 diff --git a/build/bootstrap/root.pi b/build/bootstrap/root.pi index 80ed9013a..af18e8d88 100644 --- a/build/bootstrap/root.pi +++ b/build/bootstrap/root.pi @@ -1 +1 @@ -/ip4/147.75.80.29/tcp/1347/p2p/12D3KooWB4pVpuRAcW2873V6m3uPWevKcZ1VWr1FGPUTwDZUN5mg +/ip4/147.75.80.29/tcp/1347/p2p/12D3KooWSw9h3e6YrYZfRWDcir8qMV7ctZG9VmtXwSaP2ntsKXYf diff --git a/build/genesis/devnet.car b/build/genesis/devnet.car index 7d400c111..07aa06257 100644 Binary files a/build/genesis/devnet.car and b/build/genesis/devnet.car differ diff --git a/build/params.go b/build/params.go index f54491068..35ccd69f0 100644 --- a/build/params.go +++ b/build/params.go @@ -37,13 +37,13 @@ const PaymentChannelClosingDelay = 6 * 60 * 2 // six hours // Consensus / Network // Seconds -const BlockDelay = 10 +const BlockDelay = 12 // Seconds const AllowableClockDrift = BlockDelay * 2 // Blocks -const ForkLengthThreshold = 100 +const ForkLengthThreshold = Finality // Blocks (e) const BlocksPerEpoch = 5 @@ -60,7 +60,7 @@ const WRatioDen = 2 // Proofs // Blocks -const ProvingPeriodDuration = 160 +const ProvingPeriodDuration uint64 = 300 // PoStChallangeTime sets the window in which post computation should happen // Blocks diff --git a/chain/actors/actor_miner.go b/chain/actors/actor_miner.go index f8a4cd8f3..2f2a1df33 100644 --- a/chain/actors/actor_miner.go +++ b/chain/actors/actor_miner.go @@ -1,6 +1,7 @@ package actors import ( + "bytes" "context" "encoding/binary" "fmt" @@ -62,14 +63,11 @@ type StorageMinerActorState struct { // Amount of power this miner has. Power types.BigInt - // List of sectors that this miner was slashed for. - //SlashedSet SectorSet + // Active is set to true after the miner has submitted their first PoSt + Active bool // The height at which this miner was slashed at. - SlashedAt types.BigInt - - // The amount of storage collateral that is owed to clients, and cannot be used for collateral anymore. - OwedStorageCollateral types.BigInt + SlashedAt uint64 ProvingPeriodEnd uint64 } @@ -132,7 +130,7 @@ type maMethods struct { UpdatePeerID uint64 ChangeWorker uint64 IsSlashed uint64 - IsLate uint64 + CheckMiner uint64 DeclareFaults uint64 SlashConsensusFault uint64 } @@ -156,8 +154,8 @@ func (sma StorageMinerActor) Exports() []interface{} { 13: sma.GetSectorSize, 14: sma.UpdatePeerID, //15: sma.ChangeWorker, - //16: sma.IsSlashed, - //17: sma.IsLate, + 16: sma.IsSlashed, + 17: sma.CheckMiner, 18: sma.DeclareFaults, 19: sma.SlashConsensusFault, } @@ -529,7 +527,23 @@ func (sma StorageMinerActor) SubmitPoSt(act *types.Actor, vmctx types.VMContext, self.Power = types.BigMul(types.NewInt(pss.Count-uint64(len(faults))), types.NewInt(mi.SectorSize)) - enc, err := SerializeParams(&UpdateStorageParams{Delta: types.BigSub(self.Power, oldPower)}) + 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 { return nil, err } @@ -734,9 +748,68 @@ func (sma StorageMinerActor) GetSectorSize(act *types.Actor, vmctx types.VMConte return types.NewInt(mi.SectorSize).Bytes(), nil } -type PaymentVerifyParams struct { - Extra []byte - Proof []byte +func isLate(height uint64, self *StorageMinerActorState) bool { + return self.ProvingPeriodEnd > 0 && height >= self.ProvingPeriodEnd // TODO: review: maybe > ? +} + +func (sma StorageMinerActor) IsSlashed(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) { + _, self, err := loadState(vmctx) + if err != nil { + return nil, err + } + + return cbg.EncodeBool(self.SlashedAt != 0), nil +} + +type CheckMinerParams struct { + NetworkPower types.BigInt +} + +// TODO: better name +func (sma StorageMinerActor) CheckMiner(act *types.Actor, vmctx types.VMContext, params *CheckMinerParams) ([]byte, ActorError) { + if vmctx.Message().From != StoragePowerAddress { + return nil, aerrors.New(2, "only the storage power actor can check miner") + } + + oldstate, self, err := loadState(vmctx) + if err != nil { + return nil, err + } + + if !isLate(vmctx.BlockHeight(), self) { + // Everything's fine + return nil, nil + } + + if self.SlashedAt != 0 { + // Don't slash more than necessary + return nil, nil + } + + if params.NetworkPower.Equals(self.Power) { + // Don't break the network when there's only one miner left + + log.Warnf("can't slash miner %s for missed PoSt, no power would be left in the network", vmctx.Message().To) + return nil, nil + } + + // Slash for being late + + self.SlashedAt = vmctx.BlockHeight() + + nstate, err := vmctx.Storage().Put(self) + if err != nil { + return nil, err + } + if err := vmctx.Storage().Commit(oldstate, nstate); err != nil { + return nil, err + } + + var out bytes.Buffer + if err := self.Power.MarshalCBOR(&out); err != nil { + return nil, aerrors.HandleExternalError(err, "marshaling return value") + } + return out.Bytes(), nil } type DeclareFaultsParams struct { diff --git a/chain/actors/actor_paych.go b/chain/actors/actor_paych.go index 30c8565f8..6d3e74039 100644 --- a/chain/actors/actor_paych.go +++ b/chain/actors/actor_paych.go @@ -97,6 +97,11 @@ func hash(b []byte) []byte { panic("blake 2b hash pls") } +type PaymentVerifyParams struct { + Extra []byte + Proof []byte +} + func (pca PaymentChannelActor) UpdateChannelState(act *types.Actor, vmctx types.VMContext, params *PCAUpdateChannelStateParams) ([]byte, ActorError) { var self PaymentChannelActorState oldstate := vmctx.Storage().GetHead() diff --git a/chain/actors/actor_storagepower.go b/chain/actors/actor_storagepower.go index e3f7cbfd3..e76cdd054 100644 --- a/chain/actors/actor_storagepower.go +++ b/chain/actors/actor_storagepower.go @@ -1,18 +1,22 @@ package actors import ( + "bytes" "context" + "io" + + "github.com/filecoin-project/go-amt-ipld" + cid "github.com/ipfs/go-cid" + hamt "github.com/ipfs/go-hamt-ipld" + "github.com/libp2p/go-libp2p-core/peer" + cbg "github.com/whyrusleeping/cbor-gen" + "go.opencensus.io/trace" + xerrors "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" - - cid "github.com/ipfs/go-cid" - hamt "github.com/ipfs/go-hamt-ipld" - "github.com/libp2p/go-libp2p-core/peer" - cbg "github.com/whyrusleeping/cbor-gen" - xerrors "golang.org/x/xerrors" ) type StoragePowerActor struct{} @@ -24,11 +28,12 @@ type spaMethods struct { UpdateStorage uint64 GetTotalStorage uint64 PowerLookup uint64 - IsMiner uint64 + IsValidMiner uint64 PledgeCollateralForSize uint64 + CheckProofSubmissions uint64 } -var SPAMethods = spaMethods{1, 2, 3, 4, 5, 6, 7, 8} +var SPAMethods = spaMethods{1, 2, 3, 4, 5, 6, 7, 8, 9} func (spa StoragePowerActor) Exports() []interface{} { return []interface{}{ @@ -38,14 +43,17 @@ func (spa StoragePowerActor) Exports() []interface{} { 4: spa.UpdateStorage, 5: spa.GetTotalStorage, 6: spa.PowerLookup, - 7: spa.IsMiner, + 7: spa.IsValidMiner, 8: spa.PledgeCollateralForSize, + 9: spa.CheckProofSubmissions, } } type StoragePowerState struct { - Miners cid.Cid - MinerCount uint64 + Miners cid.Cid + ProvingBuckets cid.Cid // amt[ProvingPeriodBucket]hamt[minerAddress]struct{} + MinerCount uint64 + LastMinerCheck uint64 TotalStorage types.BigInt } @@ -259,7 +267,9 @@ func shouldSlash(block1, block2 *types.BlockHeader) bool { } type UpdateStorageParams struct { - Delta types.BigInt + Delta types.BigInt + NextProvingPeriodEnd uint64 + PreviousProvingPeriodEnd uint64 } func (spa StoragePowerActor) UpdateStorage(act *types.Actor, vmctx types.VMContext, params *UpdateStorageParams) ([]byte, ActorError) { @@ -273,13 +283,50 @@ func (spa StoragePowerActor) UpdateStorage(act *types.Actor, vmctx types.VMConte if err != nil { return nil, err } - if !has { return nil, aerrors.New(1, "update storage must only be called by a miner actor") } self.TotalStorage = types.BigAdd(self.TotalStorage, params.Delta) + previousBucket := params.PreviousProvingPeriodEnd % build.ProvingPeriodDuration + nextBucket := params.NextProvingPeriodEnd % build.ProvingPeriodDuration + + if previousBucket == nextBucket && params.PreviousProvingPeriodEnd != 0 { + nroot, err := vmctx.Storage().Put(&self) + if err != nil { + return nil, err + } + + if err := vmctx.Storage().Commit(old, nroot); err != nil { + return nil, err + } + + return nil, nil // Nothing to do + } + + buckets, eerr := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingBuckets) + if eerr != nil { + return nil, aerrors.HandleExternalError(eerr, "loading proving buckets amt") + } + + if params.PreviousProvingPeriodEnd != 0 { // delete from previous bucket + err := deleteMinerFromBucket(vmctx, buckets, previousBucket) + if err != nil { + return nil, err + } + } + + err = addMinerToBucket(vmctx, buckets, nextBucket) + if err != nil { + return nil, err + } + + self.ProvingBuckets, eerr = buckets.Flush() + if eerr != nil { + return nil, aerrors.HandleExternalError(eerr, "flushing proving buckets") + } + nroot, err := vmctx.Storage().Put(&self) if err != nil { return nil, err @@ -292,6 +339,82 @@ func (spa StoragePowerActor) UpdateStorage(act *types.Actor, vmctx types.VMConte return nil, nil } +func deleteMinerFromBucket(vmctx types.VMContext, buckets *amt.Root, previousBucket uint64) aerrors.ActorError { + var bucket cid.Cid + err := buckets.Get(previousBucket, &bucket) + switch err.(type) { + case *amt.ErrNotFound: + return aerrors.HandleExternalError(err, "proving bucket missing") + case nil: // noop + default: + return aerrors.HandleExternalError(err, "getting proving bucket") + } + + bhamt, err := hamt.LoadNode(vmctx.Context(), vmctx.Ipld(), bucket) + if err != nil { + return aerrors.HandleExternalError(err, "failed to load proving bucket") + } + err = bhamt.Delete(vmctx.Context(), string(vmctx.Message().From.Bytes())) + if err != nil { + return aerrors.HandleExternalError(err, "deleting miner from proving bucket") + } + + err = bhamt.Flush(vmctx.Context()) + if err != nil { + return aerrors.HandleExternalError(err, "flushing previous proving bucket") + } + + bucket, err = vmctx.Ipld().Put(vmctx.Context(), bhamt) + if err != nil { + return aerrors.HandleExternalError(err, "putting previous proving bucket hamt") + } + + err = buckets.Set(previousBucket, bucket) + if err != nil { + return aerrors.HandleExternalError(err, "setting previous proving bucket cid in amt") + } + + return nil +} + +func addMinerToBucket(vmctx types.VMContext, buckets *amt.Root, nextBucket uint64) aerrors.ActorError { + var bhamt *hamt.Node + var bucket cid.Cid + err := buckets.Get(nextBucket, &bucket) + switch err.(type) { + case *amt.ErrNotFound: + bhamt = hamt.NewNode(vmctx.Ipld()) + case nil: + bhamt, err = hamt.LoadNode(vmctx.Context(), vmctx.Ipld(), bucket) + if err != nil { + return aerrors.HandleExternalError(err, "failed to load proving bucket") + } + default: + return aerrors.HandleExternalError(err, "getting proving bucket") + } + + err = bhamt.Set(vmctx.Context(), string(vmctx.Message().From.Bytes()), cborNull) + if err != nil { + return aerrors.HandleExternalError(err, "setting miner in proving bucket") + } + + err = bhamt.Flush(vmctx.Context()) + if err != nil { + return aerrors.HandleExternalError(err, "flushing previous proving bucket") + } + + bucket, err = vmctx.Ipld().Put(vmctx.Context(), bhamt) + if err != nil { + return aerrors.HandleExternalError(err, "putting previous proving bucket hamt") + } + + err = buckets.Set(nextBucket, bucket) + if err != nil { + return aerrors.HandleExternalError(err, "setting previous proving bucket cid in amt") + } + return nil +} + func (spa StoragePowerActor) GetTotalStorage(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) { var self StoragePowerState if err := vmctx.Storage().Get(vmctx.Storage().GetHead(), &self); err != nil { @@ -338,11 +461,11 @@ func powerLookup(ctx context.Context, vmctx types.VMContext, self *StoragePowerS return types.BigFromBytes(ret), nil } -type IsMinerParam struct { +type IsValidMinerParam struct { Addr address.Address } -func (spa StoragePowerActor) IsMiner(act *types.Actor, vmctx types.VMContext, param *IsMinerParam) ([]byte, ActorError) { +func (spa StoragePowerActor) IsValidMiner(act *types.Actor, vmctx types.VMContext, param *IsValidMinerParam) ([]byte, ActorError) { var self StoragePowerState if err := vmctx.Storage().Get(vmctx.Storage().GetHead(), &self); err != nil { return nil, err @@ -353,7 +476,24 @@ func (spa StoragePowerActor) IsMiner(act *types.Actor, vmctx types.VMContext, pa return nil, err } - return cbg.EncodeBool(has), nil + if !has { + log.Warnf("Miner INVALID: not in set: %s", param.Addr) + + return cbg.CborBoolFalse, nil + } + + ret, err := vmctx.Send(param.Addr, MAMethods.IsSlashed, types.NewInt(0), nil) + if err != nil { + return nil, err + } + + slashed := bytes.Equal(ret, cbg.CborBoolTrue) + + if slashed { + log.Warnf("Miner INVALID: /SLASHED/ : %s", param.Addr) + } + + return cbg.EncodeBool(!slashed), nil } type PledgeCollateralParams struct { @@ -426,6 +566,108 @@ func pledgeCollateralForSize(vmctx types.VMContext, size, totalStorage types.Big return types.BigAdd(powerCollateral, perCapCollateral), nil } +func (spa StoragePowerActor) CheckProofSubmissions(act *types.Actor, vmctx types.VMContext, param *struct{}) ([]byte, ActorError) { + if vmctx.Message().From != StoragePowerAddress { + return nil, aerrors.New(1, "CheckProofSubmissions is only callable as a part of tipset state computation") + } + + var self StoragePowerState + old := vmctx.Storage().GetHead() + if err := vmctx.Storage().Get(old, &self); err != nil { + return nil, err + } + + for i := self.LastMinerCheck; i < vmctx.BlockHeight(); i++ { + height := i + 1 + + err := checkProofSubmissionsAtH(vmctx, &self, height) + if err != nil { + return nil, err + } + } + + self.LastMinerCheck = vmctx.BlockHeight() + + nroot, aerr := vmctx.Storage().Put(&self) + if aerr != nil { + return nil, aerr + } + + if err := vmctx.Storage().Commit(old, nroot); err != nil { + return nil, err + } + + return nil, nil +} + +func checkProofSubmissionsAtH(vmctx types.VMContext, self *StoragePowerState, height uint64) aerrors.ActorError { + bucketID := height % build.ProvingPeriodDuration + + buckets, eerr := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingBuckets) + if eerr != nil { + return aerrors.HandleExternalError(eerr, "loading proving buckets amt") + } + + var bucket cid.Cid + err := buckets.Get(bucketID, &bucket) + switch err.(type) { + case *amt.ErrNotFound: + return nil // nothing to do + case nil: + default: + return aerrors.HandleExternalError(err, "getting proving bucket") + } + + bhamt, err := hamt.LoadNode(vmctx.Context(), vmctx.Ipld(), bucket) + if err != nil { + return aerrors.HandleExternalError(err, "failed to load proving bucket") + } + + err = bhamt.ForEach(vmctx.Context(), func(k string, val interface{}) error { + _, span := trace.StartSpan(vmctx.Context(), "StoragePowerActor.CheckProofSubmissions.loop") + defer span.End() + + maddr, err := address.NewFromBytes([]byte(k)) + if err != nil { + return aerrors.Escalate(err, "parsing miner address") + } + + span.AddAttributes(trace.StringAttribute("miner", maddr.String())) + + params, err := SerializeParams(&CheckMinerParams{NetworkPower: self.TotalStorage}) + if err != nil { + return err + } + + ret, err := vmctx.Send(maddr, MAMethods.CheckMiner, types.NewInt(0), params) + if err != nil { + return err + } + + if len(ret) == 0 { + return nil // miner is fine + } + + var power types.BigInt + if err := power.UnmarshalCBOR(bytes.NewReader(ret)); err != nil { + return xerrors.Errorf("unmarshaling CheckMiner response (%x): %w", ret, err) + } + + if power.GreaterThan(types.NewInt(0)) { + log.Warnf("slashing miner %s for missed PoSt (%s B, H: %d, Bucket: %d)", maddr, power, height, bucketID) + + self.TotalStorage = types.BigSub(self.TotalStorage, power) + } + return nil + }) + + if err != nil { + return aerrors.HandleExternalError(err, "iterating miners in proving bucket") + } + + return nil +} + func MinerSetHas(vmctx types.VMContext, rcid cid.Cid, maddr address.Address) (bool, aerrors.ActorError) { nd, err := hamt.LoadNode(vmctx.Context(), vmctx.Ipld(), rcid) if err != nil { @@ -523,3 +765,33 @@ func MinerSetRemove(ctx context.Context, vmctx types.VMContext, rcid cid.Cid, ma return c, nil } + +type cbgNull struct{} + +var cborNull = &cbgNull{} + +func (cbgNull) MarshalCBOR(w io.Writer) error { + n, err := w.Write(cbg.CborNull) + if err != nil { + return err + } + if n != 1 { + return xerrors.New("expected to write 1 byte") + } + return nil +} + +func (cbgNull) UnmarshalCBOR(r io.Reader) error { + b := [1]byte{} + n, err := r.Read(b[:]) + if err != nil { + return err + } + if n != 1 { + return xerrors.New("expected 1 byte") + } + if !bytes.Equal(b[:], cbg.CborNull) { + return xerrors.New("expected cbor null") + } + return nil +} diff --git a/chain/actors/actor_storagepower_test.go b/chain/actors/actor_storagepower_test.go index b11edc71c..b5330be5a 100644 --- a/chain/actors/actor_storagepower_test.go +++ b/chain/actors/actor_storagepower_test.go @@ -52,8 +52,8 @@ func TestStorageMarketCreateAndSlashMiner(t *testing.T) { } { - ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.IsMiner, - &IsMinerParam{Addr: minerAddr}) + ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.IsValidMiner, + &IsValidMinerParam{Addr: minerAddr}) ApplyOK(t, ret) var output bool @@ -63,7 +63,7 @@ func TestStorageMarketCreateAndSlashMiner(t *testing.T) { } if !output { - t.Fatalf("%s is miner but IsMiner call returned false", minerAddr) + t.Fatalf("%s is miner but IsValidMiner call returned false", minerAddr) } } @@ -108,7 +108,7 @@ func TestStorageMarketCreateAndSlashMiner(t *testing.T) { } { - ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.IsMiner, &IsMinerParam{minerAddr}) + ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.IsValidMiner, &IsValidMinerParam{minerAddr}) ApplyOK(t, ret) assert.Equal(t, ret.Return, cbg.CborBoolFalse) } diff --git a/chain/actors/cbor_gen.go b/chain/actors/cbor_gen.go index fbb289fd2..53de185e1 100644 --- a/chain/actors/cbor_gen.go +++ b/chain/actors/cbor_gen.go @@ -268,13 +268,13 @@ func (t *StorageMinerActorState) MarshalCBOR(w io.Writer) error { return err } - // t.t.SlashedAt (types.BigInt) (struct) - if err := t.SlashedAt.MarshalCBOR(w); err != nil { + // t.t.Active (bool) (bool) + if err := cbg.WriteBool(w, t.Active); err != nil { return err } - // t.t.OwedStorageCollateral (types.BigInt) (struct) - if err := t.OwedStorageCollateral.MarshalCBOR(w); err != nil { + // t.t.SlashedAt (uint64) (uint64) + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.SlashedAt))); err != nil { return err } @@ -425,24 +425,33 @@ func (t *StorageMinerActorState) UnmarshalCBOR(r io.Reader) error { } } - // t.t.SlashedAt (types.BigInt) (struct) - - { - - if err := t.SlashedAt.UnmarshalCBOR(br); err != nil { - return err - } + // t.t.Active (bool) (bool) + maj, extra, err = cbg.CborReadHeader(br) + if err != nil { + return err } - // t.t.OwedStorageCollateral (types.BigInt) (struct) - - { - - if err := t.OwedStorageCollateral.UnmarshalCBOR(br); err != nil { - return err - } - + if maj != cbg.MajOther { + return fmt.Errorf("booleans must be major type 7") } + switch extra { + case 20: + t.Active = false + case 21: + t.Active = true + default: + return fmt.Errorf("booleans are either major type 7, value 20 or 21 (got %d)", extra) + } + // t.t.SlashedAt (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.SlashedAt = uint64(extra) // t.t.ProvingPeriodEnd (uint64) (uint64) maj, extra, err = cbg.CborReadHeader(br) @@ -2408,7 +2417,7 @@ func (t *StoragePowerState) MarshalCBOR(w io.Writer) error { _, err := w.Write(cbg.CborNull) return err } - if _, err := w.Write([]byte{131}); err != nil { + if _, err := w.Write([]byte{133}); err != nil { return err } @@ -2418,11 +2427,22 @@ func (t *StoragePowerState) MarshalCBOR(w io.Writer) error { return xerrors.Errorf("failed to write cid field t.Miners: %w", err) } + // t.t.ProvingBuckets (cid.Cid) (struct) + + if err := cbg.WriteCid(w, t.ProvingBuckets); err != nil { + return xerrors.Errorf("failed to write cid field t.ProvingBuckets: %w", err) + } + // t.t.MinerCount (uint64) (uint64) if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.MinerCount))); err != nil { return err } + // t.t.LastMinerCheck (uint64) (uint64) + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.LastMinerCheck))); err != nil { + return err + } + // t.t.TotalStorage (types.BigInt) (struct) if err := t.TotalStorage.MarshalCBOR(w); err != nil { return err @@ -2441,7 +2461,7 @@ func (t *StoragePowerState) UnmarshalCBOR(r io.Reader) error { return fmt.Errorf("cbor input should be of type array") } - if extra != 3 { + if extra != 5 { return fmt.Errorf("cbor input had wrong number of fields") } @@ -2456,6 +2476,18 @@ func (t *StoragePowerState) UnmarshalCBOR(r io.Reader) error { t.Miners = c + } + // t.t.ProvingBuckets (cid.Cid) (struct) + + { + + c, err := cbg.ReadCid(br) + if err != nil { + return xerrors.Errorf("failed to read cid field t.ProvingBuckets: %w", err) + } + + t.ProvingBuckets = c + } // t.t.MinerCount (uint64) (uint64) @@ -2467,6 +2499,16 @@ func (t *StoragePowerState) UnmarshalCBOR(r io.Reader) error { return fmt.Errorf("wrong type for uint64 field") } t.MinerCount = uint64(extra) + // t.t.LastMinerCheck (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.LastMinerCheck = uint64(extra) // t.t.TotalStorage (types.BigInt) (struct) { @@ -2569,7 +2611,7 @@ func (t *CreateStorageMinerParams) UnmarshalCBOR(r io.Reader) error { return nil } -func (t *IsMinerParam) MarshalCBOR(w io.Writer) error { +func (t *IsValidMinerParam) MarshalCBOR(w io.Writer) error { if t == nil { _, err := w.Write(cbg.CborNull) return err @@ -2585,7 +2627,7 @@ func (t *IsMinerParam) MarshalCBOR(w io.Writer) error { return nil } -func (t *IsMinerParam) UnmarshalCBOR(r io.Reader) error { +func (t *IsValidMinerParam) UnmarshalCBOR(r io.Reader) error { br := cbg.GetPeeker(r) maj, extra, err := cbg.CborReadHeader(br) @@ -2660,7 +2702,7 @@ func (t *UpdateStorageParams) MarshalCBOR(w io.Writer) error { _, err := w.Write(cbg.CborNull) return err } - if _, err := w.Write([]byte{129}); err != nil { + if _, err := w.Write([]byte{131}); err != nil { return err } @@ -2668,6 +2710,16 @@ func (t *UpdateStorageParams) MarshalCBOR(w io.Writer) error { if err := t.Delta.MarshalCBOR(w); err != nil { return err } + + // t.t.NextProvingPeriodEnd (uint64) (uint64) + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.NextProvingPeriodEnd))); err != nil { + return err + } + + // t.t.PreviousProvingPeriodEnd (uint64) (uint64) + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.PreviousProvingPeriodEnd))); err != nil { + return err + } return nil } @@ -2682,7 +2734,7 @@ func (t *UpdateStorageParams) UnmarshalCBOR(r io.Reader) error { return fmt.Errorf("cbor input should be of type array") } - if extra != 1 { + if extra != 3 { return fmt.Errorf("cbor input had wrong number of fields") } @@ -2695,6 +2747,26 @@ func (t *UpdateStorageParams) UnmarshalCBOR(r io.Reader) error { } } + // t.t.NextProvingPeriodEnd (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.NextProvingPeriodEnd = uint64(extra) + // t.t.PreviousProvingPeriodEnd (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.PreviousProvingPeriodEnd = uint64(extra) return nil } @@ -3861,3 +3933,46 @@ func (t *SectorProveCommitInfo) UnmarshalCBOR(r io.Reader) error { return nil } + +func (t *CheckMinerParams) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + if _, err := w.Write([]byte{129}); err != nil { + return err + } + + // t.t.NetworkPower (types.BigInt) (struct) + if err := t.NetworkPower.MarshalCBOR(w); err != nil { + return err + } + return nil +} + +func (t *CheckMinerParams) 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 != 1 { + return fmt.Errorf("cbor input had wrong number of fields") + } + + // t.t.NetworkPower (types.BigInt) (struct) + + { + + if err := t.NetworkPower.UnmarshalCBOR(br); err != nil { + return err + } + + } + return nil +} diff --git a/chain/blocksync/cbor_gen.go b/chain/blocksync/cbor_gen.go index 4af61a490..579b3f857 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" - "github.com/ipfs/go-cid" + cid "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" xerrors "golang.org/x/xerrors" ) diff --git a/chain/deals/cbor_gen.go b/chain/deals/cbor_gen.go index ba11e69ee..1b10f1772 100644 --- a/chain/deals/cbor_gen.go +++ b/chain/deals/cbor_gen.go @@ -861,7 +861,7 @@ func (t *StorageDataTransferVoucher) MarshalCBOR(w io.Writer) error { return err } - // t.t.Proposal (cid.Cid) + // t.t.Proposal (cid.Cid) (struct) if err := cbg.WriteCid(w, t.Proposal); err != nil { return xerrors.Errorf("failed to write cid field t.Proposal: %w", err) @@ -885,7 +885,7 @@ func (t *StorageDataTransferVoucher) UnmarshalCBOR(r io.Reader) error { return fmt.Errorf("cbor input had wrong number of fields") } - // t.t.Proposal (cid.Cid) + // t.t.Proposal (cid.Cid) (struct) { diff --git a/chain/gen/mining.go b/chain/gen/mining.go index 5977fc66a..5cfc3a694 100644 --- a/chain/gen/mining.go +++ b/chain/gen/mining.go @@ -7,7 +7,6 @@ import ( bls "github.com/filecoin-project/go-bls-sigs" cid "github.com/ipfs/go-cid" hamt "github.com/ipfs/go-hamt-ipld" - "github.com/pkg/errors" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" @@ -22,7 +21,7 @@ import ( 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) { st, recpts, err := sm.TipSetState(ctx, parents) if err != nil { - return nil, errors.Wrap(err, "failed to load tipset state") + return nil, xerrors.Errorf("failed to load tipset state: %w", err) } height := parents.Height() + uint64(len(tickets)) diff --git a/chain/gen/utils.go b/chain/gen/utils.go index 303878c94..7bc830e7e 100644 --- a/chain/gen/utils.go +++ b/chain/gen/utils.go @@ -152,9 +152,16 @@ func SetupStoragePowerActor(bs bstore.Blockstore) (*types.Actor, error) { return nil, err } + blks := amt.WrapBlockstore(bs) + emptyamt, err := amt.FromArray(blks, nil) + if err != nil { + return nil, xerrors.Errorf("amt build failed: %w", err) + } + sms := &actors.StoragePowerState{ - Miners: emptyhamt, - TotalStorage: types.NewInt(0), + Miners: emptyhamt, + ProvingBuckets: emptyamt, + TotalStorage: types.NewInt(0), } stcid, err := cst.Put(context.TODO(), sms) diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index e8c13fd92..5b6eafb81 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -197,6 +197,30 @@ func (sm *StateManager) computeTipSetState(ctx context.Context, blks []*types.Bl } } + // TODO: this nonce-getting is a ting bit ugly + spa, err := vmi.StateTree().GetActor(actors.StoragePowerAddress) + if err != nil { + return cid.Undef, cid.Undef, err + } + + // TODO: cron actor + ret, err := vmi.ApplyMessage(ctx, &types.Message{ + To: actors.StoragePowerAddress, + From: actors.StoragePowerAddress, + Nonce: spa.Nonce, + Value: types.NewInt(0), + GasPrice: types.NewInt(0), + GasLimit: types.NewInt(1 << 30), // Make super sure this is never too little + Method: actors.SPAMethods.CheckProofSubmissions, + Params: nil, + }) + if err != nil { + return cid.Undef, cid.Undef, err + } + if ret.ExitCode != 0 { + return cid.Undef, cid.Undef, xerrors.Errorf("CheckProofSubmissions exit was non-zero: %d", ret.ExitCode) + } + bs := amt.WrapBlockstore(sm.cs.Blockstore()) rectroot, err := amt.FromArray(bs, receipts) if err != nil { diff --git a/chain/sync.go b/chain/sync.go index 3ff5d7ae7..89823c7f0 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -1,6 +1,7 @@ package chain import ( + "bytes" "context" "errors" "fmt" @@ -418,7 +419,7 @@ func (syncer *Syncer) ValidateTipSet(ctx context.Context, fts *store.FullTipSet) func (syncer *Syncer) minerIsValid(ctx context.Context, maddr address.Address, baseTs *types.TipSet) error { var err error - enc, err := actors.SerializeParams(&actors.IsMinerParam{Addr: maddr}) + enc, err := actors.SerializeParams(&actors.IsValidMinerParam{Addr: maddr}) if err != nil { return err } @@ -426,7 +427,7 @@ func (syncer *Syncer) minerIsValid(ctx context.Context, maddr address.Address, b ret, err := syncer.sm.Call(ctx, &types.Message{ To: actors.StoragePowerAddress, From: maddr, - Method: actors.SPAMethods.IsMiner, + Method: actors.SPAMethods.IsValidMiner, Params: enc, }, baseTs) if err != nil { @@ -434,10 +435,12 @@ func (syncer *Syncer) minerIsValid(ctx context.Context, maddr address.Address, b } if ret.ExitCode != 0 { - return xerrors.Errorf("StorageMarket.IsMiner check failed (exit code %d)", ret.ExitCode) + return xerrors.Errorf("StorageMarket.IsValidMiner check failed (exit code %d)", ret.ExitCode) } - // TODO: ensure the miner is currently not late on their PoSt submission (this hasnt landed in the spec yet) + if !bytes.Equal(ret.Return, cbg.CborBoolTrue) { + return xerrors.New("miner isn't valid") + } return nil } diff --git a/chain/types/bigint.go b/chain/types/bigint.go index b958ecf26..77f6f4643 100644 --- a/chain/types/bigint.go +++ b/chain/types/bigint.go @@ -92,11 +92,16 @@ func (bi BigInt) LessThan(o BigInt) bool { return BigCmp(bi, o) < 0 } -// LessThan returns true if bi > o +// GreaterThan returns true if bi > o func (bi BigInt) GreaterThan(o BigInt) bool { return BigCmp(bi, o) > 0 } +// Equals returns true if bi == o +func (bi BigInt) Equals(o BigInt) bool { + return BigCmp(bi, o) == 0 +} + func (bi *BigInt) MarshalJSON() ([]byte, error) { return json.Marshal(bi.String()) } @@ -186,7 +191,7 @@ func (bi *BigInt) UnmarshalCBOR(br io.Reader) error { } if maj != cbg.MajByteString { - return fmt.Errorf("cbor input for fil big int was not a byte string") + return fmt.Errorf("cbor input for fil big int was not a byte string (%x)", maj) } if extra == 0 { diff --git a/chain/types/cbor_gen.go b/chain/types/cbor_gen.go index 1ebe94a13..fa3cddaf4 100644 --- a/chain/types/cbor_gen.go +++ b/chain/types/cbor_gen.go @@ -5,7 +5,7 @@ import ( "io" "math" - "github.com/ipfs/go-cid" + cid "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" xerrors "golang.org/x/xerrors" ) diff --git a/gen/main.go b/gen/main.go index 4ad37060e..c74a10f0f 100644 --- a/gen/main.go +++ b/gen/main.go @@ -110,7 +110,7 @@ func main() { actors.PaymentInfo{}, actors.StoragePowerState{}, actors.CreateStorageMinerParams{}, - actors.IsMinerParam{}, + actors.IsValidMinerParam{}, actors.PowerLookupParams{}, actors.UpdateStorageParams{}, actors.ArbitrateConsensusFaultParams{}, @@ -128,6 +128,7 @@ func main() { actors.OnChainDeal{}, actors.ComputeDataCommitmentParams{}, actors.SectorProveCommitInfo{}, + actors.CheckMinerParams{}, ) if err != nil { fmt.Println(err) diff --git a/lotuspond/front/src/State.js b/lotuspond/front/src/State.js index 93252505d..0af240c2e 100644 --- a/lotuspond/front/src/State.js +++ b/lotuspond/front/src/State.js @@ -74,18 +74,26 @@ class PowerState extends React.Component { constructor(props) { super(props) - this.state = {actors: []} + this.state = {actors: [], state: {State: {}}} } async componentDidMount() { const tipset = await this.props.client.call("Filecoin.ChainHead", []) // TODO: from props const actors = await this.props.client.call("Filecoin.StateListMiners", [tipset]) - this.setState({actors: actors}) + const state = await this.props.client.call('Filecoin.StateReadState', [this.props.actor, tipset]) + + this.setState({actors, state}) } render() { - return this.state.actors.sort((a, b) => (Number(a.substr(1)) > Number(b.substr(1)))) - .map(addr =>
) + return
+
+
Total Power: {this.state.state.State.TotalStorage}
+
+
---
+
{this.state.actors.sort((a, b) => (Number(a.substr(1)) > Number(b.substr(1)))) + .map(addr =>
)}
+
} } @@ -174,6 +182,7 @@ class MinerState extends React.Component {
Sector Size: {this.state.sectorSize/1024} KiB
Power: {state.Power} ({state.Power/this.state.networkPower*100}%)
Proving Period End: {state.ProvingPeriodEnd}
+
Slashed: {state.SlashedAt === 0 ? "NO" : state.SlashedAt}
----
Sectors:
diff --git a/peermgr/peermgr.go b/peermgr/peermgr.go index 4ad64a5a1..60714f219 100644 --- a/peermgr/peermgr.go +++ b/peermgr/peermgr.go @@ -89,7 +89,7 @@ func (pmgr *PeerMgr) Run(ctx context.Context) { if pcount < pmgr.minFilPeers { pmgr.expandPeers() } else if pcount > pmgr.maxFilPeers { - log.Infof("peer count about threshold: %d > %d", pcount, pmgr.maxFilPeers) + log.Debug("peer count about threshold: %d > %d", pcount, pmgr.maxFilPeers) } } } diff --git a/scripts/deploy-devnet.sh b/scripts/deploy-devnet.sh index ac5b800d1..dd4409b01 100755 --- a/scripts/deploy-devnet.sh +++ b/scripts/deploy-devnet.sh @@ -71,6 +71,8 @@ scp scripts/bootstrap.toml "${GENESIS_HOST}:.lotus/config.toml" & ssh < "${GENPATH}/wallet.key" $GENESIS_HOST '/usr/local/bin/lotus wallet import' & wait +ssh $GENESIS_HOST "echo -e '[Metrics]\nNickname=\"Boot-genesis\"' >> .lotus/config.toml" + ssh $GENESIS_HOST 'systemctl restart lotus-daemon' log 'Starting genesis mining' @@ -109,9 +111,24 @@ do ssh "$host" 'systemctl start lotus-daemon' scp scripts/bootstrap.toml "${host}:.lotus/config.toml" + ssh "$host" "echo -e '[Metrics]\nNickname=\"Boot-$host\"' >> .lotus/config.toml" ssh "$host" 'systemctl restart lotus-daemon' log 'Extracting addr info' ssh "$host" 'lotus net listen' | grep -v '/10' | grep -v '/127' >> build/bootstrap/bootstrappers.pi done + +log 'Updating genesis node with bootstrapable binaries' + +ssh "$GENESIS_HOST" 'systemctl stop lotus-daemon' & +ssh "$GENESIS_HOST" 'systemctl stop lotus-storage-miner' & +wait + +scp -C lotus "${GENESIS_HOST}":/usr/local/bin/lotus & +scp -C lotus-storage-miner "${GENESIS_HOST}":/usr/local/bin/lotus-storage-miner & +wait + +ssh "$GENESIS_HOST" 'systemctl start lotus-daemon' & +ssh "$GENESIS_HOST" 'systemctl start lotus-storage-miner' & +wait