Compare commits
78
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6f454ac33 | ||
|
|
ee20eec451 | ||
|
|
9e2f56b0b7 | ||
|
|
5c315b4503 | ||
|
|
817358f1bb | ||
|
|
3debf005a8 | ||
|
|
087955e927 | ||
|
|
e33d398c33 | ||
|
|
9fc674feb4 | ||
|
|
0c875774ce | ||
|
|
3027283756 | ||
|
|
161cb32078 | ||
|
|
84bbf7f420 | ||
|
|
d022dba14a | ||
|
|
bf98ebe33e | ||
|
|
7d704bf86b | ||
|
|
aabb90293d | ||
|
|
6b8beabad0 | ||
|
|
438fa637df | ||
|
|
30b28b7f7b | ||
|
|
4b80bc93b0 | ||
|
|
2e78f2c267 | ||
|
|
8df9c483f3 | ||
|
|
7c1e0d2132 | ||
|
|
4bf7619d32 | ||
|
|
659babc6b3 | ||
|
|
38a2f82f78 | ||
|
|
7456cdeb42 | ||
|
|
0eac90b596 | ||
|
|
393ac5a7c7 | ||
|
|
d1718369a1 | ||
|
|
57af7e8d1e | ||
|
|
af3fe206d6 | ||
|
|
29f6d50c49 | ||
|
|
896edfe072 | ||
|
|
2010bf55b7 | ||
|
|
ccd000cc16 | ||
|
|
de05120e69 | ||
|
|
d8a669c85a | ||
|
|
87f1b7564c | ||
|
|
807504fa16 | ||
|
|
93e566d7e0 | ||
|
|
6193f4e062 | ||
|
|
364e3229b9 | ||
|
|
98d6d25c74 | ||
|
|
67bf952c5a | ||
|
|
0acc461cb1 | ||
|
|
cd82f79d35 | ||
|
|
e035e29595 | ||
|
|
b89e94ee86 | ||
|
|
fc41b72bb6 | ||
|
|
0086f76a90 | ||
|
|
92faf5c5f9 | ||
|
|
3eeec8d438 | ||
|
|
bf10de82df | ||
|
|
e9c526b218 | ||
|
|
2675ffb128 | ||
|
|
885f357c59 | ||
|
|
de41f63fa4 | ||
|
|
a4807b18bc | ||
|
|
b42625d824 | ||
|
|
71cf358ee3 | ||
|
|
5ea61abfe1 | ||
|
|
0292c979ab | ||
|
|
a78258efdf | ||
|
|
bee6c0e26e | ||
|
|
6e492e41d1 | ||
|
|
f943ea7d5d | ||
|
|
ed098390cb | ||
|
|
05f1a23c94 | ||
|
|
fecaeda382 | ||
|
|
3df85ca784 | ||
|
|
f1632376e7 | ||
|
|
20cb9425cc | ||
|
|
c7a6e32c96 | ||
|
|
2b15831672 | ||
|
|
043b63d8d8 | ||
|
|
f05cff478f |
+143
-2
@@ -1,7 +1,148 @@
|
||||
# lotus changelog
|
||||
# Lotus changelog
|
||||
|
||||
## 0.5.0 / 2020-08-2020
|
||||
|
||||
This version of Lotus will be used for the incentivized testnet Space Race competition,
|
||||
and can be considered mainnet-ready code. It includes some protocol
|
||||
changes, upgrades of core dependencies, and various bugfixes and UX/performance improvements.
|
||||
|
||||
# Highlights
|
||||
|
||||
Among the highlights included in this release are:
|
||||
|
||||
- Gas changes: We implemented EIP-1559 and introduced real gas values.
|
||||
- Deal-making: We now support "Committed Capacity" sectors, "fast-retrieval" deals,
|
||||
and the packing of multiple deals into a single sector.
|
||||
- Renamed features: We renamed some of the binaries, environment variables, and default
|
||||
paths associated with a Lotus node.
|
||||
|
||||
## Gas changes
|
||||
|
||||
We made some significant changes to the mechanics of gas in this release.
|
||||
|
||||
#### Network fee
|
||||
|
||||
We implemented something similar to
|
||||
[Ethereum's EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md).
|
||||
The `Message` structure had three changes:
|
||||
- The `GasPrice` field has been removed
|
||||
- A new `GasFeeCap` field has been added, which controls the maximum cost
|
||||
the sender incurs for the message
|
||||
- A new `GasPremium` field has been added, which controls the reward a miner
|
||||
earns for including the message
|
||||
|
||||
A sender will never be charged more than `GasFeeCap * GasLimit`.
|
||||
A miner will typically earn `GasPremium * GasLimit` as a reward.
|
||||
|
||||
The `Blockheader` structure has one new field, called `ParentBaseFee`.
|
||||
Informally speaking,the `ParentBaseFee`
|
||||
is increased when blocks are densely packed with messages, and decreased otherwise.
|
||||
|
||||
The `ParentBaseFee` is used when calculating how much a sender burns when executing a message. _Burning_ simply refers to sending attoFIL to a dedicated, unreachable account.
|
||||
A message causes `ParentBaseFee * GasUsed` attoFIL to be burnt.
|
||||
|
||||
#### Real gas values
|
||||
|
||||
This release also includes our first "real" gas costs for primitive operations.
|
||||
The costs were designed to account for both the _time_ that message execution takes,
|
||||
as well as the _space_ a message adds to the state tree.
|
||||
|
||||
## Deal-making changes
|
||||
|
||||
There are three key changes to the deal-making process.
|
||||
|
||||
#### Committed Capacity sectors
|
||||
|
||||
Miners can now pledge "Committed Capacity" (CC) sectors, which are explicitly
|
||||
stated as containing junk data, and must not include any deals. Miners can do this
|
||||
to increase their storage power, and win block rewards from this pledged storage.
|
||||
|
||||
They can mark these sectors as "upgradable" with `lotus-miner sectors mark-for-upgrade`.
|
||||
If the miner receives and accepts one or more storage deals, the sector that includes
|
||||
those deals will _replace_ the CC sector. This is intended to maximize the amount of useful
|
||||
storage on the Filecoin network.
|
||||
|
||||
#### Fast-retrieval deals
|
||||
|
||||
Clients can now include a `fast-retrieval` flag when proposing deals with storage miners.
|
||||
If set to true, the miner will include an extra copy of the deal data. This
|
||||
data can be quickly served in a retrieval deal, since it will not need to be unsealed.
|
||||
|
||||
#### Multiple deals per sector
|
||||
|
||||
Miners can now pack multiple deals into a single sector, so long as all the deals
|
||||
fit into the sector capacity. This should increase the packing efficiency of miners.
|
||||
|
||||
## Renamed features
|
||||
|
||||
To improve the user experience, we updated several names to mainatin
|
||||
standard prefixing, and to better reflect the meaning of the features being referenced.
|
||||
|
||||
In particular, the Lotus miner binary is now called `lotus-miner`, the default
|
||||
path for miner data is now `~/.lotusminer`, and the environment variable
|
||||
that sets the path for miner data is now `$LOTUS_MINER_PATH`. A full list of renamed
|
||||
features can be found [here](https://github.com/filecoin-project/lotus/issues/2304).
|
||||
|
||||
# Changelog
|
||||
|
||||
#### Downstream upgrades
|
||||
- Upgrades markets to v0.5.6 (https://github.com/filecoin-project/lotus/pull/3058)
|
||||
- Upgrades specs-actors to v0.9.3 (https://github.com/filecoin-project/lotus/pull/3151)
|
||||
|
||||
#### Core protocol
|
||||
- Introduces gas values, replacing placeholders (https://github.com/filecoin-project/lotus/pull/2343)
|
||||
- Implements EIP-1559, introducing a network base fee, message gas fee cap, and message gas fee premium (https://github.com/filecoin-project/lotus/pull/2874)
|
||||
- Implements Poisson Sortition for elections (https://github.com/filecoin-project/lotus/pull/2084)
|
||||
|
||||
#### Deal-making lifecycle
|
||||
- Introduces "Committed Capacity" sectors (https://github.com/filecoin-project/lotus/pull/2220)
|
||||
- Introduces "fast-retrieval" flag for deals (https://github.com/filecoin-project/lotus/pull/2323
|
||||
- Supports packing multiple deals into one sector (https://github.com/filecoin-project/storage-fsm/pull/38)
|
||||
|
||||
#### Enhancements
|
||||
|
||||
- Optimized message pool selection logic (https://github.com/filecoin-project/lotus/pull/2838)
|
||||
- Window-based scheduling of sealing tasks (https://github.com/filecoin-project/sector-storage/pull/67)
|
||||
- Faster window PoSt (https://github.com/filecoin-project/lotus/pull/2209/files)
|
||||
- Refactors the payment channel manager (https://github.com/filecoin-project/lotus/pull/2640)
|
||||
- Refactors blocksync (https://github.com/filecoin-project/lotus/pull/2715/files)
|
||||
|
||||
#### UX
|
||||
|
||||
- Provide status updates for data-transfer (https://github.com/filecoin-project/lotus/pull/3162, https://github.com/filecoin-project/lotus/pull/3191)
|
||||
- Miners can customise asks (https://github.com/filecoin-project/lotus/pull/2046)
|
||||
- Miners can toggle auto-acceptance of deals (https://github.com/filecoin-project/lotus/pull/1994)
|
||||
- Miners can maintain a blocklist of piece CIDs (https://github.com/filecoin-project/lotus/pull/2069)
|
||||
|
||||
# Contributors
|
||||
|
||||
The following contributors had 10 or more commits go into this release.
|
||||
We are grateful for every contribution!
|
||||
|
||||
| Contributor | Commits | Lines ± |
|
||||
|--------------------|---------|---------------|
|
||||
| magik6k | 361 | +13197/-6136 |
|
||||
| Kubuxu | 227 | +5670/-2587 |
|
||||
| arajasek | 120 | +2916/-1264 |
|
||||
| whyrusleeping | 112 | +3979/-1089 |
|
||||
| vyzo | 99 | +3343/-1305 |
|
||||
| dirkmc | 68 | +8732/-3621 |
|
||||
| laser | 45 | +1489/-501 |
|
||||
| hannahhoward | 43 | +2654/-990 |
|
||||
| frrist | 37 | +6630/-4338 |
|
||||
| schomatis | 28 | +3016/-1368 |
|
||||
| placer14 | 27 | +824/-350 |
|
||||
| raulk | 25 | +28718/-29849 |
|
||||
| mrsmkl | 22 | +560/-368 |
|
||||
| travisperson | 18 | +1354/-314 |
|
||||
| nonsense | 16 | +2956/-2842 |
|
||||
| ingar | 13 | +331/-123 |
|
||||
| daviddias | 11 | +311/-11 |
|
||||
| Stebalien | 11 | +1204/-980 |
|
||||
| RobQuistNL | 10 | +69/-74 |
|
||||
|
||||
## 0.1.0 / 2019-12-11
|
||||
|
||||
We are very excited to release **lotus** 0.1.0. This is our testnet release. To install lotus and join the testnet, please visit [docs.lotu.sh](docs.lotu.sh). Please file bug reports as [issues](https://github.com/filecoin-project/lotus/issues).
|
||||
We are very excited to release **lotus** 0.1.0. This is our testnet release. To install lotus and join the testnet, please visit [lotu.sh](lotu.sh). Please file bug reports as [issues](https://github.com/filecoin-project/lotus/issues).
|
||||
|
||||
A huge thank you to all contributors for this testnet release!
|
||||
@@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
<a href="https://docs.lotu.sh/" title="Lotus Docs">
|
||||
<a href="https://lotu.sh/" title="Lotus Docs">
|
||||
<img src="documentation/images/lotus_logo_h.png" alt="Project Lotus Logo" width="244" />
|
||||
</a>
|
||||
</p>
|
||||
@@ -18,7 +18,7 @@ Lotus is an implementation of the Filecoin Distributed Storage Network. For more
|
||||
|
||||
## Building & Documentation
|
||||
|
||||
For instructions on how to build lotus from source, please visit [https://docs.lotu.sh](https://docs.lotu.sh) or read the source [here](https://github.com/filecoin-project/lotus/tree/master/documentation).
|
||||
For instructions on how to build lotus from source, please visit [https://lotu.sh](https://lotu.sh) or read the source [here](https://github.com/filecoin-project/lotus/tree/master/documentation).
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
||||
@@ -132,6 +132,9 @@ type FullNode interface {
|
||||
GasEstimateGasPremium(_ context.Context, nblocksincl uint64,
|
||||
sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error)
|
||||
|
||||
// GasEstimateMessageGas estimates gas values for unset message gas fields
|
||||
GasEstimateMessageGas(context.Context, *types.Message, *MessageSendSpec, types.TipSetKey) (*types.Message, error)
|
||||
|
||||
// MethodGroup: Sync
|
||||
// The Sync method group contains methods for interacting with and
|
||||
// observing the lotus sync service.
|
||||
@@ -181,6 +184,9 @@ type FullNode interface {
|
||||
MpoolGetNonce(context.Context, address.Address) (uint64, error)
|
||||
MpoolSub(context.Context) (<-chan MpoolUpdate, error)
|
||||
|
||||
// MpoolClear clears pending messages from the mpool
|
||||
MpoolClear(context.Context, bool) error
|
||||
|
||||
// MpoolGetConfig returns (a copy of) the current mpool config
|
||||
MpoolGetConfig(context.Context) (*types.MpoolConfig, error)
|
||||
// MpoolSetConfig sets the mpool config to (a copy of) the supplied config
|
||||
@@ -475,6 +481,8 @@ type DealInfo struct {
|
||||
Duration uint64
|
||||
|
||||
DealID abi.DealID
|
||||
|
||||
CreationTime time.Time
|
||||
}
|
||||
|
||||
type MsgLookup struct {
|
||||
|
||||
@@ -79,6 +79,8 @@ type StorageMiner interface {
|
||||
MarketGetAsk(ctx context.Context) (*storagemarket.SignedStorageAsk, error)
|
||||
MarketSetRetrievalAsk(ctx context.Context, rask *retrievalmarket.Ask) error
|
||||
MarketGetRetrievalAsk(ctx context.Context) (*retrievalmarket.Ask, error)
|
||||
MarketListDataTransfers(ctx context.Context) ([]DataTransferChannel, error)
|
||||
MarketDataTransferUpdates(ctx context.Context) (<-chan DataTransferChannel, error)
|
||||
|
||||
DealsImportData(ctx context.Context, dealPropCid cid.Cid, file string) error
|
||||
DealsList(ctx context.Context) ([]storagemarket.StorageDeal, error)
|
||||
|
||||
+29
-10
@@ -90,9 +90,10 @@ type FullNodeStruct struct {
|
||||
|
||||
BeaconGetEntry func(ctx context.Context, epoch abi.ChainEpoch) (*types.BeaconEntry, error) `perm:"read"`
|
||||
|
||||
GasEstimateGasPremium func(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
GasEstimateGasLimit func(context.Context, *types.Message, types.TipSetKey) (int64, error) `perm:"read"`
|
||||
GasEstimateFeeCap func(context.Context, *types.Message, int64, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
GasEstimateGasPremium func(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
GasEstimateGasLimit func(context.Context, *types.Message, types.TipSetKey) (int64, error) `perm:"read"`
|
||||
GasEstimateFeeCap func(context.Context, *types.Message, int64, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
GasEstimateMessageGas func(context.Context, *types.Message, *api.MessageSendSpec, types.TipSetKey) (*types.Message, error) `perm:"read"`
|
||||
|
||||
SyncState func(context.Context) (*api.SyncState, error) `perm:"read"`
|
||||
SyncSubmitBlock func(ctx context.Context, blk *types.BlockMsg) error `perm:"write"`
|
||||
@@ -105,7 +106,9 @@ type FullNodeStruct struct {
|
||||
|
||||
MpoolSelect func(context.Context, types.TipSetKey, float64) ([]*types.SignedMessage, error) `perm:"read"`
|
||||
|
||||
MpoolPending func(context.Context, types.TipSetKey) ([]*types.SignedMessage, error) `perm:"read"`
|
||||
MpoolPending func(context.Context, types.TipSetKey) ([]*types.SignedMessage, error) `perm:"read"`
|
||||
MpoolClear func(context.Context, bool) error `perm:"write"`
|
||||
|
||||
MpoolPush func(context.Context, *types.SignedMessage) (cid.Cid, error) `perm:"write"`
|
||||
MpoolPushMessage func(context.Context, *types.Message, *api.MessageSendSpec) (*types.SignedMessage, error) `perm:"sign"`
|
||||
MpoolGetNonce func(context.Context, address.Address) (uint64, error) `perm:"read"`
|
||||
@@ -237,6 +240,8 @@ type StorageMinerStruct struct {
|
||||
MarketGetAsk func(ctx context.Context) (*storagemarket.SignedStorageAsk, error) `perm:"read"`
|
||||
MarketSetRetrievalAsk func(ctx context.Context, rask *retrievalmarket.Ask) error `perm:"admin"`
|
||||
MarketGetRetrievalAsk func(ctx context.Context) (*retrievalmarket.Ask, error) `perm:"read"`
|
||||
MarketListDataTransfers func(ctx context.Context) ([]api.DataTransferChannel, error) `perm:"write"`
|
||||
MarketDataTransferUpdates func(ctx context.Context) (<-chan api.DataTransferChannel, error) `perm:"write"`
|
||||
|
||||
PledgeSector func(context.Context) error `perm:"write"`
|
||||
|
||||
@@ -459,17 +464,19 @@ func (c *FullNodeStruct) ClientDataTransferUpdates(ctx context.Context) (<-chan
|
||||
return c.Internal.ClientDataTransferUpdates(ctx)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) GasEstimateGasPremium(ctx context.Context, nblocksincl uint64,
|
||||
sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
func (c *FullNodeStruct) GasEstimateGasPremium(ctx context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
return c.Internal.GasEstimateGasPremium(ctx, nblocksincl, sender, gaslimit, tsk)
|
||||
}
|
||||
func (c *FullNodeStruct) GasEstimateFeeCap(ctx context.Context, msg *types.Message,
|
||||
maxqueueblks int64, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
|
||||
func (c *FullNodeStruct) GasEstimateFeeCap(ctx context.Context, msg *types.Message, maxqueueblks int64, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
return c.Internal.GasEstimateFeeCap(ctx, msg, maxqueueblks, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) GasEstimateGasLimit(ctx context.Context, msg *types.Message,
|
||||
tsk types.TipSetKey) (int64, error) {
|
||||
func (c *FullNodeStruct) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) {
|
||||
return c.Internal.GasEstimateMessageGas(ctx, msg, spec, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) GasEstimateGasLimit(ctx context.Context, msg *types.Message, tsk types.TipSetKey) (int64, error) {
|
||||
return c.Internal.GasEstimateGasLimit(ctx, msg, tsk)
|
||||
}
|
||||
|
||||
@@ -489,6 +496,10 @@ func (c *FullNodeStruct) MpoolPending(ctx context.Context, tsk types.TipSetKey)
|
||||
return c.Internal.MpoolPending(ctx, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MpoolClear(ctx context.Context, local bool) error {
|
||||
return c.Internal.MpoolClear(ctx, local)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MpoolPush(ctx context.Context, smsg *types.SignedMessage) (cid.Cid, error) {
|
||||
return c.Internal.MpoolPush(ctx, smsg)
|
||||
}
|
||||
@@ -1077,6 +1088,14 @@ func (c *StorageMinerStruct) MarketGetRetrievalAsk(ctx context.Context) (*retrie
|
||||
return c.Internal.MarketGetRetrievalAsk(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) MarketListDataTransfers(ctx context.Context) ([]api.DataTransferChannel, error) {
|
||||
return c.Internal.MarketListDataTransfers(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) MarketDataTransferUpdates(ctx context.Context) (<-chan api.DataTransferChannel, error) {
|
||||
return c.Internal.MarketDataTransferUpdates(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) DealsImportData(ctx context.Context, dealPropCid cid.Cid, file string) error {
|
||||
return c.Internal.DealsImportData(ctx, dealPropCid, file)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/lotus/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
)
|
||||
|
||||
type BlockMiner struct {
|
||||
ctx context.Context
|
||||
t *testing.T
|
||||
miner TestStorageNode
|
||||
blocktime time.Duration
|
||||
mine int64
|
||||
nulls int64
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func NewBlockMiner(ctx context.Context, t *testing.T, miner TestStorageNode, blocktime time.Duration) *BlockMiner {
|
||||
return &BlockMiner{
|
||||
ctx: ctx,
|
||||
t: t,
|
||||
miner: miner,
|
||||
blocktime: blocktime,
|
||||
mine: int64(1),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (bm *BlockMiner) MineBlocks() {
|
||||
time.Sleep(time.Second)
|
||||
go func() {
|
||||
defer close(bm.done)
|
||||
for atomic.LoadInt64(&bm.mine) == 1 {
|
||||
time.Sleep(bm.blocktime)
|
||||
nulls := atomic.SwapInt64(&bm.nulls, 0)
|
||||
if err := bm.miner.MineOne(bm.ctx, miner.MineReq{
|
||||
InjectNulls: abi.ChainEpoch(nulls),
|
||||
Done: func(bool, error) {},
|
||||
}); err != nil {
|
||||
bm.t.Error(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (bm *BlockMiner) Stop() {
|
||||
atomic.AddInt64(&bm.mine, -1)
|
||||
fmt.Println("shutting down mining")
|
||||
<-bm.done
|
||||
}
|
||||
@@ -20,7 +20,7 @@ func TestCCUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 1, oneMiner)
|
||||
n, sn := b(t, 1, OneMiner)
|
||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||
miner := sn[0]
|
||||
|
||||
|
||||
+4
-4
@@ -47,7 +47,7 @@ func TestDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration, carExport
|
||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 1, oneMiner)
|
||||
n, sn := b(t, 1, OneMiner)
|
||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||
miner := sn[0]
|
||||
|
||||
@@ -84,7 +84,7 @@ func TestDoubleDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 1, oneMiner)
|
||||
n, sn := b(t, 1, OneMiner)
|
||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||
miner := sn[0]
|
||||
|
||||
@@ -148,7 +148,7 @@ func TestFastRetrievalDealFlow(t *testing.T, b APIBuilder, blocktime time.Durati
|
||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 1, oneMiner)
|
||||
n, sn := b(t, 1, OneMiner)
|
||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||
miner := sn[0]
|
||||
|
||||
@@ -203,7 +203,7 @@ func TestSenondDealRetrieval(t *testing.T, b APIBuilder, blocktime time.Duration
|
||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 1, oneMiner)
|
||||
n, sn := b(t, 1, OneMiner)
|
||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||
miner := sn[0]
|
||||
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ var log = logging.Logger("apitest")
|
||||
|
||||
func (ts *testSuite) testMining(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
apis, sn := ts.makeNodes(t, 1, oneMiner)
|
||||
apis, sn := ts.makeNodes(t, 1, OneMiner)
|
||||
api := apis[0]
|
||||
|
||||
newHeads, err := api.ChainNotify(ctx)
|
||||
@@ -55,7 +55,7 @@ func (ts *testSuite) testMiningReal(t *testing.T) {
|
||||
}()
|
||||
|
||||
ctx := context.Background()
|
||||
apis, sn := ts.makeNodes(t, 1, oneMiner)
|
||||
apis, sn := ts.makeNodes(t, 1, OneMiner)
|
||||
api := apis[0]
|
||||
|
||||
newHeads, err := api.ChainNotify(ctx)
|
||||
|
||||
+6
-77
@@ -23,14 +23,13 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/events/state"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
"github.com/filecoin-project/lotus/miner"
|
||||
)
|
||||
|
||||
func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 2, oneMiner)
|
||||
n, sn := b(t, 2, OneMiner)
|
||||
|
||||
paymentCreator := n[0]
|
||||
paymentReceiver := n[1]
|
||||
@@ -51,8 +50,8 @@ func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
||||
}
|
||||
|
||||
// start mining blocks
|
||||
bm := newBlockMiner(ctx, t, miner, blocktime)
|
||||
bm.mineBlocks()
|
||||
bm := NewBlockMiner(ctx, t, miner, blocktime)
|
||||
bm.MineBlocks()
|
||||
|
||||
// send some funds to register the receiver
|
||||
receiverAddr, err := paymentReceiver.WalletNew(ctx, wallet.ActSigType("secp256k1"))
|
||||
@@ -60,7 +59,7 @@ func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sendFunds(ctx, t, paymentCreator, receiverAddr, abi.NewTokenAmount(1e18))
|
||||
SendFunds(ctx, t, paymentCreator, receiverAddr, abi.NewTokenAmount(1e18))
|
||||
|
||||
// setup the payment channel
|
||||
createrAddr, err := paymentCreator.WalletDefaultAddress(ctx)
|
||||
@@ -201,10 +200,10 @@ func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
||||
}
|
||||
|
||||
// shut down mining
|
||||
bm.stop()
|
||||
bm.Stop()
|
||||
}
|
||||
|
||||
func waitForBlocks(ctx context.Context, t *testing.T, bm *blockMiner, paymentReceiver TestNode, receiverAddr address.Address, count int) {
|
||||
func waitForBlocks(ctx context.Context, t *testing.T, bm *BlockMiner, paymentReceiver TestNode, receiverAddr address.Address, count int) {
|
||||
// We need to add null blocks in batches, if we add too many the chain can't sync
|
||||
batchSize := 60
|
||||
for i := 0; i < count; i += batchSize {
|
||||
@@ -249,73 +248,3 @@ func waitForMessage(ctx context.Context, t *testing.T, paymentCreator TestNode,
|
||||
fmt.Println("Confirmed", desc)
|
||||
return res
|
||||
}
|
||||
|
||||
type blockMiner struct {
|
||||
ctx context.Context
|
||||
t *testing.T
|
||||
miner TestStorageNode
|
||||
blocktime time.Duration
|
||||
mine int64
|
||||
nulls int64
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func newBlockMiner(ctx context.Context, t *testing.T, miner TestStorageNode, blocktime time.Duration) *blockMiner {
|
||||
return &blockMiner{
|
||||
ctx: ctx,
|
||||
t: t,
|
||||
miner: miner,
|
||||
blocktime: blocktime,
|
||||
mine: int64(1),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (bm *blockMiner) mineBlocks() {
|
||||
time.Sleep(time.Second)
|
||||
go func() {
|
||||
defer close(bm.done)
|
||||
for atomic.LoadInt64(&bm.mine) == 1 {
|
||||
time.Sleep(bm.blocktime)
|
||||
nulls := atomic.SwapInt64(&bm.nulls, 0)
|
||||
if err := bm.miner.MineOne(bm.ctx, miner.MineReq{
|
||||
InjectNulls: abi.ChainEpoch(nulls),
|
||||
Done: func(bool, error) {},
|
||||
}); err != nil {
|
||||
bm.t.Error(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (bm *blockMiner) stop() {
|
||||
atomic.AddInt64(&bm.mine, -1)
|
||||
fmt.Println("shutting down mining")
|
||||
<-bm.done
|
||||
}
|
||||
|
||||
func sendFunds(ctx context.Context, t *testing.T, sender TestNode, addr address.Address, amount abi.TokenAmount) {
|
||||
|
||||
senderAddr, err := sender.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
From: senderAddr,
|
||||
To: addr,
|
||||
Value: amount,
|
||||
}
|
||||
|
||||
sm, err := sender.MpoolPushMessage(ctx, msg, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res, err := sender.StateWaitMsg(ctx, sm.Cid(), 1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if res.Receipt.ExitCode != 0 {
|
||||
t.Fatal("did not successfully send money")
|
||||
}
|
||||
}
|
||||
|
||||
+12
-4
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -14,10 +16,16 @@ import (
|
||||
|
||||
type TestNode struct {
|
||||
api.FullNode
|
||||
// ListenAddr is the address on which an API server is listening, if an
|
||||
// API server is created for this Node
|
||||
ListenAddr multiaddr.Multiaddr
|
||||
}
|
||||
|
||||
type TestStorageNode struct {
|
||||
api.StorageMiner
|
||||
// ListenAddr is the address on which an API server is listening, if an
|
||||
// API server is created for this Node
|
||||
ListenAddr multiaddr.Multiaddr
|
||||
|
||||
MineOne func(context.Context, miner.MineReq) error
|
||||
}
|
||||
@@ -54,11 +62,11 @@ func TestApis(t *testing.T, b APIBuilder) {
|
||||
t.Run("testMiningReal", ts.testMiningReal)
|
||||
}
|
||||
|
||||
var oneMiner = []StorageMiner{{Full: 0, Preseal: PresealGenesis}}
|
||||
var OneMiner = []StorageMiner{{Full: 0, Preseal: PresealGenesis}}
|
||||
|
||||
func (ts *testSuite) testVersion(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
apis, _ := ts.makeNodes(t, 1, oneMiner)
|
||||
apis, _ := ts.makeNodes(t, 1, OneMiner)
|
||||
api := apis[0]
|
||||
|
||||
v, err := api.Version(ctx)
|
||||
@@ -70,7 +78,7 @@ func (ts *testSuite) testVersion(t *testing.T) {
|
||||
|
||||
func (ts *testSuite) testID(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
apis, _ := ts.makeNodes(t, 1, oneMiner)
|
||||
apis, _ := ts.makeNodes(t, 1, OneMiner)
|
||||
api := apis[0]
|
||||
|
||||
id, err := api.ID(ctx)
|
||||
@@ -82,7 +90,7 @@ func (ts *testSuite) testID(t *testing.T) {
|
||||
|
||||
func (ts *testSuite) testConnectTwo(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
apis, _ := ts.makeNodes(t, 2, oneMiner)
|
||||
apis, _ := ts.makeNodes(t, 2, OneMiner)
|
||||
|
||||
p, err := apis[0].NetPeers(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func SendFunds(ctx context.Context, t *testing.T, sender TestNode, addr address.Address, amount abi.TokenAmount) {
|
||||
senderAddr, err := sender.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
From: senderAddr,
|
||||
To: addr,
|
||||
Value: amount,
|
||||
}
|
||||
|
||||
sm, err := sender.MpoolPushMessage(ctx, msg, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res, err := sender.StateWaitMsg(ctx, sm.Cid(), 1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if res.Receipt.ExitCode != 0 {
|
||||
t.Fatal("did not successfully send money")
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ func TestPledgeSector(t *testing.T, b APIBuilder, blocktime time.Duration, nSect
|
||||
os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 1, oneMiner)
|
||||
n, sn := b(t, 1, OneMiner)
|
||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||
miner := sn[0]
|
||||
|
||||
@@ -113,7 +113,7 @@ func TestWindowPost(t *testing.T, b APIBuilder, blocktime time.Duration, nSector
|
||||
os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 1, oneMiner)
|
||||
n, sn := b(t, 1, OneMiner)
|
||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||
miner := sn[0]
|
||||
|
||||
|
||||
+44
-7
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
datatransfer "github.com/filecoin-project/go-data-transfer"
|
||||
@@ -49,9 +50,10 @@ type PubsubScore struct {
|
||||
}
|
||||
|
||||
type MinerInfo struct {
|
||||
Owner address.Address // Must be an ID-address.
|
||||
Worker address.Address // Must be an ID-address.
|
||||
NewWorker address.Address // Must be an ID-address.
|
||||
Owner address.Address // Must be an ID-address.
|
||||
Worker address.Address // Must be an ID-address.
|
||||
NewWorker address.Address // Must be an ID-address.
|
||||
ControlAddresses []address.Address // Must be an ID-addresses.
|
||||
WorkerChangeEpoch abi.ChainEpoch
|
||||
PeerId *peer.ID
|
||||
Multiaddrs []abi.Multiaddrs
|
||||
@@ -67,10 +69,13 @@ func NewApiMinerInfo(info *miner.MinerInfo) MinerInfo {
|
||||
}
|
||||
|
||||
mi := MinerInfo{
|
||||
Owner: info.Owner,
|
||||
Worker: info.Worker,
|
||||
NewWorker: address.Undef,
|
||||
WorkerChangeEpoch: -1,
|
||||
Owner: info.Owner,
|
||||
Worker: info.Worker,
|
||||
ControlAddresses: info.ControlAddresses,
|
||||
|
||||
NewWorker: address.Undef,
|
||||
WorkerChangeEpoch: -1,
|
||||
|
||||
PeerId: pid,
|
||||
Multiaddrs: info.Multiaddrs,
|
||||
SealProofType: info.SealProofType,
|
||||
@@ -113,3 +118,35 @@ type DataTransferChannel struct {
|
||||
OtherPeer peer.ID
|
||||
Transferred uint64
|
||||
}
|
||||
|
||||
// NewDataTransferChannel constructs an API DataTransferChannel type from full channel state snapshot and a host id
|
||||
func NewDataTransferChannel(hostID peer.ID, channelState datatransfer.ChannelState) DataTransferChannel {
|
||||
channel := DataTransferChannel{
|
||||
TransferID: channelState.TransferID(),
|
||||
Status: channelState.Status(),
|
||||
BaseCID: channelState.BaseCID(),
|
||||
IsSender: channelState.Sender() == hostID,
|
||||
Message: channelState.Message(),
|
||||
}
|
||||
stringer, ok := channelState.Voucher().(fmt.Stringer)
|
||||
if ok {
|
||||
channel.Voucher = stringer.String()
|
||||
} else {
|
||||
voucherJSON, err := json.Marshal(channelState.Voucher())
|
||||
if err != nil {
|
||||
channel.Voucher = fmt.Errorf("Voucher Serialization: %w", err).Error()
|
||||
} else {
|
||||
channel.Voucher = string(voucherJSON)
|
||||
}
|
||||
}
|
||||
if channel.IsSender {
|
||||
channel.IsInitiator = !channelState.IsPull()
|
||||
channel.Transferred = channelState.Sent()
|
||||
channel.OtherPeer = channelState.Recipient()
|
||||
} else {
|
||||
channel.IsInitiator = channelState.IsPull()
|
||||
channel.Transferred = channelState.Received()
|
||||
channel.OtherPeer = channelState.Sender()
|
||||
}
|
||||
return channel
|
||||
}
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
/dns4/bootstrap-0-sin.fil-test.net/tcp/1347/p2p/12D3KooWPdUquftaQvoQEtEdsRBAhwD6jopbF2oweVTzR59VbHEd
|
||||
/ip4/86.109.15.57/tcp/1347/p2p/12D3KooWPdUquftaQvoQEtEdsRBAhwD6jopbF2oweVTzR59VbHEd
|
||||
/dns4/bootstrap-0-dfw.fil-test.net/tcp/1347/p2p/12D3KooWQSCkHCzosEyrh8FgYfLejKgEPM5VB6qWzZE3yDAuXn8d
|
||||
/ip4/139.178.84.45/tcp/1347/p2p/12D3KooWQSCkHCzosEyrh8FgYfLejKgEPM5VB6qWzZE3yDAuXn8d
|
||||
/dns4/bootstrap-0-fra.fil-test.net/tcp/1347/p2p/12D3KooWEXN2eQmoyqnNjde9PBAQfQLHN67jcEdWU6JougWrgXJK
|
||||
/ip4/136.144.49.17/tcp/1347/p2p/12D3KooWEXN2eQmoyqnNjde9PBAQfQLHN67jcEdWU6JougWrgXJK
|
||||
/dns4/bootstrap-1-sin.fil-test.net/tcp/1347/p2p/12D3KooWLmJkZd33mJhjg5RrpJ6NFep9SNLXWc4uVngV4TXKwzYw
|
||||
/ip4/86.109.15.123/tcp/1347/p2p/12D3KooWLmJkZd33mJhjg5RrpJ6NFep9SNLXWc4uVngV4TXKwzYw
|
||||
/dns4/bootstrap-1-dfw.fil-test.net/tcp/1347/p2p/12D3KooWGXLHjiz6pTRu7x2pkgTVCoxcCiVxcNLpMnWcJ3JiNEy5
|
||||
/ip4/139.178.86.3/tcp/1347/p2p/12D3KooWGXLHjiz6pTRu7x2pkgTVCoxcCiVxcNLpMnWcJ3JiNEy5
|
||||
/dns4/bootstrap-1-fra.fil-test.net/tcp/1347/p2p/12D3KooW9szZmKttS9A1FafH3Zc2pxKwwmvCWCGKkRP4KmbhhC4R
|
||||
/ip4/136.144.49.131/tcp/1347/p2p/12D3KooW9szZmKttS9A1FafH3Zc2pxKwwmvCWCGKkRP4KmbhhC4R
|
||||
/dns4/bootstrap-0.testnet.fildev.network/tcp/1347/p2p/12D3KooWJTUBUjtzWJGWU1XSiY21CwmHaCNLNYn2E7jqHEHyZaP7
|
||||
/dns4/bootstrap-1.testnet.fildev.network/tcp/1347/p2p/12D3KooW9yeKXha4hdrJKq74zEo99T8DhriQdWNoojWnnQbsgB3v
|
||||
/dns4/bootstrap-2.testnet.fildev.network/tcp/1347/p2p/12D3KooWCrx8yVG9U9Kf7w8KLN3Edkj5ZKDhgCaeMqQbcQUoB6CT
|
||||
/dns4/bootstrap-4.testnet.fildev.network/tcp/1347/p2p/12D3KooWPkL9LrKRQgHtq7kn9ecNhGU9QaziG8R5tX8v9v7t3h34
|
||||
/dns4/bootstrap-3.testnet.fildev.network/tcp/1347/p2p/12D3KooWKYSsbpgZ3HAjax5M1BXCwXLa6gVkUARciz7uN3FNtr7T
|
||||
/dns4/bootstrap-5.testnet.fildev.network/tcp/1347/p2p/12D3KooWQYzqnLASJAabyMpPb1GcWZvNSe7JDcRuhdRqonFoiK9W
|
||||
|
||||
+15
-1
@@ -2,7 +2,7 @@ package build
|
||||
|
||||
import "github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
|
||||
var DrandNetwork = DrandMainnet
|
||||
var DrandNetwork = DrandIncentinet
|
||||
|
||||
func DrandConfig() dtypes.DrandConfig {
|
||||
return DrandConfigs[DrandNetwork]
|
||||
@@ -15,6 +15,7 @@ const (
|
||||
DrandTestnet
|
||||
DrandDevnet
|
||||
DrandLocalnet
|
||||
DrandIncentinet
|
||||
)
|
||||
|
||||
var DrandConfigs = map[DrandEnum]dtypes.DrandConfig{
|
||||
@@ -55,4 +56,17 @@ var DrandConfigs = map[DrandEnum]dtypes.DrandConfig{
|
||||
},
|
||||
ChainInfoJSON: `{"public_key":"8cda589f88914aa728fd183f383980b35789ce81b274e5daee1f338b77d02566ef4d3fb0098af1f844f10f9c803c1827","period":25,"genesis_time":1595348225,"hash":"e73b7dc3c4f6a236378220c0dd6aa110eb16eed26c11259606e07ee122838d4f","groupHash":"567d4785122a5a3e75a9bc9911d7ea807dd85ff76b78dc4ff06b075712898607"}`,
|
||||
},
|
||||
DrandIncentinet: {
|
||||
Servers: []string{
|
||||
"https://pl-eu.incentinet.drand.sh",
|
||||
"https://pl-us.incentinet.drand.sh",
|
||||
"https://pl-sin.incentinet.drand.sh",
|
||||
},
|
||||
Relays: []string{
|
||||
"/dnsaddr/pl-eu.incentinet.drand.sh/",
|
||||
"/dnsaddr/pl-us.incentinet.drand.sh/",
|
||||
"/dnsaddr/pl-sin.incentinet.drand.sh/",
|
||||
},
|
||||
ChainInfoJSON: `{"public_key":"8cad0c72c606ab27d36ee06de1d5b2db1faf92e447025ca37575ab3a8aac2eaae83192f846fc9e158bc738423753d000","period":30,"genesis_time":1595873820,"hash":"80c8b872c714f4c00fdd3daa465d5514049f457f01f85a4caf68cdcd394ba039","groupHash":"d9406aaed487f7af71851b4399448e311f2328923d454e971536c05398ce2d9b"}`,
|
||||
},
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
power.ConsensusMinerMinPower = big.NewInt(1024 << 30)
|
||||
power.ConsensusMinerMinPower = big.NewInt(10 << 40)
|
||||
miner.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{
|
||||
abi.RegisteredSealProof_StackedDrg32GiBV1: {},
|
||||
abi.RegisteredSealProof_StackedDrg64GiBV1: {},
|
||||
|
||||
@@ -2,6 +2,7 @@ package genesis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
@@ -118,11 +119,6 @@ func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template ge
|
||||
return nil, nil, xerrors.Errorf("making new state tree: %w", err)
|
||||
}
|
||||
|
||||
emptyobject, err := cst.Put(context.TODO(), []struct{}{})
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("failed putting empty object: %w", err)
|
||||
}
|
||||
|
||||
// Create system actor
|
||||
|
||||
sysact, err := SetupSystemActor(bs)
|
||||
@@ -191,11 +187,18 @@ func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template ge
|
||||
return nil, nil, xerrors.Errorf("set market actor: %w", err)
|
||||
}
|
||||
|
||||
burntRoot, err := cst.Put(ctx, &account.State{
|
||||
Address: builtin.BurntFundsActorAddr,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("failed to setup burnt funds actor state: %w", err)
|
||||
}
|
||||
|
||||
// Setup burnt-funds
|
||||
err = state.SetActor(builtin.BurntFundsActorAddr, &types.Actor{
|
||||
Code: builtin.AccountActorCodeID,
|
||||
Balance: types.NewInt(0),
|
||||
Head: emptyobject,
|
||||
Head: burntRoot,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("set burnt funds account actor: %w", err)
|
||||
@@ -297,6 +300,8 @@ func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template ge
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
template.RemainderAccount.Balance = remainingFil
|
||||
|
||||
if err := createMultisigAccount(ctx, bs, cst, state, remAccKey, template.RemainderAccount, keyIDs); err != nil {
|
||||
return nil, nil, xerrors.Errorf("failed to set up remainder account: %w", err)
|
||||
}
|
||||
@@ -508,8 +513,10 @@ func MakeGenesisBlock(ctx context.Context, bs bstore.Blockstore, sys vm.SyscallB
|
||||
|
||||
log.Infof("Empty Genesis root: %s", emptyroot)
|
||||
|
||||
tickBuf := make([]byte, 32)
|
||||
_, _ = rand.Read(tickBuf)
|
||||
genesisticket := &types.Ticket{
|
||||
VRFProof: []byte("vrf proof0000000vrf proof0000000"),
|
||||
VRFProof: tickBuf,
|
||||
}
|
||||
|
||||
filecoinGenesisCid, err := cid.Decode("bafyreiaqpwbbyjo4a42saasj36kkrpv4tsherf2e7bvezkert2a7dhonoi")
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
@@ -744,30 +745,42 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet)
|
||||
}
|
||||
}
|
||||
|
||||
var merr error
|
||||
|
||||
for _, ts := range revert {
|
||||
pts, err := mp.api.LoadTipSet(ts.Parents())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgs, err := mp.MessagesForBlocks(ts.Blocks())
|
||||
if err != nil {
|
||||
return err
|
||||
log.Errorf("error loading reverted tipset parent: %s", err)
|
||||
merr = multierror.Append(merr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
mp.curTs = pts
|
||||
|
||||
msgs, err := mp.MessagesForBlocks(ts.Blocks())
|
||||
if err != nil {
|
||||
log.Errorf("error retrieving messages for reverted block: %s", err)
|
||||
merr = multierror.Append(merr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, msg := range msgs {
|
||||
add(msg)
|
||||
}
|
||||
}
|
||||
|
||||
for _, ts := range apply {
|
||||
mp.curTs = ts
|
||||
|
||||
for _, b := range ts.Blocks() {
|
||||
bmsgs, smsgs, err := mp.api.MessagesForBlock(b)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get messages for apply block %s(height %d) (msgroot = %s): %w", b.Cid(), b.Height, b.Messages, err)
|
||||
xerr := xerrors.Errorf("failed to get messages for apply block %s(height %d) (msgroot = %s): %w", b.Cid(), b.Height, b.Messages, err)
|
||||
log.Errorf("error retrieving messages for block: %s", xerr)
|
||||
merr = multierror.Append(merr, xerr)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, msg := range smsgs {
|
||||
rm(msg.Message.From, msg.Message.Nonce)
|
||||
maybeRepub(msg.Cid())
|
||||
@@ -778,8 +791,6 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet)
|
||||
maybeRepub(msg.Cid())
|
||||
}
|
||||
}
|
||||
|
||||
mp.curTs = ts
|
||||
}
|
||||
|
||||
if repubTrigger {
|
||||
@@ -862,7 +873,7 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return merr
|
||||
}
|
||||
|
||||
type statBucket struct {
|
||||
@@ -962,3 +973,40 @@ func (mp *MessagePool) loadLocal() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *MessagePool) Clear(local bool) {
|
||||
mp.lk.Lock()
|
||||
defer mp.lk.Unlock()
|
||||
|
||||
// remove everything if local is true, including removing local messages from
|
||||
// the datastore
|
||||
if local {
|
||||
for a := range mp.localAddrs {
|
||||
mset, ok := mp.pending[a]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, m := range mset.msgs {
|
||||
err := mp.localMsgs.Delete(datastore.NewKey(string(m.Cid().Bytes())))
|
||||
if err != nil {
|
||||
log.Warnf("error deleting local message: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mp.pending = make(map[address.Address]*msgSet)
|
||||
mp.republished = nil
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// remove everything except the local messages
|
||||
for a := range mp.pending {
|
||||
_, isLocal := mp.localAddrs[a]
|
||||
if isLocal {
|
||||
continue
|
||||
}
|
||||
delete(mp.pending, a)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,7 +546,11 @@ func (mv *MessageValidator) Validate(ctx context.Context, pid peer.ID, msg *pubs
|
||||
)
|
||||
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
|
||||
switch {
|
||||
case xerrors.Is(err, messagepool.ErrBroadcastAnyway) || xerrors.Is(err, messagepool.ErrRBFTooLowPremium):
|
||||
case xerrors.Is(err, messagepool.ErrBroadcastAnyway):
|
||||
fallthrough
|
||||
case xerrors.Is(err, messagepool.ErrRBFTooLowPremium):
|
||||
fallthrough
|
||||
case xerrors.Is(err, messagepool.ErrNonceTooLow):
|
||||
return pubsub.ValidationIgnore
|
||||
default:
|
||||
return pubsub.ValidationReject
|
||||
|
||||
+11
-6
@@ -997,6 +997,10 @@ var clientListDeals = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
sort.Slice(localDeals, func(i, j int) bool {
|
||||
return localDeals[i].CreationTime.Before(localDeals[j].CreationTime)
|
||||
})
|
||||
|
||||
var deals []deal
|
||||
for _, v := range localDeals {
|
||||
if v.DealID == 0 {
|
||||
@@ -1025,7 +1029,7 @@ var clientListDeals = &cli.Command{
|
||||
|
||||
if cctx.Bool("verbose") {
|
||||
w := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0)
|
||||
fmt.Fprintf(w, "DealCid\tDealId\tProvider\tState\tOn Chain?\tSlashed?\tPieceCID\tSize\tPrice\tDuration\tMessage\n")
|
||||
fmt.Fprintf(w, "Created\tDealCid\tDealId\tProvider\tState\tOn Chain?\tSlashed?\tPieceCID\tSize\tPrice\tDuration\tMessage\n")
|
||||
for _, d := range deals {
|
||||
onChain := "N"
|
||||
if d.OnChainDealState.SectorStartEpoch != -1 {
|
||||
@@ -1038,7 +1042,7 @@ var clientListDeals = &cli.Command{
|
||||
}
|
||||
|
||||
price := types.FIL(types.BigMul(d.LocalDeal.PricePerEpoch, types.NewInt(d.LocalDeal.Duration)))
|
||||
fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d\t%s\n", d.LocalDeal.ProposalCid, d.LocalDeal.DealID, d.LocalDeal.Provider, dealStateString(color, d.LocalDeal.State), onChain, slashed, d.LocalDeal.PieceCID, types.SizeStr(types.NewInt(d.LocalDeal.Size)), price, d.LocalDeal.Duration, d.LocalDeal.Message)
|
||||
fmt.Fprintf(w, "%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d\t%s\n", d.LocalDeal.CreationTime.Format(time.Stamp), d.LocalDeal.ProposalCid, d.LocalDeal.DealID, d.LocalDeal.Provider, dealStateString(color, d.LocalDeal.State), onChain, slashed, d.LocalDeal.PieceCID, types.SizeStr(types.NewInt(d.LocalDeal.Size)), price, d.LocalDeal.Duration, d.LocalDeal.Message)
|
||||
}
|
||||
return w.Flush()
|
||||
} else {
|
||||
@@ -1254,7 +1258,7 @@ var clientListTransfers = &cli.Command{
|
||||
|
||||
tm.MoveCursor(1, 1)
|
||||
|
||||
outputChannels(tm.Screen, channels, completed, color)
|
||||
OutputDataTransferChannels(tm.Screen, channels, completed, color)
|
||||
|
||||
tm.Flush()
|
||||
|
||||
@@ -1279,12 +1283,13 @@ var clientListTransfers = &cli.Command{
|
||||
}
|
||||
}
|
||||
}
|
||||
outputChannels(os.Stdout, channels, completed, color)
|
||||
OutputDataTransferChannels(os.Stdout, channels, completed, color)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func outputChannels(out io.Writer, channels []api.DataTransferChannel, completed bool, color bool) {
|
||||
// OutputDataTransferChannels generates table output for a list of channels
|
||||
func OutputDataTransferChannels(out io.Writer, channels []lapi.DataTransferChannel, completed bool, color bool) {
|
||||
sort.Slice(channels, func(i, j int) bool {
|
||||
return channels[i].TransferID < channels[j].TransferID
|
||||
})
|
||||
@@ -1346,7 +1351,7 @@ func channelStatusString(useColor bool, status datatransfer.Status) string {
|
||||
}
|
||||
}
|
||||
|
||||
func toChannelOutput(useColor bool, otherPartyColumn string, channel api.DataTransferChannel) map[string]interface{} {
|
||||
func toChannelOutput(useColor bool, otherPartyColumn string, channel lapi.DataTransferChannel) map[string]interface{} {
|
||||
rootCid := channel.BaseCID.String()
|
||||
rootCid = "..." + rootCid[len(rootCid)-8:]
|
||||
|
||||
|
||||
+27
@@ -67,6 +67,19 @@ func (a APIInfo) AuthHeader() http.Header {
|
||||
return nil
|
||||
}
|
||||
|
||||
// The flag passed on the command line with the listen address of the API
|
||||
// server (only used by the tests)
|
||||
func flagForAPI(t repo.RepoType) string {
|
||||
switch t {
|
||||
case repo.FullNode:
|
||||
return "api"
|
||||
case repo.StorageMiner:
|
||||
return "miner-api"
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown repo type: %v", t))
|
||||
}
|
||||
}
|
||||
|
||||
func flagForRepo(t repo.RepoType) string {
|
||||
switch t {
|
||||
case repo.FullNode:
|
||||
@@ -102,6 +115,20 @@ func envForRepoDeprecation(t repo.RepoType) string {
|
||||
}
|
||||
|
||||
func GetAPIInfo(ctx *cli.Context, t repo.RepoType) (APIInfo, error) {
|
||||
// Check if there was a flag passed with the listen address of the API
|
||||
// server (only used by the tests)
|
||||
apiFlag := flagForAPI(t)
|
||||
if ctx.IsSet(apiFlag) {
|
||||
strma := ctx.String(apiFlag)
|
||||
strma = strings.TrimSpace(strma)
|
||||
|
||||
apima, err := multiaddr.NewMultiaddr(strma)
|
||||
if err != nil {
|
||||
return APIInfo{}, err
|
||||
}
|
||||
return APIInfo{Addr: apima}, nil
|
||||
}
|
||||
|
||||
envKey := envForRepo(t)
|
||||
env, ok := os.LookupEnv(envKey)
|
||||
if !ok {
|
||||
|
||||
@@ -20,6 +20,7 @@ var mpoolCmd = &cli.Command{
|
||||
Usage: "Manage message pool",
|
||||
Subcommands: []*cli.Command{
|
||||
mpoolPending,
|
||||
mpoolClear,
|
||||
mpoolSub,
|
||||
mpoolStat,
|
||||
mpoolReplaceCmd,
|
||||
@@ -83,6 +84,38 @@ var mpoolPending = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var mpoolClear = &cli.Command{
|
||||
Name: "clear",
|
||||
Usage: "Clear all pending messages from the mpool (USE WITH CARE)",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "local",
|
||||
Usage: "also clear local messages",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "really-do-it",
|
||||
Usage: "must be specified for the action to take effect",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
|
||||
really := cctx.Bool("really-do-it")
|
||||
if !really {
|
||||
return fmt.Errorf("--really-do-it must be specified for this action to have an effect; you have been warned.")
|
||||
}
|
||||
|
||||
local := cctx.Bool("local")
|
||||
|
||||
ctx := ReqContext(cctx)
|
||||
return api.MpoolClear(ctx, local)
|
||||
},
|
||||
}
|
||||
|
||||
var mpoolSub = &cli.Command{
|
||||
Name: "sub",
|
||||
Usage: "Subscribe to mpool changes",
|
||||
|
||||
+12
-13
@@ -5,9 +5,8 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
@@ -71,7 +70,7 @@ var paychGetCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(chAddr)
|
||||
fmt.Fprintln(cctx.App.Writer, chAddr)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -94,7 +93,7 @@ var paychListCmd = &cli.Command{
|
||||
}
|
||||
|
||||
for _, v := range chs {
|
||||
fmt.Println(v.String())
|
||||
fmt.Fprintln(cctx.App.Writer, v.String())
|
||||
}
|
||||
return nil
|
||||
},
|
||||
@@ -135,7 +134,7 @@ var paychSettleCmd = &cli.Command{
|
||||
return fmt.Errorf("settle message execution failed (exit code %d)", mwait.Receipt.ExitCode)
|
||||
}
|
||||
|
||||
fmt.Printf("Settled channel %s\n", ch)
|
||||
fmt.Fprintf(cctx.App.Writer, "Settled channel %s\n", ch)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -175,7 +174,7 @@ var paychCloseCmd = &cli.Command{
|
||||
return fmt.Errorf("collect message execution failed (exit code %d)", mwait.Receipt.ExitCode)
|
||||
}
|
||||
|
||||
fmt.Printf("Collected funds for channel %s\n", ch)
|
||||
fmt.Fprintf(cctx.App.Writer, "Collected funds for channel %s\n", ch)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -239,7 +238,7 @@ var paychVoucherCreateCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(enc)
|
||||
fmt.Fprintln(cctx.App.Writer, enc)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -275,7 +274,7 @@ var paychVoucherCheckCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("voucher is valid")
|
||||
fmt.Fprintln(cctx.App.Writer, "voucher is valid")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -356,9 +355,9 @@ var paychVoucherListCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Lane %d, Nonce %d: %s; %s\n", v.Lane, v.Nonce, v.Amount.String(), enc)
|
||||
fmt.Fprintf(cctx.App.Writer, "Lane %d, Nonce %d: %s; %s\n", v.Lane, v.Nonce, v.Amount.String(), enc)
|
||||
} else {
|
||||
fmt.Printf("Lane %d, Nonce %d: %s\n", v.Lane, v.Nonce, v.Amount.String())
|
||||
fmt.Fprintf(cctx.App.Writer, "Lane %d, Nonce %d: %s\n", v.Lane, v.Nonce, v.Amount.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,8 +414,8 @@ var paychVoucherBestSpendableCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(enc)
|
||||
fmt.Printf("Amount: %s\n", best.Amount)
|
||||
fmt.Fprintln(cctx.App.Writer, enc)
|
||||
fmt.Fprintf(cctx.App.Writer, "Amount: %s\n", best.Amount)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -462,7 +461,7 @@ var paychVoucherSubmitCmd = &cli.Command{
|
||||
return fmt.Errorf("message execution failed (exit code %d)", mwait.Receipt.ExitCode)
|
||||
}
|
||||
|
||||
fmt.Println("channel updated successfully")
|
||||
fmt.Fprintln(cctx.App.Writer, "channel updated successfully")
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"flag"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
saminer "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
||||
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/events"
|
||||
|
||||
"github.com/filecoin-project/lotus/api/apibstore"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
|
||||
"github.com/filecoin-project/lotus/api/test"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
builder "github.com/filecoin-project/lotus/node/test"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
power.ConsensusMinerMinPower = big.NewInt(2048)
|
||||
saminer.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{
|
||||
abi.RegisteredSealProof_StackedDrg2KiBV1: {},
|
||||
}
|
||||
verifreg.MinVerifiedDealSize = big.NewInt(256)
|
||||
}
|
||||
|
||||
// TestPaymentChannels does a basic test to exercise the payment channel CLI
|
||||
// commands
|
||||
func TestPaymentChannels(t *testing.T) {
|
||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
blocktime := 5 * time.Millisecond
|
||||
ctx := context.Background()
|
||||
n, sn := builder.RPCMockSbBuilder(t, 2, test.OneMiner)
|
||||
|
||||
paymentCreator := n[0]
|
||||
paymentReceiver := n[1]
|
||||
miner := sn[0]
|
||||
|
||||
// Get everyone connected
|
||||
addrs, err := paymentCreator.NetAddrsListen(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := paymentReceiver.NetConnect(ctx, addrs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := miner.NetConnect(ctx, addrs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Start mining blocks
|
||||
bm := test.NewBlockMiner(ctx, t, miner, blocktime)
|
||||
bm.MineBlocks()
|
||||
|
||||
// Send some funds to register the receiver
|
||||
receiverAddr, err := paymentReceiver.WalletNew(ctx, wallet.ActSigType("secp256k1"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
test.SendFunds(ctx, t, paymentCreator, receiverAddr, abi.NewTokenAmount(1e18))
|
||||
|
||||
// Get the creator's address
|
||||
creatorAddr, err := paymentCreator.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create mock CLI
|
||||
mockCLI := newMockCLI(t)
|
||||
creatorCLI := mockCLI.client(paymentCreator.ListenAddr)
|
||||
receiverCLI := mockCLI.client(paymentReceiver.ListenAddr)
|
||||
|
||||
// creator: paych get <creator> <receiver> <amount>
|
||||
channelAmt := "100000"
|
||||
cmd := []string{creatorAddr.String(), receiverAddr.String(), channelAmt}
|
||||
chstr := creatorCLI.runCmd(paychGetCmd, cmd)
|
||||
|
||||
chAddr, err := address.NewFromString(chstr)
|
||||
require.NoError(t, err)
|
||||
|
||||
// creator: paych voucher create <channel> <amount>
|
||||
voucherAmt := 100
|
||||
vamt := strconv.Itoa(voucherAmt)
|
||||
cmd = []string{chAddr.String(), vamt}
|
||||
voucher := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
|
||||
|
||||
// receiver: paych voucher add <channel> <voucher>
|
||||
cmd = []string{chAddr.String(), voucher}
|
||||
receiverCLI.runCmd(paychVoucherAddCmd, cmd)
|
||||
|
||||
// creator: paych settle <channel>
|
||||
cmd = []string{chAddr.String()}
|
||||
creatorCLI.runCmd(paychSettleCmd, cmd)
|
||||
|
||||
// Wait for the chain to reach the settle height
|
||||
chState := getPaychState(ctx, t, paymentReceiver, chAddr)
|
||||
waitForHeight(ctx, t, paymentReceiver, chState.SettlingAt)
|
||||
|
||||
// receiver: paych collect <channel>
|
||||
cmd = []string{chAddr.String()}
|
||||
receiverCLI.runCmd(paychCloseCmd, cmd)
|
||||
}
|
||||
|
||||
type mockCLI struct {
|
||||
t *testing.T
|
||||
cctx *cli.Context
|
||||
out *bytes.Buffer
|
||||
}
|
||||
|
||||
func newMockCLI(t *testing.T) *mockCLI {
|
||||
// Create a CLI App with an --api flag so that we can specify which node
|
||||
// the command should be executed against
|
||||
app := cli.NewApp()
|
||||
app.Flags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "api",
|
||||
Hidden: true,
|
||||
},
|
||||
}
|
||||
var out bytes.Buffer
|
||||
app.Writer = &out
|
||||
app.Setup()
|
||||
|
||||
cctx := cli.NewContext(app, &flag.FlagSet{}, nil)
|
||||
return &mockCLI{t: t, cctx: cctx, out: &out}
|
||||
}
|
||||
|
||||
func (c *mockCLI) client(addr multiaddr.Multiaddr) *mockCLIClient {
|
||||
return &mockCLIClient{t: c.t, addr: addr, cctx: c.cctx, out: c.out}
|
||||
}
|
||||
|
||||
// mockCLIClient runs commands against a particular node
|
||||
type mockCLIClient struct {
|
||||
t *testing.T
|
||||
addr multiaddr.Multiaddr
|
||||
cctx *cli.Context
|
||||
out *bytes.Buffer
|
||||
}
|
||||
|
||||
func (c *mockCLIClient) runCmd(cmd *cli.Command, input []string) string {
|
||||
// prepend --api=<node api listener address>
|
||||
apiFlag := "--api=" + c.addr.String()
|
||||
input = append([]string{apiFlag}, input...)
|
||||
|
||||
fs := c.flagSet(cmd)
|
||||
err := fs.Parse(input)
|
||||
require.NoError(c.t, err)
|
||||
|
||||
err = cmd.Action(cli.NewContext(c.cctx.App, fs, c.cctx))
|
||||
require.NoError(c.t, err)
|
||||
|
||||
// Get the output
|
||||
str := strings.TrimSpace(c.out.String())
|
||||
c.out.Reset()
|
||||
return str
|
||||
}
|
||||
|
||||
func (c *mockCLIClient) flagSet(cmd *cli.Command) *flag.FlagSet {
|
||||
// Apply app level flags (so we can process --api flag)
|
||||
fs := &flag.FlagSet{}
|
||||
for _, f := range c.cctx.App.Flags {
|
||||
err := f.Apply(fs)
|
||||
if err != nil {
|
||||
c.t.Fatal(err)
|
||||
}
|
||||
}
|
||||
// Apply command level flags
|
||||
for _, f := range cmd.Flags {
|
||||
err := f.Apply(fs)
|
||||
if err != nil {
|
||||
c.t.Fatal(err)
|
||||
}
|
||||
}
|
||||
return fs
|
||||
}
|
||||
|
||||
// waitForHeight waits for the node to reach the given chain epoch
|
||||
func waitForHeight(ctx context.Context, t *testing.T, node test.TestNode, height abi.ChainEpoch) {
|
||||
atHeight := make(chan struct{})
|
||||
chainEvents := events.NewEvents(ctx, node)
|
||||
err := chainEvents.ChainAt(func(ctx context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
close(atHeight)
|
||||
return nil
|
||||
}, nil, 1, height)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-atHeight:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
|
||||
// getPaychState gets the state of the payment channel with the given address
|
||||
func getPaychState(ctx context.Context, t *testing.T, node test.TestNode, chAddr address.Address) paych.State {
|
||||
act, err := node.StateGetActor(ctx, chAddr, types.EmptyTSK)
|
||||
require.NoError(t, err)
|
||||
|
||||
store := cbor.NewCborStore(apibstore.NewAPIBlockstore(node))
|
||||
var chState paych.State
|
||||
err = store.Get(ctx, act.Head, &chState)
|
||||
require.NoError(t, err)
|
||||
|
||||
return chState
|
||||
}
|
||||
@@ -105,6 +105,9 @@ var stateMinerInfo = &cli.Command{
|
||||
|
||||
fmt.Printf("Owner:\t%s\n", mi.Owner)
|
||||
fmt.Printf("Worker:\t%s\n", mi.Worker)
|
||||
for i, controlAddress := range mi.ControlAddresses {
|
||||
fmt.Printf("Control %d: \t%s\n", i, controlAddress)
|
||||
}
|
||||
fmt.Printf("PeerID:\t%s\n", mi.PeerId)
|
||||
fmt.Printf("SectorSize:\t%s (%d)\n", types.SizeStr(types.NewInt(uint64(mi.SectorSize))), mi.SectorSize)
|
||||
fmt.Printf("Multiaddrs: \t")
|
||||
|
||||
@@ -88,6 +88,8 @@ create table if not exists sector_info
|
||||
verified_deal_weight text not null,
|
||||
|
||||
initial_pledge text not null,
|
||||
expected_day_reward text not null,
|
||||
expected_storage_pledge text not null,
|
||||
|
||||
constraint sector_info_pk
|
||||
primary key (miner_id, sector_id, sealed_cid)
|
||||
@@ -307,6 +309,7 @@ func (p *Processor) storeMinerPreCommitInfo(ctx context.Context, miners []minerA
|
||||
}
|
||||
|
||||
stmt, err := tx.Prepare(`copy spi (miner_id, sector_id, sealed_cid, state_root, seal_rand_epoch, expiration_epoch, precommit_deposit, precommit_epoch, deal_weight, verified_deal_weight, is_replace_capacity, replace_sector_deadline, replace_sector_partition, replace_sector_number) from STDIN`)
|
||||
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to prepare miner precommit info statement: %w", err)
|
||||
}
|
||||
@@ -431,7 +434,7 @@ func (p *Processor) storeMinerSectorInfo(ctx context.Context, miners []minerActo
|
||||
return xerrors.Errorf("Failed to create temp table for sector_: %w", err)
|
||||
}
|
||||
|
||||
stmt, err := tx.Prepare(`copy si (miner_id, sector_id, sealed_cid, state_root, activation_epoch, expiration_epoch, deal_weight, verified_deal_weight, initial_pledge) from STDIN`)
|
||||
stmt, err := tx.Prepare(`copy si (miner_id, sector_id, sealed_cid, state_root, activation_epoch, expiration_epoch, deal_weight, verified_deal_weight, initial_pledge, expected_day_reward, expected_storage_pledge) from STDIN`)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to prepare miner sector info statement: %w", err)
|
||||
}
|
||||
@@ -463,6 +466,8 @@ func (p *Processor) storeMinerSectorInfo(ctx context.Context, miners []minerActo
|
||||
added.DealWeight.String(),
|
||||
added.VerifiedDealWeight.String(),
|
||||
added.InitialPledge.String(),
|
||||
added.ExpectedDayReward.String(),
|
||||
added.ExpectedStoragePledge.String(),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/util/smoothing"
|
||||
)
|
||||
|
||||
type powerActorInfo struct {
|
||||
common actorInfo
|
||||
|
||||
epochSmoothingEstimate *smoothing.FilterEstimate
|
||||
}
|
||||
|
||||
func (p *Processor) setupPower() error {
|
||||
tx, err := p.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`
|
||||
create table if not exists power_smoothing_estimates
|
||||
(
|
||||
state_root text not null
|
||||
constraint power_smoothing_estimates_pk
|
||||
primary key,
|
||||
position_estimate text not null,
|
||||
velocity_estimate text not null
|
||||
);
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (p *Processor) HandlePowerChanges(ctx context.Context, powerTips ActorTips) error {
|
||||
powerChanges, err := p.processPowerActors(ctx, powerTips)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to process power actors: %w", err)
|
||||
}
|
||||
|
||||
if err := p.persistPowerActors(ctx, powerChanges); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Processor) processPowerActors(ctx context.Context, powerTips ActorTips) ([]powerActorInfo, error) {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
log.Debugw("Processed Power Actors", "duration", time.Since(start).String())
|
||||
}()
|
||||
|
||||
var out []powerActorInfo
|
||||
for tipset, powers := range powerTips {
|
||||
for _, act := range powers {
|
||||
var pw powerActorInfo
|
||||
pw.common = act
|
||||
|
||||
powerActor, err := p.node.StateGetActor(ctx, builtin.StoragePowerActorAddr, tipset)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("get power state (@ %s): %w", pw.common.stateroot.String(), err)
|
||||
}
|
||||
|
||||
powerStateRaw, err := p.node.ChainReadObj(ctx, powerActor.Head)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("read state obj (@ %s): %w", pw.common.stateroot.String(), err)
|
||||
}
|
||||
|
||||
var powerActorState power.State
|
||||
if err := powerActorState.UnmarshalCBOR(bytes.NewReader(powerStateRaw)); err != nil {
|
||||
return nil, xerrors.Errorf("unmarshal state (@ %s): %w", pw.common.stateroot.String(), err)
|
||||
}
|
||||
|
||||
pw.epochSmoothingEstimate = powerActorState.ThisEpochQAPowerSmoothed
|
||||
out = append(out, pw)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (p *Processor) persistPowerActors(ctx context.Context, powers []powerActorInfo) error {
|
||||
// NB: use errgroup when there is more than a single store operation
|
||||
return p.storePowerSmoothingEstimates(powers)
|
||||
}
|
||||
|
||||
func (p *Processor) storePowerSmoothingEstimates(powers []powerActorInfo) error {
|
||||
tx, err := p.db.Begin()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("begin power_smoothing_estimates tx: %w", err)
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`create temp table rse (like power_smoothing_estimates) on commit drop`); err != nil {
|
||||
return xerrors.Errorf("prep power_smoothing_estimates: %w", err)
|
||||
}
|
||||
|
||||
stmt, err := tx.Prepare(`copy rse (state_root, position_estimate, velocity_estimate) from stdin;`)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("prepare tmp power_smoothing_estimates: %w", err)
|
||||
}
|
||||
|
||||
for _, powerState := range powers {
|
||||
if _, err := stmt.Exec(
|
||||
powerState.common.stateroot.String(),
|
||||
powerState.epochSmoothingEstimate.PositionEstimate.String(),
|
||||
powerState.epochSmoothingEstimate.VelocityEstimate.String(),
|
||||
); err != nil {
|
||||
return xerrors.Errorf("failed to store smoothing estimate: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := stmt.Close(); err != nil {
|
||||
return xerrors.Errorf("close prepared power_smoothing_estimates: %w", err)
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`insert into power_smoothing_estimates select * from rse on conflict do nothing`); err != nil {
|
||||
return xerrors.Errorf("insert power_smoothing_estimates from tmp: %w", err)
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return xerrors.Errorf("commit power_smoothing_estimates tx: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
@@ -88,6 +88,10 @@ func (p *Processor) setupSchemas() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := p.setupPower(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -166,6 +170,14 @@ func (p *Processor) Start(ctx context.Context) {
|
||||
return nil
|
||||
})
|
||||
|
||||
grp.Go(func() error {
|
||||
if err := p.HandlePowerChanges(ctx, actorChanges[builtin.StoragePowerActorCodeID]); err != nil {
|
||||
return xerrors.Errorf("Failed to handle power actor changes: %w", err)
|
||||
}
|
||||
log.Info("Processes Power Changes")
|
||||
return nil
|
||||
})
|
||||
|
||||
grp.Go(func() error {
|
||||
if err := p.HandleMessageChanges(ctx, toProcess); err != nil {
|
||||
return xerrors.Errorf("Failed to handle message changes: %w", err)
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/reward"
|
||||
"github.com/filecoin-project/specs-actors/actors/util/smoothing"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
@@ -24,6 +25,8 @@ type rewardActorInfo struct {
|
||||
|
||||
// base reward in attofil for each block found during this epoch
|
||||
baseBlockReward big.Int
|
||||
|
||||
epochSmoothingEstimate *smoothing.FilterEstimate
|
||||
}
|
||||
|
||||
func (p *Processor) setupRewards() error {
|
||||
@@ -53,6 +56,15 @@ create table if not exists chain_power
|
||||
primary key,
|
||||
baseline_power text not null
|
||||
);
|
||||
|
||||
create table if not exists reward_smoothing_estimates
|
||||
(
|
||||
state_root text not null
|
||||
constraint reward_smoothing_estimates_pk
|
||||
primary key,
|
||||
position_estimate text not null,
|
||||
velocity_estimate text not null
|
||||
);
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -63,7 +75,7 @@ create table if not exists chain_power
|
||||
func (p *Processor) HandleRewardChanges(ctx context.Context, rewardTips ActorTips, nullRounds []types.TipSetKey) error {
|
||||
rewardChanges, err := p.processRewardActors(ctx, rewardTips, nullRounds)
|
||||
if err != nil {
|
||||
log.Fatalw("Failed to process reward actors", "error", err)
|
||||
return xerrors.Errorf("Failed to process reward actors: %w", err)
|
||||
}
|
||||
|
||||
if err := p.persistRewardActors(ctx, rewardChanges); err != nil {
|
||||
@@ -103,6 +115,7 @@ func (p *Processor) processRewardActors(ctx context.Context, rewardTips ActorTip
|
||||
|
||||
rw.baseBlockReward = rewardActorState.ThisEpochReward
|
||||
rw.baselinePower = rewardActorState.ThisEpochBaselinePower
|
||||
rw.epochSmoothingEstimate = rewardActorState.ThisEpochRewardSmoothed
|
||||
out = append(out, rw)
|
||||
}
|
||||
}
|
||||
@@ -162,6 +175,13 @@ func (p *Processor) persistRewardActors(ctx context.Context, rewards []rewardAct
|
||||
return nil
|
||||
})
|
||||
|
||||
grp.Go(func() error {
|
||||
if err := p.storeRewardSmoothingEstimates(rewards); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return grp.Wait()
|
||||
}
|
||||
|
||||
@@ -243,3 +263,46 @@ func (p *Processor) storeBaseBlockReward(rewards []rewardActorInfo) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Processor) storeRewardSmoothingEstimates(rewards []rewardActorInfo) error {
|
||||
tx, err := p.db.Begin()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("begin reward_smoothing_estimates tx: %w", err)
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`create temp table rse (like reward_smoothing_estimates) on commit drop`); err != nil {
|
||||
return xerrors.Errorf("prep reward_smoothing_estimates: %w", err)
|
||||
}
|
||||
|
||||
stmt, err := tx.Prepare(`copy rse (state_root, position_estimate, velocity_estimate) from stdin;`)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("prepare tmp reward_smoothing_estimates: %w", err)
|
||||
}
|
||||
|
||||
for _, rewardState := range rewards {
|
||||
if rewardState.epochSmoothingEstimate == nil {
|
||||
continue
|
||||
}
|
||||
if _, err := stmt.Exec(
|
||||
rewardState.common.stateroot.String(),
|
||||
rewardState.epochSmoothingEstimate.PositionEstimate.String(),
|
||||
rewardState.epochSmoothingEstimate.VelocityEstimate.String(),
|
||||
); err != nil {
|
||||
return xerrors.Errorf("failed to store smoothing estimate: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := stmt.Close(); err != nil {
|
||||
return xerrors.Errorf("close prepared reward_smoothing_estimates: %w", err)
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`insert into reward_smoothing_estimates select * from rse on conflict do nothing`); err != nil {
|
||||
return xerrors.Errorf("insert reward_smoothing_estimates from tmp: %w", err)
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return xerrors.Errorf("commit reward_smoothing_estimates tx: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<span>When creating miner, DO NOT REFRESH THE PAGE, wait for it to load. This can take more than 5min.</span>
|
||||
</div>
|
||||
<div class="Index-node">
|
||||
<span>If you don't have an owner/worker address, you can create it by following <a target="_blank" href="https://docs.lotu.sh/en+mining#get-started-22083">these instructions</a>.</span>
|
||||
<span>If you don't have an owner/worker address, you can create it by following <a target="_blank" href="https://lotu.sh/en+mining#get-started-22083">these instructions</a>.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Index-footer">
|
||||
|
||||
@@ -394,7 +394,7 @@ func (r *refunder) ProcessTipset(ctx context.Context, tipset *types.TipSet) (*Mi
|
||||
}
|
||||
|
||||
if r.percentExtra > 0 {
|
||||
refundValue = types.BigAdd(refundValue, types.BigDiv(types.BigMul(refundValue, types.NewInt(100)), types.NewInt(uint64(r.percentExtra))))
|
||||
refundValue = types.BigAdd(refundValue, types.BigMul(types.BigDiv(refundValue, types.NewInt(100)), types.NewInt(uint64(r.percentExtra))))
|
||||
}
|
||||
|
||||
log.Debugw("processing message", "method", messageMethod, "cid", msg.Cid, "from", m.From, "to", m.To, "value", m.Value, "gas_fee_cap", m.GasFeeCap, "gas_premium", m.GasPremium, "gas_used", recps[i].GasUsed, "refund", refundValue)
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/gbrlsnchs/jwt/v3"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/filecoin-project/lotus/api/apistruct"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/node/modules"
|
||||
)
|
||||
|
||||
var jwtCmd = &cli.Command{
|
||||
Name: "jwt",
|
||||
Usage: "work with lotus jwt secrets and tokens",
|
||||
Description: `The subcommands of jwt provide helpful tools for working with jwt files without
|
||||
having to run the lotus daemon.`,
|
||||
Subcommands: []*cli.Command{
|
||||
jwtNewCmd,
|
||||
},
|
||||
}
|
||||
|
||||
var jwtNewCmd = &cli.Command{
|
||||
Name: "new",
|
||||
Usage: "create a new jwt secret and token for lotus",
|
||||
ArgsUsage: "<name>",
|
||||
Description: `Jwt tokens are used to authenticate api requests to the lotus daemon.
|
||||
|
||||
The created jwt token have full privileges and should not be shared.`,
|
||||
Flags: []cli.Flag{},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if !cctx.Args().Present() {
|
||||
return fmt.Errorf("please specify a name")
|
||||
}
|
||||
|
||||
keyName := cctx.Args().First()
|
||||
|
||||
sk, err := ioutil.ReadAll(io.LimitReader(rand.Reader, 32))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keyInfo := types.KeyInfo{
|
||||
Type: modules.KTJwtHmacSecret,
|
||||
PrivateKey: sk,
|
||||
}
|
||||
|
||||
p := modules.JwtPayload{
|
||||
Allow: apistruct.AllPermissions,
|
||||
}
|
||||
|
||||
token, err := jwt.Sign(&p, jwt.NewHS256(keyInfo.PrivateKey))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("jwt-%s.jwts", keyName)
|
||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := file.Close(); err != nil {
|
||||
log.Warnf("failed to close output file: %w", err)
|
||||
}
|
||||
}()
|
||||
|
||||
bytes, err := json.Marshal(keyInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
encoded := hex.EncodeToString(bytes)
|
||||
if _, err := file.Write([]byte(encoded)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filenameToken := fmt.Sprintf("jwt-%s.token", keyName)
|
||||
return ioutil.WriteFile(filenameToken, token, 0600)
|
||||
},
|
||||
}
|
||||
@@ -330,7 +330,7 @@ var keyinfoNewCmd = &cli.Command{
|
||||
filename = strings.ReplaceAll(filename, "<addr>", keyAddr)
|
||||
filename = strings.ReplaceAll(filename, "<type>", keyType)
|
||||
|
||||
file, err := os.Create(filename)
|
||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ func main() {
|
||||
base16Cmd,
|
||||
bitFieldCmd,
|
||||
keyinfoCmd,
|
||||
jwtCmd,
|
||||
noncefix,
|
||||
bigIntParseCmd,
|
||||
staterootCmd,
|
||||
|
||||
@@ -2,19 +2,26 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"golang.org/x/xerrors"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
"github.com/filecoin-project/lotus/lib/tablewriter"
|
||||
"github.com/filecoin-project/lotus/storage"
|
||||
)
|
||||
|
||||
var actorCmd = &cli.Command{
|
||||
@@ -24,6 +31,7 @@ var actorCmd = &cli.Command{
|
||||
actorSetAddrsCmd,
|
||||
actorWithdrawCmd,
|
||||
actorSetPeeridCmd,
|
||||
actorControl,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -240,3 +248,232 @@ var actorWithdrawCmd = &cli.Command{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var actorControl = &cli.Command{
|
||||
Name: "control",
|
||||
Usage: "Manage control addresses",
|
||||
Subcommands: []*cli.Command{
|
||||
actorControlList,
|
||||
actorControlSet,
|
||||
},
|
||||
}
|
||||
|
||||
var actorControlList = &cli.Command{
|
||||
Name: "list",
|
||||
Usage: "Get currently set control addresses",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "color",
|
||||
Value: true,
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
color.NoColor = !cctx.Bool("color")
|
||||
|
||||
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
|
||||
api, acloser, err := lcli.GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer acloser()
|
||||
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
maddr, err := nodeApi.ActorAddress(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tw := tablewriter.New(
|
||||
tablewriter.Col("name"),
|
||||
tablewriter.Col("ID"),
|
||||
tablewriter.Col("key"),
|
||||
tablewriter.Col("use"),
|
||||
tablewriter.Col("balance"),
|
||||
)
|
||||
|
||||
postAddr, err := storage.AddressFor(ctx, api, mi, storage.PoStAddr, types.FromFil(1))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting address for post: %w", err)
|
||||
}
|
||||
|
||||
printKey := func(name string, a address.Address) {
|
||||
b, err := api.WalletBalance(ctx, a)
|
||||
if err != nil {
|
||||
fmt.Printf("%s\t%s: error getting balance: %s\n", name, a, err)
|
||||
return
|
||||
}
|
||||
|
||||
k, err := api.StateAccountKey(ctx, a, types.EmptyTSK)
|
||||
if err != nil {
|
||||
fmt.Printf("%s\t%s: error getting account key: %s\n", name, a, err)
|
||||
return
|
||||
}
|
||||
|
||||
kstr := k.String()
|
||||
if !cctx.Bool("verbose") {
|
||||
kstr = kstr[:9] + "..."
|
||||
}
|
||||
|
||||
bstr := types.FIL(b).String()
|
||||
switch {
|
||||
case b.LessThan(types.FromFil(10)):
|
||||
bstr = color.RedString(bstr)
|
||||
case b.LessThan(types.FromFil(50)):
|
||||
bstr = color.YellowString(bstr)
|
||||
default:
|
||||
bstr = color.GreenString(bstr)
|
||||
}
|
||||
|
||||
var uses []string
|
||||
if a == mi.Worker {
|
||||
uses = append(uses, color.YellowString("other"))
|
||||
}
|
||||
if a == postAddr {
|
||||
uses = append(uses, color.GreenString("post"))
|
||||
}
|
||||
|
||||
tw.Write(map[string]interface{}{
|
||||
"name": name,
|
||||
"ID": a,
|
||||
"key": kstr,
|
||||
"use": strings.Join(uses, " "),
|
||||
"balance": bstr,
|
||||
})
|
||||
}
|
||||
|
||||
printKey("owner", mi.Owner)
|
||||
printKey("worker", mi.Worker)
|
||||
for i, ca := range mi.ControlAddresses {
|
||||
printKey(fmt.Sprintf("control-%d", i), ca)
|
||||
}
|
||||
|
||||
return tw.Flush(os.Stdout)
|
||||
},
|
||||
}
|
||||
|
||||
var actorControlSet = &cli.Command{
|
||||
Name: "set",
|
||||
Usage: "Set control address(-es)",
|
||||
ArgsUsage: "[...address]",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "really-do-it",
|
||||
Usage: "Actually send transaction performing the action",
|
||||
Value: false,
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
|
||||
api, acloser, err := lcli.GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer acloser()
|
||||
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
maddr, err := nodeApi.ActorAddress(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
del := map[address.Address]struct{}{}
|
||||
existing := map[address.Address]struct{}{}
|
||||
for _, controlAddress := range mi.ControlAddresses {
|
||||
ka, err := api.StateAccountKey(ctx, controlAddress, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
del[ka] = struct{}{}
|
||||
existing[ka] = struct{}{}
|
||||
}
|
||||
|
||||
var toSet []address.Address
|
||||
|
||||
for i, as := range cctx.Args().Slice() {
|
||||
a, err := address.NewFromString(as)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("parsing address %d: %w", i, err)
|
||||
}
|
||||
|
||||
ka, err := api.StateAccountKey(ctx, a, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// make sure the address exists on chain
|
||||
_, err = api.StateLookupID(ctx, ka, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("looking up %s: %w", ka, err)
|
||||
}
|
||||
|
||||
delete(del, ka)
|
||||
toSet = append(toSet, ka)
|
||||
}
|
||||
|
||||
for a := range del {
|
||||
fmt.Println("Remove", a)
|
||||
}
|
||||
for _, a := range toSet {
|
||||
if _, exists := existing[a]; !exists {
|
||||
fmt.Println("Add", a)
|
||||
}
|
||||
}
|
||||
|
||||
if !cctx.Bool("really-do-it") {
|
||||
fmt.Println("Pass --really-do-it to actually execute this action")
|
||||
return nil
|
||||
}
|
||||
|
||||
cwp := &miner.ChangeWorkerAddressParams{
|
||||
NewWorker: mi.Worker,
|
||||
NewControlAddrs: toSet,
|
||||
}
|
||||
|
||||
sp, err := actors.SerializeParams(cwp)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("serializing params: %w", err)
|
||||
}
|
||||
|
||||
smsg, err := api.MpoolPushMessage(ctx, &types.Message{
|
||||
From: mi.Owner,
|
||||
To: maddr,
|
||||
Method: builtin.MethodsMiner.ChangeWorkerAddress,
|
||||
|
||||
Value: big.Zero(),
|
||||
Params: sp,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("mpool push: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("Message CID:", smsg.Cid())
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -143,6 +143,7 @@ func infoCmdAct(cctx *cli.Context) error {
|
||||
|
||||
fmt.Printf("Miner Balance: %s\n", color.YellowString("%s", types.FIL(mact.Balance)))
|
||||
fmt.Printf("\tPreCommit: %s\n", types.FIL(mas.PreCommitDeposits))
|
||||
fmt.Printf("\tPledge: %s\n", types.FIL(mas.InitialPledgeRequirement))
|
||||
fmt.Printf("\tLocked: %s\n", types.FIL(mas.LockedFunds))
|
||||
color.Green("\tAvailable: %s", types.FIL(types.BigSub(mact.Balance, types.BigAdd(mas.LockedFunds, mas.PreCommitDeposits))))
|
||||
wb, err := api.WalletBalance(ctx, mi.Worker)
|
||||
|
||||
@@ -7,12 +7,13 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
|
||||
"github.com/docker/go-units"
|
||||
"github.com/google/uuid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
@@ -107,6 +108,10 @@ var initCmd = &cli.Command{
|
||||
Usage: "set gas premium for initialization messages in AttoFIL",
|
||||
Value: "0",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "from",
|
||||
Usage: "select which address to send actor creation message from",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
log.Info("Initializing lotus miner")
|
||||
@@ -631,9 +636,18 @@ func createStorageMiner(ctx context.Context, api lapi.FullNode, peerid peer.ID,
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
sender := owner
|
||||
if fromstr := cctx.String("from"); fromstr != "" {
|
||||
faddr, err := address.NewFromString(fromstr)
|
||||
if err != nil {
|
||||
return address.Undef, fmt.Errorf("could not parse from address: %w", err)
|
||||
}
|
||||
sender = faddr
|
||||
}
|
||||
|
||||
createStorageMinerMsg := &types.Message{
|
||||
To: builtin.StoragePowerActorAddr,
|
||||
From: owner,
|
||||
From: sender,
|
||||
Value: big.Zero(),
|
||||
|
||||
Method: builtin.MethodsPower.CreateMiner,
|
||||
|
||||
@@ -37,6 +37,7 @@ func main() {
|
||||
lcli.WithCategory("chain", infoCmd),
|
||||
lcli.WithCategory("market", storageDealsCmd),
|
||||
lcli.WithCategory("market", retrievalDealsCmd),
|
||||
lcli.WithCategory("market", dataTransfersCmd),
|
||||
lcli.WithCategory("storage", sectorsCmd),
|
||||
lcli.WithCategory("storage", provingCmd),
|
||||
lcli.WithCategory("storage", storageCmd),
|
||||
|
||||
@@ -5,10 +5,12 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
tm "github.com/buger/goterm"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-cidutil/cidenc"
|
||||
@@ -358,12 +360,16 @@ var dealsListCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
sort.Slice(deals, func(i, j int) bool {
|
||||
return deals[i].CreationTime.Time().Before(deals[j].CreationTime.Time())
|
||||
})
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0)
|
||||
|
||||
verbose := cctx.Bool("verbose")
|
||||
|
||||
if verbose {
|
||||
_, _ = fmt.Fprintf(w, "ProposalCid\tDealId\tState\tClient\tSize\tPrice\tDuration\tMessage\n")
|
||||
_, _ = fmt.Fprintf(w, "Creation\tProposalCid\tDealId\tState\tClient\tSize\tPrice\tDuration\tMessage\n")
|
||||
} else {
|
||||
_, _ = fmt.Fprintf(w, "ProposalCid\tDealId\tState\tClient\tSize\tPrice\tDuration\n")
|
||||
}
|
||||
@@ -376,6 +382,10 @@ var dealsListCmd = &cli.Command{
|
||||
|
||||
fil := types.FIL(types.BigMul(deal.Proposal.StoragePricePerEpoch, types.NewInt(uint64(deal.Proposal.Duration()))))
|
||||
|
||||
if verbose {
|
||||
_, _ = fmt.Fprintf(w, "%s\t", deal.CreationTime.Time().Format(time.Stamp))
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\t%s\t%s", propcid, deal.DealID, storagemarket.DealStates[deal.State], deal.Proposal.Client, units.BytesSize(float64(deal.Proposal.PieceSize)), fil, deal.Proposal.Duration())
|
||||
if verbose {
|
||||
_, _ = fmt.Fprintf(w, "\t%s", deal.Message)
|
||||
@@ -506,3 +516,87 @@ var setSealDurationCmd = &cli.Command{
|
||||
return nodeApi.SectorSetExpectedSealDuration(ctx, time.Duration(delay))
|
||||
},
|
||||
}
|
||||
|
||||
var dataTransfersCmd = &cli.Command{
|
||||
Name: "data-transfers",
|
||||
Usage: "Manage data transfers",
|
||||
Subcommands: []*cli.Command{
|
||||
transfersListCmd,
|
||||
},
|
||||
}
|
||||
|
||||
var transfersListCmd = &cli.Command{
|
||||
Name: "list",
|
||||
Usage: "List ongoing data transfers for this miner",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "color",
|
||||
Usage: "use color in display output",
|
||||
Value: true,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "completed",
|
||||
Usage: "show completed data transfers",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "watch",
|
||||
Usage: "watch deal updates in real-time, rather than a one time list",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := lcli.GetStorageMinerAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
channels, err := api.MarketListDataTransfers(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
completed := cctx.Bool("completed")
|
||||
color := cctx.Bool("color")
|
||||
watch := cctx.Bool("watch")
|
||||
|
||||
if watch {
|
||||
channelUpdates, err := api.MarketDataTransferUpdates(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
tm.Clear() // Clear current screen
|
||||
|
||||
tm.MoveCursor(1, 1)
|
||||
|
||||
lcli.OutputDataTransferChannels(tm.Screen, channels, completed, color)
|
||||
|
||||
tm.Flush()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case channelUpdate := <-channelUpdates:
|
||||
var found bool
|
||||
for i, existing := range channels {
|
||||
if existing.TransferID == channelUpdate.TransferID &&
|
||||
existing.OtherPeer == channelUpdate.OtherPeer &&
|
||||
existing.IsSender == channelUpdate.IsSender &&
|
||||
existing.IsInitiator == channelUpdate.IsInitiator {
|
||||
channels[i] = channelUpdate
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
channels = append(channels, channelUpdate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lcli.OutputDataTransferChannels(os.Stdout, channels, completed, color)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -67,6 +67,12 @@
|
||||
"slug": "en+setup-troubleshooting",
|
||||
"github": "en/setup-troubleshooting.md",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"title": "Environment Variables",
|
||||
"slug": "en+env-vars",
|
||||
"github": "en/environment-vars.md",
|
||||
"value": null
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -143,6 +149,13 @@
|
||||
"value": null,
|
||||
"posts": []
|
||||
},
|
||||
{
|
||||
"title": "Payment Channels",
|
||||
"slug": "en+payment-channels",
|
||||
"github": "en/payment-channels.md",
|
||||
"value": null,
|
||||
"posts": []
|
||||
},
|
||||
{
|
||||
"title": "Command Line Interface",
|
||||
"slug": "en+cli",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Pond UI
|
||||
|
||||
Pond is a graphical testbed for [Lotus](https://docs.lotu.sh). Using it will setup a separate local network which is helpful for debugging. Pond will spin up nodes, connect them in a given topology, start them mining, and observe how they function over time.
|
||||
Pond is a graphical testbed for [Lotus](https://lotu.sh). Using it will setup a separate local network which is helpful for debugging. Pond will spin up nodes, connect them in a given topology, start them mining, and observe how they function over time.
|
||||
|
||||
## Build
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ It is important to note that the key store also holds the *private* key associat
|
||||
|
||||
There is no explicit Filecoin message or CLI command that creates an account actor but rather it is implicit in the public-key address reflected by the wallet. The first time the VM sees it as the receiving address of a Filecoin message it will automatically create the associated account actor.
|
||||
|
||||
We will trigger its creation then by sending funds to it, needed for the creation of the storage miner (note this document reflects the Filecoin Testnet, where we actually don't need funds to create a miner but will do the full procedure regardless to show the full cycle). In the Testnet we can send funds to an account trough the [Faucet](https://faucet.testnet.filecoin.io/funds.html), inserting the address returned by the `wallet new` command.
|
||||
We will trigger its creation then by sending funds to it, needed for the creation of the storage miner (note this document reflects the Filecoin Testnet, where we actually don't need funds to create a miner but will do the full procedure regardless to show the full cycle). In the Testnet we can send funds to an account through the [Faucet](http://spacerace.faucet.glif.io/), inserting the address returned by the `wallet new` command.
|
||||
|
||||
FIXME: Is there an easy way to visualize the message generated by the Faucet?
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# Lotus Environment Variables
|
||||
|
||||
## Building
|
||||
|
||||
## Common
|
||||
|
||||
The environment variables are common across most lotus binaries.
|
||||
|
||||
### `LOTUS_FD_MAX`
|
||||
|
||||
Sets the file descriptor limit for the process. This should be set high (8192
|
||||
or higher) if you ever notice 'too many open file descriptor' errors.
|
||||
|
||||
### `LOTUS_JAEGER`
|
||||
|
||||
This can be set to enable jaeger trace reporting. The value should be the url
|
||||
of the jaeger trace collector, the default for most jaeger setups should be
|
||||
`localhost:6831`.
|
||||
|
||||
### `LOTUS_DEV`
|
||||
|
||||
If set to a non-empty value, certain parts of the application will print more
|
||||
verbose information to aid in development of the software. Not recommended for
|
||||
end users.
|
||||
|
||||
## Lotus Daemon
|
||||
|
||||
### `LOTUS_PATH`
|
||||
|
||||
Sets the location for the lotus daemon on-disk repo. If left empty, this defaults to `~/.lotus`.
|
||||
|
||||
### `LOTUS_SKIP_GENESIS_CHECK`
|
||||
|
||||
Can be set to `_yes_` if you wish to run a lotus network with a different
|
||||
genesis than the default one built into your lotus binary.
|
||||
|
||||
### `LOTUS_CHAIN_TIPSET_CACHE`
|
||||
|
||||
Sets the cache size for the chainstore tipset cache. The default value is 8192,
|
||||
but if your usage of the lotus API involves frequent arbitrary tipset lookups,
|
||||
you may want to increase this.
|
||||
|
||||
### `LOTUS_CHAIN_INDEX_CACHE`
|
||||
|
||||
Sets the cache size for the chainstore epoch index cache. The default value is 32768,
|
||||
but if your usage of the lotus API involves frequent deep chain lookups for
|
||||
block heights that are very far from the current chain height, you may want to
|
||||
increase this.
|
||||
|
||||
|
||||
|
||||
## Lotus Miner
|
||||
|
||||
A number of environment variables are respected for configuring the behavior of the filecoin proving subsystem. For more details on those [see here](https://github.com/filecoin-project/rust-fil-proofs/#settings).
|
||||
|
||||
### `LOTUS_MINER_PATH`
|
||||
|
||||
Sets the location for the lotus miners on-disk repo. If left empty, this defaults to `~/.lotusminer`.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ to a Lotus Node over the JSON-RPC API.
|
||||
|
||||
### How do I set up a Lotus Node?
|
||||
|
||||
Follow the instructions found [here](https://docs.lotu.sh/en+getting-started).
|
||||
Follow the instructions found [here](https://lotu.sh/en+getting-started).
|
||||
|
||||
### Where can I get the latest version of Lotus?
|
||||
|
||||
@@ -52,7 +52,7 @@ Stop the Lotus daemon, and delete all related files, including sealed and chain
|
||||
running `rm ~/.lotus ~/.lotusminer`.
|
||||
|
||||
Then, install Lotus afresh by following the instructions
|
||||
found [here](https://docs.lotu.sh/en+getting-started).
|
||||
found [here](https://lotu.sh/en+getting-started).
|
||||
|
||||
### Can I configure where the node's config and data goes?
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ For more details about Filecoin, check out the [Filecoin Docs](https://docs.file
|
||||
|
||||
## What can I learn here?
|
||||
|
||||
- How to install Lotus on [Arch Linux](https://docs.lotu.sh/en+install-lotus-arch), [Ubuntu](https://docs.lotu.sh/en+install-lotus-ubuntu), or [MacOS](https://docs.lotu.sh/en+install-lotus-macos).
|
||||
- Joining the [Lotus Testnet](https://docs.lotu.sh/en+join-testnet).
|
||||
- [Storing](https://docs.lotu.sh/en+storing-data) or [retrieving](https://docs.lotu.sh/en+retrieving-data) data.
|
||||
- Mining Filecoin using the **Lotus Miner** in your [CLI](https://docs.lotu.sh/en+mining).
|
||||
- How to install Lotus on [Arch Linux](https://lotu.sh/en+install-lotus-arch), [Ubuntu](https://lotu.sh/en+install-lotus-ubuntu), or [MacOS](https://lotu.sh/en+install-lotus-macos).
|
||||
- Joining the [Lotus Testnet](https://lotu.sh/en+join-testnet).
|
||||
- [Storing](https://lotu.sh/en+storing-data) or [retrieving](https://lotu.sh/en+retrieving-data) data.
|
||||
- Mining Filecoin using the **Lotus Miner** in your [CLI](https://lotu.sh/en+mining).
|
||||
|
||||
## How is Lotus designed?
|
||||
|
||||
|
||||
@@ -48,4 +48,4 @@ env env RUSTFLAGS="-C target-cpu=native -g" FFI_BUILD_FROM_SOURCE=1 make clean d
|
||||
sudo make install
|
||||
```
|
||||
|
||||
After installing Lotus, you can run the `lotus` command directly from your CLI to see usage documentation. Next, you can join the [Lotus Testnet](https://docs.lotu.sh/en+join-testnet).
|
||||
After installing Lotus, you can run the `lotus` command directly from your CLI to see usage documentation. Next, you can join the [Lotus Testnet](https://lotu.sh/en+join-testnet).
|
||||
|
||||
@@ -51,4 +51,4 @@ env env RUSTFLAGS="-C target-cpu=native -g" FFI_BUILD_FROM_SOURCE=1 make clean d
|
||||
sudo make install
|
||||
```
|
||||
|
||||
After installing Lotus, you can run the `lotus` command directly from your CLI to see usage documentation. Next, you can join the [Lotus TestNet](https://docs.lotu.sh/en+join-testnet).
|
||||
After installing Lotus, you can run the `lotus` command directly from your CLI to see usage documentation. Next, you can join the [Lotus TestNet](https://lotu.sh/en+join-testnet).
|
||||
|
||||
@@ -59,4 +59,4 @@ make clean && make all
|
||||
sudo make install
|
||||
```
|
||||
|
||||
After installing Lotus, you can run the `lotus` command directly from your CLI to see usage documentation. Next, you can join the [Lotus Testnet](https://docs.lotu.sh/en+join-testnet).
|
||||
After installing Lotus, you can run the `lotus` command directly from your CLI to see usage documentation. Next, you can join the [Lotus Testnet](https://lotu.sh/en+join-testnet).
|
||||
|
||||
@@ -51,4 +51,4 @@ sudo make install
|
||||
```
|
||||
|
||||
|
||||
After installing Lotus, you can run the `lotus` command directly from your CLI to see usage documentation. Next, you can join the [Lotus Testnet](https://docs.lotu.sh/en+join-testnet).
|
||||
After installing Lotus, you can run the `lotus` command directly from your CLI to see usage documentation. Next, you can join the [Lotus Testnet](https://lotu.sh/en+join-testnet).
|
||||
|
||||
@@ -26,9 +26,9 @@ In another terminal window, check your connection with peers:
|
||||
lotus net peers | wc -l
|
||||
```
|
||||
|
||||
In order to connect to the network, you need to be connected to at least 1 peer. If you’re seeing 0 peers, read our [troubleshooting notes](https://docs.lotu.sh/en+setup-troubleshooting).
|
||||
In order to connect to the network, you need to be connected to at least 1 peer. If you’re seeing 0 peers, read our [troubleshooting notes](https://lotu.sh/en+setup-troubleshooting).
|
||||
|
||||
Make sure that you have a reasonable "open files limit" set on your machine, such as 10000. If you're seeing a lower value, such as 256 (default on macOS), read our [troubleshooting notes](https://docs.lotu.sh/en+setup-troubleshooting) on how to update it prior to starting the Lotus daemon.
|
||||
Make sure that you have a reasonable "open files limit" set on your machine, such as 10000. If you're seeing a lower value, such as 256 (default on macOS), read our [troubleshooting notes](https://lotu.sh/en+setup-troubleshooting) on how to update it prior to starting the Lotus daemon.
|
||||
|
||||
## Chain sync
|
||||
|
||||
@@ -57,7 +57,7 @@ Here is an example of the response:
|
||||
t1aswwvjsae63tcrniz6x5ykvsuotlgkvlulnqpsi
|
||||
```
|
||||
|
||||
- Visit the [faucet](https://faucet.testnet.filecoin.io) to add funds.
|
||||
- Visit the [faucet](http://spacerace.faucet.glif.io/) to add funds.
|
||||
- Paste the address you created.
|
||||
- Press the send button.
|
||||
|
||||
|
||||
@@ -34,11 +34,11 @@ RemoteListenAddress = "127.0.0.1:2345"
|
||||
|
||||
To make your node accessible over the local area network, you will need to determine your machines IP on the LAN, and change the `127.0.0.1` in the file to that address.
|
||||
|
||||
A more permissive and less secure option is to change it to `0.0.0.0`. This will allow anyone who can connect to your computer on that port to access the [API](https://docs.lotu.sh/en+api). They will still need an auth token.
|
||||
A more permissive and less secure option is to change it to `0.0.0.0`. This will allow anyone who can connect to your computer on that port to access the [API](https://lotu.sh/en+api). They will still need an auth token.
|
||||
|
||||
`RemoteListenAddress` must be set to an address which other nodes on your network will be able to reach.
|
||||
|
||||
Next, you will need to [create an authentication token](https://docs.lotu.sh/en+api-scripting-support#generate-a-jwt-46). All Lotus APIs require authentication tokens to ensure your processes are as secure against attackers attempting to make unauthenticated requests to them.
|
||||
Next, you will need to [create an authentication token](https://lotu.sh/en+api-scripting-support#generate-a-jwt-46). All Lotus APIs require authentication tokens to ensure your processes are as secure against attackers attempting to make unauthenticated requests to them.
|
||||
|
||||
### Connect the Lotus Worker
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ lotus-miner info
|
||||
# WARN main lotus-storage-miner/main.go:73 failed to get api endpoint: (/Users/myrmidon/.lotusminer) %!w(*errors.errorString=&{API not running (no endpoint)}):
|
||||
```
|
||||
|
||||
If you see this, that means your **Lotus Miner** isn't ready yet. You need to finish [syncing the chain](https://docs.lotu.sh/en+join-testnet).
|
||||
If you see this, that means your **Lotus Miner** isn't ready yet. You need to finish [syncing the chain](https://lotu.sh/en+join-testnet).
|
||||
|
||||
## Error: Your computer may not be fast enough
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Storage Mining
|
||||
|
||||
Here are instructions to learn how to perform storage mining. For hardware specifications please read [this](https://docs.lotu.sh/en+hardware-mining).
|
||||
Here are instructions to learn how to perform storage mining. For hardware specifications please read [this](https://lotu.sh/en+hardware-mining).
|
||||
|
||||
It is useful to [join the Testnet](https://docs.lotu.sh/en+join-testnet) prior to attempting storage mining for the first time.
|
||||
It is useful to [join the Testnet](https://lotu.sh/en+join-testnet) prior to attempting storage mining for the first time.
|
||||
|
||||
## Note: Using the Lotus Miner from China
|
||||
|
||||
@@ -28,7 +28,7 @@ lotus wallet new bls
|
||||
|
||||
With your wallet address:
|
||||
|
||||
- Visit the [faucet](https://faucet.testnet.filecoin.io)
|
||||
- Visit the [faucet](http://spacerace.faucet.glif.io/)
|
||||
- Click "Create Miner"
|
||||
- DO NOT REFRESH THE PAGE. THIS OPERATION CAN TAKE SOME TIME.
|
||||
|
||||
@@ -109,8 +109,8 @@ The addresses passed to `set-addrs` parameter in the commands below should be cu
|
||||
|
||||
Once the config file has been updated, set the on-chain record of the miner's listen addresses:
|
||||
|
||||
```
|
||||
lotus-miner actor set-addrs <multiaddr_1> <multiaddr_2> ... <multiaddr_n>
|
||||
```
|
||||
lotus-miner actor set-addrs <multiaddr_1> <multiaddr_2> ... <multiaddr_n>
|
||||
```
|
||||
|
||||
This updates the `MinerInfo` object in the miner's actor, which will be looked up
|
||||
@@ -119,5 +119,50 @@ when a client attempts to make a deal. Any number of addresses can be provided.
|
||||
Example:
|
||||
|
||||
```
|
||||
lotus-miner actor set-addrs /ip4/123.123.73.123/tcp/12345 /ip4/223.223.83.223/tcp/23456
|
||||
lotus-miner actor set-addrs /ip4/123.123.73.123/tcp/12345 /ip4/223.223.83.223/tcp/23456
|
||||
```
|
||||
|
||||
# Separate address for windowPoSt messages
|
||||
|
||||
WindowPoSt is the mechanism through which storage is verified in Filecoin. It requires miners to submit proofs for all sectors every 24h, which require sending messages to the chain.
|
||||
|
||||
Because many other mining related actions require sending messages to the chain, and not all of those are "high value", it may be desirable to use a separate account to send PoSt messages from. This allows for setting lower GasFeeCaps on the lower value messages without creating head-of-line blocking problems for the PoSt messages in congested chain conditions
|
||||
|
||||
To set this up, first create a new account, and send it some funds for gas fees:
|
||||
```sh
|
||||
lotus wallet new bls
|
||||
t3defg...
|
||||
|
||||
lotus send t3defg... 100
|
||||
```
|
||||
|
||||
Next add the control address
|
||||
```sh
|
||||
lotus-miner actor control set t3defg...
|
||||
Add t3defg...
|
||||
Pass --really-do-it to actually execute this action
|
||||
```
|
||||
|
||||
Now actually set the addresses
|
||||
```sh
|
||||
lotus-miner actor control set --really-do-it t3defg...
|
||||
Add t3defg...
|
||||
Message CID: bafy2..
|
||||
```
|
||||
|
||||
Wait for the message to land on chain
|
||||
```sh
|
||||
lotus state wait-msg bafy2..
|
||||
...
|
||||
Exit Code: 0
|
||||
...
|
||||
```
|
||||
|
||||
Check miner control address list to make sure the address was correctly setup
|
||||
```sh
|
||||
lotus-miner actor control list
|
||||
name ID key use balance
|
||||
owner t01111 t3abcd... other 300 FIL
|
||||
worker t01111 t3abcd... other 300 FIL
|
||||
control-0 t02222 t3defg... post 100 FIL
|
||||
```
|
||||
|
||||
@@ -20,6 +20,7 @@ The full node API defines the following methods for interacting with the mpool:
|
||||
MpoolSub(context.Context) (<-chan MpoolUpdate, error)
|
||||
MpoolGetConfig(context.Context) (*types.MpoolConfig, error)
|
||||
MpoolSetConfig(context.Context, *types.MpoolConfig) error
|
||||
MpoolClear(context.Context, local bool) error
|
||||
```
|
||||
|
||||
### MpoolPending
|
||||
@@ -61,6 +62,13 @@ Returns (a copy of) the current mpool configuration.
|
||||
|
||||
Sets the mpool configuration to (a copy of) the supplied configuration object.
|
||||
|
||||
### MpoolClear
|
||||
|
||||
Clears pending messages from the mpool; if `local` is `true` then local messages are also cleared and removed from the datastore.
|
||||
|
||||
This should be used with extreme care and only in the case of errors during head changes that
|
||||
would leave the mpool in an inconsistent state.
|
||||
|
||||
|
||||
## Command Line Interfae
|
||||
|
||||
@@ -75,6 +83,7 @@ lotus mpool stat [--local]
|
||||
lotus mpool replace [--gas-feecap <feecap>] [--gas-premium <premium>] [--gas-limit <limit>] [from] [nonce]
|
||||
lotus mpool find [--from <address>] [--to <address>] [--method <int>]
|
||||
lotus mpool config [<configuration>]
|
||||
lotus mpool clear [--local]
|
||||
```
|
||||
|
||||
### lotus mpool pending
|
||||
@@ -98,6 +107,12 @@ Searches for messages in the mpool.
|
||||
### lotus mpool config
|
||||
Gets or sets the current mpool configuration.
|
||||
|
||||
### lotus mpool clear
|
||||
Unconditionally clears pending messages from the mpool.
|
||||
If the `--local` flag is passed, then local messages are also cleared; otherwise local messages are retained.
|
||||
|
||||
*Warning*: this command should only be used in the case of head change errors leaving the mpool in an state.
|
||||
|
||||
## Configuration
|
||||
|
||||
The mpool a few parameters that can be configured by the user, either through the API
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
# Payment Channels
|
||||
|
||||
Payment channels are used to transfer funds between two actors.
|
||||
|
||||
For example in lotus a payment channel is created when a client wants to fetch data from a provider.
|
||||
The client sends vouchers for the payment channel, and the provider sends data in response.
|
||||
|
||||
The payment channel is created on-chain with an initial amount.
|
||||
Vouchers allow the client and the provider to exchange funds incrementally off-chain.
|
||||
The provider can submit vouchers to chain at any stage.
|
||||
Either party to the payment channel can settle the payment channel on chain.
|
||||
After a settlement period (currently 12 hours) either party to the payment channel can call collect on chain.
|
||||
Collect sends the value of submitted vouchers to the channel recipient (the provider), and refunds the remaining channel balance to the channel creator (the client).
|
||||
|
||||
Vouchers have a lane, a nonce and a value, where vouchers with a higher nonce supersede vouchers with a lower nonce in the same lane.
|
||||
Each deal is created on a different lane.
|
||||
|
||||
Note that payment channels and vouchers can be used for any situation in which two parties need to incrementally transfer value between each other off-chain.
|
||||
|
||||
## Using the CLI
|
||||
|
||||
For example a client creates a payment channel to a provider with value 10 FIL.
|
||||
|
||||
```sh
|
||||
$ lotus paych get <client addr> <provider addr> 10
|
||||
<channel addr>
|
||||
```
|
||||
|
||||
The client creates a voucher in lane 0 (implied) with nonce 1 (implied) and value 2.
|
||||
|
||||
```sh
|
||||
$ lotus paych voucher create <channel addr> 2
|
||||
<voucher>
|
||||
```
|
||||
|
||||
The client sends the voucher to the provider and the provider adds the voucher to their local store.
|
||||
|
||||
```sh
|
||||
$ lotus paych voucher add <channel addr> <voucher>
|
||||
```
|
||||
|
||||
The provider sends some data to the client.
|
||||
|
||||
The client creates a voucher in lane 0 (implied) with nonce 2 (implied) and value 4.
|
||||
|
||||
```sh
|
||||
$ lotus paych voucher create <channel addr> 4
|
||||
<voucher>
|
||||
```
|
||||
|
||||
The client sends the voucher to the provider and the provider adds the voucher and sends back more data.
|
||||
etc.
|
||||
|
||||
The client can add value to the channel after it has been created by calling `paych get` with the same client and provider addresses.
|
||||
|
||||
```sh
|
||||
$ lotus paych get <client addr> <provider addr> 5
|
||||
<channel addr> # Same address as above. Channel now has 15
|
||||
```
|
||||
|
||||
Once the client has received all their data, they may settle the channel.
|
||||
Note that settlement doesn't have to be done immediately.
|
||||
For example the client may keep the channel open as long as it wants to continue making deals with the provider.
|
||||
|
||||
```sh
|
||||
$ lotus paych settle <channel addr>
|
||||
```
|
||||
|
||||
The provider can submit vouchers to chain (note that lotus does this automatically when it sees a settle message appear on chain).
|
||||
The provider may have received many vouchers with incrementally higher values.
|
||||
The provider should submit the best vouchers. Note that there will be one best voucher for each lane.
|
||||
|
||||
```sh
|
||||
$ lotus paych voucher best-spendable <channel addr>
|
||||
<voucher>
|
||||
<voucher>
|
||||
<voucher>
|
||||
|
||||
$ lotus paych voucher submit <channel addr> <voucher>
|
||||
```
|
||||
|
||||
Once the settlement period is over, either the client or provider can call collect to disburse funds.
|
||||
|
||||
```sh
|
||||
$ lotus paych collect <channel addr>
|
||||
```
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Here are the operations you can perform after you have stored and sealed a **Data CID** with the **Lotus Miner** in the network.
|
||||
|
||||
If you would like to learn how to store a **Data CID** on a miner, read the instructions [here](https://docs.lotu.sh/en+storing-data).
|
||||
If you would like to learn how to store a **Data CID** on a miner, read the instructions [here](https://lotu.sh/en+storing-data).
|
||||
|
||||
## Find by Data CID
|
||||
|
||||
|
||||
@@ -24,4 +24,4 @@ In order to retrieve a file, it must be sealed. Miners can check sealing progres
|
||||
lotus-miner sectors list
|
||||
```
|
||||
|
||||
When sealing is complete, `pSet: NO` will become `pSet: YES`. From now on the **Data CID** is [retrievable](https://docs.lotu.sh/en+retrieving-data) from the **Lotus Miner**.
|
||||
When sealing is complete, `pSet: NO` will become `pSet: YES`. From now on the **Data CID** is [retrievable](https://lotu.sh/en+retrieving-data) from the **Lotus Miner**.
|
||||
|
||||
Vendored
+1
-1
@@ -79,7 +79,7 @@ func checkPrecommit(ctx context.Context, maddr address.Address, si SectorInfo, t
|
||||
return &ErrApi{xerrors.Errorf("calling StateComputeDataCommitment: %w", err)}
|
||||
}
|
||||
|
||||
if !commD.Equals(*si.CommD) {
|
||||
if si.CommD == nil || !commD.Equals(*si.CommD) {
|
||||
return &ErrBadCommD{xerrors.Errorf("on chain CommD differs from sector: %s != %s", commD, si.CommD)}
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -308,6 +308,10 @@ func (m *Sealing) handleCommitting(ctx statemachine.Context, sector SectorInfo)
|
||||
|
||||
log.Infof("KOMIT %d %x(%d); %x(%d); %v; r:%x; d:%x", sector.SectorNumber, sector.TicketValue, sector.TicketEpoch, sector.SeedValue, sector.SeedEpoch, sector.pieceInfos(), sector.CommR, sector.CommD)
|
||||
|
||||
if sector.CommD == nil || sector.CommR == nil {
|
||||
return ctx.Send(SectorCommitFailed{xerrors.Errorf("sector had nil commR or commD")})
|
||||
}
|
||||
|
||||
cids := storage.SectorCids{
|
||||
Unsealed: *sector.CommD,
|
||||
Sealed: *sector.CommR,
|
||||
|
||||
+3
@@ -44,6 +44,9 @@ func (m *Sealing) MarkForUpgrade(id abi.SectorNumber) error {
|
||||
}
|
||||
|
||||
func (m *Sealing) tryUpgradeSector(ctx context.Context, params *miner.SectorPreCommitInfo) big.Int {
|
||||
if len(params.DealIDs) == 0 {
|
||||
return big.Zero()
|
||||
}
|
||||
replace := m.maybeUpgradableSector()
|
||||
if replace != nil {
|
||||
loc, err := m.api.StateSectorPartition(ctx, m.maddr, *replace, nil)
|
||||
|
||||
@@ -28,9 +28,9 @@ require (
|
||||
github.com/filecoin-project/go-bitfield v0.2.0
|
||||
github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2
|
||||
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03
|
||||
github.com/filecoin-project/go-data-transfer v0.6.1
|
||||
github.com/filecoin-project/go-data-transfer v0.6.2
|
||||
github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f
|
||||
github.com/filecoin-project/go-fil-markets v0.5.6
|
||||
github.com/filecoin-project/go-fil-markets v0.5.7
|
||||
github.com/filecoin-project/go-jsonrpc v0.1.2-0.20200817153016-2ea5cbaf5ec0
|
||||
github.com/filecoin-project/go-multistore v0.0.3
|
||||
github.com/filecoin-project/go-padreader v0.0.0-20200210211231-548257017ca6
|
||||
@@ -94,7 +94,7 @@ require (
|
||||
github.com/libp2p/go-libp2p-noise v0.1.1
|
||||
github.com/libp2p/go-libp2p-peer v0.2.0
|
||||
github.com/libp2p/go-libp2p-peerstore v0.2.6
|
||||
github.com/libp2p/go-libp2p-pubsub v0.3.4
|
||||
github.com/libp2p/go-libp2p-pubsub v0.3.5-0.20200821075113-efd56962bced
|
||||
github.com/libp2p/go-libp2p-quic-transport v0.7.1
|
||||
github.com/libp2p/go-libp2p-record v0.1.3
|
||||
github.com/libp2p/go-libp2p-routing-helpers v0.2.3
|
||||
@@ -119,7 +119,7 @@ require (
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/urfave/cli/v2 v2.2.0
|
||||
github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20200812213548-958ddffe352c
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20200814224545-656e08ce49ee
|
||||
github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7
|
||||
github.com/whyrusleeping/pubsub v0.0.0-20131020042734-02de8aa2db3d
|
||||
github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542
|
||||
|
||||
@@ -240,13 +240,13 @@ github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2 h1:a
|
||||
github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2/go.mod h1:pqTiPHobNkOVM5thSRsHYjyQfq7O5QSCMhvuu9JoDlg=
|
||||
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus=
|
||||
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ=
|
||||
github.com/filecoin-project/go-data-transfer v0.6.1 h1:EA6X8fSiBRNVVwKm5pA7+njZnBbdqpRtedZagrrwHHI=
|
||||
github.com/filecoin-project/go-data-transfer v0.6.1/go.mod h1:uRYBRKVBVM12CSusBtVrzDHkVw/3DKZpkxKJVP1Ydas=
|
||||
github.com/filecoin-project/go-data-transfer v0.6.2 h1:IgbkwcHoyWGglzfsY7P9L1GapzoiLNKuzfZY2bxER8E=
|
||||
github.com/filecoin-project/go-data-transfer v0.6.2/go.mod h1:uRYBRKVBVM12CSusBtVrzDHkVw/3DKZpkxKJVP1Ydas=
|
||||
github.com/filecoin-project/go-fil-commcid v0.0.0-20200208005934-2b8bd03caca5/go.mod h1:JbkIgFF/Z9BDlvrJO1FuKkaWsH673/UdFaiVS6uIHlA=
|
||||
github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f h1:GxJzR3oRIMTPtpZ0b7QF8FKPK6/iPAc7trhlL5k/g+s=
|
||||
github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f/go.mod h1:Eaox7Hvus1JgPrL5+M3+h7aSPHc0cVqpSxA+TxIEpZQ=
|
||||
github.com/filecoin-project/go-fil-markets v0.5.6 h1:WmBbV0qBU4NvLJ64xROpzrKUbkZxZqszZiEiCGmCEIY=
|
||||
github.com/filecoin-project/go-fil-markets v0.5.6/go.mod h1:SJApXAKr5jyGpbzDEOhvemui0pih7hhT8r2MXJxCP1E=
|
||||
github.com/filecoin-project/go-fil-markets v0.5.7 h1:kzyMHqez8ssxchj5s9M1hkC3CTwRGh2MeglJGfUksQU=
|
||||
github.com/filecoin-project/go-fil-markets v0.5.7/go.mod h1:KnvFG3kSQ77vKYSY/QdrXET81wVCBByHXjG7AyxnbUw=
|
||||
github.com/filecoin-project/go-jsonrpc v0.1.2-0.20200817153016-2ea5cbaf5ec0 h1:/GT3V+3f+H5w5odb7LcCWJ1zPw8H8m9TsGQcU0cGSHo=
|
||||
github.com/filecoin-project/go-jsonrpc v0.1.2-0.20200817153016-2ea5cbaf5ec0/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4=
|
||||
github.com/filecoin-project/go-multistore v0.0.3 h1:vaRBY4YiA2UZFPK57RNuewypB8u0DzzQwqsL0XarpnI=
|
||||
@@ -885,8 +885,8 @@ github.com/libp2p/go-libp2p-protocol v0.0.1/go.mod h1:Af9n4PiruirSDjHycM1QuiMi/1
|
||||
github.com/libp2p/go-libp2p-protocol v0.1.0/go.mod h1:KQPHpAabB57XQxGrXCNvbL6UEXfQqUgC/1adR2Xtflk=
|
||||
github.com/libp2p/go-libp2p-pubsub v0.1.1/go.mod h1:ZwlKzRSe1eGvSIdU5bD7+8RZN/Uzw0t1Bp9R1znpR/Q=
|
||||
github.com/libp2p/go-libp2p-pubsub v0.3.2-0.20200527132641-c0712c6e92cf/go.mod h1:TxPOBuo1FPdsTjFnv+FGZbNbWYsp74Culx+4ViQpato=
|
||||
github.com/libp2p/go-libp2p-pubsub v0.3.4 h1:8PollxXtUvzy0DMn5XFMg/JihjaKboWyk3ML6yRW1Lk=
|
||||
github.com/libp2p/go-libp2p-pubsub v0.3.4/go.mod h1:DTMSVmZZfXodB/pvdTGrY2eHPZ9W2ev7hzTH83OKHrI=
|
||||
github.com/libp2p/go-libp2p-pubsub v0.3.5-0.20200821075113-efd56962bced h1:mgcPX7FRZNpIhg2i8B91T4fO0UEpeuhRS0YX5uMAic4=
|
||||
github.com/libp2p/go-libp2p-pubsub v0.3.5-0.20200821075113-efd56962bced/go.mod h1:DTMSVmZZfXodB/pvdTGrY2eHPZ9W2ev7hzTH83OKHrI=
|
||||
github.com/libp2p/go-libp2p-quic-transport v0.1.1/go.mod h1:wqG/jzhF3Pu2NrhJEvE+IE0NTHNXslOPn9JQzyCAxzU=
|
||||
github.com/libp2p/go-libp2p-quic-transport v0.5.0/go.mod h1:IEcuC5MLxvZ5KuHKjRu+dr3LjCT1Be3rcD/4d8JrX8M=
|
||||
github.com/libp2p/go-libp2p-quic-transport v0.7.1 h1:X6Ond9GANspXpgwJlSR9yxcMMD6SLBnGKRtwjBG5awc=
|
||||
@@ -1403,6 +1403,8 @@ github.com/whyrusleeping/cbor-gen v0.0.0-20200810223238-211df3b9e24c h1:BMg3YUwL
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20200810223238-211df3b9e24c/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20200812213548-958ddffe352c h1:otRnI08JoahNBxUFqX3372Ab9GnTj8L5J9iP5ImyxGU=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20200812213548-958ddffe352c/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20200814224545-656e08ce49ee h1:U7zWWvvAjT76EiuWPSOiZlQDnaQYPxPoxugTtTAcJK0=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20200814224545-656e08ce49ee/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ=
|
||||
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E=
|
||||
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8=
|
||||
github.com/whyrusleeping/go-ctrlnet v0.0.0-20180313164037-f564fbbdaa95/go.mod h1:SJqKCCPXRfBFCwXjfNT/skfsceF7+MBFLI2OrvuRA7g=
|
||||
|
||||
+11
-6
@@ -28,14 +28,19 @@ const minFds = 2048
|
||||
// default max file descriptor limit.
|
||||
const maxFds = 16 << 10
|
||||
|
||||
// userMaxFDs returns the value of IPFS_FD_MAX
|
||||
// userMaxFDs returns the value of LOTUS_FD_MAX
|
||||
func userMaxFDs() uint64 {
|
||||
// check if the IPFS_FD_MAX is set up and if it does
|
||||
// check if the LOTUS_FD_MAX is set up and if it does
|
||||
// not have a valid fds number notify the user
|
||||
if val := os.Getenv("IPFS_FD_MAX"); val != "" {
|
||||
val := os.Getenv("LOTUS_FD_MAX")
|
||||
if val == "" {
|
||||
val = os.Getenv("IPFS_FD_MAX")
|
||||
}
|
||||
|
||||
if val != "" {
|
||||
fds, err := strconv.ParseUint(val, 10, 64)
|
||||
if err != nil {
|
||||
log.Errorf("bad value for IPFS_FD_MAX: %s", err)
|
||||
log.Errorf("bad value for LOTUS_FD_MAX: %s", err)
|
||||
return 0
|
||||
}
|
||||
return fds
|
||||
@@ -44,7 +49,7 @@ func userMaxFDs() uint64 {
|
||||
}
|
||||
|
||||
// ManageFdLimit raise the current max file descriptor count
|
||||
// of the process based on the IPFS_FD_MAX value
|
||||
// of the process based on the LOTUS_FD_MAX value
|
||||
func ManageFdLimit() (changed bool, newLimit uint64, err error) {
|
||||
if !supportsFDManagement {
|
||||
return false, 0, nil
|
||||
@@ -93,7 +98,7 @@ func ManageFdLimit() (changed bool, newLimit uint64, err error) {
|
||||
|
||||
if newLimit < userLimit {
|
||||
err = fmt.Errorf(
|
||||
"failed to raise ulimit to IPFS_FD_MAX (%d): set to %d",
|
||||
"failed to raise ulimit to LOTUS_FD_MAX (%d): set to %d",
|
||||
userLimit,
|
||||
newLimit,
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ func TestManageInvalidNFds(t *testing.T) {
|
||||
t.Errorf("ManageFdLimit should return an error: changed %t, new: %d", changed, new)
|
||||
} else if err != nil {
|
||||
flag := strings.Contains(err.Error(),
|
||||
"failed to raise ulimit to IPFS_FD_MAX")
|
||||
"failed to raise ulimit to LOTUS_FD_MAX")
|
||||
if !flag {
|
||||
t.Error("ManageFdLimit returned unexpected error", err)
|
||||
}
|
||||
|
||||
@@ -44,6 +44,9 @@ var (
|
||||
PubsubDeliverMessage = stats.Int64("pubsub/delivered", "Counter for total delivered messages", stats.UnitDimensionless)
|
||||
PubsubRejectMessage = stats.Int64("pubsub/rejected", "Counter for total rejected messages", stats.UnitDimensionless)
|
||||
PubsubDuplicateMessage = stats.Int64("pubsub/duplicate", "Counter for total duplicate messages", stats.UnitDimensionless)
|
||||
PubsubRecvRPC = stats.Int64("pubsub/recv_rpc", "Counter for total received RPCs", stats.UnitDimensionless)
|
||||
PubsubSendRPC = stats.Int64("pubsub/send_rpc", "Counter for total sent RPCs", stats.UnitDimensionless)
|
||||
PubsubDropRPC = stats.Int64("pubsub/drop_rpc", "Counter for total dropped RPCs", stats.UnitDimensionless)
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
+1
-1
@@ -174,7 +174,7 @@ func libp2p() Option {
|
||||
Override(AddrsFactoryKey, lp2p.AddrsFactory(nil, nil)),
|
||||
Override(SmuxTransportKey, lp2p.SmuxTransport(true)),
|
||||
Override(RelayKey, lp2p.NoRelay()),
|
||||
Override(SecurityKey, lp2p.Security(true, true)),
|
||||
Override(SecurityKey, lp2p.Security(true, false)),
|
||||
|
||||
Override(BaseRoutingKey, lp2p.BaseRouting),
|
||||
Override(new(routing.Routing), lp2p.Routing),
|
||||
|
||||
@@ -2,7 +2,6 @@ package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -197,6 +196,7 @@ func (a *API) ClientListDeals(ctx context.Context) ([]api.DealInfo, error) {
|
||||
PricePerEpoch: v.Proposal.StoragePricePerEpoch,
|
||||
Duration: uint64(v.Proposal.Duration()),
|
||||
DealID: v.DealID,
|
||||
CreationTime: v.CreationTime.Time(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,6 +219,7 @@ func (a *API) ClientGetDealInfo(ctx context.Context, d cid.Cid) (*api.DealInfo,
|
||||
PricePerEpoch: v.Proposal.StoragePricePerEpoch,
|
||||
Duration: uint64(v.Proposal.Duration()),
|
||||
DealID: v.DealID,
|
||||
CreationTime: v.CreationTime.Time(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -773,7 +774,7 @@ func (a *API) ClientListDataTransfers(ctx context.Context) ([]api.DataTransferCh
|
||||
|
||||
apiChannels := make([]api.DataTransferChannel, 0, len(inProgressChannels))
|
||||
for _, channelState := range inProgressChannels {
|
||||
apiChannels = append(apiChannels, toAPIChannel(a.Host.ID(), channelState))
|
||||
apiChannels = append(apiChannels, api.NewDataTransferChannel(a.Host.ID(), channelState))
|
||||
}
|
||||
|
||||
return apiChannels, nil
|
||||
@@ -783,7 +784,7 @@ func (a *API) ClientDataTransferUpdates(ctx context.Context) (<-chan api.DataTra
|
||||
channels := make(chan api.DataTransferChannel)
|
||||
|
||||
unsub := a.DataTransfer.SubscribeToEvents(func(evt datatransfer.Event, channelState datatransfer.ChannelState) {
|
||||
channel := toAPIChannel(a.Host.ID(), channelState)
|
||||
channel := api.NewDataTransferChannel(a.Host.ID(), channelState)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case channels <- channel:
|
||||
@@ -797,34 +798,3 @@ func (a *API) ClientDataTransferUpdates(ctx context.Context) (<-chan api.DataTra
|
||||
|
||||
return channels, nil
|
||||
}
|
||||
|
||||
func toAPIChannel(hostID peer.ID, channelState datatransfer.ChannelState) api.DataTransferChannel {
|
||||
channel := api.DataTransferChannel{
|
||||
TransferID: channelState.TransferID(),
|
||||
Status: channelState.Status(),
|
||||
BaseCID: channelState.BaseCID(),
|
||||
IsSender: channelState.Sender() == hostID,
|
||||
Message: channelState.Message(),
|
||||
}
|
||||
stringer, ok := channelState.Voucher().(fmt.Stringer)
|
||||
if ok {
|
||||
channel.Voucher = stringer.String()
|
||||
} else {
|
||||
voucherJSON, err := json.Marshal(channelState.Voucher())
|
||||
if err != nil {
|
||||
channel.Voucher = fmt.Errorf("Voucher Serialization: %w", err).Error()
|
||||
} else {
|
||||
channel.Voucher = string(voucherJSON)
|
||||
}
|
||||
}
|
||||
if channel.IsSender {
|
||||
channel.IsInitiator = !channelState.IsPull()
|
||||
channel.Transferred = channelState.Sent()
|
||||
channel.OtherPeer = channelState.Recipient()
|
||||
} else {
|
||||
channel.IsInitiator = channelState.IsPull()
|
||||
channel.Transferred = channelState.Received()
|
||||
channel.OtherPeer = channelState.Sender()
|
||||
}
|
||||
return channel
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"math/rand"
|
||||
"sort"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/messagepool"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
@@ -176,3 +177,33 @@ func (a *GasAPI) GasEstimateGasLimit(ctx context.Context, msgIn *types.Message,
|
||||
|
||||
return res.MsgRct.GasUsed, nil
|
||||
}
|
||||
|
||||
func (a *GasAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, _ types.TipSetKey) (*types.Message, error) {
|
||||
if msg.GasLimit == 0 {
|
||||
gasLimit, err := a.GasEstimateGasLimit(ctx, msg, types.TipSetKey{})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("estimating gas used: %w", err)
|
||||
}
|
||||
msg.GasLimit = int64(float64(gasLimit) * a.Mpool.GetConfig().GasLimitOverestimation)
|
||||
}
|
||||
|
||||
if msg.GasPremium == types.EmptyInt || types.BigCmp(msg.GasPremium, types.NewInt(0)) == 0 {
|
||||
gasPremium, err := a.GasEstimateGasPremium(ctx, 2, msg.From, msg.GasLimit, types.TipSetKey{})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("estimating gas price: %w", err)
|
||||
}
|
||||
msg.GasPremium = gasPremium
|
||||
}
|
||||
|
||||
if msg.GasFeeCap == types.EmptyInt || types.BigCmp(msg.GasFeeCap, types.NewInt(0)) == 0 {
|
||||
feeCap, err := a.GasEstimateFeeCap(ctx, msg, 10, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("estimating fee cap: %w", err)
|
||||
}
|
||||
msg.GasFeeCap = big.Add(feeCap, msg.GasPremium)
|
||||
}
|
||||
|
||||
capGasFee(msg, spec.Get().MaxFee)
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
+8
-24
@@ -105,6 +105,11 @@ func (a *MpoolAPI) MpoolPending(ctx context.Context, tsk types.TipSetKey) ([]*ty
|
||||
}
|
||||
}
|
||||
|
||||
func (a *MpoolAPI) MpoolClear(ctx context.Context, local bool) error {
|
||||
a.Mpool.Clear(local)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *MpoolAPI) MpoolPush(ctx context.Context, smsg *types.SignedMessage) (cid.Cid, error) {
|
||||
return a.Mpool.Push(smsg)
|
||||
}
|
||||
@@ -146,32 +151,12 @@ func (a *MpoolAPI) MpoolPushMessage(ctx context.Context, msg *types.Message, spe
|
||||
if msg.Nonce != 0 {
|
||||
return nil, xerrors.Errorf("MpoolPushMessage expects message nonce to be 0, was %d", msg.Nonce)
|
||||
}
|
||||
if msg.GasLimit == 0 {
|
||||
gasLimit, err := a.GasEstimateGasLimit(ctx, msg, types.TipSetKey{})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("estimating gas used: %w", err)
|
||||
}
|
||||
msg.GasLimit = int64(float64(gasLimit) * a.Mpool.GetConfig().GasLimitOverestimation)
|
||||
}
|
||||
|
||||
if msg.GasPremium == types.EmptyInt || types.BigCmp(msg.GasPremium, types.NewInt(0)) == 0 {
|
||||
gasPremium, err := a.GasEstimateGasPremium(ctx, 2, msg.From, msg.GasLimit, types.TipSetKey{})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("estimating gas price: %w", err)
|
||||
}
|
||||
msg.GasPremium = gasPremium
|
||||
msg, err := a.GasAPI.GasEstimateMessageGas(ctx, msg, spec, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("GasEstimateMessageGas error: %w", err)
|
||||
}
|
||||
|
||||
if msg.GasFeeCap == types.EmptyInt || types.BigCmp(msg.GasFeeCap, types.NewInt(0)) == 0 {
|
||||
feeCap, err := a.GasEstimateFeeCap(ctx, msg, 10, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("estimating fee cap: %w", err)
|
||||
}
|
||||
msg.GasFeeCap = big.Add(feeCap, msg.GasPremium)
|
||||
}
|
||||
|
||||
capGasFee(msg, spec.Get().MaxFee)
|
||||
|
||||
sign := func(from address.Address, nonce uint64) (*types.SignedMessage, error) {
|
||||
msg.Nonce = nonce
|
||||
if msg.From.Protocol() == address.ID {
|
||||
@@ -192,7 +177,6 @@ func (a *MpoolAPI) MpoolPushMessage(ctx context.Context, msg *types.Message, spe
|
||||
}
|
||||
|
||||
var m *types.SignedMessage
|
||||
var err error
|
||||
again:
|
||||
m, err = a.Mpool.PushWithNonce(ctx, msg.From, sign)
|
||||
if err == messagepool.ErrTryAgain {
|
||||
|
||||
@@ -8,8 +8,10 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
datatransfer "github.com/filecoin-project/go-data-transfer"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/libp2p/go-libp2p-core/host"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
@@ -51,6 +53,8 @@ type StorageMinerAPI struct {
|
||||
StorageMgr *sectorstorage.Manager `optional:"true"`
|
||||
IStorageMgr sectorstorage.SectorManager
|
||||
*stores.Index
|
||||
DataTransfer dtypes.ProviderDataTransfer
|
||||
Host host.Host
|
||||
|
||||
ConsiderOnlineStorageDealsConfigFunc dtypes.ConsiderOnlineStorageDealsConfigFunc
|
||||
SetConsiderOnlineStorageDealsConfigFunc dtypes.SetConsiderOnlineStorageDealsConfigFunc
|
||||
@@ -354,6 +358,39 @@ func (sm *StorageMinerAPI) MarketGetRetrievalAsk(ctx context.Context) (*retrieva
|
||||
return sm.RetrievalProvider.GetAsk(), nil
|
||||
}
|
||||
|
||||
func (sm *StorageMinerAPI) MarketListDataTransfers(ctx context.Context) ([]api.DataTransferChannel, error) {
|
||||
inProgressChannels, err := sm.DataTransfer.InProgressChannels(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
apiChannels := make([]api.DataTransferChannel, 0, len(inProgressChannels))
|
||||
for _, channelState := range inProgressChannels {
|
||||
apiChannels = append(apiChannels, api.NewDataTransferChannel(sm.Host.ID(), channelState))
|
||||
}
|
||||
|
||||
return apiChannels, nil
|
||||
}
|
||||
|
||||
func (sm *StorageMinerAPI) MarketDataTransferUpdates(ctx context.Context) (<-chan api.DataTransferChannel, error) {
|
||||
channels := make(chan api.DataTransferChannel)
|
||||
|
||||
unsub := sm.DataTransfer.SubscribeToEvents(func(evt datatransfer.Event, channelState datatransfer.ChannelState) {
|
||||
channel := api.NewDataTransferChannel(sm.Host.ID(), channelState)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case channels <- channel:
|
||||
}
|
||||
})
|
||||
|
||||
go func() {
|
||||
defer unsub()
|
||||
<-ctx.Done()
|
||||
}()
|
||||
|
||||
return channels, nil
|
||||
}
|
||||
|
||||
func (sm *StorageMinerAPI) DealsList(ctx context.Context) ([]storagemarket.StorageDeal, error) {
|
||||
return sm.StorageProvider.ListDeals(ctx)
|
||||
}
|
||||
|
||||
@@ -37,8 +37,9 @@ func RecordValidator(ps peerstore.Peerstore) record.Validator {
|
||||
}
|
||||
|
||||
const JWTSecretName = "auth-jwt-private" //nolint:gosec
|
||||
const KTJwtHmacSecret = "jwt-hmac-secret"
|
||||
|
||||
type jwtPayload struct {
|
||||
type JwtPayload struct {
|
||||
Allow []auth.Permission
|
||||
}
|
||||
|
||||
@@ -54,7 +55,7 @@ func APISecret(keystore types.KeyStore, lr repo.LockedRepo) (*dtypes.APIAlg, err
|
||||
}
|
||||
|
||||
key = types.KeyInfo{
|
||||
Type: "jwt-hmac-secret",
|
||||
Type: KTJwtHmacSecret,
|
||||
PrivateKey: sk,
|
||||
}
|
||||
|
||||
@@ -63,7 +64,7 @@ func APISecret(keystore types.KeyStore, lr repo.LockedRepo) (*dtypes.APIAlg, err
|
||||
}
|
||||
|
||||
// TODO: make this configurable
|
||||
p := jwtPayload{
|
||||
p := JwtPayload{
|
||||
Allow: apistruct.AllPermissions,
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ func init() {
|
||||
pubsub.GossipSubDhi = 12
|
||||
pubsub.GossipSubDlazy = 12
|
||||
pubsub.GossipSubDirectConnectInitialDelay = 30 * time.Second
|
||||
pubsub.GossipSubIWantFollowupTime = 5 * time.Second
|
||||
pubsub.GossipSubHistoryLength = 10
|
||||
}
|
||||
func ScoreKeeper() *dtypes.ScoreKeeper {
|
||||
return new(dtypes.ScoreKeeper)
|
||||
@@ -110,8 +112,9 @@ func GossipSub(in GossipIn) (service *pubsub.PubSub, err error) {
|
||||
// IPColocationFactorWhitelist: map[string]struct{}{},
|
||||
|
||||
// P7: behavioural penalties, decay after 1hr
|
||||
BehaviourPenaltyWeight: -10,
|
||||
BehaviourPenaltyDecay: pubsub.ScoreParameterDecay(time.Hour),
|
||||
BehaviourPenaltyThreshold: 6,
|
||||
BehaviourPenaltyWeight: -10,
|
||||
BehaviourPenaltyDecay: pubsub.ScoreParameterDecay(time.Hour),
|
||||
|
||||
DecayInterval: pubsub.DefaultDecayInterval,
|
||||
DecayToZero: pubsub.DefaultDecayToZero,
|
||||
@@ -371,5 +374,11 @@ func (trw *tracerWrapper) Trace(evt *pubsub_pb.TraceEvent) {
|
||||
if trw.tr != nil {
|
||||
trw.tr.Trace(evt)
|
||||
}
|
||||
case pubsub_pb.TraceEvent_RECV_RPC:
|
||||
stats.Record(context.TODO(), metrics.PubsubRecvRPC.M(1))
|
||||
case pubsub_pb.TraceEvent_SEND_RPC:
|
||||
stats.Record(context.TODO(), metrics.PubsubSendRPC.M(1))
|
||||
case pubsub_pb.TraceEvent_DROP_RPC:
|
||||
stats.Record(context.TODO(), metrics.PubsubDropRPC.M(1))
|
||||
}
|
||||
}
|
||||
|
||||
+19
-478
@@ -1,55 +1,21 @@
|
||||
package node_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"io/ioutil"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
builder "github.com/filecoin-project/lotus/node/test"
|
||||
|
||||
"github.com/filecoin-project/lotus/lib/lotuslog"
|
||||
"github.com/filecoin-project/lotus/storage/mockstorage"
|
||||
|
||||
"github.com/filecoin-project/go-storedcounter"
|
||||
"github.com/ipfs/go-datastore"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/libp2p/go-libp2p-core/crypto"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
saminer "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/api/client"
|
||||
"github.com/filecoin-project/lotus/api/test"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
genesis2 "github.com/filecoin-project/lotus/chain/gen/genesis"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
"github.com/filecoin-project/lotus/cmd/lotus-seed/seed"
|
||||
sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage"
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/mock"
|
||||
"github.com/filecoin-project/lotus/genesis"
|
||||
"github.com/filecoin-project/lotus/miner"
|
||||
"github.com/filecoin-project/lotus/node"
|
||||
"github.com/filecoin-project/lotus/node/modules"
|
||||
modtest "github.com/filecoin-project/lotus/node/modules/testing"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -62,437 +28,12 @@ func init() {
|
||||
verifreg.MinVerifiedDealSize = big.NewInt(256)
|
||||
}
|
||||
|
||||
func testStorageNode(ctx context.Context, t *testing.T, waddr address.Address, act address.Address, pk crypto.PrivKey, tnd test.TestNode, mn mocknet.Mocknet, opts node.Option) test.TestStorageNode {
|
||||
r := repo.NewMemory(nil)
|
||||
|
||||
lr, err := r.Lock(repo.StorageMiner)
|
||||
require.NoError(t, err)
|
||||
|
||||
ks, err := lr.KeyStore()
|
||||
require.NoError(t, err)
|
||||
|
||||
kbytes, err := pk.Bytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ks.Put("libp2p-host", types.KeyInfo{
|
||||
Type: "libp2p-host",
|
||||
PrivateKey: kbytes,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
ds, err := lr.Datastore("/metadata")
|
||||
require.NoError(t, err)
|
||||
err = ds.Put(datastore.NewKey("miner-address"), act.Bytes())
|
||||
require.NoError(t, err)
|
||||
|
||||
nic := storedcounter.New(ds, datastore.NewKey(modules.StorageCounterDSPrefix))
|
||||
for i := 0; i < test.GenesisPreseals; i++ {
|
||||
_, err := nic.Next()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
_, err = nic.Next()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = lr.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
peerid, err := peer.IDFromPrivateKey(pk)
|
||||
require.NoError(t, err)
|
||||
|
||||
enc, err := actors.SerializeParams(&saminer.ChangePeerIDParams{NewID: abi.PeerID(peerid)})
|
||||
require.NoError(t, err)
|
||||
|
||||
msg := &types.Message{
|
||||
To: act,
|
||||
From: waddr,
|
||||
Method: builtin.MethodsMiner.ChangePeerID,
|
||||
Params: enc,
|
||||
Value: types.NewInt(0),
|
||||
}
|
||||
|
||||
_, err = tnd.MpoolPushMessage(ctx, msg, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// start node
|
||||
var minerapi api.StorageMiner
|
||||
|
||||
mineBlock := make(chan miner.MineReq)
|
||||
// TODO: use stop
|
||||
_, err = node.New(ctx,
|
||||
node.StorageMiner(&minerapi),
|
||||
node.Online(),
|
||||
node.Repo(r),
|
||||
node.Test(),
|
||||
|
||||
node.MockHost(mn),
|
||||
|
||||
node.Override(new(api.FullNode), tnd),
|
||||
node.Override(new(*miner.Miner), miner.NewTestMiner(mineBlock, act)),
|
||||
|
||||
opts,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to construct node: %v", err)
|
||||
}
|
||||
|
||||
/*// Bootstrap with full node
|
||||
remoteAddrs, err := tnd.NetAddrsListen(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = minerapi.NetConnect(ctx, remoteAddrs)
|
||||
require.NoError(t, err)*/
|
||||
mineOne := func(ctx context.Context, req miner.MineReq) error {
|
||||
select {
|
||||
case mineBlock <- req:
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
return test.TestStorageNode{StorageMiner: minerapi, MineOne: mineOne}
|
||||
}
|
||||
|
||||
func builder(t *testing.T, nFull int, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) {
|
||||
ctx := context.Background()
|
||||
mn := mocknet.New(ctx)
|
||||
|
||||
fulls := make([]test.TestNode, nFull)
|
||||
storers := make([]test.TestStorageNode, len(storage))
|
||||
|
||||
pk, _, err := crypto.GenerateEd25519Key(rand.Reader)
|
||||
require.NoError(t, err)
|
||||
|
||||
minerPid, err := peer.IDFromPrivateKey(pk)
|
||||
require.NoError(t, err)
|
||||
|
||||
var genbuf bytes.Buffer
|
||||
|
||||
if len(storage) > 1 {
|
||||
panic("need more peer IDs")
|
||||
}
|
||||
// PRESEAL SECTION, TRY TO REPLACE WITH BETTER IN THE FUTURE
|
||||
// TODO: would be great if there was a better way to fake the preseals
|
||||
|
||||
var genms []genesis.Miner
|
||||
var maddrs []address.Address
|
||||
var genaccs []genesis.Actor
|
||||
var keys []*wallet.Key
|
||||
|
||||
var presealDirs []string
|
||||
for i := 0; i < len(storage); i++ {
|
||||
maddr, err := address.NewIDAddress(genesis2.MinerStart + uint64(i))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tdir, err := ioutil.TempDir("", "preseal-memgen")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
genm, k, err := seed.PreSeal(maddr, abi.RegisteredSealProof_StackedDrg2KiBV1, 0, test.GenesisPreseals, tdir, []byte("make genesis mem random"), nil, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
genm.PeerId = minerPid
|
||||
|
||||
wk, err := wallet.NewKey(*k)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
genaccs = append(genaccs, genesis.Actor{
|
||||
Type: genesis.TAccount,
|
||||
Balance: big.Mul(big.NewInt(400_000_000), types.NewInt(build.FilecoinPrecision)),
|
||||
Meta: (&genesis.AccountMeta{Owner: wk.Address}).ActorMeta(),
|
||||
})
|
||||
|
||||
keys = append(keys, wk)
|
||||
presealDirs = append(presealDirs, tdir)
|
||||
maddrs = append(maddrs, maddr)
|
||||
genms = append(genms, *genm)
|
||||
}
|
||||
templ := &genesis.Template{
|
||||
Accounts: genaccs,
|
||||
Miners: genms,
|
||||
Timestamp: uint64(time.Now().Unix() - 10000), // some time sufficiently far in the past
|
||||
VerifregRootKey: gen.DefaultVerifregRootkeyActor,
|
||||
RemainderAccount: gen.DefaultRemainderAccountActor,
|
||||
}
|
||||
|
||||
// END PRESEAL SECTION
|
||||
|
||||
for i := 0; i < nFull; i++ {
|
||||
var genesis node.Option
|
||||
if i == 0 {
|
||||
genesis = node.Override(new(modules.Genesis), modtest.MakeGenesisMem(&genbuf, *templ))
|
||||
} else {
|
||||
genesis = node.Override(new(modules.Genesis), modules.LoadGenesis(genbuf.Bytes()))
|
||||
}
|
||||
|
||||
var err error
|
||||
// TODO: Don't ignore stop
|
||||
_, err = node.New(ctx,
|
||||
node.FullAPI(&fulls[i].FullNode),
|
||||
node.Online(),
|
||||
node.Repo(repo.NewMemory(nil)),
|
||||
node.MockHost(mn),
|
||||
node.Test(),
|
||||
|
||||
genesis,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for i, def := range storage {
|
||||
// TODO: support non-bootstrap miners
|
||||
if i != 0 {
|
||||
t.Fatal("only one storage node supported")
|
||||
}
|
||||
if def.Full != 0 {
|
||||
t.Fatal("storage nodes only supported on the first full node")
|
||||
}
|
||||
|
||||
f := fulls[def.Full]
|
||||
if _, err := f.FullNode.WalletImport(ctx, &keys[i].KeyInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.FullNode.WalletSetDefault(ctx, keys[i].Address); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
genMiner := maddrs[i]
|
||||
wa := genms[i].Worker
|
||||
|
||||
storers[i] = testStorageNode(ctx, t, wa, genMiner, pk, f, mn, node.Options())
|
||||
if err := storers[i].StorageAddLocal(ctx, presealDirs[i]); err != nil {
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
/*
|
||||
sma := storers[i].StorageMiner.(*impl.StorageMinerAPI)
|
||||
|
||||
psd := presealDirs[i]
|
||||
*/
|
||||
}
|
||||
|
||||
if err := mn.LinkAll(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(storers) > 0 {
|
||||
// Mine 2 blocks to setup some CE stuff in some actors
|
||||
var wait sync.Mutex
|
||||
wait.Lock()
|
||||
|
||||
storers[0].MineOne(ctx, miner.MineReq{Done: func(bool, error) {
|
||||
wait.Unlock()
|
||||
}})
|
||||
wait.Lock()
|
||||
storers[0].MineOne(ctx, miner.MineReq{Done: func(bool, error) {
|
||||
wait.Unlock()
|
||||
}})
|
||||
wait.Lock()
|
||||
}
|
||||
|
||||
return fulls, storers
|
||||
}
|
||||
|
||||
func mockSbBuilder(t *testing.T, nFull int, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) {
|
||||
ctx := context.Background()
|
||||
mn := mocknet.New(ctx)
|
||||
|
||||
fulls := make([]test.TestNode, nFull)
|
||||
storers := make([]test.TestStorageNode, len(storage))
|
||||
|
||||
var genbuf bytes.Buffer
|
||||
|
||||
// PRESEAL SECTION, TRY TO REPLACE WITH BETTER IN THE FUTURE
|
||||
// TODO: would be great if there was a better way to fake the preseals
|
||||
|
||||
var genms []genesis.Miner
|
||||
var genaccs []genesis.Actor
|
||||
var maddrs []address.Address
|
||||
var presealDirs []string
|
||||
var keys []*wallet.Key
|
||||
var pidKeys []crypto.PrivKey
|
||||
for i := 0; i < len(storage); i++ {
|
||||
maddr, err := address.NewIDAddress(genesis2.MinerStart + uint64(i))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tdir, err := ioutil.TempDir("", "preseal-memgen")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
preseals := storage[i].Preseal
|
||||
if preseals == test.PresealGenesis {
|
||||
preseals = test.GenesisPreseals
|
||||
}
|
||||
|
||||
genm, k, err := mockstorage.PreSeal(2048, maddr, preseals)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pk, _, err := crypto.GenerateEd25519Key(rand.Reader)
|
||||
require.NoError(t, err)
|
||||
|
||||
minerPid, err := peer.IDFromPrivateKey(pk)
|
||||
require.NoError(t, err)
|
||||
|
||||
genm.PeerId = minerPid
|
||||
|
||||
wk, err := wallet.NewKey(*k)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
genaccs = append(genaccs, genesis.Actor{
|
||||
Type: genesis.TAccount,
|
||||
Balance: big.Mul(big.NewInt(400_000_000), types.NewInt(build.FilecoinPrecision)),
|
||||
Meta: (&genesis.AccountMeta{Owner: wk.Address}).ActorMeta(),
|
||||
})
|
||||
|
||||
keys = append(keys, wk)
|
||||
pidKeys = append(pidKeys, pk)
|
||||
presealDirs = append(presealDirs, tdir)
|
||||
maddrs = append(maddrs, maddr)
|
||||
genms = append(genms, *genm)
|
||||
}
|
||||
templ := &genesis.Template{
|
||||
Accounts: genaccs,
|
||||
Miners: genms,
|
||||
Timestamp: uint64(time.Now().Unix()) - (build.BlockDelaySecs * 20000),
|
||||
VerifregRootKey: gen.DefaultVerifregRootkeyActor,
|
||||
RemainderAccount: gen.DefaultRemainderAccountActor,
|
||||
}
|
||||
|
||||
// END PRESEAL SECTION
|
||||
|
||||
for i := 0; i < nFull; i++ {
|
||||
var genesis node.Option
|
||||
if i == 0 {
|
||||
genesis = node.Override(new(modules.Genesis), modtest.MakeGenesisMem(&genbuf, *templ))
|
||||
} else {
|
||||
genesis = node.Override(new(modules.Genesis), modules.LoadGenesis(genbuf.Bytes()))
|
||||
}
|
||||
|
||||
var err error
|
||||
// TODO: Don't ignore stop
|
||||
_, err = node.New(ctx,
|
||||
node.FullAPI(&fulls[i].FullNode),
|
||||
node.Online(),
|
||||
node.Repo(repo.NewMemory(nil)),
|
||||
node.MockHost(mn),
|
||||
node.Test(),
|
||||
|
||||
node.Override(new(ffiwrapper.Verifier), mock.MockVerifier),
|
||||
|
||||
genesis,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for i, def := range storage {
|
||||
// TODO: support non-bootstrap miners
|
||||
|
||||
minerID := abi.ActorID(genesis2.MinerStart + uint64(i))
|
||||
|
||||
if def.Full != 0 {
|
||||
t.Fatal("storage nodes only supported on the first full node")
|
||||
}
|
||||
|
||||
f := fulls[def.Full]
|
||||
if _, err := f.FullNode.WalletImport(ctx, &keys[i].KeyInfo); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
if err := f.FullNode.WalletSetDefault(ctx, keys[i].Address); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sectors := make([]abi.SectorID, len(genms[i].Sectors))
|
||||
for i, sector := range genms[i].Sectors {
|
||||
sectors[i] = abi.SectorID{
|
||||
Miner: minerID,
|
||||
Number: sector.SectorID,
|
||||
}
|
||||
}
|
||||
|
||||
storers[i] = testStorageNode(ctx, t, genms[i].Worker, maddrs[i], pidKeys[i], f, mn, node.Options(
|
||||
node.Override(new(sectorstorage.SectorManager), func() (sectorstorage.SectorManager, error) {
|
||||
return mock.NewMockSectorMgr(build.DefaultSectorSize(), sectors), nil
|
||||
}),
|
||||
node.Override(new(ffiwrapper.Verifier), mock.MockVerifier),
|
||||
node.Unset(new(*sectorstorage.Manager)),
|
||||
))
|
||||
}
|
||||
|
||||
if err := mn.LinkAll(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(storers) > 0 {
|
||||
// Mine 2 blocks to setup some CE stuff in some actors
|
||||
var wait sync.Mutex
|
||||
wait.Lock()
|
||||
|
||||
storers[0].MineOne(ctx, miner.MineReq{Done: func(bool, error) {
|
||||
wait.Unlock()
|
||||
}})
|
||||
wait.Lock()
|
||||
storers[0].MineOne(ctx, miner.MineReq{Done: func(bool, error) {
|
||||
wait.Unlock()
|
||||
}})
|
||||
wait.Lock()
|
||||
}
|
||||
|
||||
return fulls, storers
|
||||
}
|
||||
|
||||
func TestAPI(t *testing.T) {
|
||||
test.TestApis(t, builder)
|
||||
}
|
||||
|
||||
func rpcBuilder(t *testing.T, nFull int, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) {
|
||||
fullApis, storaApis := builder(t, nFull, storage)
|
||||
fulls := make([]test.TestNode, nFull)
|
||||
storers := make([]test.TestStorageNode, len(storage))
|
||||
|
||||
for i, a := range fullApis {
|
||||
rpcServer := jsonrpc.NewServer()
|
||||
rpcServer.Register("Filecoin", a)
|
||||
testServ := httptest.NewServer(rpcServer) // todo: close
|
||||
|
||||
var err error
|
||||
fulls[i].FullNode, _, err = client.NewFullNodeRPC("ws://"+testServ.Listener.Addr().String(), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
for i, a := range storaApis {
|
||||
rpcServer := jsonrpc.NewServer()
|
||||
rpcServer.Register("Filecoin", a)
|
||||
testServ := httptest.NewServer(rpcServer) // todo: close
|
||||
|
||||
var err error
|
||||
storers[i].StorageMiner, _, err = client.NewStorageMinerRPC("ws://"+testServ.Listener.Addr().String(), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
storers[i].MineOne = a.MineOne
|
||||
}
|
||||
|
||||
return fulls, storers
|
||||
test.TestApis(t, builder.Builder)
|
||||
}
|
||||
|
||||
func TestAPIRPC(t *testing.T) {
|
||||
test.TestApis(t, rpcBuilder)
|
||||
test.TestApis(t, builder.RPCBuilder)
|
||||
}
|
||||
|
||||
func TestAPIDealFlow(t *testing.T) {
|
||||
@@ -503,16 +44,16 @@ func TestAPIDealFlow(t *testing.T) {
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
t.Run("TestDealFlow", func(t *testing.T) {
|
||||
test.TestDealFlow(t, mockSbBuilder, 10*time.Millisecond, false, false)
|
||||
test.TestDealFlow(t, builder.MockSbBuilder, 10*time.Millisecond, false, false)
|
||||
})
|
||||
t.Run("WithExportedCAR", func(t *testing.T) {
|
||||
test.TestDealFlow(t, mockSbBuilder, 10*time.Millisecond, true, false)
|
||||
test.TestDealFlow(t, builder.MockSbBuilder, 10*time.Millisecond, true, false)
|
||||
})
|
||||
t.Run("TestDoubleDealFlow", func(t *testing.T) {
|
||||
test.TestDoubleDealFlow(t, mockSbBuilder, 10*time.Millisecond)
|
||||
test.TestDoubleDealFlow(t, builder.MockSbBuilder, 10*time.Millisecond)
|
||||
})
|
||||
t.Run("TestFastRetrievalDealFlow", func(t *testing.T) {
|
||||
test.TestFastRetrievalDealFlow(t, mockSbBuilder, 10*time.Millisecond)
|
||||
test.TestFastRetrievalDealFlow(t, builder.MockSbBuilder, 10*time.Millisecond)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -530,15 +71,15 @@ func TestAPIDealFlowReal(t *testing.T) {
|
||||
saminer.PreCommitChallengeDelay = 5
|
||||
|
||||
t.Run("basic", func(t *testing.T) {
|
||||
test.TestDealFlow(t, builder, time.Second, false, false)
|
||||
test.TestDealFlow(t, builder.Builder, time.Second, false, false)
|
||||
})
|
||||
|
||||
t.Run("fast-retrieval", func(t *testing.T) {
|
||||
test.TestDealFlow(t, builder, time.Second, false, true)
|
||||
test.TestDealFlow(t, builder.Builder, time.Second, false, true)
|
||||
})
|
||||
|
||||
t.Run("retrieval-second", func(t *testing.T) {
|
||||
test.TestSenondDealRetrieval(t, builder, time.Second)
|
||||
test.TestSenondDealRetrieval(t, builder.Builder, time.Second)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -549,7 +90,7 @@ func TestDealMining(t *testing.T) {
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
test.TestDealMining(t, mockSbBuilder, 50*time.Millisecond, false)
|
||||
test.TestDealMining(t, builder.MockSbBuilder, 50*time.Millisecond, false)
|
||||
}
|
||||
|
||||
func TestPledgeSectors(t *testing.T) {
|
||||
@@ -560,11 +101,11 @@ func TestPledgeSectors(t *testing.T) {
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
t.Run("1", func(t *testing.T) {
|
||||
test.TestPledgeSector(t, mockSbBuilder, 50*time.Millisecond, 1)
|
||||
test.TestPledgeSector(t, builder.MockSbBuilder, 50*time.Millisecond, 1)
|
||||
})
|
||||
|
||||
t.Run("100", func(t *testing.T) {
|
||||
test.TestPledgeSector(t, mockSbBuilder, 50*time.Millisecond, 100)
|
||||
test.TestPledgeSector(t, builder.MockSbBuilder, 50*time.Millisecond, 100)
|
||||
})
|
||||
|
||||
t.Run("1000", func(t *testing.T) {
|
||||
@@ -572,7 +113,7 @@ func TestPledgeSectors(t *testing.T) {
|
||||
t.Skip("skipping test in short mode")
|
||||
}
|
||||
|
||||
test.TestPledgeSector(t, mockSbBuilder, 50*time.Millisecond, 1000)
|
||||
test.TestPledgeSector(t, builder.MockSbBuilder, 50*time.Millisecond, 1000)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -587,7 +128,7 @@ func TestWindowedPost(t *testing.T) {
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
test.TestWindowPost(t, mockSbBuilder, 2*time.Millisecond, 10)
|
||||
test.TestWindowPost(t, builder.MockSbBuilder, 2*time.Millisecond, 10)
|
||||
}
|
||||
|
||||
func TestCCUpgrade(t *testing.T) {
|
||||
@@ -597,7 +138,7 @@ func TestCCUpgrade(t *testing.T) {
|
||||
logging.SetLogLevel("sub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
test.TestCCUpgrade(t, mockSbBuilder, 5*time.Millisecond)
|
||||
test.TestCCUpgrade(t, builder.MockSbBuilder, 5*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestPaymentChannels(t *testing.T) {
|
||||
@@ -608,5 +149,5 @@ func TestPaymentChannels(t *testing.T) {
|
||||
logging.SetLogLevel("pubsub", "ERROR")
|
||||
logging.SetLogLevel("storageminer", "ERROR")
|
||||
|
||||
test.TestPaymentChannels(t, mockSbBuilder, 5*time.Millisecond)
|
||||
test.TestPaymentChannels(t, builder.MockSbBuilder, 5*time.Millisecond)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,500 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http/httptest"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
"github.com/filecoin-project/go-storedcounter"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/api/client"
|
||||
"github.com/filecoin-project/lotus/api/test"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
genesis2 "github.com/filecoin-project/lotus/chain/gen/genesis"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
"github.com/filecoin-project/lotus/cmd/lotus-seed/seed"
|
||||
sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage"
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/mock"
|
||||
"github.com/filecoin-project/lotus/genesis"
|
||||
miner2 "github.com/filecoin-project/lotus/miner"
|
||||
"github.com/filecoin-project/lotus/node"
|
||||
"github.com/filecoin-project/lotus/node/modules"
|
||||
testing2 "github.com/filecoin-project/lotus/node/modules/testing"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
"github.com/filecoin-project/lotus/storage/mockstorage"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/ipfs/go-datastore"
|
||||
"github.com/libp2p/go-libp2p-core/crypto"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func CreateTestStorageNode(ctx context.Context, t *testing.T, waddr address.Address, act address.Address, pk crypto.PrivKey, tnd test.TestNode, mn mocknet.Mocknet, opts node.Option) test.TestStorageNode {
|
||||
r := repo.NewMemory(nil)
|
||||
|
||||
lr, err := r.Lock(repo.StorageMiner)
|
||||
require.NoError(t, err)
|
||||
|
||||
ks, err := lr.KeyStore()
|
||||
require.NoError(t, err)
|
||||
|
||||
kbytes, err := pk.Bytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ks.Put("libp2p-host", types.KeyInfo{
|
||||
Type: "libp2p-host",
|
||||
PrivateKey: kbytes,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
ds, err := lr.Datastore("/metadata")
|
||||
require.NoError(t, err)
|
||||
err = ds.Put(datastore.NewKey("miner-address"), act.Bytes())
|
||||
require.NoError(t, err)
|
||||
|
||||
nic := storedcounter.New(ds, datastore.NewKey(modules.StorageCounterDSPrefix))
|
||||
for i := 0; i < test.GenesisPreseals; i++ {
|
||||
_, err := nic.Next()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
_, err = nic.Next()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = lr.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
peerid, err := peer.IDFromPrivateKey(pk)
|
||||
require.NoError(t, err)
|
||||
|
||||
enc, err := actors.SerializeParams(&miner.ChangePeerIDParams{NewID: abi.PeerID(peerid)})
|
||||
require.NoError(t, err)
|
||||
|
||||
msg := &types.Message{
|
||||
To: act,
|
||||
From: waddr,
|
||||
Method: builtin.MethodsMiner.ChangePeerID,
|
||||
Params: enc,
|
||||
Value: types.NewInt(0),
|
||||
}
|
||||
|
||||
_, err = tnd.MpoolPushMessage(ctx, msg, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// start node
|
||||
var minerapi api.StorageMiner
|
||||
|
||||
mineBlock := make(chan miner2.MineReq)
|
||||
// TODO: use stop
|
||||
_, err = node.New(ctx,
|
||||
node.StorageMiner(&minerapi),
|
||||
node.Online(),
|
||||
node.Repo(r),
|
||||
node.Test(),
|
||||
|
||||
node.MockHost(mn),
|
||||
|
||||
node.Override(new(api.FullNode), tnd),
|
||||
node.Override(new(*miner2.Miner), miner2.NewTestMiner(mineBlock, act)),
|
||||
|
||||
opts,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to construct node: %v", err)
|
||||
}
|
||||
|
||||
/*// Bootstrap with full node
|
||||
remoteAddrs, err := tnd.NetAddrsListen(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = minerapi.NetConnect(ctx, remoteAddrs)
|
||||
require.NoError(t, err)*/
|
||||
mineOne := func(ctx context.Context, req miner2.MineReq) error {
|
||||
select {
|
||||
case mineBlock <- req:
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
return test.TestStorageNode{StorageMiner: minerapi, MineOne: mineOne}
|
||||
}
|
||||
|
||||
func Builder(t *testing.T, nFull int, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) {
|
||||
ctx := context.Background()
|
||||
mn := mocknet.New(ctx)
|
||||
|
||||
fulls := make([]test.TestNode, nFull)
|
||||
storers := make([]test.TestStorageNode, len(storage))
|
||||
|
||||
pk, _, err := crypto.GenerateEd25519Key(rand.Reader)
|
||||
require.NoError(t, err)
|
||||
|
||||
minerPid, err := peer.IDFromPrivateKey(pk)
|
||||
require.NoError(t, err)
|
||||
|
||||
var genbuf bytes.Buffer
|
||||
|
||||
if len(storage) > 1 {
|
||||
panic("need more peer IDs")
|
||||
}
|
||||
// PRESEAL SECTION, TRY TO REPLACE WITH BETTER IN THE FUTURE
|
||||
// TODO: would be great if there was a better way to fake the preseals
|
||||
|
||||
var genms []genesis.Miner
|
||||
var maddrs []address.Address
|
||||
var genaccs []genesis.Actor
|
||||
var keys []*wallet.Key
|
||||
|
||||
var presealDirs []string
|
||||
for i := 0; i < len(storage); i++ {
|
||||
maddr, err := address.NewIDAddress(genesis2.MinerStart + uint64(i))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tdir, err := ioutil.TempDir("", "preseal-memgen")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
genm, k, err := seed.PreSeal(maddr, abi.RegisteredSealProof_StackedDrg2KiBV1, 0, test.GenesisPreseals, tdir, []byte("make genesis mem random"), nil, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
genm.PeerId = minerPid
|
||||
|
||||
wk, err := wallet.NewKey(*k)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
genaccs = append(genaccs, genesis.Actor{
|
||||
Type: genesis.TAccount,
|
||||
Balance: big.Mul(big.NewInt(400000000), types.NewInt(build.FilecoinPrecision)),
|
||||
Meta: (&genesis.AccountMeta{Owner: wk.Address}).ActorMeta(),
|
||||
})
|
||||
|
||||
keys = append(keys, wk)
|
||||
presealDirs = append(presealDirs, tdir)
|
||||
maddrs = append(maddrs, maddr)
|
||||
genms = append(genms, *genm)
|
||||
}
|
||||
templ := &genesis.Template{
|
||||
Accounts: genaccs,
|
||||
Miners: genms,
|
||||
Timestamp: uint64(time.Now().Unix() - 10000), // some time sufficiently far in the past
|
||||
VerifregRootKey: gen.DefaultVerifregRootkeyActor,
|
||||
RemainderAccount: gen.DefaultRemainderAccountActor,
|
||||
}
|
||||
|
||||
// END PRESEAL SECTION
|
||||
|
||||
for i := 0; i < nFull; i++ {
|
||||
var genesis node.Option
|
||||
if i == 0 {
|
||||
genesis = node.Override(new(modules.Genesis), testing2.MakeGenesisMem(&genbuf, *templ))
|
||||
} else {
|
||||
genesis = node.Override(new(modules.Genesis), modules.LoadGenesis(genbuf.Bytes()))
|
||||
}
|
||||
|
||||
var err error
|
||||
// TODO: Don't ignore stop
|
||||
_, err = node.New(ctx,
|
||||
node.FullAPI(&fulls[i].FullNode),
|
||||
node.Online(),
|
||||
node.Repo(repo.NewMemory(nil)),
|
||||
node.MockHost(mn),
|
||||
node.Test(),
|
||||
|
||||
genesis,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for i, def := range storage {
|
||||
// TODO: support non-bootstrap miners
|
||||
if i != 0 {
|
||||
t.Fatal("only one storage node supported")
|
||||
}
|
||||
if def.Full != 0 {
|
||||
t.Fatal("storage nodes only supported on the first full node")
|
||||
}
|
||||
|
||||
f := fulls[def.Full]
|
||||
if _, err := f.FullNode.WalletImport(ctx, &keys[i].KeyInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.FullNode.WalletSetDefault(ctx, keys[i].Address); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
genMiner := maddrs[i]
|
||||
wa := genms[i].Worker
|
||||
|
||||
storers[i] = CreateTestStorageNode(ctx, t, wa, genMiner, pk, f, mn, node.Options())
|
||||
if err := storers[i].StorageAddLocal(ctx, presealDirs[i]); err != nil {
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
/*
|
||||
sma := storers[i].StorageMiner.(*impl.StorageMinerAPI)
|
||||
|
||||
psd := presealDirs[i]
|
||||
*/
|
||||
}
|
||||
|
||||
if err := mn.LinkAll(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(storers) > 0 {
|
||||
// Mine 2 blocks to setup some CE stuff in some actors
|
||||
var wait sync.Mutex
|
||||
wait.Lock()
|
||||
|
||||
_ = storers[0].MineOne(ctx, miner2.MineReq{Done: func(bool, error) {
|
||||
wait.Unlock()
|
||||
}})
|
||||
wait.Lock()
|
||||
_ = storers[0].MineOne(ctx, miner2.MineReq{Done: func(bool, error) {
|
||||
wait.Unlock()
|
||||
}})
|
||||
wait.Lock()
|
||||
}
|
||||
|
||||
return fulls, storers
|
||||
}
|
||||
|
||||
func MockSbBuilder(t *testing.T, nFull int, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) {
|
||||
ctx := context.Background()
|
||||
mn := mocknet.New(ctx)
|
||||
|
||||
fulls := make([]test.TestNode, nFull)
|
||||
storers := make([]test.TestStorageNode, len(storage))
|
||||
|
||||
var genbuf bytes.Buffer
|
||||
|
||||
// PRESEAL SECTION, TRY TO REPLACE WITH BETTER IN THE FUTURE
|
||||
// TODO: would be great if there was a better way to fake the preseals
|
||||
|
||||
var genms []genesis.Miner
|
||||
var genaccs []genesis.Actor
|
||||
var maddrs []address.Address
|
||||
var keys []*wallet.Key
|
||||
var pidKeys []crypto.PrivKey
|
||||
for i := 0; i < len(storage); i++ {
|
||||
maddr, err := address.NewIDAddress(genesis2.MinerStart + uint64(i))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
preseals := storage[i].Preseal
|
||||
if preseals == test.PresealGenesis {
|
||||
preseals = test.GenesisPreseals
|
||||
}
|
||||
|
||||
genm, k, err := mockstorage.PreSeal(2048, maddr, preseals)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pk, _, err := crypto.GenerateEd25519Key(rand.Reader)
|
||||
require.NoError(t, err)
|
||||
|
||||
minerPid, err := peer.IDFromPrivateKey(pk)
|
||||
require.NoError(t, err)
|
||||
|
||||
genm.PeerId = minerPid
|
||||
|
||||
wk, err := wallet.NewKey(*k)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
genaccs = append(genaccs, genesis.Actor{
|
||||
Type: genesis.TAccount,
|
||||
Balance: big.Mul(big.NewInt(400000000), types.NewInt(build.FilecoinPrecision)),
|
||||
Meta: (&genesis.AccountMeta{Owner: wk.Address}).ActorMeta(),
|
||||
})
|
||||
|
||||
keys = append(keys, wk)
|
||||
pidKeys = append(pidKeys, pk)
|
||||
maddrs = append(maddrs, maddr)
|
||||
genms = append(genms, *genm)
|
||||
}
|
||||
templ := &genesis.Template{
|
||||
Accounts: genaccs,
|
||||
Miners: genms,
|
||||
Timestamp: uint64(time.Now().Unix()) - (build.BlockDelaySecs * 20000),
|
||||
VerifregRootKey: gen.DefaultVerifregRootkeyActor,
|
||||
RemainderAccount: gen.DefaultRemainderAccountActor,
|
||||
}
|
||||
|
||||
// END PRESEAL SECTION
|
||||
|
||||
for i := 0; i < nFull; i++ {
|
||||
var genesis node.Option
|
||||
if i == 0 {
|
||||
genesis = node.Override(new(modules.Genesis), testing2.MakeGenesisMem(&genbuf, *templ))
|
||||
} else {
|
||||
genesis = node.Override(new(modules.Genesis), modules.LoadGenesis(genbuf.Bytes()))
|
||||
}
|
||||
|
||||
var err error
|
||||
// TODO: Don't ignore stop
|
||||
_, err = node.New(ctx,
|
||||
node.FullAPI(&fulls[i].FullNode),
|
||||
node.Online(),
|
||||
node.Repo(repo.NewMemory(nil)),
|
||||
node.MockHost(mn),
|
||||
node.Test(),
|
||||
|
||||
node.Override(new(ffiwrapper.Verifier), mock.MockVerifier),
|
||||
|
||||
genesis,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for i, def := range storage {
|
||||
// TODO: support non-bootstrap miners
|
||||
|
||||
minerID := abi.ActorID(genesis2.MinerStart + uint64(i))
|
||||
|
||||
if def.Full != 0 {
|
||||
t.Fatal("storage nodes only supported on the first full node")
|
||||
}
|
||||
|
||||
f := fulls[def.Full]
|
||||
if _, err := f.FullNode.WalletImport(ctx, &keys[i].KeyInfo); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
if err := f.FullNode.WalletSetDefault(ctx, keys[i].Address); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sectors := make([]abi.SectorID, len(genms[i].Sectors))
|
||||
for i, sector := range genms[i].Sectors {
|
||||
sectors[i] = abi.SectorID{
|
||||
Miner: minerID,
|
||||
Number: sector.SectorID,
|
||||
}
|
||||
}
|
||||
|
||||
storers[i] = CreateTestStorageNode(ctx, t, genms[i].Worker, maddrs[i], pidKeys[i], f, mn, node.Options(
|
||||
node.Override(new(sectorstorage.SectorManager), func() (sectorstorage.SectorManager, error) {
|
||||
return mock.NewMockSectorMgr(build.DefaultSectorSize(), sectors), nil
|
||||
}),
|
||||
node.Override(new(ffiwrapper.Verifier), mock.MockVerifier),
|
||||
node.Unset(new(*sectorstorage.Manager)),
|
||||
))
|
||||
}
|
||||
|
||||
if err := mn.LinkAll(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(storers) > 0 {
|
||||
// Mine 2 blocks to setup some CE stuff in some actors
|
||||
var wait sync.Mutex
|
||||
wait.Lock()
|
||||
|
||||
_ = storers[0].MineOne(ctx, miner2.MineReq{Done: func(bool, error) {
|
||||
wait.Unlock()
|
||||
}})
|
||||
wait.Lock()
|
||||
_ = storers[0].MineOne(ctx, miner2.MineReq{Done: func(bool, error) {
|
||||
wait.Unlock()
|
||||
}})
|
||||
wait.Lock()
|
||||
}
|
||||
|
||||
return fulls, storers
|
||||
}
|
||||
|
||||
func RPCBuilder(t *testing.T, nFull int, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) {
|
||||
return rpcWithBuilder(t, Builder, nFull, storage)
|
||||
}
|
||||
|
||||
func RPCMockSbBuilder(t *testing.T, nFull int, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) {
|
||||
return rpcWithBuilder(t, MockSbBuilder, nFull, storage)
|
||||
}
|
||||
|
||||
func rpcWithBuilder(t *testing.T, b test.APIBuilder, nFull int, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) {
|
||||
fullApis, storaApis := b(t, nFull, storage)
|
||||
fulls := make([]test.TestNode, nFull)
|
||||
storers := make([]test.TestStorageNode, len(storage))
|
||||
|
||||
for i, a := range fullApis {
|
||||
rpcServer := jsonrpc.NewServer()
|
||||
rpcServer.Register("Filecoin", a)
|
||||
testServ := httptest.NewServer(rpcServer) // todo: close
|
||||
|
||||
addr := testServ.Listener.Addr()
|
||||
listenAddr := "ws://" + addr.String()
|
||||
var err error
|
||||
fulls[i].FullNode, _, err = client.NewFullNodeRPC(listenAddr, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ma, err := parseWSSMultiAddr(addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fulls[i].ListenAddr = ma
|
||||
}
|
||||
|
||||
for i, a := range storaApis {
|
||||
rpcServer := jsonrpc.NewServer()
|
||||
rpcServer.Register("Filecoin", a)
|
||||
testServ := httptest.NewServer(rpcServer) // todo: close
|
||||
|
||||
addr := testServ.Listener.Addr()
|
||||
listenAddr := "ws://" + addr.String()
|
||||
var err error
|
||||
storers[i].StorageMiner, _, err = client.NewStorageMinerRPC(listenAddr, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ma, err := parseWSSMultiAddr(addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
storers[i].ListenAddr = ma
|
||||
storers[i].MineOne = a.MineOne
|
||||
}
|
||||
|
||||
return fulls, storers
|
||||
}
|
||||
|
||||
func parseWSSMultiAddr(addr net.Addr) (multiaddr.Multiaddr, error) {
|
||||
host, port, err := net.SplitHostPort(addr.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ma, err := multiaddr.NewMultiaddr("/ip4/" + host + "/" + addr.Network() + "/" + port + "/wss")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ma, nil
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type AddrUse int
|
||||
|
||||
const (
|
||||
PreCommitAddr AddrUse = iota
|
||||
CommitAddr
|
||||
PoStAddr
|
||||
)
|
||||
|
||||
type addrSelectApi interface {
|
||||
WalletBalance(context.Context, address.Address) (types.BigInt, error)
|
||||
WalletHas(context.Context, address.Address) (bool, error)
|
||||
|
||||
StateAccountKey(context.Context, address.Address, types.TipSetKey) (address.Address, error)
|
||||
}
|
||||
|
||||
func AddressFor(ctx context.Context, a addrSelectApi, mi api.MinerInfo, use AddrUse, minFunds abi.TokenAmount) (address.Address, error) {
|
||||
switch use {
|
||||
case PreCommitAddr, CommitAddr:
|
||||
// always use worker, at least for now
|
||||
return mi.Worker, nil
|
||||
}
|
||||
|
||||
for _, addr := range mi.ControlAddresses {
|
||||
b, err := a.WalletBalance(ctx, addr)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("checking control address balance: %w", err)
|
||||
}
|
||||
|
||||
if b.GreaterThanEqual(minFunds) {
|
||||
k, err := a.StateAccountKey(ctx, addr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
log.Errorw("getting account key", "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
have, err := a.WalletHas(ctx, k)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("failed to check control address: %w", err)
|
||||
}
|
||||
|
||||
if !have {
|
||||
log.Errorw("don't have key", "key", k)
|
||||
continue
|
||||
}
|
||||
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
log.Warnw("control address didn't have enough funds for PoSt message", "address", addr, "required", types.FIL(minFunds), "balance", types.FIL(b))
|
||||
}
|
||||
|
||||
// Try to use the owner account if we can, fallback to worker if we can't
|
||||
|
||||
b, err := a.WalletBalance(ctx, mi.Owner)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("checking owner balance: %w", err)
|
||||
}
|
||||
|
||||
if !b.GreaterThanEqual(minFunds) {
|
||||
return mi.Worker, nil
|
||||
}
|
||||
|
||||
k, err := a.StateAccountKey(ctx, mi.Owner, types.EmptyTSK)
|
||||
if err != nil {
|
||||
log.Errorw("getting owner account key", "error", err)
|
||||
return mi.Worker, nil
|
||||
}
|
||||
|
||||
have, err := a.WalletHas(ctx, k)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("failed to check owner address: %w", err)
|
||||
}
|
||||
|
||||
if !have {
|
||||
return mi.Worker, nil
|
||||
}
|
||||
|
||||
return mi.Owner, nil
|
||||
}
|
||||
@@ -67,9 +67,12 @@ type storageMinerApi interface {
|
||||
StateMarketStorageDeal(context.Context, abi.DealID, types.TipSetKey) (*api.MarketDeal, error)
|
||||
StateMinerFaults(context.Context, address.Address, types.TipSetKey) (abi.BitField, error)
|
||||
StateMinerRecoveries(context.Context, address.Address, types.TipSetKey) (abi.BitField, error)
|
||||
StateAccountKey(context.Context, address.Address, types.TipSetKey) (address.Address, error)
|
||||
|
||||
MpoolPushMessage(context.Context, *types.Message, *api.MessageSendSpec) (*types.SignedMessage, error)
|
||||
|
||||
GasEstimateMessageGas(context.Context, *types.Message, *api.MessageSendSpec, types.TipSetKey) (*types.Message, error)
|
||||
|
||||
ChainHead(context.Context) (*types.TipSet, error)
|
||||
ChainNotify(context.Context) (<-chan []*api.HeadChange, error)
|
||||
ChainGetRandomnessFromTickets(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error)
|
||||
|
||||
+39
-2
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/go-bitfield"
|
||||
@@ -177,6 +178,8 @@ func (s *WindowPoStScheduler) checkNextRecoveries(ctx context.Context, dlIdx uin
|
||||
Params: enc,
|
||||
Value: types.NewInt(0),
|
||||
}
|
||||
spec := &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)}
|
||||
s.setSender(ctx, msg, spec)
|
||||
|
||||
sm, err := s.api.MpoolPushMessage(ctx, msg, &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)})
|
||||
if err != nil {
|
||||
@@ -259,8 +262,10 @@ func (s *WindowPoStScheduler) checkNextFaults(ctx context.Context, dlIdx uint64,
|
||||
Params: enc,
|
||||
Value: types.NewInt(0), // TODO: Is there a fee?
|
||||
}
|
||||
spec := &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)}
|
||||
s.setSender(ctx, msg, spec)
|
||||
|
||||
sm, err := s.api.MpoolPushMessage(ctx, msg, &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)})
|
||||
sm, err := s.api.MpoolPushMessage(ctx, msg, spec)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("pushing message to mpool: %w", err)
|
||||
}
|
||||
@@ -465,9 +470,11 @@ func (s *WindowPoStScheduler) submitPost(ctx context.Context, proof *miner.Submi
|
||||
Params: enc,
|
||||
Value: types.NewInt(1000), // currently hard-coded late fee in actor, returned if not late
|
||||
}
|
||||
spec := &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)}
|
||||
s.setSender(ctx, msg, spec)
|
||||
|
||||
// TODO: consider maybe caring about the output
|
||||
sm, err := s.api.MpoolPushMessage(ctx, msg, &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)})
|
||||
sm, err := s.api.MpoolPushMessage(ctx, msg, spec)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("pushing message to mpool: %w", err)
|
||||
}
|
||||
@@ -490,3 +497,33 @@ func (s *WindowPoStScheduler) submitPost(ctx context.Context, proof *miner.Submi
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *WindowPoStScheduler) setSender(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec) {
|
||||
mi, err := s.api.StateMinerInfo(ctx, s.actor, types.EmptyTSK)
|
||||
if err != nil {
|
||||
log.Errorw("error getting miner info", "error", err)
|
||||
|
||||
// better than just failing
|
||||
msg.From = s.worker
|
||||
return
|
||||
}
|
||||
|
||||
gm, err := s.api.GasEstimateMessageGas(ctx, msg, spec, types.EmptyTSK)
|
||||
if err != nil {
|
||||
log.Errorw("estimating gas", "error", err)
|
||||
msg.From = s.worker
|
||||
return
|
||||
}
|
||||
*msg = *gm
|
||||
|
||||
minFunds := big.Add(msg.RequiredFunds(), msg.Value)
|
||||
|
||||
pa, err := AddressFor(ctx, s.api, mi, PoStAddr, minFunds)
|
||||
if err != nil {
|
||||
log.Errorw("error selecting address for post", "error", err)
|
||||
msg.From = s.worker
|
||||
return
|
||||
}
|
||||
|
||||
msg.From = pa
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user