From 008fbbd652326a878e6848d3a922eb8efbd0f171 Mon Sep 17 00:00:00 2001 From: Darko Brdareski Date: Wed, 2 Feb 2022 17:08:50 +0100 Subject: [PATCH 01/82] Add unit and integration tests for mempool --- chain/messagepool/check_test.go | 224 +++++++++++++ chain/messagepool/messagepool_test.go | 299 +++++++++++++++++ chain/messagepool/repub_test.go | 3 + chain/messagepool/selection_test.go | 26 ++ itests/mempool_test.go | 455 ++++++++++++++++++++++++++ 5 files changed, 1007 insertions(+) create mode 100644 chain/messagepool/check_test.go create mode 100644 itests/mempool_test.go diff --git a/chain/messagepool/check_test.go b/chain/messagepool/check_test.go new file mode 100644 index 000000000..ffcac74e5 --- /dev/null +++ b/chain/messagepool/check_test.go @@ -0,0 +1,224 @@ +//stm: #unit +package messagepool + +import ( + "context" + "fmt" + "testing" + + "github.com/ipfs/go-datastore" + logging "github.com/ipfs/go-log/v2" + "github.com/stretchr/testify/assert" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/consensus/filcns" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/types/mock" + "github.com/filecoin-project/lotus/chain/wallet" + _ "github.com/filecoin-project/lotus/lib/sigs/bls" + _ "github.com/filecoin-project/lotus/lib/sigs/secp" +) + +func init() { + _ = logging.SetLogLevel("*", "INFO") +} + +func getCheckMessageStatus(statusCode api.CheckStatusCode, msgStatuses []api.MessageCheckStatus) (*api.MessageCheckStatus, error) { + for i := 0; i < len(msgStatuses); i++ { + iMsgStatuses := msgStatuses[i] + if iMsgStatuses.CheckStatus.Code == statusCode { + return &iMsgStatuses, nil + } + } + return nil, fmt.Errorf("Could not find CheckStatusCode %s", statusCode) +} + +func TestCheckMessages(t *testing.T) { + //stm: @CHAIN_MEMPOOL_CHECK_MESSAGES_001 + tma := newTestMpoolAPI() + + w, err := wallet.NewWallet(wallet.NewMemKeyStore()) + if err != nil { + t.Fatal(err) + } + + ds := datastore.NewMapDatastore() + + mp, err := New(context.Background(), tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil) + if err != nil { + t.Fatal(err) + } + + sender, err := w.WalletNew(context.Background(), types.KTSecp256k1) + if err != nil { + t.Fatal(err) + } + + tma.setBalance(sender, 1000e15) + target := mock.Address(1001) + + var protos []*api.MessagePrototype + for i := 0; i < 5; i++ { + msg := &types.Message{ + To: target, + From: sender, + Value: types.NewInt(1), + Nonce: uint64(i), + GasLimit: 50000000, + GasFeeCap: types.NewInt(minimumBaseFee.Uint64()), + GasPremium: types.NewInt(1), + Params: make([]byte, 2<<10), + } + proto := &api.MessagePrototype{ + Message: *msg, + ValidNonce: true, + } + protos = append(protos, proto) + } + + messageStatuses, err := mp.CheckMessages(context.TODO(), protos) + assert.NoError(t, err) + for i := 0; i < len(messageStatuses); i++ { + iMsgStatuses := messageStatuses[i] + for j := 0; j < len(iMsgStatuses); j++ { + jStatus := iMsgStatuses[i] + assert.True(t, jStatus.OK) + } + } +} + +func TestCheckPendingMessages(t *testing.T) { + //stm: @CHAIN_MEMPOOL_CHECK_PENDING_MESSAGES_001 + tma := newTestMpoolAPI() + + w, err := wallet.NewWallet(wallet.NewMemKeyStore()) + if err != nil { + t.Fatal(err) + } + + ds := datastore.NewMapDatastore() + + mp, err := New(context.Background(), tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil) + if err != nil { + t.Fatal(err) + } + + sender, err := w.WalletNew(context.Background(), types.KTSecp256k1) + if err != nil { + t.Fatal(err) + } + + tma.setBalance(sender, 1000e15) + target := mock.Address(1001) + + // add a valid message to the pool + msg := &types.Message{ + To: target, + From: sender, + Value: types.NewInt(1), + Nonce: 0, + GasLimit: 50000000, + GasFeeCap: types.NewInt(minimumBaseFee.Uint64()), + GasPremium: types.NewInt(1), + Params: make([]byte, 2<<10), + } + + sig, err := w.WalletSign(context.TODO(), sender, msg.Cid().Bytes(), api.MsgMeta{}) + if err != nil { + panic(err) + } + sm := &types.SignedMessage{ + Message: *msg, + Signature: *sig, + } + mustAdd(t, mp, sm) + + messageStatuses, err := mp.CheckPendingMessages(context.TODO(), sender) + assert.NoError(t, err) + for i := 0; i < len(messageStatuses); i++ { + iMsgStatuses := messageStatuses[i] + for j := 0; j < len(iMsgStatuses); j++ { + jStatus := iMsgStatuses[i] + assert.True(t, jStatus.OK) + } + } +} + +func TestCheckReplaceMessages(t *testing.T) { + //stm: @CHAIN_MEMPOOL_CHECK_REPLACE_MESSAGES_001 + tma := newTestMpoolAPI() + + w, err := wallet.NewWallet(wallet.NewMemKeyStore()) + if err != nil { + t.Fatal(err) + } + + ds := datastore.NewMapDatastore() + + mp, err := New(context.Background(), tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil) + if err != nil { + t.Fatal(err) + } + + sender, err := w.WalletNew(context.Background(), types.KTSecp256k1) + if err != nil { + t.Fatal(err) + } + + tma.setBalance(sender, 1000e15) + target := mock.Address(1001) + + // add a valid message to the pool + msg := &types.Message{ + To: target, + From: sender, + Value: types.NewInt(1), + Nonce: 0, + GasLimit: 50000000, + GasFeeCap: types.NewInt(minimumBaseFee.Uint64()), + GasPremium: types.NewInt(1), + Params: make([]byte, 2<<10), + } + + sig, err := w.WalletSign(context.TODO(), sender, msg.Cid().Bytes(), api.MsgMeta{}) + if err != nil { + panic(err) + } + sm := &types.SignedMessage{ + Message: *msg, + Signature: *sig, + } + mustAdd(t, mp, sm) + + // create a new message with the same data, except that it is too big + var msgs []*types.Message + invalidmsg := &types.Message{ + To: target, + From: sender, + Value: types.NewInt(1), + Nonce: 0, + GasLimit: 50000000, + GasFeeCap: types.NewInt(minimumBaseFee.Uint64()), + GasPremium: types.NewInt(1), + Params: make([]byte, 128<<10), + } + msgs = append(msgs, invalidmsg) + + { + messageStatuses, err := mp.CheckReplaceMessages(context.TODO(), msgs) + if err != nil { + t.Fatal(err) + } + for i := 0; i < len(messageStatuses); i++ { + iMsgStatuses := messageStatuses[i] + + status, err := getCheckMessageStatus(api.CheckStatusMessageSize, iMsgStatuses) + if err != nil { + t.Fatal(err) + } + // the replacement message should cause a status error + assert.False(t, status.OK) + } + } + +} diff --git a/chain/messagepool/messagepool_test.go b/chain/messagepool/messagepool_test.go index 6bd60da34..86c3a49d1 100644 --- a/chain/messagepool/messagepool_test.go +++ b/chain/messagepool/messagepool_test.go @@ -9,6 +9,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/crypto" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" logging "github.com/ipfs/go-log/v2" @@ -226,6 +227,8 @@ func mustAdd(t *testing.T, mp *MessagePool, msg *types.SignedMessage) { } func TestMessagePool(t *testing.T) { + //stm: @CHAIN_MEMPOOL_GET_NONCE_001 + tma := newTestMpoolAPI() w, err := wallet.NewWallet(wallet.NewMemKeyStore()) @@ -327,6 +330,7 @@ func TestCheckMessageBig(t *testing.T) { Message: *msg, Signature: *sig, } + //stm: @CHAIN_MEMPOOL_PUSH_001 err = mp.Add(context.TODO(), sm) assert.ErrorIs(t, err, ErrMessageTooBig) } @@ -760,3 +764,298 @@ func TestUpdates(t *testing.T) { t.Fatal("expected closed channel, but got an update instead") } } + +func TestMessageBelowMinGasFee(t *testing.T) { + //stm: @CHAIN_MEMPOOL_PUSH_001 + tma := newTestMpoolAPI() + + w, err := wallet.NewWallet(wallet.NewMemKeyStore()) + assert.NoError(t, err) + + from, err := w.WalletNew(context.Background(), types.KTBLS) + assert.NoError(t, err) + + tma.setBalance(from, 1000e9) + + ds := datastore.NewMapDatastore() + + mp, err := New(context.Background(), tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil) + assert.NoError(t, err) + + to := mock.Address(1001) + + // fee is just below minimum gas fee + fee := minimumBaseFee.Uint64() - 1 + { + msg := &types.Message{ + To: to, + From: from, + Value: types.NewInt(1), + Nonce: 0, + GasLimit: 50000000, + GasFeeCap: types.NewInt(fee), + GasPremium: types.NewInt(1), + Params: make([]byte, 32<<10), + } + + sig, err := w.WalletSign(context.TODO(), from, msg.Cid().Bytes(), api.MsgMeta{}) + if err != nil { + panic(err) + } + sm := &types.SignedMessage{ + Message: *msg, + Signature: *sig, + } + err = mp.Add(context.TODO(), sm) + assert.ErrorIs(t, err, ErrGasFeeCapTooLow) + } +} + +func TestMessageValueTooHigh(t *testing.T) { + //stm: @CHAIN_MEMPOOL_PUSH_001 + tma := newTestMpoolAPI() + + w, err := wallet.NewWallet(wallet.NewMemKeyStore()) + assert.NoError(t, err) + + from, err := w.WalletNew(context.Background(), types.KTBLS) + assert.NoError(t, err) + + tma.setBalance(from, 1000e9) + + ds := datastore.NewMapDatastore() + + mp, err := New(context.Background(), tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil) + assert.NoError(t, err) + + to := mock.Address(1001) + + totalFil := types.TotalFilecoinInt + extra := types.NewInt(1) + + value := types.BigAdd(totalFil, extra) + { + msg := &types.Message{ + To: to, + From: from, + Value: value, + Nonce: 0, + GasLimit: 50000000, + GasFeeCap: types.NewInt(minimumBaseFee.Uint64()), + GasPremium: types.NewInt(1), + Params: make([]byte, 32<<10), + } + + sig, err := w.WalletSign(context.TODO(), from, msg.Cid().Bytes(), api.MsgMeta{}) + if err != nil { + panic(err) + } + sm := &types.SignedMessage{ + Message: *msg, + Signature: *sig, + } + + err = mp.Add(context.TODO(), sm) + assert.Error(t, err) + } +} + +func TestMessageSignatureInvalid(t *testing.T) { + //stm: @CHAIN_MEMPOOL_PUSH_001 + tma := newTestMpoolAPI() + + w, err := wallet.NewWallet(wallet.NewMemKeyStore()) + assert.NoError(t, err) + + from, err := w.WalletNew(context.Background(), types.KTBLS) + assert.NoError(t, err) + + tma.setBalance(from, 1000e9) + + ds := datastore.NewMapDatastore() + + mp, err := New(context.Background(), tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil) + assert.NoError(t, err) + + to := mock.Address(1001) + + { + msg := &types.Message{ + To: to, + From: from, + Value: types.NewInt(1), + Nonce: 0, + GasLimit: 50000000, + GasFeeCap: types.NewInt(minimumBaseFee.Uint64()), + GasPremium: types.NewInt(1), + Params: make([]byte, 32<<10), + } + + badSig := &crypto.Signature{ + Type: crypto.SigTypeSecp256k1, + Data: make([]byte, 0), + } + sm := &types.SignedMessage{ + Message: *msg, + Signature: *badSig, + } + err = mp.Add(context.TODO(), sm) + assert.Error(t, err) + assert.Contains(t, err.Error(), "invalid signature length") + } +} + +func TestAddMessageTwice(t *testing.T) { + //stm: @CHAIN_MEMPOOL_PUSH_001 + tma := newTestMpoolAPI() + + w, err := wallet.NewWallet(wallet.NewMemKeyStore()) + assert.NoError(t, err) + + from, err := w.WalletNew(context.Background(), types.KTBLS) + assert.NoError(t, err) + + tma.setBalance(from, 1000e9) + + ds := datastore.NewMapDatastore() + + mp, err := New(context.Background(), tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil) + assert.NoError(t, err) + + to := mock.Address(1001) + + { + // create a valid messages + sm := makeTestMessage(w, from, to, 0, 50_000_000, minimumBaseFee.Uint64()) + mustAdd(t, mp, sm) + + // try to add it twice + err = mp.Add(context.TODO(), sm) + assert.Contains(t, err.Error(), "with nonce 0 already in mpool") + } +} + +func TestAddMessageTwiceNonceGap(t *testing.T) { + //stm: @CHAIN_MEMPOOL_PUSH_001 + tma := newTestMpoolAPI() + + w, err := wallet.NewWallet(wallet.NewMemKeyStore()) + assert.NoError(t, err) + + from, err := w.WalletNew(context.Background(), types.KTBLS) + assert.NoError(t, err) + + tma.setBalance(from, 1000e9) + + ds := datastore.NewMapDatastore() + + mp, err := New(context.Background(), tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil) + assert.NoError(t, err) + + to := mock.Address(1001) + + { + // create message with invalid nonce (1) + sm := makeTestMessage(w, from, to, 1, 50_000_000, minimumBaseFee.Uint64()) + mustAdd(t, mp, sm) + + // then try to add message again + err = mp.Add(context.TODO(), sm) + assert.Contains(t, err.Error(), "unfulfilled nonce gap") + } +} + +func TestAddMessageTwiceCidDiff(t *testing.T) { + tma := newTestMpoolAPI() + + w, err := wallet.NewWallet(wallet.NewMemKeyStore()) + assert.NoError(t, err) + + from, err := w.WalletNew(context.Background(), types.KTBLS) + assert.NoError(t, err) + + tma.setBalance(from, 1000e9) + + ds := datastore.NewMapDatastore() + + mp, err := New(context.Background(), tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil) + assert.NoError(t, err) + + to := mock.Address(1001) + + { + sm := makeTestMessage(w, from, to, 0, 50_000_000, minimumBaseFee.Uint64()) + mustAdd(t, mp, sm) + + // Create message with different data, so CID is different + sm2 := makeTestMessage(w, from, to, 0, 50_000_001, minimumBaseFee.Uint64()) + + //stm: @CHAIN_MEMPOOL_PUSH_001 + // then try to add message again + err = mp.Add(context.TODO(), sm2) + assert.Contains(t, err.Error(), "replace by fee has too low GasPremium") + } +} + +func TestAddMessageTwiceCidDiffReplaced(t *testing.T) { + //stm: @CHAIN_MEMPOOL_PUSH_001 + tma := newTestMpoolAPI() + + w, err := wallet.NewWallet(wallet.NewMemKeyStore()) + assert.NoError(t, err) + + from, err := w.WalletNew(context.Background(), types.KTBLS) + assert.NoError(t, err) + + tma.setBalance(from, 1000e9) + + ds := datastore.NewMapDatastore() + + mp, err := New(context.Background(), tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil) + assert.NoError(t, err) + + to := mock.Address(1001) + + { + sm := makeTestMessage(w, from, to, 0, 50_000_000, minimumBaseFee.Uint64()) + mustAdd(t, mp, sm) + + // Create message with different data, so CID is different + sm2 := makeTestMessage(w, from, to, 0, 50_000_000, minimumBaseFee.Uint64()*2) + mustAdd(t, mp, sm2) + } +} + +func TestRemoveMessage(t *testing.T) { + //stm: @CHAIN_MEMPOOL_PUSH_001 + tma := newTestMpoolAPI() + + w, err := wallet.NewWallet(wallet.NewMemKeyStore()) + assert.NoError(t, err) + + from, err := w.WalletNew(context.Background(), types.KTBLS) + assert.NoError(t, err) + + tma.setBalance(from, 1000e9) + + ds := datastore.NewMapDatastore() + + mp, err := New(context.Background(), tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil) + assert.NoError(t, err) + + to := mock.Address(1001) + + { + sm := makeTestMessage(w, from, to, 0, 50_000_000, minimumBaseFee.Uint64()) + mustAdd(t, mp, sm) + + //stm: @CHAIN_MEMPOOL_REMOVE_001 + // remove message for sender + mp.Remove(context.TODO(), from, sm.Message.Nonce, true) + + //stm: @CHAIN_MEMPOOL_PENDING_FOR_001 + // check messages in pool: should be none present + msgs := mp.pendingFor(context.TODO(), from) + assert.Len(t, msgs, 0) + } +} diff --git a/chain/messagepool/repub_test.go b/chain/messagepool/repub_test.go index de32eaa6b..18a75d881 100644 --- a/chain/messagepool/repub_test.go +++ b/chain/messagepool/repub_test.go @@ -1,3 +1,4 @@ +//stm: #unit package messagepool import ( @@ -16,6 +17,7 @@ import ( ) func TestRepubMessages(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001 oldRepublishBatchDelay := RepublishBatchDelay RepublishBatchDelay = time.Microsecond defer func() { @@ -57,6 +59,7 @@ func TestRepubMessages(t *testing.T) { for i := 0; i < 10; i++ { m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1)) + //stm: @CHAIN_MEMPOOL_PUSH_001 _, err := mp.Push(context.TODO(), m) if err != nil { t.Fatal(err) diff --git a/chain/messagepool/selection_test.go b/chain/messagepool/selection_test.go index 2ae99cd77..e97d5208e 100644 --- a/chain/messagepool/selection_test.go +++ b/chain/messagepool/selection_test.go @@ -1,3 +1,4 @@ +//stm: #unit package messagepool import ( @@ -74,6 +75,8 @@ func makeTestMpool() (*MessagePool, *testMpoolAPI) { } func TestMessageChains(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001 + //stm: @CHAIN_MEMPOOL_CREATE_MSG_CHAINS_001 mp, tma := makeTestMpool() // the actors @@ -310,6 +313,8 @@ func TestMessageChains(t *testing.T) { } func TestMessageChainSkipping(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001, @CHAIN_MEMPOOL_CREATE_MSG_CHAINS_001 + // regression test for chain skip bug mp, tma := makeTestMpool() @@ -382,6 +387,7 @@ func TestMessageChainSkipping(t *testing.T) { } func TestBasicMessageSelection(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001, @CHAIN_MEMPOOL_SELECT_001 oldMaxNonceGap := MaxNonceGap MaxNonceGap = 1000 defer func() { @@ -532,6 +538,7 @@ func TestBasicMessageSelection(t *testing.T) { } func TestMessageSelectionTrimmingGas(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001, @CHAIN_MEMPOOL_SELECT_001 mp, tma := makeTestMpool() // the actors @@ -595,6 +602,7 @@ func TestMessageSelectionTrimmingGas(t *testing.T) { } func TestMessageSelectionTrimmingMsgsBasic(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001, @CHAIN_MEMPOOL_SELECT_001 mp, tma := makeTestMpool() // the actors @@ -641,6 +649,7 @@ func TestMessageSelectionTrimmingMsgsBasic(t *testing.T) { } func TestMessageSelectionTrimmingMsgsTwoSendersBasic(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001, @CHAIN_MEMPOOL_SELECT_001 mp, tma := makeTestMpool() // the actors @@ -707,6 +716,7 @@ func TestMessageSelectionTrimmingMsgsTwoSendersBasic(t *testing.T) { } func TestMessageSelectionTrimmingMsgsTwoSendersAdvanced(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001, @CHAIN_MEMPOOL_SELECT_001 mp, tma := makeTestMpool() // the actors @@ -788,6 +798,7 @@ func TestMessageSelectionTrimmingMsgsTwoSendersAdvanced(t *testing.T) { } func TestPriorityMessageSelection(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001, @CHAIN_MEMPOOL_SELECT_001 mp, tma := makeTestMpool() // the actors @@ -867,6 +878,7 @@ func TestPriorityMessageSelection(t *testing.T) { } func TestPriorityMessageSelection2(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001, @CHAIN_MEMPOOL_SELECT_001 mp, tma := makeTestMpool() // the actors @@ -934,6 +946,7 @@ func TestPriorityMessageSelection2(t *testing.T) { } func TestPriorityMessageSelection3(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001, @CHAIN_MEMPOOL_SELECT_001 mp, tma := makeTestMpool() // the actors @@ -1028,6 +1041,8 @@ func TestPriorityMessageSelection3(t *testing.T) { } func TestOptimalMessageSelection1(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001, @CHAIN_MEMPOOL_SELECT_001 + // this test uses just a single actor sending messages with a low tq // the chain depenent merging algorithm should pick messages from the actor // from the start @@ -1094,6 +1109,8 @@ func TestOptimalMessageSelection1(t *testing.T) { } func TestOptimalMessageSelection2(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001, @CHAIN_MEMPOOL_SELECT_001 + // this test uses two actors sending messages to each other, with the first // actor paying (much) higher gas premium than the second. // We select with a low ticket quality; the chain depenent merging algorithm should pick @@ -1173,6 +1190,8 @@ func TestOptimalMessageSelection2(t *testing.T) { } func TestOptimalMessageSelection3(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001, @CHAIN_MEMPOOL_SELECT_001 + // this test uses 10 actors sending a block of messages to each other, with the the first // actors paying higher gas premium than the subsequent actors. // We select with a low ticket quality; the chain dependent merging algorithm should pick @@ -1416,6 +1435,8 @@ func makeZipfPremiumDistribution(rng *rand.Rand) func() uint64 { } func TestCompetitiveMessageSelectionExp(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001, @CHAIN_MEMPOOL_SELECT_001 + if testing.Short() { t.Skip("skipping in short mode") } @@ -1439,6 +1460,8 @@ func TestCompetitiveMessageSelectionExp(t *testing.T) { } func TestCompetitiveMessageSelectionZipf(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001, @CHAIN_MEMPOOL_SELECT_001 + if testing.Short() { t.Skip("skipping in short mode") } @@ -1462,6 +1485,7 @@ func TestCompetitiveMessageSelectionZipf(t *testing.T) { } func TestGasReward(t *testing.T) { + //stm: @CHAIN_MEMPOOL_GET_GAS_REWARD_001 tests := []struct { Premium uint64 FeeCap uint64 @@ -1494,6 +1518,8 @@ func TestGasReward(t *testing.T) { } func TestRealWorldSelection(t *testing.T) { + //stm: @TOKEN_WALLET_NEW_001, @TOKEN_WALLET_SIGN_001, @CHAIN_MEMPOOL_SELECT_001 + // load test-messages.json.gz and rewrite the messages so that // 1) we map each real actor to a test actor so that we can sign the messages // 2) adjust the nonces so that they start from 0 diff --git a/itests/mempool_test.go b/itests/mempool_test.go new file mode 100644 index 000000000..f5fb408e0 --- /dev/null +++ b/itests/mempool_test.go @@ -0,0 +1,455 @@ +//stm: #integration +package itests + +import ( + "context" + "testing" + "time" + + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/itests/kit" + "github.com/stretchr/testify/require" +) + +func TestMemPoolPushSingleNode(t *testing.T) { + //stm: @CHAIN_MEMPOOL_CREATE_MSG_CHAINS_001, @CHAIN_MEMPOOL_SELECT_001 + //stm: @CHAIN_MEMPOOL_PENDING_001, @CHAIN_STATE_WAIT_MSG_001, @CHAIN_MEMPOOL_CAP_GAS_FEE_001 + //stm: @CHAIN_MEMPOOL_PUSH_002 + ctx := context.Background() + const blockTime = 100 * time.Millisecond + firstNode, _, _, ens := kit.EnsembleTwoOne(t, kit.MockProofs()) + ens.InterconnectAll().BeginMining(blockTime) + kit.QuietMiningLogs() + + sender := firstNode.DefaultKey.Address + + addr, err := firstNode.WalletNew(ctx, types.KTBLS) + require.NoError(t, err) + + const totalMessages = 10 + + bal, err := firstNode.WalletBalance(ctx, sender) + require.NoError(t, err) + toSend := big.Div(bal, big.NewInt(10)) + each := big.Div(toSend, big.NewInt(totalMessages)) + + // add messages to be mined/published + var sms []*types.SignedMessage + for i := 0; i < totalMessages; i++ { + msg := &types.Message{ + From: sender, + To: addr, + Value: each, + } + + sm, err := firstNode.MpoolPushMessage(ctx, msg, nil) + require.NoError(t, err) + require.EqualValues(t, i, sm.Message.Nonce) + + sms = append(sms, sm) + } + + // check pending messages for address + msgStatuses, _ := firstNode.MpoolCheckPendingMessages(ctx, sender) + require.Equal(t, totalMessages, len(msgStatuses)) + for _, msgStatusList := range msgStatuses { + for _, status := range msgStatusList { + require.True(t, status.OK) + } + } + + // verify messages should be the ones included in the next block + selected, _ := firstNode.MpoolSelect(ctx, types.EmptyTSK, 0) + for _, msg := range sms { + found := false + for _, selectedMsg := range selected { + if selectedMsg.Cid() == msg.Cid() { + found = true + break + } + } + require.True(t, found) + } + + time.Sleep(10 * blockTime) + + // pool pending list should be empty + pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) + require.NoError(t, err) + require.Equal(t, 0, len(pending)) + + // all messages should be added to the chain + for _, lookMsg := range sms { + msgLookup, err := firstNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + require.NoError(t, err) + require.NotNil(t, msgLookup) + } +} + +func TestMemPoolPushTwoNodes(t *testing.T) { + //stm: @CHAIN_MEMPOOL_CREATE_MSG_CHAINS_001, @CHAIN_MEMPOOL_SELECT_001 + //stm: @CHAIN_MEMPOOL_PENDING_001, @CHAIN_STATE_WAIT_MSG_001, @CHAIN_MEMPOOL_CAP_GAS_FEE_001 + //stm: @CHAIN_MEMPOOL_PUSH_002 + ctx := context.Background() + const blockTime = 100 * time.Millisecond + firstNode, secondNode, _, ens := kit.EnsembleTwoOne(t, kit.MockProofs()) + ens.InterconnectAll().BeginMining(blockTime) + kit.QuietMiningLogs() + + sender := firstNode.DefaultKey.Address + sender2 := secondNode.DefaultKey.Address + + addr, _ := firstNode.WalletNew(ctx, types.KTBLS) + addr2, _ := secondNode.WalletNew(ctx, types.KTBLS) + + bal, err := firstNode.WalletBalance(ctx, sender) + require.NoError(t, err) + + const totalMessages = 10 + + toSend := big.Div(bal, big.NewInt(10)) + each := big.Div(toSend, big.NewInt(totalMessages)) + + var sms []*types.SignedMessage + // push messages to message pools of both nodes + for i := 0; i < totalMessages; i++ { + // first + msg1 := &types.Message{ + From: sender, + To: addr, + Value: each, + } + + sm1, err := firstNode.MpoolPushMessage(ctx, msg1, nil) + require.NoError(t, err) + require.EqualValues(t, i, sm1.Message.Nonce) + sms = append(sms, sm1) + + // second + msg2 := &types.Message{ + From: sender2, + To: addr2, + Value: each, + } + + sm2, err := secondNode.MpoolPushMessage(ctx, msg2, nil) + require.NoError(t, err) + require.EqualValues(t, i, sm2.Message.Nonce) + sms = append(sms, sm2) + } + + time.Sleep(10 * blockTime) + + pending1, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) + require.NoError(t, err) + require.Equal(t, 0, len(pending1)) + + pending2, err := secondNode.MpoolPending(context.TODO(), types.EmptyTSK) + require.NoError(t, err) + require.Equal(t, 0, len(pending2)) + + // Check messages on both nodes + for _, lookMsg := range sms { + msgLookup1, err := firstNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + require.NoError(t, err) + require.NotNil(t, msgLookup1) + + msgLookup2, err := secondNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + require.NoError(t, err) + require.NotNil(t, msgLookup2) + } +} + +func TestMemPoolClearPending(t *testing.T) { + //stm: @CHAIN_MEMPOOL_PUSH_001, @CHAIN_MEMPOOL_PENDING_001 + //stm: @CHAIN_STATE_WAIT_MSG_001, @CHAIN_MEMPOOL_CLEAR_001, @CHAIN_MEMPOOL_CAP_GAS_FEE_001 + ctx := context.Background() + const blockTime = 100 * time.Millisecond + firstNode, _, _, ens := kit.EnsembleTwoOne(t, kit.MockProofs()) + ens.InterconnectAll().BeginMining(blockTime) + kit.QuietMiningLogs() + + sender := firstNode.DefaultKey.Address + + addr, _ := firstNode.WalletNew(ctx, types.KTBLS) + + const totalMessages = 10 + + bal, err := firstNode.WalletBalance(ctx, sender) + require.NoError(t, err) + toSend := big.Div(bal, big.NewInt(10)) + each := big.Div(toSend, big.NewInt(totalMessages)) + + // Add single message, then clear the pool + msg := &types.Message{ + From: sender, + To: addr, + Value: each, + } + _, err = firstNode.MpoolPushMessage(ctx, msg, nil) + require.NoError(t, err) + + err = firstNode.MpoolClear(ctx, true) + require.NoError(t, err) + + // pool should be empty now + pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) + require.NoError(t, err) + require.Equal(t, 0, len(pending)) + + time.Sleep(2 * blockTime) + + // waiting for the message should produce nothing + _, err = firstNode.StateWaitMsg(ctx, msg.Cid(), 3, api.LookbackNoLimit, true) + require.Error(t, err) +} + +func TestMemPoolBatchPush(t *testing.T) { + //stm: @CHAIN_MEMPOOL_CREATE_MSG_CHAINS_001, @CHAIN_MEMPOOL_SELECT_001, @CHAIN_MEMPOOL_CAP_GAS_FEE_001 + //stm: @CHAIN_MEMPOOL_CHECK_PENDING_MESSAGES_001, @CHAIN_MEMPOOL_SELECT_001 + //stm: @CHAIN_MEMPOOL_PENDING_001, @CHAIN_STATE_WAIT_MSG_001 + //stm: @CHAIN_MEMPOOL_BATCH_PUSH_001 + ctx := context.Background() + const blockTime = 100 * time.Millisecond + firstNode, _, _, ens := kit.EnsembleTwoOne(t, kit.MockProofs()) + ens.InterconnectAll().BeginMining(blockTime) + kit.QuietMiningLogs() + + sender := firstNode.DefaultKey.Address + + addr, _ := firstNode.WalletNew(ctx, types.KTBLS) + + const totalMessages = 10 + + bal, err := firstNode.WalletBalance(ctx, sender) + require.NoError(t, err) + toSend := big.Div(bal, big.NewInt(10)) + each := big.Div(toSend, big.NewInt(totalMessages)) + + // add messages to be mined/published + var sms []*types.SignedMessage + for i := 0; i < totalMessages; i++ { + msg := &types.Message{ + From: sender, + To: addr, + Value: each, + Nonce: uint64(i), + GasLimit: 50_000_000, + GasFeeCap: types.NewInt(100_000_000), + GasPremium: types.NewInt(1), + } + + signedMessage, err := firstNode.WalletSignMessage(ctx, sender, msg) + require.NoError(t, err) + + sms = append(sms, signedMessage) + } + + _, err = firstNode.MpoolBatchPush(ctx, sms) + require.NoError(t, err) + + // check pending messages for address + msgStatuses, err := firstNode.MpoolCheckPendingMessages(ctx, sender) + require.NoError(t, err) + require.Equal(t, totalMessages, len(msgStatuses)) + for _, msgStatusList := range msgStatuses { + for _, status := range msgStatusList { + require.True(t, status.OK) + } + } + + // verify messages should be the ones included in the next block + selected, _ := firstNode.MpoolSelect(ctx, types.EmptyTSK, 0) + require.NoError(t, err) + for _, msg := range sms { + found := false + for _, selectedMsg := range selected { + if selectedMsg.Cid() == msg.Cid() { + found = true + break + } + } + require.True(t, found) + } + + time.Sleep(10 * blockTime) + + // pool pending list should be empty + pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) + require.NoError(t, err) + require.Equal(t, 0, len(pending)) + + // all messages should be added to the chain + for _, lookMsg := range sms { + msgLookup, err := firstNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + require.NoError(t, err) + require.NotNil(t, msgLookup) + } +} + +func TestMemPoolPushSingleNodeUntrusted(t *testing.T) { + //stm: @CHAIN_MEMPOOL_CREATE_MSG_CHAINS_001, @CHAIN_MEMPOOL_SELECT_001, @CHAIN_MEMPOOL_CAP_GAS_FEE_001 + //stm: @CHAIN_MEMPOOL_CHECK_PENDING_MESSAGES_001, @CHAIN_MEMPOOL_SELECT_001 + //stm: @CHAIN_MEMPOOL_PENDING_001, @CHAIN_STATE_WAIT_MSG_001 + //stm: @CHAIN_MEMPOOL_PUSH_003 + ctx := context.Background() + const blockTime = 100 * time.Millisecond + firstNode, _, _, ens := kit.EnsembleTwoOne(t, kit.MockProofs()) + ens.InterconnectAll().BeginMining(blockTime) + kit.QuietMiningLogs() + + sender := firstNode.DefaultKey.Address + + addr, _ := firstNode.WalletNew(ctx, types.KTBLS) + + const totalMessages = 10 + + bal, err := firstNode.WalletBalance(ctx, sender) + require.NoError(t, err) + toSend := big.Div(bal, big.NewInt(10)) + each := big.Div(toSend, big.NewInt(totalMessages)) + + // add messages to be mined/published + var sms []*types.SignedMessage + for i := 0; i < totalMessages; i++ { + msg := &types.Message{ + From: sender, + To: addr, + Value: each, + Nonce: uint64(i), + GasLimit: 50_000_000, + GasFeeCap: types.NewInt(100_000_000), + GasPremium: types.NewInt(1), + } + + signedMessage, err := firstNode.WalletSignMessage(ctx, sender, msg) + require.NoError(t, err) + + // push untrusted messages + pushedCid, err := firstNode.MpoolPushUntrusted(ctx, signedMessage) + require.NoError(t, err) + require.Equal(t, msg.Cid(), pushedCid) + + sms = append(sms, signedMessage) + } + + // check pending messages for address + msgStatuses, _ := firstNode.MpoolCheckPendingMessages(ctx, sender) + require.Equal(t, totalMessages, len(msgStatuses)) + for _, msgStatusList := range msgStatuses { + for _, status := range msgStatusList { + require.True(t, status.OK) + } + } + + // verify messages should be the ones included in the next block + selected, _ := firstNode.MpoolSelect(ctx, types.EmptyTSK, 0) + for _, msg := range sms { + found := false + for _, selectedMsg := range selected { + if selectedMsg.Cid() == msg.Cid() { + found = true + break + } + } + require.True(t, found) + } + + time.Sleep(10 * blockTime) + + // pool pending list should be empty + pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) + require.NoError(t, err) + require.Equal(t, 0, len(pending)) + + // all messages should be added to the chain + for _, lookMsg := range sms { + msgLookup, err := firstNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + require.NoError(t, err) + require.NotNil(t, msgLookup) + } +} + +func TestMemPoolBatchPushUntrusted(t *testing.T) { + //stm: @CHAIN_MEMPOOL_CREATE_MSG_CHAINS_001, @CHAIN_MEMPOOL_SELECT_001, @CHAIN_MEMPOOL_CAP_GAS_FEE_001 + //stm: @CHAIN_MEMPOOL_CHECK_PENDING_MESSAGES_001, @CHAIN_MEMPOOL_SELECT_001 + //stm: @CHAIN_MEMPOOL_PENDING_001, @CHAIN_STATE_WAIT_MSG_001 + //stm: @CHAIN_MEMPOOL_BATCH_PUSH_002 + ctx := context.Background() + const blockTime = 100 * time.Millisecond + firstNode, _, _, ens := kit.EnsembleTwoOne(t, kit.MockProofs()) + ens.InterconnectAll().BeginMining(blockTime) + kit.QuietMiningLogs() + + sender := firstNode.DefaultKey.Address + + addr, _ := firstNode.WalletNew(ctx, types.KTBLS) + + const totalMessages = 10 + + bal, err := firstNode.WalletBalance(ctx, sender) + require.NoError(t, err) + toSend := big.Div(bal, big.NewInt(10)) + each := big.Div(toSend, big.NewInt(totalMessages)) + + // add messages to be mined/published + var sms []*types.SignedMessage + for i := 0; i < totalMessages; i++ { + msg := &types.Message{ + From: sender, + To: addr, + Value: each, + Nonce: uint64(i), + GasLimit: 50_000_000, + GasFeeCap: types.NewInt(100_000_000), + GasPremium: types.NewInt(1), + } + + signedMessage, err := firstNode.WalletSignMessage(ctx, sender, msg) + require.NoError(t, err) + + sms = append(sms, signedMessage) + } + + _, err = firstNode.MpoolBatchPushUntrusted(ctx, sms) + require.NoError(t, err) + + // check pending messages for address + msgStatuses, err := firstNode.MpoolCheckPendingMessages(ctx, sender) + require.NoError(t, err) + require.Equal(t, totalMessages, len(msgStatuses)) + for _, msgStatusList := range msgStatuses { + for _, status := range msgStatusList { + require.True(t, status.OK) + } + } + + // verify messages should be the ones included in the next block + selected, _ := firstNode.MpoolSelect(ctx, types.EmptyTSK, 0) + for _, msg := range sms { + found := false + for _, selectedMsg := range selected { + if selectedMsg.Cid() == msg.Cid() { + found = true + break + } + } + require.True(t, found) + } + + time.Sleep(10 * blockTime) + + // pool pending list should be empty + pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) + require.NoError(t, err) + require.Equal(t, 0, len(pending)) + + // all messages should be added to the chain + for _, lookMsg := range sms { + msgLookup, err := firstNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + require.NoError(t, err) + require.NotNil(t, msgLookup) + } +} From 4cddfd1074ba9d78871cff6978518e30653ef314 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 4 Feb 2022 14:29:50 +0200 Subject: [PATCH 02/82] background cold object reification --- blockstore/splitstore/splitstore.go | 14 ++ blockstore/splitstore/splitstore_reify.go | 181 ++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 blockstore/splitstore/splitstore_reify.go diff --git a/blockstore/splitstore/splitstore.go b/blockstore/splitstore/splitstore.go index 6a65e01df..0d7ad0779 100644 --- a/blockstore/splitstore/splitstore.go +++ b/blockstore/splitstore/splitstore.go @@ -161,6 +161,12 @@ type SplitStore struct { txnSyncCond sync.Cond txnSync bool + // background cold object reification + reifyMx sync.Mutex + reifyCond sync.Cond + reifyPend map[cid.Cid]struct{} + reifyInProgress map[cid.Cid]struct{} + // registered protectors protectors []func(func(cid.Cid) error) error } @@ -202,6 +208,10 @@ func Open(path string, ds dstore.Datastore, hot, cold bstore.Blockstore, cfg *Co ss.txnSyncCond.L = &ss.txnSyncMx ss.ctx, ss.cancel = context.WithCancel(context.Background()) + ss.reifyCond.L = &ss.reifyMx + ss.reifyPend = make(map[cid.Cid]struct{}) + ss.reifyInProgress = make(map[cid.Cid]struct{}) + if enableDebugLog { ss.debug, err = openDebugLog(path) if err != nil { @@ -645,6 +655,9 @@ func (s *SplitStore) Start(chain ChainAccessor, us stmgr.UpgradeSchedule) error } } + // spawn the reifier + go s.reifyOrchestrator() + // watch the chain chain.SubscribeHeadChanges(s.HeadChange) @@ -676,6 +689,7 @@ func (s *SplitStore) Close() error { } } + s.reifyCond.Broadcast() s.cancel() return multierr.Combine(s.markSetEnv.Close(), s.debug.Close()) } diff --git a/blockstore/splitstore/splitstore_reify.go b/blockstore/splitstore/splitstore_reify.go new file mode 100644 index 000000000..f60100d8a --- /dev/null +++ b/blockstore/splitstore/splitstore_reify.go @@ -0,0 +1,181 @@ +package splitstore + +import ( + "runtime" + "sync/atomic" + + "golang.org/x/xerrors" + + blocks "github.com/ipfs/go-block-format" + cid "github.com/ipfs/go-cid" +) + +func (s *SplitStore) reifyColdObject(c cid.Cid) { + if isUnitaryObject(c) { + return + } + + s.reifyMx.Lock() + defer s.reifyMx.Unlock() + + _, ok := s.reifyInProgress[c] + if ok { + return + } + + s.reifyPend[c] = struct{}{} + s.reifyCond.Broadcast() +} + +func (s *SplitStore) reifyOrchestrator() { + workers := runtime.NumCPU() / 4 + if workers < 2 { + workers = 2 + } + + workch := make(chan cid.Cid, workers) + defer close(workch) + + for i := 0; i < workers; i++ { + go s.reifyWorker(workch) + } + + for { + s.reifyMx.Lock() + for len(s.reifyPend) == 0 && atomic.LoadInt32(&s.closing) == 0 { + s.reifyCond.Wait() + } + + if atomic.LoadInt32(&s.closing) != 0 { + s.reifyMx.Unlock() + return + } + + reifyPend := s.reifyPend + s.reifyPend = make(map[cid.Cid]struct{}) + s.reifyMx.Unlock() + + for c := range reifyPend { + select { + case workch <- c: + case <-s.ctx.Done(): + return + } + } + } +} + +func (s *SplitStore) reifyWorker(workch chan cid.Cid) { + for c := range workch { + s.doReify(c) + } +} + +func (s *SplitStore) doReify(c cid.Cid) { + var toreify, totrack, toforget []cid.Cid + + defer func() { + s.reifyMx.Lock() + defer s.reifyMx.Unlock() + + for _, c := range toreify { + delete(s.reifyInProgress, c) + } + for _, c := range totrack { + delete(s.reifyInProgress, c) + } + for _, c := range toforget { + delete(s.reifyInProgress, c) + } + }() + + s.txnLk.RLock() + defer s.txnLk.RUnlock() + + err := s.walkObject(c, newTmpVisitor(), + func(c cid.Cid) error { + if isUnitaryObject(c) { + return errStopWalk + } + + s.reifyMx.Lock() + _, inProgress := s.reifyInProgress[c] + if !inProgress { + s.reifyInProgress[c] = struct{}{} + } + s.reifyMx.Unlock() + + if inProgress { + return errStopWalk + } + + has, err := s.hot.Has(s.ctx, c) + if err != nil { + return xerrors.Errorf("error checking hotstore: %w", err) + } + + if has { + if s.txnMarkSet != nil { + hasMark, err := s.txnMarkSet.Has(c) + if err != nil { + log.Warnf("error checking markset: %s", err) + } else if hasMark { + toforget = append(toforget, c) + return errStopWalk + } + } else { + totrack = append(totrack, c) + return errStopWalk + } + } + + toreify = append(toreify, c) + return nil + }) + + if err != nil { + log.Warnf("error walking cold object for reification (cid: %s): %s", c, err) + return + } + + log.Debugf("reifying %d objects rooted at %s", len(toreify), c) + + batch := make([]blocks.Block, 0, len(toreify)) + for _, c := range toreify { + blk, err := s.cold.Get(s.ctx, c) + if err != nil { + log.Warnf("error retrieving cold object for reification (cid: %s): %s", c, err) + continue + } + + if err := s.checkClosing(); err != nil { + return + } + + batch = append(batch, blk) + } + + if len(batch) > 0 { + err = s.hot.PutMany(s.ctx, batch) + if err != nil { + log.Warnf("error reifying cold object (cid: %s): %s", c, err) + return + } + } + + if s.txnMarkSet != nil { + if len(toreify) > 0 { + s.markLiveRefs(toreify) + } + if len(totrack) > 0 { + s.markLiveRefs(totrack) + } + } else { + if len(toreify) > 0 { + s.trackTxnRefMany(toreify) + } + if len(totrack) > 0 { + s.trackTxnRefMany(totrack) + } + } +} From 268366e4467d179c2b9a2e4350e2b0078612803c Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 4 Feb 2022 16:07:58 +0200 Subject: [PATCH 03/82] cold object reification context option --- blockstore/context.go | 21 +++++++++++++++++++++ blockstore/splitstore/splitstore.go | 21 +++++++++++++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 blockstore/context.go diff --git a/blockstore/context.go b/blockstore/context.go new file mode 100644 index 000000000..61cb93b30 --- /dev/null +++ b/blockstore/context.go @@ -0,0 +1,21 @@ +package blockstore + +import ( + "context" +) + +type hotViewKey struct{} + +var hotView = hotViewKey{} + +// WithHotView constructs a new context with an option that provides a hint to the blockstore +// (e.g. the splitstore) that the object (and its ipld references) should be kept hot. +func WithHotView(ctx context.Context) context.Context { + return context.WithValue(ctx, hotView, struct{}{}) +} + +// GetHotView returns true if the hot view option is set in the context +func GetHotView(ctx context.Context) bool { + v := ctx.Value(hotView) + return v != nil +} diff --git a/blockstore/splitstore/splitstore.go b/blockstore/splitstore/splitstore.go index 0d7ad0779..5c2cf7203 100644 --- a/blockstore/splitstore/splitstore.go +++ b/blockstore/splitstore/splitstore.go @@ -274,7 +274,13 @@ func (s *SplitStore) Has(ctx context.Context, cid cid.Cid) (bool, error) { return true, nil } - return s.cold.Has(ctx, cid) + has, err = s.cold.Has(ctx, cid) + if has && bstore.GetHotView(ctx) { + s.reifyColdObject(cid) + } + + return has, err + } func (s *SplitStore) Get(ctx context.Context, cid cid.Cid) (blocks.Block, error) { @@ -318,8 +324,11 @@ func (s *SplitStore) Get(ctx context.Context, cid cid.Cid) (blocks.Block, error) blk, err = s.cold.Get(ctx, cid) if err == nil { - stats.Record(s.ctx, metrics.SplitstoreMiss.M(1)) + if bstore.GetHotView(ctx) { + s.reifyColdObject(cid) + } + stats.Record(s.ctx, metrics.SplitstoreMiss.M(1)) } return blk, err @@ -369,6 +378,10 @@ func (s *SplitStore) GetSize(ctx context.Context, cid cid.Cid) (int, error) { size, err = s.cold.GetSize(ctx, cid) if err == nil { + if bstore.GetHotView(ctx) { + s.reifyColdObject(cid) + } + stats.Record(s.ctx, metrics.SplitstoreMiss.M(1)) } return size, err @@ -546,6 +559,10 @@ func (s *SplitStore) View(ctx context.Context, cid cid.Cid, cb func([]byte) erro err = s.cold.View(ctx, cid, cb) if err == nil { + if bstore.GetHotView(ctx) { + s.reifyColdObject(cid) + } + stats.Record(s.ctx, metrics.SplitstoreMiss.M(1)) } return err From 929a05e8981c1b08b1e3619a2c7764258b933b6c Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 4 Feb 2022 16:16:34 +0200 Subject: [PATCH 04/82] add reification test --- blockstore/splitstore/splitstore_test.go | 130 +++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/blockstore/splitstore/splitstore_test.go b/blockstore/splitstore/splitstore_test.go index 27d58bf10..6b7e60e6c 100644 --- a/blockstore/splitstore/splitstore_test.go +++ b/blockstore/splitstore/splitstore_test.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io/ioutil" + "math/rand" "os" "sync" "sync/atomic" @@ -387,6 +388,135 @@ func TestSplitStoreSuppressCompactionNearUpgrade(t *testing.T) { } } +func testSplitStoreReification(t *testing.T, f func(context.Context, blockstore.Blockstore, cid.Cid) error) { + ds := dssync.MutexWrap(datastore.NewMapDatastore()) + hot := newMockStore() + cold := newMockStore() + + mkRandomBlock := func() blocks.Block { + data := make([]byte, 128) + _, err := rand.Read(data) + if err != nil { + t.Fatal(err) + } + + return blocks.NewBlock(data) + } + + block1 := mkRandomBlock() + block2 := mkRandomBlock() + block3 := mkRandomBlock() + + hdr := mock.MkBlock(nil, 0, 0) + hdr.Messages = block1.Cid() + hdr.ParentMessageReceipts = block2.Cid() + hdr.ParentStateRoot = block3.Cid() + block4, err := hdr.ToStorageBlock() + if err != nil { + t.Fatal(err) + } + + allBlocks := []blocks.Block{block1, block2, block3, block4} + for _, blk := range allBlocks { + err := cold.Put(context.Background(), blk) + if err != nil { + t.Fatal(err) + } + } + + path, err := ioutil.TempDir("", "splitstore.*") + if err != nil { + t.Fatal(err) + } + + t.Cleanup(func() { + _ = os.RemoveAll(path) + }) + + ss, err := Open(path, ds, hot, cold, &Config{MarkSetType: "map"}) + if err != nil { + t.Fatal(err) + } + defer ss.Close() //nolint + + ss.warmupEpoch = 1 + go ss.reifyOrchestrator() + + waitForReification := func() { + for { + ss.reifyMx.Lock() + ready := len(ss.reifyPend) == 0 && len(ss.reifyInProgress) == 0 + ss.reifyMx.Unlock() + + if ready { + return + } + + time.Sleep(time.Millisecond) + } + } + + // first access using the standard view + err = f(context.Background(), ss, block4.Cid()) + if err != nil { + t.Fatal(err) + } + + // nothing should be reified + waitForReification() + for _, blk := range allBlocks { + has, err := hot.Has(context.Background(), blk.Cid()) + if err != nil { + t.Fatal(err) + } + + if has { + t.Fatal("block unexpectedly reified") + } + } + + // now make the hot/reifying view and ensure access reifies + err = f(blockstore.WithHotView(context.Background()), ss, block4.Cid()) + if err != nil { + t.Fatal(err) + } + + // everything should be reified + waitForReification() + for i, blk := range allBlocks { + has, err := hot.Has(context.Background(), blk.Cid()) + if err != nil { + t.Fatal(err) + } + + if !has { + t.Fatalf("block%d was not reified", i+1) + } + } +} + +func TestSplitStoreReification(t *testing.T) { + t.Log("test reification with Has") + testSplitStoreReification(t, func(ctx context.Context, s blockstore.Blockstore, c cid.Cid) error { + _, err := s.Has(ctx, c) + return err + }) + t.Log("test reification with Get") + testSplitStoreReification(t, func(ctx context.Context, s blockstore.Blockstore, c cid.Cid) error { + _, err := s.Get(ctx, c) + return err + }) + t.Log("test reification with GetSize") + testSplitStoreReification(t, func(ctx context.Context, s blockstore.Blockstore, c cid.Cid) error { + _, err := s.GetSize(ctx, c) + return err + }) + t.Log("test reification with View") + testSplitStoreReification(t, func(ctx context.Context, s blockstore.Blockstore, c cid.Cid) error { + return s.View(ctx, c, func(_ []byte) error { return nil }) + }) +} + type mockChain struct { t testing.TB From 73c741f20ce4fff4bd15824dd75e2110bcfa4c8b Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 4 Feb 2022 16:19:28 +0200 Subject: [PATCH 05/82] reify cold objects on block validation/application --- chain/consensus/filcns/compute_state.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chain/consensus/filcns/compute_state.go b/chain/consensus/filcns/compute_state.go index f7f6284d0..34bc439ac 100644 --- a/chain/consensus/filcns/compute_state.go +++ b/chain/consensus/filcns/compute_state.go @@ -32,6 +32,7 @@ import ( /* inline-gen end */ + "github.com/filecoin-project/lotus/blockstore" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/builtin" @@ -106,7 +107,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, sm *stmgr.StateManager LookbackState: stmgr.LookbackStateGetterForTipset(sm, ts), } - return sm.VMConstructor()(ctx, vmopt) + return sm.VMConstructor()(blockstore.WithHotView(ctx), vmopt) } runCron := func(vmCron *vm.VM, epoch abi.ChainEpoch) error { From 9d92b6eb9213cfd968fda564bc63fb16aa5bad5d Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 4 Feb 2022 16:57:08 +0200 Subject: [PATCH 06/82] correctly wrap hotview in the context for compute_state --- chain/consensus/filcns/compute_state.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chain/consensus/filcns/compute_state.go b/chain/consensus/filcns/compute_state.go index 34bc439ac..44b792854 100644 --- a/chain/consensus/filcns/compute_state.go +++ b/chain/consensus/filcns/compute_state.go @@ -93,6 +93,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, sm *stmgr.StateManager partDone() }() + ctx = blockstore.WithHotView(ctx) makeVmWithBaseStateAndEpoch := func(base cid.Cid, e abi.ChainEpoch) (*vm.VM, error) { vmopt := &vm.VMOpts{ StateBase: base, @@ -107,7 +108,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, sm *stmgr.StateManager LookbackState: stmgr.LookbackStateGetterForTipset(sm, ts), } - return sm.VMConstructor()(blockstore.WithHotView(ctx), vmopt) + return sm.VMConstructor()(ctx, vmopt) } runCron := func(vmCron *vm.VM, epoch abi.ChainEpoch) error { From a32b7a32f3f91dadb8d44e7676f2cf40e0114a39 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 5 Feb 2022 20:00:15 +0200 Subject: [PATCH 07/82] directly mark objects in cold object reification --- blockstore/splitstore/splitstore_reify.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockstore/splitstore/splitstore_reify.go b/blockstore/splitstore/splitstore_reify.go index f60100d8a..ad51687c1 100644 --- a/blockstore/splitstore/splitstore_reify.go +++ b/blockstore/splitstore/splitstore_reify.go @@ -165,10 +165,10 @@ func (s *SplitStore) doReify(c cid.Cid) { if s.txnMarkSet != nil { if len(toreify) > 0 { - s.markLiveRefs(toreify) + s.txnMarkSet.MarkMany(toreify) } if len(totrack) > 0 { - s.markLiveRefs(totrack) + s.txnMarkSet.MarkMany(totrack) } } else { if len(toreify) > 0 { From 713edd565ccaf9483cbf812ee8585caee2188b2c Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 5 Feb 2022 21:30:53 +0200 Subject: [PATCH 08/82] fix lint --- blockstore/splitstore/splitstore_reify.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/blockstore/splitstore/splitstore_reify.go b/blockstore/splitstore/splitstore_reify.go index ad51687c1..3c65bbce8 100644 --- a/blockstore/splitstore/splitstore_reify.go +++ b/blockstore/splitstore/splitstore_reify.go @@ -165,10 +165,14 @@ func (s *SplitStore) doReify(c cid.Cid) { if s.txnMarkSet != nil { if len(toreify) > 0 { - s.txnMarkSet.MarkMany(toreify) + if err := s.txnMarkSet.MarkMany(toreify); err != nil { + log.Warnf("error marking reified objects: %s", err) + } } if len(totrack) > 0 { - s.txnMarkSet.MarkMany(totrack) + if err := s.txnMarkSet.MarkMany(totrack); err != nil { + log.Warnf("error marking tracked objects: %s", err) + } } } else { if len(toreify) > 0 { From 73208b8081a73f36eb459da9dae15192d3c989e2 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Tue, 8 Feb 2022 02:10:09 -0500 Subject: [PATCH 09/82] update the proofs in test plans` --- testplans/docker-images/proof-parameters.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/testplans/docker-images/proof-parameters.json b/testplans/docker-images/proof-parameters.json index c991c7e18..88bb0bfa3 100644 --- a/testplans/docker-images/proof-parameters.json +++ b/testplans/docker-images/proof-parameters.json @@ -30,23 +30,23 @@ "sector_size": 2048 }, "v28-empty-sector-update-merkletree-poseidon_hasher-8-8-0-3b7f44a9362e3985369454947bc94022e118211e49fd672d52bec1cbfd599d18.params": { - "cid": "QmeNqDvsvyam4vqwCkstbxgb9S7RZEUeBDrJvBWKcpFKr6", - "digest": "532b53883ed4f794cb9d0db583d0df59", + "cid": "QmNPc75iEfcahCwNKdqnWLtxnjspUGGR4iscjiz3wP3RtS", + "digest": "1b3cfd761a961543f9eb273e435a06a2", "sector_size": 34359738368 }, "v28-empty-sector-update-merkletree-poseidon_hasher-8-8-0-3b7f44a9362e3985369454947bc94022e118211e49fd672d52bec1cbfd599d18.vk": { - "cid": "QmdLWr6moLUPScJZwoBckWqAeJkrBPAJPNLz8mWAfTdmXH", - "digest": "46990eb1bf5159c394a10309f269c1b6", + "cid": "QmdFFUe1gcz9MMHc6YW8aoV48w4ckvcERjt7PkydQAMfCN", + "digest": "3a6941983754737fde880d29c7094905", "sector_size": 34359738368 }, "v28-empty-sector-update-merkletree-poseidon_hasher-8-8-2-102e1444a7e9a97ebf1e3d6855dcc77e66c011ea66f936d9b2c508f87f2f83a7.params": { - "cid": "QmdQsi9uFhxK9cGwuK4rHuwKQoHkz6upYTCz4UdLiy1vA2", - "digest": "4223c63dbd94de1538006a14f37179e3", + "cid": "QmUB6xTVjzBQGuDNeyJMrrJ1byk58vhPm8eY2Lv9pgwanp", + "digest": "1a392e7b759fb18e036c7559b5ece816", "sector_size": 68719476736 }, "v28-empty-sector-update-merkletree-poseidon_hasher-8-8-2-102e1444a7e9a97ebf1e3d6855dcc77e66c011ea66f936d9b2c508f87f2f83a7.vk": { - "cid": "QmPirFX9wX99iMGA6zFY2CvcrdcDkj73X4MP6DLduvpbk9", - "digest": "ce39b614d788d3aef26bac1b28521d94", + "cid": "Qmd794Jty7k26XJ8Eg4NDEks65Qk8G4GVfGkwqvymv8HAg", + "digest": "80e366df2f1011953c2d01c7b7c9ee8e", "sector_size": 68719476736 }, "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0170db1f394b35d995252228ee359194b13199d259380541dc529fb0099096b0.params": { From 03bc45a26a867a8e17ac9dd3d5c51571754d78f7 Mon Sep 17 00:00:00 2001 From: zenground0 Date: Tue, 8 Feb 2022 12:47:23 -0500 Subject: [PATCH 10/82] Update ci config to match auto gen --- .circleci/config.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 53611d565..5672130eb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -850,6 +850,11 @@ workflows: suite: itest-get_messages_in_ts target: "./itests/get_messages_in_ts_test.go" + - test: + name: test-itest-mempool + suite: itest-mempool + target: "./itests/mempool_test.go" + - test: name: test-itest-multisig suite: itest-multisig From 7d2810abbcc312ef970e56f3cb1363c80c12a586 Mon Sep 17 00:00:00 2001 From: Nikola Divic Date: Wed, 9 Feb 2022 19:54:45 +0100 Subject: [PATCH 11/82] test: don't parse err messages in messagepool_test Per @vyzo's feedback, we shouldn't parse err messages but figure out a way to do this smarter. I updated the code just check for error existence and @brdji should figure out what to do next. --- chain/messagepool/messagepool_test.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/chain/messagepool/messagepool_test.go b/chain/messagepool/messagepool_test.go index 86c3a49d1..d7f075aab 100644 --- a/chain/messagepool/messagepool_test.go +++ b/chain/messagepool/messagepool_test.go @@ -901,7 +901,8 @@ func TestMessageSignatureInvalid(t *testing.T) { } err = mp.Add(context.TODO(), sm) assert.Error(t, err) - assert.Contains(t, err.Error(), "invalid signature length") + // assert.Contains(t, err.Error(), "invalid signature length") + assert.Error(t, err) } } @@ -931,7 +932,8 @@ func TestAddMessageTwice(t *testing.T) { // try to add it twice err = mp.Add(context.TODO(), sm) - assert.Contains(t, err.Error(), "with nonce 0 already in mpool") + // assert.Contains(t, err.Error(), "with nonce 0 already in mpool") + assert.Error(t, err) } } @@ -961,7 +963,8 @@ func TestAddMessageTwiceNonceGap(t *testing.T) { // then try to add message again err = mp.Add(context.TODO(), sm) - assert.Contains(t, err.Error(), "unfulfilled nonce gap") + // assert.Contains(t, err.Error(), "unfulfilled nonce gap") + assert.Error(t, err) } } @@ -993,7 +996,8 @@ func TestAddMessageTwiceCidDiff(t *testing.T) { //stm: @CHAIN_MEMPOOL_PUSH_001 // then try to add message again err = mp.Add(context.TODO(), sm2) - assert.Contains(t, err.Error(), "replace by fee has too low GasPremium") + // assert.Contains(t, err.Error(), "replace by fee has too low GasPremium") + assert.Error(t, err) } } From 3c82ee49c0e7b0336c6dc505631ae8dd10d99cdf Mon Sep 17 00:00:00 2001 From: Marcel Telka Date: Thu, 10 Feb 2022 12:45:57 +0100 Subject: [PATCH 12/82] Allow lotus-miner info to complete without admin permission --- cmd/lotus-miner/info.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-miner/info.go b/cmd/lotus-miner/info.go index 1133908ca..46070ca96 100644 --- a/cmd/lotus-miner/info.go +++ b/cmd/lotus-miner/info.go @@ -126,7 +126,7 @@ func infoCmdAct(cctx *cli.Context) error { alerts, err := minerApi.LogAlerts(ctx) if err != nil { - return xerrors.Errorf("getting alerts: %w", err) + fmt.Printf("ERROR: getting alerts: %s\n", err) } activeAlerts := make([]alerting.Alert, 0) From 212f5ddb4f071811c57f8d1ff07839a0dd3909d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 2 Feb 2022 20:23:35 +0000 Subject: [PATCH 13/82] wip FinalizeReplicaUpdate --- api/api_worker.go | 1 + api/proxy_gen.go | 13 ++++ .../sector-storage/ffiwrapper/sealer_cgo.go | 76 +++++++++++++++++++ extern/sector-storage/manager.go | 68 +++++++++++++++++ extern/sector-storage/sealtasks/task.go | 18 +++-- extern/sector-storage/storiface/worker.go | 1 + extern/sector-storage/worker_local.go | 50 ++++++++---- .../storage-sealing/states_replica_update.go | 9 +++ go.mod | 2 +- go.sum | 2 + 10 files changed, 217 insertions(+), 23 deletions(-) diff --git a/api/api_worker.go b/api/api_worker.go index 68d8e7baf..ba50a9459 100644 --- a/api/api_worker.go +++ b/api/api_worker.go @@ -39,6 +39,7 @@ type Worker interface { SealCommit1(ctx context.Context, sector storage.SectorRef, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness, pieces []abi.PieceInfo, cids storage.SectorCids) (storiface.CallID, error) //perm:admin SealCommit2(ctx context.Context, sector storage.SectorRef, c1o storage.Commit1Out) (storiface.CallID, error) //perm:admin FinalizeSector(ctx context.Context, sector storage.SectorRef, keepUnsealed []storage.Range) (storiface.CallID, error) //perm:admin + FinalizeReplicaUpdate(ctx context.Context, sector storage.SectorRef, keepUnsealed []storage.Range) (storiface.CallID, error) //perm:admin ReplicaUpdate(ctx context.Context, sector storage.SectorRef, pieces []abi.PieceInfo) (storiface.CallID, error) //perm:admin ProveReplicaUpdate1(ctx context.Context, sector storage.SectorRef, sectorKey, newSealed, newUnsealed cid.Cid) (storiface.CallID, error) //perm:admin ProveReplicaUpdate2(ctx context.Context, sector storage.SectorRef, sectorKey, newSealed, newUnsealed cid.Cid, vanillaProofs storage.ReplicaVanillaProofs) (storiface.CallID, error) //perm:admin diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 32f59e0ec..a3b498e09 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -864,6 +864,8 @@ type WorkerStruct struct { Fetch func(p0 context.Context, p1 storage.SectorRef, p2 storiface.SectorFileType, p3 storiface.PathType, p4 storiface.AcquireMode) (storiface.CallID, error) `perm:"admin"` + FinalizeReplicaUpdate func(p0 context.Context, p1 storage.SectorRef, p2 []storage.Range) (storiface.CallID, error) `perm:"admin"` + FinalizeSector func(p0 context.Context, p1 storage.SectorRef, p2 []storage.Range) (storiface.CallID, error) `perm:"admin"` GenerateSectorKeyFromData func(p0 context.Context, p1 storage.SectorRef, p2 cid.Cid) (storiface.CallID, error) `perm:"admin"` @@ -4954,6 +4956,17 @@ func (s *WorkerStub) Fetch(p0 context.Context, p1 storage.SectorRef, p2 storifac return *new(storiface.CallID), ErrNotSupported } +func (s *WorkerStruct) FinalizeReplicaUpdate(p0 context.Context, p1 storage.SectorRef, p2 []storage.Range) (storiface.CallID, error) { + if s.Internal.FinalizeReplicaUpdate == nil { + return *new(storiface.CallID), ErrNotSupported + } + return s.Internal.FinalizeReplicaUpdate(p0, p1, p2) +} + +func (s *WorkerStub) FinalizeReplicaUpdate(p0 context.Context, p1 storage.SectorRef, p2 []storage.Range) (storiface.CallID, error) { + return *new(storiface.CallID), ErrNotSupported +} + func (s *WorkerStruct) FinalizeSector(p0 context.Context, p1 storage.SectorRef, p2 []storage.Range) (storiface.CallID, error) { if s.Internal.FinalizeSector == nil { return *new(storiface.CallID), ErrNotSupported diff --git a/extern/sector-storage/ffiwrapper/sealer_cgo.go b/extern/sector-storage/ffiwrapper/sealer_cgo.go index c35cefd56..cdda79a75 100644 --- a/extern/sector-storage/ffiwrapper/sealer_cgo.go +++ b/extern/sector-storage/ffiwrapper/sealer_cgo.go @@ -843,6 +843,82 @@ func (sb *Sealer) FinalizeSector(ctx context.Context, sector storage.SectorRef, return ffi.ClearCache(uint64(ssize), paths.Cache) } +func (sb *Sealer) FinalizeReplicaUpdate(ctx context.Context, sector storage.SectorRef, keepUnsealed []storage.Range) error { + ssize, err := sector.ProofType.SectorSize() + if err != nil { + return err + } + maxPieceSize := abi.PaddedPieceSize(ssize) + + if len(keepUnsealed) > 0 { // TODO dedupe with the above + + sr := partialfile.PieceRun(0, maxPieceSize) + + for _, s := range keepUnsealed { + si := &rlepluslazy.RunSliceIterator{} + if s.Offset != 0 { + si.Runs = append(si.Runs, rlepluslazy.Run{Val: false, Len: uint64(s.Offset)}) + } + si.Runs = append(si.Runs, rlepluslazy.Run{Val: true, Len: uint64(s.Size)}) + + var err error + sr, err = rlepluslazy.Subtract(sr, si) + if err != nil { + return err + } + } + + paths, done, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTUnsealed, 0, storiface.PathStorage) + if err != nil { + return xerrors.Errorf("acquiring sector cache path: %w", err) + } + defer done() + + pf, err := partialfile.OpenPartialFile(maxPieceSize, paths.Unsealed) + if err == nil { + var at uint64 + for sr.HasNext() { + r, err := sr.NextRun() + if err != nil { + _ = pf.Close() + return err + } + + offset := at + at += r.Len + if !r.Val { + continue + } + + err = pf.Free(storiface.PaddedByteIndex(abi.UnpaddedPieceSize(offset).Padded()), abi.UnpaddedPieceSize(r.Len).Padded()) + if err != nil { + _ = pf.Close() + return xerrors.Errorf("free partial file range: %w", err) + } + } + + if err := pf.Close(); err != nil { + return err + } + } else { + if !xerrors.Is(err, os.ErrNotExist) { + return xerrors.Errorf("opening partial file: %w", err) + } + } + + } + + paths, done, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTCache, 0, storiface.PathStorage) + if err != nil { + return xerrors.Errorf("acquiring sector cache path: %w", err) + } + defer done() + + return ffi.ClearCache(uint64(ssize), paths.Cache) + + // TODO: ^ above but for snapdeals +} + func (sb *Sealer) ReleaseUnsealed(ctx context.Context, sector storage.SectorRef, safeToFree []storage.Range) error { // This call is meant to mark storage as 'freeable'. Given that unsealing is // very expensive, we don't remove data as soon as we can - instead we only diff --git a/extern/sector-storage/manager.go b/extern/sector-storage/manager.go index 475c399e9..f1f84a057 100644 --- a/extern/sector-storage/manager.go +++ b/extern/sector-storage/manager.go @@ -577,6 +577,74 @@ func (m *Manager) FinalizeSector(ctx context.Context, sector storage.SectorRef, return nil } +func (m *Manager) FinalizeReplicaUpdate(ctx context.Context, sector storage.SectorRef, keepUnsealed []storage.Range) error { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + if err := m.index.StorageLock(ctx, sector.ID, storiface.FTNone, storiface.FTSealed|storiface.FTUnsealed|storiface.FTCache|storiface.FTUpdate|storiface.FTUpdateCache); err != nil { + return xerrors.Errorf("acquiring sector lock: %w", err) + } + + fts := storiface.FTUnsealed + { + unsealedStores, err := m.index.StorageFindSector(ctx, sector.ID, storiface.FTUnsealed, 0, false) + if err != nil { + return xerrors.Errorf("finding unsealed sector: %w", err) + } + + if len(unsealedStores) == 0 { // Is some edge-cases unsealed sector may not exist already, that's fine + fts = storiface.FTNone + } + } + + pathType := storiface.PathStorage + { + sealedStores, err := m.index.StorageFindSector(ctx, sector.ID, storiface.FTUpdate, 0, false) + if err != nil { + return xerrors.Errorf("finding sealed sector: %w", err) + } + + for _, store := range sealedStores { + if store.CanSeal { + pathType = storiface.PathSealing + break + } + } + } + + selector := newExistingSelector(m.index, sector.ID, storiface.FTCache|storiface.FTSealed|storiface.FTUpdate|storiface.FTUpdateCache, false) + + err := m.sched.Schedule(ctx, sector, sealtasks.TTFinalizeReplicaUpdate, selector, + m.schedFetch(sector, storiface.FTCache|storiface.FTSealed|storiface.FTUpdate|storiface.FTUpdateCache|fts, pathType, storiface.AcquireMove), + func(ctx context.Context, w Worker) error { + _, err := m.waitSimpleCall(ctx)(w.FinalizeReplicaUpdate(ctx, sector, keepUnsealed)) + return err + }) + if err != nil { + return err + } + + fetchSel := newAllocSelector(m.index, storiface.FTCache|storiface.FTSealed|storiface.FTUpdate|storiface.FTUpdateCache, storiface.PathStorage) + moveUnsealed := fts + { + if len(keepUnsealed) == 0 { + moveUnsealed = storiface.FTNone + } + } + + err = m.sched.Schedule(ctx, sector, sealtasks.TTFetch, fetchSel, + m.schedFetch(sector, storiface.FTCache|storiface.FTSealed|storiface.FTUpdate|storiface.FTUpdateCache|moveUnsealed, storiface.PathStorage, storiface.AcquireMove), + func(ctx context.Context, w Worker) error { + _, err := m.waitSimpleCall(ctx)(w.MoveStorage(ctx, sector, storiface.FTCache|storiface.FTSealed|storiface.FTUpdate|storiface.FTUpdateCache|moveUnsealed)) + return err + }) + if err != nil { + return xerrors.Errorf("moving sector to storage: %w", err) + } + + return nil +} + func (m *Manager) ReleaseUnsealed(ctx context.Context, sector storage.SectorRef, safeToFree []storage.Range) error { return nil } diff --git a/extern/sector-storage/sealtasks/task.go b/extern/sector-storage/sealtasks/task.go index f6104878b..89bfd3a88 100644 --- a/extern/sector-storage/sealtasks/task.go +++ b/extern/sector-storage/sealtasks/task.go @@ -14,10 +14,11 @@ const ( TTFetch TaskType = "seal/v0/fetch" TTUnseal TaskType = "seal/v0/unseal" - TTReplicaUpdate TaskType = "seal/v0/replicaupdate" - TTProveReplicaUpdate1 TaskType = "seal/v0/provereplicaupdate/1" - TTProveReplicaUpdate2 TaskType = "seal/v0/provereplicaupdate/2" - TTRegenSectorKey TaskType = "seal/v0/regensectorkey" + TTReplicaUpdate TaskType = "seal/v0/replicaupdate" + TTProveReplicaUpdate1 TaskType = "seal/v0/provereplicaupdate/1" + TTProveReplicaUpdate2 TaskType = "seal/v0/provereplicaupdate/2" + TTRegenSectorKey TaskType = "seal/v0/regensectorkey" + TTFinalizeReplicaUpdate TaskType = "seal/v0/finalize/replicaupdate" ) var order = map[TaskType]int{ @@ -48,10 +49,11 @@ var shortNames = map[TaskType]string{ TTFetch: "GET", TTUnseal: "UNS", - TTReplicaUpdate: "RU", - TTProveReplicaUpdate1: "PR1", - TTProveReplicaUpdate2: "PR2", - TTRegenSectorKey: "GSK", + TTReplicaUpdate: "RU", + TTProveReplicaUpdate1: "PR1", + TTProveReplicaUpdate2: "PR2", + TTRegenSectorKey: "GSK", + TTFinalizeReplicaUpdate: "FRU", } func (a TaskType) MuchLess(b TaskType) (bool, bool) { diff --git a/extern/sector-storage/storiface/worker.go b/extern/sector-storage/storiface/worker.go index 8bb6a256a..476854cd7 100644 --- a/extern/sector-storage/storiface/worker.go +++ b/extern/sector-storage/storiface/worker.go @@ -120,6 +120,7 @@ type WorkerCalls interface { SealCommit1(ctx context.Context, sector storage.SectorRef, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness, pieces []abi.PieceInfo, cids storage.SectorCids) (CallID, error) SealCommit2(ctx context.Context, sector storage.SectorRef, c1o storage.Commit1Out) (CallID, error) FinalizeSector(ctx context.Context, sector storage.SectorRef, keepUnsealed []storage.Range) (CallID, error) + FinalizeReplicaUpdate(ctx context.Context, sector storage.SectorRef, keepUnsealed []storage.Range) (CallID, error) ReleaseUnsealed(ctx context.Context, sector storage.SectorRef, safeToFree []storage.Range) (CallID, error) ReplicaUpdate(ctx context.Context, sector storage.SectorRef, pieces []abi.PieceInfo) (CallID, error) ProveReplicaUpdate1(ctx context.Context, sector storage.SectorRef, sectorKey, newSealed, newUnsealed cid.Cid) (CallID, error) diff --git a/extern/sector-storage/worker_local.go b/extern/sector-storage/worker_local.go index a5f5a0b9d..232877f2f 100644 --- a/extern/sector-storage/worker_local.go +++ b/extern/sector-storage/worker_local.go @@ -162,20 +162,21 @@ func (l *LocalWorker) ffiExec() (ffiwrapper.Storage, error) { type ReturnType string const ( - AddPiece ReturnType = "AddPiece" - SealPreCommit1 ReturnType = "SealPreCommit1" - SealPreCommit2 ReturnType = "SealPreCommit2" - SealCommit1 ReturnType = "SealCommit1" - SealCommit2 ReturnType = "SealCommit2" - FinalizeSector ReturnType = "FinalizeSector" - ReplicaUpdate ReturnType = "ReplicaUpdate" - ProveReplicaUpdate1 ReturnType = "ProveReplicaUpdate1" - ProveReplicaUpdate2 ReturnType = "ProveReplicaUpdate2" - GenerateSectorKey ReturnType = "GenerateSectorKey" - ReleaseUnsealed ReturnType = "ReleaseUnsealed" - MoveStorage ReturnType = "MoveStorage" - UnsealPiece ReturnType = "UnsealPiece" - Fetch ReturnType = "Fetch" + AddPiece ReturnType = "AddPiece" + SealPreCommit1 ReturnType = "SealPreCommit1" + SealPreCommit2 ReturnType = "SealPreCommit2" + SealCommit1 ReturnType = "SealCommit1" + SealCommit2 ReturnType = "SealCommit2" + FinalizeSector ReturnType = "FinalizeSector" + FinalizeReplicaUpdate ReturnType = "FinalizeReplicaUpdate" + ReplicaUpdate ReturnType = "ReplicaUpdate" + ProveReplicaUpdate1 ReturnType = "ProveReplicaUpdate1" + ProveReplicaUpdate2 ReturnType = "ProveReplicaUpdate2" + GenerateSectorKey ReturnType = "GenerateSectorKey" + ReleaseUnsealed ReturnType = "ReleaseUnsealed" + MoveStorage ReturnType = "MoveStorage" + UnsealPiece ReturnType = "UnsealPiece" + Fetch ReturnType = "Fetch" ) // in: func(WorkerReturn, context.Context, CallID, err string) @@ -456,6 +457,27 @@ func (l *LocalWorker) FinalizeSector(ctx context.Context, sector storage.SectorR }) } +func (l *LocalWorker) FinalizeReplicaUpdate(ctx context.Context, sector storage.SectorRef, keepUnsealed []storage.Range) (storiface.CallID, error) { + sb, err := l.executor() + if err != nil { + return storiface.UndefCall, err + } + + return l.asyncCall(ctx, sector, FinalizeReplicaUpdate, func(ctx context.Context, ci storiface.CallID) (interface{}, error) { + if err := sb.FinalizeReplicaUpdate(ctx, sector, keepUnsealed); err != nil { + return nil, xerrors.Errorf("finalizing sector: %w", err) + } + + if len(keepUnsealed) == 0 { + if err := l.storage.Remove(ctx, sector.ID, storiface.FTUnsealed, true, nil); err != nil { + return nil, xerrors.Errorf("removing unsealed data: %w", err) + } + } + + return nil, err + }) +} + func (l *LocalWorker) ReleaseUnsealed(ctx context.Context, sector storage.SectorRef, safeToFree []storage.Range) (storiface.CallID, error) { return storiface.UndefCall, xerrors.Errorf("implement me") } diff --git a/extern/storage-sealing/states_replica_update.go b/extern/storage-sealing/states_replica_update.go index aecd3512a..6222d49db 100644 --- a/extern/storage-sealing/states_replica_update.go +++ b/extern/storage-sealing/states_replica_update.go @@ -213,6 +213,15 @@ func (m *Sealing) handleReplicaUpdateWait(ctx statemachine.Context, sector Secto } func (m *Sealing) handleFinalizeReplicaUpdate(ctx statemachine.Context, sector SectorInfo) error { + cfg, err := m.getConfig() + if err != nil { + return xerrors.Errorf("getting sealing config: %w", err) + } + + if err := m.sealer.FinalizeReplicaUpdate(sector.sealingCtx(ctx.Context()), m.minerSector(sector.SectorType, sector.SectorNumber), sector.keepUnsealedRanges(false, cfg.AlwaysKeepUnsealedCopy)); err != nil { + return ctx.Send(SectorFinalizeFailed{xerrors.Errorf("finalize sector: %w", err)}) + } + return ctx.Send(SectorFinalized{}) } diff --git a/go.mod b/go.mod index 67bd4ca77..262763d9d 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,7 @@ require ( github.com/filecoin-project/specs-actors/v5 v5.0.4 github.com/filecoin-project/specs-actors/v6 v6.0.1 github.com/filecoin-project/specs-actors/v7 v7.0.0-rc1 - github.com/filecoin-project/specs-storage v0.1.1-0.20211228030229-6d460d25a0c9 + github.com/filecoin-project/specs-storage v0.1.1-0.20220202201749-ae62d2332aa8 github.com/filecoin-project/test-vectors/schema v0.0.5 github.com/gbrlsnchs/jwt/v3 v3.0.1 github.com/gdamore/tcell/v2 v2.2.0 diff --git a/go.sum b/go.sum index cfc8e50eb..079604134 100644 --- a/go.sum +++ b/go.sum @@ -401,6 +401,8 @@ github.com/filecoin-project/specs-actors/v7 v7.0.0-rc1 h1:FuDaXIbcw2hRsFI8SDTmsG github.com/filecoin-project/specs-actors/v7 v7.0.0-rc1/go.mod h1:TA5FwCna+Yi36POaT7SLKXsgEDvJwc0V/L6ZsO19B9M= github.com/filecoin-project/specs-storage v0.1.1-0.20211228030229-6d460d25a0c9 h1:oUYOvF7EvdXS0Zmk9mNkaB6Bu0l+WXBYPzVodKMiLug= github.com/filecoin-project/specs-storage v0.1.1-0.20211228030229-6d460d25a0c9/go.mod h1:Tb88Zq+IBJbvAn3mS89GYj3jdRThBTE/771HCVZdRJU= +github.com/filecoin-project/specs-storage v0.1.1-0.20220202201749-ae62d2332aa8 h1:lHg1G44FX6LNuIcGJWE6ty4dH+WoFIA2UIfGGV06Hpg= +github.com/filecoin-project/specs-storage v0.1.1-0.20220202201749-ae62d2332aa8/go.mod h1:Tb88Zq+IBJbvAn3mS89GYj3jdRThBTE/771HCVZdRJU= github.com/filecoin-project/test-vectors/schema v0.0.5 h1:w3zHQhzM4pYxJDl21avXjOKBLF8egrvwUwjpT8TquDg= github.com/filecoin-project/test-vectors/schema v0.0.5/go.mod h1:iQ9QXLpYWL3m7warwvK1JC/pTri8mnfEmKygNDqqY6E= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= From 9a117fa39a38ecc7bd3078a7d3b031f66f8ab3ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 3 Feb 2022 12:51:59 +0000 Subject: [PATCH 14/82] try ClearCache for update cache --- build/openrpc/full.json.gz | Bin 25709 -> 25710 bytes build/openrpc/miner.json.gz | Bin 11709 -> 11709 bytes build/openrpc/worker.json.gz | Bin 3805 -> 3850 bytes documentation/en/api-v0-methods-worker.md | 31 ++++++ .../sector-storage/ffiwrapper/sealer_cgo.go | 90 +++++++----------- extern/sector-storage/mock/mock.go | 4 + itests/ccupgrade_test.go | 6 +- 7 files changed, 70 insertions(+), 61 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 9d481a1f121c4bc3066fbd8381c57b71d1f6378a..a83bcb256f711631e4b48a860cfc10cefea9ec8a 100644 GIT binary patch delta 17625 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 diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index ffc06e07141d49cc1a74e5fe20023441453302d1..f1955f2595ab5f8254c49e955286d3e72726cf95 100644 GIT binary patch delta 11622 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 delta 11622 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 diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 7d3d3760d647b511751929e1888f1ab41ce72f33..faa7eacced8fbdc143dd7e790a904530d8883116 100644 GIT binary patch literal 3850 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 z 0 { // TODO dedupe with the above + if err := sb.freeUnsealed(ctx, sector, keepUnsealed); err != nil { + return err + } - sr := partialfile.PieceRun(0, maxPieceSize) - - for _, s := range keepUnsealed { - si := &rlepluslazy.RunSliceIterator{} - if s.Offset != 0 { - si.Runs = append(si.Runs, rlepluslazy.Run{Val: false, Len: uint64(s.Offset)}) - } - si.Runs = append(si.Runs, rlepluslazy.Run{Val: true, Len: uint64(s.Size)}) - - var err error - sr, err = rlepluslazy.Subtract(sr, si) - if err != nil { - return err - } - } - - paths, done, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTUnsealed, 0, storiface.PathStorage) + { + paths, done, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTCache, 0, storiface.PathStorage) if err != nil { return xerrors.Errorf("acquiring sector cache path: %w", err) } defer done() - pf, err := partialfile.OpenPartialFile(maxPieceSize, paths.Unsealed) - if err == nil { - var at uint64 - for sr.HasNext() { - r, err := sr.NextRun() - if err != nil { - _ = pf.Close() - return err - } - - offset := at - at += r.Len - if !r.Val { - continue - } - - err = pf.Free(storiface.PaddedByteIndex(abi.UnpaddedPieceSize(offset).Padded()), abi.UnpaddedPieceSize(r.Len).Padded()) - if err != nil { - _ = pf.Close() - return xerrors.Errorf("free partial file range: %w", err) - } - } - - if err := pf.Close(); err != nil { - return err - } - } else { - if !xerrors.Is(err, os.ErrNotExist) { - return xerrors.Errorf("opening partial file: %w", err) - } + if err := ffi.ClearCache(uint64(ssize), paths.Cache); err != nil { + return xerrors.Errorf("clear cache: %w", err) } - } - paths, done, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTCache, 0, storiface.PathStorage) - if err != nil { - return xerrors.Errorf("acquiring sector cache path: %w", err) + { + paths, done, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTUpdateCache, 0, storiface.PathStorage) + if err != nil { + return xerrors.Errorf("acquiring sector cache path: %w", err) + } + defer done() + + if err := ffi.ClearCache(uint64(ssize), paths.UpdateCache); err != nil { + return xerrors.Errorf("clear cache: %w", err) + } } - defer done() - return ffi.ClearCache(uint64(ssize), paths.Cache) - - // TODO: ^ above but for snapdeals + return nil } func (sb *Sealer) ReleaseUnsealed(ctx context.Context, sector storage.SectorRef, safeToFree []storage.Range) error { diff --git a/extern/sector-storage/mock/mock.go b/extern/sector-storage/mock/mock.go index c99af89e7..9f7c17f2c 100644 --- a/extern/sector-storage/mock/mock.go +++ b/extern/sector-storage/mock/mock.go @@ -477,6 +477,10 @@ func (mgr *SectorMgr) FinalizeSector(context.Context, storage.SectorRef, []stora return nil } +func (mgr *SectorMgr) FinalizeReplicaUpdate(context.Context, storage.SectorRef, []storage.Range) error { + return nil +} + func (mgr *SectorMgr) ReleaseUnsealed(ctx context.Context, sector storage.SectorRef, safeToFree []storage.Range) error { return nil } diff --git a/itests/ccupgrade_test.go b/itests/ccupgrade_test.go index b63e96d24..4ce1b3427 100644 --- a/itests/ccupgrade_test.go +++ b/itests/ccupgrade_test.go @@ -27,12 +27,12 @@ func TestCCUpgrade(t *testing.T) { } { height := height // make linters happy by copying t.Run(fmt.Sprintf("upgrade-%d", height), func(t *testing.T) { - runTestCCUpgrade(t, height) + runTestCCUpgrade(t) }) } } -func runTestCCUpgrade(t *testing.T, upgradeHeight abi.ChainEpoch) *kit.TestFullNode { +func runTestCCUpgrade(t *testing.T) *kit.TestFullNode { ctx := context.Background() blockTime := 1 * time.Millisecond @@ -126,7 +126,7 @@ func TestCCUpgradeAndPoSt(t *testing.T) { kit.QuietMiningLogs() t.Run("upgrade and then post", func(t *testing.T) { ctx := context.Background() - n := runTestCCUpgrade(t, 100) + n := runTestCCUpgrade(t) ts, err := n.ChainHead(ctx) require.NoError(t, err) start := ts.Height() From f2a6c9458c3b6eb800d9b983e8ae41efde7dff64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 3 Feb 2022 13:04:09 +0000 Subject: [PATCH 15/82] mod tidy --- go.sum | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.sum b/go.sum index 079604134..c83e5568b 100644 --- a/go.sum +++ b/go.sum @@ -399,8 +399,6 @@ github.com/filecoin-project/specs-actors/v7 v7.0.0-20211117170924-fd07a4c7dff9/g github.com/filecoin-project/specs-actors/v7 v7.0.0-20211222192039-c83bea50c402/go.mod h1:p6LIOFezA1rgRLMewbvdi3Pp6SAu+q9FtJ9CAleSjrE= github.com/filecoin-project/specs-actors/v7 v7.0.0-rc1 h1:FuDaXIbcw2hRsFI8SDTmsGGCE+NumpF6aiBoU/2X5W4= github.com/filecoin-project/specs-actors/v7 v7.0.0-rc1/go.mod h1:TA5FwCna+Yi36POaT7SLKXsgEDvJwc0V/L6ZsO19B9M= -github.com/filecoin-project/specs-storage v0.1.1-0.20211228030229-6d460d25a0c9 h1:oUYOvF7EvdXS0Zmk9mNkaB6Bu0l+WXBYPzVodKMiLug= -github.com/filecoin-project/specs-storage v0.1.1-0.20211228030229-6d460d25a0c9/go.mod h1:Tb88Zq+IBJbvAn3mS89GYj3jdRThBTE/771HCVZdRJU= github.com/filecoin-project/specs-storage v0.1.1-0.20220202201749-ae62d2332aa8 h1:lHg1G44FX6LNuIcGJWE6ty4dH+WoFIA2UIfGGV06Hpg= github.com/filecoin-project/specs-storage v0.1.1-0.20220202201749-ae62d2332aa8/go.mod h1:Tb88Zq+IBJbvAn3mS89GYj3jdRThBTE/771HCVZdRJU= github.com/filecoin-project/test-vectors/schema v0.0.5 h1:w3zHQhzM4pYxJDl21avXjOKBLF8egrvwUwjpT8TquDg= From 9a01964f5ffce15b04ec30bb41170a3f902a366e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 8 Feb 2022 14:28:13 +0100 Subject: [PATCH 16/82] itests: Print API info env vars in ThroughRPC tests --- itests/ccupgrade_test.go | 13 ++----------- itests/kit/rpc.go | 5 ++++- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/itests/ccupgrade_test.go b/itests/ccupgrade_test.go index 4ce1b3427..6f4896c21 100644 --- a/itests/ccupgrade_test.go +++ b/itests/ccupgrade_test.go @@ -20,23 +20,14 @@ import ( func TestCCUpgrade(t *testing.T) { kit.QuietMiningLogs() - for _, height := range []abi.ChainEpoch{ - -1, // before - 162, // while sealing - 560, // after upgrade deal - } { - height := height // make linters happy by copying - t.Run(fmt.Sprintf("upgrade-%d", height), func(t *testing.T) { - runTestCCUpgrade(t) - }) - } + runTestCCUpgrade(t) } func runTestCCUpgrade(t *testing.T) *kit.TestFullNode { ctx := context.Background() blockTime := 1 * time.Millisecond - client, miner, ens := kit.EnsembleMinimal(t, kit.GenesisNetworkVersion(network.Version15)) + client, miner, ens := kit.EnsembleMinimal(t, kit.GenesisNetworkVersion(network.Version15), kit.ThroughRPC()) ens.InterconnectAll().BeginMiningMustPost(blockTime) maddr, err := miner.ActorAddress(ctx) diff --git a/itests/kit/rpc.go b/itests/kit/rpc.go index 35153eb64..55521fb66 100644 --- a/itests/kit/rpc.go +++ b/itests/kit/rpc.go @@ -39,6 +39,7 @@ func fullRpc(t *testing.T, f *TestFullNode) *TestFullNode { require.NoError(t, err) srv, maddr := CreateRPCServer(t, handler, l) + fmt.Printf("FULLNODE RPC ENV FOR CLI DEBUGGING `export FULLNODE_API_INFO=%s`\n", "ws://"+srv.Listener.Addr().String()) cl, stop, err := client.NewFullNodeRPCV1(context.Background(), "ws://"+srv.Listener.Addr().String()+"/rpc/v1", nil) require.NoError(t, err) @@ -54,7 +55,9 @@ func minerRpc(t *testing.T, m *TestMiner) *TestMiner { srv, maddr := CreateRPCServer(t, handler, m.RemoteListener) - fmt.Println("creating RPC server for", m.ActorAddr, "at: ", srv.Listener.Addr().String()) + fmt.Printf("creating RPC server for %s at %s\n", m.ActorAddr, srv.Listener.Addr().String()) + fmt.Printf("SP RPC ENV FOR CLI DEBUGGING `export STORAGE_API_INFO=%s`\n", "ws://"+srv.Listener.Addr().String()) + url := "ws://" + srv.Listener.Addr().String() + "/rpc/v0" cl, stop, err := client.NewStorageMinerRPCV0(context.Background(), url, nil) require.NoError(t, err) From 53e1f75c2cf20eedf84fbd581291991f7f4f8519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 8 Feb 2022 15:06:42 +0100 Subject: [PATCH 17/82] Add FinalizeReplicaUpdate into some more places --- api/api_storage.go | 1 + api/proxy_gen.go | 13 ++++++++++ cmd/lotus-seal-worker/main.go | 2 +- extern/sector-storage/manager.go | 6 ++++- extern/sector-storage/mock/mock.go | 4 ++++ extern/sector-storage/storiface/worker.go | 1 + extern/sector-storage/worker_local.go | 29 ++++++++++++----------- extern/sector-storage/worker_tracked.go | 4 ++++ itests/kit/rpc.go | 2 +- 9 files changed, 45 insertions(+), 17 deletions(-) diff --git a/api/api_storage.go b/api/api_storage.go index 58fb70635..398921e40 100644 --- a/api/api_storage.go +++ b/api/api_storage.go @@ -132,6 +132,7 @@ type StorageMiner interface { ReturnProveReplicaUpdate1(ctx context.Context, callID storiface.CallID, vanillaProofs storage.ReplicaVanillaProofs, err *storiface.CallError) error //perm:admin retry:true ReturnProveReplicaUpdate2(ctx context.Context, callID storiface.CallID, proof storage.ReplicaUpdateProof, err *storiface.CallError) error //perm:admin retry:true ReturnGenerateSectorKeyFromData(ctx context.Context, callID storiface.CallID, err *storiface.CallError) error //perm:admin retry:true + ReturnFinalizeReplicaUpdate(ctx context.Context, callID storiface.CallID, err *storiface.CallError) error //perm:admin retry:true ReturnReleaseUnsealed(ctx context.Context, callID storiface.CallID, err *storiface.CallError) error //perm:admin retry:true ReturnMoveStorage(ctx context.Context, callID storiface.CallID, err *storiface.CallError) error //perm:admin retry:true ReturnUnsealPiece(ctx context.Context, callID storiface.CallID, err *storiface.CallError) error //perm:admin retry:true diff --git a/api/proxy_gen.go b/api/proxy_gen.go index a3b498e09..0eecb27b9 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -715,6 +715,8 @@ type StorageMinerStruct struct { ReturnFetch func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` + ReturnFinalizeReplicaUpdate func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` + ReturnFinalizeSector func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` ReturnGenerateSectorKeyFromData func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` @@ -4230,6 +4232,17 @@ func (s *StorageMinerStub) ReturnFetch(p0 context.Context, p1 storiface.CallID, return ErrNotSupported } +func (s *StorageMinerStruct) ReturnFinalizeReplicaUpdate(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error { + if s.Internal.ReturnFinalizeReplicaUpdate == nil { + return ErrNotSupported + } + return s.Internal.ReturnFinalizeReplicaUpdate(p0, p1, p2) +} + +func (s *StorageMinerStub) ReturnFinalizeReplicaUpdate(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error { + return ErrNotSupported +} + func (s *StorageMinerStruct) ReturnFinalizeSector(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error { if s.Internal.ReturnFinalizeSector == nil { return ErrNotSupported diff --git a/cmd/lotus-seal-worker/main.go b/cmd/lotus-seal-worker/main.go index 2e326e9c7..84ff1ccdd 100644 --- a/cmd/lotus-seal-worker/main.go +++ b/cmd/lotus-seal-worker/main.go @@ -261,7 +261,7 @@ var runCmd = &cli.Command{ var taskTypes []sealtasks.TaskType - taskTypes = append(taskTypes, sealtasks.TTFetch, sealtasks.TTCommit1, sealtasks.TTProveReplicaUpdate1, sealtasks.TTFinalize) + taskTypes = append(taskTypes, sealtasks.TTFetch, sealtasks.TTCommit1, sealtasks.TTProveReplicaUpdate1, sealtasks.TTFinalize, sealtasks.TTFinalizeReplicaUpdate) if cctx.Bool("addpiece") { taskTypes = append(taskTypes, sealtasks.TTAddPiece) diff --git a/extern/sector-storage/manager.go b/extern/sector-storage/manager.go index f1f84a057..fcbfa2e69 100644 --- a/extern/sector-storage/manager.go +++ b/extern/sector-storage/manager.go @@ -146,7 +146,7 @@ func New(ctx context.Context, lstor *stores.Local, stor *stores.Remote, ls store go m.sched.runSched() localTasks := []sealtasks.TaskType{ - sealtasks.TTCommit1, sealtasks.TTProveReplicaUpdate1, sealtasks.TTFinalize, sealtasks.TTFetch, + sealtasks.TTCommit1, sealtasks.TTProveReplicaUpdate1, sealtasks.TTFinalize, sealtasks.TTFetch, sealtasks.TTFinalizeReplicaUpdate, } if sc.AllowAddPiece { localTasks = append(localTasks, sealtasks.TTAddPiece) @@ -943,6 +943,10 @@ func (m *Manager) ReturnProveReplicaUpdate2(ctx context.Context, callID storifac return m.returnResult(ctx, callID, proof, err) } +func (m *Manager) ReturnFinalizeReplicaUpdate(ctx context.Context, callID storiface.CallID, err *storiface.CallError) error { + return m.returnResult(ctx, callID, nil, err) +} + func (m *Manager) ReturnGenerateSectorKeyFromData(ctx context.Context, callID storiface.CallID, err *storiface.CallError) error { return m.returnResult(ctx, callID, nil, err) } diff --git a/extern/sector-storage/mock/mock.go b/extern/sector-storage/mock/mock.go index 9f7c17f2c..771265176 100644 --- a/extern/sector-storage/mock/mock.go +++ b/extern/sector-storage/mock/mock.go @@ -581,6 +581,10 @@ func (mgr *SectorMgr) ReturnGenerateSectorKeyFromData(ctx context.Context, callI panic("not supported") } +func (mgr *SectorMgr) ReturnFinalizeReplicaUpdate(ctx context.Context, callID storiface.CallID, err *storiface.CallError) error { + panic("not supported") +} + func (m mockVerifProver) VerifySeal(svi proof.SealVerifyInfo) (bool, error) { plen, err := svi.SealProof.ProofSize() if err != nil { diff --git a/extern/sector-storage/storiface/worker.go b/extern/sector-storage/storiface/worker.go index 476854cd7..eedbc8207 100644 --- a/extern/sector-storage/storiface/worker.go +++ b/extern/sector-storage/storiface/worker.go @@ -183,6 +183,7 @@ type WorkerReturn interface { ReturnProveReplicaUpdate1(ctx context.Context, callID CallID, proofs storage.ReplicaVanillaProofs, err *CallError) error ReturnProveReplicaUpdate2(ctx context.Context, callID CallID, proof storage.ReplicaUpdateProof, err *CallError) error ReturnGenerateSectorKeyFromData(ctx context.Context, callID CallID, err *CallError) error + ReturnFinalizeReplicaUpdate(ctx context.Context, callID CallID, err *CallError) error ReturnMoveStorage(ctx context.Context, callID CallID, err *CallError) error ReturnUnsealPiece(ctx context.Context, callID CallID, err *CallError) error ReturnReadPiece(ctx context.Context, callID CallID, ok bool, err *CallError) error diff --git a/extern/sector-storage/worker_local.go b/extern/sector-storage/worker_local.go index 232877f2f..572d482ed 100644 --- a/extern/sector-storage/worker_local.go +++ b/extern/sector-storage/worker_local.go @@ -214,20 +214,21 @@ func rfunc(in interface{}) func(context.Context, storiface.CallID, storiface.Wor } var returnFunc = map[ReturnType]func(context.Context, storiface.CallID, storiface.WorkerReturn, interface{}, *storiface.CallError) error{ - AddPiece: rfunc(storiface.WorkerReturn.ReturnAddPiece), - SealPreCommit1: rfunc(storiface.WorkerReturn.ReturnSealPreCommit1), - SealPreCommit2: rfunc(storiface.WorkerReturn.ReturnSealPreCommit2), - SealCommit1: rfunc(storiface.WorkerReturn.ReturnSealCommit1), - SealCommit2: rfunc(storiface.WorkerReturn.ReturnSealCommit2), - FinalizeSector: rfunc(storiface.WorkerReturn.ReturnFinalizeSector), - ReleaseUnsealed: rfunc(storiface.WorkerReturn.ReturnReleaseUnsealed), - ReplicaUpdate: rfunc(storiface.WorkerReturn.ReturnReplicaUpdate), - ProveReplicaUpdate1: rfunc(storiface.WorkerReturn.ReturnProveReplicaUpdate1), - ProveReplicaUpdate2: rfunc(storiface.WorkerReturn.ReturnProveReplicaUpdate2), - GenerateSectorKey: rfunc(storiface.WorkerReturn.ReturnGenerateSectorKeyFromData), - MoveStorage: rfunc(storiface.WorkerReturn.ReturnMoveStorage), - UnsealPiece: rfunc(storiface.WorkerReturn.ReturnUnsealPiece), - Fetch: rfunc(storiface.WorkerReturn.ReturnFetch), + AddPiece: rfunc(storiface.WorkerReturn.ReturnAddPiece), + SealPreCommit1: rfunc(storiface.WorkerReturn.ReturnSealPreCommit1), + SealPreCommit2: rfunc(storiface.WorkerReturn.ReturnSealPreCommit2), + SealCommit1: rfunc(storiface.WorkerReturn.ReturnSealCommit1), + SealCommit2: rfunc(storiface.WorkerReturn.ReturnSealCommit2), + FinalizeSector: rfunc(storiface.WorkerReturn.ReturnFinalizeSector), + ReleaseUnsealed: rfunc(storiface.WorkerReturn.ReturnReleaseUnsealed), + ReplicaUpdate: rfunc(storiface.WorkerReturn.ReturnReplicaUpdate), + ProveReplicaUpdate1: rfunc(storiface.WorkerReturn.ReturnProveReplicaUpdate1), + ProveReplicaUpdate2: rfunc(storiface.WorkerReturn.ReturnProveReplicaUpdate2), + GenerateSectorKey: rfunc(storiface.WorkerReturn.ReturnGenerateSectorKeyFromData), + FinalizeReplicaUpdate: rfunc(storiface.WorkerReturn.ReturnFinalizeReplicaUpdate), + MoveStorage: rfunc(storiface.WorkerReturn.ReturnMoveStorage), + UnsealPiece: rfunc(storiface.WorkerReturn.ReturnUnsealPiece), + Fetch: rfunc(storiface.WorkerReturn.ReturnFetch), } func (l *LocalWorker) asyncCall(ctx context.Context, sector storage.SectorRef, rt ReturnType, work func(ctx context.Context, ci storiface.CallID) (interface{}, error)) (storiface.CallID, error) { diff --git a/extern/sector-storage/worker_tracked.go b/extern/sector-storage/worker_tracked.go index a1c647422..91da0fee5 100644 --- a/extern/sector-storage/worker_tracked.go +++ b/extern/sector-storage/worker_tracked.go @@ -215,4 +215,8 @@ func (t *trackedWorker) ProveReplicaUpdate2(ctx context.Context, sector storage. }) } +func (t *trackedWorker) FinalizeReplicaUpdate(ctx context.Context, sector storage.SectorRef, keepUnsealed []storage.Range) (storiface.CallID, error) { + return t.tracker.track(ctx, t.execute, t.wid, t.workerInfo, sector, sealtasks.TTFinalizeReplicaUpdate, func() (storiface.CallID, error) { return t.Worker.FinalizeReplicaUpdate(ctx, sector, keepUnsealed) }) +} + var _ Worker = &trackedWorker{} diff --git a/itests/kit/rpc.go b/itests/kit/rpc.go index 55521fb66..61c8a7b23 100644 --- a/itests/kit/rpc.go +++ b/itests/kit/rpc.go @@ -56,7 +56,7 @@ func minerRpc(t *testing.T, m *TestMiner) *TestMiner { srv, maddr := CreateRPCServer(t, handler, m.RemoteListener) fmt.Printf("creating RPC server for %s at %s\n", m.ActorAddr, srv.Listener.Addr().String()) - fmt.Printf("SP RPC ENV FOR CLI DEBUGGING `export STORAGE_API_INFO=%s`\n", "ws://"+srv.Listener.Addr().String()) + fmt.Printf("SP RPC ENV FOR CLI DEBUGGING `export MINER_API_INFO=%s`\n", "ws://"+srv.Listener.Addr().String()) url := "ws://" + srv.Listener.Addr().String() + "/rpc/v0" cl, stop, err := client.NewStorageMinerRPCV0(context.Background(), url, nil) From 229cfafa8d1ed0ce863fa1357f69e942c5118e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 8 Feb 2022 15:29:30 +0100 Subject: [PATCH 18/82] make gen --- build/openrpc/full.json.gz | Bin 25710 -> 25709 bytes build/openrpc/miner.json.gz | Bin 11709 -> 11748 bytes build/openrpc/worker.json.gz | Bin 3850 -> 3850 bytes documentation/en/api-v0-methods-miner.md | 25 +++++++++++++++++++++++ itests/ccupgrade_test.go | 5 +++++ itests/kit/node_miner.go | 6 ++++-- 6 files changed, 34 insertions(+), 2 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index a83bcb256f711631e4b48a860cfc10cefea9ec8a..991332240196cc42304a6500f2d2697927a173bc 100644 GIT binary patch delta 17166 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 GM*;x(9YO5? delta 8274 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 diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index faa7eacced8fbdc143dd7e790a904530d8883116..c5c4ffdfac03d4a2e55fb88459ebff87c3c671d1 100644 GIT binary patch delta 2388 zcmV-a39I&s9*Q2Y5d#9&#*-5RK?3!alU4(4R}Ok5@W8^*wtk?`$g?d2-g+jayz5_u zb?`2RZPGtOyx;}_MGQ0s3QAhL0SN*ev_gT~01h%zeb{0aN7w!_pA$r@^=-KFbYRCE zxHKmGs_?6?&94sClNkgi0!S;9K?Ll7W4+j$U*oyU>9|>u;!_OSBZDmGJ}=C^S8}+! z>bRV4%xNQs9EzCRV-!-Esu+gb;Swrm8>y6BP;b8Q%>2e+nDmpW;4e0pi#&g z(=)P?HKu1{C2vgCBaCUznx7V#9zmYpm=1c%pr;PC0+)LPA3JJFfi=~CRJEt7eSN4Z zCCn&FC3Y^0`5EN+(R8gp9%>_PFe)&q16^13zB1NzB`Y0DPZ{fj{-9=l2BTqbtPcl6 zZE(01n`_@LWxBQfLfSMeUE3k*!tB__5>^X%rWntY% zdiym)f&%FZP$j{*u^VdOiC~K3X75 z!ZYFzXeAb2+j5M5PaH9A7Qw#wrB`vpw5>->Q{Yut)V4^iu&BbK3XA$8Eoy+-U&55O zVeeI(3J|9Po-w52R6v_f1+3ScH`aHTVxKk_w&R=q>wOUV(2* zgyVd7*o)`^LZMIjo*OMv>20HP!;}0{;mMjx4!em~0_=h0l=f%76^wN@l71Fyd=8PoX-V#~H+h;7KxP+2`4R8|Y1 zGHalK%dOCQ0hfPr*Dv7m3m6#zm)klr{ZijkhtR-d7VSU!eRDZso z2oiJ~9xO*DC&JH^KIQAA8PZJw^?VD(>7-VFDQX*lxp<#c_)pV45J@94t+@PJq%j=zhyUX-bM42c;pUtM~xuzD2d%`A-2+1w<7P^=L0TE_p)# z20{BO41&#jlrd3~&x1zL4%(l?Q@0oq67J!$UadG~b_ z&1lLWr+Z55;|kBb$1~@(2-;rWE;du^UiD~ZZ> zSU9%h&>QM8BCnHuD$2X{H}szW6#Z}SlM%Sen-%O{sRGYi8fjR2|&@e?nXO0 z;FC)MT|)McMF2sRT6l4X9k>D%J|F87?*npe|tM9WEe6><5M90Wfhl1G~nP z`m9$gDHW5@1Zvm>1{Fu)E9*?ZlO_!~0b!G04LJc+lbH=00l1U34OD-7=}1*duH{6> zcf&nRPVGsQJoG`RTkJv8MCPw6imtJVzHjg6Y`%om-&j9TcrT~$xJLiLkmL#eo7!$k zcagY{BlcY?Jua0~i{{>!QglTd>4&~D_3ZVVoEkUS619$I delta 2388 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 G 0 { tm.FlushSealingBatches(ctx) @@ -105,9 +108,8 @@ func (tm *TestMiner) PledgeSectors(ctx context.Context, n, existing int, blockNo } build.Clock.Sleep(100 * time.Millisecond) - fmt.Printf("WaitSeal: %d %+v\n", len(toCheck), states) + fmt.Printf("WaitSectorsProving: %d %+v\n", len(toCheck), states) } - } func (tm *TestMiner) StartPledge(ctx context.Context, n, existing int, blockNotif <-chan struct{}) map[abi.SectorNumber]struct{} { From ea32b415f094a2bc350af86fd93241aa96acbd5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 8 Feb 2022 17:03:51 +0100 Subject: [PATCH 19/82] Fix missing FinalizeReplicaUpdate in tests --- extern/sector-storage/sched_test.go | 4 ++++ extern/sector-storage/teststorage_test.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/extern/sector-storage/sched_test.go b/extern/sector-storage/sched_test.go index 29e791ba3..61e0bc374 100644 --- a/extern/sector-storage/sched_test.go +++ b/extern/sector-storage/sched_test.go @@ -118,6 +118,10 @@ func (s *schedTestWorker) GenerateSectorKeyFromData(ctx context.Context, sector panic("implement me") } +func (s *schedTestWorker) FinalizeReplicaUpdate(ctx context.Context, sector storage.SectorRef, keepUnsealed []storage.Range) (storiface.CallID, error) { + panic("implement me") +} + func (s *schedTestWorker) MoveStorage(ctx context.Context, sector storage.SectorRef, types storiface.SectorFileType) (storiface.CallID, error) { panic("implement me") } diff --git a/extern/sector-storage/teststorage_test.go b/extern/sector-storage/teststorage_test.go index cb15184be..6c6eef0a6 100644 --- a/extern/sector-storage/teststorage_test.go +++ b/extern/sector-storage/teststorage_test.go @@ -87,6 +87,10 @@ func (t *testExec) GenerateSectorKeyFromData(ctx context.Context, sector storage panic("implement me") } +func (t *testExec) FinalizeReplicaUpdate(ctx context.Context, sector storage.SectorRef, keepUnsealed []storage.Range) error { + panic("implement me") +} + func (t *testExec) NewSector(ctx context.Context, sector storage.SectorRef) error { panic("implement me") } From ecc1f4b8f17ace1f749200a993cd30d70c849e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 8 Feb 2022 17:45:26 +0100 Subject: [PATCH 20/82] Make FinalizeReplicaUpdate actually do cleanup --- extern/sector-storage/ffiwrapper/sealer_cgo.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extern/sector-storage/ffiwrapper/sealer_cgo.go b/extern/sector-storage/ffiwrapper/sealer_cgo.go index 5b7f2acc5..01b40a869 100644 --- a/extern/sector-storage/ffiwrapper/sealer_cgo.go +++ b/extern/sector-storage/ffiwrapper/sealer_cgo.go @@ -13,6 +13,7 @@ import ( "io" "math/bits" "os" + "path/filepath" "runtime" "github.com/ipfs/go-cid" @@ -885,8 +886,8 @@ func (sb *Sealer) FinalizeReplicaUpdate(ctx context.Context, sector storage.Sect } defer done() - if err := ffi.ClearCache(uint64(ssize), paths.UpdateCache); err != nil { - return xerrors.Errorf("clear cache: %w", err) + if err := os.Remove(filepath.Join(paths.UpdateCache, "sc-02-data-tree-d.dat")); err != nil { + return xerrors.Errorf("clear update cache: %w", err) } } From 263de5b0c16fea61bdb4d08b62b73a51c0474807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 9 Feb 2022 11:28:37 +0100 Subject: [PATCH 21/82] Use tagged specs-storage --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 262763d9d..ac266416f 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,7 @@ require ( github.com/filecoin-project/specs-actors/v5 v5.0.4 github.com/filecoin-project/specs-actors/v6 v6.0.1 github.com/filecoin-project/specs-actors/v7 v7.0.0-rc1 - github.com/filecoin-project/specs-storage v0.1.1-0.20220202201749-ae62d2332aa8 + github.com/filecoin-project/specs-storage v0.2.0 github.com/filecoin-project/test-vectors/schema v0.0.5 github.com/gbrlsnchs/jwt/v3 v3.0.1 github.com/gdamore/tcell/v2 v2.2.0 diff --git a/go.sum b/go.sum index c83e5568b..80afa40c0 100644 --- a/go.sum +++ b/go.sum @@ -399,8 +399,8 @@ github.com/filecoin-project/specs-actors/v7 v7.0.0-20211117170924-fd07a4c7dff9/g github.com/filecoin-project/specs-actors/v7 v7.0.0-20211222192039-c83bea50c402/go.mod h1:p6LIOFezA1rgRLMewbvdi3Pp6SAu+q9FtJ9CAleSjrE= github.com/filecoin-project/specs-actors/v7 v7.0.0-rc1 h1:FuDaXIbcw2hRsFI8SDTmsGGCE+NumpF6aiBoU/2X5W4= github.com/filecoin-project/specs-actors/v7 v7.0.0-rc1/go.mod h1:TA5FwCna+Yi36POaT7SLKXsgEDvJwc0V/L6ZsO19B9M= -github.com/filecoin-project/specs-storage v0.1.1-0.20220202201749-ae62d2332aa8 h1:lHg1G44FX6LNuIcGJWE6ty4dH+WoFIA2UIfGGV06Hpg= -github.com/filecoin-project/specs-storage v0.1.1-0.20220202201749-ae62d2332aa8/go.mod h1:Tb88Zq+IBJbvAn3mS89GYj3jdRThBTE/771HCVZdRJU= +github.com/filecoin-project/specs-storage v0.2.0 h1:Y4UDv0apRQ3zI2GiPPubi8JblpUZZphEdaJUxCutfyg= +github.com/filecoin-project/specs-storage v0.2.0/go.mod h1:Tb88Zq+IBJbvAn3mS89GYj3jdRThBTE/771HCVZdRJU= github.com/filecoin-project/test-vectors/schema v0.0.5 h1:w3zHQhzM4pYxJDl21avXjOKBLF8egrvwUwjpT8TquDg= github.com/filecoin-project/test-vectors/schema v0.0.5/go.mod h1:iQ9QXLpYWL3m7warwvK1JC/pTri8mnfEmKygNDqqY6E= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= From 4289ce10e43a85a3b652057d79477e765c0949a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 9 Feb 2022 12:41:39 +0100 Subject: [PATCH 22/82] fsm: Call ReleaseSectorKey after WinningPoSt lookback period --- cmd/lotus-miner/info.go | 3 + extern/storage-sealing/fsm.go | 18 ++++- extern/storage-sealing/fsm_events.go | 15 ++++ extern/storage-sealing/sector_state.go | 8 ++- extern/storage-sealing/states_failed.go | 10 +++ .../storage-sealing/states_replica_update.go | 72 +++++++++++++++++++ 6 files changed, 124 insertions(+), 2 deletions(-) diff --git a/cmd/lotus-miner/info.go b/cmd/lotus-miner/info.go index 39de942aa..1133908ca 100644 --- a/cmd/lotus-miner/info.go +++ b/cmd/lotus-miner/info.go @@ -466,6 +466,7 @@ var stateOrder = map[sealing.SectorState]stateMeta{} var stateList = []stateMeta{ {col: 39, state: "Total"}, {col: color.FgGreen, state: sealing.Proving}, + {col: color.FgGreen, state: sealing.UpdateActivating}, {col: color.FgBlue, state: sealing.Empty}, {col: color.FgBlue, state: sealing.WaitDeals}, @@ -496,6 +497,7 @@ var stateList = []stateMeta{ {col: color.FgYellow, state: sealing.SubmitReplicaUpdate}, {col: color.FgYellow, state: sealing.ReplicaUpdateWait}, {col: color.FgYellow, state: sealing.FinalizeReplicaUpdate}, + {col: color.FgYellow, state: sealing.ReleaseSectorKey}, {col: color.FgCyan, state: sealing.Terminating}, {col: color.FgCyan, state: sealing.TerminateWait}, @@ -524,6 +526,7 @@ var stateList = []stateMeta{ {col: color.FgRed, state: sealing.SnapDealsAddPieceFailed}, {col: color.FgRed, state: sealing.SnapDealsDealsExpired}, {col: color.FgRed, state: sealing.ReplicaUpdateFailed}, + {col: color.FgRed, state: sealing.ReleaseSectorKeyFailed}, } func init() { diff --git a/extern/storage-sealing/fsm.go b/extern/storage-sealing/fsm.go index 1b18efcc7..251d3a74a 100644 --- a/extern/storage-sealing/fsm.go +++ b/extern/storage-sealing/fsm.go @@ -174,7 +174,14 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto on(SectorAbortUpgrade{}, AbortUpgrade), ), FinalizeReplicaUpdate: planOne( - on(SectorFinalized{}, Proving), + on(SectorFinalized{}, UpdateActivating), + ), + UpdateActivating: planOne( + on(SectorUpdateActive{}, ReleaseSectorKey), + ), + ReleaseSectorKey: planOne( + on(SectorKeyReleased{}, Proving), + on(SectorReleaseKeyFailed{}, ReleaseSectorKeyFailed), ), // Sealing errors @@ -257,6 +264,9 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto on(SectorDealsExpired{}, SnapDealsDealsExpired), on(SectorAbortUpgrade{}, AbortUpgrade), ), + ReleaseSectorKeyFailed: planOne( + on(SectorUpdateActive{}, ReleaseSectorKey), + ), // Post-seal @@ -484,6 +494,10 @@ func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(sta return m.handleReplicaUpdateWait, processed, nil case FinalizeReplicaUpdate: return m.handleFinalizeReplicaUpdate, processed, nil + case UpdateActivating: + return m.handleUpdateActivating, processed, nil + case ReleaseSectorKey: + return m.handleReleaseSectorKey, processed, nil // Handled failure modes case AddPieceFailed: @@ -520,6 +534,8 @@ func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(sta return m.handleSnapDealsRecoverDealIDs, processed, nil case ReplicaUpdateFailed: return m.handleSubmitReplicaUpdateFailed, processed, nil + case ReleaseSectorKeyFailed: + return m.handleReleaseSectorKeyFailed, 0, err case AbortUpgrade: return m.handleAbortUpgrade, processed, nil diff --git a/extern/storage-sealing/fsm_events.go b/extern/storage-sealing/fsm_events.go index 395c4b94a..fc3b774f9 100644 --- a/extern/storage-sealing/fsm_events.go +++ b/extern/storage-sealing/fsm_events.go @@ -335,6 +335,14 @@ type SectorReplicaUpdateLanded struct{} func (evt SectorReplicaUpdateLanded) apply(state *SectorInfo) {} +type SectorUpdateActive struct{} + +func (evt SectorUpdateActive) apply(state *SectorInfo) {} + +type SectorKeyReleased struct{} + +func (evt SectorKeyReleased) apply(state *SectorInfo) {} + // Failed state recovery type SectorRetrySealPreCommit1 struct{} @@ -445,6 +453,13 @@ type SectorSubmitReplicaUpdateFailed struct{} func (evt SectorSubmitReplicaUpdateFailed) apply(state *SectorInfo) {} +type SectorReleaseKeyFailed struct{ error } + +func (evt SectorReleaseKeyFailed) FormatError(xerrors.Printer) (next error) { + return evt.error +} +func (evt SectorReleaseKeyFailed) apply(state *SectorInfo) {} + // Faults type SectorFaulty struct{} diff --git a/extern/storage-sealing/sector_state.go b/extern/storage-sealing/sector_state.go index ba6df7ff4..5c2c56171 100644 --- a/extern/storage-sealing/sector_state.go +++ b/extern/storage-sealing/sector_state.go @@ -52,11 +52,14 @@ var ExistSectorStateList = map[SectorState]struct{}{ ProveReplicaUpdate: {}, SubmitReplicaUpdate: {}, ReplicaUpdateWait: {}, + UpdateActivating: {}, + ReleaseSectorKey: {}, FinalizeReplicaUpdate: {}, SnapDealsAddPieceFailed: {}, SnapDealsDealsExpired: {}, SnapDealsRecoverDealIDs: {}, ReplicaUpdateFailed: {}, + ReleaseSectorKeyFailed: {}, AbortUpgrade: {}, } @@ -104,6 +107,8 @@ const ( SubmitReplicaUpdate SectorState = "SubmitReplicaUpdate" ReplicaUpdateWait SectorState = "ReplicaUpdateWait" FinalizeReplicaUpdate SectorState = "FinalizeReplicaUpdate" + UpdateActivating SectorState = "UpdateActivating" + ReleaseSectorKey SectorState = "ReleaseSectorKey" // error modes FailedUnrecoverable SectorState = "FailedUnrecoverable" @@ -124,6 +129,7 @@ const ( SnapDealsRecoverDealIDs SectorState = "SnapDealsRecoverDealIDs" AbortUpgrade SectorState = "AbortUpgrade" ReplicaUpdateFailed SectorState = "ReplicaUpdateFailed" + ReleaseSectorKeyFailed SectorState = "ReleaseSectorKeyFailed" Faulty SectorState = "Faulty" // sector is corrupted or gone for some reason FaultReported SectorState = "FaultReported" // sector has been declared as a fault on chain @@ -153,7 +159,7 @@ func toStatState(st SectorState, finEarly bool) statSectorState { return sstProving } return sstSealing - case Proving, Removed, Removing, Terminating, TerminateWait, TerminateFinality, TerminateFailed: + case Proving, UpdateActivating, ReleaseSectorKey, Removed, Removing, Terminating, TerminateWait, TerminateFinality, TerminateFailed: return sstProving } diff --git a/extern/storage-sealing/states_failed.go b/extern/storage-sealing/states_failed.go index 6d40bf097..a1c3be460 100644 --- a/extern/storage-sealing/states_failed.go +++ b/extern/storage-sealing/states_failed.go @@ -255,6 +255,16 @@ func (m *Sealing) handleSubmitReplicaUpdateFailed(ctx statemachine.Context, sect return ctx.Send(SectorRetrySubmitReplicaUpdate{}) } +func (m *Sealing) handleReleaseSectorKeyFailed(ctx statemachine.Context, sector SectorInfo) error { + // not much we can do, wait for a bit and try again + + if err := failedCooldown(ctx, sector); err != nil { + return err + } + + return ctx.Send(SectorUpdateActive{}) +} + func (m *Sealing) handleCommitFailed(ctx statemachine.Context, sector SectorInfo) error { tok, _, err := m.Api.ChainHead(ctx.Context()) if err != nil { diff --git a/extern/storage-sealing/states_replica_update.go b/extern/storage-sealing/states_replica_update.go index 6222d49db..4c3faed82 100644 --- a/extern/storage-sealing/states_replica_update.go +++ b/extern/storage-sealing/states_replica_update.go @@ -2,12 +2,15 @@ package sealing import ( "bytes" + "context" + "time" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/exitcode" statemachine "github.com/filecoin-project/go-statemachine" api "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/policy" "golang.org/x/xerrors" ) @@ -225,6 +228,75 @@ func (m *Sealing) handleFinalizeReplicaUpdate(ctx statemachine.Context, sector S return ctx.Send(SectorFinalized{}) } +func (m *Sealing) handleUpdateActivating(ctx statemachine.Context, sector SectorInfo) error { + try := func() error { + mw, err := m.Api.StateWaitMsg(ctx.Context(), *sector.ReplicaUpdateMessage) + if err != nil { + return err + } + + tok, height, err := m.Api.ChainHead(ctx.Context()) + if err != nil { + return err + } + + nv, err := m.Api.StateNetworkVersion(ctx.Context(), tok) + if err != nil { + return err + } + + lb := policy.GetWinningPoStSectorSetLookback(nv) + + targetHeight := mw.Height + lb + InteractivePoRepConfidence + delay := 50 * time.Millisecond + + for { + if height >= targetHeight { + break + } + + wctx, cancel := context.WithTimeout(ctx.Context(), delay) + <-wctx.Done() + cancel() + + // increasing backoff; can't just calculate the correct time because integration tests do funny things with time + delay = delay * 10 / 3 + if delay > 5*time.Minute { + delay = 5 * time.Minute + } + + _, height, err = m.Api.ChainHead(ctx.Context()) + if err != nil { + return err + } + } + + return nil + } + + for { + err := try() + if err == nil { + break + } + + log.Errorw("error in handleUpdateActivating", "error", err) + + // likely an API issue, sleep for a bit and retry + time.Sleep(time.Minute) + } + + return ctx.Send(SectorUpdateActive{}) +} + +func (m *Sealing) handleReleaseSectorKey(ctx statemachine.Context, sector SectorInfo) error { + if err := m.sealer.ReleaseSectorKey(sector.sealingCtx(ctx.Context()), m.minerSector(sector.SectorType, sector.SectorNumber)); err != nil { + return ctx.Send(SectorReleaseKeyFailed{err}) + } + + return ctx.Send(SectorKeyReleased{}) +} + func handleErrors(ctx statemachine.Context, err error, sector SectorInfo) error { switch err.(type) { case *ErrApi: From 9f54c4d002e9a1e9c703cf3797ae093cbc30bcad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 9 Feb 2022 14:41:36 +0100 Subject: [PATCH 23/82] Fix CheckProvable with updated sectors --- extern/sector-storage/faults.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/extern/sector-storage/faults.go b/extern/sector-storage/faults.go index 7fdf5c337..f7a764e50 100644 --- a/extern/sector-storage/faults.go +++ b/extern/sector-storage/faults.go @@ -55,6 +55,25 @@ func (m *Manager) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, return nil } + // temporary hack to make the check work with snapdeals + // will go away in https://github.com/filecoin-project/lotus/pull/7971 + if lp.Sealed == "" || lp.Cache == "" { + // maybe it's update + lockedUpdate, err := m.index.StorageTryLock(ctx, sector.ID, storiface.FTUpdate|storiface.FTUpdateCache, storiface.FTNone) + if err != nil { + return xerrors.Errorf("acquiring sector lock: %w", err) + } + if lockedUpdate { + lp, _, err = m.localStore.AcquireSector(ctx, sector, storiface.FTUpdate|storiface.FTUpdateCache, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove) + if err != nil { + log.Warnw("CheckProvable Sector FAULT: acquire sector in checkProvable", "sector", sector, "error", err) + bad[sector.ID] = fmt.Sprintf("acquire sector failed: %s", err) + return nil + } + lp.Sealed, lp.Cache = lp.Update, lp.UpdateCache + } + } + if lp.Sealed == "" || lp.Cache == "" { log.Warnw("CheckProvable Sector FAULT: cache and/or sealed paths not found", "sector", sector, "sealed", lp.Sealed, "cache", lp.Cache) bad[sector.ID] = fmt.Sprintf("cache and/or sealed paths not found, cache %q, sealed %q", lp.Cache, lp.Sealed) From a5bddbcdc6ac729045d8767d9212e89a48b69e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 9 Feb 2022 14:46:29 +0100 Subject: [PATCH 24/82] proofs master --- extern/filecoin-ffi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index f66026bd3..5ec5d805c 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit f66026bd3d165e009b11172c82b44204197a7666 +Subproject commit 5ec5d805c01ea85224f6448dd6c6fa0a2a73c028 From 02c0fb7b653334d6bb4f6385456e124e827919ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 9 Feb 2022 14:54:36 +0100 Subject: [PATCH 25/82] ffiwrapper: Use ClearCache for update cache cleanup --- extern/sector-storage/ffiwrapper/sealer_cgo.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/extern/sector-storage/ffiwrapper/sealer_cgo.go b/extern/sector-storage/ffiwrapper/sealer_cgo.go index 01b40a869..5b7f2acc5 100644 --- a/extern/sector-storage/ffiwrapper/sealer_cgo.go +++ b/extern/sector-storage/ffiwrapper/sealer_cgo.go @@ -13,7 +13,6 @@ import ( "io" "math/bits" "os" - "path/filepath" "runtime" "github.com/ipfs/go-cid" @@ -886,8 +885,8 @@ func (sb *Sealer) FinalizeReplicaUpdate(ctx context.Context, sector storage.Sect } defer done() - if err := os.Remove(filepath.Join(paths.UpdateCache, "sc-02-data-tree-d.dat")); err != nil { - return xerrors.Errorf("clear update cache: %w", err) + if err := ffi.ClearCache(uint64(ssize), paths.UpdateCache); err != nil { + return xerrors.Errorf("clear cache: %w", err) } } From 9e76a788e29ac3f8091612068aff5e647f5e0594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 9 Feb 2022 17:47:53 +0100 Subject: [PATCH 26/82] sealing: Use ChainAt in handleUpdateActivating --- .../storage-sealing/states_replica_update.go | 34 +++++-------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/extern/storage-sealing/states_replica_update.go b/extern/storage-sealing/states_replica_update.go index 4c3faed82..8683a11d8 100644 --- a/extern/storage-sealing/states_replica_update.go +++ b/extern/storage-sealing/states_replica_update.go @@ -5,6 +5,7 @@ import ( "context" "time" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/exitcode" statemachine "github.com/filecoin-project/go-statemachine" @@ -235,7 +236,7 @@ func (m *Sealing) handleUpdateActivating(ctx statemachine.Context, sector Sector return err } - tok, height, err := m.Api.ChainHead(ctx.Context()) + tok, _, err := m.Api.ChainHead(ctx.Context()) if err != nil { return err } @@ -248,30 +249,13 @@ func (m *Sealing) handleUpdateActivating(ctx statemachine.Context, sector Sector lb := policy.GetWinningPoStSectorSetLookback(nv) targetHeight := mw.Height + lb + InteractivePoRepConfidence - delay := 50 * time.Millisecond - for { - if height >= targetHeight { - break - } - - wctx, cancel := context.WithTimeout(ctx.Context(), delay) - <-wctx.Done() - cancel() - - // increasing backoff; can't just calculate the correct time because integration tests do funny things with time - delay = delay * 10 / 3 - if delay > 5*time.Minute { - delay = 5 * time.Minute - } - - _, height, err = m.Api.ChainHead(ctx.Context()) - if err != nil { - return err - } - } - - return nil + return m.events.ChainAt(func(context.Context, TipSetToken, abi.ChainEpoch) error { + return ctx.Send(SectorUpdateActive{}) + }, func(ctx context.Context, ts TipSetToken) error { + log.Warn("revert in handleUpdateActivating") + return nil + }, InteractivePoRepConfidence, targetHeight) } for { @@ -286,7 +270,7 @@ func (m *Sealing) handleUpdateActivating(ctx statemachine.Context, sector Sector time.Sleep(time.Minute) } - return ctx.Send(SectorUpdateActive{}) + return nil } func (m *Sealing) handleReleaseSectorKey(ctx statemachine.Context, sector SectorInfo) error { From 407c6cf120abaa5d3eca7c0bc67ce5f08f4797a4 Mon Sep 17 00:00:00 2001 From: zenground0 Date: Tue, 1 Feb 2022 11:39:42 +0530 Subject: [PATCH 27/82] Stop recovery attempts after fault --- extern/storage-sealing/upgrade_queue.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extern/storage-sealing/upgrade_queue.go b/extern/storage-sealing/upgrade_queue.go index 86083930d..1e5bef67c 100644 --- a/extern/storage-sealing/upgrade_queue.go +++ b/extern/storage-sealing/upgrade_queue.go @@ -108,7 +108,8 @@ func sectorActive(ctx context.Context, api SealingAPI, maddr address.Address, to if err != nil { return false, xerrors.Errorf("failed to check active sectors: %w", err) } - // Check if sector is among active sectors + + // Ensure the upgraded sector is active var found bool for _, si := range active { if si.SectorNumber == sector { From a1098f6dc20d8ddb1951a43fcc81f25d19562b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 18 Jan 2022 10:24:43 +0100 Subject: [PATCH 28/82] Cleanup go.mod --- go.mod | 4 ---- 1 file changed, 4 deletions(-) diff --git a/go.mod b/go.mod index ac266416f..9edf85a52 100644 --- a/go.mod +++ b/go.mod @@ -168,7 +168,3 @@ require ( replace github.com/filecoin-project/filecoin-ffi => ./extern/filecoin-ffi replace github.com/filecoin-project/test-vectors => ./extern/test-vectors - -//replace github.com/filecoin-project/specs-actors/v7 => /Users/zenground0/pl/repos/specs-actors - -// replace github.com/filecon-project/specs-storage => /Users/zenground0/pl/repos/specs-storage From cb2518716c381922c3284a461448dce4eb6776a4 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Thu, 10 Feb 2022 17:43:57 -0500 Subject: [PATCH 29/82] v1.14.0-rc7 --- CHANGELOG.md | 4 ++-- build/openrpc/full.json.gz | Bin 25709 -> 25710 bytes build/openrpc/miner.json.gz | Bin 11748 -> 11748 bytes build/openrpc/worker.json.gz | Bin 3850 -> 3850 bytes build/version.go | 2 +- documentation/en/cli-lotus-miner.md | 2 +- documentation/en/cli-lotus-worker.md | 2 +- documentation/en/cli-lotus.md | 2 +- 8 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b1e87ce7..8d7916f2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Lotus changelog -# 1.14.0-rc6 / 2022-02-08 +# 1.14.0-rc7 / 2022-02-010 This is the sixth release candidate for the mandatory release v1.14.0 of Lotus that introduces [Filecoin network v15, codenamed the OhSnap upgrade](https://github.com/filecoin-project/community/discussions/74?sort=new#discussioncomment-1922550). @@ -19,7 +19,7 @@ The calibnet will be upgraded to Network v15 OhSnap at epoch 682006, around 2022 To join the network, simply build lotus by running `make calibnet`. New proof params for Snap Deals should be downloaded upon your nodes restart. - - The parameters are pinged on IPFS gateway https://proofs.filecoin.io and the CIDs can be found [here](https://github.com/filecoin-project/lotus/blob/release/v1.14.0/build/proof-params/parameters.json), please let the lotus team know in #lotus-ohsnap if the params are not fetched automatically. You can also download the params manually from s3://proof-params-ap/filecoin-snapdeal-parameters/. + - The parameters are pinged on IPFS gateway https://proofs.filecoin.io and the CIDs can be found [here](https://github.com/filecoin-project/lotus/blob/release/v1.14.0/build/proof-params/parameters.json), please let the lotus team know in #fil-lotus-dev if the params are not fetched automatically. For users in China, you can also get the proofs by setting `export IPFS_GATEWAY=https://proof-parameters.s3.cn-south-1.jdcloud-oss.com/ipfs/` ## New Features and Changes diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 991332240196cc42304a6500f2d2697927a173bc..8e68ed74d5e51c4a3ca22d57fcbc712b3e3e45bd 100644 GIT binary patch literal 25710 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 Z4~;TY;dg`2$3;Fqt|Nh%c1W-g{{#1x>Bj&7 literal 25709 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 diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 3796c840b1f2c95c6d4ede9ba6ee0d59445a96dc..3284dd17a6cd4b240e4716ad5ea915b0ba06cca1 100644 GIT binary patch delta 22 ecmaD7{UmxqJCpB}jh(@|9M?*^-Ws_xF#rI2<_UKI delta 22 dcmaD7{UmxqJ5%q}jh(@|90lSWlZ@P%7yxx92p9kW diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index c5c4ffdfac03d4a2e55fb88459ebff87c3c671d1..ce6f88d90fe789401b30b0b4d1a6ae16659b4ba1 100644 GIT binary patch delta 21 ccmeB@>yn$$${4V*t%i?7Y0@Tn&L<2E08)Ae@&Et; delta 21 ccmeB@>yn$$%IL7Mt%i>ysq5WG&L<2E09fP*C;$Ke diff --git a/build/version.go b/build/version.go index d7ca40c54..2c4351fff 100644 --- a/build/version.go +++ b/build/version.go @@ -37,7 +37,7 @@ func BuildTypeString() string { } // BuildVersion is the local build version -const BuildVersion = "1.14.0-rc6" +const BuildVersion = "1.14.0-rc7" func UserVersion() string { if os.Getenv("LOTUS_VERSION_IGNORE_COMMIT") == "1" { diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index 1a505a23b..15746d4af 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -7,7 +7,7 @@ USAGE: lotus-miner [global options] command [command options] [arguments...] VERSION: - 1.14.0-rc6 + 1.14.0-rc7 COMMANDS: init Initialize a lotus miner repo diff --git a/documentation/en/cli-lotus-worker.md b/documentation/en/cli-lotus-worker.md index eb7ae4c42..c75fdaa87 100644 --- a/documentation/en/cli-lotus-worker.md +++ b/documentation/en/cli-lotus-worker.md @@ -7,7 +7,7 @@ USAGE: lotus-worker [global options] command [command options] [arguments...] VERSION: - 1.14.0-rc6 + 1.14.0-rc7 COMMANDS: run Start lotus worker diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index 5882bd114..803c4e580 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -7,7 +7,7 @@ USAGE: lotus [global options] command [command options] [arguments...] VERSION: - 1.14.0-rc6 + 1.14.0-rc7 COMMANDS: daemon Start a lotus daemon process From 7fd4c9617d1b25d51ab089312f36a625b162462a Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Thu, 10 Feb 2022 23:46:49 -0500 Subject: [PATCH 30/82] typo in change log --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d7916f2a..e0480b448 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ # Lotus changelog -# 1.14.0-rc7 / 2022-02-010 +# 1.14.0-rc7 / 2022-02-10 -This is the sixth release candidate for the mandatory release v1.14.0 of Lotus that introduces [Filecoin network v15, codenamed the OhSnap upgrade](https://github.com/filecoin-project/community/discussions/74?sort=new#discussioncomment-1922550). +This is the 7th release candidate for the mandatory release v1.14.0 of Lotus that introduces [Filecoin network v15, +codenamed the OhSnap upgrade](https://github.com/filecoin-project/community/discussions/74?sort=new#discussioncomment-1922550). The OhSnap upgrade introduces the following FIPs, delivered in [actors v7-rc1](https://github.com/filecoin-project/specs-actors/releases/tag/v7.0.0-rc1): - [FIP-0019 Snap Deals](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0019.md) From e89f7d90238748bdec8dfe1a15e1f54bd6d2eb98 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 11 Feb 2022 10:09:40 -0800 Subject: [PATCH 31/82] feat: shed: add a state-tree diff command to lotus shed This makes it easier to debug state mismatches by providing a nice way to figure out which actors differ between two state-trees and how (balance, nonce, state, etc.). It doesn't provide a way to actually _diff_ those state-trees, but one can use `lotus chain get` to figure that out (although it would be _nice_ to provide something a bit smarter). --- cmd/lotus-shed/diff.go | 104 +++++++++++++++++++++++++++++++++++++++++ cmd/lotus-shed/main.go | 1 + 2 files changed, 105 insertions(+) create mode 100644 cmd/lotus-shed/diff.go diff --git a/cmd/lotus-shed/diff.go b/cmd/lotus-shed/diff.go new file mode 100644 index 000000000..bcaa04122 --- /dev/null +++ b/cmd/lotus-shed/diff.go @@ -0,0 +1,104 @@ +package main + +import ( + "fmt" + + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/types" + lcli "github.com/filecoin-project/lotus/cli" +) + +var diffCmd = &cli.Command{ + Name: "diff", + Usage: "diff state objects", + Subcommands: []*cli.Command{diffStateTrees}, +} + +var diffStateTrees = &cli.Command{ + Name: "state-trees", + Usage: "diff two state-trees", + ArgsUsage: " ", + Action: func(cctx *cli.Context) error { + api, closer, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + + defer closer() + ctx := lcli.ReqContext(cctx) + + if cctx.NArg() != 2 { + return xerrors.Errorf("expected two state-tree roots") + } + + argA := cctx.Args().Get(1) + rootA, err := cid.Parse(argA) + if err != nil { + return xerrors.Errorf("first state-tree root (%q) is not a CID: %w", argA, err) + } + argB := cctx.Args().Get(1) + rootB, err := cid.Parse(argB) + if err != nil { + return xerrors.Errorf("second state-tree root (%q) is not a CID: %w", argB, err) + } + + if rootA == rootB { + fmt.Println("state trees do not differ") + return nil + } + + changedB, err := api.StateChangedActors(ctx, rootA, rootB) + if err != nil { + return err + } + changedA, err := api.StateChangedActors(ctx, rootB, rootA) + if err != nil { + return err + } + + diff := func(stateA, stateB types.Actor) { + if stateB.Code != stateA.Code { + fmt.Printf(" code: %s != %s\n", stateA.Code, stateB.Code) + } + if stateB.Head != stateA.Head { + fmt.Printf(" state: %s != %s\n", stateA.Head, stateB.Head) + } + if stateB.Nonce != stateA.Nonce { + fmt.Printf(" nonce: %d != %d\n", stateA.Nonce, stateB.Nonce) + } + if !stateB.Balance.Equals(stateA.Balance) { + fmt.Printf(" balance: %s != %s\n", stateA.Balance, stateB.Balance) + } + } + + fmt.Printf("state differences between %s (first) and %s (second):\n\n", rootA, rootB) + for addr, stateA := range changedA { + fmt.Println(addr) + stateB, ok := changedB[addr] + if ok { + diff(stateA, stateB) + continue + } else { + fmt.Printf(" actor does not exist in second state-tree (%s)\n", rootB) + } + fmt.Println() + delete(changedB, addr) + } + for addr, stateB := range changedB { + fmt.Println(addr) + stateA, ok := changedA[addr] + if ok { + diff(stateA, stateB) + continue + } else { + fmt.Printf(" actor does not exist in first state-tree (%s)\n", rootA) + } + fmt.Println() + } + return nil + }, +} diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index 9bcea7224..45fd24e18 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -68,6 +68,7 @@ func main() { sendCsvCmd, terminationsCmd, migrationsCmd, + diffCmd, } app := &cli.App{ From aca2a0fd1b8fafb594a3e9375d1d7231aca3f15d Mon Sep 17 00:00:00 2001 From: Nikola Divic Date: Sat, 12 Feb 2022 17:48:45 +0100 Subject: [PATCH 32/82] test: fix flake in TestMemPoolBatchPushUntrusted integration test The flake was caused by improper waiting for certain chain operations to finish: - We didn't wait for messages to be registered as pushed - We improperly waited for a fixed time (10 seconds) for messages to be mined, which in the best case would wait longer than necessary and in the worst case would cause the test to break. What I did: - fixed by waiting in a loop for "just enough time". This fixed the flake and made the test run faster, on average, because we don't have unnecessary waiting. - I added a "circuit-breaker" where the wait loop will timeout after 10 seconds. --- itests/mempool_test.go | 66 ++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/itests/mempool_test.go b/itests/mempool_test.go index f5fb408e0..51e0afafd 100644 --- a/itests/mempool_test.go +++ b/itests/mempool_test.go @@ -3,6 +3,7 @@ package itests import ( "context" + "fmt" "testing" "time" @@ -380,7 +381,7 @@ func TestMemPoolBatchPushUntrusted(t *testing.T) { ctx := context.Background() const blockTime = 100 * time.Millisecond firstNode, _, _, ens := kit.EnsembleTwoOne(t, kit.MockProofs()) - ens.InterconnectAll().BeginMining(blockTime) + ens.InterconnectAll() kit.QuietMiningLogs() sender := firstNode.DefaultKey.Address @@ -416,18 +417,33 @@ func TestMemPoolBatchPushUntrusted(t *testing.T) { _, err = firstNode.MpoolBatchPushUntrusted(ctx, sms) require.NoError(t, err) - // check pending messages for address - msgStatuses, err := firstNode.MpoolCheckPendingMessages(ctx, sender) - require.NoError(t, err) - require.Equal(t, totalMessages, len(msgStatuses)) - for _, msgStatusList := range msgStatuses { - for _, status := range msgStatusList { - require.True(t, status.OK) + // check pending messages for address, wait until they are all pushed + timeout := time.After(time.Second * 10) + for { + msgStatuses, err := firstNode.MpoolCheckPendingMessages(ctx, sender) + require.NoError(t, err) + + if len(msgStatuses) == totalMessages { + for _, msgStatusList := range msgStatuses { + for _, status := range msgStatusList { + require.True(t, status.OK) + } + } + break + } + + select { + case <-timeout: + t.Fatal("waiting for batch push timed out") + default: + fmt.Printf("waiting for %d more messages to be pushed\n", len(msgStatuses)-totalMessages) + time.Sleep(time.Millisecond * 100) } } // verify messages should be the ones included in the next block selected, _ := firstNode.MpoolSelect(ctx, types.EmptyTSK, 0) + for _, msg := range sms { found := false for _, selectedMsg := range selected { @@ -439,17 +455,31 @@ func TestMemPoolBatchPushUntrusted(t *testing.T) { require.True(t, found) } - time.Sleep(10 * blockTime) + ens.BeginMining(blockTime) - // pool pending list should be empty - pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) - require.NoError(t, err) - require.Equal(t, 0, len(pending)) - - // all messages should be added to the chain - for _, lookMsg := range sms { - msgLookup, err := firstNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + // wait until pending messages are mined + timeout = time.After(time.Second * 10) + for { + // pool pending list should be empty + pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) require.NoError(t, err) - require.NotNil(t, msgLookup) + + if len(pending) == 0 { + // all messages should be added to the chain + for _, lookMsg := range sms { + msgLookup, err := firstNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + require.NoError(t, err) + require.NotNil(t, msgLookup) + } + break + } + + select { + case <-timeout: + t.Fatal("waiting for pending messages to be mined timed out") + default: + fmt.Printf("waiting for %d more messages to be mined\n", len(pending)) + time.Sleep(time.Millisecond * 100) + } } } From 34387326d16b14c03c7ad97ce74cc988f3a5dfc8 Mon Sep 17 00:00:00 2001 From: Nikola Divic Date: Sat, 12 Feb 2022 19:52:51 +0100 Subject: [PATCH 33/82] test: fix flaky message pool integration tests Using the same pattern described in my previous commit. I also added the CircuitBreaker to the itests kit as it may be useful for other integration tests when debugging flakyness caused by timeouts. --- itests/kit/circuit.go | 34 ++++++ itests/mempool_test.go | 246 +++++++++++++++++++++++------------------ 2 files changed, 175 insertions(+), 105 deletions(-) create mode 100644 itests/kit/circuit.go diff --git a/itests/kit/circuit.go b/itests/kit/circuit.go new file mode 100644 index 000000000..d2857010e --- /dev/null +++ b/itests/kit/circuit.go @@ -0,0 +1,34 @@ +package kit + +import ( + "fmt" + "testing" + "time" +) + +/* +CircuitBreaker implements a simple time-based circuit breaker used for waiting for async operations to finish. + +This is how it works: + - It runs the `cb` function until it returns true, + - waiting for `throttle` duration between each iteration, + - or at most `timeout` duration until it breaks test execution. + +You can use it if t.Deadline() is not "granular" enough, and you want to know which specific piece of code timed out, +or you need to set different deadlines in the same test. +*/ +func CircuitBreaker(t *testing.T, label string, throttle, timeout time.Duration, cb func() bool) { + tmo := time.After(timeout) + for { + if cb() { + break + } + select { + case <-tmo: + t.Fatal("timeout: ", label) + default: + fmt.Printf("waiting: %s\n", label) + time.Sleep(throttle) + } + } +} diff --git a/itests/mempool_test.go b/itests/mempool_test.go index 51e0afafd..a1c2a330e 100644 --- a/itests/mempool_test.go +++ b/itests/mempool_test.go @@ -3,7 +3,6 @@ package itests import ( "context" - "fmt" "testing" "time" @@ -14,6 +13,9 @@ import ( "github.com/stretchr/testify/require" ) +const mPoolThrottle = time.Millisecond * 100 +const mPoolTimeout = time.Second * 10 + func TestMemPoolPushSingleNode(t *testing.T) { //stm: @CHAIN_MEMPOOL_CREATE_MSG_CHAINS_001, @CHAIN_MEMPOOL_SELECT_001 //stm: @CHAIN_MEMPOOL_PENDING_001, @CHAIN_STATE_WAIT_MSG_001, @CHAIN_MEMPOOL_CAP_GAS_FEE_001 @@ -21,7 +23,7 @@ func TestMemPoolPushSingleNode(t *testing.T) { ctx := context.Background() const blockTime = 100 * time.Millisecond firstNode, _, _, ens := kit.EnsembleTwoOne(t, kit.MockProofs()) - ens.InterconnectAll().BeginMining(blockTime) + ens.InterconnectAll() kit.QuietMiningLogs() sender := firstNode.DefaultKey.Address @@ -53,13 +55,18 @@ func TestMemPoolPushSingleNode(t *testing.T) { } // check pending messages for address - msgStatuses, _ := firstNode.MpoolCheckPendingMessages(ctx, sender) - require.Equal(t, totalMessages, len(msgStatuses)) - for _, msgStatusList := range msgStatuses { - for _, status := range msgStatusList { - require.True(t, status.OK) + kit.CircuitBreaker(t, "push messages", mPoolThrottle, mPoolTimeout, func() bool { + msgStatuses, _ := firstNode.MpoolCheckPendingMessages(ctx, sender) + if len(msgStatuses) == totalMessages { + for _, msgStatusList := range msgStatuses { + for _, status := range msgStatusList { + require.True(t, status.OK) + } + } + return true } - } + return false + }) // verify messages should be the ones included in the next block selected, _ := firstNode.MpoolSelect(ctx, types.EmptyTSK, 0) @@ -74,19 +81,24 @@ func TestMemPoolPushSingleNode(t *testing.T) { require.True(t, found) } - time.Sleep(10 * blockTime) + ens.BeginMining(blockTime) - // pool pending list should be empty - pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) - require.NoError(t, err) - require.Equal(t, 0, len(pending)) - - // all messages should be added to the chain - for _, lookMsg := range sms { - msgLookup, err := firstNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + kit.CircuitBreaker(t, "mine messages", mPoolThrottle, mPoolTimeout, func() bool { + // pool pending list should be empty + pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) require.NoError(t, err) - require.NotNil(t, msgLookup) - } + + if len(pending) == 0 { + // all messages should be added to the chain + for _, lookMsg := range sms { + msgLookup, err := firstNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + require.NoError(t, err) + require.NotNil(t, msgLookup) + } + return true + } + return false + }) } func TestMemPoolPushTwoNodes(t *testing.T) { @@ -96,7 +108,7 @@ func TestMemPoolPushTwoNodes(t *testing.T) { ctx := context.Background() const blockTime = 100 * time.Millisecond firstNode, secondNode, _, ens := kit.EnsembleTwoOne(t, kit.MockProofs()) - ens.InterconnectAll().BeginMining(blockTime) + ens.InterconnectAll() kit.QuietMiningLogs() sender := firstNode.DefaultKey.Address @@ -141,26 +153,30 @@ func TestMemPoolPushTwoNodes(t *testing.T) { sms = append(sms, sm2) } - time.Sleep(10 * blockTime) + ens.BeginMining(blockTime) - pending1, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) - require.NoError(t, err) - require.Equal(t, 0, len(pending1)) - - pending2, err := secondNode.MpoolPending(context.TODO(), types.EmptyTSK) - require.NoError(t, err) - require.Equal(t, 0, len(pending2)) - - // Check messages on both nodes - for _, lookMsg := range sms { - msgLookup1, err := firstNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + kit.CircuitBreaker(t, "push & mine messages", mPoolThrottle, mPoolTimeout, func() bool { + pending1, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) require.NoError(t, err) - require.NotNil(t, msgLookup1) - msgLookup2, err := secondNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + pending2, err := secondNode.MpoolPending(context.TODO(), types.EmptyTSK) require.NoError(t, err) - require.NotNil(t, msgLookup2) - } + + if len(pending1) == 0 && len(pending2) == 0 { + // Check messages on both nodes + for _, lookMsg := range sms { + msgLookup1, err := firstNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + require.NoError(t, err) + require.NotNil(t, msgLookup1) + + msgLookup2, err := secondNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + require.NoError(t, err) + require.NotNil(t, msgLookup2) + } + return true + } + return false + }) } func TestMemPoolClearPending(t *testing.T) { @@ -169,7 +185,7 @@ func TestMemPoolClearPending(t *testing.T) { ctx := context.Background() const blockTime = 100 * time.Millisecond firstNode, _, _, ens := kit.EnsembleTwoOne(t, kit.MockProofs()) - ens.InterconnectAll().BeginMining(blockTime) + ens.InterconnectAll() kit.QuietMiningLogs() sender := firstNode.DefaultKey.Address @@ -192,17 +208,30 @@ func TestMemPoolClearPending(t *testing.T) { _, err = firstNode.MpoolPushMessage(ctx, msg, nil) require.NoError(t, err) + // message should be in the mempool + kit.CircuitBreaker(t, "push message", mPoolThrottle, mPoolTimeout, func() bool { + pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) + require.NoError(t, err) + + return len(pending) == 1 + }) + err = firstNode.MpoolClear(ctx, true) require.NoError(t, err) // pool should be empty now - pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) - require.NoError(t, err) - require.Equal(t, 0, len(pending)) + kit.CircuitBreaker(t, "clear mempool", mPoolThrottle, mPoolTimeout, func() bool { + pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) + require.NoError(t, err) - time.Sleep(2 * blockTime) + return len(pending) == 0 + }) - // waiting for the message should produce nothing + // mine a couple of blocks + ens.BeginMining(blockTime) + time.Sleep(5 * blockTime) + + // make sure that the cleared message wasn't picked up and mined _, err = firstNode.StateWaitMsg(ctx, msg.Cid(), 3, api.LookbackNoLimit, true) require.Error(t, err) } @@ -215,7 +244,7 @@ func TestMemPoolBatchPush(t *testing.T) { ctx := context.Background() const blockTime = 100 * time.Millisecond firstNode, _, _, ens := kit.EnsembleTwoOne(t, kit.MockProofs()) - ens.InterconnectAll().BeginMining(blockTime) + ens.InterconnectAll() kit.QuietMiningLogs() sender := firstNode.DefaultKey.Address @@ -252,14 +281,20 @@ func TestMemPoolBatchPush(t *testing.T) { require.NoError(t, err) // check pending messages for address - msgStatuses, err := firstNode.MpoolCheckPendingMessages(ctx, sender) - require.NoError(t, err) - require.Equal(t, totalMessages, len(msgStatuses)) - for _, msgStatusList := range msgStatuses { - for _, status := range msgStatusList { - require.True(t, status.OK) + kit.CircuitBreaker(t, "batch push", mPoolThrottle, mPoolTimeout, func() bool { + msgStatuses, err := firstNode.MpoolCheckPendingMessages(ctx, sender) + require.NoError(t, err) + + if len(msgStatuses) == totalMessages { + for _, msgStatusList := range msgStatuses { + for _, status := range msgStatusList { + require.True(t, status.OK) + } + } + return true } - } + return false + }) // verify messages should be the ones included in the next block selected, _ := firstNode.MpoolSelect(ctx, types.EmptyTSK, 0) @@ -275,19 +310,24 @@ func TestMemPoolBatchPush(t *testing.T) { require.True(t, found) } - time.Sleep(10 * blockTime) + ens.BeginMining(blockTime) - // pool pending list should be empty - pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) - require.NoError(t, err) - require.Equal(t, 0, len(pending)) - - // all messages should be added to the chain - for _, lookMsg := range sms { - msgLookup, err := firstNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + kit.CircuitBreaker(t, "mine messages", mPoolThrottle, mPoolTimeout, func() bool { + // pool pending list should be empty + pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) require.NoError(t, err) - require.NotNil(t, msgLookup) - } + + if len(pending) == 0 { + // all messages should be added to the chain + for _, lookMsg := range sms { + msgLookup, err := firstNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + require.NoError(t, err) + require.NotNil(t, msgLookup) + } + return true + } + return false + }) } func TestMemPoolPushSingleNodeUntrusted(t *testing.T) { @@ -298,7 +338,7 @@ func TestMemPoolPushSingleNodeUntrusted(t *testing.T) { ctx := context.Background() const blockTime = 100 * time.Millisecond firstNode, _, _, ens := kit.EnsembleTwoOne(t, kit.MockProofs()) - ens.InterconnectAll().BeginMining(blockTime) + ens.InterconnectAll() kit.QuietMiningLogs() sender := firstNode.DefaultKey.Address @@ -336,14 +376,20 @@ func TestMemPoolPushSingleNodeUntrusted(t *testing.T) { sms = append(sms, signedMessage) } - // check pending messages for address - msgStatuses, _ := firstNode.MpoolCheckPendingMessages(ctx, sender) - require.Equal(t, totalMessages, len(msgStatuses)) - for _, msgStatusList := range msgStatuses { - for _, status := range msgStatusList { - require.True(t, status.OK) + kit.CircuitBreaker(t, "push untrusted messages", mPoolThrottle, mPoolTimeout, func() bool { + // check pending messages for address + msgStatuses, _ := firstNode.MpoolCheckPendingMessages(ctx, sender) + + if len(msgStatuses) == totalMessages { + for _, msgStatusList := range msgStatuses { + for _, status := range msgStatusList { + require.True(t, status.OK) + } + } + return true } - } + return false + }) // verify messages should be the ones included in the next block selected, _ := firstNode.MpoolSelect(ctx, types.EmptyTSK, 0) @@ -358,19 +404,25 @@ func TestMemPoolPushSingleNodeUntrusted(t *testing.T) { require.True(t, found) } - time.Sleep(10 * blockTime) + ens.BeginMining(blockTime) - // pool pending list should be empty - pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) - require.NoError(t, err) - require.Equal(t, 0, len(pending)) - - // all messages should be added to the chain - for _, lookMsg := range sms { - msgLookup, err := firstNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + kit.CircuitBreaker(t, "mine untrusted messages", mPoolThrottle, mPoolTimeout, func() bool { + // pool pending list should be empty + pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) require.NoError(t, err) - require.NotNil(t, msgLookup) - } + + if len(pending) == 0 { + // all messages should be added to the chain + for _, lookMsg := range sms { + msgLookup, err := firstNode.StateWaitMsg(ctx, lookMsg.Cid(), 3, api.LookbackNoLimit, true) + require.NoError(t, err) + require.NotNil(t, msgLookup) + } + return true + } + return false + }) + } func TestMemPoolBatchPushUntrusted(t *testing.T) { @@ -418,8 +470,7 @@ func TestMemPoolBatchPushUntrusted(t *testing.T) { require.NoError(t, err) // check pending messages for address, wait until they are all pushed - timeout := time.After(time.Second * 10) - for { + kit.CircuitBreaker(t, "push untrusted messages", mPoolThrottle, mPoolTimeout, func() bool { msgStatuses, err := firstNode.MpoolCheckPendingMessages(ctx, sender) require.NoError(t, err) @@ -429,17 +480,10 @@ func TestMemPoolBatchPushUntrusted(t *testing.T) { require.True(t, status.OK) } } - break + return true } - - select { - case <-timeout: - t.Fatal("waiting for batch push timed out") - default: - fmt.Printf("waiting for %d more messages to be pushed\n", len(msgStatuses)-totalMessages) - time.Sleep(time.Millisecond * 100) - } - } + return false + }) // verify messages should be the ones included in the next block selected, _ := firstNode.MpoolSelect(ctx, types.EmptyTSK, 0) @@ -457,10 +501,8 @@ func TestMemPoolBatchPushUntrusted(t *testing.T) { ens.BeginMining(blockTime) - // wait until pending messages are mined - timeout = time.After(time.Second * 10) - for { - // pool pending list should be empty + // wait until pending messages are mined, pool pending list should be empty + kit.CircuitBreaker(t, "mine untrusted messages", mPoolThrottle, mPoolTimeout, func() bool { pending, err := firstNode.MpoolPending(context.TODO(), types.EmptyTSK) require.NoError(t, err) @@ -471,15 +513,9 @@ func TestMemPoolBatchPushUntrusted(t *testing.T) { require.NoError(t, err) require.NotNil(t, msgLookup) } - break + return true } + return false + }) - select { - case <-timeout: - t.Fatal("waiting for pending messages to be mined timed out") - default: - fmt.Printf("waiting for %d more messages to be mined\n", len(pending)) - time.Sleep(time.Millisecond * 100) - } - } } From e6c8c9a6ab8a8ad148a9edec3995bc1b405652b3 Mon Sep 17 00:00:00 2001 From: Nikola Divic Date: Sun, 13 Feb 2022 17:14:27 +0100 Subject: [PATCH 34/82] doc: add stm annotation to cli chain tests --- cli/chain_test.go | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/cli/chain_test.go b/cli/chain_test.go index f3da80197..841cfb689 100644 --- a/cli/chain_test.go +++ b/cli/chain_test.go @@ -32,6 +32,7 @@ func TestChainHead(t *testing.T) { mockApi.EXPECT().ChainHead(ctx).Return(ts, nil), ) + //stm: @CLI_CHAIN_HEAD_001 err := app.Run([]string{"chain", "head"}) assert.NoError(t, err) @@ -55,6 +56,7 @@ func TestGetBlock(t *testing.T) { mockApi.EXPECT().ChainGetParentReceipts(ctx, block.Cid()).Return([]*types.MessageReceipt{}, nil), ) + //stm: @CLI_CHAIN_GET_BLOCK_001 err := app.Run([]string{"chain", "getblock", block.Cid().String()}) assert.NoError(t, err) @@ -89,6 +91,7 @@ func TestReadOjb(t *testing.T) { mockApi.EXPECT().ChainReadObj(ctx, block.Cid()).Return(obj.Bytes(), nil), ) + //stm: @CLI_CHAIN_READ_OBJECT_001 err = app.Run([]string{"chain", "read-obj", block.Cid().String()}) assert.NoError(t, err) @@ -104,6 +107,7 @@ func TestChainDeleteObj(t *testing.T) { app, _, _, done := NewMockAppWithFullAPI(t, cmd) defer done() + //stm: @CLI_CHAIN_DELETE_OBJECT_002 err := app.Run([]string{"chain", "delete-obj", block.Cid().String()}) assert.Error(t, err) }) @@ -120,6 +124,7 @@ func TestChainDeleteObj(t *testing.T) { mockApi.EXPECT().ChainDeleteObj(ctx, block.Cid()).Return(nil), ) + //stm: @CLI_CHAIN_DELETE_OBJECT_001 err := app.Run([]string{"chain", "delete-obj", "--really-do-it=true", block.Cid().String()}) assert.NoError(t, err) @@ -152,6 +157,7 @@ func TestChainStatObj(t *testing.T) { mockApi.EXPECT().ChainStatObj(ctx, block.Cid(), cid.Undef).Return(stat, nil), ) + //stm: @CLI_CHAIN_STAT_OBJECT_001 err := app.Run([]string{"chain", "stat-obj", block.Cid().String()}) assert.NoError(t, err) @@ -170,6 +176,7 @@ func TestChainStatObj(t *testing.T) { mockApi.EXPECT().ChainStatObj(ctx, block.Cid(), block.Cid()).Return(stat, nil), ) + //stm: @CLI_CHAIN_STAT_OBJECT_002 err := app.Run([]string{"chain", "stat-obj", fmt.Sprintf("-base=%s", block.Cid().String()), block.Cid().String()}) assert.NoError(t, err) @@ -200,6 +207,7 @@ func TestChainGetMsg(t *testing.T) { mockApi.EXPECT().ChainReadObj(ctx, msg.Cid()).Return(obj.Bytes(), nil), ) + //stm: @CLI_CHAIN_GET_MESSAGE_001 err = app.Run([]string{"chain", "getmessage", msg.Cid().String()}) assert.NoError(t, err) @@ -229,6 +237,7 @@ func TestSetHead(t *testing.T) { mockApi.EXPECT().ChainSetHead(ctx, genesis.Key()).Return(nil), ) + //stm: @CLI_CHAIN_SET_HEAD_003 err := app.Run([]string{"chain", "sethead", "-genesis=true", ts.Key().String()}) assert.NoError(t, err) }) @@ -246,6 +255,7 @@ func TestSetHead(t *testing.T) { mockApi.EXPECT().ChainSetHead(ctx, genesis.Key()).Return(nil), ) + //stm: @CLI_CHAIN_SET_HEAD_002 err := app.Run([]string{"chain", "sethead", fmt.Sprintf("-epoch=%s", epoch), ts.Key().String()}) assert.NoError(t, err) }) @@ -263,8 +273,7 @@ func TestSetHead(t *testing.T) { mockApi.EXPECT().ChainSetHead(ctx, ts.Key()).Return(nil), ) - // ts.Key should be passed as an array of arguments (CIDs) - // since we have only one CID in the key, this is ok + //stm: @CLI_CHAIN_SET_HEAD_001 err := app.Run([]string{"chain", "sethead", ts.Key().Cids()[0].String()}) assert.NoError(t, err) }) @@ -303,6 +312,7 @@ func TestInspectUsage(t *testing.T) { mockApi.EXPECT().StateGetActor(ctx, *to, ts.Key()).Return(actor, nil), ) + //stm: @CLI_CHAIN_INSPECT_USAGE_001 err := app.Run([]string{"chain", "inspect-usage"}) assert.NoError(t, err) @@ -352,6 +362,7 @@ func TestChainList(t *testing.T) { mockApi.EXPECT().ChainGetBlockMessages(ctx, head.Blocks()[0].Cid()).Return(blockMsgs, nil), ) + //stm: CLI_CHAIN_LIST_001 err := app.Run([]string{"chain", "love", "--gas-stats=true"}) // chain is love ❤️ assert.NoError(t, err) @@ -382,6 +393,7 @@ func TestChainGet(t *testing.T) { mockApi.EXPECT().ChainGetNode(ctx, path).Return(&api.IpldObject{Cid: blk.Cid(), Obj: blk}, nil), ) + //stm: @CLI_CHAIN_GET_001 err := app.Run([]string{"chain", "get", path}) assert.NoError(t, err) @@ -407,6 +419,7 @@ func TestChainGet(t *testing.T) { mockApi.EXPECT().ChainGetNode(ctx, p2).Return(&api.IpldObject{Cid: blk.Cid(), Obj: blk}, nil), ) + //stm: @CLI_CHAIN_GET_002 err := app.Run([]string{"chain", "get", p1}) assert.NoError(t, err) @@ -430,6 +443,7 @@ func TestChainGet(t *testing.T) { mockApi.EXPECT().ChainGetNode(ctx, path).Return(&api.IpldObject{Cid: blk.Cid(), Obj: blk}, nil), ) + //stm: @CLI_CHAIN_GET_004 err := app.Run([]string{"chain", "get", "-as-type=foo", path}) assert.Error(t, err) }) @@ -465,6 +479,7 @@ func TestChainBisect(t *testing.T) { mockApi.EXPECT().ChainGetNode(ctx, path).Return(&api.IpldObject{Cid: blk2.Cid(), Obj: blk2}, nil), ) + //stm: @CLI_CHAIN_BISECT_001 err := app.Run([]string{"chain", "bisect", fmt.Sprintf("%d", minHeight), fmt.Sprintf("%d", maxHeight), subpath, shell}) assert.NoError(t, err) @@ -497,6 +512,7 @@ func TestChainExport(t *testing.T) { mockApi.EXPECT().ChainExport(ctx, abi.ChainEpoch(0), false, ts.Key()).Return(export, nil), ) + //stm: @CLI_CHAIN_EXPORT_001 err := app.Run([]string{"chain", "export", "whatever.car"}) assert.NoError(t, err) @@ -522,6 +538,7 @@ func TestChainGasPrice(t *testing.T) { calls++ }) + //stm: @CLI_CHAIN_GAS_PRICE_001 err := app.Run([]string{"chain", "gas-price"}) assert.NoError(t, err) From b576785aac0e1cbcde8a0f59eb0cfc196e9c03dc Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 14 Feb 2022 16:03:17 +0200 Subject: [PATCH 35/82] rename GetHotView to IsHotView --- blockstore/context.go | 4 ++-- blockstore/splitstore/splitstore.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/blockstore/context.go b/blockstore/context.go index 61cb93b30..ebb6fafe3 100644 --- a/blockstore/context.go +++ b/blockstore/context.go @@ -14,8 +14,8 @@ func WithHotView(ctx context.Context) context.Context { return context.WithValue(ctx, hotView, struct{}{}) } -// GetHotView returns true if the hot view option is set in the context -func GetHotView(ctx context.Context) bool { +// IsHotView returns true if the hot view option is set in the context +func IsHotView(ctx context.Context) bool { v := ctx.Value(hotView) return v != nil } diff --git a/blockstore/splitstore/splitstore.go b/blockstore/splitstore/splitstore.go index 5c2cf7203..6f499b366 100644 --- a/blockstore/splitstore/splitstore.go +++ b/blockstore/splitstore/splitstore.go @@ -275,7 +275,7 @@ func (s *SplitStore) Has(ctx context.Context, cid cid.Cid) (bool, error) { } has, err = s.cold.Has(ctx, cid) - if has && bstore.GetHotView(ctx) { + if has && bstore.IsHotView(ctx) { s.reifyColdObject(cid) } @@ -324,7 +324,7 @@ func (s *SplitStore) Get(ctx context.Context, cid cid.Cid) (blocks.Block, error) blk, err = s.cold.Get(ctx, cid) if err == nil { - if bstore.GetHotView(ctx) { + if bstore.IsHotView(ctx) { s.reifyColdObject(cid) } @@ -378,7 +378,7 @@ func (s *SplitStore) GetSize(ctx context.Context, cid cid.Cid) (int, error) { size, err = s.cold.GetSize(ctx, cid) if err == nil { - if bstore.GetHotView(ctx) { + if bstore.IsHotView(ctx) { s.reifyColdObject(cid) } @@ -559,7 +559,7 @@ func (s *SplitStore) View(ctx context.Context, cid cid.Cid, cb func([]byte) erro err = s.cold.View(ctx, cid, cb) if err == nil { - if bstore.GetHotView(ctx) { + if bstore.IsHotView(ctx) { s.reifyColdObject(cid) } From a428f4479356260cf035add6867d3c7a20c8a307 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 14 Feb 2022 16:04:39 +0200 Subject: [PATCH 36/82] don't reify objects while still warming up --- blockstore/splitstore/splitstore_reify.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/blockstore/splitstore/splitstore_reify.go b/blockstore/splitstore/splitstore_reify.go index 3c65bbce8..3e6d7cd22 100644 --- a/blockstore/splitstore/splitstore_reify.go +++ b/blockstore/splitstore/splitstore_reify.go @@ -11,6 +11,10 @@ import ( ) func (s *SplitStore) reifyColdObject(c cid.Cid) { + if !s.isWarm() { + return + } + if isUnitaryObject(c) { return } From 6c7ababd3f145bb494691d073bab6cd4bb6a4930 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 14 Feb 2022 16:06:12 +0200 Subject: [PATCH 37/82] add comment about trackTxnRefs being noops if txnActive is false --- blockstore/splitstore/splitstore_reify.go | 1 + 1 file changed, 1 insertion(+) diff --git a/blockstore/splitstore/splitstore_reify.go b/blockstore/splitstore/splitstore_reify.go index 3e6d7cd22..daa10c513 100644 --- a/blockstore/splitstore/splitstore_reify.go +++ b/blockstore/splitstore/splitstore_reify.go @@ -179,6 +179,7 @@ func (s *SplitStore) doReify(c cid.Cid) { } } } else { + // if txnActive is false these are noops if len(toreify) > 0 { s.trackTxnRefMany(toreify) } From 4524fbe936134f60e1005326418b6deafb002c63 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 14 Feb 2022 16:10:54 +0200 Subject: [PATCH 38/82] wait for reify workers to finish when closing --- blockstore/splitstore/splitstore.go | 2 ++ blockstore/splitstore/splitstore_reify.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/blockstore/splitstore/splitstore.go b/blockstore/splitstore/splitstore.go index 6f499b366..a351df76a 100644 --- a/blockstore/splitstore/splitstore.go +++ b/blockstore/splitstore/splitstore.go @@ -162,6 +162,7 @@ type SplitStore struct { txnSync bool // background cold object reification + reifyWorkers sync.WaitGroup reifyMx sync.Mutex reifyCond sync.Cond reifyPend map[cid.Cid]struct{} @@ -707,6 +708,7 @@ func (s *SplitStore) Close() error { } s.reifyCond.Broadcast() + s.reifyWorkers.Wait() s.cancel() return multierr.Combine(s.markSetEnv.Close(), s.debug.Close()) } diff --git a/blockstore/splitstore/splitstore_reify.go b/blockstore/splitstore/splitstore_reify.go index daa10c513..cb86c9789 100644 --- a/blockstore/splitstore/splitstore_reify.go +++ b/blockstore/splitstore/splitstore_reify.go @@ -41,6 +41,7 @@ func (s *SplitStore) reifyOrchestrator() { defer close(workch) for i := 0; i < workers; i++ { + s.reifyWorkers.Add(1) go s.reifyWorker(workch) } @@ -70,6 +71,7 @@ func (s *SplitStore) reifyOrchestrator() { } func (s *SplitStore) reifyWorker(workch chan cid.Cid) { + defer s.reifyWorkers.Done() for c := range workch { s.doReify(c) } From 6bcade5e6decb3dfd1671cc0247fba308834cce3 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 14 Feb 2022 16:13:54 +0200 Subject: [PATCH 39/82] add comment about bigness of reification batch --- blockstore/splitstore/splitstore_reify.go | 1 + 1 file changed, 1 insertion(+) diff --git a/blockstore/splitstore/splitstore_reify.go b/blockstore/splitstore/splitstore_reify.go index cb86c9789..14648a652 100644 --- a/blockstore/splitstore/splitstore_reify.go +++ b/blockstore/splitstore/splitstore_reify.go @@ -146,6 +146,7 @@ func (s *SplitStore) doReify(c cid.Cid) { log.Debugf("reifying %d objects rooted at %s", len(toreify), c) + // this should not get too big, maybe some 100s of objects. batch := make([]blocks.Block, 0, len(toreify)) for _, c := range toreify { blk, err := s.cold.Get(s.ctx, c) From 2c06eb76d6b7afaf00159bd5c96ea60484de30f1 Mon Sep 17 00:00:00 2001 From: zenground0 Date: Mon, 14 Feb 2022 09:47:18 -0500 Subject: [PATCH 40/82] Improve MineBlocksMustPost use it in PaychAPI itest --- itests/kit/blockminer.go | 96 +++++++++++++++++++++------------------- itests/paych_api_test.go | 2 +- 2 files changed, 52 insertions(+), 46 deletions(-) diff --git a/itests/kit/blockminer.go b/itests/kit/blockminer.go index 91ddc2e26..cae3d26e3 100644 --- a/itests/kit/blockminer.go +++ b/itests/kit/blockminer.go @@ -10,6 +10,7 @@ import ( "github.com/filecoin-project/go-bitfield" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/dline" "github.com/filecoin-project/lotus/api" aminer "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" @@ -82,11 +83,49 @@ func (p *partitionTracker) recordIfPost(t *testing.T, bm *BlockMiner, smsg *type return } +func (bm *BlockMiner) forcePoSt(ctx context.Context, ts *types.TipSet, dlinfo *dline.Info) { + + tracker := newPartitionTracker(ctx, dlinfo.Index, bm) + if !tracker.done(bm.t) { // need to wait for post + bm.t.Logf("expect %d partitions proved but only see %d", len(tracker.partitions), tracker.count(bm.t)) + poolEvts, err := bm.miner.FullNode.MpoolSub(ctx) //subscribe before checking pending so we don't miss any events + require.NoError(bm.t, err) + + // First check pending messages we'll mine this epoch + msgs, err := bm.miner.FullNode.MpoolPending(ctx, types.EmptyTSK) + require.NoError(bm.t, err) + for _, msg := range msgs { + tracker.recordIfPost(bm.t, bm, msg) + } + + // post not yet in mpool, wait for it + if !tracker.done(bm.t) { + bm.t.Logf("post missing from mpool, block mining suspended until it arrives") + POOL: + for { + bm.t.Logf("mpool event wait loop at block height %d, ts: %s", ts.Height(), ts.Key()) + select { + case <-ctx.Done(): + return + case evt := <-poolEvts: + bm.t.Logf("pool event: %d", evt.Type) + if evt.Type == api.MpoolAdd { + bm.t.Logf("incoming message %v", evt.Message) + if tracker.recordIfPost(bm.t, bm, evt.Message) { + break POOL + } + } + } + } + bm.t.Logf("done waiting on mpool") + } + } +} + // Like MineBlocks but refuses to mine until the window post scheduler has wdpost messages in the mempool // and everything shuts down if a post fails. It also enforces that every block mined succeeds func (bm *BlockMiner) MineBlocksMustPost(ctx context.Context, blocktime time.Duration) { - - time.Sleep(3 * time.Second) + time.Sleep(time.Second) // wrap context in a cancellable context. ctx, bm.cancel = context.WithCancel(ctx) @@ -94,8 +133,6 @@ func (bm *BlockMiner) MineBlocksMustPost(ctx context.Context, blocktime time.Dur go func() { defer bm.wg.Done() - activeDeadlines := make(map[int]struct{}) - _ = activeDeadlines ts, err := bm.miner.FullNode.ChainHead(ctx) require.NoError(bm.t, err) wait := make(chan bool) @@ -103,7 +140,7 @@ func (bm *BlockMiner) MineBlocksMustPost(ctx context.Context, blocktime time.Dur require.NoError(bm.t, err) // read current out curr := <-chg - require.Equal(bm.t, ts.Height(), curr[0].Val.Height()) + require.Equal(bm.t, ts.Height(), curr[0].Val.Height(), "failed sanity check: are multiple miners mining with must post?") for { select { case <-time.After(blocktime): @@ -111,52 +148,15 @@ func (bm *BlockMiner) MineBlocksMustPost(ctx context.Context, blocktime time.Dur return } nulls := atomic.SwapInt64(&bm.nextNulls, 0) - require.Equal(bm.t, int64(0), nulls, "Injecting > 0 null blocks while `MustPost` mining is currently unsupported") // Wake up and figure out if we are at the end of an active deadline ts, err := bm.miner.FullNode.ChainHead(ctx) require.NoError(bm.t, err) - tsk := ts.Key() - dlinfo, err := bm.miner.FullNode.StateMinerProvingDeadline(ctx, bm.miner.ActorAddr, tsk) + dlinfo, err := bm.miner.FullNode.StateMinerProvingDeadline(ctx, bm.miner.ActorAddr, ts.Key()) require.NoError(bm.t, err) - if ts.Height()+1 == dlinfo.Last() { // Last epoch in dline, we need to check that miner has posted - - tracker := newPartitionTracker(ctx, dlinfo.Index, bm) - if !tracker.done(bm.t) { // need to wait for post - bm.t.Logf("expect %d partitions proved but only see %d", len(tracker.partitions), tracker.count(bm.t)) - poolEvts, err := bm.miner.FullNode.MpoolSub(ctx) - require.NoError(bm.t, err) - - // First check pending messages we'll mine this epoch - msgs, err := bm.miner.FullNode.MpoolPending(ctx, types.EmptyTSK) - require.NoError(bm.t, err) - for _, msg := range msgs { - tracker.recordIfPost(bm.t, bm, msg) - } - - // post not yet in mpool, wait for it - if !tracker.done(bm.t) { - bm.t.Logf("post missing from mpool, block mining suspended until it arrives") - POOL: - for { - bm.t.Logf("mpool event wait loop at block height %d, ts: %s", ts.Height(), ts.Key()) - select { - case <-ctx.Done(): - return - case evt := <-poolEvts: - bm.t.Logf("pool event: %d", evt.Type) - if evt.Type == api.MpoolAdd { - bm.t.Logf("incoming message %v", evt.Message) - if tracker.recordIfPost(bm.t, bm, evt.Message) { - break POOL - } - } - } - } - bm.t.Logf("done waiting on mpool") - } - } + if ts.Height()+1+abi.ChainEpoch(nulls) >= dlinfo.Last() { // Next block brings us past the last epoch in dline, we need to wait for miner to post + bm.forcePoSt(ctx, ts, dlinfo) } var target abi.ChainEpoch @@ -173,6 +173,12 @@ func (bm *BlockMiner) MineBlocksMustPost(ctx context.Context, blocktime time.Dur Done: reportSuccessFn, }) success = <-wait + if !success { + // if we are mining a new null block and it brings us past deadline boundary we need to wait for miner to post + if ts.Height()+1+abi.ChainEpoch(nulls+i) >= dlinfo.Last() { + bm.forcePoSt(ctx, ts, dlinfo) + } + } } // Wait until it shows up on the given full nodes ChainHead diff --git a/itests/paych_api_test.go b/itests/paych_api_test.go index a07c499f9..7e135a9be 100644 --- a/itests/paych_api_test.go +++ b/itests/paych_api_test.go @@ -51,7 +51,7 @@ func TestPaymentChannelsAPI(t *testing.T) { Miner(&miner, &paymentCreator, kit.WithAllSubsystems()). Start(). InterconnectAll() - bms := ens.BeginMining(blockTime) + bms := ens.BeginMiningMustPost(blockTime) bm := bms[0] // send some funds to register the receiver From b260c849f750ff2ecebd71f99520f210f7dc2b91 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 14 Feb 2022 19:43:07 +0200 Subject: [PATCH 41/82] deps: update go-libp2p-resource-manager to v0.1.4 --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index f22400ded..aa80f0bdc 100644 --- a/go.mod +++ b/go.mod @@ -118,7 +118,7 @@ require ( github.com/libp2p/go-libp2p-pubsub v0.6.1 github.com/libp2p/go-libp2p-quic-transport v0.16.1 github.com/libp2p/go-libp2p-record v0.1.3 - github.com/libp2p/go-libp2p-resource-manager v0.1.3 + github.com/libp2p/go-libp2p-resource-manager v0.1.4 github.com/libp2p/go-libp2p-routing-helpers v0.2.3 github.com/libp2p/go-libp2p-swarm v0.10.1 github.com/libp2p/go-libp2p-tls v0.3.1 diff --git a/go.sum b/go.sum index 6b725b5df..292747fc8 100644 --- a/go.sum +++ b/go.sum @@ -1158,8 +1158,9 @@ github.com/libp2p/go-libp2p-record v0.1.2/go.mod h1:pal0eNcT5nqZaTV7UGhqeGqxFgGd github.com/libp2p/go-libp2p-record v0.1.3 h1:R27hoScIhQf/A8XJZ8lYpnqh9LatJ5YbHs28kCIfql0= github.com/libp2p/go-libp2p-record v0.1.3/go.mod h1:yNUff/adKIfPnYQXgp6FQmNu3gLJ6EMg7+/vv2+9pY4= github.com/libp2p/go-libp2p-resource-manager v0.1.0/go.mod h1:wJPNjeE4XQlxeidwqVY5G6DLOKqFK33u2n8blpl0I6Y= -github.com/libp2p/go-libp2p-resource-manager v0.1.3 h1:Umf0tW6WNXSb6Uoma0YT56azB5iikL/aeGAP7s7+f5o= github.com/libp2p/go-libp2p-resource-manager v0.1.3/go.mod h1:wJPNjeE4XQlxeidwqVY5G6DLOKqFK33u2n8blpl0I6Y= +github.com/libp2p/go-libp2p-resource-manager v0.1.4 h1:RcxMD0pytOUimx3BqTVs6IqItb3H5Qg44SD7XyT68lw= +github.com/libp2p/go-libp2p-resource-manager v0.1.4/go.mod h1:wJPNjeE4XQlxeidwqVY5G6DLOKqFK33u2n8blpl0I6Y= github.com/libp2p/go-libp2p-routing v0.0.1/go.mod h1:N51q3yTr4Zdr7V8Jt2JIktVU+3xBBylx1MZeVA6t1Ys= github.com/libp2p/go-libp2p-routing v0.1.0/go.mod h1:zfLhI1RI8RLEzmEaaPwzonRvXeeSHddONWkcTcB54nE= github.com/libp2p/go-libp2p-routing-helpers v0.2.3 h1:xY61alxJ6PurSi+MXbywZpelvuU4U4p/gPTxjqCqTzY= From a9ec40884491533e923569ecb32cf566803d88fb Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 14 Feb 2022 19:46:05 +0200 Subject: [PATCH 42/82] collect resource manager metrics --- metrics/metrics.go | 107 +++++++++++++++++++++++++++++++++ node/modules/lp2p/rcmgr.go | 120 +++++++++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+) diff --git a/metrics/metrics.go b/metrics/metrics.go index b4032bb1d..400e76537 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -47,6 +47,12 @@ var ( WorkerHostname, _ = tag.NewKey("worker_hostname") StorageID, _ = tag.NewKey("storage_id") SectorState, _ = tag.NewKey("sector_state") + + // rcmgr + ServiceID, _ = tag.NewKey("svc") + ProtocolID, _ = tag.NewKey("proto") + Direction, _ = tag.NewKey("direction") + UseFD, _ = tag.NewKey("use_fd") ) // Measures @@ -143,6 +149,22 @@ var ( SplitstoreCompactionHot = stats.Int64("splitstore/hot", "Number of hot blocks in last compaction", stats.UnitDimensionless) SplitstoreCompactionCold = stats.Int64("splitstore/cold", "Number of cold blocks in last compaction", stats.UnitDimensionless) SplitstoreCompactionDead = stats.Int64("splitstore/dead", "Number of dead blocks in last compaction", stats.UnitDimensionless) + + // rcmgr + RcmgrAllowConn = stats.Int64("rcmgr/allow_conn", "Number of allowed connections", stats.UnitDimensionless) + RcmgrBlockConn = stats.Int64("rcmgr/block_conn", "Number of blocked connections", stats.UnitDimensionless) + RcmgrAllowStream = stats.Int64("rcmgr/allow_stream", "Number of allowed streams", stats.UnitDimensionless) + RcmgrBlockStream = stats.Int64("rcmgr/block_stream", "Number of blocked streams", stats.UnitDimensionless) + RcmgrAllowPeer = stats.Int64("rcmgr/allow_peer", "Number of allowed peer connections", stats.UnitDimensionless) + RcmgrBlockPeer = stats.Int64("rcmgr/block_peer", "Number of blocked peer connections", stats.UnitDimensionless) + RcmgrAllowProto = stats.Int64("rcmgr/allow_proto", "Number of allowed streams attached to a protocol", stats.UnitDimensionless) + RcmgrBlockProto = stats.Int64("rcmgr/block_proto", "Number of blocked blocked streams attached to a protocol", stats.UnitDimensionless) + RcmgrBlockProtoPeer = stats.Int64("rcmgr/block_proto", "Number of blocked blocked streams attached to a protocol for a specific peer", stats.UnitDimensionless) + RcmgrAllowSvc = stats.Int64("rcmgr/allow_svc", "Number of allowed streams attached to a service", stats.UnitDimensionless) + RcmgrBlockSvc = stats.Int64("rcmgr/block_svc", "Number of blocked blocked streams attached to a service", stats.UnitDimensionless) + RcmgrBlockSvcPeer = stats.Int64("rcmgr/block_svc", "Number of blocked blocked streams attached to a service for a specific peer", stats.UnitDimensionless) + RcmgrAllowMem = stats.Int64("rcmgr/allow_mem", "Number of allowed memory reservations", stats.UnitDimensionless) + RcmgrBlockMem = stats.Int64("rcmgr/block_mem", "Number of blocked memory reservations", stats.UnitDimensionless) ) var ( @@ -496,6 +518,76 @@ var ( Measure: GraphsyncSendingPeersPending, Aggregation: view.LastValue(), } + + // rcmgr + RcmgrAllowConnView = &view.View{ + Measure: RcmgrAllowConn, + Aggregation: view.Count(), + TagKeys: []tag.Key{Direction, UseFD}, + } + RcmgrBlockConnView = &view.View{ + Measure: RcmgrBlockConn, + Aggregation: view.Count(), + TagKeys: []tag.Key{Direction, UseFD}, + } + RcmgrAllowStreamView = &view.View{ + Measure: RcmgrAllowStream, + Aggregation: view.Count(), + TagKeys: []tag.Key{PeerID, Direction}, + } + RcmgrBlockStreamView = &view.View{ + Measure: RcmgrBlockStream, + Aggregation: view.Count(), + TagKeys: []tag.Key{PeerID, Direction}, + } + RcmgrAllowPeerView = &view.View{ + Measure: RcmgrAllowPeer, + Aggregation: view.Count(), + TagKeys: []tag.Key{PeerID}, + } + RcmgrBlockPeerView = &view.View{ + Measure: RcmgrBlockPeer, + Aggregation: view.Count(), + TagKeys: []tag.Key{PeerID}, + } + RcmgrAllowProtoView = &view.View{ + Measure: RcmgrAllowProto, + Aggregation: view.Count(), + TagKeys: []tag.Key{ProtocolID}, + } + RcmgrBlockProtoView = &view.View{ + Measure: RcmgrBlockProto, + Aggregation: view.Count(), + TagKeys: []tag.Key{ProtocolID}, + } + RcmgrBlockProtoPeerView = &view.View{ + Measure: RcmgrBlockProtoPeer, + Aggregation: view.Count(), + TagKeys: []tag.Key{ProtocolID, PeerID}, + } + RcmgrAllowSvcView = &view.View{ + Measure: RcmgrAllowSvc, + Aggregation: view.Count(), + TagKeys: []tag.Key{ServiceID}, + } + RcmgrBlockSvcView = &view.View{ + Measure: RcmgrBlockSvc, + Aggregation: view.Count(), + TagKeys: []tag.Key{ServiceID}, + } + RcmgrBlockSvcPeerView = &view.View{ + Measure: RcmgrBlockSvcPeer, + Aggregation: view.Count(), + TagKeys: []tag.Key{ServiceID, PeerID}, + } + RcmgrAllowMemView = &view.View{ + Measure: RcmgrAllowMem, + Aggregation: view.Count(), + } + RcmgrBlockMemView = &view.View{ + Measure: RcmgrBlockMem, + Aggregation: view.Count(), + } ) // DefaultViews is an array of OpenCensus views for metric gathering purposes @@ -517,6 +609,21 @@ var DefaultViews = func() []*view.View { GraphsyncSendingTotalMemoryAllocatedView, GraphsyncSendingTotalPendingAllocationsView, GraphsyncSendingPeersPendingView, + + RcmgrAllowConnView, + RcmgrBlockConnView, + RcmgrAllowStreamView, + RcmgrBlockStreamView, + RcmgrAllowPeerView, + RcmgrBlockPeerView, + RcmgrAllowProtoView, + RcmgrBlockProtoView, + RcmgrBlockProtoPeerView, + RcmgrAllowSvcView, + RcmgrBlockSvcView, + RcmgrBlockSvcPeerView, + RcmgrAllowMemView, + RcmgrBlockMemView, } views = append(views, blockstore.DefaultViews...) views = append(views, rpcmetrics.DefaultViews...) diff --git a/node/modules/lp2p/rcmgr.go b/node/modules/lp2p/rcmgr.go index 8b286ff5e..71ed4e754 100644 --- a/node/modules/lp2p/rcmgr.go +++ b/node/modules/lp2p/rcmgr.go @@ -11,9 +11,15 @@ import ( "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p-core/network" + "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p-core/protocol" rcmgr "github.com/libp2p/go-libp2p-resource-manager" + "github.com/filecoin-project/lotus/metrics" "github.com/filecoin-project/lotus/node/repo" + + "go.opencensus.io/stats" + "go.opencensus.io/tag" ) func ResourceManager(lc fx.Lifecycle, repo repo.LockedRepo) (network.ResourceManager, error) { @@ -43,6 +49,8 @@ func ResourceManager(lc fx.Lifecycle, repo repo.LockedRepo) (network.ResourceMan // TODO: also set appropriate default limits for lotus protocols libp2p.SetDefaultServiceLimits(limiter) + opts = append(opts, rcmgr.WithMetrics(rcmgrMetrics{})) + if os.Getenv("LOTUS_DEBUG_RCMGR") != "" { debugPath := filepath.Join(repoPath, "debug") if err := os.MkdirAll(debugPath, 0755); err != nil { @@ -70,3 +78,115 @@ func ResourceManagerOption(mgr network.ResourceManager) Libp2pOpts { Opts: []libp2p.Option{libp2p.ResourceManager(mgr)}, } } + +type rcmgrMetrics struct{} + +func (r rcmgrMetrics) AllowConn(dir network.Direction, usefd bool) { + ctx := context.Background() + if dir == network.DirInbound { + ctx, _ = tag.New(ctx, tag.Upsert(metrics.Direction, "inbound")) + } else { + ctx, _ = tag.New(ctx, tag.Upsert(metrics.Direction, "outbound")) + } + if usefd { + ctx, _ = tag.New(ctx, tag.Upsert(metrics.UseFD, "true")) + } else { + ctx, _ = tag.New(ctx, tag.Upsert(metrics.UseFD, "false")) + } + stats.Record(ctx, metrics.RcmgrAllowConn.M(1)) +} + +func (r rcmgrMetrics) BlockConn(dir network.Direction, usefd bool) { + ctx := context.Background() + if dir == network.DirInbound { + ctx, _ = tag.New(ctx, tag.Upsert(metrics.Direction, "inbound")) + } else { + ctx, _ = tag.New(ctx, tag.Upsert(metrics.Direction, "outbound")) + } + if usefd { + ctx, _ = tag.New(ctx, tag.Upsert(metrics.UseFD, "true")) + } else { + ctx, _ = tag.New(ctx, tag.Upsert(metrics.UseFD, "false")) + } + stats.Record(ctx, metrics.RcmgrBlockConn.M(1)) +} + +func (r rcmgrMetrics) AllowStream(p peer.ID, dir network.Direction) { + ctx := context.Background() + if dir == network.DirInbound { + ctx, _ = tag.New(ctx, tag.Upsert(metrics.Direction, "inbound")) + } else { + ctx, _ = tag.New(ctx, tag.Upsert(metrics.Direction, "outbound")) + } + ctx, _ = tag.New(ctx, tag.Upsert(metrics.PeerID, p.Pretty())) + stats.Record(ctx, metrics.RcmgrAllowStream.M(1)) +} + +func (r rcmgrMetrics) BlockStream(p peer.ID, dir network.Direction) { + ctx := context.Background() + if dir == network.DirInbound { + ctx, _ = tag.New(ctx, tag.Upsert(metrics.Direction, "inbound")) + } else { + ctx, _ = tag.New(ctx, tag.Upsert(metrics.Direction, "outbound")) + } + ctx, _ = tag.New(ctx, tag.Upsert(metrics.PeerID, p.Pretty())) + stats.Record(ctx, metrics.RcmgrBlockStream.M(1)) +} + +func (r rcmgrMetrics) AllowPeer(p peer.ID) { + ctx := context.Background() + ctx, _ = tag.New(ctx, tag.Upsert(metrics.PeerID, p.Pretty())) + stats.Record(ctx, metrics.RcmgrAllowPeer.M(1)) +} + +func (r rcmgrMetrics) BlockPeer(p peer.ID) { + ctx := context.Background() + ctx, _ = tag.New(ctx, tag.Upsert(metrics.PeerID, p.Pretty())) + stats.Record(ctx, metrics.RcmgrBlockPeer.M(1)) +} + +func (r rcmgrMetrics) AllowProtocol(proto protocol.ID) { + ctx := context.Background() + ctx, _ = tag.New(ctx, tag.Upsert(metrics.ProtocolID, string(proto))) + stats.Record(ctx, metrics.RcmgrAllowProto.M(1)) +} + +func (r rcmgrMetrics) BlockProtocol(proto protocol.ID) { + ctx := context.Background() + ctx, _ = tag.New(ctx, tag.Upsert(metrics.ProtocolID, string(proto))) + stats.Record(ctx, metrics.RcmgrBlockProto.M(1)) +} + +func (r rcmgrMetrics) BlockProtocolPeer(proto protocol.ID, p peer.ID) { + ctx := context.Background() + ctx, _ = tag.New(ctx, tag.Upsert(metrics.ProtocolID, string(proto))) + ctx, _ = tag.New(ctx, tag.Upsert(metrics.PeerID, p.Pretty())) + stats.Record(ctx, metrics.RcmgrBlockProtoPeer.M(1)) +} + +func (r rcmgrMetrics) AllowService(svc string) { + ctx := context.Background() + ctx, _ = tag.New(ctx, tag.Upsert(metrics.ServiceID, svc)) + stats.Record(ctx, metrics.RcmgrAllowSvc.M(1)) +} + +func (r rcmgrMetrics) BlockService(svc string) { + ctx := context.Background() + ctx, _ = tag.New(ctx, tag.Upsert(metrics.ServiceID, svc)) + stats.Record(ctx, metrics.RcmgrBlockSvc.M(1)) +} + +func (r rcmgrMetrics) BlockServicePeer(svc string, p peer.ID) { + ctx := context.Background() + ctx, _ = tag.New(ctx, tag.Upsert(metrics.ServiceID, svc)) + ctx, _ = tag.New(ctx, tag.Upsert(metrics.PeerID, p.Pretty())) + stats.Record(ctx, metrics.RcmgrBlockSvcPeer.M(1)) +} + +func (r rcmgrMetrics) AllowMemory(size int) { + stats.Record(context.Background(), metrics.RcmgrAllowMem.M(1)) +} + +func (r rcmgrMetrics) BlockMemory(size int) { + stats.Record(context.Background(), metrics.RcmgrBlockMem.M(1)) +} From 977351f41911a12b81d0521c838e9155fa7c7f32 Mon Sep 17 00:00:00 2001 From: zenground0 Date: Mon, 14 Feb 2022 14:00:41 -0500 Subject: [PATCH 43/82] Fix from Magik to remove hanging behavior --- itests/kit/blockminer.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/itests/kit/blockminer.go b/itests/kit/blockminer.go index cae3d26e3..a232d82e0 100644 --- a/itests/kit/blockminer.go +++ b/itests/kit/blockminer.go @@ -3,6 +3,7 @@ package kit import ( "bytes" "context" + "fmt" "sync" "sync/atomic" "testing" @@ -64,11 +65,10 @@ func (p *partitionTracker) done(t *testing.T) bool { return uint64(len(p.partitions)) == p.count(t) } -func (p *partitionTracker) recordIfPost(t *testing.T, bm *BlockMiner, smsg *types.SignedMessage) (ret bool) { +func (p *partitionTracker) recordIfPost(t *testing.T, bm *BlockMiner, msg *types.Message) (ret bool) { defer func() { ret = p.done(t) }() - msg := smsg.Message if !(msg.To == bm.miner.ActorAddr) { return } @@ -95,7 +95,26 @@ func (bm *BlockMiner) forcePoSt(ctx context.Context, ts *types.TipSet, dlinfo *d msgs, err := bm.miner.FullNode.MpoolPending(ctx, types.EmptyTSK) require.NoError(bm.t, err) for _, msg := range msgs { - tracker.recordIfPost(bm.t, bm, msg) + if tracker.recordIfPost(bm.t, bm, &msg.Message) { + fmt.Printf("found post in mempool pending\n") + } + } + + // Account for included but not yet executed messages + for _, bc := range ts.Cids() { + msgs, err := bm.miner.FullNode.ChainGetBlockMessages(ctx, bc) + require.NoError(bm.t, err) + for _, msg := range msgs.BlsMessages { + if tracker.recordIfPost(bm.t, bm, msg) { + fmt.Printf("found post in message of prev tipset\n") + } + + } + for _, msg := range msgs.SecpkMessages { + if tracker.recordIfPost(bm.t, bm, &msg.Message) { + fmt.Printf("found post in message of prev tipset\n") + } + } } // post not yet in mpool, wait for it @@ -111,7 +130,8 @@ func (bm *BlockMiner) forcePoSt(ctx context.Context, ts *types.TipSet, dlinfo *d bm.t.Logf("pool event: %d", evt.Type) if evt.Type == api.MpoolAdd { bm.t.Logf("incoming message %v", evt.Message) - if tracker.recordIfPost(bm.t, bm, evt.Message) { + if tracker.recordIfPost(bm.t, bm, &evt.Message.Message) { + fmt.Printf("found post in mempool evt\n") break POOL } } From eebe78419123088d05a2b9f0733647e12860c2a1 Mon Sep 17 00:00:00 2001 From: Aayush Date: Mon, 14 Feb 2022 13:28:49 -0500 Subject: [PATCH 44/82] fix: sealer: allow enable/disabling ReplicaUpdate tasks --- cmd/lotus-miner/init.go | 15 +++++++++------ cmd/lotus-seal-worker/main.go | 8 ++++++++ cmd/lotus-seal-worker/tasks.go | 13 ++++++++----- documentation/en/cli-lotus-worker.md | 5 +++-- documentation/en/default-lotus-miner-config.toml | 3 +++ extern/sector-storage/manager.go | 4 ++++ node/config/def.go | 1 + 7 files changed, 36 insertions(+), 13 deletions(-) diff --git a/cmd/lotus-miner/init.go b/cmd/lotus-miner/init.go index ae742c663..59ea75b10 100644 --- a/cmd/lotus-miner/init.go +++ b/cmd/lotus-miner/init.go @@ -467,12 +467,15 @@ func storageMinerInit(ctx context.Context, cctx *cli.Context, api v1api.FullNode stor := stores.NewRemote(lstor, si, http.Header(sa), 10, &stores.DefaultPartialFileHandler{}) smgr, err := sectorstorage.New(ctx, lstor, stor, lr, si, sectorstorage.SealerConfig{ - ParallelFetchLimit: 10, - AllowAddPiece: true, - AllowPreCommit1: true, - AllowPreCommit2: true, - AllowCommit: true, - AllowUnseal: true, + ParallelFetchLimit: 10, + AllowAddPiece: true, + AllowPreCommit1: true, + AllowPreCommit2: true, + AllowCommit: true, + AllowUnseal: true, + AllowReplicaUpdate: true, + AllowProveReplicaUpdate2: true, + AllowRegenSectorKey: true, }, wsts, smsts) if err != nil { return err diff --git a/cmd/lotus-seal-worker/main.go b/cmd/lotus-seal-worker/main.go index 84ff1ccdd..2116dd228 100644 --- a/cmd/lotus-seal-worker/main.go +++ b/cmd/lotus-seal-worker/main.go @@ -173,6 +173,11 @@ var runCmd = &cli.Command{ Usage: "enable prove replica update 2", Value: true, }, + &cli.BoolFlag{ + Name: "regen-sector-key", + Usage: "enable regen sector key", + Value: true, + }, &cli.IntFlag{ Name: "parallel-fetch-limit", Usage: "maximum fetch operations to run in parallel", @@ -284,6 +289,9 @@ var runCmd = &cli.Command{ if cctx.Bool("prove-replica-update2") { taskTypes = append(taskTypes, sealtasks.TTProveReplicaUpdate2) } + if cctx.Bool("regen-sector-key") { + taskTypes = append(taskTypes, sealtasks.TTRegenSectorKey) + } if len(taskTypes) == 0 { return xerrors.Errorf("no task types specified") diff --git a/cmd/lotus-seal-worker/tasks.go b/cmd/lotus-seal-worker/tasks.go index 02e5d6cfd..52133d09d 100644 --- a/cmd/lotus-seal-worker/tasks.go +++ b/cmd/lotus-seal-worker/tasks.go @@ -22,11 +22,14 @@ var tasksCmd = &cli.Command{ } var allowSetting = map[sealtasks.TaskType]struct{}{ - sealtasks.TTAddPiece: {}, - sealtasks.TTPreCommit1: {}, - sealtasks.TTPreCommit2: {}, - sealtasks.TTCommit2: {}, - sealtasks.TTUnseal: {}, + sealtasks.TTAddPiece: {}, + sealtasks.TTPreCommit1: {}, + sealtasks.TTPreCommit2: {}, + sealtasks.TTCommit2: {}, + sealtasks.TTUnseal: {}, + sealtasks.TTReplicaUpdate: {}, + sealtasks.TTProveReplicaUpdate2: {}, + sealtasks.TTRegenSectorKey: {}, } var settableStr = func() string { diff --git a/documentation/en/cli-lotus-worker.md b/documentation/en/cli-lotus-worker.md index c74d8e8da..e610cff62 100644 --- a/documentation/en/cli-lotus-worker.md +++ b/documentation/en/cli-lotus-worker.md @@ -46,6 +46,7 @@ OPTIONS: --commit enable commit (32G sectors: all cores or GPUs, 128GiB Memory + 64GiB swap) (default: true) --replica-update enable replica update (default: true) --prove-replica-update2 enable prove replica update 2 (default: true) + --regen-sector-key enable regen sector key (default: true) --parallel-fetch-limit value maximum fetch operations to run in parallel (default: 5) --timeout value used when 'listen' is unspecified. must be a valid duration recognized by golang's time.ParseDuration function (default: "30m") --help, -h show help (default: false) @@ -170,7 +171,7 @@ NAME: lotus-worker tasks enable - Enable a task type USAGE: - lotus-worker tasks enable [command options] [UNS|C2|PC2|PC1|AP] + lotus-worker tasks enable [command options] [UNS|C2|PC2|PC1|PR2|RU|AP|GSK] OPTIONS: --help, -h show help (default: false) @@ -183,7 +184,7 @@ NAME: lotus-worker tasks disable - Disable a task type USAGE: - lotus-worker tasks disable [command options] [UNS|C2|PC2|PC1|AP] + lotus-worker tasks disable [command options] [UNS|C2|PC2|PC1|PR2|RU|AP|GSK] OPTIONS: --help, -h show help (default: false) diff --git a/documentation/en/default-lotus-miner-config.toml b/documentation/en/default-lotus-miner-config.toml index d8c774c75..818f0b73c 100644 --- a/documentation/en/default-lotus-miner-config.toml +++ b/documentation/en/default-lotus-miner-config.toml @@ -438,6 +438,9 @@ # env var: LOTUS_STORAGE_ALLOWPROVEREPLICAUPDATE2 #AllowProveReplicaUpdate2 = true + # env var: LOTUS_STORAGE_ALLOWREGENSECTORKEY + #AllowRegenSectorKey = true + # env var: LOTUS_STORAGE_RESOURCEFILTERING #ResourceFiltering = "hardware" diff --git a/extern/sector-storage/manager.go b/extern/sector-storage/manager.go index fcbfa2e69..897ba4f06 100644 --- a/extern/sector-storage/manager.go +++ b/extern/sector-storage/manager.go @@ -105,6 +105,7 @@ type SealerConfig struct { AllowUnseal bool AllowReplicaUpdate bool AllowProveReplicaUpdate2 bool + AllowRegenSectorKey bool // ResourceFiltering instructs the system which resource filtering strategy // to use when evaluating tasks against this worker. An empty value defaults @@ -169,6 +170,9 @@ func New(ctx context.Context, lstor *stores.Local, stor *stores.Remote, ls store if sc.AllowProveReplicaUpdate2 { localTasks = append(localTasks, sealtasks.TTProveReplicaUpdate2) } + if sc.AllowRegenSectorKey { + localTasks = append(localTasks, sealtasks.TTRegenSectorKey) + } wcfg := WorkerConfig{ IgnoreResourceFiltering: sc.ResourceFiltering == ResourceFilteringDisabled, diff --git a/node/config/def.go b/node/config/def.go index 157350866..aceeaadf5 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -139,6 +139,7 @@ func DefaultStorageMiner() *StorageMiner { AllowUnseal: true, AllowReplicaUpdate: true, AllowProveReplicaUpdate2: true, + AllowRegenSectorKey: true, // Default to 10 - tcp should still be able to figure this out, and // it's the ratio between 10gbit / 1gbit From 56df886b554c2ac8b7769c234dd4a03174ba56bc Mon Sep 17 00:00:00 2001 From: Aayush Date: Mon, 14 Feb 2022 13:28:49 -0500 Subject: [PATCH 45/82] fix: sealer: allow enable/disabling ReplicaUpdate tasks --- cmd/lotus-miner/init.go | 15 +++++++++------ cmd/lotus-seal-worker/main.go | 8 ++++++++ cmd/lotus-seal-worker/tasks.go | 13 ++++++++----- documentation/en/cli-lotus-worker.md | 5 +++-- documentation/en/default-lotus-miner-config.toml | 3 +++ extern/sector-storage/manager.go | 4 ++++ node/config/def.go | 1 + 7 files changed, 36 insertions(+), 13 deletions(-) diff --git a/cmd/lotus-miner/init.go b/cmd/lotus-miner/init.go index b2199dd94..c43ed6cf0 100644 --- a/cmd/lotus-miner/init.go +++ b/cmd/lotus-miner/init.go @@ -467,12 +467,15 @@ func storageMinerInit(ctx context.Context, cctx *cli.Context, api v1api.FullNode stor := stores.NewRemote(lstor, si, http.Header(sa), 10, &stores.DefaultPartialFileHandler{}) smgr, err := sectorstorage.New(ctx, lstor, stor, lr, si, sectorstorage.SealerConfig{ - ParallelFetchLimit: 10, - AllowAddPiece: true, - AllowPreCommit1: true, - AllowPreCommit2: true, - AllowCommit: true, - AllowUnseal: true, + ParallelFetchLimit: 10, + AllowAddPiece: true, + AllowPreCommit1: true, + AllowPreCommit2: true, + AllowCommit: true, + AllowUnseal: true, + AllowReplicaUpdate: true, + AllowProveReplicaUpdate2: true, + AllowRegenSectorKey: true, }, wsts, smsts) if err != nil { return err diff --git a/cmd/lotus-seal-worker/main.go b/cmd/lotus-seal-worker/main.go index 84ff1ccdd..2116dd228 100644 --- a/cmd/lotus-seal-worker/main.go +++ b/cmd/lotus-seal-worker/main.go @@ -173,6 +173,11 @@ var runCmd = &cli.Command{ Usage: "enable prove replica update 2", Value: true, }, + &cli.BoolFlag{ + Name: "regen-sector-key", + Usage: "enable regen sector key", + Value: true, + }, &cli.IntFlag{ Name: "parallel-fetch-limit", Usage: "maximum fetch operations to run in parallel", @@ -284,6 +289,9 @@ var runCmd = &cli.Command{ if cctx.Bool("prove-replica-update2") { taskTypes = append(taskTypes, sealtasks.TTProveReplicaUpdate2) } + if cctx.Bool("regen-sector-key") { + taskTypes = append(taskTypes, sealtasks.TTRegenSectorKey) + } if len(taskTypes) == 0 { return xerrors.Errorf("no task types specified") diff --git a/cmd/lotus-seal-worker/tasks.go b/cmd/lotus-seal-worker/tasks.go index 02e5d6cfd..52133d09d 100644 --- a/cmd/lotus-seal-worker/tasks.go +++ b/cmd/lotus-seal-worker/tasks.go @@ -22,11 +22,14 @@ var tasksCmd = &cli.Command{ } var allowSetting = map[sealtasks.TaskType]struct{}{ - sealtasks.TTAddPiece: {}, - sealtasks.TTPreCommit1: {}, - sealtasks.TTPreCommit2: {}, - sealtasks.TTCommit2: {}, - sealtasks.TTUnseal: {}, + sealtasks.TTAddPiece: {}, + sealtasks.TTPreCommit1: {}, + sealtasks.TTPreCommit2: {}, + sealtasks.TTCommit2: {}, + sealtasks.TTUnseal: {}, + sealtasks.TTReplicaUpdate: {}, + sealtasks.TTProveReplicaUpdate2: {}, + sealtasks.TTRegenSectorKey: {}, } var settableStr = func() string { diff --git a/documentation/en/cli-lotus-worker.md b/documentation/en/cli-lotus-worker.md index c75fdaa87..29ed4f8bc 100644 --- a/documentation/en/cli-lotus-worker.md +++ b/documentation/en/cli-lotus-worker.md @@ -46,6 +46,7 @@ OPTIONS: --commit enable commit (32G sectors: all cores or GPUs, 128GiB Memory + 64GiB swap) (default: true) --replica-update enable replica update (default: true) --prove-replica-update2 enable prove replica update 2 (default: true) + --regen-sector-key enable regen sector key (default: true) --parallel-fetch-limit value maximum fetch operations to run in parallel (default: 5) --timeout value used when 'listen' is unspecified. must be a valid duration recognized by golang's time.ParseDuration function (default: "30m") --help, -h show help (default: false) @@ -170,7 +171,7 @@ NAME: lotus-worker tasks enable - Enable a task type USAGE: - lotus-worker tasks enable [command options] [UNS|C2|PC2|PC1|AP] + lotus-worker tasks enable [command options] [UNS|C2|PC2|PC1|PR2|RU|AP|GSK] OPTIONS: --help, -h show help (default: false) @@ -183,7 +184,7 @@ NAME: lotus-worker tasks disable - Disable a task type USAGE: - lotus-worker tasks disable [command options] [UNS|C2|PC2|PC1|AP] + lotus-worker tasks disable [command options] [UNS|C2|PC2|PC1|PR2|RU|AP|GSK] OPTIONS: --help, -h show help (default: false) diff --git a/documentation/en/default-lotus-miner-config.toml b/documentation/en/default-lotus-miner-config.toml index 486ffed51..a9a7dbeb5 100644 --- a/documentation/en/default-lotus-miner-config.toml +++ b/documentation/en/default-lotus-miner-config.toml @@ -419,6 +419,9 @@ # env var: LOTUS_STORAGE_ALLOWPROVEREPLICAUPDATE2 #AllowProveReplicaUpdate2 = true + # env var: LOTUS_STORAGE_ALLOWREGENSECTORKEY + #AllowRegenSectorKey = true + # env var: LOTUS_STORAGE_RESOURCEFILTERING #ResourceFiltering = "hardware" diff --git a/extern/sector-storage/manager.go b/extern/sector-storage/manager.go index fcbfa2e69..897ba4f06 100644 --- a/extern/sector-storage/manager.go +++ b/extern/sector-storage/manager.go @@ -105,6 +105,7 @@ type SealerConfig struct { AllowUnseal bool AllowReplicaUpdate bool AllowProveReplicaUpdate2 bool + AllowRegenSectorKey bool // ResourceFiltering instructs the system which resource filtering strategy // to use when evaluating tasks against this worker. An empty value defaults @@ -169,6 +170,9 @@ func New(ctx context.Context, lstor *stores.Local, stor *stores.Remote, ls store if sc.AllowProveReplicaUpdate2 { localTasks = append(localTasks, sealtasks.TTProveReplicaUpdate2) } + if sc.AllowRegenSectorKey { + localTasks = append(localTasks, sealtasks.TTRegenSectorKey) + } wcfg := WorkerConfig{ IgnoreResourceFiltering: sc.ResourceFiltering == ResourceFilteringDisabled, diff --git a/node/config/def.go b/node/config/def.go index e89d480b2..cfd25dc86 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -139,6 +139,7 @@ func DefaultStorageMiner() *StorageMiner { AllowUnseal: true, AllowReplicaUpdate: true, AllowProveReplicaUpdate2: true, + AllowRegenSectorKey: true, // Default to 10 - tcp should still be able to figure this out, and // it's the ratio between 10gbit / 1gbit From 0b7addc031e78f6e0e9a9325e268eafab43ce54f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 15 Feb 2022 14:19:42 +0100 Subject: [PATCH 46/82] lotus-miner sectors list --initial-pledge --- cmd/lotus-miner/sectors.go | 16 +++++++++++++--- documentation/en/cli-lotus-miner.md | 17 +++++++++-------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/cmd/lotus-miner/sectors.go b/cmd/lotus-miner/sectors.go index c779f5a8b..d8c3e9c7c 100644 --- a/cmd/lotus-miner/sectors.go +++ b/cmd/lotus-miner/sectors.go @@ -161,7 +161,7 @@ var sectorsStatusCmd = &cli.Command{ fmt.Printf("Expiration:\t\t%v\n", status.Expiration) fmt.Printf("DealWeight:\t\t%v\n", status.DealWeight) fmt.Printf("VerifiedDealWeight:\t\t%v\n", status.VerifiedDealWeight) - fmt.Printf("InitialPledge:\t\t%v\n", status.InitialPledge) + fmt.Printf("InitialPledge:\t\t%v\n", types.FIL(status.InitialPledge)) fmt.Printf("\nExpiration Info\n") fmt.Printf("OnTime:\t\t%v\n", status.OnTime) fmt.Printf("Early:\t\t%v\n", status.Early) @@ -294,8 +294,14 @@ var sectorsListCmd = &cli.Command{ Aliases: []string{"e"}, }, &cli.BoolFlag{ - Name: "seal-time", - Usage: "display how long it took for the sector to be sealed", + Name: "initial-pledge", + Usage: "display initial pledge", + Aliases: []string{"p"}, + }, + &cli.BoolFlag{ + Name: "seal-time", + Usage: "display how long it took for the sector to be sealed", + Aliases: []string{"t"}, }, &cli.StringFlag{ Name: "states", @@ -405,6 +411,7 @@ var sectorsListCmd = &cli.Command{ tablewriter.Col("Deals"), tablewriter.Col("DealWeight"), tablewriter.Col("VerifiedPower"), + tablewriter.Col("Pledge"), tablewriter.NewLineCol("Error"), tablewriter.NewLineCol("RecoveryTimeout")) @@ -483,6 +490,9 @@ var sectorsListCmd = &cli.Command{ m["RecoveryTimeout"] = color.YellowString(lcli.EpochTime(head.Height(), st.Early)) } } + if inSSet && cctx.Bool("initial-pledge") { + m["Pledge"] = types.FIL(st.InitialPledge).Short() + } } if !fast && deals > 0 { diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index 1fd3e91a3..848a9c864 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -1621,14 +1621,15 @@ USAGE: lotus-miner sectors list [command options] [arguments...] OPTIONS: - --show-removed, -r show removed sectors (default: false) - --color, -c use color in display output (default: depends on output being a TTY) - --fast, -f don't show on-chain info for better performance (default: false) - --events, -e display number of events the sector has received (default: false) - --seal-time display how long it took for the sector to be sealed (default: false) - --states value filter sectors by a comma-separated list of states - --unproven, -u only show sectors which aren't in the 'Proving' state (default: false) - --help, -h show help (default: false) + --show-removed, -r show removed sectors (default: false) + --color, -c use color in display output (default: depends on output being a TTY) + --fast, -f don't show on-chain info for better performance (default: false) + --events, -e display number of events the sector has received (default: false) + --initial-pledge, -p display initial pledge (default: false) + --seal-time, -t display how long it took for the sector to be sealed (default: false) + --states value filter sectors by a comma-separated list of states + --unproven, -u only show sectors which aren't in the 'Proving' state (default: false) + --help, -h show help (default: false) ``` From 9c00af1b860f8b4d5fd2213b2e2ac6c01744ae22 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 15 Feb 2022 16:08:31 +0200 Subject: [PATCH 47/82] don't track peer ids in rcmgr metrics --- node/modules/lp2p/rcmgr.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/node/modules/lp2p/rcmgr.go b/node/modules/lp2p/rcmgr.go index 71ed4e754..0bc4dd6b2 100644 --- a/node/modules/lp2p/rcmgr.go +++ b/node/modules/lp2p/rcmgr.go @@ -118,7 +118,6 @@ func (r rcmgrMetrics) AllowStream(p peer.ID, dir network.Direction) { } else { ctx, _ = tag.New(ctx, tag.Upsert(metrics.Direction, "outbound")) } - ctx, _ = tag.New(ctx, tag.Upsert(metrics.PeerID, p.Pretty())) stats.Record(ctx, metrics.RcmgrAllowStream.M(1)) } @@ -129,19 +128,16 @@ func (r rcmgrMetrics) BlockStream(p peer.ID, dir network.Direction) { } else { ctx, _ = tag.New(ctx, tag.Upsert(metrics.Direction, "outbound")) } - ctx, _ = tag.New(ctx, tag.Upsert(metrics.PeerID, p.Pretty())) stats.Record(ctx, metrics.RcmgrBlockStream.M(1)) } func (r rcmgrMetrics) AllowPeer(p peer.ID) { ctx := context.Background() - ctx, _ = tag.New(ctx, tag.Upsert(metrics.PeerID, p.Pretty())) stats.Record(ctx, metrics.RcmgrAllowPeer.M(1)) } func (r rcmgrMetrics) BlockPeer(p peer.ID) { ctx := context.Background() - ctx, _ = tag.New(ctx, tag.Upsert(metrics.PeerID, p.Pretty())) stats.Record(ctx, metrics.RcmgrBlockPeer.M(1)) } @@ -160,7 +156,6 @@ func (r rcmgrMetrics) BlockProtocol(proto protocol.ID) { func (r rcmgrMetrics) BlockProtocolPeer(proto protocol.ID, p peer.ID) { ctx := context.Background() ctx, _ = tag.New(ctx, tag.Upsert(metrics.ProtocolID, string(proto))) - ctx, _ = tag.New(ctx, tag.Upsert(metrics.PeerID, p.Pretty())) stats.Record(ctx, metrics.RcmgrBlockProtoPeer.M(1)) } @@ -179,7 +174,6 @@ func (r rcmgrMetrics) BlockService(svc string) { func (r rcmgrMetrics) BlockServicePeer(svc string, p peer.ID) { ctx := context.Background() ctx, _ = tag.New(ctx, tag.Upsert(metrics.ServiceID, svc)) - ctx, _ = tag.New(ctx, tag.Upsert(metrics.PeerID, p.Pretty())) stats.Record(ctx, metrics.RcmgrBlockSvcPeer.M(1)) } From f677995603287362a38b0009e9ae383e63f7f61a Mon Sep 17 00:00:00 2001 From: Kevin Li Date: Wed, 16 Feb 2022 15:30:07 +0800 Subject: [PATCH 48/82] feat: add MsigGetVestingSchedule to gateway api expose MsigGetVestingSchedule method to gateway api --- api/api_gateway.go | 3 ++- api/proxy_gen.go | 13 +++++++++++++ build/openrpc/full.json.gz | Bin 26596 -> 26596 bytes build/openrpc/miner.json.gz | Bin 12903 -> 12906 bytes build/openrpc/worker.json.gz | Bin 3960 -> 3959 bytes gateway/node.go | 8 ++++++++ 6 files changed, 23 insertions(+), 1 deletion(-) diff --git a/api/api_gateway.go b/api/api_gateway.go index fbe2e0cd6..be4b3b83c 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -45,8 +45,9 @@ type Gateway interface { GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) MpoolPush(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) - MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error) MsigGetPending(context.Context, address.Address, types.TipSetKey) ([]*MsigTransaction, error) + MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error) + MsigGetVestingSchedule(ctx context.Context, addr address.Address, tsk types.TipSetKey) (MsigVesting, error) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) StateDealProviderCollateralBounds(ctx context.Context, size abi.PaddedPieceSize, verified bool, tsk types.TipSetKey) (DealCollateralBounds, error) StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error) diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 15866e3e5..73aa2c774 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -516,6 +516,8 @@ type GatewayStruct struct { MsigGetVested func(p0 context.Context, p1 address.Address, p2 types.TipSetKey, p3 types.TipSetKey) (types.BigInt, error) `` + MsigGetVestingSchedule func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MsigVesting, error) `` + StateAccountKey func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `` StateDealProviderCollateralBounds func(p0 context.Context, p1 abi.PaddedPieceSize, p2 bool, p3 types.TipSetKey) (DealCollateralBounds, error) `` @@ -3285,6 +3287,17 @@ func (s *GatewayStub) MsigGetVested(p0 context.Context, p1 address.Address, p2 t return *new(types.BigInt), ErrNotSupported } +func (s *GatewayStruct) MsigGetVestingSchedule(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MsigVesting, error) { + if s.Internal.MsigGetVestingSchedule == nil { + return *new(MsigVesting), ErrNotSupported + } + return s.Internal.MsigGetVestingSchedule(p0, p1, p2) +} + +func (s *GatewayStub) MsigGetVestingSchedule(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MsigVesting, error) { + return *new(MsigVesting), ErrNotSupported +} + func (s *GatewayStruct) StateAccountKey(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) { if s.Internal.StateAccountKey == nil { return *new(address.Address), ErrNotSupported diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 5c79a80e3e9e646222aeb1c9fc8c962552c94a0e..b5d1d4e4464a584400b4cded18780eb4da7dc33e 100644 GIT binary patch delta 25257 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

|FasKQQqbu%Jv2)#7J1l7BR1ksh7-JcVU|K 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 TFUa-PnIkp5HUSJ{>aIW3%Q+58a$(# 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 diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 89b05d015822b156630895d5a86cb048d0decbf6..4021b573d7df9e690f217a8548ac9c93933b2922 100644 GIT binary patch delta 9996 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 S<^Kl&0RR89CBiWV<^TXQJ<_cJ diff --git a/gateway/node.go b/gateway/node.go index a0c120d39..87a8551b1 100644 --- a/gateway/node.go +++ b/gateway/node.go @@ -51,6 +51,7 @@ type TargetAPI interface { MpoolPushUntrusted(ctx context.Context, sm *types.SignedMessage) (cid.Cid, error) MsigGetAvailableBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (types.BigInt, error) MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error) + MsigGetVestingSchedule(context.Context, address.Address, types.TipSetKey) (api.MsigVesting, error) MsigGetPending(ctx context.Context, addr address.Address, ts types.TipSetKey) ([]*api.MsigTransaction, error) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) StateDealProviderCollateralBounds(ctx context.Context, size abi.PaddedPieceSize, verified bool, tsk types.TipSetKey) (api.DealCollateralBounds, error) @@ -282,6 +283,13 @@ func (gw *Node) MsigGetVested(ctx context.Context, addr address.Address, start t return gw.target.MsigGetVested(ctx, addr, start, end) } +func (gw *Node) MsigGetVestingSchedule(ctx context.Context, addr address.Address, tsk types.TipSetKey) (api.MsigVesting, error) { + if err := gw.checkTipsetKey(ctx, tsk); err != nil { + return api.MsigVesting{}, err + } + return gw.target.MsigGetVestingSchedule(ctx, addr, tsk) +} + func (gw *Node) MsigGetPending(ctx context.Context, addr address.Address, tsk types.TipSetKey) ([]*api.MsigTransaction, error) { if err := gw.checkTipsetKey(ctx, tsk); err != nil { return nil, err From afc29ed445126228765a10bf2f2028c4d1aa429e Mon Sep 17 00:00:00 2001 From: Aayush Date: Wed, 16 Feb 2022 13:04:48 -0500 Subject: [PATCH 49/82] feat: tweak v15 migration params --- chain/consensus/filcns/upgrades.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/chain/consensus/filcns/upgrades.go b/chain/consensus/filcns/upgrades.go index 2fa020d3d..116684b9f 100644 --- a/chain/consensus/filcns/upgrades.go +++ b/chain/consensus/filcns/upgrades.go @@ -165,13 +165,8 @@ func DefaultUpgradeSchedule() stmgr.UpgradeSchedule { Migration: UpgradeActorsV7, PreMigrations: []stmgr.PreMigration{{ PreMigration: PreUpgradeActorsV7, - StartWithin: 120, + StartWithin: 180, DontStartWithin: 60, - StopWithin: 35, - }, { - PreMigration: PreUpgradeActorsV7, - StartWithin: 30, - DontStartWithin: 15, StopWithin: 5, }}, Expensive: true, @@ -1264,7 +1259,7 @@ func upgradeActorsV7Common( root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet, config nv15.Config, ) (cid.Cid, error) { - writeStore := blockstore.NewAutobatch(ctx, sm.ChainStore().StateBlockstore(), units.GiB) + writeStore := blockstore.NewAutobatch(ctx, sm.ChainStore().StateBlockstore(), units.GiB/4) // TODO: pretty sure we'd achieve nothing by doing this, confirm in review //buf := blockstore.NewTieredBstore(sm.ChainStore().StateBlockstore(), writeStore) store := store.ActorStore(ctx, writeStore) From ca9bcc90a5cc3d9bdf4049c238dea0f6bedca0e1 Mon Sep 17 00:00:00 2001 From: Travis Person Date: Wed, 16 Feb 2022 20:35:55 +0000 Subject: [PATCH 50/82] lotus-seed: set current network version from params allows automation to correctly set the network version for the currently built network with no variable inputs. --- cmd/lotus-seed/genesis.go | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/cmd/lotus-seed/genesis.go b/cmd/lotus-seed/genesis.go index a27cc0a2f..89feec33a 100644 --- a/cmd/lotus-seed/genesis.go +++ b/cmd/lotus-seed/genesis.go @@ -508,12 +508,19 @@ var genesisSetRemainderCmd = &cli.Command{ } var genesisSetActorVersionCmd = &cli.Command{ - Name: "set-network-version", - Usage: "Set the version that this network will start from", - ArgsUsage: " ", + Name: "set-network-version", + Usage: "Set the version that this network will start from", + Flags: []cli.Flag{ + &cli.IntFlag{ + Name: "network-version", + Usage: "network version to start genesis with", + Value: int(build.GenesisNetworkVersion), + }, + }, + ArgsUsage: "", Action: func(cctx *cli.Context) error { - if cctx.Args().Len() != 2 { - return fmt.Errorf("must specify genesis file and network version (e.g. '0'") + if cctx.Args().Len() != 1 { + return fmt.Errorf("must specify genesis file") } genf, err := homedir.Expand(cctx.Args().First()) @@ -531,16 +538,12 @@ var genesisSetActorVersionCmd = &cli.Command{ return xerrors.Errorf("unmarshal genesis template: %w", err) } - nv, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64) - if err != nil { - return xerrors.Errorf("parsing network version: %w", err) - } - - if nv > uint64(build.NewestNetworkVersion) { + nv := network.Version(cctx.Int("network-version")) + if nv > build.NewestNetworkVersion { return xerrors.Errorf("invalid network version: %d", nv) } - template.NetworkVersion = network.Version(nv) + template.NetworkVersion = nv b, err = json.MarshalIndent(&template, "", " ") if err != nil { From 3b3b0725edd772a228376e39e23cb13b1e3ded1e Mon Sep 17 00:00:00 2001 From: Aayush Date: Wed, 16 Feb 2022 13:04:48 -0500 Subject: [PATCH 51/82] feat: tweak v15 migration params --- chain/consensus/filcns/upgrades.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/chain/consensus/filcns/upgrades.go b/chain/consensus/filcns/upgrades.go index d0ca7d092..42575a2e2 100644 --- a/chain/consensus/filcns/upgrades.go +++ b/chain/consensus/filcns/upgrades.go @@ -165,13 +165,8 @@ func DefaultUpgradeSchedule() stmgr.UpgradeSchedule { Migration: UpgradeActorsV7, PreMigrations: []stmgr.PreMigration{{ PreMigration: PreUpgradeActorsV7, - StartWithin: 120, + StartWithin: 180, DontStartWithin: 60, - StopWithin: 35, - }, { - PreMigration: PreUpgradeActorsV7, - StartWithin: 30, - DontStartWithin: 15, StopWithin: 5, }}, Expensive: true, @@ -1264,7 +1259,7 @@ func upgradeActorsV7Common( root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet, config nv15.Config, ) (cid.Cid, error) { - writeStore := blockstore.NewAutobatch(ctx, sm.ChainStore().StateBlockstore(), units.GiB) + writeStore := blockstore.NewAutobatch(ctx, sm.ChainStore().StateBlockstore(), units.GiB/4) // TODO: pretty sure we'd achieve nothing by doing this, confirm in review //buf := blockstore.NewTieredBstore(sm.ChainStore().StateBlockstore(), writeStore) store := store.ActorStore(ctx, writeStore) From 36aa243c5699481378bae874c7af6960541be087 Mon Sep 17 00:00:00 2001 From: Aayush Date: Wed, 16 Feb 2022 19:24:28 -0500 Subject: [PATCH 52/82] sealer: fix error message --- extern/storage-sealing/checks.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/extern/storage-sealing/checks.go b/extern/storage-sealing/checks.go index 56b0677c4..dc045ded2 100644 --- a/extern/storage-sealing/checks.go +++ b/extern/storage-sealing/checks.go @@ -214,8 +214,13 @@ func checkReplicaUpdate(ctx context.Context, maddr address.Address, si SectorInf if err != nil { return &ErrApi{xerrors.Errorf("calling StateComputeDataCommitment: %w", err)} } - if si.UpdateUnsealed == nil || !commD.Equals(*si.UpdateUnsealed) { - return &ErrBadRU{xerrors.Errorf("on chain CommD differs from sector: %s != %s", commD, si.CommD)} + + if si.UpdateUnsealed == nil { + return &ErrBadRU{xerrors.New("nil UpdateUnsealed cid after replica update")} + } + + if !commD.Equals(*si.UpdateUnsealed) { + return &ErrBadRU{xerrors.Errorf("calculated CommD differs from updated replica: %s != %s", commD, *si.UpdateUnsealed)} } if si.UpdateSealed == nil { From 43c0344fb26fc45ae3196ddfb2758ebd6467a324 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Wed, 16 Feb 2022 19:31:45 -0500 Subject: [PATCH 53/82] typo in variable name --- cmd/lotus-seal-worker/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-seal-worker/main.go b/cmd/lotus-seal-worker/main.go index 84ff1ccdd..1f7b4888e 100644 --- a/cmd/lotus-seal-worker/main.go +++ b/cmd/lotus-seal-worker/main.go @@ -278,7 +278,7 @@ var runCmd = &cli.Command{ if cctx.Bool("commit") { taskTypes = append(taskTypes, sealtasks.TTCommit2) } - if cctx.Bool("replicaupdate") { + if cctx.Bool("replica-update") { taskTypes = append(taskTypes, sealtasks.TTReplicaUpdate) } if cctx.Bool("prove-replica-update2") { From 355b73843c031c73134bcba2d3c1dddfac6d8379 Mon Sep 17 00:00:00 2001 From: Aayush Date: Wed, 16 Feb 2022 19:34:45 -0500 Subject: [PATCH 54/82] makefile: add make jen --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index f7b13cc18..f91e74e33 100644 --- a/Makefile +++ b/Makefile @@ -345,6 +345,8 @@ gen: actors-gen type-gen method-gen cfgdoc-gen docsgen api-gen circleci @echo ">>> IF YOU'VE MODIFIED THE CLI OR CONFIG, REMEMBER TO ALSO MAKE docsgen-cli" .PHONY: gen +jen: gen + snap: lotus lotus-miner lotus-worker snapcraft # snapcraft upload ./lotus_*.snap From 74556edcffbd1e39eb38e54ff8e3bc43b84fe8f5 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 17 Feb 2022 12:52:52 +0200 Subject: [PATCH 55/82] don't fail reification on missing references --- blockstore/splitstore/splitstore_reify.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/blockstore/splitstore/splitstore_reify.go b/blockstore/splitstore/splitstore_reify.go index 14648a652..dc44ab21c 100644 --- a/blockstore/splitstore/splitstore_reify.go +++ b/blockstore/splitstore/splitstore_reify.go @@ -98,7 +98,7 @@ func (s *SplitStore) doReify(c cid.Cid) { s.txnLk.RLock() defer s.txnLk.RUnlock() - err := s.walkObject(c, newTmpVisitor(), + err := s.walkObjectIncomplete(c, newTmpVisitor(), func(c cid.Cid) error { if isUnitaryObject(c) { return errStopWalk @@ -137,6 +137,10 @@ func (s *SplitStore) doReify(c cid.Cid) { toreify = append(toreify, c) return nil + }, + func(missing cid.Cid) error { + log.Warnf("missing reference while reifying %s: %s", c, missing) + return errStopWalk }) if err != nil { From 6feae1993d3ddd7e4babc2b68e30962f5ca48e7d Mon Sep 17 00:00:00 2001 From: Darko Brdareski Date: Thu, 17 Feb 2022 14:24:42 +0100 Subject: [PATCH 56/82] Fix PR comments. Refactor random addr generation to use a rand seed. Remove unused lines in tests. --- chain/types/mock/chain.go | 29 +++++++++++++++++------------ cli/chain_test.go | 23 ++++++++++++----------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/chain/types/mock/chain.go b/chain/types/mock/chain.go index c69f7f56f..9a911c987 100644 --- a/chain/types/mock/chain.go +++ b/chain/types/mock/chain.go @@ -2,8 +2,8 @@ package mock import ( "context" - "crypto/rand" "fmt" + "math/rand" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" @@ -90,19 +90,24 @@ func TipSet(blks ...*types.BlockHeader) *types.TipSet { return ts } -func RandomActorAddress() (*address.Address, error) { - bytes := make([]byte, 32) - _, err := rand.Read(bytes) - if err != nil { - return nil, err - } +// Generates count new addresses using the provided seed, and returns them +func RandomActorAddresses(seed int64, count int) ([]*address.Address, error) { + randAddrs := make([]*address.Address, count) + source := rand.New(rand.NewSource(seed)) + for i := 0; i < count; i++ { + bytes := make([]byte, 32) + _, err := source.Read(bytes) + if err != nil { + return nil, err + } - addr, err := address.NewActorAddress(bytes) - if err != nil { - return nil, err + addr, err := address.NewActorAddress(bytes) + if err != nil { + return nil, err + } + randAddrs[i] = &addr } - - return &addr, nil + return randAddrs, nil } func UnsignedMessage(from, to address.Address, nonce uint64) *types.Message { diff --git a/cli/chain_test.go b/cli/chain_test.go index 841cfb689..48c4af05d 100644 --- a/cli/chain_test.go +++ b/cli/chain_test.go @@ -188,11 +188,11 @@ func TestChainGetMsg(t *testing.T) { app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("chain", ChainGetMsgCmd)) defer done() - from, err := mock.RandomActorAddress() + addrs, err := mock.RandomActorAddresses(12345, 2) assert.NoError(t, err) - to, err := mock.RandomActorAddress() - assert.NoError(t, err) + from := addrs[0] + to := addrs[1] msg := mock.UnsignedMessage(*from, *to, 0) @@ -283,11 +283,11 @@ func TestInspectUsage(t *testing.T) { cmd := WithCategory("chain", ChainInspectUsage) ts := mock.TipSet(mock.MkBlock(nil, 0, 0)) - from, err := mock.RandomActorAddress() + addrs, err := mock.RandomActorAddresses(12345, 2) assert.NoError(t, err) - to, err := mock.RandomActorAddress() - assert.NoError(t, err) + from := addrs[0] + to := addrs[1] msg := mock.UnsignedMessage(*from, *to, 0) msgs := []api.Message{{Cid: msg.Cid(), Message: msg}} @@ -321,6 +321,9 @@ func TestInspectUsage(t *testing.T) { // output is plaintext, had to do string matching assert.Contains(t, out, from.String()) assert.Contains(t, out, to.String()) + // check for gas by sender + assert.Contains(t, out, "By Sender") + // check for gas by method assert.Contains(t, out, "Send") }) } @@ -332,13 +335,11 @@ func TestChainList(t *testing.T) { blk.Height = 1 head := mock.TipSet(blk) - head.Height() - - from, err := mock.RandomActorAddress() + addrs, err := mock.RandomActorAddresses(12345, 2) assert.NoError(t, err) - to, err := mock.RandomActorAddress() - assert.NoError(t, err) + from := addrs[0] + to := addrs[1] msg := mock.UnsignedMessage(*from, *to, 0) msgs := []api.Message{{Cid: msg.Cid(), Message: msg}} From 7823363bba0ea96b88f1727e3409b81cbbb21863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 17 Feb 2022 16:29:27 +0100 Subject: [PATCH 57/82] lotus-miner: Support update files in storage find --- cmd/lotus-miner/storage.go | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/cmd/lotus-miner/storage.go b/cmd/lotus-miner/storage.go index 6f7a627f6..0fea2a3a5 100644 --- a/cmd/lotus-miner/storage.go +++ b/cmd/lotus-miner/storage.go @@ -368,6 +368,7 @@ type storedSector struct { store stores.SectorStorageInfo unsealed, sealed, cache bool + update, updatecache bool } var storageFindCmd = &cli.Command{ @@ -421,6 +422,16 @@ var storageFindCmd = &cli.Command{ return xerrors.Errorf("finding cache: %w", err) } + us, err := nodeApi.StorageFindSector(ctx, sid, storiface.FTUpdate, 0, false) + if err != nil { + return xerrors.Errorf("finding sealed: %w", err) + } + + uc, err := nodeApi.StorageFindSector(ctx, sid, storiface.FTUpdateCache, 0, false) + if err != nil { + return xerrors.Errorf("finding cache: %w", err) + } + byId := map[stores.ID]*storedSector{} for _, info := range u { sts, ok := byId[info.ID] @@ -455,6 +466,28 @@ var storageFindCmd = &cli.Command{ } sts.cache = true } + for _, info := range us { + sts, ok := byId[info.ID] + if !ok { + sts = &storedSector{ + id: info.ID, + store: info, + } + byId[info.ID] = sts + } + sts.update = true + } + for _, info := range uc { + sts, ok := byId[info.ID] + if !ok { + sts = &storedSector{ + id: info.ID, + store: info, + } + byId[info.ID] = sts + } + sts.updatecache = true + } local, err := nodeApi.StorageLocal(ctx) if err != nil { @@ -480,6 +513,12 @@ var storageFindCmd = &cli.Command{ if info.cache { types += "Cache, " } + if info.update { + types += "Update, " + } + if info.updatecache { + types += "UpdateCache, " + } fmt.Printf("In %s (%s)\n", info.id, types[:len(types)-2]) fmt.Printf("\tSealing: %t; Storage: %t\n", info.store.CanSeal, info.store.CanStore) From c9748724945c6e454d3fd55516aeff9b3633a310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 17 Feb 2022 16:35:09 +0100 Subject: [PATCH 58/82] lotus-miner: More sector-related data in info all --- cmd/lotus-miner/info_all.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmd/lotus-miner/info_all.go b/cmd/lotus-miner/info_all.go index bd1147b22..fa5a413e7 100644 --- a/cmd/lotus-miner/info_all.go +++ b/cmd/lotus-miner/info_all.go @@ -96,6 +96,11 @@ var infoAllCmd = &cli.Command{ fmt.Println("ERROR: ", err) } + fmt.Println("\n#: Storage Locks") + if err := storageLocks.Action(cctx); err != nil { + fmt.Println("ERROR: ", err) + } + fmt.Println("\n#: Sched Diag") if err := sealingSchedDiagCmd.Action(cctx); err != nil { fmt.Println("ERROR: ", err) @@ -192,6 +197,11 @@ var infoAllCmd = &cli.Command{ fmt.Println("ERROR: ", err) } + fmt.Println("\n#: Storage Sector List") + if err := storageListSectorsCmd.Action(cctx); err != nil { + fmt.Println("ERROR: ", err) + } + fmt.Println("\n#: Expired Sectors") if err := sectorsExpiredCmd.Action(cctx); err != nil { fmt.Println("ERROR: ", err) From 6d32a2f29950c48f3e21db57da27e03dae896261 Mon Sep 17 00:00:00 2001 From: Florian Ruen Date: Thu, 17 Feb 2022 17:35:33 +0100 Subject: [PATCH 59/82] Update client.go Bug fixed : if hitting return instead of filling with a valid value, the CLI crashed (line 796) Change return err by continue to ask a valid value again --- cli/client.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cli/client.go b/cli/client.go index 634bd18e5..e9a25f520 100644 --- a/cli/client.go +++ b/cli/client.go @@ -795,14 +795,17 @@ uiLoop: case "find-count": afmt.Print("Deals to make (1): ") dealcStr, _, err := rl.ReadLine() + if err != nil { printErr(xerrors.Errorf("reading deal count: %w", err)) continue } dealCount, err = strconv.ParseInt(string(dealcStr), 10, 64) + if err != nil { - return err + printErr(xerrors.Errorf("reading deal count: invalid number")) + continue } color.Blue(".. Picking miners") From 78817bd0014f0e751824521ce483849d8f31a7d3 Mon Sep 17 00:00:00 2001 From: Florian Ruen Date: Thu, 17 Feb 2022 17:57:42 +0100 Subject: [PATCH 60/82] Fix #8100 Bug fixed : if hitting return instead of filling with a valid value, the CLI crashed (line 796) Change return err by continue to ask a valid value again --- cli/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/client.go b/cli/client.go index e9a25f520..ae8ad857e 100644 --- a/cli/client.go +++ b/cli/client.go @@ -801,10 +801,10 @@ uiLoop: continue } - dealCount, err = strconv.ParseInt(string(dealcStr), 10, 64) + dealCount, err = strconv.ParseInt(string(dealcStr), 10, 64) if err != nil { - printErr(xerrors.Errorf("reading deal count: invalid number")) + printErr(xerrors.Errorf("reading deal count: invalid number")) // TO DO : Use 1 as default value for number of deals ? continue } From ddd369e7319590194087f3ecf60f08ddc6713aa4 Mon Sep 17 00:00:00 2001 From: Florian Ruen Date: Thu, 17 Feb 2022 18:53:21 +0100 Subject: [PATCH 61/82] lint reported errors on CircleCI Commit to fix lint reported errors during tests on CircleCI (remove blank lines x2) --- cli/client.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cli/client.go b/cli/client.go index ae8ad857e..68d57376d 100644 --- a/cli/client.go +++ b/cli/client.go @@ -795,16 +795,14 @@ uiLoop: case "find-count": afmt.Print("Deals to make (1): ") dealcStr, _, err := rl.ReadLine() - if err != nil { printErr(xerrors.Errorf("reading deal count: %w", err)) continue } dealCount, err = strconv.ParseInt(string(dealcStr), 10, 64) - if err != nil { - printErr(xerrors.Errorf("reading deal count: invalid number")) // TO DO : Use 1 as default value for number of deals ? + printErr(xerrors.Errorf("reading deal count: invalid number")) continue } From a20c6cb04b634508c34f5f60570084b91c69b022 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 17 Feb 2022 19:56:50 +0200 Subject: [PATCH 62/82] temporarily disable reification big reifications can use a lot of memory during sync apparently. --- blockstore/splitstore/splitstore_reify.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/blockstore/splitstore/splitstore_reify.go b/blockstore/splitstore/splitstore_reify.go index dc44ab21c..652900747 100644 --- a/blockstore/splitstore/splitstore_reify.go +++ b/blockstore/splitstore/splitstore_reify.go @@ -10,7 +10,13 @@ import ( cid "github.com/ipfs/go-cid" ) +var EnableReification = false + func (s *SplitStore) reifyColdObject(c cid.Cid) { + if !EnableReification { + return + } + if !s.isWarm() { return } From 6ce93210949f81adf1e1db45396c08c71f052b1c Mon Sep 17 00:00:00 2001 From: Florian Ruen Date: Thu, 17 Feb 2022 19:01:52 +0100 Subject: [PATCH 63/82] lint reported errors on CircleCI Commit to fix lint reported errors during tests on CircleCI (remove trailling whitespace) --- cli/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/client.go b/cli/client.go index 68d57376d..dc4ab86e6 100644 --- a/cli/client.go +++ b/cli/client.go @@ -800,7 +800,7 @@ uiLoop: continue } - dealCount, err = strconv.ParseInt(string(dealcStr), 10, 64) + dealCount, err = strconv.ParseInt(string(dealcStr), 10, 64) if err != nil { printErr(xerrors.Errorf("reading deal count: invalid number")) continue From 899a65ae870d84deff4ed06735808a0b26ad3fb1 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 17 Feb 2022 20:13:46 +0200 Subject: [PATCH 64/82] fix test --- blockstore/splitstore/splitstore_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/blockstore/splitstore/splitstore_test.go b/blockstore/splitstore/splitstore_test.go index 6b7e60e6c..c7f9cb6fc 100644 --- a/blockstore/splitstore/splitstore_test.go +++ b/blockstore/splitstore/splitstore_test.go @@ -496,6 +496,7 @@ func testSplitStoreReification(t *testing.T, f func(context.Context, blockstore. } func TestSplitStoreReification(t *testing.T) { + EnableReification = true t.Log("test reification with Has") testSplitStoreReification(t, func(ctx context.Context, s blockstore.Blockstore, c cid.Cid) error { _, err := s.Has(ctx, c) From f6e545bce5c698fab1337cdb9fed0cb5694575f1 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Wed, 16 Feb 2022 19:31:45 -0500 Subject: [PATCH 65/82] typo in variable name --- cmd/lotus-seal-worker/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-seal-worker/main.go b/cmd/lotus-seal-worker/main.go index 2116dd228..9e6843dbf 100644 --- a/cmd/lotus-seal-worker/main.go +++ b/cmd/lotus-seal-worker/main.go @@ -283,7 +283,7 @@ var runCmd = &cli.Command{ if cctx.Bool("commit") { taskTypes = append(taskTypes, sealtasks.TTCommit2) } - if cctx.Bool("replicaupdate") { + if cctx.Bool("replica-update") { taskTypes = append(taskTypes, sealtasks.TTReplicaUpdate) } if cctx.Bool("prove-replica-update2") { From b708fbcd203abf580338e60f2463f8a53429ea84 Mon Sep 17 00:00:00 2001 From: Aayush Date: Wed, 16 Feb 2022 19:24:28 -0500 Subject: [PATCH 66/82] sealer: fix error message --- extern/storage-sealing/checks.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/extern/storage-sealing/checks.go b/extern/storage-sealing/checks.go index 56b0677c4..dc045ded2 100644 --- a/extern/storage-sealing/checks.go +++ b/extern/storage-sealing/checks.go @@ -214,8 +214,13 @@ func checkReplicaUpdate(ctx context.Context, maddr address.Address, si SectorInf if err != nil { return &ErrApi{xerrors.Errorf("calling StateComputeDataCommitment: %w", err)} } - if si.UpdateUnsealed == nil || !commD.Equals(*si.UpdateUnsealed) { - return &ErrBadRU{xerrors.Errorf("on chain CommD differs from sector: %s != %s", commD, si.CommD)} + + if si.UpdateUnsealed == nil { + return &ErrBadRU{xerrors.New("nil UpdateUnsealed cid after replica update")} + } + + if !commD.Equals(*si.UpdateUnsealed) { + return &ErrBadRU{xerrors.Errorf("calculated CommD differs from updated replica: %s != %s", commD, *si.UpdateUnsealed)} } if si.UpdateSealed == nil { From 0521d793db28afe0b33d9e825dba671caacccf43 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 17 Feb 2022 19:11:41 -0500 Subject: [PATCH 67/82] tidy go.sum --- testplans/lotus-soup/go.sum | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testplans/lotus-soup/go.sum b/testplans/lotus-soup/go.sum index d3f2c4841..2ddbf5fa7 100644 --- a/testplans/lotus-soup/go.sum +++ b/testplans/lotus-soup/go.sum @@ -483,8 +483,8 @@ github.com/filecoin-project/specs-actors/v7 v7.0.0-20211117170924-fd07a4c7dff9/g github.com/filecoin-project/specs-actors/v7 v7.0.0-20211222192039-c83bea50c402/go.mod h1:p6LIOFezA1rgRLMewbvdi3Pp6SAu+q9FtJ9CAleSjrE= github.com/filecoin-project/specs-actors/v7 v7.0.0-rc1 h1:FuDaXIbcw2hRsFI8SDTmsGGCE+NumpF6aiBoU/2X5W4= github.com/filecoin-project/specs-actors/v7 v7.0.0-rc1/go.mod h1:TA5FwCna+Yi36POaT7SLKXsgEDvJwc0V/L6ZsO19B9M= -github.com/filecoin-project/specs-storage v0.1.1-0.20211228030229-6d460d25a0c9 h1:oUYOvF7EvdXS0Zmk9mNkaB6Bu0l+WXBYPzVodKMiLug= -github.com/filecoin-project/specs-storage v0.1.1-0.20211228030229-6d460d25a0c9/go.mod h1:Tb88Zq+IBJbvAn3mS89GYj3jdRThBTE/771HCVZdRJU= +github.com/filecoin-project/specs-storage v0.2.0 h1:Y4UDv0apRQ3zI2GiPPubi8JblpUZZphEdaJUxCutfyg= +github.com/filecoin-project/specs-storage v0.2.0/go.mod h1:Tb88Zq+IBJbvAn3mS89GYj3jdRThBTE/771HCVZdRJU= github.com/filecoin-project/test-vectors/schema v0.0.5/go.mod h1:iQ9QXLpYWL3m7warwvK1JC/pTri8mnfEmKygNDqqY6E= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ= From b311c26e21d11039cc971f91493eca7e519de42c Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 17 Feb 2022 20:00:41 -0500 Subject: [PATCH 68/82] Lotus version 1.14.0: set OhSnap upgrade epoch --- CHANGELOG.md | 25 ++++++++++--------------- build/openrpc/full.json.gz | Bin 25710 -> 25706 bytes build/openrpc/miner.json.gz | Bin 11748 -> 11745 bytes build/openrpc/worker.json.gz | Bin 3850 -> 3845 bytes build/params_mainnet.go | 3 ++- build/version.go | 2 +- documentation/en/cli-lotus-miner.md | 2 +- documentation/en/cli-lotus-worker.md | 2 +- documentation/en/cli-lotus.md | 2 +- 9 files changed, 16 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0480b448..39f015158 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,27 +1,22 @@ # Lotus changelog -# 1.14.0-rc7 / 2022-02-10 +# 1.14.0 / 2022-02-17 -This is the 7th release candidate for the mandatory release v1.14.0 of Lotus that introduces [Filecoin network v15, +This is a MANDATORY release of Lotus that introduces [Filecoin network v15, codenamed the OhSnap upgrade](https://github.com/filecoin-project/community/discussions/74?sort=new#discussioncomment-1922550). -The OhSnap upgrade introduces the following FIPs, delivered in [actors v7-rc1](https://github.com/filecoin-project/specs-actors/releases/tag/v7.0.0-rc1): +The network is scheduled to upgrade to v15 on March 1st at 2022-03-01T15:00:00Z. All node operators, including storage providers, must upgrade to this release (or a later release) before that time. Storage providers must update their daemons, miners, and worker(s). + +The OhSnap upgrade introduces the following FIPs, delivered in [actors v7](https://github.com/filecoin-project/specs-actors/releases/tag/v7.0.0): - [FIP-0019 Snap Deals](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0019.md) - [FIP-0028 Remove Datacap from Verified clients](https://github.com/filecoin-project/FIPs/pull/226) -Note: -- This release candidate includes the [final proof params](https://proofs.filecoin.io) for Snap Deals. -- It only sets upgrade epoch for Butterfly-SnapNet and calibnet, and it does not set the upgrade epochs for mainnet. - -## Calibration Upgrade - -The calibnet will be upgraded to Network v15 OhSnap at epoch 682006, around 2022-02-10T19:23:00Z. - -To join the network, simply build lotus by running `make calibnet`. - -New proof params for Snap Deals should be downloaded upon your nodes restart. - - The parameters are pinged on IPFS gateway https://proofs.filecoin.io and the CIDs can be found [here](https://github.com/filecoin-project/lotus/blob/release/v1.14.0/build/proof-params/parameters.json), please let the lotus team know in #fil-lotus-dev if the params are not fetched automatically. For users in China, you can also get the proofs by setting `export IPFS_GATEWAY=https://proof-parameters.s3.cn-south-1.jdcloud-oss.com/ipfs/` +It is recommended that storage providers download the new params before updating their node, miner, and workers. To do so: +- Download Lotus v1.14.0 or later +- run `make lotus-shed` +- run `./lotus-shed fetch-params` with the appropriate `proving-params` flag +- Upgrade the Lotus daemon and miner **when the previous step is complete** ## New Features and Changes - Integrate actor v7-rc1: diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 8e68ed74d5e51c4a3ca22d57fcbc712b3e3e45bd..59ed94d906fbb37742708833d85ee3df3bf34e96 100644 GIT binary patch delta 25582 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 delta 25594 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 diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index ce6f88d90fe789401b30b0b4d1a6ae16659b4ba1..9e8ba2ff2f321d4729101ba8576b69bdd0abc8fb 100644 GIT binary patch delta 3775 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 Date: Thu, 17 Feb 2022 20:50:52 -0500 Subject: [PATCH 69/82] CHANGELOG: add a note about premigrations --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39f015158..8325634ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ It is recommended that storage providers download the new params before updating - run `make lotus-shed` - run `./lotus-shed fetch-params` with the appropriate `proving-params` flag - Upgrade the Lotus daemon and miner **when the previous step is complete** + +All node operators, including storage providers, should be aware that a pre-migration will begin at 2022-03-01T13:30:00Z (150 minutes before the real upgrade). The pre-migration will take between 20 and 50 minutes, depending on hardware specs. During this time, expect slower block validation times, increased CPU and memory usage, and longer delays for API queries. ## New Features and Changes - Integrate actor v7-rc1: From cbd23c2b1bdbde16f982e61be13deed684684aa1 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 18 Feb 2022 12:19:09 +0200 Subject: [PATCH 70/82] add reification limit --- blockstore/splitstore/splitstore_reify.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/blockstore/splitstore/splitstore_reify.go b/blockstore/splitstore/splitstore_reify.go index 652900747..85c4fa289 100644 --- a/blockstore/splitstore/splitstore_reify.go +++ b/blockstore/splitstore/splitstore_reify.go @@ -1,6 +1,7 @@ package splitstore import ( + "errors" "runtime" "sync/atomic" @@ -10,13 +11,12 @@ import ( cid "github.com/ipfs/go-cid" ) -var EnableReification = false +var ( + errReifyLimit = errors.New("reification limit reached") + ReifyLimit = 16384 +) func (s *SplitStore) reifyColdObject(c cid.Cid) { - if !EnableReification { - return - } - if !s.isWarm() { return } @@ -104,12 +104,18 @@ func (s *SplitStore) doReify(c cid.Cid) { s.txnLk.RLock() defer s.txnLk.RUnlock() + count := 0 err := s.walkObjectIncomplete(c, newTmpVisitor(), func(c cid.Cid) error { if isUnitaryObject(c) { return errStopWalk } + count++ + if count > ReifyLimit { + return errReifyLimit + } + s.reifyMx.Lock() _, inProgress := s.reifyInProgress[c] if !inProgress { @@ -150,6 +156,11 @@ func (s *SplitStore) doReify(c cid.Cid) { }) if err != nil { + if xerrors.Is(err, errReifyLimit) { + log.Debug("reification aborted; reify limit reached") + return + } + log.Warnf("error walking cold object for reification (cid: %s): %s", c, err) return } From 2795995989848153a80f1c1d1ca524f4636ea5cf Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 18 Feb 2022 12:19:19 +0200 Subject: [PATCH 71/82] add reification limit test --- blockstore/splitstore/splitstore_test.go | 101 ++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/blockstore/splitstore/splitstore_test.go b/blockstore/splitstore/splitstore_test.go index c7f9cb6fc..ee30400a4 100644 --- a/blockstore/splitstore/splitstore_test.go +++ b/blockstore/splitstore/splitstore_test.go @@ -495,8 +495,102 @@ func testSplitStoreReification(t *testing.T, f func(context.Context, blockstore. } } +func testSplitStoreReificationLimit(t *testing.T, f func(context.Context, blockstore.Blockstore, cid.Cid) error) { + ds := dssync.MutexWrap(datastore.NewMapDatastore()) + hot := newMockStore() + cold := newMockStore() + + mkRandomBlock := func() blocks.Block { + data := make([]byte, 128) + _, err := rand.Read(data) + if err != nil { + t.Fatal(err) + } + + return blocks.NewBlock(data) + } + + block1 := mkRandomBlock() + block2 := mkRandomBlock() + block3 := mkRandomBlock() + + hdr := mock.MkBlock(nil, 0, 0) + hdr.Messages = block1.Cid() + hdr.ParentMessageReceipts = block2.Cid() + hdr.ParentStateRoot = block3.Cid() + block4, err := hdr.ToStorageBlock() + if err != nil { + t.Fatal(err) + } + + allBlocks := []blocks.Block{block1, block2, block3, block4} + for _, blk := range allBlocks { + err := cold.Put(context.Background(), blk) + if err != nil { + t.Fatal(err) + } + } + + path, err := ioutil.TempDir("", "splitstore.*") + if err != nil { + t.Fatal(err) + } + + t.Cleanup(func() { + _ = os.RemoveAll(path) + }) + + ss, err := Open(path, ds, hot, cold, &Config{MarkSetType: "map"}) + if err != nil { + t.Fatal(err) + } + defer ss.Close() //nolint + + ss.warmupEpoch = 1 + go ss.reifyOrchestrator() + + waitForReification := func() { + for { + ss.reifyMx.Lock() + ready := len(ss.reifyPend) == 0 && len(ss.reifyInProgress) == 0 + ss.reifyMx.Unlock() + + if ready { + return + } + + time.Sleep(time.Millisecond) + } + } + + // do a hot access -- nothing should be reified as the limit should be exceeded + oldReifyLimit := ReifyLimit + ReifyLimit = 2 + t.Cleanup(func() { + ReifyLimit = oldReifyLimit + }) + + err = f(blockstore.WithHotView(context.Background()), ss, block4.Cid()) + if err != nil { + t.Fatal(err) + } + + waitForReification() + + for _, blk := range allBlocks { + has, err := hot.Has(context.Background(), blk.Cid()) + if err != nil { + t.Fatal(err) + } + + if has { + t.Fatal("block unexpectedly reified") + } + } + +} + func TestSplitStoreReification(t *testing.T) { - EnableReification = true t.Log("test reification with Has") testSplitStoreReification(t, func(ctx context.Context, s blockstore.Blockstore, c cid.Cid) error { _, err := s.Has(ctx, c) @@ -516,6 +610,11 @@ func TestSplitStoreReification(t *testing.T) { testSplitStoreReification(t, func(ctx context.Context, s blockstore.Blockstore, c cid.Cid) error { return s.View(ctx, c, func(_ []byte) error { return nil }) }) + t.Log("test reification limit") + testSplitStoreReificationLimit(t, func(ctx context.Context, s blockstore.Blockstore, c cid.Cid) error { + _, err := s.Has(ctx, c) + return err + }) } type mockChain struct { From a7b1d8653378bb114558a92737757c3fed6e34e2 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 18 Feb 2022 12:27:46 +0200 Subject: [PATCH 72/82] make cidset (in memory) visitors smarter; no need to ever visit unitary objects --- blockstore/splitstore/visitor.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/blockstore/splitstore/visitor.go b/blockstore/splitstore/visitor.go index 9dfbb78e7..4a78f1db1 100644 --- a/blockstore/splitstore/visitor.go +++ b/blockstore/splitstore/visitor.go @@ -26,6 +26,10 @@ type tmpVisitor struct { var _ ObjectVisitor = (*tmpVisitor)(nil) func (v *tmpVisitor) Visit(c cid.Cid) (bool, error) { + if isUnitaryObject(c) { + return false, nil + } + return v.set.Visit(c), nil } @@ -45,6 +49,10 @@ func newConcurrentVisitor() *concurrentVisitor { } func (v *concurrentVisitor) Visit(c cid.Cid) (bool, error) { + if isUnitaryObject(c) { + return false, nil + } + v.mx.Lock() defer v.mx.Unlock() From c8edd5bd52a43186564792b96d344fdb250ef6d3 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 17 Feb 2022 20:50:52 -0500 Subject: [PATCH 73/82] CHANGELOG: add a note about premigrations --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39f015158..8325634ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ It is recommended that storage providers download the new params before updating - run `make lotus-shed` - run `./lotus-shed fetch-params` with the appropriate `proving-params` flag - Upgrade the Lotus daemon and miner **when the previous step is complete** + +All node operators, including storage providers, should be aware that a pre-migration will begin at 2022-03-01T13:30:00Z (150 minutes before the real upgrade). The pre-migration will take between 20 and 50 minutes, depending on hardware specs. During this time, expect slower block validation times, increased CPU and memory usage, and longer delays for API queries. ## New Features and Changes - Integrate actor v7-rc1: From c5bd019a7c162b748e09e5e86aa4344ad0a2274f Mon Sep 17 00:00:00 2001 From: Jiaying Wang <42981373+jennijuju@users.noreply.github.com> Date: Fri, 18 Feb 2022 08:06:47 -0500 Subject: [PATCH 74/82] Fix mainnet upgrade date, epoch is correct --- build/params_mainnet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/params_mainnet.go b/build/params_mainnet.go index c6311ec35..0a9f6e775 100644 --- a/build/params_mainnet.go +++ b/build/params_mainnet.go @@ -67,7 +67,7 @@ const UpgradeHyperdriveHeight = 892800 // 2021-10-26T13:30:00Z const UpgradeChocolateHeight = 1231620 -// 2022-03-02T15:00:00Z +// 2022-03-01T15:00:00Z var UpgradeOhSnapHeight = abi.ChainEpoch(1594680) func init() { From 94bfd5cf664cdcb14cf65c5cd9d6815aaff7aab3 Mon Sep 17 00:00:00 2001 From: jennijuju Date: Fri, 18 Feb 2022 08:40:23 -0500 Subject: [PATCH 75/82] version v1.14.1 --- CHANGELOG.md | 4 ++++ build/openrpc/full.json.gz | Bin 25706 -> 25706 bytes build/openrpc/miner.json.gz | Bin 11745 -> 11745 bytes build/openrpc/worker.json.gz | Bin 3845 -> 3846 bytes build/version.go | 2 +- documentation/en/cli-lotus-miner.md | 2 +- documentation/en/cli-lotus-worker.md | 2 +- documentation/en/cli-lotus.md | 2 +- 8 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8325634ba..b6098a615 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Lotus changelog +# 1.14.1 / 2022-02-18 + +This is an **optional** release of lotus, that fixes the incorrect *comment* of network v15 OhSnap upgrade **date**. Note the actual upgrade epoch in [v1.14.0](https://github.com/filecoin-project/lotus/releases/tag/v1.14.0) was correct. + # 1.14.0 / 2022-02-17 This is a MANDATORY release of Lotus that introduces [Filecoin network v15, diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 59ed94d906fbb37742708833d85ee3df3bf34e96..af980b822822ac72d5957b3d032716c8f83d8b9b 100644 GIT binary patch delta 23 fcmaELg7MV}#tEHFZ@+BpzLCuSJy|G=o0S0onA8f# delta 23 fcmaELg7MV}#tEHFx!*T--$-Wfx>c3M&B_1(lhO*j diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 0c373615f6ed00e70a7855e85961e11c0f2d82b6..e4cd5c9b827614c3e277177091a8fa58f50b23a2 100644 GIT binary patch delta 8234 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 delta 8234 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 diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 9e8ba2ff2f321d4729101ba8576b69bdd0abc8fb..34d68c8b325a588c9bbf20ecbe934fefde29f04b 100644 GIT binary patch delta 3754 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 UeYgI900030{|lEULIvai0GM@IcK`qY delta 3753 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 diff --git a/build/version.go b/build/version.go index 3f0c9a571..fd790a795 100644 --- a/build/version.go +++ b/build/version.go @@ -37,7 +37,7 @@ func BuildTypeString() string { } // BuildVersion is the local build version -const BuildVersion = "1.14.0" +const BuildVersion = "1.14.1" func UserVersion() string { if os.Getenv("LOTUS_VERSION_IGNORE_COMMIT") == "1" { diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index 436ce90b2..dc7ba1bea 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -7,7 +7,7 @@ USAGE: lotus-miner [global options] command [command options] [arguments...] VERSION: - 1.14.0 + 1.14.1 COMMANDS: init Initialize a lotus miner repo diff --git a/documentation/en/cli-lotus-worker.md b/documentation/en/cli-lotus-worker.md index 0a0689664..09cd157e9 100644 --- a/documentation/en/cli-lotus-worker.md +++ b/documentation/en/cli-lotus-worker.md @@ -7,7 +7,7 @@ USAGE: lotus-worker [global options] command [command options] [arguments...] VERSION: - 1.14.0 + 1.14.1 COMMANDS: run Start lotus worker diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index 79131f3df..f2a0542d6 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -7,7 +7,7 @@ USAGE: lotus [global options] command [command options] [arguments...] VERSION: - 1.14.0 + 1.14.1 COMMANDS: daemon Start a lotus daemon process From 1c397305313e21754d0283e9de0b95767631bc43 Mon Sep 17 00:00:00 2001 From: "florian@eyesr.fr" Date: Fri, 18 Feb 2022 15:46:40 +0000 Subject: [PATCH 76/82] Fix #8095 Clear the list of miner addresses and successfull get-asked before asking for new ones Previous behavior : if get-ask failed for miner(s), the faulty miner will be retried each time, so you have to stop the command and start again to change this faulty miner (instead of removing from the list on the new attempt) --- cli/client.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cli/client.go b/cli/client.go index 634bd18e5..64114fa17 100644 --- a/cli/client.go +++ b/cli/client.go @@ -667,6 +667,8 @@ uiLoop: state = "miner" case "miner": + maddrs = maddrs[:0] + ask = ask[:0] afmt.Print("Miner Addresses (f0.. f0..), none to find: ") _maddrsStr, _, err := rl.ReadLine() @@ -802,7 +804,8 @@ uiLoop: dealCount, err = strconv.ParseInt(string(dealcStr), 10, 64) if err != nil { - return err + printErr(xerrors.Errorf("reading deal count: invalid number")) + continue } color.Blue(".. Picking miners") @@ -859,12 +862,13 @@ uiLoop: a, err := api.ClientQueryAsk(ctx, *mi.PeerId, maddr) if err != nil { - printErr(xerrors.Errorf("failed to query ask: %w", err)) + printErr(xerrors.Errorf("failed to query ask for miner %s: %w", maddr.String(), err)) state = "miner" continue uiLoop } ask = append(ask, *a) + } // TODO: run more validation From e2a93a3febfea5b42ebb5f7bb48a38ae7007ad41 Mon Sep 17 00:00:00 2001 From: Elijah Seed-Arita Date: Fri, 18 Feb 2022 11:09:56 -1000 Subject: [PATCH 77/82] add itests ensemble mocknet getter --- itests/kit/ensemble.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/itests/kit/ensemble.go b/itests/kit/ensemble.go index 0227ee81e..6a0158429 100644 --- a/itests/kit/ensemble.go +++ b/itests/kit/ensemble.go @@ -151,6 +151,11 @@ func NewEnsemble(t *testing.T, opts ...EnsembleOpt) *Ensemble { return n } +// Mocknet returns the underlying mocknet. +func (n *Ensemble) Mocknet() mocknet.Mocknet { + return n.mn +} + // FullNode enrolls a new full node. func (n *Ensemble) FullNode(full *TestFullNode, opts ...NodeOpt) *Ensemble { options := DefaultNodeOpts From 9bb9bb56828df5886def1e72c8bfbaa3e7bf801d Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Sat, 19 Feb 2022 16:25:18 +0100 Subject: [PATCH 78/82] Retract force-pushed v1.14.0 to work around stale gomod caches --- go.mod | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go.mod b/go.mod index aa80f0bdc..293a17bc4 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module github.com/filecoin-project/lotus go 1.16 +retract v1.14.0 // Accidentally force-pushed tag, use v1.14.1+ instead. + require ( contrib.go.opencensus.io/exporter/prometheus v0.4.0 github.com/BurntSushi/toml v0.4.1 From 87c63e2cb739207a31e9ebb53fa9b35c226304da Mon Sep 17 00:00:00 2001 From: Darko Brdareski Date: Mon, 21 Feb 2022 12:33:07 +0100 Subject: [PATCH 79/82] Add stm file annotation --- cli/chain_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/chain_test.go b/cli/chain_test.go index 48c4af05d..f525a0b35 100644 --- a/cli/chain_test.go +++ b/cli/chain_test.go @@ -1,3 +1,4 @@ +//stm: #integration package cli import ( From 675012fe1c1bfe380f77da0a43333374d65d71e1 Mon Sep 17 00:00:00 2001 From: Darko Brdareski Date: Mon, 21 Feb 2022 16:49:07 +0100 Subject: [PATCH 80/82] Fix matching for TestInspectUsage --- cli/chain_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/chain_test.go b/cli/chain_test.go index f525a0b35..efd1db14d 100644 --- a/cli/chain_test.go +++ b/cli/chain_test.go @@ -325,7 +325,7 @@ func TestInspectUsage(t *testing.T) { // check for gas by sender assert.Contains(t, out, "By Sender") // check for gas by method - assert.Contains(t, out, "Send") + assert.Contains(t, out, "By Method:\nSend") }) } From 580fa86ea3f173b6d8b7689f31050b16f7ffa075 Mon Sep 17 00:00:00 2001 From: Darko Brdareski Date: Mon, 21 Feb 2022 17:57:01 +0100 Subject: [PATCH 81/82] Change annotation to #cli --- cli/chain_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/chain_test.go b/cli/chain_test.go index efd1db14d..0b3cce728 100644 --- a/cli/chain_test.go +++ b/cli/chain_test.go @@ -1,4 +1,4 @@ -//stm: #integration +//stm: #cli package cli import ( From 012cf96d600c669fb90f87dec501b774a3069b12 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 22 Feb 2022 12:19:26 +0200 Subject: [PATCH 82/82] update go-libp2p to v0.18.0-rc5 --- go.mod | 4 ++-- go.sum | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 293a17bc4..0fae1713c 100644 --- a/go.mod +++ b/go.mod @@ -110,7 +110,7 @@ require ( github.com/kelseyhightower/envconfig v1.4.0 github.com/libp2p/go-buffer-pool v0.0.2 github.com/libp2p/go-eventbus v0.2.1 - github.com/libp2p/go-libp2p v0.18.0-rc4 + github.com/libp2p/go-libp2p v0.18.0-rc5 github.com/libp2p/go-libp2p-connmgr v0.3.1 // indirect github.com/libp2p/go-libp2p-core v0.14.0 github.com/libp2p/go-libp2p-discovery v0.6.0 @@ -122,7 +122,7 @@ require ( github.com/libp2p/go-libp2p-record v0.1.3 github.com/libp2p/go-libp2p-resource-manager v0.1.4 github.com/libp2p/go-libp2p-routing-helpers v0.2.3 - github.com/libp2p/go-libp2p-swarm v0.10.1 + github.com/libp2p/go-libp2p-swarm v0.10.2 github.com/libp2p/go-libp2p-tls v0.3.1 github.com/libp2p/go-libp2p-yamux v0.8.2 github.com/libp2p/go-maddr-filter v0.1.0 diff --git a/go.sum b/go.sum index 292747fc8..417267e37 100644 --- a/go.sum +++ b/go.sum @@ -995,8 +995,8 @@ github.com/libp2p/go-libp2p v0.14.4/go.mod h1:EIRU0Of4J5S8rkockZM7eJp2S0UrCyi55m github.com/libp2p/go-libp2p v0.16.0/go.mod h1:ump42BsirwAWxKzsCiFnTtN1Yc+DuPu76fyMX364/O4= github.com/libp2p/go-libp2p v0.17.0/go.mod h1:Fkin50rsGdv5mm5BshBUtPRZknt9esfmYXBOYcwOTgw= github.com/libp2p/go-libp2p v0.18.0-rc1/go.mod h1:RgYlH7IIWHXREimC92bw5Lg1V2R5XmSzuLHb5fTnr+8= -github.com/libp2p/go-libp2p v0.18.0-rc4 h1:OUsSbeu7q+Ck/bV9wHDxFzb08ORqBupHhpCmRBhWrJ8= -github.com/libp2p/go-libp2p v0.18.0-rc4/go.mod h1:wzmsk1ioOq9FGQys2BN5BIw4nugP6+R+CyW3JbPEbbs= +github.com/libp2p/go-libp2p v0.18.0-rc5 h1:88wWDHb9nNo0vBNCupLde3OTnFAkugOCNkrDfl3ivK4= +github.com/libp2p/go-libp2p v0.18.0-rc5/go.mod h1:aZPS5l84bDvCvP4jkyEUT/J6YOpUq33Fgqrs3K59mpI= github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052/go.mod h1:nRMRTab+kZuk0LnKZpxhOVH/ndsdr2Nr//Zltc/vwgo= github.com/libp2p/go-libp2p-asn-util v0.1.0 h1:rABPCO77SjdbJ/eJ/ynIo8vWICy1VEnL5JAxJbQLo1E= github.com/libp2p/go-libp2p-asn-util v0.1.0/go.mod h1:wu+AnM9Ii2KgO5jMmS1rz9dvzTdj8BXqsPR9HR0XB7I= @@ -1182,8 +1182,8 @@ github.com/libp2p/go-libp2p-swarm v0.5.3/go.mod h1:NBn7eNW2lu568L7Ns9wdFrOhgRlkR github.com/libp2p/go-libp2p-swarm v0.8.0/go.mod h1:sOMp6dPuqco0r0GHTzfVheVBh6UEL0L1lXUZ5ot2Fvc= github.com/libp2p/go-libp2p-swarm v0.9.0/go.mod h1:2f8d8uxTJmpeqHF/1ujjdXZp+98nNIbujVOMEZxCbZ8= github.com/libp2p/go-libp2p-swarm v0.10.0/go.mod h1:71ceMcV6Rg/0rIQ97rsZWMzto1l9LnNquef+efcRbmA= -github.com/libp2p/go-libp2p-swarm v0.10.1 h1:lXW3pgGt+BVmkzcFX61erX7l6Lt+WAamNhwa2Kf3eJM= -github.com/libp2p/go-libp2p-swarm v0.10.1/go.mod h1:Pdkq0QU5a+qu+oyqIV3bknMsnzk9lnNyKvB9acJ5aZs= +github.com/libp2p/go-libp2p-swarm v0.10.2 h1:UaXf+CTq6Ns1N2V1EgqJ9Q3xaRsiN7ImVlDMpirMAWw= +github.com/libp2p/go-libp2p-swarm v0.10.2/go.mod h1:Pdkq0QU5a+qu+oyqIV3bknMsnzk9lnNyKvB9acJ5aZs= github.com/libp2p/go-libp2p-testing v0.0.1/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= @@ -1299,8 +1299,9 @@ github.com/libp2p/go-tcp-transport v0.2.3/go.mod h1:9dvr03yqrPyYGIEN6Dy5UvdJZjyP github.com/libp2p/go-tcp-transport v0.2.4/go.mod h1:9dvr03yqrPyYGIEN6Dy5UvdJZjyPFvl1S/igQ5QD1SU= github.com/libp2p/go-tcp-transport v0.2.7/go.mod h1:lue9p1b3VmZj1MhhEGB/etmvF/nBQ0X9CW2DutBT3MM= github.com/libp2p/go-tcp-transport v0.4.0/go.mod h1:0y52Rwrn4076xdJYu/51/qJIdxz+EWDAOG2S45sV3VI= -github.com/libp2p/go-tcp-transport v0.5.0 h1:3ZPW8HAuyRAuFzyabE0hSrCXKKSWzROnZZX7DtcIatY= github.com/libp2p/go-tcp-transport v0.5.0/go.mod h1:UPPL0DIjQqiWRwVAb+CEQlaAG0rp/mCqJfIhFcLHc4Y= +github.com/libp2p/go-tcp-transport v0.5.1 h1:edOOs688VLZAozWC7Kj5/6HHXKNwi9M6wgRmmLa8M6Q= +github.com/libp2p/go-tcp-transport v0.5.1/go.mod h1:UPPL0DIjQqiWRwVAb+CEQlaAG0rp/mCqJfIhFcLHc4Y= github.com/libp2p/go-testutil v0.0.1/go.mod h1:iAcJc/DKJQanJ5ws2V+u5ywdL2n12X1WbbEG+Jjy69I= github.com/libp2p/go-testutil v0.1.0/go.mod h1:81b2n5HypcVyrCg/MJx4Wgfp/VHojytjVe/gLzZ2Ehc= github.com/libp2p/go-ws-transport v0.0.5/go.mod h1:Qbl4BxPfXXhhd/o0wcrgoaItHqA9tnZjoFZnxykuaXU=