Merge pull request #3177 from filecoin-project/master
Merge latest master into ntwk-calibration
This commit is contained in:
commit
5c6b99c9dc
@ -132,6 +132,9 @@ type FullNode interface {
|
|||||||
GasEstimateGasPremium(_ context.Context, nblocksincl uint64,
|
GasEstimateGasPremium(_ context.Context, nblocksincl uint64,
|
||||||
sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error)
|
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
|
// MethodGroup: Sync
|
||||||
// The Sync method group contains methods for interacting with and
|
// The Sync method group contains methods for interacting with and
|
||||||
// observing the lotus sync service.
|
// observing the lotus sync service.
|
||||||
|
@ -90,9 +90,10 @@ type FullNodeStruct struct {
|
|||||||
|
|
||||||
BeaconGetEntry func(ctx context.Context, epoch abi.ChainEpoch) (*types.BeaconEntry, error) `perm:"read"`
|
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"`
|
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"`
|
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"`
|
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"`
|
SyncState func(context.Context) (*api.SyncState, error) `perm:"read"`
|
||||||
SyncSubmitBlock func(ctx context.Context, blk *types.BlockMsg) error `perm:"write"`
|
SyncSubmitBlock func(ctx context.Context, blk *types.BlockMsg) error `perm:"write"`
|
||||||
@ -459,17 +460,19 @@ func (c *FullNodeStruct) ClientDataTransferUpdates(ctx context.Context) (<-chan
|
|||||||
return c.Internal.ClientDataTransferUpdates(ctx)
|
return c.Internal.ClientDataTransferUpdates(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FullNodeStruct) GasEstimateGasPremium(ctx context.Context, nblocksincl uint64,
|
func (c *FullNodeStruct) GasEstimateGasPremium(ctx context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) {
|
||||||
sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) {
|
|
||||||
return c.Internal.GasEstimateGasPremium(ctx, nblocksincl, sender, gaslimit, tsk)
|
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)
|
return c.Internal.GasEstimateFeeCap(ctx, msg, maxqueueblks, tsk)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FullNodeStruct) GasEstimateGasLimit(ctx context.Context, msg *types.Message,
|
func (c *FullNodeStruct) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) {
|
||||||
tsk types.TipSetKey) (int64, 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)
|
return c.Internal.GasEstimateGasLimit(ctx, msg, tsk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
56
api/test/blockminer.go
Normal file
56
api/test/blockminer.go
Normal 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")
|
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
n, sn := b(t, 1, oneMiner)
|
n, sn := b(t, 1, OneMiner)
|
||||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||||
miner := sn[0]
|
miner := sn[0]
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ func TestDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration, carExport
|
|||||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
n, sn := b(t, 1, oneMiner)
|
n, sn := b(t, 1, OneMiner)
|
||||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||||
miner := sn[0]
|
miner := sn[0]
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ func TestDoubleDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
|||||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
n, sn := b(t, 1, oneMiner)
|
n, sn := b(t, 1, OneMiner)
|
||||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||||
miner := sn[0]
|
miner := sn[0]
|
||||||
|
|
||||||
@ -148,7 +148,7 @@ func TestFastRetrievalDealFlow(t *testing.T, b APIBuilder, blocktime time.Durati
|
|||||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
n, sn := b(t, 1, oneMiner)
|
n, sn := b(t, 1, OneMiner)
|
||||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||||
miner := sn[0]
|
miner := sn[0]
|
||||||
|
|
||||||
@ -203,7 +203,7 @@ func TestSenondDealRetrieval(t *testing.T, b APIBuilder, blocktime time.Duration
|
|||||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
n, sn := b(t, 1, oneMiner)
|
n, sn := b(t, 1, OneMiner)
|
||||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||||
miner := sn[0]
|
miner := sn[0]
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ var log = logging.Logger("apitest")
|
|||||||
|
|
||||||
func (ts *testSuite) testMining(t *testing.T) {
|
func (ts *testSuite) testMining(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
apis, sn := ts.makeNodes(t, 1, oneMiner)
|
apis, sn := ts.makeNodes(t, 1, OneMiner)
|
||||||
api := apis[0]
|
api := apis[0]
|
||||||
|
|
||||||
newHeads, err := api.ChainNotify(ctx)
|
newHeads, err := api.ChainNotify(ctx)
|
||||||
@ -55,7 +55,7 @@ func (ts *testSuite) testMiningReal(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
apis, sn := ts.makeNodes(t, 1, oneMiner)
|
apis, sn := ts.makeNodes(t, 1, OneMiner)
|
||||||
api := apis[0]
|
api := apis[0]
|
||||||
|
|
||||||
newHeads, err := api.ChainNotify(ctx)
|
newHeads, err := api.ChainNotify(ctx)
|
||||||
|
@ -23,14 +23,13 @@ import (
|
|||||||
"github.com/filecoin-project/lotus/chain/events/state"
|
"github.com/filecoin-project/lotus/chain/events/state"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
"github.com/filecoin-project/lotus/chain/wallet"
|
"github.com/filecoin-project/lotus/chain/wallet"
|
||||||
"github.com/filecoin-project/lotus/miner"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
||||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
n, sn := b(t, 2, oneMiner)
|
n, sn := b(t, 2, OneMiner)
|
||||||
|
|
||||||
paymentCreator := n[0]
|
paymentCreator := n[0]
|
||||||
paymentReceiver := n[1]
|
paymentReceiver := n[1]
|
||||||
@ -51,8 +50,8 @@ func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// start mining blocks
|
// start mining blocks
|
||||||
bm := newBlockMiner(ctx, t, miner, blocktime)
|
bm := NewBlockMiner(ctx, t, miner, blocktime)
|
||||||
bm.mineBlocks()
|
bm.MineBlocks()
|
||||||
|
|
||||||
// send some funds to register the receiver
|
// send some funds to register the receiver
|
||||||
receiverAddr, err := paymentReceiver.WalletNew(ctx, wallet.ActSigType("secp256k1"))
|
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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendFunds(ctx, t, paymentCreator, receiverAddr, abi.NewTokenAmount(1e18))
|
SendFunds(ctx, t, paymentCreator, receiverAddr, abi.NewTokenAmount(1e18))
|
||||||
|
|
||||||
// setup the payment channel
|
// setup the payment channel
|
||||||
createrAddr, err := paymentCreator.WalletDefaultAddress(ctx)
|
createrAddr, err := paymentCreator.WalletDefaultAddress(ctx)
|
||||||
@ -201,10 +200,10 @@ func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// shut down mining
|
// 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
|
// We need to add null blocks in batches, if we add too many the chain can't sync
|
||||||
batchSize := 60
|
batchSize := 60
|
||||||
for i := 0; i < count; i += batchSize {
|
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)
|
fmt.Println("Confirmed", desc)
|
||||||
return res
|
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/multiformats/go-multiaddr"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
@ -14,10 +16,16 @@ import (
|
|||||||
|
|
||||||
type TestNode struct {
|
type TestNode struct {
|
||||||
api.FullNode
|
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 {
|
type TestStorageNode struct {
|
||||||
api.StorageMiner
|
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
|
MineOne func(context.Context, miner.MineReq) error
|
||||||
}
|
}
|
||||||
@ -54,11 +62,11 @@ func TestApis(t *testing.T, b APIBuilder) {
|
|||||||
t.Run("testMiningReal", ts.testMiningReal)
|
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) {
|
func (ts *testSuite) testVersion(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
apis, _ := ts.makeNodes(t, 1, oneMiner)
|
apis, _ := ts.makeNodes(t, 1, OneMiner)
|
||||||
api := apis[0]
|
api := apis[0]
|
||||||
|
|
||||||
v, err := api.Version(ctx)
|
v, err := api.Version(ctx)
|
||||||
@ -70,7 +78,7 @@ func (ts *testSuite) testVersion(t *testing.T) {
|
|||||||
|
|
||||||
func (ts *testSuite) testID(t *testing.T) {
|
func (ts *testSuite) testID(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
apis, _ := ts.makeNodes(t, 1, oneMiner)
|
apis, _ := ts.makeNodes(t, 1, OneMiner)
|
||||||
api := apis[0]
|
api := apis[0]
|
||||||
|
|
||||||
id, err := api.ID(ctx)
|
id, err := api.ID(ctx)
|
||||||
@ -82,7 +90,7 @@ func (ts *testSuite) testID(t *testing.T) {
|
|||||||
|
|
||||||
func (ts *testSuite) testConnectTwo(t *testing.T) {
|
func (ts *testSuite) testConnectTwo(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
apis, _ := ts.makeNodes(t, 2, oneMiner)
|
apis, _ := ts.makeNodes(t, 2, OneMiner)
|
||||||
|
|
||||||
p, err := apis[0].NetPeers(ctx)
|
p, err := apis[0].NetPeers(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
36
api/test/util.go
Normal file
36
api/test/util.go
Normal file
@ -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")
|
os.Setenv("BELLMAN_NO_GPU", "1")
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
n, sn := b(t, 1, oneMiner)
|
n, sn := b(t, 1, OneMiner)
|
||||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||||
miner := sn[0]
|
miner := sn[0]
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ func TestWindowPost(t *testing.T, b APIBuilder, blocktime time.Duration, nSector
|
|||||||
os.Setenv("BELLMAN_NO_GPU", "1")
|
os.Setenv("BELLMAN_NO_GPU", "1")
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
n, sn := b(t, 1, oneMiner)
|
n, sn := b(t, 1, OneMiner)
|
||||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||||
miner := sn[0]
|
miner := sn[0]
|
||||||
|
|
||||||
|
18
api/types.go
18
api/types.go
@ -49,9 +49,10 @@ type PubsubScore struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MinerInfo struct {
|
type MinerInfo struct {
|
||||||
Owner address.Address // Must be an ID-address.
|
Owner address.Address // Must be an ID-address.
|
||||||
Worker address.Address // Must be an ID-address.
|
Worker address.Address // Must be an ID-address.
|
||||||
NewWorker 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
|
WorkerChangeEpoch abi.ChainEpoch
|
||||||
PeerId *peer.ID
|
PeerId *peer.ID
|
||||||
Multiaddrs []abi.Multiaddrs
|
Multiaddrs []abi.Multiaddrs
|
||||||
@ -67,10 +68,13 @@ func NewApiMinerInfo(info *miner.MinerInfo) MinerInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mi := MinerInfo{
|
mi := MinerInfo{
|
||||||
Owner: info.Owner,
|
Owner: info.Owner,
|
||||||
Worker: info.Worker,
|
Worker: info.Worker,
|
||||||
NewWorker: address.Undef,
|
ControlAddresses: info.ControlAddresses,
|
||||||
WorkerChangeEpoch: -1,
|
|
||||||
|
NewWorker: address.Undef,
|
||||||
|
WorkerChangeEpoch: -1,
|
||||||
|
|
||||||
PeerId: pid,
|
PeerId: pid,
|
||||||
Multiaddrs: info.Multiaddrs,
|
Multiaddrs: info.Multiaddrs,
|
||||||
SealProofType: info.SealProofType,
|
SealProofType: info.SealProofType,
|
||||||
|
@ -546,7 +546,11 @@ func (mv *MessageValidator) Validate(ctx context.Context, pid peer.ID, msg *pubs
|
|||||||
)
|
)
|
||||||
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
|
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
|
||||||
switch {
|
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
|
return pubsub.ValidationIgnore
|
||||||
default:
|
default:
|
||||||
return pubsub.ValidationReject
|
return pubsub.ValidationReject
|
||||||
|
27
cli/cmd.go
27
cli/cmd.go
@ -67,6 +67,19 @@ func (a APIInfo) AuthHeader() http.Header {
|
|||||||
return nil
|
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 {
|
func flagForRepo(t repo.RepoType) string {
|
||||||
switch t {
|
switch t {
|
||||||
case repo.FullNode:
|
case repo.FullNode:
|
||||||
@ -102,6 +115,20 @@ func envForRepoDeprecation(t repo.RepoType) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetAPIInfo(ctx *cli.Context, t repo.RepoType) (APIInfo, error) {
|
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)
|
envKey := envForRepo(t)
|
||||||
env, ok := os.LookupEnv(envKey)
|
env, ok := os.LookupEnv(envKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
25
cli/paych.go
25
cli/paych.go
@ -5,9 +5,8 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/build"
|
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/lotus/build"
|
||||||
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
@ -71,7 +70,7 @@ var paychGetCmd = &cli.Command{
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(chAddr)
|
fmt.Fprintln(cctx.App.Writer, chAddr)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -94,7 +93,7 @@ var paychListCmd = &cli.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range chs {
|
for _, v := range chs {
|
||||||
fmt.Println(v.String())
|
fmt.Fprintln(cctx.App.Writer, v.String())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
@ -135,7 +134,7 @@ var paychSettleCmd = &cli.Command{
|
|||||||
return fmt.Errorf("settle message execution failed (exit code %d)", mwait.Receipt.ExitCode)
|
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
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -175,7 +174,7 @@ var paychCloseCmd = &cli.Command{
|
|||||||
return fmt.Errorf("collect message execution failed (exit code %d)", mwait.Receipt.ExitCode)
|
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
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -239,7 +238,7 @@ var paychVoucherCreateCmd = &cli.Command{
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(enc)
|
fmt.Fprintln(cctx.App.Writer, enc)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -275,7 +274,7 @@ var paychVoucherCheckCmd = &cli.Command{
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("voucher is valid")
|
fmt.Fprintln(cctx.App.Writer, "voucher is valid")
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -356,9 +355,9 @@ var paychVoucherListCmd = &cli.Command{
|
|||||||
return err
|
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 {
|
} 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(enc)
|
fmt.Fprintln(cctx.App.Writer, enc)
|
||||||
fmt.Printf("Amount: %s\n", best.Amount)
|
fmt.Fprintf(cctx.App.Writer, "Amount: %s\n", best.Amount)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -462,7 +461,7 @@ var paychVoucherSubmitCmd = &cli.Command{
|
|||||||
return fmt.Errorf("message execution failed (exit code %d)", mwait.Receipt.ExitCode)
|
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
|
return nil
|
||||||
},
|
},
|
||||||
|
228
cli/paych_test.go
Normal file
228
cli/paych_test.go
Normal file
@ -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("Owner:\t%s\n", mi.Owner)
|
||||||
fmt.Printf("Worker:\t%s\n", mi.Worker)
|
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("PeerID:\t%s\n", mi.PeerId)
|
||||||
fmt.Printf("SectorSize:\t%s (%d)\n", types.SizeStr(types.NewInt(uint64(mi.SectorSize))), mi.SectorSize)
|
fmt.Printf("SectorSize:\t%s (%d)\n", types.SizeStr(types.NewInt(uint64(mi.SectorSize))), mi.SectorSize)
|
||||||
fmt.Printf("Multiaddrs: \t")
|
fmt.Printf("Multiaddrs: \t")
|
||||||
|
@ -88,6 +88,8 @@ create table if not exists sector_info
|
|||||||
verified_deal_weight text not null,
|
verified_deal_weight text not null,
|
||||||
|
|
||||||
initial_pledge text not null,
|
initial_pledge text not null,
|
||||||
|
expected_day_reward text not null,
|
||||||
|
expected_storage_pledge text not null,
|
||||||
|
|
||||||
constraint sector_info_pk
|
constraint sector_info_pk
|
||||||
primary key (miner_id, sector_id, sealed_cid)
|
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`)
|
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 {
|
if err != nil {
|
||||||
return xerrors.Errorf("Failed to prepare miner precommit info statement: %w", err)
|
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)
|
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 {
|
if err != nil {
|
||||||
return xerrors.Errorf("Failed to prepare miner sector info statement: %w", err)
|
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.DealWeight.String(),
|
||||||
added.VerifiedDealWeight.String(),
|
added.VerifiedDealWeight.String(),
|
||||||
added.InitialPledge.String(),
|
added.InitialPledge.String(),
|
||||||
|
added.ExpectedDayReward.String(),
|
||||||
|
added.ExpectedStoragePledge.String(),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
135
cmd/lotus-chainwatch/processor/power.go
Normal file
135
cmd/lotus-chainwatch/processor/power.go
Normal file
@ -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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := p.setupPower(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,6 +170,14 @@ func (p *Processor) Start(ctx context.Context) {
|
|||||||
return nil
|
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 {
|
grp.Go(func() error {
|
||||||
if err := p.HandleMessageChanges(ctx, toProcess); err != nil {
|
if err := p.HandleMessageChanges(ctx, toProcess); err != nil {
|
||||||
return xerrors.Errorf("Failed to handle message changes: %w", err)
|
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/abi/big"
|
||||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||||
"github.com/filecoin-project/specs-actors/actors/builtin/reward"
|
"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/build"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"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
|
// base reward in attofil for each block found during this epoch
|
||||||
baseBlockReward big.Int
|
baseBlockReward big.Int
|
||||||
|
|
||||||
|
epochSmoothingEstimate *smoothing.FilterEstimate
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Processor) setupRewards() error {
|
func (p *Processor) setupRewards() error {
|
||||||
@ -53,6 +56,15 @@ create table if not exists chain_power
|
|||||||
primary key,
|
primary key,
|
||||||
baseline_power text not null
|
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 {
|
`); err != nil {
|
||||||
return err
|
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 {
|
func (p *Processor) HandleRewardChanges(ctx context.Context, rewardTips ActorTips, nullRounds []types.TipSetKey) error {
|
||||||
rewardChanges, err := p.processRewardActors(ctx, rewardTips, nullRounds)
|
rewardChanges, err := p.processRewardActors(ctx, rewardTips, nullRounds)
|
||||||
if err != nil {
|
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 {
|
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.baseBlockReward = rewardActorState.ThisEpochReward
|
||||||
rw.baselinePower = rewardActorState.ThisEpochBaselinePower
|
rw.baselinePower = rewardActorState.ThisEpochBaselinePower
|
||||||
|
rw.epochSmoothingEstimate = rewardActorState.ThisEpochRewardSmoothed
|
||||||
out = append(out, rw)
|
out = append(out, rw)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,6 +175,13 @@ func (p *Processor) persistRewardActors(ctx context.Context, rewards []rewardAct
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
grp.Go(func() error {
|
||||||
|
if err := p.storeRewardSmoothingEstimates(rewards); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
return grp.Wait()
|
return grp.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,3 +263,43 @@ func (p *Processor) storeBaseBlockReward(rewards []rewardActorInfo) error {
|
|||||||
|
|
||||||
return nil
|
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 _, 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
|
||||||
|
}
|
||||||
|
89
cmd/lotus-shed/jwt.go
Normal file
89
cmd/lotus-shed/jwt.go
Normal file
@ -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, "<addr>", keyAddr)
|
||||||
filename = strings.ReplaceAll(filename, "<type>", keyType)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ func main() {
|
|||||||
base16Cmd,
|
base16Cmd,
|
||||||
bitFieldCmd,
|
bitFieldCmd,
|
||||||
keyinfoCmd,
|
keyinfoCmd,
|
||||||
|
jwtCmd,
|
||||||
noncefix,
|
noncefix,
|
||||||
bigIntParseCmd,
|
bigIntParseCmd,
|
||||||
staterootCmd,
|
staterootCmd,
|
||||||
|
@ -2,19 +2,26 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
"os"
|
||||||
"github.com/libp2p/go-libp2p-core/peer"
|
"strings"
|
||||||
"golang.org/x/xerrors"
|
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/libp2p/go-libp2p-core/peer"
|
||||||
ma "github.com/multiformats/go-multiaddr"
|
ma "github.com/multiformats/go-multiaddr"
|
||||||
"github.com/urfave/cli/v2"
|
"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"
|
||||||
|
"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/specs-actors/actors/builtin/miner"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/chain/actors"
|
"github.com/filecoin-project/lotus/chain/actors"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
lcli "github.com/filecoin-project/lotus/cli"
|
lcli "github.com/filecoin-project/lotus/cli"
|
||||||
|
"github.com/filecoin-project/lotus/lib/tablewriter"
|
||||||
|
"github.com/filecoin-project/lotus/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
var actorCmd = &cli.Command{
|
var actorCmd = &cli.Command{
|
||||||
@ -24,6 +31,7 @@ var actorCmd = &cli.Command{
|
|||||||
actorSetAddrsCmd,
|
actorSetAddrsCmd,
|
||||||
actorWithdrawCmd,
|
actorWithdrawCmd,
|
||||||
actorSetPeeridCmd,
|
actorSetPeeridCmd,
|
||||||
|
actorControl,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,3 +248,232 @@ var actorWithdrawCmd = &cli.Command{
|
|||||||
return nil
|
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("Miner Balance: %s\n", color.YellowString("%s", types.FIL(mact.Balance)))
|
||||||
fmt.Printf("\tPreCommit: %s\n", types.FIL(mas.PreCommitDeposits))
|
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))
|
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))))
|
color.Green("\tAvailable: %s", types.FIL(types.BigSub(mact.Balance, types.BigAdd(mas.LockedFunds, mas.PreCommitDeposits))))
|
||||||
wb, err := api.WalletBalance(ctx, mi.Worker)
|
wb, err := api.WalletBalance(ctx, mi.Worker)
|
||||||
|
@ -143,6 +143,13 @@
|
|||||||
"value": null,
|
"value": null,
|
||||||
"posts": []
|
"posts": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "Payment Channels",
|
||||||
|
"slug": "en+payment-channels",
|
||||||
|
"github": "en/payment-channels.md",
|
||||||
|
"value": null,
|
||||||
|
"posts": []
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "Command Line Interface",
|
"title": "Command Line Interface",
|
||||||
"slug": "en+cli",
|
"slug": "en+cli",
|
||||||
|
@ -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:
|
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
|
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:
|
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
|
||||||
```
|
```
|
||||||
|
86
documentation/en/payment-channels.md
Normal file
86
documentation/en/payment-channels.md
Normal file
@ -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>
|
||||||
|
```
|
@ -6,6 +6,7 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/api"
|
||||||
"github.com/filecoin-project/lotus/build"
|
"github.com/filecoin-project/lotus/build"
|
||||||
"github.com/filecoin-project/lotus/chain/messagepool"
|
"github.com/filecoin-project/lotus/chain/messagepool"
|
||||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
"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
|
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
|
||||||
|
}
|
||||||
|
@ -146,32 +146,12 @@ func (a *MpoolAPI) MpoolPushMessage(ctx context.Context, msg *types.Message, spe
|
|||||||
if msg.Nonce != 0 {
|
if msg.Nonce != 0 {
|
||||||
return nil, xerrors.Errorf("MpoolPushMessage expects message nonce to be 0, was %d", msg.Nonce)
|
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 {
|
msg, err := a.GasAPI.GasEstimateMessageGas(ctx, msg, spec, types.EmptyTSK)
|
||||||
gasPremium, err := a.GasEstimateGasPremium(ctx, 2, msg.From, msg.GasLimit, types.TipSetKey{})
|
if err != nil {
|
||||||
if err != nil {
|
return nil, xerrors.Errorf("GasEstimateMessageGas error: %w", err)
|
||||||
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)
|
|
||||||
|
|
||||||
sign := func(from address.Address, nonce uint64) (*types.SignedMessage, error) {
|
sign := func(from address.Address, nonce uint64) (*types.SignedMessage, error) {
|
||||||
msg.Nonce = nonce
|
msg.Nonce = nonce
|
||||||
if msg.From.Protocol() == address.ID {
|
if msg.From.Protocol() == address.ID {
|
||||||
@ -192,7 +172,6 @@ func (a *MpoolAPI) MpoolPushMessage(ctx context.Context, msg *types.Message, spe
|
|||||||
}
|
}
|
||||||
|
|
||||||
var m *types.SignedMessage
|
var m *types.SignedMessage
|
||||||
var err error
|
|
||||||
again:
|
again:
|
||||||
m, err = a.Mpool.PushWithNonce(ctx, msg.From, sign)
|
m, err = a.Mpool.PushWithNonce(ctx, msg.From, sign)
|
||||||
if err == messagepool.ErrTryAgain {
|
if err == messagepool.ErrTryAgain {
|
||||||
|
@ -37,8 +37,9 @@ func RecordValidator(ps peerstore.Peerstore) record.Validator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const JWTSecretName = "auth-jwt-private" //nolint:gosec
|
const JWTSecretName = "auth-jwt-private" //nolint:gosec
|
||||||
|
const KTJwtHmacSecret = "jwt-hmac-secret"
|
||||||
|
|
||||||
type jwtPayload struct {
|
type JwtPayload struct {
|
||||||
Allow []auth.Permission
|
Allow []auth.Permission
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ func APISecret(keystore types.KeyStore, lr repo.LockedRepo) (*dtypes.APIAlg, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
key = types.KeyInfo{
|
key = types.KeyInfo{
|
||||||
Type: "jwt-hmac-secret",
|
Type: KTJwtHmacSecret,
|
||||||
PrivateKey: sk,
|
PrivateKey: sk,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +64,7 @@ func APISecret(keystore types.KeyStore, lr repo.LockedRepo) (*dtypes.APIAlg, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make this configurable
|
// TODO: make this configurable
|
||||||
p := jwtPayload{
|
p := JwtPayload{
|
||||||
Allow: apistruct.AllPermissions,
|
Allow: apistruct.AllPermissions,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,55 +1,21 @@
|
|||||||
package node_test
|
package node_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"crypto/rand"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http/httptest"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
builder "github.com/filecoin-project/lotus/node/test"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/lib/lotuslog"
|
"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"
|
||||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
"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"
|
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/power"
|
||||||
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
"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/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() {
|
func init() {
|
||||||
@ -62,437 +28,12 @@ func init() {
|
|||||||
verifreg.MinVerifiedDealSize = big.NewInt(256)
|
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) {
|
func TestAPI(t *testing.T) {
|
||||||
test.TestApis(t, builder)
|
test.TestApis(t, builder.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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIRPC(t *testing.T) {
|
func TestAPIRPC(t *testing.T) {
|
||||||
test.TestApis(t, rpcBuilder)
|
test.TestApis(t, builder.RPCBuilder)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIDealFlow(t *testing.T) {
|
func TestAPIDealFlow(t *testing.T) {
|
||||||
@ -503,16 +44,16 @@ func TestAPIDealFlow(t *testing.T) {
|
|||||||
logging.SetLogLevel("storageminer", "ERROR")
|
logging.SetLogLevel("storageminer", "ERROR")
|
||||||
|
|
||||||
t.Run("TestDealFlow", func(t *testing.T) {
|
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) {
|
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) {
|
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) {
|
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
|
saminer.PreCommitChallengeDelay = 5
|
||||||
|
|
||||||
t.Run("basic", func(t *testing.T) {
|
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) {
|
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) {
|
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("sub", "ERROR")
|
||||||
logging.SetLogLevel("storageminer", "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) {
|
func TestPledgeSectors(t *testing.T) {
|
||||||
@ -560,11 +101,11 @@ func TestPledgeSectors(t *testing.T) {
|
|||||||
logging.SetLogLevel("storageminer", "ERROR")
|
logging.SetLogLevel("storageminer", "ERROR")
|
||||||
|
|
||||||
t.Run("1", func(t *testing.T) {
|
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) {
|
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) {
|
t.Run("1000", func(t *testing.T) {
|
||||||
@ -572,7 +113,7 @@ func TestPledgeSectors(t *testing.T) {
|
|||||||
t.Skip("skipping test in short mode")
|
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("sub", "ERROR")
|
||||||
logging.SetLogLevel("storageminer", "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) {
|
func TestCCUpgrade(t *testing.T) {
|
||||||
@ -597,7 +138,7 @@ func TestCCUpgrade(t *testing.T) {
|
|||||||
logging.SetLogLevel("sub", "ERROR")
|
logging.SetLogLevel("sub", "ERROR")
|
||||||
logging.SetLogLevel("storageminer", "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) {
|
func TestPaymentChannels(t *testing.T) {
|
||||||
@ -608,5 +149,5 @@ func TestPaymentChannels(t *testing.T) {
|
|||||||
logging.SetLogLevel("pubsub", "ERROR")
|
logging.SetLogLevel("pubsub", "ERROR")
|
||||||
logging.SetLogLevel("storageminer", "ERROR")
|
logging.SetLogLevel("storageminer", "ERROR")
|
||||||
|
|
||||||
test.TestPaymentChannels(t, mockSbBuilder, 5*time.Millisecond)
|
test.TestPaymentChannels(t, builder.MockSbBuilder, 5*time.Millisecond)
|
||||||
}
|
}
|
||||||
|
500
node/test/builder.go
Normal file
500
node/test/builder.go
Normal file
@ -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
|
||||||
|
}
|
92
storage/addresses.go
Normal file
92
storage/addresses.go
Normal file
@ -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)
|
StateMarketStorageDeal(context.Context, abi.DealID, types.TipSetKey) (*api.MarketDeal, error)
|
||||||
StateMinerFaults(context.Context, address.Address, types.TipSetKey) (abi.BitField, error)
|
StateMinerFaults(context.Context, address.Address, types.TipSetKey) (abi.BitField, error)
|
||||||
StateMinerRecoveries(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)
|
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)
|
ChainHead(context.Context) (*types.TipSet, error)
|
||||||
ChainNotify(context.Context) (<-chan []*api.HeadChange, 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)
|
ChainGetRandomnessFromTickets(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error)
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-bitfield"
|
"github.com/filecoin-project/go-bitfield"
|
||||||
@ -177,6 +178,8 @@ func (s *WindowPoStScheduler) checkNextRecoveries(ctx context.Context, dlIdx uin
|
|||||||
Params: enc,
|
Params: enc,
|
||||||
Value: types.NewInt(0),
|
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)})
|
sm, err := s.api.MpoolPushMessage(ctx, msg, &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -259,8 +262,10 @@ func (s *WindowPoStScheduler) checkNextFaults(ctx context.Context, dlIdx uint64,
|
|||||||
Params: enc,
|
Params: enc,
|
||||||
Value: types.NewInt(0), // TODO: Is there a fee?
|
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 {
|
if err != nil {
|
||||||
return xerrors.Errorf("pushing message to mpool: %w", err)
|
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,
|
Params: enc,
|
||||||
Value: types.NewInt(1000), // currently hard-coded late fee in actor, returned if not late
|
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
|
// 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 {
|
if err != nil {
|
||||||
return xerrors.Errorf("pushing message to mpool: %w", err)
|
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
|
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
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user