Merge branch 'master' into feat/fvm

This commit is contained in:
Aayush 2022-02-28 22:17:20 -05:00
commit dd91857bf6
62 changed files with 2503 additions and 186 deletions

View File

@ -1,5 +1,106 @@
# Lotus changelog
# 1.14.2 / 2022-02-24
This is an **optional** release of lotus, that's had a couple more improvements w.r.t Snap experience for storage providers in preparation of the[upcoming OhSnap upgrade](https://github.com/filecoin-project/community/discussions/74?sort=new#discussioncomment-1922550).
Note that the network is STILL scheduled to upgrade to v15 on March 1st at 2022-03-01T15:00:00Z. All node operators, including storage providers, must upgrade to at least Lotus v1.14.0 before that time. Storage providers must update their daemons, miners, and worker(s).
Wanna know how to Snap your deal? Check [this](https://github.com/filecoin-project/lotus/discussions/8141) out!
## Bug Fixes
- fix lotus-bench for sealing jobs (#8173)
- fix:sealing:really-do-it flag for abort upgrade (#8181)
- fix:proving:post check sector handles snap deals replica faults (#8177)
- fix: sealing: missing file type (#8180)
## Others
- Retract force-pushed v1.14.0 to work around stale gomod caches (#8159): We originally tagged v1.14.0 off the wrong
commit and fixed that by a force push, in which is a really bad practise since it messes up the go mod. Therefore,
we want to retract it and users may use v1.14.1&^.
## Contributors
| Contributor | Commits | Lines ± | Files Changed |
|-------------|---------|---------|---------------|
| @zenground0 | 2 | +73/-58 | 12 |
| @eben.xie | 1 | +7/-0 | 1 |
| @jennijuju | 1 | +4/-0 | 1 |
| @jennijuju | 1 | +2/-1 | 1 |
| @ribasushi | 1 | +2/-0 | 1 |
# 1.14.1 / 2022-02-18
This is an **optional** release of lotus, that fixes the incorrect *comment* of network v15 OhSnap upgrade **date**. Note the actual upgrade epoch in [v1.14.0](https://github.com/filecoin-project/lotus/releases/tag/v1.14.0) was correct.
# 1.14.0 / 2022-02-17
This is a MANDATORY release of Lotus that introduces [Filecoin network v15,
codenamed the OhSnap upgrade](https://github.com/filecoin-project/community/discussions/74?sort=new#discussioncomment-1922550).
The network is scheduled to upgrade to v15 on March 1st at 2022-03-01T15:00:00Z. All node operators, including storage providers, must upgrade to this release (or a later release) before that time. Storage providers must update their daemons, miners, and worker(s).
The OhSnap upgrade introduces the following FIPs, delivered in [actors v7](https://github.com/filecoin-project/specs-actors/releases/tag/v7.0.0):
- [FIP-0019 Snap Deals](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0019.md)
- [FIP-0028 Remove Datacap from Verified clients](https://github.com/filecoin-project/FIPs/pull/226)
It is recommended that storage providers download the new params before updating their node, miner, and workers. To do so:
- Download Lotus v1.14.0 or later
- run `make lotus-shed`
- run `./lotus-shed fetch-params` with the appropriate `proving-params` flag
- Upgrade the Lotus daemon and miner **when the previous step is complete**
All node operators, including storage providers, should be aware that a pre-migration will begin at 2022-03-01T13:30:00Z (90 minutes before the real upgrade). The pre-migration will take between 20 and 50 minutes, depending on hardware specs. During this time, expect slower block validation times, increased CPU and memory usage, and longer delays for API queries.
## New Features and Changes
- Integrate actor v7-rc1:
- Integrate v7 actors ([#7617](https://github.com/filecoin-project/lotus/pull/7617))
- feat: state: Fast migration for v15 ([#7933](https://github.com/filecoin-project/lotus/pull/7933))
- fix: blockstore: Add missing locks to autobatch::Get() [#7939](https://github.com/filecoin-project/lotus/pull/7939))
- correctness fixes for the autobatch blockstore ([#7940](https://github.com/filecoin-project/lotus/pull/7940))
- Implement and support [FIP-0019 Snap Deals](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0019.md)
- chore: deps: Integrate proof v11.0.0 ([#7923](https://github.com/filecoin-project/lotus/pull/7923))
- Snap Deals Lotus Integration: FSM Posting and integration test ([#7810](https://github.com/filecoin-project/lotus/pull/7810))
- Feat/sector storage unseal ([#7730](https://github.com/filecoin-project/lotus/pull/7730))
- Feat/snap deals storage ([#7615](https://github.com/filecoin-project/lotus/pull/7615))
- fix: sealing: Add more deal expiration checks during PRU pipeline ([#7871](https://github.com/filecoin-project/lotus/pull/7871))
- chore: deps: Update go-paramfetch ([#7917](https://github.com/filecoin-project/lotus/pull/7917))
- feat: #7880 gas: add gas charge for VerifyReplicaUpdate ([#7897](https://github.com/filecoin-project/lotus/pull/7897))
- enhancement: sectors: disable existing cc upgrade path 2 days before the upgrade epoch ([#7900](https://github.com/filecoin-project/lotus/pull/7900))
## Improvements
- updating to new datastore/blockstore code with contexts ([#7646](https://github.com/filecoin-project/lotus/pull/7646))
- reorder transfer checks so as to ensure sending 2B FIL to yourself fails if you don't have that amount ([#7637](https://github.com/filecoin-project/lotus/pull/7637))
- VM: Circ supply should be constant per epoch ([#7811](https://github.com/filecoin-project/lotus/pull/7811))
## Bug Fixes
- Fix: state: circsuypply calc around null blocks ([#7890](https://github.com/filecoin-project/lotus/pull/7890))
- Mempool msg selection should respect block message limits ([#7321](https://github.com/filecoin-project/lotus/pull/7321))
SplitStore: supress compaction near upgrades ([#7734](https://github.com/filecoin-project/lotus/pull/7734))
## Others
- chore: create pull_request_template.md ([#7726](https://github.com/filecoin-project/lotus/pull/7726))
## Contributors
| Contributor | Commits | Lines ± | Files Changed |
|-------------|---------|---------|---------------|
| Aayush Rajasekaran | 41 | +5538/-1205 | 189 |
| zenground0 | 11 | +3316/-524 | 124 |
| Jennifer Wang | 29 | +714/-599 | 68 |
| ZenGround0 | 3 | +263/-25 | 11 |
| c r | 2 | +198/-30 | 6 |
| vyzo | 4 | +189/-7 | 7 |
| Aayush | 11 | +146/-48 | 49 |
| web3-bot | 10 | +99/-17 | 10 |
| Steven Allen | 1 | +55/-37 | 1 |
| Jiaying Wang | 5 | +30/-8 | 5 |
| Jakub Sztandera | 2 | +8/-3 | 3 |
| Łukasz Magiera | 1 | +3/-3 | 2 |
| Travis Person | 1 | +2/-2 | 2 |
| Rod Vagg | 1 | +2/-2 | 2 |
# v1.13.2 / 2022-01-09
Lotus v1.13.2 is a *highly recommended* feature release with remarkable retrieval improvements, new features like

View File

@ -45,8 +45,9 @@ type Gateway interface {
GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *MessageSendSpec, tsk types.TipSetKey) (*types.Message, error)
MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error)
MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error)
MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error)
MsigGetPending(context.Context, address.Address, types.TipSetKey) ([]*MsigTransaction, error)
MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error)
MsigGetVestingSchedule(ctx context.Context, addr address.Address, tsk types.TipSetKey) (MsigVesting, error)
StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error)
StateDealProviderCollateralBounds(ctx context.Context, size abi.PaddedPieceSize, verified bool, tsk types.TipSetKey) (DealCollateralBounds, error)
StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error)

View File

@ -256,7 +256,7 @@ type StorageMiner interface {
// the path specified when calling CreateBackup is within the base path
CreateBackup(ctx context.Context, fpath string) error //perm:admin
CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef, expensive bool) (map[abi.SectorNumber]string, error) //perm:admin
CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef, update []bool, expensive bool) (map[abi.SectorNumber]string, error) //perm:admin
ComputeProof(ctx context.Context, ssi []builtin.ExtendedSectorInfo, rand abi.PoStRandomness, poStEpoch abi.ChainEpoch, nv abinetwork.Version) ([]builtin.PoStProof, error) //perm:read
}

View File

@ -516,6 +516,8 @@ type GatewayStruct struct {
MsigGetVested func(p0 context.Context, p1 address.Address, p2 types.TipSetKey, p3 types.TipSetKey) (types.BigInt, error) ``
MsigGetVestingSchedule func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MsigVesting, error) ``
StateAccountKey func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) ``
StateDealProviderCollateralBounds func(p0 context.Context, p1 abi.PaddedPieceSize, p2 bool, p3 types.TipSetKey) (DealCollateralBounds, error) ``
@ -625,7 +627,7 @@ type StorageMinerStruct struct {
ActorSectorSize func(p0 context.Context, p1 address.Address) (abi.SectorSize, error) `perm:"read"`
CheckProvable func(p0 context.Context, p1 abi.RegisteredPoStProof, p2 []storage.SectorRef, p3 bool) (map[abi.SectorNumber]string, error) `perm:"admin"`
CheckProvable func(p0 context.Context, p1 abi.RegisteredPoStProof, p2 []storage.SectorRef, p3 []bool, p4 bool) (map[abi.SectorNumber]string, error) `perm:"admin"`
ComputeProof func(p0 context.Context, p1 []builtin.ExtendedSectorInfo, p2 abi.PoStRandomness, p3 abi.ChainEpoch, p4 abinetwork.Version) ([]builtin.PoStProof, error) `perm:"read"`
@ -3285,6 +3287,17 @@ func (s *GatewayStub) MsigGetVested(p0 context.Context, p1 address.Address, p2 t
return *new(types.BigInt), ErrNotSupported
}
func (s *GatewayStruct) MsigGetVestingSchedule(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MsigVesting, error) {
if s.Internal.MsigGetVestingSchedule == nil {
return *new(MsigVesting), ErrNotSupported
}
return s.Internal.MsigGetVestingSchedule(p0, p1, p2)
}
func (s *GatewayStub) MsigGetVestingSchedule(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MsigVesting, error) {
return *new(MsigVesting), ErrNotSupported
}
func (s *GatewayStruct) StateAccountKey(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) {
if s.Internal.StateAccountKey == nil {
return *new(address.Address), ErrNotSupported
@ -3747,14 +3760,14 @@ func (s *StorageMinerStub) ActorSectorSize(p0 context.Context, p1 address.Addres
return *new(abi.SectorSize), ErrNotSupported
}
func (s *StorageMinerStruct) CheckProvable(p0 context.Context, p1 abi.RegisteredPoStProof, p2 []storage.SectorRef, p3 bool) (map[abi.SectorNumber]string, error) {
func (s *StorageMinerStruct) CheckProvable(p0 context.Context, p1 abi.RegisteredPoStProof, p2 []storage.SectorRef, p3 []bool, p4 bool) (map[abi.SectorNumber]string, error) {
if s.Internal.CheckProvable == nil {
return *new(map[abi.SectorNumber]string), ErrNotSupported
}
return s.Internal.CheckProvable(p0, p1, p2, p3)
return s.Internal.CheckProvable(p0, p1, p2, p3, p4)
}
func (s *StorageMinerStub) CheckProvable(p0 context.Context, p1 abi.RegisteredPoStProof, p2 []storage.SectorRef, p3 bool) (map[abi.SectorNumber]string, error) {
func (s *StorageMinerStub) CheckProvable(p0 context.Context, p1 abi.RegisteredPoStProof, p2 []storage.SectorRef, p3 []bool, p4 bool) (map[abi.SectorNumber]string, error) {
return *new(map[abi.SectorNumber]string), ErrNotSupported
}

View File

@ -57,7 +57,7 @@ var (
FullAPIVersion0 = newVer(1, 5, 0)
FullAPIVersion1 = newVer(2, 2, 0)
MinerAPIVersion0 = newVer(1, 3, 0)
MinerAPIVersion0 = newVer(1, 4, 0)
WorkerAPIVersion0 = newVer(1, 5, 0)
)

View File

@ -1,6 +1,7 @@
package splitstore
import (
"errors"
"runtime"
"sync/atomic"
@ -10,13 +11,12 @@ import (
cid "github.com/ipfs/go-cid"
)
var EnableReification = false
var (
errReifyLimit = errors.New("reification limit reached")
ReifyLimit = 16384
)
func (s *SplitStore) reifyColdObject(c cid.Cid) {
if !EnableReification {
return
}
if !s.isWarm() {
return
}
@ -104,12 +104,18 @@ func (s *SplitStore) doReify(c cid.Cid) {
s.txnLk.RLock()
defer s.txnLk.RUnlock()
count := 0
err := s.walkObjectIncomplete(c, newTmpVisitor(),
func(c cid.Cid) error {
if isUnitaryObject(c) {
return errStopWalk
}
count++
if count > ReifyLimit {
return errReifyLimit
}
s.reifyMx.Lock()
_, inProgress := s.reifyInProgress[c]
if !inProgress {
@ -150,6 +156,11 @@ func (s *SplitStore) doReify(c cid.Cid) {
})
if err != nil {
if xerrors.Is(err, errReifyLimit) {
log.Debug("reification aborted; reify limit reached")
return
}
log.Warnf("error walking cold object for reification (cid: %s): %s", c, err)
return
}

View File

@ -495,8 +495,102 @@ func testSplitStoreReification(t *testing.T, f func(context.Context, blockstore.
}
}
func testSplitStoreReificationLimit(t *testing.T, f func(context.Context, blockstore.Blockstore, cid.Cid) error) {
ds := dssync.MutexWrap(datastore.NewMapDatastore())
hot := newMockStore()
cold := newMockStore()
mkRandomBlock := func() blocks.Block {
data := make([]byte, 128)
_, err := rand.Read(data)
if err != nil {
t.Fatal(err)
}
return blocks.NewBlock(data)
}
block1 := mkRandomBlock()
block2 := mkRandomBlock()
block3 := mkRandomBlock()
hdr := mock.MkBlock(nil, 0, 0)
hdr.Messages = block1.Cid()
hdr.ParentMessageReceipts = block2.Cid()
hdr.ParentStateRoot = block3.Cid()
block4, err := hdr.ToStorageBlock()
if err != nil {
t.Fatal(err)
}
allBlocks := []blocks.Block{block1, block2, block3, block4}
for _, blk := range allBlocks {
err := cold.Put(context.Background(), blk)
if err != nil {
t.Fatal(err)
}
}
path, err := ioutil.TempDir("", "splitstore.*")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
_ = os.RemoveAll(path)
})
ss, err := Open(path, ds, hot, cold, &Config{MarkSetType: "map"})
if err != nil {
t.Fatal(err)
}
defer ss.Close() //nolint
ss.warmupEpoch = 1
go ss.reifyOrchestrator()
waitForReification := func() {
for {
ss.reifyMx.Lock()
ready := len(ss.reifyPend) == 0 && len(ss.reifyInProgress) == 0
ss.reifyMx.Unlock()
if ready {
return
}
time.Sleep(time.Millisecond)
}
}
// do a hot access -- nothing should be reified as the limit should be exceeded
oldReifyLimit := ReifyLimit
ReifyLimit = 2
t.Cleanup(func() {
ReifyLimit = oldReifyLimit
})
err = f(blockstore.WithHotView(context.Background()), ss, block4.Cid())
if err != nil {
t.Fatal(err)
}
waitForReification()
for _, blk := range allBlocks {
has, err := hot.Has(context.Background(), blk.Cid())
if err != nil {
t.Fatal(err)
}
if has {
t.Fatal("block unexpectedly reified")
}
}
}
func TestSplitStoreReification(t *testing.T) {
EnableReification = true
t.Log("test reification with Has")
testSplitStoreReification(t, func(ctx context.Context, s blockstore.Blockstore, c cid.Cid) error {
_, err := s.Has(ctx, c)
@ -516,6 +610,11 @@ func TestSplitStoreReification(t *testing.T) {
testSplitStoreReification(t, func(ctx context.Context, s blockstore.Blockstore, c cid.Cid) error {
return s.View(ctx, c, func(_ []byte) error { return nil })
})
t.Log("test reification limit")
testSplitStoreReificationLimit(t, func(ctx context.Context, s blockstore.Blockstore, c cid.Cid) error {
_, err := s.Has(ctx, c)
return err
})
}
type mockChain struct {

View File

@ -26,6 +26,10 @@ type tmpVisitor struct {
var _ ObjectVisitor = (*tmpVisitor)(nil)
func (v *tmpVisitor) Visit(c cid.Cid) (bool, error) {
if isUnitaryObject(c) {
return false, nil
}
return v.set.Visit(c), nil
}
@ -45,6 +49,10 @@ func newConcurrentVisitor() *concurrentVisitor {
}
func (v *concurrentVisitor) Visit(c cid.Cid) (bool, error) {
if isUnitaryObject(c) {
return false, nil
}
v.mx.Lock()
defer v.mx.Unlock()

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -67,7 +67,8 @@ const UpgradeHyperdriveHeight = 892800
// 2021-10-26T13:30:00Z
const UpgradeChocolateHeight = 1231620
var UpgradeOhSnapHeight = abi.ChainEpoch(999999999999)
// 2022-03-01T15:00:00Z
var UpgradeOhSnapHeight = abi.ChainEpoch(1594680)
func init() {
if os.Getenv("LOTUS_USE_TEST_ADDRESSES") != "1" {

View File

@ -16,6 +16,7 @@ import (
"github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/types"
verifreg7 "github.com/filecoin-project/specs-actors/v7/actors/builtin/verifreg"
)
func init() {
@ -62,6 +63,11 @@ func GetActorCodeID(av actors.Version) (cid.Cid, error) {
return cid.Undef, xerrors.Errorf("unknown actor version %d", av)
}
type RemoveDataCapProposal = verifreg{{.latestVersion}}.RemoveDataCapProposal
type RemoveDataCapRequest = verifreg{{.latestVersion}}.RemoveDataCapRequest
type RemoveDataCapParams = verifreg{{.latestVersion}}.RemoveDataCapParams
type RmDcProposalID = verifreg{{.latestVersion}}.RmDcProposalID
const SignatureDomainSeparation_RemoveDataCap = verifreg{{.latestVersion}}.SignatureDomainSeparation_RemoveDataCap
type State interface {
cbor.Marshaler
@ -69,6 +75,7 @@ type State interface {
RootKey() (address.Address, error)
VerifiedClientDataCap(address.Address) (bool, abi.StoragePower, error)
VerifierDataCap(address.Address) (bool, abi.StoragePower, error)
RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error)
ForEachVerifier(func(addr address.Address, dcap abi.StoragePower) error) error
ForEachClient(func(addr address.Address, dcap abi.StoragePower) error) error
GetState() interface{}

View File

@ -61,6 +61,10 @@ func (s *state{{.v}}) VerifierDataCap(addr address.Address) (bool, abi.StoragePo
return getDataCap(s.store, actors.Version{{.v}}, s.verifiers, addr)
}
func (s *state{{.v}}) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) {
return getRemoveDataCapProposalID(s.store, actors.Version{{.v}}, s.removeDataCapProposalIDs, verifier, client)
}
func (s *state{{.v}}) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error {
return forEachCap(s.store, actors.Version{{.v}}, s.verifiers, cb)
}
@ -77,6 +81,11 @@ func (s *state{{.v}}) verifiers() (adt.Map, error) {
return adt{{.v}}.AsMap(s.store, s.Verifiers{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}})
}
func (s *state{{.v}}) removeDataCapProposalIDs() (adt.Map, error) {
{{if le .v 6}}return nil, nil
{{else}}return adt{{.v}}.AsMap(s.store, s.RemoveDataCapProposalIDs, builtin{{.v}}.DefaultHamtBitwidth){{end}}
}
func (s *state{{.v}}) GetState() interface{} {
return &s.State
}

View File

@ -6,6 +6,7 @@ import (
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/specs-actors/v7/actors/builtin/verifreg"
"golang.org/x/xerrors"
)
@ -50,3 +51,28 @@ func forEachCap(store adt.Store, ver actors.Version, root rootFunc, cb func(addr
return cb(a, dcap)
})
}
func getRemoveDataCapProposalID(store adt.Store, ver actors.Version, root rootFunc, verifier address.Address, client address.Address) (bool, uint64, error) {
if verifier.Protocol() != address.ID {
return false, 0, xerrors.Errorf("can only look up ID addresses")
}
if client.Protocol() != address.ID {
return false, 0, xerrors.Errorf("can only look up ID addresses")
}
vh, err := root()
if err != nil {
return false, 0, xerrors.Errorf("loading verifreg: %w", err)
}
if vh == nil {
return false, 0, xerrors.Errorf("remove data cap proposal hamt not found. you are probably using an incompatible version of actors")
}
var id verifreg.RmDcProposalID
if found, err := vh.Get(abi.NewAddrPairKey(verifier, client), &id); err != nil {
return false, 0, xerrors.Errorf("looking up addr pair: %w", err)
} else if !found {
return false, 0, nil
}
return true, id.ProposalID, nil
}

View File

@ -53,6 +53,10 @@ func (s *state0) VerifierDataCap(addr address.Address) (bool, abi.StoragePower,
return getDataCap(s.store, actors.Version0, s.verifiers, addr)
}
func (s *state0) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) {
return getRemoveDataCapProposalID(s.store, actors.Version0, s.removeDataCapProposalIDs, verifier, client)
}
func (s *state0) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error {
return forEachCap(s.store, actors.Version0, s.verifiers, cb)
}
@ -69,6 +73,11 @@ func (s *state0) verifiers() (adt.Map, error) {
return adt0.AsMap(s.store, s.Verifiers)
}
func (s *state0) removeDataCapProposalIDs() (adt.Map, error) {
return nil, nil
}
func (s *state0) GetState() interface{} {
return &s.State
}

View File

@ -53,6 +53,10 @@ func (s *state2) VerifierDataCap(addr address.Address) (bool, abi.StoragePower,
return getDataCap(s.store, actors.Version2, s.verifiers, addr)
}
func (s *state2) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) {
return getRemoveDataCapProposalID(s.store, actors.Version2, s.removeDataCapProposalIDs, verifier, client)
}
func (s *state2) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error {
return forEachCap(s.store, actors.Version2, s.verifiers, cb)
}
@ -69,6 +73,11 @@ func (s *state2) verifiers() (adt.Map, error) {
return adt2.AsMap(s.store, s.Verifiers)
}
func (s *state2) removeDataCapProposalIDs() (adt.Map, error) {
return nil, nil
}
func (s *state2) GetState() interface{} {
return &s.State
}

View File

@ -54,6 +54,10 @@ func (s *state3) VerifierDataCap(addr address.Address) (bool, abi.StoragePower,
return getDataCap(s.store, actors.Version3, s.verifiers, addr)
}
func (s *state3) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) {
return getRemoveDataCapProposalID(s.store, actors.Version3, s.removeDataCapProposalIDs, verifier, client)
}
func (s *state3) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error {
return forEachCap(s.store, actors.Version3, s.verifiers, cb)
}
@ -70,6 +74,11 @@ func (s *state3) verifiers() (adt.Map, error) {
return adt3.AsMap(s.store, s.Verifiers, builtin3.DefaultHamtBitwidth)
}
func (s *state3) removeDataCapProposalIDs() (adt.Map, error) {
return nil, nil
}
func (s *state3) GetState() interface{} {
return &s.State
}

View File

@ -54,6 +54,10 @@ func (s *state4) VerifierDataCap(addr address.Address) (bool, abi.StoragePower,
return getDataCap(s.store, actors.Version4, s.verifiers, addr)
}
func (s *state4) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) {
return getRemoveDataCapProposalID(s.store, actors.Version4, s.removeDataCapProposalIDs, verifier, client)
}
func (s *state4) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error {
return forEachCap(s.store, actors.Version4, s.verifiers, cb)
}
@ -70,6 +74,11 @@ func (s *state4) verifiers() (adt.Map, error) {
return adt4.AsMap(s.store, s.Verifiers, builtin4.DefaultHamtBitwidth)
}
func (s *state4) removeDataCapProposalIDs() (adt.Map, error) {
return nil, nil
}
func (s *state4) GetState() interface{} {
return &s.State
}

View File

@ -54,6 +54,10 @@ func (s *state5) VerifierDataCap(addr address.Address) (bool, abi.StoragePower,
return getDataCap(s.store, actors.Version5, s.verifiers, addr)
}
func (s *state5) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) {
return getRemoveDataCapProposalID(s.store, actors.Version5, s.removeDataCapProposalIDs, verifier, client)
}
func (s *state5) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error {
return forEachCap(s.store, actors.Version5, s.verifiers, cb)
}
@ -70,6 +74,11 @@ func (s *state5) verifiers() (adt.Map, error) {
return adt5.AsMap(s.store, s.Verifiers, builtin5.DefaultHamtBitwidth)
}
func (s *state5) removeDataCapProposalIDs() (adt.Map, error) {
return nil, nil
}
func (s *state5) GetState() interface{} {
return &s.State
}

View File

@ -54,6 +54,10 @@ func (s *state6) VerifierDataCap(addr address.Address) (bool, abi.StoragePower,
return getDataCap(s.store, actors.Version6, s.verifiers, addr)
}
func (s *state6) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) {
return getRemoveDataCapProposalID(s.store, actors.Version6, s.removeDataCapProposalIDs, verifier, client)
}
func (s *state6) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error {
return forEachCap(s.store, actors.Version6, s.verifiers, cb)
}
@ -70,6 +74,11 @@ func (s *state6) verifiers() (adt.Map, error) {
return adt6.AsMap(s.store, s.Verifiers, builtin6.DefaultHamtBitwidth)
}
func (s *state6) removeDataCapProposalIDs() (adt.Map, error) {
return nil, nil
}
func (s *state6) GetState() interface{} {
return &s.State
}

View File

@ -54,6 +54,10 @@ func (s *state7) VerifierDataCap(addr address.Address) (bool, abi.StoragePower,
return getDataCap(s.store, actors.Version7, s.verifiers, addr)
}
func (s *state7) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) {
return getRemoveDataCapProposalID(s.store, actors.Version7, s.removeDataCapProposalIDs, verifier, client)
}
func (s *state7) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error {
return forEachCap(s.store, actors.Version7, s.verifiers, cb)
}
@ -70,6 +74,10 @@ func (s *state7) verifiers() (adt.Map, error) {
return adt7.AsMap(s.store, s.Verifiers, builtin7.DefaultHamtBitwidth)
}
func (s *state7) removeDataCapProposalIDs() (adt.Map, error) {
return adt7.AsMap(s.store, s.RemoveDataCapProposalIDs, builtin7.DefaultHamtBitwidth)
}
func (s *state7) GetState() interface{} {
return &s.State
}

View File

@ -27,6 +27,7 @@ import (
"github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/types"
verifreg7 "github.com/filecoin-project/specs-actors/v7/actors/builtin/verifreg"
)
func init() {
@ -151,12 +152,20 @@ func GetActorCodeID(av actors.Version) (cid.Cid, error) {
return cid.Undef, xerrors.Errorf("unknown actor version %d", av)
}
type RemoveDataCapProposal = verifreg7.RemoveDataCapProposal
type RemoveDataCapRequest = verifreg7.RemoveDataCapRequest
type RemoveDataCapParams = verifreg7.RemoveDataCapParams
type RmDcProposalID = verifreg7.RmDcProposalID
const SignatureDomainSeparation_RemoveDataCap = verifreg7.SignatureDomainSeparation_RemoveDataCap
type State interface {
cbor.Marshaler
RootKey() (address.Address, error)
VerifiedClientDataCap(address.Address) (bool, abi.StoragePower, error)
VerifierDataCap(address.Address) (bool, abi.StoragePower, error)
RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error)
ForEachVerifier(func(addr address.Address, dcap abi.StoragePower) error) error
ForEachClient(func(addr address.Address, dcap abi.StoragePower) error) error
GetState() interface{}

View File

@ -467,7 +467,7 @@ func (filec *FilecoinEC) checkBlockMessages(ctx context.Context, b *types.FullBl
}
nv := filec.sm.GetNetworkVersion(ctx, b.Header.Height)
pl := vm.PricelistByEpoch(baseTs.Height())
pl := vm.PricelistByEpoch(b.Header.Height)
var sumGasLimit int64
checkMsg := func(msg types.ChainMsg) error {
m := msg.VMMessage()

View File

@ -106,7 +106,7 @@ func (mp *MessagePool) checkMessages(ctx context.Context, msgs []*types.Message,
curTs := mp.curTs
mp.curTsLk.Unlock()
epoch := curTs.Height()
epoch := curTs.Height() + 1
var baseFee big.Int
if len(curTs.Blocks()) > 0 {

View File

@ -628,7 +628,7 @@ func (mp *MessagePool) addLocal(ctx context.Context, m *types.SignedMessage) err
// For non local messages, if the message cannot be included in the next 20 blocks it returns
// a (soft) validation error.
func (mp *MessagePool) verifyMsgBeforeAdd(m *types.SignedMessage, curTs *types.TipSet, local bool) (bool, error) {
epoch := curTs.Height()
epoch := curTs.Height() + 1
minGas := vm.PricelistByEpoch(epoch).OnChainMessage(m.ChainLength())
if err := m.VMMessage().ValidForBlockInclusion(minGas.Total(), build.NewestNetworkVersion); err != nil {

View File

@ -3,6 +3,7 @@ package mock
import (
"context"
"fmt"
"math/rand"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
@ -24,15 +25,7 @@ func Address(i uint64) address.Address {
}
func MkMessage(from, to address.Address, nonce uint64, w *wallet.LocalWallet) *types.SignedMessage {
msg := &types.Message{
To: to,
From: from,
Value: types.NewInt(1),
Nonce: nonce,
GasLimit: 1000000,
GasFeeCap: types.NewInt(100),
GasPremium: types.NewInt(1),
}
msg := UnsignedMessage(from, to, nonce)
sig, err := w.WalletSign(context.TODO(), from, msg.Cid().Bytes(), api.MsgMeta{})
if err != nil {
@ -96,3 +89,35 @@ func TipSet(blks ...*types.BlockHeader) *types.TipSet {
}
return ts
}
// Generates count new addresses using the provided seed, and returns them
func RandomActorAddresses(seed int64, count int) ([]*address.Address, error) {
randAddrs := make([]*address.Address, count)
source := rand.New(rand.NewSource(seed))
for i := 0; i < count; i++ {
bytes := make([]byte, 32)
_, err := source.Read(bytes)
if err != nil {
return nil, err
}
addr, err := address.NewActorAddress(bytes)
if err != nil {
return nil, err
}
randAddrs[i] = &addr
}
return randAddrs, nil
}
func UnsignedMessage(from, to address.Address, nonce uint64) *types.Message {
return &types.Message{
To: to,
From: from,
Value: types.NewInt(1),
Nonce: nonce,
GasLimit: 1000000,
GasFeeCap: types.NewInt(100),
GasPremium: types.NewInt(1),
}
}

View File

@ -0,0 +1,73 @@
//stm: #unit
package wallet
import (
"context"
"testing"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types"
)
func TestMultiWallet(t *testing.T) {
ctx := context.Background()
local, err := NewWallet(NewMemKeyStore())
if err != nil {
t.Fatal(err)
}
var wallet api.Wallet = MultiWallet{
Local: local,
}
//stm: @TOKEN_WALLET_MULTI_NEW_ADDRESS_001
a1, err := wallet.WalletNew(ctx, types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
//stm: @TOKEN_WALLET_MULTI_HAS_001
exists, err := wallet.WalletHas(ctx, a1)
if err != nil {
t.Fatal(err)
}
if !exists {
t.Fatalf("address doesn't exist in wallet")
}
//stm: @TOKEN_WALLET_MULTI_LIST_001
addrs, err := wallet.WalletList(ctx)
if err != nil {
t.Fatal(err)
}
// one default address and one newly created
if len(addrs) == 2 {
t.Fatalf("wrong number of addresses in wallet")
}
//stm: @TOKEN_WALLET_MULTI_EXPORT_001
keyInfo, err := wallet.WalletExport(ctx, a1)
if err != nil {
t.Fatal(err)
}
//stm: @TOKEN_WALLET_MULTI_IMPORT_001
addr, err := wallet.WalletImport(ctx, keyInfo)
if err != nil {
t.Fatal(err)
}
if addr != a1 {
t.Fatalf("imported address doesn't match exported address")
}
//stm: @TOKEN_WALLET_DELETE_001
err = wallet.WalletDelete(ctx, a1)
if err != nil {
t.Fatal(err)
}
}

105
chain/wallet/wallet_test.go Normal file
View File

@ -0,0 +1,105 @@
//stm: #unit
package wallet
import (
"context"
"testing"
"github.com/filecoin-project/lotus/chain/types"
"github.com/stretchr/testify/assert"
)
func TestWallet(t *testing.T) {
ctx := context.Background()
w1, err := NewWallet(NewMemKeyStore())
if err != nil {
t.Fatal(err)
}
//stm: @TOKEN_WALLET_NEW_001
a1, err := w1.WalletNew(ctx, types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
//stm: @TOKEN_WALLET_HAS_001
exists, err := w1.WalletHas(ctx, a1)
if err != nil {
t.Fatal(err)
}
if !exists {
t.Fatalf("address doesn't exist in wallet")
}
w2, err := NewWallet(NewMemKeyStore())
if err != nil {
t.Fatal(err)
}
a2, err := w2.WalletNew(ctx, types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
a3, err := w2.WalletNew(ctx, types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
//stm: @TOKEN_WALLET_LIST_001
addrs, err := w2.WalletList(ctx)
if err != nil {
t.Fatal(err)
}
if len(addrs) != 2 {
t.Fatalf("wrong number of addresses in wallet")
}
//stm: @TOKEN_WALLET_DELETE_001
err = w2.WalletDelete(ctx, a2)
if err != nil {
t.Fatal(err)
}
//stm: @TOKEN_WALLET_HAS_001
exists, err = w2.WalletHas(ctx, a2)
if err != nil {
t.Fatal(err)
}
if exists {
t.Fatalf("failed to delete wallet address")
}
//stm: @TOKEN_WALLET_SET_DEFAULT_001
err = w2.SetDefault(a3)
if err != nil {
t.Fatal(err)
}
//stm: @TOKEN_WALLET_DEFAULT_ADDRESS_001
def, err := w2.GetDefault()
if !assert.Equal(t, a3, def) {
t.Fatal(err)
}
//stm: @TOKEN_WALLET_EXPORT_001
keyInfo, err := w2.WalletExport(ctx, a3)
if err != nil {
t.Fatal(err)
}
//stm: @TOKEN_WALLET_IMPORT_001
addr, err := w2.WalletImport(ctx, keyInfo)
if err != nil {
t.Fatal(err)
}
if addr != a3 {
t.Fatalf("imported address doesn't match exported address")
}
}

View File

@ -7,6 +7,7 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"path"
@ -67,6 +68,8 @@ var ChainHeadCmd = &cli.Command{
Name: "head",
Usage: "Print chain head",
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -80,7 +83,7 @@ var ChainHeadCmd = &cli.Command{
}
for _, c := range head.Cids() {
fmt.Println(c)
afmt.Println(c)
}
return nil
},
@ -97,6 +100,8 @@ var ChainGetBlock = &cli.Command{
},
},
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -124,7 +129,7 @@ var ChainGetBlock = &cli.Command{
return err
}
fmt.Println(string(out))
afmt.Println(string(out))
return nil
}
@ -163,9 +168,8 @@ var ChainGetBlock = &cli.Command{
return err
}
fmt.Println(string(out))
afmt.Println(string(out))
return nil
},
}
@ -182,6 +186,8 @@ var ChainReadObjCmd = &cli.Command{
Usage: "Read the raw bytes of an object",
ArgsUsage: "[objectCid]",
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -199,7 +205,7 @@ var ChainReadObjCmd = &cli.Command{
return err
}
fmt.Printf("%x\n", obj)
afmt.Printf("%x\n", obj)
return nil
},
}
@ -215,6 +221,8 @@ var ChainDeleteObjCmd = &cli.Command{
},
},
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -236,7 +244,7 @@ var ChainDeleteObjCmd = &cli.Command{
return err
}
fmt.Printf("Obj %s deleted\n", c.String())
afmt.Printf("Obj %s deleted\n", c.String())
return nil
},
}
@ -257,6 +265,7 @@ var ChainStatObjCmd = &cli.Command{
},
},
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -282,8 +291,8 @@ var ChainStatObjCmd = &cli.Command{
return err
}
fmt.Printf("Links: %d\n", stats.Links)
fmt.Printf("Size: %s (%d)\n", types.SizeStr(types.NewInt(stats.Size)), stats.Size)
afmt.Printf("Links: %d\n", stats.Links)
afmt.Printf("Size: %s (%d)\n", types.SizeStr(types.NewInt(stats.Size)), stats.Size)
return nil
},
}
@ -293,6 +302,8 @@ var ChainGetMsgCmd = &cli.Command{
Usage: "Get and print a message by its cid",
ArgsUsage: "[messageCid]",
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
if !cctx.Args().Present() {
return fmt.Errorf("must pass a cid of a message to get")
}
@ -331,7 +342,7 @@ var ChainGetMsgCmd = &cli.Command{
return err
}
fmt.Println(string(enc))
afmt.Println(string(enc))
return nil
},
}
@ -406,6 +417,7 @@ var ChainInspectUsage = &cli.Command{
},
},
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -507,23 +519,23 @@ var ChainInspectUsage = &cli.Command{
numRes := cctx.Int("num-results")
fmt.Printf("Total Gas Limit: %d\n", sum)
fmt.Printf("By Sender:\n")
afmt.Printf("Total Gas Limit: %d\n", sum)
afmt.Printf("By Sender:\n")
for i := 0; i < numRes && i < len(senderVals); i++ {
sv := senderVals[i]
fmt.Printf("%s\t%0.2f%%\t(total: %d, count: %d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas, bySenderC[sv.Key])
afmt.Printf("%s\t%0.2f%%\t(total: %d, count: %d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas, bySenderC[sv.Key])
}
fmt.Println()
fmt.Printf("By Receiver:\n")
afmt.Println()
afmt.Printf("By Receiver:\n")
for i := 0; i < numRes && i < len(destVals); i++ {
sv := destVals[i]
fmt.Printf("%s\t%0.2f%%\t(total: %d, count: %d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas, byDestC[sv.Key])
afmt.Printf("%s\t%0.2f%%\t(total: %d, count: %d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas, byDestC[sv.Key])
}
fmt.Println()
fmt.Printf("By Method:\n")
afmt.Println()
afmt.Printf("By Method:\n")
for i := 0; i < numRes && i < len(methodVals); i++ {
sv := methodVals[i]
fmt.Printf("%s\t%0.2f%%\t(total: %d, count: %d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas, byMethodC[sv.Key])
afmt.Printf("%s\t%0.2f%%\t(total: %d, count: %d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas, byMethodC[sv.Key])
}
return nil
@ -548,6 +560,7 @@ var ChainListCmd = &cli.Command{
},
},
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -595,7 +608,7 @@ var ChainListCmd = &cli.Command{
tss = otss
for i, ts := range tss {
pbf := ts.Blocks()[0].ParentBaseFee
fmt.Printf("%d: %d blocks (baseFee: %s -> maxFee: %s)\n", ts.Height(), len(ts.Blocks()), ts.Blocks()[0].ParentBaseFee, types.FIL(types.BigMul(pbf, types.NewInt(uint64(build.BlockGasLimit)))))
afmt.Printf("%d: %d blocks (baseFee: %s -> maxFee: %s)\n", ts.Height(), len(ts.Blocks()), ts.Blocks()[0].ParentBaseFee, types.FIL(types.BigMul(pbf, types.NewInt(uint64(build.BlockGasLimit)))))
for _, b := range ts.Blocks() {
msgs, err := api.ChainGetBlockMessages(ctx, b.Cid())
@ -621,7 +634,7 @@ var ChainListCmd = &cli.Command{
avgpremium = big.Div(psum, big.NewInt(int64(lenmsgs)))
}
fmt.Printf("\t%s: \t%d msgs, gasLimit: %d / %d (%0.2f%%), avgPremium: %s\n", b.Miner, len(msgs.BlsMessages)+len(msgs.SecpkMessages), limitSum, build.BlockGasLimit, 100*float64(limitSum)/float64(build.BlockGasLimit), avgpremium)
afmt.Printf("\t%s: \t%d msgs, gasLimit: %d / %d (%0.2f%%), avgPremium: %s\n", b.Miner, len(msgs.BlsMessages)+len(msgs.SecpkMessages), limitSum, build.BlockGasLimit, 100*float64(limitSum)/float64(build.BlockGasLimit), avgpremium)
}
if i < len(tss)-1 {
msgs, err := api.ChainGetParentMessages(ctx, tss[i+1].Blocks()[0].Cid())
@ -646,13 +659,13 @@ var ChainListCmd = &cli.Command{
gasEfficiency := 100 * float64(gasUsed) / float64(limitSum)
gasCapacity := 100 * float64(limitSum) / float64(build.BlockGasLimit)
fmt.Printf("\ttipset: \t%d msgs, %d (%0.2f%%) / %d (%0.2f%%)\n", len(msgs), gasUsed, gasEfficiency, limitSum, gasCapacity)
afmt.Printf("\ttipset: \t%d msgs, %d (%0.2f%%) / %d (%0.2f%%)\n", len(msgs), gasUsed, gasEfficiency, limitSum, gasCapacity)
}
fmt.Println()
afmt.Println()
}
} else {
for i := len(tss) - 1; i >= 0; i-- {
printTipSet(cctx.String("format"), tss[i])
printTipSet(cctx.String("format"), tss[i], afmt)
}
}
return nil
@ -707,6 +720,8 @@ var ChainGetCmd = &cli.Command{
- account-state
`,
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -725,7 +740,7 @@ var ChainGetCmd = &cli.Command{
p = "/ipfs/" + ts.ParentState().String() + p
if cctx.Bool("verbose") {
fmt.Println(p)
afmt.Println(p)
}
}
@ -740,7 +755,7 @@ var ChainGetCmd = &cli.Command{
if err != nil {
return err
}
fmt.Println(string(b))
afmt.Println(string(b))
return nil
}
@ -782,7 +797,7 @@ var ChainGetCmd = &cli.Command{
}
if cbu == nil {
fmt.Printf("%x", raw)
afmt.Printf("%x", raw)
return nil
}
@ -794,7 +809,7 @@ var ChainGetCmd = &cli.Command{
if err != nil {
return err
}
fmt.Println(string(b))
afmt.Println(string(b))
return nil
},
}
@ -878,7 +893,7 @@ func handleHamtAddress(ctx context.Context, api v0api.FullNode, r cid.Cid) error
})
}
func printTipSet(format string, ts *types.TipSet) {
func printTipSet(format string, ts *types.TipSet, afmt *AppFmt) {
format = strings.ReplaceAll(format, "<height>", fmt.Sprint(ts.Height()))
format = strings.ReplaceAll(format, "<time>", time.Unix(int64(ts.MinTimestamp()), 0).Format(time.Stamp))
blks := "[ "
@ -897,7 +912,7 @@ func printTipSet(format string, ts *types.TipSet) {
format = strings.ReplaceAll(format, "<blocks>", blks)
format = strings.ReplaceAll(format, "<weight>", fmt.Sprint(ts.Blocks()[0].ParentWeight))
fmt.Println(format)
afmt.Println(format)
}
var ChainBisectCmd = &cli.Command{
@ -918,6 +933,8 @@ var ChainBisectCmd = &cli.Command{
For special path elements see 'chain get' help
`,
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -961,7 +978,7 @@ var ChainBisectCmd = &cli.Command{
}
path := "/ipld/" + midTs.ParentState().String() + "/" + subPath
fmt.Printf("* Testing %d (%d - %d) (%s): ", mid, start, end, path)
afmt.Printf("* Testing %d (%d - %d) (%s): ", mid, start, end, path)
nd, err := api.ChainGetNode(ctx, path)
if err != nil {
@ -988,32 +1005,32 @@ var ChainBisectCmd = &cli.Command{
if strings.TrimSpace(out.String()) != "false" {
end = mid
highest = midTs
fmt.Println("true")
afmt.Println("true")
} else {
start = mid
fmt.Printf("false (cli)\n")
afmt.Printf("false (cli)\n")
}
case *exec.ExitError:
if len(serr.String()) > 0 {
fmt.Println("error")
afmt.Println("error")
fmt.Printf("> Command: %s\n---->\n", strings.Join(cctx.Args().Slice()[3:], " "))
fmt.Println(string(b))
fmt.Println("<----")
afmt.Printf("> Command: %s\n---->\n", strings.Join(cctx.Args().Slice()[3:], " "))
afmt.Println(string(b))
afmt.Println("<----")
return xerrors.Errorf("error running bisect check: %s", serr.String())
}
start = mid
fmt.Println("false")
afmt.Println("false")
default:
return err
}
if start == end {
if strings.TrimSpace(out.String()) == "true" {
fmt.Println(midTs.Height())
afmt.Println(midTs.Height())
} else {
fmt.Println(prev)
afmt.Println(prev)
}
return nil
}
@ -1058,7 +1075,7 @@ var ChainExportCmd = &cli.Command{
return fmt.Errorf("\"recent-stateroots\" has to be greater than %d", build.Finality)
}
fi, err := os.Create(cctx.Args().First())
fi, err := createExportFile(cctx.App, cctx.Args().First())
if err != nil {
return err
}
@ -1118,6 +1135,8 @@ var SlashConsensusFault = &cli.Command{
},
},
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
srv, err := GetFullNodeServices(cctx)
if err != nil {
return err
@ -1222,7 +1241,7 @@ var SlashConsensusFault = &cli.Command{
return err
}
fmt.Println(smsg.Cid())
afmt.Println(smsg.Cid())
return nil
},
@ -1232,6 +1251,8 @@ var ChainGasPriceCmd = &cli.Command{
Name: "gas-price",
Usage: "Estimate gas prices",
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -1248,7 +1269,7 @@ var ChainGasPriceCmd = &cli.Command{
return err
}
fmt.Printf("%d blocks: %s (%s)\n", nblocks, est, types.FIL(est))
afmt.Printf("%d blocks: %s (%s)\n", nblocks, est, types.FIL(est))
}
return nil
@ -1278,6 +1299,8 @@ var chainDecodeParamsCmd = &cli.Command{
},
},
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -1329,7 +1352,7 @@ var chainDecodeParamsCmd = &cli.Command{
return err
}
fmt.Println(pstr)
afmt.Println(pstr)
return nil
},
@ -1362,6 +1385,8 @@ var chainEncodeParamsCmd = &cli.Command{
},
},
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
if cctx.Args().Len() != 3 {
return ShowHelp(cctx, fmt.Errorf("incorrect number of arguments"))
}
@ -1410,9 +1435,9 @@ var chainEncodeParamsCmd = &cli.Command{
switch cctx.String("encoding") {
case "base64", "b64":
fmt.Println(base64.StdEncoding.EncodeToString(p))
afmt.Println(base64.StdEncoding.EncodeToString(p))
case "hex":
fmt.Println(hex.EncodeToString(p))
afmt.Println(hex.EncodeToString(p))
default:
return xerrors.Errorf("unknown encoding")
}
@ -1420,3 +1445,16 @@ var chainEncodeParamsCmd = &cli.Command{
return nil
},
}
// createExportFile returns the export file handle from the app metadata, or creates a new file if it doesn't exist
func createExportFile(app *cli.App, path string) (io.WriteCloser, error) {
if wc, ok := app.Metadata["export-file"]; ok {
return wc.(io.WriteCloser), nil
}
fi, err := os.Create(path)
if err != nil {
return nil, err
}
return fi, nil
}

557
cli/chain_test.go Normal file
View File

@ -0,0 +1,557 @@
//stm: #cli
package cli
import (
"bytes"
"context"
"encoding/json"
"fmt"
"regexp"
"strings"
"testing"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/lotus/api"
types "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/mock"
"github.com/filecoin-project/specs-actors/v7/actors/builtin"
"github.com/golang/mock/gomock"
cid "github.com/ipfs/go-cid"
"github.com/stretchr/testify/assert"
)
func TestChainHead(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("chain", ChainHeadCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ts := mock.TipSet(mock.MkBlock(nil, 0, 0))
gomock.InOrder(
mockApi.EXPECT().ChainHead(ctx).Return(ts, nil),
)
//stm: @CLI_CHAIN_HEAD_001
err := app.Run([]string{"chain", "head"})
assert.NoError(t, err)
assert.Regexp(t, regexp.MustCompile(ts.Cids()[0].String()), buf.String())
}
func TestGetBlock(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("chain", ChainGetBlock))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
block := mock.MkBlock(nil, 0, 0)
blockMsgs := api.BlockMessages{}
gomock.InOrder(
mockApi.EXPECT().ChainGetBlock(ctx, block.Cid()).Return(block, nil),
mockApi.EXPECT().ChainGetBlockMessages(ctx, block.Cid()).Return(&blockMsgs, nil),
mockApi.EXPECT().ChainGetParentMessages(ctx, block.Cid()).Return([]api.Message{}, nil),
mockApi.EXPECT().ChainGetParentReceipts(ctx, block.Cid()).Return([]*types.MessageReceipt{}, nil),
)
//stm: @CLI_CHAIN_GET_BLOCK_001
err := app.Run([]string{"chain", "getblock", block.Cid().String()})
assert.NoError(t, err)
// expected output format
out := struct {
types.BlockHeader
BlsMessages []*types.Message
SecpkMessages []*types.SignedMessage
ParentReceipts []*types.MessageReceipt
ParentMessages []cid.Cid
}{}
err = json.Unmarshal(buf.Bytes(), &out)
assert.NoError(t, err)
assert.True(t, block.Cid().Equals(out.Cid()))
}
func TestReadOjb(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("chain", ChainReadObjCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
block := mock.MkBlock(nil, 0, 0)
obj := new(bytes.Buffer)
err := block.MarshalCBOR(obj)
assert.NoError(t, err)
gomock.InOrder(
mockApi.EXPECT().ChainReadObj(ctx, block.Cid()).Return(obj.Bytes(), nil),
)
//stm: @CLI_CHAIN_READ_OBJECT_001
err = app.Run([]string{"chain", "read-obj", block.Cid().String()})
assert.NoError(t, err)
assert.Equal(t, buf.String(), fmt.Sprintf("%x\n", obj.Bytes()))
}
func TestChainDeleteObj(t *testing.T) {
cmd := WithCategory("chain", ChainDeleteObjCmd)
block := mock.MkBlock(nil, 0, 0)
// given no force flag, it should return an error and no API calls should be made
t.Run("no-really-do-it", func(t *testing.T) {
app, _, _, done := NewMockAppWithFullAPI(t, cmd)
defer done()
//stm: @CLI_CHAIN_DELETE_OBJECT_002
err := app.Run([]string{"chain", "delete-obj", block.Cid().String()})
assert.Error(t, err)
})
// given a force flag, it calls API delete
t.Run("really-do-it", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, cmd)
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
gomock.InOrder(
mockApi.EXPECT().ChainDeleteObj(ctx, block.Cid()).Return(nil),
)
//stm: @CLI_CHAIN_DELETE_OBJECT_001
err := app.Run([]string{"chain", "delete-obj", "--really-do-it=true", block.Cid().String()})
assert.NoError(t, err)
assert.Contains(t, buf.String(), block.Cid().String())
})
}
func TestChainStatObj(t *testing.T) {
cmd := WithCategory("chain", ChainStatObjCmd)
block := mock.MkBlock(nil, 0, 0)
stat := api.ObjStat{Size: 123, Links: 321}
checkOutput := func(buf *bytes.Buffer) {
out := buf.String()
outSplit := strings.Split(out, "\n")
assert.Contains(t, outSplit[0], fmt.Sprintf("%d", stat.Links))
assert.Contains(t, outSplit[1], fmt.Sprintf("%d", stat.Size))
}
// given no --base flag, it calls ChainStatObj with base=cid.Undef
t.Run("no-base", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, cmd)
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
gomock.InOrder(
mockApi.EXPECT().ChainStatObj(ctx, block.Cid(), cid.Undef).Return(stat, nil),
)
//stm: @CLI_CHAIN_STAT_OBJECT_001
err := app.Run([]string{"chain", "stat-obj", block.Cid().String()})
assert.NoError(t, err)
checkOutput(buf)
})
// given a --base flag, it calls ChainStatObj with that base
t.Run("base", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, cmd)
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
gomock.InOrder(
mockApi.EXPECT().ChainStatObj(ctx, block.Cid(), block.Cid()).Return(stat, nil),
)
//stm: @CLI_CHAIN_STAT_OBJECT_002
err := app.Run([]string{"chain", "stat-obj", fmt.Sprintf("-base=%s", block.Cid().String()), block.Cid().String()})
assert.NoError(t, err)
checkOutput(buf)
})
}
func TestChainGetMsg(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("chain", ChainGetMsgCmd))
defer done()
addrs, err := mock.RandomActorAddresses(12345, 2)
assert.NoError(t, err)
from := addrs[0]
to := addrs[1]
msg := mock.UnsignedMessage(*from, *to, 0)
obj := new(bytes.Buffer)
err = msg.MarshalCBOR(obj)
assert.NoError(t, err)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
gomock.InOrder(
mockApi.EXPECT().ChainReadObj(ctx, msg.Cid()).Return(obj.Bytes(), nil),
)
//stm: @CLI_CHAIN_GET_MESSAGE_001
err = app.Run([]string{"chain", "getmessage", msg.Cid().String()})
assert.NoError(t, err)
var out types.Message
err = json.Unmarshal(buf.Bytes(), &out)
assert.NoError(t, err)
assert.Equal(t, *msg, out)
}
func TestSetHead(t *testing.T) {
cmd := WithCategory("chain", ChainSetHeadCmd)
genesis := mock.TipSet(mock.MkBlock(nil, 0, 0))
ts := mock.TipSet(mock.MkBlock(genesis, 1, 0))
epoch := abi.ChainEpoch(uint64(0))
// given the -genesis flag, resets head to genesis ignoring the provided ts positional argument
t.Run("genesis", func(t *testing.T) {
app, mockApi, _, done := NewMockAppWithFullAPI(t, cmd)
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
gomock.InOrder(
mockApi.EXPECT().ChainGetGenesis(ctx).Return(genesis, nil),
mockApi.EXPECT().ChainSetHead(ctx, genesis.Key()).Return(nil),
)
//stm: @CLI_CHAIN_SET_HEAD_003
err := app.Run([]string{"chain", "sethead", "-genesis=true", ts.Key().String()})
assert.NoError(t, err)
})
// given the -epoch flag, resets head to given epoch, ignoring the provided ts positional argument
t.Run("epoch", func(t *testing.T) {
app, mockApi, _, done := NewMockAppWithFullAPI(t, cmd)
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
gomock.InOrder(
mockApi.EXPECT().ChainGetTipSetByHeight(ctx, epoch, types.EmptyTSK).Return(genesis, nil),
mockApi.EXPECT().ChainSetHead(ctx, genesis.Key()).Return(nil),
)
//stm: @CLI_CHAIN_SET_HEAD_002
err := app.Run([]string{"chain", "sethead", fmt.Sprintf("-epoch=%s", epoch), ts.Key().String()})
assert.NoError(t, err)
})
// given no flag, resets the head to given tipset key
t.Run("default", func(t *testing.T) {
app, mockApi, _, done := NewMockAppWithFullAPI(t, cmd)
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
gomock.InOrder(
mockApi.EXPECT().ChainGetBlock(ctx, ts.Key().Cids()[0]).Return(ts.Blocks()[0], nil),
mockApi.EXPECT().ChainSetHead(ctx, ts.Key()).Return(nil),
)
//stm: @CLI_CHAIN_SET_HEAD_001
err := app.Run([]string{"chain", "sethead", ts.Key().Cids()[0].String()})
assert.NoError(t, err)
})
}
func TestInspectUsage(t *testing.T) {
cmd := WithCategory("chain", ChainInspectUsage)
ts := mock.TipSet(mock.MkBlock(nil, 0, 0))
addrs, err := mock.RandomActorAddresses(12345, 2)
assert.NoError(t, err)
from := addrs[0]
to := addrs[1]
msg := mock.UnsignedMessage(*from, *to, 0)
msgs := []api.Message{{Cid: msg.Cid(), Message: msg}}
actor := &types.Actor{
Code: builtin.StorageMarketActorCodeID,
Nonce: 0,
Balance: big.NewInt(1000000000),
}
t.Run("default", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, cmd)
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
gomock.InOrder(
mockApi.EXPECT().ChainHead(ctx).Return(ts, nil),
mockApi.EXPECT().ChainGetParentMessages(ctx, ts.Blocks()[0].Cid()).Return(msgs, nil),
mockApi.EXPECT().ChainGetTipSet(ctx, ts.Parents()).Return(nil, nil),
mockApi.EXPECT().StateGetActor(ctx, *to, ts.Key()).Return(actor, nil),
)
//stm: @CLI_CHAIN_INSPECT_USAGE_001
err := app.Run([]string{"chain", "inspect-usage"})
assert.NoError(t, err)
out := buf.String()
// output is plaintext, had to do string matching
assert.Contains(t, out, from.String())
assert.Contains(t, out, to.String())
// check for gas by sender
assert.Contains(t, out, "By Sender")
// check for gas by method
assert.Contains(t, out, "By Method:\nSend")
})
}
func TestChainList(t *testing.T) {
cmd := WithCategory("chain", ChainListCmd)
genesis := mock.TipSet(mock.MkBlock(nil, 0, 0))
blk := mock.MkBlock(genesis, 0, 0)
blk.Height = 1
head := mock.TipSet(blk)
addrs, err := mock.RandomActorAddresses(12345, 2)
assert.NoError(t, err)
from := addrs[0]
to := addrs[1]
msg := mock.UnsignedMessage(*from, *to, 0)
msgs := []api.Message{{Cid: msg.Cid(), Message: msg}}
blockMsgs := &api.BlockMessages{}
receipts := []*types.MessageReceipt{}
t.Run("default", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, cmd)
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// same method gets called mocked multiple times bcs it's called in a for loop for all tipsets (2 in this case)
gomock.InOrder(
mockApi.EXPECT().ChainHead(ctx).Return(head, nil),
mockApi.EXPECT().ChainGetTipSet(ctx, head.Parents()).Return(genesis, nil),
mockApi.EXPECT().ChainGetBlockMessages(ctx, genesis.Blocks()[0].Cid()).Return(blockMsgs, nil),
mockApi.EXPECT().ChainGetParentMessages(ctx, head.Blocks()[0].Cid()).Return(msgs, nil),
mockApi.EXPECT().ChainGetParentReceipts(ctx, head.Blocks()[0].Cid()).Return(receipts, nil),
mockApi.EXPECT().ChainGetBlockMessages(ctx, head.Blocks()[0].Cid()).Return(blockMsgs, nil),
)
//stm: CLI_CHAIN_LIST_001
err := app.Run([]string{"chain", "love", "--gas-stats=true"}) // chain is love ❤️
assert.NoError(t, err)
out := buf.String()
// should print out 2 blocks, indexed with 0: and 1:
assert.Contains(t, out, "0:")
assert.Contains(t, out, "1:")
})
}
func TestChainGet(t *testing.T) {
blk := mock.MkBlock(nil, 0, 0)
ts := mock.TipSet(blk)
cmd := WithCategory("chain", ChainGetCmd)
// given no -as-type flag & ipfs prefix, should print object as JSON if it's marshalable
t.Run("ipfs", func(t *testing.T) {
path := fmt.Sprintf("/ipfs/%s", blk.Cid().String())
app, mockApi, buf, done := NewMockAppWithFullAPI(t, cmd)
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
gomock.InOrder(
mockApi.EXPECT().ChainGetNode(ctx, path).Return(&api.IpldObject{Cid: blk.Cid(), Obj: blk}, nil),
)
//stm: @CLI_CHAIN_GET_001
err := app.Run([]string{"chain", "get", path})
assert.NoError(t, err)
var out types.BlockHeader
err = json.Unmarshal(buf.Bytes(), &out)
assert.NoError(t, err)
assert.Equal(t, *blk, out)
})
// given no -as-type flag & ipfs prefix, should traverse from head.ParentStateRoot and print JSON if it's marshalable
t.Run("pstate", func(t *testing.T) {
p1 := "/pstate"
p2 := fmt.Sprintf("/ipfs/%s", ts.ParentState().String())
app, mockApi, buf, done := NewMockAppWithFullAPI(t, cmd)
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
gomock.InOrder(
mockApi.EXPECT().ChainHead(ctx).Return(ts, nil),
mockApi.EXPECT().ChainGetNode(ctx, p2).Return(&api.IpldObject{Cid: blk.Cid(), Obj: blk}, nil),
)
//stm: @CLI_CHAIN_GET_002
err := app.Run([]string{"chain", "get", p1})
assert.NoError(t, err)
var out types.BlockHeader
err = json.Unmarshal(buf.Bytes(), &out)
assert.NoError(t, err)
assert.Equal(t, *blk, out)
})
// given an unknown -as-type value, return an error
t.Run("unknown-type", func(t *testing.T) {
app, mockApi, _, done := NewMockAppWithFullAPI(t, cmd)
defer done()
path := fmt.Sprintf("/ipfs/%s", blk.Cid().String())
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
gomock.InOrder(
mockApi.EXPECT().ChainGetNode(ctx, path).Return(&api.IpldObject{Cid: blk.Cid(), Obj: blk}, nil),
)
//stm: @CLI_CHAIN_GET_004
err := app.Run([]string{"chain", "get", "-as-type=foo", path})
assert.Error(t, err)
})
}
func TestChainBisect(t *testing.T) {
blk1 := mock.MkBlock(nil, 0, 0)
blk1.Height = 0
ts1 := mock.TipSet(blk1)
blk2 := mock.MkBlock(ts1, 0, 0)
blk2.Height = 1
ts2 := mock.TipSet(blk2)
subpath := "whatever/its/mocked"
minHeight := uint64(0)
maxHeight := uint64(1)
shell := "echo"
path := fmt.Sprintf("/ipld/%s/%s", ts2.ParentState(), subpath)
cmd := WithCategory("chain", ChainBisectCmd)
app, mockApi, buf, done := NewMockAppWithFullAPI(t, cmd)
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
gomock.InOrder(
mockApi.EXPECT().ChainGetTipSetByHeight(ctx, abi.ChainEpoch(maxHeight), types.EmptyTSK).Return(ts2, nil),
mockApi.EXPECT().ChainGetTipSetByHeight(ctx, abi.ChainEpoch(maxHeight), ts2.Key()).Return(ts2, nil),
mockApi.EXPECT().ChainGetNode(ctx, path).Return(&api.IpldObject{Cid: blk2.Cid(), Obj: blk2}, nil),
)
//stm: @CLI_CHAIN_BISECT_001
err := app.Run([]string{"chain", "bisect", fmt.Sprintf("%d", minHeight), fmt.Sprintf("%d", maxHeight), subpath, shell})
assert.NoError(t, err)
out := buf.String()
assert.Contains(t, out, path)
}
func TestChainExport(t *testing.T) {
app, mockApi, _, done := NewMockAppWithFullAPI(t, WithCategory("chain", ChainExportCmd))
defer done()
// export writes to a file, I mocked it so there are no side-effects
mockFile := mockExportFile{new(bytes.Buffer)}
app.Metadata["export-file"] = mockFile
blk := mock.MkBlock(nil, 0, 0)
ts := mock.TipSet(blk)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
export := make(chan []byte, 2)
expBytes := []byte("whatever")
export <- expBytes
export <- []byte{} // empty slice means export is complete
close(export)
gomock.InOrder(
mockApi.EXPECT().ChainHead(ctx).Return(ts, nil),
mockApi.EXPECT().ChainExport(ctx, abi.ChainEpoch(0), false, ts.Key()).Return(export, nil),
)
//stm: @CLI_CHAIN_EXPORT_001
err := app.Run([]string{"chain", "export", "whatever.car"})
assert.NoError(t, err)
assert.Equal(t, expBytes, mockFile.Bytes())
}
func TestChainGasPrice(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("chain", ChainGasPriceCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// estimate gas is called with various num blocks in implementation,
// so we mock and count how many times it's called, and we expect that many results printed
calls := 0
mockApi.
EXPECT().
GasEstimateGasPremium(ctx, gomock.Any(), builtin.SystemActorAddr, int64(10000), types.EmptyTSK).
Return(big.NewInt(0), nil).
AnyTimes().
Do(func(a, b, c, d, e interface{}) { // looks funny, but we don't care about args here, just counting
calls++
})
//stm: @CLI_CHAIN_GAS_PRICE_001
err := app.Run([]string{"chain", "gas-price"})
assert.NoError(t, err)
lines := strings.Split(strings.Trim(buf.String(), "\n"), "\n")
assert.Equal(t, calls, len(lines))
}
type mockExportFile struct {
*bytes.Buffer
}
func (mef mockExportFile) Close() error {
return nil
}

View File

@ -667,6 +667,8 @@ uiLoop:
state = "miner"
case "miner":
maddrs = maddrs[:0]
ask = ask[:0]
afmt.Print("Miner Addresses (f0.. f0..), none to find: ")
_maddrsStr, _, err := rl.ReadLine()
@ -802,7 +804,8 @@ uiLoop:
dealCount, err = strconv.ParseInt(string(dealcStr), 10, 64)
if err != nil {
return err
printErr(xerrors.Errorf("reading deal count: invalid number"))
continue
}
color.Blue(".. Picking miners")
@ -859,12 +862,13 @@ uiLoop:
a, err := api.ClientQueryAsk(ctx, *mi.PeerId, maddr)
if err != nil {
printErr(xerrors.Errorf("failed to query ask: %w", err))
printErr(xerrors.Errorf("failed to query ask for miner %s: %w", maddr.String(), err))
state = "miner"
continue uiLoop
}
ask = append(ask, *a)
}
// TODO: run more validation

View File

@ -1,7 +1,9 @@
package cli
import (
"bytes"
"context"
"encoding/hex"
"fmt"
verifreg4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/verifreg"
@ -34,6 +36,7 @@ var filplusCmd = &cli.Command{
filplusListClientsCmd,
filplusCheckClientCmd,
filplusCheckNotaryCmd,
filplusSignRemoveDataCapProposal,
},
}
@ -274,3 +277,112 @@ func checkNotary(ctx context.Context, api v0api.FullNode, vaddr address.Address)
return st.VerifierDataCap(vid)
}
var filplusSignRemoveDataCapProposal = &cli.Command{
Name: "sign-remove-data-cap-proposal",
Usage: "allows a notary to sign a Remove Data Cap Proposal",
Flags: []cli.Flag{
&cli.Int64Flag{
Name: "id",
Usage: "specify the RemoveDataCapProposal ID (will look up on chain if unspecified)",
Required: false,
},
},
Action: func(cctx *cli.Context) error {
if cctx.Args().Len() != 3 {
return fmt.Errorf("must specify three arguments: notary address, client address, and allowance to remove")
}
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return xerrors.Errorf("failed to get full node api: %w", err)
}
defer closer()
ctx := ReqContext(cctx)
act, err := api.StateGetActor(ctx, verifreg.Address, types.EmptyTSK)
if err != nil {
return xerrors.Errorf("failed to get verifreg actor: %w", err)
}
apibs := blockstore.NewAPIBlockstore(api)
store := adt.WrapStore(ctx, cbor.NewCborStore(apibs))
st, err := verifreg.Load(store, act)
if err != nil {
return xerrors.Errorf("failed to load verified registry state: %w", err)
}
verifier, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
verifierIdAddr, err := api.StateLookupID(ctx, verifier, types.EmptyTSK)
if err != nil {
return err
}
client, err := address.NewFromString(cctx.Args().Get(1))
if err != nil {
return err
}
clientIdAddr, err := api.StateLookupID(ctx, client, types.EmptyTSK)
if err != nil {
return err
}
allowanceToRemove, err := types.BigFromString(cctx.Args().Get(2))
if err != nil {
return err
}
_, dataCap, err := st.VerifiedClientDataCap(clientIdAddr)
if err != nil {
return xerrors.Errorf("failed to find verified client data cap: %w", err)
}
if dataCap.LessThanEqual(big.Zero()) {
return xerrors.Errorf("client data cap %s is less than amount requested to be removed %s", dataCap.String(), allowanceToRemove.String())
}
found, _, err := checkNotary(ctx, api, verifier)
if err != nil {
return xerrors.Errorf("failed to check notary status: %w", err)
}
if !found {
return xerrors.New("verifier address must be a notary")
}
id := cctx.Uint64("id")
if id == 0 {
_, id, err = st.RemoveDataCapProposalID(verifierIdAddr, clientIdAddr)
if err != nil {
return xerrors.Errorf("failed find remove data cap proposal id: %w", err)
}
}
params := verifreg.RemoveDataCapProposal{
RemovalProposalID: verifreg.RmDcProposalID{ProposalID: id},
DataCapAmount: allowanceToRemove,
VerifiedClient: clientIdAddr,
}
paramBuf := new(bytes.Buffer)
paramBuf.WriteString(verifreg.SignatureDomainSeparation_RemoveDataCap)
err = params.MarshalCBOR(paramBuf)
if err != nil {
return xerrors.Errorf("failed to marshall paramBuf: %w", err)
}
sig, err := api.WalletSign(ctx, verifier, paramBuf.Bytes())
if err != nil {
return xerrors.Errorf("failed to sign message: %w", err)
}
sigBytes := append([]byte{byte(sig.Type)}, sig.Data...)
fmt.Println(hex.EncodeToString(sigBytes))
return nil
},
}

32
cli/mocks_test.go Normal file
View File

@ -0,0 +1,32 @@
package cli
import (
"bytes"
"testing"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/mocks"
"github.com/golang/mock/gomock"
ucli "github.com/urfave/cli/v2"
)
// newMockAppWithFullAPI returns a gomock-ed CLI app used for unit tests
// see cli/util/api.go:GetFullNodeAPI for mock API injection
func NewMockAppWithFullAPI(t *testing.T, cmd *ucli.Command) (*ucli.App, *mocks.MockFullNode, *bytes.Buffer, func()) {
app := ucli.NewApp()
app.Commands = ucli.Commands{cmd}
app.Setup()
// create and inject the mock API into app Metadata
ctrl := gomock.NewController(t)
mockFullNode := mocks.NewMockFullNode(ctrl)
var fullNode api.FullNode = mockFullNode
app.Metadata["test-full-api"] = fullNode
// this will only work if the implementation uses the app.Writer,
// if it uses fmt.*, it has to be refactored
buf := &bytes.Buffer{}
app.Writer = buf
return app, mockFullNode, buf, ctrl.Finish
}

View File

@ -60,6 +60,8 @@ var MpoolPending = &cli.Command{
},
},
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -72,7 +74,7 @@ var MpoolPending = &cli.Command{
if tos := cctx.String("to"); tos != "" {
a, err := address.NewFromString(tos)
if err != nil {
return fmt.Errorf("given 'to' address %q was invalid: %w", tos, err)
return xerrors.Errorf("given 'to' address %q was invalid: %w", tos, err)
}
toa = a
}
@ -80,7 +82,7 @@ var MpoolPending = &cli.Command{
if froms := cctx.String("from"); froms != "" {
a, err := address.NewFromString(froms)
if err != nil {
return fmt.Errorf("given 'from' address %q was invalid: %w", froms, err)
return xerrors.Errorf("given 'from' address %q was invalid: %w", froms, err)
}
froma = a
}
@ -119,13 +121,13 @@ var MpoolPending = &cli.Command{
}
if cctx.Bool("cids") {
fmt.Println(msg.Cid())
afmt.Println(msg.Cid())
} else {
out, err := json.MarshalIndent(msg, "", " ")
if err != nil {
return err
}
fmt.Println(string(out))
afmt.Println(string(out))
}
}
@ -216,6 +218,8 @@ var MpoolStat = &cli.Command{
},
},
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -234,6 +238,7 @@ var MpoolStat = &cli.Command{
currTs := ts
for i := 0; i < cctx.Int("basefee-lookback"); i++ {
currTs, err = api.ChainGetTipSet(ctx, currTs.Parents())
if err != nil {
return xerrors.Errorf("walking chain: %w", err)
}
@ -296,7 +301,7 @@ var MpoolStat = &cli.Command{
for a, bkt := range buckets {
act, err := api.StateGetActor(ctx, a, ts.Key())
if err != nil {
fmt.Printf("%s, err: %s\n", a, err)
afmt.Printf("%s, err: %s\n", a, err)
continue
}
@ -350,11 +355,11 @@ var MpoolStat = &cli.Command{
total.belowPast += stat.belowPast
total.gasLimit = big.Add(total.gasLimit, stat.gasLimit)
fmt.Printf("%s: Nonce past: %d, cur: %d, future: %d; FeeCap cur: %d, min-%d: %d, gasLimit: %s\n", stat.addr, stat.past, stat.cur, stat.future, stat.belowCurr, cctx.Int("basefee-lookback"), stat.belowPast, stat.gasLimit)
afmt.Printf("%s: Nonce past: %d, cur: %d, future: %d; FeeCap cur: %d, min-%d: %d, gasLimit: %s\n", stat.addr, stat.past, stat.cur, stat.future, stat.belowCurr, cctx.Int("basefee-lookback"), stat.belowPast, stat.gasLimit)
}
fmt.Println("-----")
fmt.Printf("total: Nonce past: %d, cur: %d, future: %d; FeeCap cur: %d, min-%d: %d, gasLimit: %s\n", total.past, total.cur, total.future, total.belowCurr, cctx.Int("basefee-lookback"), total.belowPast, total.gasLimit)
afmt.Println("-----")
afmt.Printf("total: Nonce past: %d, cur: %d, future: %d; FeeCap cur: %d, min-%d: %d, gasLimit: %s\n", total.past, total.cur, total.future, total.belowCurr, cctx.Int("basefee-lookback"), total.belowPast, total.gasLimit)
return nil
},
@ -385,8 +390,9 @@ var MpoolReplaceCmd = &cli.Command{
Usage: "Spend up to X FIL for this message in units of FIL. Previously when flag was `max-fee` units were in attoFIL. Applicable for auto mode",
},
},
ArgsUsage: "<from nonce> | <message-cid>",
ArgsUsage: "<from> <nonce> | <message-cid>",
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
@ -407,13 +413,14 @@ var MpoolReplaceCmd = &cli.Command{
msg, err := api.ChainGetMessage(ctx, mcid)
if err != nil {
return fmt.Errorf("could not find referenced message: %w", err)
return xerrors.Errorf("could not find referenced message: %w", err)
}
from = msg.From
nonce = msg.Nonce
case 2:
f, err := address.NewFromString(cctx.Args().Get(0))
arg0 := cctx.Args().Get(0)
f, err := address.NewFromString(arg0)
if err != nil {
return err
}
@ -448,7 +455,7 @@ var MpoolReplaceCmd = &cli.Command{
}
if found == nil {
return fmt.Errorf("no pending message found from %s with nonce %d", from, nonce)
return xerrors.Errorf("no pending message found from %s with nonce %d", from, nonce)
}
msg := found.Message
@ -460,7 +467,7 @@ var MpoolReplaceCmd = &cli.Command{
if cctx.IsSet("fee-limit") {
maxFee, err := types.ParseFIL(cctx.String("fee-limit"))
if err != nil {
return fmt.Errorf("parsing max-spend: %w", err)
return xerrors.Errorf("parsing max-spend: %w", err)
}
mss = &lapi.MessageSendSpec{
MaxFee: abi.TokenAmount(maxFee),
@ -472,7 +479,7 @@ var MpoolReplaceCmd = &cli.Command{
msg.GasPremium = abi.NewTokenAmount(0)
retm, err := api.GasEstimateMessageGas(ctx, &msg, mss, types.EmptyTSK)
if err != nil {
return fmt.Errorf("failed to estimate gas values: %w", err)
return xerrors.Errorf("failed to estimate gas values: %w", err)
}
msg.GasPremium = big.Max(retm.GasPremium, minRBF)
@ -489,26 +496,26 @@ var MpoolReplaceCmd = &cli.Command{
}
msg.GasPremium, err = types.BigFromString(cctx.String("gas-premium"))
if err != nil {
return fmt.Errorf("parsing gas-premium: %w", err)
return xerrors.Errorf("parsing gas-premium: %w", err)
}
// TODO: estimate fee cap here
msg.GasFeeCap, err = types.BigFromString(cctx.String("gas-feecap"))
if err != nil {
return fmt.Errorf("parsing gas-feecap: %w", err)
return xerrors.Errorf("parsing gas-feecap: %w", err)
}
}
smsg, err := api.WalletSignMessage(ctx, msg.From, &msg)
if err != nil {
return fmt.Errorf("failed to sign message: %w", err)
return xerrors.Errorf("failed to sign message: %w", err)
}
cid, err := api.MpoolPush(ctx, smsg)
if err != nil {
return fmt.Errorf("failed to push new message to mempool: %w", err)
return xerrors.Errorf("failed to push new message to mempool: %w", err)
}
fmt.Println("new message cid: ", cid)
afmt.Println("new message cid: ", cid)
return nil
},
}
@ -531,6 +538,8 @@ var MpoolFindCmd = &cli.Command{
},
},
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -548,7 +557,7 @@ var MpoolFindCmd = &cli.Command{
if cctx.IsSet("to") {
a, err := address.NewFromString(cctx.String("to"))
if err != nil {
return fmt.Errorf("'to' address was invalid: %w", err)
return xerrors.Errorf("'to' address was invalid: %w", err)
}
toFilter = a
@ -557,7 +566,7 @@ var MpoolFindCmd = &cli.Command{
if cctx.IsSet("from") {
a, err := address.NewFromString(cctx.String("from"))
if err != nil {
return fmt.Errorf("'from' address was invalid: %w", err)
return xerrors.Errorf("'from' address was invalid: %w", err)
}
fromFilter = a
@ -591,7 +600,7 @@ var MpoolFindCmd = &cli.Command{
return err
}
fmt.Println(string(b))
afmt.Println(string(b))
return nil
},
}
@ -605,6 +614,8 @@ var MpoolConfig = &cli.Command{
return cli.ShowCommandHelp(cctx, cctx.Command.Name)
}
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -624,7 +635,7 @@ var MpoolConfig = &cli.Command{
return err
}
fmt.Println(string(bytes))
afmt.Println(string(bytes))
} else {
cfg := new(types.MpoolConfig)
bytes := []byte(cctx.Args().Get(0))
@ -651,6 +662,8 @@ var MpoolGasPerfCmd = &cli.Command{
},
},
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -717,7 +730,7 @@ var MpoolGasPerfCmd = &cli.Command{
gasReward := getGasReward(m)
gasPerf := getGasPerf(gasReward, m.Message.GasLimit)
fmt.Printf("%s\t%d\t%s\t%f\n", m.Message.From, m.Message.Nonce, gasReward, gasPerf)
afmt.Printf("%s\t%d\t%s\t%f\n", m.Message.From, m.Message.Nonce, gasReward, gasPerf)
}
return nil

582
cli/mpool_test.go Normal file
View File

@ -0,0 +1,582 @@
//stm: #cli
package cli
import (
"context"
"fmt"
"testing"
"encoding/json"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/mock"
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)
func TestStat(t *testing.T) {
t.Run("local", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("mpool", MpoolStat))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// add blocks to the chain
first := mock.TipSet(mock.MkBlock(nil, 5, 4))
head := mock.TipSet(mock.MkBlock(first, 15, 7))
// create a signed message to be returned as a pending message
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
senderAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
toAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
sm := mock.MkMessage(senderAddr, toAddr, 1, w)
// mock actor to return for the sender
actor := types.Actor{Nonce: 2, Balance: big.NewInt(200000)}
gomock.InOrder(
mockApi.EXPECT().ChainHead(ctx).Return(head, nil),
mockApi.EXPECT().ChainGetTipSet(ctx, head.Parents()).Return(first, nil),
mockApi.EXPECT().WalletList(ctx).Return([]address.Address{senderAddr, toAddr}, nil),
mockApi.EXPECT().MpoolPending(ctx, types.EmptyTSK).Return([]*types.SignedMessage{sm}, nil),
mockApi.EXPECT().StateGetActor(ctx, senderAddr, head.Key()).Return(&actor, nil),
)
//stm: @CLI_MEMPOOL_STAT_002
err = app.Run([]string{"mpool", "stat", "--basefee-lookback", "1", "--local"})
assert.NoError(t, err)
assert.Contains(t, buf.String(), "Nonce past: 1")
})
t.Run("all", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("mpool", MpoolStat))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// add blocks to the chain
first := mock.TipSet(mock.MkBlock(nil, 5, 4))
head := mock.TipSet(mock.MkBlock(first, 15, 7))
// create a signed message to be returned as a pending message
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
senderAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
toAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
sm := mock.MkMessage(senderAddr, toAddr, 1, w)
// mock actor to return for the sender
actor := types.Actor{Nonce: 2, Balance: big.NewInt(200000)}
gomock.InOrder(
mockApi.EXPECT().ChainHead(ctx).Return(head, nil),
mockApi.EXPECT().ChainGetTipSet(ctx, head.Parents()).Return(first, nil),
mockApi.EXPECT().MpoolPending(ctx, types.EmptyTSK).Return([]*types.SignedMessage{sm}, nil),
mockApi.EXPECT().StateGetActor(ctx, senderAddr, head.Key()).Return(&actor, nil),
)
//stm: @CLI_MEMPOOL_STAT_001
err = app.Run([]string{"mpool", "stat", "--basefee-lookback", "1"})
assert.NoError(t, err)
assert.Contains(t, buf.String(), "Nonce past: 1")
})
}
func TestPending(t *testing.T) {
t.Run("all", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("mpool", MpoolPending))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// create a signed message to be returned as a pending message
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
senderAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
toAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
sm := mock.MkMessage(senderAddr, toAddr, 1, w)
gomock.InOrder(
mockApi.EXPECT().MpoolPending(ctx, types.EmptyTSK).Return([]*types.SignedMessage{sm}, nil),
)
//stm: @CLI_MEMPOOL_PENDING_001
err = app.Run([]string{"mpool", "pending", "--cids"})
assert.NoError(t, err)
assert.Contains(t, buf.String(), sm.Cid().String())
})
t.Run("local", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("mpool", MpoolPending))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// create a signed message to be returned as a pending message
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
senderAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
toAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
sm := mock.MkMessage(senderAddr, toAddr, 1, w)
gomock.InOrder(
mockApi.EXPECT().WalletList(ctx).Return([]address.Address{senderAddr}, nil),
mockApi.EXPECT().MpoolPending(ctx, types.EmptyTSK).Return([]*types.SignedMessage{sm}, nil),
)
//stm: @CLI_MEMPOOL_PENDING_002
err = app.Run([]string{"mpool", "pending", "--local"})
assert.NoError(t, err)
assert.Contains(t, buf.String(), sm.Cid().String())
})
t.Run("to", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("mpool", MpoolPending))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// create a signed message to be returned as a pending message
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
senderAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
toAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
sm := mock.MkMessage(senderAddr, toAddr, 1, w)
gomock.InOrder(
mockApi.EXPECT().MpoolPending(ctx, types.EmptyTSK).Return([]*types.SignedMessage{sm}, nil),
)
//stm: @CLI_MEMPOOL_PENDING_003
err = app.Run([]string{"mpool", "pending", "--to", sm.Message.To.String()})
assert.NoError(t, err)
assert.Contains(t, buf.String(), sm.Cid().String())
})
t.Run("from", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("mpool", MpoolPending))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// create a signed message to be returned as a pending message
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
senderAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
toAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
sm := mock.MkMessage(senderAddr, toAddr, 1, w)
gomock.InOrder(
mockApi.EXPECT().MpoolPending(ctx, types.EmptyTSK).Return([]*types.SignedMessage{sm}, nil),
)
//stm: @CLI_MEMPOOL_PENDING_004
err = app.Run([]string{"mpool", "pending", "--from", sm.Message.From.String()})
assert.NoError(t, err)
assert.Contains(t, buf.String(), sm.Cid().String())
})
}
func TestReplace(t *testing.T) {
t.Run("manual", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("mpool", MpoolReplaceCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// create a signed message to be returned as a pending message
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
senderAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
toAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
sm := mock.MkMessage(senderAddr, toAddr, 1, w)
gomock.InOrder(
mockApi.EXPECT().ChainGetMessage(ctx, sm.Cid()).Return(&sm.Message, nil),
mockApi.EXPECT().ChainHead(ctx).Return(nil, nil),
mockApi.EXPECT().MpoolPending(ctx, types.EmptyTSK).Return([]*types.SignedMessage{sm}, nil),
mockApi.EXPECT().WalletSignMessage(ctx, sm.Message.From, &sm.Message).Return(sm, nil),
mockApi.EXPECT().MpoolPush(ctx, sm).Return(sm.Cid(), nil),
)
//stm: @CLI_MEMPOOL_REPLACE_002
err = app.Run([]string{"mpool", "replace", "--gas-premium", "1", "--gas-feecap", "100", sm.Cid().String()})
assert.NoError(t, err)
assert.Contains(t, buf.String(), sm.Cid().String())
})
t.Run("auto", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("mpool", MpoolReplaceCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// create a signed message to be returned as a pending message
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
senderAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
toAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
sm := mock.MkMessage(senderAddr, toAddr, 1, w)
// gas fee param should be equal to the one passed in the cli invocation (used below)
maxFee := "1000000"
parsedFee, err := types.ParseFIL(maxFee)
if err != nil {
t.Fatal(err)
}
mss := api.MessageSendSpec{MaxFee: abi.TokenAmount(parsedFee)}
gomock.InOrder(
mockApi.EXPECT().ChainGetMessage(ctx, sm.Cid()).Return(&sm.Message, nil),
mockApi.EXPECT().ChainHead(ctx).Return(nil, nil),
mockApi.EXPECT().MpoolPending(ctx, types.EmptyTSK).Return([]*types.SignedMessage{sm}, nil),
// use gomock.any to match the message in expected api calls
// since the replace function modifies the message between calls, it would be pointless to try to match the exact argument
mockApi.EXPECT().GasEstimateMessageGas(ctx, gomock.Any(), &mss, types.EmptyTSK).Return(&sm.Message, nil),
mockApi.EXPECT().WalletSignMessage(ctx, sm.Message.From, gomock.Any()).Return(sm, nil),
mockApi.EXPECT().MpoolPush(ctx, sm).Return(sm.Cid(), nil),
)
//stm: @CLI_MEMPOOL_REPLACE_002
err = app.Run([]string{"mpool", "replace", "--auto", "--fee-limit", maxFee, sm.Cid().String()})
assert.NoError(t, err)
assert.Contains(t, buf.String(), sm.Cid().String())
})
t.Run("sender / nonce", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("mpool", MpoolReplaceCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// create a signed message to be returned as a pending message
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
senderAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
toAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
sm := mock.MkMessage(senderAddr, toAddr, 1, w)
// gas fee param should be equal to the one passed in the cli invocation (used below)
maxFee := "1000000"
parsedFee, err := types.ParseFIL(maxFee)
if err != nil {
t.Fatal(err)
}
mss := api.MessageSendSpec{MaxFee: abi.TokenAmount(parsedFee)}
gomock.InOrder(
mockApi.EXPECT().ChainHead(ctx).Return(nil, nil),
mockApi.EXPECT().MpoolPending(ctx, types.EmptyTSK).Return([]*types.SignedMessage{sm}, nil),
// use gomock.any to match the message in expected api calls
// since the replace function modifies the message between calls, it would be pointless to try to match the exact argument
mockApi.EXPECT().GasEstimateMessageGas(ctx, gomock.Any(), &mss, types.EmptyTSK).Return(&sm.Message, nil),
mockApi.EXPECT().WalletSignMessage(ctx, sm.Message.From, gomock.Any()).Return(sm, nil),
mockApi.EXPECT().MpoolPush(ctx, sm).Return(sm.Cid(), nil),
)
//stm: @CLI_MEMPOOL_REPLACE_001
err = app.Run([]string{"mpool", "replace", "--auto", "--fee-limit", maxFee, sm.Message.From.String(), fmt.Sprint(sm.Message.Nonce)})
assert.NoError(t, err)
assert.Contains(t, buf.String(), sm.Cid().String())
})
}
func TestFindMsg(t *testing.T) {
t.Run("from", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("mpool", MpoolFindCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// create a signed message to be returned as a pending message
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
senderAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
toAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
sm := mock.MkMessage(senderAddr, toAddr, 1, w)
gomock.InOrder(
mockApi.EXPECT().MpoolPending(ctx, types.EmptyTSK).Return([]*types.SignedMessage{sm}, nil),
)
//stm: @CLI_MEMPOOL_FIND_001
err = app.Run([]string{"mpool", "find", "--from", sm.Message.From.String()})
assert.NoError(t, err)
assert.Contains(t, buf.String(), sm.Cid().String())
})
t.Run("to", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("mpool", MpoolFindCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// create a signed message to be returned as a pending message
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
senderAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
toAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
sm := mock.MkMessage(senderAddr, toAddr, 1, w)
gomock.InOrder(
mockApi.EXPECT().MpoolPending(ctx, types.EmptyTSK).Return([]*types.SignedMessage{sm}, nil),
)
//stm: @CLI_MEMPOOL_FIND_002
err = app.Run([]string{"mpool", "find", "--to", sm.Message.To.String()})
assert.NoError(t, err)
assert.Contains(t, buf.String(), sm.Cid().String())
})
t.Run("method", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("mpool", MpoolFindCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// create a signed message to be returned as a pending message
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
senderAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
toAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
sm := mock.MkMessage(senderAddr, toAddr, 1, w)
gomock.InOrder(
mockApi.EXPECT().MpoolPending(ctx, types.EmptyTSK).Return([]*types.SignedMessage{sm}, nil),
)
//stm: @CLI_MEMPOOL_FIND_003
err = app.Run([]string{"mpool", "find", "--method", sm.Message.Method.String()})
assert.NoError(t, err)
assert.Contains(t, buf.String(), sm.Cid().String())
})
}
func TestGasPerf(t *testing.T) {
t.Run("all", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("mpool", MpoolGasPerfCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// add blocks to the chain
first := mock.TipSet(mock.MkBlock(nil, 5, 4))
head := mock.TipSet(mock.MkBlock(first, 15, 7))
// create a signed message to be returned as a pending message
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
senderAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
toAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
sm := mock.MkMessage(senderAddr, toAddr, 13, w)
gomock.InOrder(
mockApi.EXPECT().MpoolPending(ctx, types.EmptyTSK).Return([]*types.SignedMessage{sm}, nil),
mockApi.EXPECT().ChainHead(ctx).Return(head, nil),
)
//stm: @CLI_MEMPOOL_GAS_PERF_002
err = app.Run([]string{"mpool", "gas-perf", "--all", "true"})
assert.NoError(t, err)
assert.Contains(t, buf.String(), sm.Message.From.String())
assert.Contains(t, buf.String(), fmt.Sprint(sm.Message.Nonce))
})
t.Run("local", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("mpool", MpoolGasPerfCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// add blocks to the chain
first := mock.TipSet(mock.MkBlock(nil, 5, 4))
head := mock.TipSet(mock.MkBlock(first, 15, 7))
// create a signed message to be returned as a pending message
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
senderAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
toAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
sm := mock.MkMessage(senderAddr, toAddr, 13, w)
gomock.InOrder(
mockApi.EXPECT().MpoolPending(ctx, types.EmptyTSK).Return([]*types.SignedMessage{sm}, nil),
mockApi.EXPECT().WalletList(ctx).Return([]address.Address{senderAddr}, nil),
mockApi.EXPECT().ChainHead(ctx).Return(head, nil),
)
//stm: @CLI_MEMPOOL_GAS_PERF_001
err = app.Run([]string{"mpool", "gas-perf"})
assert.NoError(t, err)
assert.Contains(t, buf.String(), sm.Message.From.String())
assert.Contains(t, buf.String(), fmt.Sprint(sm.Message.Nonce))
})
}
func TestConfig(t *testing.T) {
t.Run("get", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("mpool", MpoolConfig))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
senderAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
mpoolCfg := &types.MpoolConfig{PriorityAddrs: []address.Address{senderAddr}, SizeLimitHigh: 1234567, SizeLimitLow: 6, ReplaceByFeeRatio: 0.25}
gomock.InOrder(
mockApi.EXPECT().MpoolGetConfig(ctx).Return(mpoolCfg, nil),
)
//stm: @CLI_MEMPOOL_CONFIG_001
err = app.Run([]string{"mpool", "config"})
assert.NoError(t, err)
assert.Contains(t, buf.String(), mpoolCfg.PriorityAddrs[0].String())
assert.Contains(t, buf.String(), fmt.Sprint(mpoolCfg.SizeLimitHigh))
assert.Contains(t, buf.String(), fmt.Sprint(mpoolCfg.SizeLimitLow))
assert.Contains(t, buf.String(), fmt.Sprint(mpoolCfg.ReplaceByFeeRatio))
})
t.Run("set", func(t *testing.T) {
app, mockApi, _, done := NewMockAppWithFullAPI(t, WithCategory("mpool", MpoolConfig))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
senderAddr, err := w.WalletNew(context.Background(), types.KTSecp256k1)
if err != nil {
t.Fatal(err)
}
mpoolCfg := &types.MpoolConfig{PriorityAddrs: []address.Address{senderAddr}, SizeLimitHigh: 234567, SizeLimitLow: 3, ReplaceByFeeRatio: 0.33}
gomock.InOrder(
mockApi.EXPECT().MpoolSetConfig(ctx, mpoolCfg).Return(nil),
)
bytes, err := json.Marshal(mpoolCfg)
if err != nil {
t.Fatal(err)
}
//stm: @CLI_MEMPOOL_CONFIG_002
err = app.Run([]string{"mpool", "config", string(bytes)})
assert.NoError(t, err)
})
}

View File

@ -33,6 +33,8 @@ var SyncStatusCmd = &cli.Command{
Name: "status",
Usage: "check sync status",
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
apic, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -45,9 +47,9 @@ var SyncStatusCmd = &cli.Command{
return err
}
fmt.Println("sync status:")
afmt.Println("sync status:")
for _, ss := range state.ActiveSyncs {
fmt.Printf("worker %d:\n", ss.WorkerID)
afmt.Printf("worker %d:\n", ss.WorkerID)
var base, target []cid.Cid
var heightDiff int64
var theight abi.ChainEpoch
@ -62,20 +64,20 @@ var SyncStatusCmd = &cli.Command{
} else {
heightDiff = 0
}
fmt.Printf("\tBase:\t%s\n", base)
fmt.Printf("\tTarget:\t%s (%d)\n", target, theight)
fmt.Printf("\tHeight diff:\t%d\n", heightDiff)
fmt.Printf("\tStage: %s\n", ss.Stage)
fmt.Printf("\tHeight: %d\n", ss.Height)
afmt.Printf("\tBase:\t%s\n", base)
afmt.Printf("\tTarget:\t%s (%d)\n", target, theight)
afmt.Printf("\tHeight diff:\t%d\n", heightDiff)
afmt.Printf("\tStage: %s\n", ss.Stage)
afmt.Printf("\tHeight: %d\n", ss.Height)
if ss.End.IsZero() {
if !ss.Start.IsZero() {
fmt.Printf("\tElapsed: %s\n", time.Since(ss.Start))
afmt.Printf("\tElapsed: %s\n", time.Since(ss.Start))
}
} else {
fmt.Printf("\tElapsed: %s\n", ss.End.Sub(ss.Start))
afmt.Printf("\tElapsed: %s\n", ss.End.Sub(ss.Start))
}
if ss.Stage == api.StageSyncErrored {
fmt.Printf("\tError: %s\n", ss.Message)
afmt.Printf("\tError: %s\n", ss.Message)
}
}
return nil
@ -168,6 +170,8 @@ var SyncCheckBadCmd = &cli.Command{
Usage: "check if the given block was marked bad, and for what reason",
ArgsUsage: "[blockCid]",
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
napi, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@ -190,11 +194,11 @@ var SyncCheckBadCmd = &cli.Command{
}
if reason == "" {
fmt.Println("block was not marked as bad")
afmt.Println("block was not marked as bad")
return nil
}
fmt.Println(reason)
afmt.Println(reason)
return nil
},
}

189
cli/sync_test.go Normal file
View File

@ -0,0 +1,189 @@
package cli
import (
"context"
"fmt"
"testing"
"time"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/mock"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)
func TestSyncStatus(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("sync", SyncStatusCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ts1 := mock.TipSet(mock.MkBlock(nil, 0, 0))
ts2 := mock.TipSet(mock.MkBlock(ts1, 0, 0))
start := time.Now()
end := start.Add(time.Minute)
state := &api.SyncState{
ActiveSyncs: []api.ActiveSync{{
WorkerID: 1,
Base: ts1,
Target: ts2,
Stage: api.StageMessages,
Height: abi.ChainEpoch(0),
Start: start,
End: end,
Message: "whatever",
}},
VMApplied: 0,
}
mockApi.EXPECT().SyncState(ctx).Return(state, nil)
//stm: @CLI_SYNC_STATUS_001
err := app.Run([]string{"sync", "status"})
assert.NoError(t, err)
out := buf.String()
// output is plaintext, had to do string matching
assert.Contains(t, out, fmt.Sprintf("Base:\t[%s]", ts1.Blocks()[0].Cid().String()))
assert.Contains(t, out, fmt.Sprintf("Target:\t[%s]", ts2.Blocks()[0].Cid().String()))
assert.Contains(t, out, "Height diff:\t1")
assert.Contains(t, out, "Stage: message sync")
assert.Contains(t, out, "Height: 0")
assert.Contains(t, out, "Elapsed: 1m0s")
}
func TestSyncMarkBad(t *testing.T) {
app, mockApi, _, done := NewMockAppWithFullAPI(t, WithCategory("sync", SyncMarkBadCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
blk := mock.MkBlock(nil, 0, 0)
mockApi.EXPECT().SyncMarkBad(ctx, blk.Cid()).Return(nil)
//stm: @CLI_SYNC_MARK_BAD_001
err := app.Run([]string{"sync", "mark-bad", blk.Cid().String()})
assert.NoError(t, err)
}
func TestSyncUnmarkBad(t *testing.T) {
t.Run("one-block", func(t *testing.T) {
app, mockApi, _, done := NewMockAppWithFullAPI(t, WithCategory("sync", SyncUnmarkBadCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
blk := mock.MkBlock(nil, 0, 0)
mockApi.EXPECT().SyncUnmarkBad(ctx, blk.Cid()).Return(nil)
//stm: @CLI_SYNC_UNMARK_BAD_001
err := app.Run([]string{"sync", "unmark-bad", blk.Cid().String()})
assert.NoError(t, err)
})
t.Run("all", func(t *testing.T) {
app, mockApi, _, done := NewMockAppWithFullAPI(t, WithCategory("sync", SyncUnmarkBadCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
mockApi.EXPECT().SyncUnmarkAllBad(ctx).Return(nil)
//stm: @CLI_SYNC_UNMARK_BAD_002
err := app.Run([]string{"sync", "unmark-bad", "-all"})
assert.NoError(t, err)
})
}
func TestSyncCheckBad(t *testing.T) {
t.Run("not-bad", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("sync", SyncCheckBadCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
blk := mock.MkBlock(nil, 0, 0)
mockApi.EXPECT().SyncCheckBad(ctx, blk.Cid()).Return("", nil)
//stm: @CLI_SYNC_CHECK_BAD_002
err := app.Run([]string{"sync", "check-bad", blk.Cid().String()})
assert.NoError(t, err)
assert.Contains(t, buf.String(), "block was not marked as bad")
})
t.Run("bad", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("sync", SyncCheckBadCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
blk := mock.MkBlock(nil, 0, 0)
reason := "whatever"
mockApi.EXPECT().SyncCheckBad(ctx, blk.Cid()).Return(reason, nil)
//stm: @CLI_SYNC_CHECK_BAD_001
err := app.Run([]string{"sync", "check-bad", blk.Cid().String()})
assert.NoError(t, err)
assert.Contains(t, buf.String(), reason)
})
}
func TestSyncCheckpoint(t *testing.T) {
t.Run("tipset", func(t *testing.T) {
app, mockApi, _, done := NewMockAppWithFullAPI(t, WithCategory("sync", SyncCheckpointCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
blk := mock.MkBlock(nil, 0, 0)
ts := mock.TipSet(blk)
gomock.InOrder(
mockApi.EXPECT().ChainGetBlock(ctx, blk.Cid()).Return(blk, nil),
mockApi.EXPECT().SyncCheckpoint(ctx, ts.Key()).Return(nil),
)
//stm: @CLI_SYNC_CHECKPOINT_001
err := app.Run([]string{"sync", "checkpoint", blk.Cid().String()})
assert.NoError(t, err)
})
t.Run("epoch", func(t *testing.T) {
app, mockApi, _, done := NewMockAppWithFullAPI(t, WithCategory("sync", SyncCheckpointCmd))
defer done()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
epoch := abi.ChainEpoch(0)
blk := mock.MkBlock(nil, 0, 0)
ts := mock.TipSet(blk)
gomock.InOrder(
mockApi.EXPECT().ChainGetTipSetByHeight(ctx, epoch, types.EmptyTSK).Return(ts, nil),
mockApi.EXPECT().SyncCheckpoint(ctx, ts.Key()).Return(nil),
)
//stm: @CLI_SYNC_CHECKPOINT_002
err := app.Run([]string{"sync", "checkpoint", fmt.Sprintf("-epoch=%d", epoch)})
assert.NoError(t, err)
})
}

View File

@ -223,6 +223,11 @@ func GetCommonAPI(ctx *cli.Context) (api.CommonNet, jsonrpc.ClientCloser, error)
}
func GetFullNodeAPI(ctx *cli.Context) (v0api.FullNode, jsonrpc.ClientCloser, error) {
// use the mocked API in CLI unit tests, see cli/mocks_test.go for mock definition
if mock, ok := ctx.App.Metadata["test-full-api"]; ok {
return &v0api.WrapperV1Full{FullNode: mock.(v1api.FullNode)}, func() {}, nil
}
if tn, ok := ctx.App.Metadata["testnode-full"]; ok {
return &v0api.WrapperV1Full{FullNode: tn.(v1api.FullNode)}, func() {}, nil
}

View File

@ -276,6 +276,13 @@ var sealBenchCmd = &cli.Command{
if err != nil {
return xerrors.Errorf("failed to run seals: %w", err)
}
for _, s := range extendedSealedSectors {
sealedSectors = append(sealedSectors, proof.SectorInfo{
SealedCID: s.SealedCID,
SectorNumber: s.SectorNumber,
SealProof: s.SealProof,
})
}
} else {
// TODO: implement sbfs.List() and use that for all cases (preexisting sectorbuilder or not)

View File

@ -437,6 +437,7 @@ var provingCheckProvableCmd = &cli.Command{
}
var tocheck []storage.SectorRef
var update []bool
for _, info := range sectorInfos {
si := abi.SectorID{
Miner: abi.ActorID(mid),
@ -454,9 +455,10 @@ var provingCheckProvableCmd = &cli.Command{
ProofType: info.SealProof,
ID: si,
})
update = append(update, info.SectorKeyCID != nil)
}
bad, err := sapi.CheckProvable(ctx, info.WindowPoStProofType, tocheck, cctx.Bool("slow"))
bad, err := sapi.CheckProvable(ctx, info.WindowPoStProofType, tocheck, update, cctx.Bool("slow"))
if err != nil {
return err
}

View File

@ -39,9 +39,12 @@ func barString(total, y, g float64) string {
yBars := int(math.Round(y / total * barCols))
gBars := int(math.Round(g / total * barCols))
eBars := int(barCols) - yBars - gBars
return color.YellowString(strings.Repeat("|", yBars)) +
color.GreenString(strings.Repeat("|", gBars)) +
strings.Repeat(" ", eBars)
var barString = color.YellowString(strings.Repeat("|", yBars)) +
color.GreenString(strings.Repeat("|", gBars))
if eBars >= 0 {
barString += strings.Repeat(" ", eBars)
}
return barString
}
var sealingWorkersCmd = &cli.Command{

View File

@ -1535,11 +1535,23 @@ var sectorsSnapAbortCmd = &cli.Command{
Name: "abort-upgrade",
Usage: "Abort the attempted (SnapDeals) upgrade of a CC sector, reverting it to as before",
ArgsUsage: "<sectorNum>",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "really-do-it",
Usage: "pass this flag if you know what you are doing",
},
},
Action: func(cctx *cli.Context) error {
if cctx.Args().Len() != 1 {
return lcli.ShowHelp(cctx, xerrors.Errorf("must pass sector number"))
}
really := cctx.Bool("really-do-it")
if !really {
//nolint:golint
return fmt.Errorf("--really-do-it must be specified for this action to have an effect; you have been warned")
}
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err

View File

@ -1,8 +1,13 @@
package main
import (
"encoding/hex"
"fmt"
"github.com/filecoin-project/lotus/chain/actors/builtin/multisig"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/go-state-types/big"
"github.com/urfave/cli/v2"
@ -35,6 +40,7 @@ var verifRegCmd = &cli.Command{
verifRegListClientsCmd,
verifRegCheckClientCmd,
verifRegCheckVerifierCmd,
verifRegRemoveVerifiedClientDataCapCmd,
},
}
@ -409,3 +415,154 @@ var verifRegCheckVerifierCmd = &cli.Command{
return nil
},
}
var verifRegRemoveVerifiedClientDataCapCmd = &cli.Command{
Name: "remove-verified-client-data-cap",
Usage: "Remove data cap from verified client",
ArgsUsage: "<message sender> <client address> <allowance to remove> <verifier 1 address> <verifier 1 signature> <verifier 2 address> <verifier 2 signature>",
Action: func(cctx *cli.Context) error {
if cctx.Args().Len() != 7 {
return fmt.Errorf("must specify seven arguments: sender, client, allowance to remove, verifier 1 address, verifier 1 signature, verifier 2 address, verifier 2 signature")
}
srv, err := lcli.GetFullNodeServices(cctx)
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck
api := srv.FullNodeAPI()
ctx := lcli.ReqContext(cctx)
sender, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
client, err := address.NewFromString(cctx.Args().Get(1))
if err != nil {
return err
}
allowanceToRemove, err := types.BigFromString(cctx.Args().Get(2))
if err != nil {
return err
}
verifier1Addr, err := address.NewFromString(cctx.Args().Get(3))
if err != nil {
return err
}
verifier1Sig, err := hex.DecodeString(cctx.Args().Get(4))
if err != nil {
return err
}
verifier2Addr, err := address.NewFromString(cctx.Args().Get(5))
if err != nil {
return err
}
verifier2Sig, err := hex.DecodeString(cctx.Args().Get(6))
if err != nil {
return err
}
var sig1 crypto.Signature
if err := sig1.UnmarshalBinary(verifier1Sig); err != nil {
return xerrors.Errorf("couldn't unmarshal sig: %w", err)
}
var sig2 crypto.Signature
if err := sig2.UnmarshalBinary(verifier2Sig); err != nil {
return xerrors.Errorf("couldn't unmarshal sig: %w", err)
}
params, err := actors.SerializeParams(&verifreg.RemoveDataCapParams{
VerifiedClientToRemove: client,
DataCapAmountToRemove: allowanceToRemove,
VerifierRequest1: verifreg.RemoveDataCapRequest{
Verifier: verifier1Addr,
VerifierSignature: sig1,
},
VerifierRequest2: verifreg.RemoveDataCapRequest{
Verifier: verifier2Addr,
VerifierSignature: sig2,
},
})
if err != nil {
return err
}
vrk, err := api.StateVerifiedRegistryRootKey(ctx, types.EmptyTSK)
if err != nil {
return err
}
vrkState, err := api.StateGetActor(ctx, vrk, types.EmptyTSK)
if err != nil {
return err
}
apibs := blockstore.NewAPIBlockstore(api)
store := adt.WrapStore(ctx, cbor.NewCborStore(apibs))
st, err := multisig.Load(store, vrkState)
if err != nil {
return err
}
signers, err := st.Signers()
if err != nil {
return err
}
senderIsSigner := false
senderIdAddr, err := address.IDFromAddress(sender)
if err != nil {
return err
}
for _, signer := range signers {
signerIdAddr, err := address.IDFromAddress(signer)
if err != nil {
return err
}
if signerIdAddr == senderIdAddr {
senderIsSigner = true
}
}
if !senderIsSigner {
return fmt.Errorf("sender must be a vrk signer")
}
proto, err := api.MsigPropose(ctx, vrk, verifreg.Address, big.Zero(), sender, uint64(verifreg.Methods.RemoveVerifiedClientDataCap), params)
if err != nil {
return err
}
sm, _, err := srv.PublishMessage(ctx, proto, false)
if err != nil {
return err
}
msgCid := sm.Cid()
fmt.Printf("message sent, now waiting on cid: %s\n", msgCid)
mwait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
if err != nil {
return err
}
if mwait.Receipt.ExitCode != 0 {
return fmt.Errorf("failed to removed verified data cap: %d", mwait.Receipt.ExitCode)
}
//TODO: Internal msg might still have failed
return nil
},
}

View File

@ -338,6 +338,9 @@ Inputs:
"ProofType": 8
}
],
[
true
],
true
]
```

View File

@ -1826,7 +1826,8 @@ USAGE:
lotus-miner sectors abort-upgrade [command options] <sectorNum>
OPTIONS:
--help, -h show help (default: false)
--really-do-it pass this flag if you know what you are doing (default: false)
--help, -h show help (default: false)
```

View File

@ -1234,12 +1234,13 @@ USAGE:
lotus filplus command [command options] [arguments...]
COMMANDS:
grant-datacap give allowance to the specified verified client address
list-notaries list all notaries
list-clients list all verified clients
check-client-datacap check verified client remaining bytes
check-notary-datacap check a notary's remaining bytes
help, h Shows a list of commands or help for one command
grant-datacap give allowance to the specified verified client address
list-notaries list all notaries
list-clients list all verified clients
check-client-datacap check verified client remaining bytes
check-notary-datacap check a notary's remaining bytes
sign-remove-data-cap-proposal allows a notary to sign a Remove Data Cap Proposal
help, h Shows a list of commands or help for one command
OPTIONS:
--help, -h show help (default: false)
@ -1313,6 +1314,20 @@ OPTIONS:
```
### lotus filplus sign-remove-data-cap-proposal
```
NAME:
lotus filplus sign-remove-data-cap-proposal - allows a notary to sign a Remove Data Cap Proposal
USAGE:
lotus filplus sign-remove-data-cap-proposal [command options] [arguments...]
OPTIONS:
--id value specify the RemoveDataCapProposal ID (will look up on chain if unspecified) (default: 0)
--help, -h show help (default: false)
```
## lotus paych
```
NAME:
@ -1643,7 +1658,7 @@ NAME:
lotus mpool replace - replace a message in the mempool
USAGE:
lotus mpool replace [command options] <from nonce> | <message-cid>
lotus mpool replace [command options] <from> <nonce> | <message-cid>
OPTIONS:
--gas-feecap value gas feecap for new message (burn and pay to miner, attoFIL/GasUnit)

View File

@ -19,11 +19,11 @@ import (
// FaultTracker TODO: Track things more actively
type FaultTracker interface {
CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef, rg storiface.RGetter) (map[abi.SectorID]string, error)
CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef, update []bool, rg storiface.RGetter) (map[abi.SectorID]string, error)
}
// CheckProvable returns unprovable sectors
func (m *Manager) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef, rg storiface.RGetter) (map[abi.SectorID]string, error) {
func (m *Manager) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef, update []bool, rg storiface.RGetter) (map[abi.SectorID]string, error) {
var bad = make(map[abi.SectorID]string)
ssize, err := pp.SectorSize()
@ -32,72 +32,76 @@ func (m *Manager) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof,
}
// TODO: More better checks
for _, sector := range sectors {
for i, sector := range sectors {
err := func() error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
var fReplica string
var fCache string
locked, err := m.index.StorageTryLock(ctx, sector.ID, storiface.FTSealed|storiface.FTCache, storiface.FTNone)
if err != nil {
return xerrors.Errorf("acquiring sector lock: %w", err)
}
if !locked {
log.Warnw("CheckProvable Sector FAULT: can't acquire read lock", "sector", sector)
bad[sector.ID] = fmt.Sprint("can't acquire read lock")
return nil
}
lp, _, err := m.localStore.AcquireSector(ctx, sector, storiface.FTSealed|storiface.FTCache, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove)
if err != nil {
log.Warnw("CheckProvable Sector FAULT: acquire sector in checkProvable", "sector", sector, "error", err)
bad[sector.ID] = fmt.Sprintf("acquire sector failed: %s", err)
return nil
}
// temporary hack to make the check work with snapdeals
// will go away in https://github.com/filecoin-project/lotus/pull/7971
if lp.Sealed == "" || lp.Cache == "" {
// maybe it's update
if update[i] {
lockedUpdate, err := m.index.StorageTryLock(ctx, sector.ID, storiface.FTUpdate|storiface.FTUpdateCache, storiface.FTNone)
if err != nil {
return xerrors.Errorf("acquiring sector lock: %w", err)
}
if lockedUpdate {
lp, _, err = m.localStore.AcquireSector(ctx, sector, storiface.FTUpdate|storiface.FTUpdateCache, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove)
if err != nil {
log.Warnw("CheckProvable Sector FAULT: acquire sector in checkProvable", "sector", sector, "error", err)
bad[sector.ID] = fmt.Sprintf("acquire sector failed: %s", err)
return nil
}
lp.Sealed, lp.Cache = lp.Update, lp.UpdateCache
if !lockedUpdate {
log.Warnw("CheckProvable Sector FAULT: can't acquire read lock on update replica", "sector", sector)
bad[sector.ID] = fmt.Sprint("can't acquire read lock")
return nil
}
lp, _, err := m.localStore.AcquireSector(ctx, sector, storiface.FTUpdate|storiface.FTUpdateCache, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove)
if err != nil {
log.Warnw("CheckProvable Sector FAULT: acquire sector update replica in checkProvable", "sector", sector, "error", err)
bad[sector.ID] = fmt.Sprintf("acquire sector failed: %s", err)
return nil
}
fReplica, fCache = lp.Update, lp.UpdateCache
} else {
locked, err := m.index.StorageTryLock(ctx, sector.ID, storiface.FTSealed|storiface.FTCache, storiface.FTNone)
if err != nil {
return xerrors.Errorf("acquiring sector lock: %w", err)
}
if !locked {
log.Warnw("CheckProvable Sector FAULT: can't acquire read lock", "sector", sector)
bad[sector.ID] = fmt.Sprint("can't acquire read lock")
return nil
}
lp, _, err := m.localStore.AcquireSector(ctx, sector, storiface.FTSealed|storiface.FTCache, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove)
if err != nil {
log.Warnw("CheckProvable Sector FAULT: acquire sector in checkProvable", "sector", sector, "error", err)
bad[sector.ID] = fmt.Sprintf("acquire sector failed: %s", err)
return nil
}
fReplica, fCache = lp.Sealed, lp.Cache
}
if lp.Sealed == "" || lp.Cache == "" {
log.Warnw("CheckProvable Sector FAULT: cache and/or sealed paths not found", "sector", sector, "sealed", lp.Sealed, "cache", lp.Cache)
bad[sector.ID] = fmt.Sprintf("cache and/or sealed paths not found, cache %q, sealed %q", lp.Cache, lp.Sealed)
if fReplica == "" || fCache == "" {
log.Warnw("CheckProvable Sector FAULT: cache and/or sealed paths not found", "sector", sector, "sealed", fReplica, "cache", fCache)
bad[sector.ID] = fmt.Sprintf("cache and/or sealed paths not found, cache %q, sealed %q", fCache, fReplica)
return nil
}
toCheck := map[string]int64{
lp.Sealed: 1,
filepath.Join(lp.Cache, "p_aux"): 0,
fReplica: 1,
filepath.Join(fCache, "p_aux"): 0,
}
addCachePathsForSectorSize(toCheck, lp.Cache, ssize)
addCachePathsForSectorSize(toCheck, fCache, ssize)
for p, sz := range toCheck {
st, err := os.Stat(p)
if err != nil {
log.Warnw("CheckProvable Sector FAULT: sector file stat error", "sector", sector, "sealed", lp.Sealed, "cache", lp.Cache, "file", p, "err", err)
log.Warnw("CheckProvable Sector FAULT: sector file stat error", "sector", sector, "sealed", fReplica, "cache", fCache, "file", p, "err", err)
bad[sector.ID] = fmt.Sprintf("%s", err)
return nil
}
if sz != 0 {
if st.Size() != int64(ssize)*sz {
log.Warnw("CheckProvable Sector FAULT: sector file is wrong size", "sector", sector, "sealed", lp.Sealed, "cache", lp.Cache, "file", p, "size", st.Size(), "expectSize", int64(ssize)*sz)
log.Warnw("CheckProvable Sector FAULT: sector file is wrong size", "sector", sector, "sealed", fReplica, "cache", fCache, "file", p, "size", st.Size(), "expectSize", int64(ssize)*sz)
bad[sector.ID] = fmt.Sprintf("%s is wrong size (got %d, expect %d)", p, st.Size(), int64(ssize)*sz)
return nil
}
@ -118,14 +122,14 @@ func (m *Manager) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof,
sector.ID.Number,
})
if err != nil {
log.Warnw("CheckProvable Sector FAULT: generating challenges", "sector", sector, "sealed", lp.Sealed, "cache", lp.Cache, "err", err)
log.Warnw("CheckProvable Sector FAULT: generating challenges", "sector", sector, "sealed", fReplica, "cache", fCache, "err", err)
bad[sector.ID] = fmt.Sprintf("generating fallback challenges: %s", err)
return nil
}
commr, err := rg(ctx, sector.ID)
if err != nil {
log.Warnw("CheckProvable Sector FAULT: getting commR", "sector", sector, "sealed", lp.Sealed, "cache", lp.Cache, "err", err)
log.Warnw("CheckProvable Sector FAULT: getting commR", "sector", sector, "sealed", fReplica, "cache", fCache, "err", err)
bad[sector.ID] = fmt.Sprintf("getting commR: %s", err)
return nil
}
@ -136,12 +140,12 @@ func (m *Manager) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof,
SectorNumber: sector.ID.Number,
SealedCID: commr,
},
CacheDirPath: lp.Cache,
CacheDirPath: fCache,
PoStProofType: wpp,
SealedSectorPath: lp.Sealed,
SealedSectorPath: fReplica,
}, ch.Challenges[sector.ID.Number])
if err != nil {
log.Warnw("CheckProvable Sector FAULT: generating vanilla proof", "sector", sector, "sealed", lp.Sealed, "cache", lp.Cache, "err", err)
log.Warnw("CheckProvable Sector FAULT: generating vanilla proof", "sector", sector, "sealed", fReplica, "cache", fCache, "err", err)
bad[sector.ID] = fmt.Sprintf("generating vanilla proof: %s", err)
return nil
}

View File

@ -505,7 +505,7 @@ func (mgr *SectorMgr) Remove(ctx context.Context, sector storage.SectorRef) erro
return nil
}
func (mgr *SectorMgr) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, ids []storage.SectorRef, rg storiface.RGetter) (map[abi.SectorID]string, error) {
func (mgr *SectorMgr) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, ids []storage.SectorRef, update []bool, rg storiface.RGetter) (map[abi.SectorID]string, error) {
bad := map[abi.SectorID]string{}
for _, sid := range ids {

View File

@ -294,6 +294,10 @@ func ftFromString(t string) (storiface.SectorFileType, error) {
return storiface.FTSealed, nil
case storiface.FTCache.String():
return storiface.FTCache, nil
case storiface.FTUpdate.String():
return storiface.FTUpdate, nil
case storiface.FTUpdateCache.String():
return storiface.FTUpdateCache, nil
default:
return 0, xerrors.Errorf("unknown sector file type: '%s'", t)
}

View File

@ -108,7 +108,8 @@ func sectorActive(ctx context.Context, api SealingAPI, maddr address.Address, to
if err != nil {
return false, xerrors.Errorf("failed to check active sectors: %w", err)
}
// Check if sector is among active sectors
// Ensure the upgraded sector is active
var found bool
for _, si := range active {
if si.SectorNumber == sector {

View File

@ -51,6 +51,7 @@ type TargetAPI interface {
MpoolPushUntrusted(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error)
MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error)
MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error)
MsigGetVestingSchedule(context.Context, address.Address, types.TipSetKey) (api.MsigVesting, error)
MsigGetPending(ctx context.Context, addr address.Address, ts types.TipSetKey) ([]*api.MsigTransaction, error)
StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error)
StateDealProviderCollateralBounds(ctx context.Context, size abi.PaddedPieceSize, verified bool, tsk types.TipSetKey) (api.DealCollateralBounds, error)
@ -282,6 +283,13 @@ func (gw *Node) MsigGetVested(ctx context.Context, addr address.Address, start t
return gw.target.MsigGetVested(ctx, addr, start, end)
}
func (gw *Node) MsigGetVestingSchedule(ctx context.Context, addr address.Address, tsk types.TipSetKey) (api.MsigVesting, error) {
if err := gw.checkTipsetKey(ctx, tsk); err != nil {
return api.MsigVesting{}, err
}
return gw.target.MsigGetVestingSchedule(ctx, addr, tsk)
}
func (gw *Node) MsigGetPending(ctx context.Context, addr address.Address, tsk types.TipSetKey) ([]*api.MsigTransaction, error) {
if err := gw.checkTipsetKey(ctx, tsk); err != nil {
return nil, err

9
go.mod
View File

@ -2,6 +2,8 @@ module github.com/filecoin-project/lotus
go 1.16
retract v1.14.0 // Accidentally force-pushed tag, use v1.14.1+ instead.
require (
contrib.go.opencensus.io/exporter/prometheus v0.4.0
github.com/BurntSushi/toml v0.4.1
@ -50,7 +52,7 @@ require (
github.com/filecoin-project/specs-actors/v4 v4.0.1
github.com/filecoin-project/specs-actors/v5 v5.0.4
github.com/filecoin-project/specs-actors/v6 v6.0.1
github.com/filecoin-project/specs-actors/v7 v7.0.0-rc1.0.20220118005651-2470cb39827e
github.com/filecoin-project/specs-actors/v7 v7.0.0
github.com/filecoin-project/specs-storage v0.2.0
github.com/filecoin-project/test-vectors/schema v0.0.5
github.com/gbrlsnchs/jwt/v3 v3.0.1
@ -98,6 +100,7 @@ require (
github.com/ipfs/go-metrics-prometheus v0.0.2
github.com/ipfs/go-path v0.0.7
github.com/ipfs/go-unixfs v0.3.1
github.com/ipfs/go-unixfsnode v1.2.0
github.com/ipfs/interface-go-ipfs-core v0.4.0
github.com/ipld/go-car v0.3.3
github.com/ipld/go-car/v2 v2.1.1
@ -108,7 +111,7 @@ require (
github.com/kelseyhightower/envconfig v1.4.0
github.com/libp2p/go-buffer-pool v0.0.2
github.com/libp2p/go-eventbus v0.2.1
github.com/libp2p/go-libp2p v0.18.0-rc4
github.com/libp2p/go-libp2p v0.18.0-rc5
github.com/libp2p/go-libp2p-connmgr v0.3.1 // indirect
github.com/libp2p/go-libp2p-core v0.14.0
github.com/libp2p/go-libp2p-discovery v0.6.0
@ -120,7 +123,7 @@ require (
github.com/libp2p/go-libp2p-record v0.1.3
github.com/libp2p/go-libp2p-resource-manager v0.1.4
github.com/libp2p/go-libp2p-routing-helpers v0.2.3
github.com/libp2p/go-libp2p-swarm v0.10.1
github.com/libp2p/go-libp2p-swarm v0.10.2
github.com/libp2p/go-libp2p-tls v0.3.1
github.com/libp2p/go-libp2p-yamux v0.8.2
github.com/libp2p/go-maddr-filter v0.1.0

14
go.sum
View File

@ -378,8 +378,9 @@ github.com/filecoin-project/specs-actors/v6 v6.0.0/go.mod h1:V1AYfi5GkHXipx1mnVi
github.com/filecoin-project/specs-actors/v6 v6.0.1 h1:laxvHNsvrq83Y9n+W7znVCePi3oLyRf0Rkl4jFO8Wew=
github.com/filecoin-project/specs-actors/v6 v6.0.1/go.mod h1:V1AYfi5GkHXipx1mnVivoICZh3wtwPxDVuds+fbfQtk=
github.com/filecoin-project/specs-actors/v7 v7.0.0-20211222192039-c83bea50c402/go.mod h1:p6LIOFezA1rgRLMewbvdi3Pp6SAu+q9FtJ9CAleSjrE=
github.com/filecoin-project/specs-actors/v7 v7.0.0-rc1.0.20220118005651-2470cb39827e h1:3P14MvJ5MA0jwEB4WDeROrci4o8KwVVAv2mJ70grbf0=
github.com/filecoin-project/specs-actors/v7 v7.0.0-rc1.0.20220118005651-2470cb39827e/go.mod h1:TA5FwCna+Yi36POaT7SLKXsgEDvJwc0V/L6ZsO19B9M=
github.com/filecoin-project/specs-actors/v7 v7.0.0 h1:FQN7tjt3o68hfb3qLFSJBoLMuOFY0REkFVLO/zXj8RU=
github.com/filecoin-project/specs-actors/v7 v7.0.0/go.mod h1:TA5FwCna+Yi36POaT7SLKXsgEDvJwc0V/L6ZsO19B9M=
github.com/filecoin-project/specs-storage v0.2.0 h1:Y4UDv0apRQ3zI2GiPPubi8JblpUZZphEdaJUxCutfyg=
github.com/filecoin-project/specs-storage v0.2.0/go.mod h1:Tb88Zq+IBJbvAn3mS89GYj3jdRThBTE/771HCVZdRJU=
github.com/filecoin-project/test-vectors/schema v0.0.5 h1:w3zHQhzM4pYxJDl21avXjOKBLF8egrvwUwjpT8TquDg=
@ -993,8 +994,8 @@ github.com/libp2p/go-libp2p v0.14.4/go.mod h1:EIRU0Of4J5S8rkockZM7eJp2S0UrCyi55m
github.com/libp2p/go-libp2p v0.16.0/go.mod h1:ump42BsirwAWxKzsCiFnTtN1Yc+DuPu76fyMX364/O4=
github.com/libp2p/go-libp2p v0.17.0/go.mod h1:Fkin50rsGdv5mm5BshBUtPRZknt9esfmYXBOYcwOTgw=
github.com/libp2p/go-libp2p v0.18.0-rc1/go.mod h1:RgYlH7IIWHXREimC92bw5Lg1V2R5XmSzuLHb5fTnr+8=
github.com/libp2p/go-libp2p v0.18.0-rc4 h1:OUsSbeu7q+Ck/bV9wHDxFzb08ORqBupHhpCmRBhWrJ8=
github.com/libp2p/go-libp2p v0.18.0-rc4/go.mod h1:wzmsk1ioOq9FGQys2BN5BIw4nugP6+R+CyW3JbPEbbs=
github.com/libp2p/go-libp2p v0.18.0-rc5 h1:88wWDHb9nNo0vBNCupLde3OTnFAkugOCNkrDfl3ivK4=
github.com/libp2p/go-libp2p v0.18.0-rc5/go.mod h1:aZPS5l84bDvCvP4jkyEUT/J6YOpUq33Fgqrs3K59mpI=
github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052/go.mod h1:nRMRTab+kZuk0LnKZpxhOVH/ndsdr2Nr//Zltc/vwgo=
github.com/libp2p/go-libp2p-asn-util v0.1.0 h1:rABPCO77SjdbJ/eJ/ynIo8vWICy1VEnL5JAxJbQLo1E=
github.com/libp2p/go-libp2p-asn-util v0.1.0/go.mod h1:wu+AnM9Ii2KgO5jMmS1rz9dvzTdj8BXqsPR9HR0XB7I=
@ -1180,8 +1181,8 @@ github.com/libp2p/go-libp2p-swarm v0.5.3/go.mod h1:NBn7eNW2lu568L7Ns9wdFrOhgRlkR
github.com/libp2p/go-libp2p-swarm v0.8.0/go.mod h1:sOMp6dPuqco0r0GHTzfVheVBh6UEL0L1lXUZ5ot2Fvc=
github.com/libp2p/go-libp2p-swarm v0.9.0/go.mod h1:2f8d8uxTJmpeqHF/1ujjdXZp+98nNIbujVOMEZxCbZ8=
github.com/libp2p/go-libp2p-swarm v0.10.0/go.mod h1:71ceMcV6Rg/0rIQ97rsZWMzto1l9LnNquef+efcRbmA=
github.com/libp2p/go-libp2p-swarm v0.10.1 h1:lXW3pgGt+BVmkzcFX61erX7l6Lt+WAamNhwa2Kf3eJM=
github.com/libp2p/go-libp2p-swarm v0.10.1/go.mod h1:Pdkq0QU5a+qu+oyqIV3bknMsnzk9lnNyKvB9acJ5aZs=
github.com/libp2p/go-libp2p-swarm v0.10.2 h1:UaXf+CTq6Ns1N2V1EgqJ9Q3xaRsiN7ImVlDMpirMAWw=
github.com/libp2p/go-libp2p-swarm v0.10.2/go.mod h1:Pdkq0QU5a+qu+oyqIV3bknMsnzk9lnNyKvB9acJ5aZs=
github.com/libp2p/go-libp2p-testing v0.0.1/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=
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=
@ -1297,8 +1298,9 @@ github.com/libp2p/go-tcp-transport v0.2.3/go.mod h1:9dvr03yqrPyYGIEN6Dy5UvdJZjyP
github.com/libp2p/go-tcp-transport v0.2.4/go.mod h1:9dvr03yqrPyYGIEN6Dy5UvdJZjyPFvl1S/igQ5QD1SU=
github.com/libp2p/go-tcp-transport v0.2.7/go.mod h1:lue9p1b3VmZj1MhhEGB/etmvF/nBQ0X9CW2DutBT3MM=
github.com/libp2p/go-tcp-transport v0.4.0/go.mod h1:0y52Rwrn4076xdJYu/51/qJIdxz+EWDAOG2S45sV3VI=
github.com/libp2p/go-tcp-transport v0.5.0 h1:3ZPW8HAuyRAuFzyabE0hSrCXKKSWzROnZZX7DtcIatY=
github.com/libp2p/go-tcp-transport v0.5.0/go.mod h1:UPPL0DIjQqiWRwVAb+CEQlaAG0rp/mCqJfIhFcLHc4Y=
github.com/libp2p/go-tcp-transport v0.5.1 h1:edOOs688VLZAozWC7Kj5/6HHXKNwi9M6wgRmmLa8M6Q=
github.com/libp2p/go-tcp-transport v0.5.1/go.mod h1:UPPL0DIjQqiWRwVAb+CEQlaAG0rp/mCqJfIhFcLHc4Y=
github.com/libp2p/go-testutil v0.0.1/go.mod h1:iAcJc/DKJQanJ5ws2V+u5ywdL2n12X1WbbEG+Jjy69I=
github.com/libp2p/go-testutil v0.1.0/go.mod h1:81b2n5HypcVyrCg/MJx4Wgfp/VHojytjVe/gLzZ2Ehc=
github.com/libp2p/go-ws-transport v0.0.5/go.mod h1:Qbl4BxPfXXhhd/o0wcrgoaItHqA9tnZjoFZnxykuaXU=

View File

@ -151,6 +151,11 @@ func NewEnsemble(t *testing.T, opts ...EnsembleOpt) *Ensemble {
return n
}
// Mocknet returns the underlying mocknet.
func (n *Ensemble) Mocknet() mocknet.Mocknet {
return n.mn
}
// FullNode enrolls a new full node.
func (n *Ensemble) FullNode(full *TestFullNode, opts ...NodeOpt) *Ensemble {
options := DefaultNodeOpts

View File

@ -6630,7 +6630,7 @@
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
"integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
"requires": {
"node-fetch": "^1.0.1",
"node-fetch": "^2.6.7",
"whatwg-fetch": ">=0.10.0"
}
},

View File

@ -11,6 +11,7 @@ import (
"github.com/ipfs/go-cid"
mdagipld "github.com/ipfs/go-ipld-format"
"github.com/ipfs/go-unixfsnode"
dagpb "github.com/ipld/go-codec-dagpb"
"github.com/ipld/go-ipld-prime"
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
@ -62,6 +63,7 @@ func TraverseDag(
return bytes.NewBuffer(node.RawData()), nil
}
unixfsnode.AddUnixFSReificationToLinkSystem(&linkSystem)
// this is how we pull the start node out of the DS
startLink := cidlink.Link{Cid: startFrom}

View File

@ -1272,7 +1272,7 @@ func (a *API) ClientCalcCommP(ctx context.Context, inpath string) (*api.CommPRet
//
// IF/WHEN this changes in the future we will have to be able to calculate
// "old style" commP, and thus will need to introduce a version switch or similar
arbitraryProofType := abi.RegisteredSealProof_StackedDrg32GiBV1_1
arbitraryProofType := abi.RegisteredSealProof_StackedDrg64GiBV1_1
rdr, err := os.Open(inpath)
if err != nil {

View File

@ -1127,7 +1127,7 @@ func (sm *StorageMinerAPI) CreateBackup(ctx context.Context, fpath string) error
return backup(ctx, sm.DS, fpath)
}
func (sm *StorageMinerAPI) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []sto.SectorRef, expensive bool) (map[abi.SectorNumber]string, error) {
func (sm *StorageMinerAPI) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []sto.SectorRef, update []bool, expensive bool) (map[abi.SectorNumber]string, error) {
var rg storiface.RGetter
if expensive {
rg = func(ctx context.Context, id abi.SectorID) (cid.Cid, error) {
@ -1143,7 +1143,7 @@ func (sm *StorageMinerAPI) CheckProvable(ctx context.Context, pp abi.RegisteredP
}
}
bad, err := sm.StorageMgr.CheckProvable(ctx, pp, sectors, rg)
bad, err := sm.StorageMgr.CheckProvable(ctx, pp, sectors, update, rg)
if err != nil {
return nil, err
}

View File

@ -206,6 +206,7 @@ func (s *WindowPoStScheduler) checkSectors(ctx context.Context, check bitfield.B
sectors := make(map[abi.SectorNumber]struct{})
var tocheck []storage.SectorRef
var update []bool
for _, info := range sectorInfos {
sectors[info.SectorNumber] = struct{}{}
tocheck = append(tocheck, storage.SectorRef{
@ -215,9 +216,10 @@ func (s *WindowPoStScheduler) checkSectors(ctx context.Context, check bitfield.B
Number: info.SectorNumber,
},
})
update = append(update, info.SectorKeyCID != nil)
}
bad, err := s.faultTracker.CheckProvable(ctx, s.proofType, tocheck, nil)
bad, err := s.faultTracker.CheckProvable(ctx, s.proofType, tocheck, update, nil)
if err != nil {
return bitfield.BitField{}, xerrors.Errorf("checking provable sectors: %w", err)
}

View File

@ -168,7 +168,7 @@ func (m mockVerif) GenerateWinningPoStSectorChallenge(context.Context, abi.Regis
type mockFaultTracker struct {
}
func (m mockFaultTracker) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef, rg storiface.RGetter) (map[abi.SectorID]string, error) {
func (m mockFaultTracker) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef, update []bool, rg storiface.RGetter) (map[abi.SectorID]string, error) {
// Returns "bad" sectors so just return empty map meaning all sectors are good
return map[abi.SectorID]string{}, nil
}

View File

@ -473,8 +473,8 @@ github.com/filecoin-project/specs-actors/v7 v7.0.0-20211117170924-fd07a4c7dff9/g
github.com/filecoin-project/specs-actors/v7 v7.0.0-20211222192039-c83bea50c402/go.mod h1:p6LIOFezA1rgRLMewbvdi3Pp6SAu+q9FtJ9CAleSjrE=
github.com/filecoin-project/specs-actors/v7 v7.0.0-rc1 h1:FuDaXIbcw2hRsFI8SDTmsGGCE+NumpF6aiBoU/2X5W4=
github.com/filecoin-project/specs-actors/v7 v7.0.0-rc1/go.mod h1:TA5FwCna+Yi36POaT7SLKXsgEDvJwc0V/L6ZsO19B9M=
github.com/filecoin-project/specs-storage v0.1.1-0.20211228030229-6d460d25a0c9 h1:oUYOvF7EvdXS0Zmk9mNkaB6Bu0l+WXBYPzVodKMiLug=
github.com/filecoin-project/specs-storage v0.1.1-0.20211228030229-6d460d25a0c9/go.mod h1:Tb88Zq+IBJbvAn3mS89GYj3jdRThBTE/771HCVZdRJU=
github.com/filecoin-project/specs-storage v0.2.0 h1:Y4UDv0apRQ3zI2GiPPubi8JblpUZZphEdaJUxCutfyg=
github.com/filecoin-project/specs-storage v0.2.0/go.mod h1:Tb88Zq+IBJbvAn3mS89GYj3jdRThBTE/771HCVZdRJU=
github.com/filecoin-project/test-vectors/schema v0.0.5/go.mod h1:iQ9QXLpYWL3m7warwvK1JC/pTri8mnfEmKygNDqqY6E=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ=