From 2ab6099e3ffcdc1ce6f4b612fa5e044ad54c02e3 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 4 Sep 2020 15:42:11 +0300 Subject: [PATCH 1/5] remove unused ts argument from Trim --- chain/messagepool/repub.go | 2 +- chain/messagepool/selection.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/chain/messagepool/repub.go b/chain/messagepool/repub.go index 1173bdb48b5..53b6e0bdea6 100644 --- a/chain/messagepool/repub.go +++ b/chain/messagepool/repub.go @@ -111,7 +111,7 @@ func (mp *MessagePool) republishPendingMessages() error { // we can't fit the current chain but there is gas to spare // trim it and push it down - chain.Trim(gasLimit, mp, baseFee, ts) + chain.Trim(gasLimit, mp, baseFee) for j := i; j < len(chains)-1; j++ { if chains[j].Before(chains[j+1]) { break diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index 5ba679d761a..65facd31736 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -217,7 +217,7 @@ tailLoop: for gasLimit >= minGas && last < len(chains) { // trim if necessary if chains[last].gasLimit > gasLimit { - chains[last].Trim(gasLimit, mp, baseFee, ts) + chains[last].Trim(gasLimit, mp, baseFee) } // push down if it hasn't been invalidated @@ -284,7 +284,7 @@ tailLoop: } // dependencies fit, just trim it - chain.Trim(gasLimit-depGasLimit, mp, baseFee, ts) + chain.Trim(gasLimit-depGasLimit, mp, baseFee) last += i continue tailLoop } @@ -389,7 +389,7 @@ func (mp *MessagePool) selectMessagesGreedy(curTs, ts *types.TipSet) ([]*types.S tailLoop: for gasLimit >= minGas && last < len(chains) { // trim - chains[last].Trim(gasLimit, mp, baseFee, ts) + chains[last].Trim(gasLimit, mp, baseFee) // push down if it hasn't been invalidated if chains[last].valid { @@ -497,7 +497,7 @@ func (mp *MessagePool) selectPriorityMessages(pending map[address.Address]map[ui tailLoop: for gasLimit >= minGas && last < len(chains) { // trim, discarding negative performing messages - chains[last].Trim(gasLimit, mp, baseFee, ts) + chains[last].Trim(gasLimit, mp, baseFee) // push down if it hasn't been invalidated if chains[last].valid { @@ -775,7 +775,7 @@ func (mc *msgChain) Before(other *msgChain) bool { (mc.gasPerf == other.gasPerf && mc.gasReward.Cmp(other.gasReward) > 0) } -func (mc *msgChain) Trim(gasLimit int64, mp *MessagePool, baseFee types.BigInt, ts *types.TipSet) { +func (mc *msgChain) Trim(gasLimit int64, mp *MessagePool, baseFee types.BigInt) { i := len(mc.msgs) - 1 for i >= 0 && (mc.gasLimit > gasLimit || mc.gasPerf < 0) { gasReward := mp.getGasReward(mc.msgs[i], baseFee) From 14f4f9bbfec7c88910e27e426f0aecc5fd78f0e1 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 4 Sep 2020 16:04:26 +0300 Subject: [PATCH 2/5] allow negative gas perf and decrease block packing when the baseFee is sky high. --- chain/messagepool/repub.go | 11 +++++-- chain/messagepool/selection.go | 58 +++++++++++++++++++++++----------- 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/chain/messagepool/repub.go b/chain/messagepool/repub.go index 53b6e0bdea6..5e767cc3649 100644 --- a/chain/messagepool/repub.go +++ b/chain/messagepool/repub.go @@ -75,6 +75,13 @@ func (mp *MessagePool) republishPendingMessages() error { gasLimit := int64(build.BlockGasLimit) minGas := int64(gasguess.MinGas) + + allowNegative := false + if skyHighBaseFeeThreshold.LessThan(baseFee) { + allowNegative = true + gasLimit = int64(float64(gasLimit) * skyHighBaseFeeGasLimitRatio) + } + var msgs []*types.SignedMessage for i := 0; i < len(chains); { chain := chains[i] @@ -91,7 +98,7 @@ func (mp *MessagePool) republishPendingMessages() error { // we don't republish negative performing chains, as they won't be included in // a block anyway - if chain.gasPerf < 0 { + if !allowNegative && chain.gasPerf < 0 { break } @@ -111,7 +118,7 @@ func (mp *MessagePool) republishPendingMessages() error { // we can't fit the current chain but there is gas to spare // trim it and push it down - chain.Trim(gasLimit, mp, baseFee) + chain.Trim(gasLimit, mp, baseFee, allowNegative) for j := i; j < len(chains)-1; j++ { if chains[j].Before(chains[j+1]) { break diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index 65facd31736..236fd825bed 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -16,7 +16,13 @@ import ( abig "github.com/filecoin-project/specs-actors/actors/abi/big" ) -var bigBlockGasLimit = big.NewInt(build.BlockGasLimit) +var ( + bigBlockGasLimit = big.NewInt(build.BlockGasLimit) + + // TODO adjust these magic numbers with some math + skyHighBaseFeeThreshold = abig.NewInt(10_000_000) + skyHighBaseFeeGasLimitRatio = 0.1 +) const MaxBlocks = 15 @@ -59,6 +65,11 @@ func (mp *MessagePool) selectMessagesOptimal(curTs, ts *types.TipSet, tq float64 return nil, xerrors.Errorf("computing basefee: %w", err) } + allowNegative := false + if skyHighBaseFeeThreshold.LessThan(baseFee) { + allowNegative = true + } + // 0. Load messages from the target tipset; if it is the same as the current tipset in // the mpool, then this is just the pending messages pending, err := mp.getPendingMessages(curTs, ts) @@ -100,8 +111,8 @@ func (mp *MessagePool) selectMessagesOptimal(curTs, ts *types.TipSet, tq float64 return chains[i].Before(chains[j]) }) - if len(chains) != 0 && chains[0].gasPerf < 0 { - log.Warnw("all messages in mpool have non-positive gas performance", "bestGasPerf", chains[0].gasPerf) + if len(chains) != 0 && !allowNegative && chains[0].gasPerf < 0 { + log.Warnw("all messages in mpool have negative gas performance", "bestGasPerf", chains[0].gasPerf) return nil, nil } @@ -153,7 +164,7 @@ func (mp *MessagePool) selectMessagesOptimal(curTs, ts *types.TipSet, tq float64 last := len(chains) for i, chain := range chains { // did we run out of performing chains? - if chain.gasPerf < 0 { + if !allowNegative && chain.gasPerf < 0 { break } @@ -217,7 +228,7 @@ tailLoop: for gasLimit >= minGas && last < len(chains) { // trim if necessary if chains[last].gasLimit > gasLimit { - chains[last].Trim(gasLimit, mp, baseFee) + chains[last].Trim(gasLimit, mp, baseFee, allowNegative) } // push down if it hasn't been invalidated @@ -243,7 +254,7 @@ tailLoop: } // if gasPerf < 0 we have no more profitable chains - if chain.gasPerf < 0 { + if !allowNegative && chain.gasPerf < 0 { break tailLoop } @@ -284,7 +295,7 @@ tailLoop: } // dependencies fit, just trim it - chain.Trim(gasLimit-depGasLimit, mp, baseFee) + chain.Trim(gasLimit-depGasLimit, mp, baseFee, allowNegative) last += i continue tailLoop } @@ -308,6 +319,11 @@ func (mp *MessagePool) selectMessagesGreedy(curTs, ts *types.TipSet) ([]*types.S return nil, xerrors.Errorf("computing basefee: %w", err) } + allowNegative := false + if skyHighBaseFeeThreshold.LessThan(baseFee) { + allowNegative = true + } + // 0. Load messages for the target tipset; if it is the same as the current tipset in the mpool // then this is just the pending messages pending, err := mp.getPendingMessages(curTs, ts) @@ -349,8 +365,8 @@ func (mp *MessagePool) selectMessagesGreedy(curTs, ts *types.TipSet) ([]*types.S return chains[i].Before(chains[j]) }) - if len(chains) != 0 && chains[0].gasPerf < 0 { - log.Warnw("all messages in mpool have non-positive gas performance", "bestGasPerf", chains[0].gasPerf) + if len(chains) != 0 && !allowNegative && chains[0].gasPerf < 0 { + log.Warnw("all messages in mpool have negative gas performance", "bestGasPerf", chains[0].gasPerf) return nil, nil } @@ -360,7 +376,7 @@ func (mp *MessagePool) selectMessagesGreedy(curTs, ts *types.TipSet) ([]*types.S last := len(chains) for i, chain := range chains { // did we run out of performing chains? - if chain.gasPerf < 0 { + if !allowNegative && chain.gasPerf < 0 { break } @@ -389,7 +405,7 @@ func (mp *MessagePool) selectMessagesGreedy(curTs, ts *types.TipSet) ([]*types.S tailLoop: for gasLimit >= minGas && last < len(chains) { // trim - chains[last].Trim(gasLimit, mp, baseFee) + chains[last].Trim(gasLimit, mp, baseFee, allowNegative) // push down if it hasn't been invalidated if chains[last].valid { @@ -409,7 +425,7 @@ tailLoop: } // if gasPerf < 0 we have no more profitable chains - if chain.gasPerf < 0 { + if !allowNegative && chain.gasPerf < 0 { break tailLoop } @@ -448,6 +464,12 @@ func (mp *MessagePool) selectPriorityMessages(pending map[address.Address]map[ui gasLimit := int64(build.BlockGasLimit) minGas := int64(gasguess.MinGas) + allowNegative := false + if skyHighBaseFeeThreshold.LessThan(baseFee) { + allowNegative = true + gasLimit = int64(float64(gasLimit) * skyHighBaseFeeGasLimitRatio) + } + // 1. Get priority actor chains var chains []*msgChain priority := mp.cfg.PriorityAddrs @@ -471,7 +493,7 @@ func (mp *MessagePool) selectPriorityMessages(pending map[address.Address]map[ui return chains[i].Before(chains[j]) }) - if len(chains) != 0 && chains[0].gasPerf < 0 { + if len(chains) != 0 && !allowNegative && chains[0].gasPerf < 0 { log.Warnw("all priority messages in mpool have negative gas performance", "bestGasPerf", chains[0].gasPerf) return nil, gasLimit } @@ -479,7 +501,7 @@ func (mp *MessagePool) selectPriorityMessages(pending map[address.Address]map[ui // 3. Merge chains until the block limit, as long as they have non-negative gas performance last := len(chains) for i, chain := range chains { - if chain.gasPerf < 0 { + if !allowNegative && chain.gasPerf < 0 { break } @@ -497,7 +519,7 @@ func (mp *MessagePool) selectPriorityMessages(pending map[address.Address]map[ui tailLoop: for gasLimit >= minGas && last < len(chains) { // trim, discarding negative performing messages - chains[last].Trim(gasLimit, mp, baseFee) + chains[last].Trim(gasLimit, mp, baseFee, allowNegative) // push down if it hasn't been invalidated if chains[last].valid { @@ -517,7 +539,7 @@ tailLoop: } // if gasPerf < 0 we have no more profitable chains - if chain.gasPerf < 0 { + if !allowNegative && chain.gasPerf < 0 { break tailLoop } @@ -775,9 +797,9 @@ func (mc *msgChain) Before(other *msgChain) bool { (mc.gasPerf == other.gasPerf && mc.gasReward.Cmp(other.gasReward) > 0) } -func (mc *msgChain) Trim(gasLimit int64, mp *MessagePool, baseFee types.BigInt) { +func (mc *msgChain) Trim(gasLimit int64, mp *MessagePool, baseFee types.BigInt, allowNegative bool) { i := len(mc.msgs) - 1 - for i >= 0 && (mc.gasLimit > gasLimit || mc.gasPerf < 0) { + for i >= 0 && (mc.gasLimit > gasLimit || (!allowNegative && mc.gasPerf < 0)) { gasReward := mp.getGasReward(mc.msgs[i], baseFee) mc.gasReward = new(big.Int).Sub(mc.gasReward, gasReward) mc.gasLimit -= mc.msgs[i].Message.GasLimit From 9647ba0b13cc8ba59adec7d10a569822d362b5cc Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 4 Sep 2020 16:42:01 +0300 Subject: [PATCH 3/5] add selection test for sky high base fee conditions --- chain/messagepool/messagepool_test.go | 5 +- chain/messagepool/selection_test.go | 110 ++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/chain/messagepool/messagepool_test.go b/chain/messagepool/messagepool_test.go index 484c72746fa..c97c031667c 100644 --- a/chain/messagepool/messagepool_test.go +++ b/chain/messagepool/messagepool_test.go @@ -34,6 +34,8 @@ type testMpoolAPI struct { tipsets []*types.TipSet published int + + baseFee types.BigInt } func newTestMpoolAPI() *testMpoolAPI { @@ -41,6 +43,7 @@ func newTestMpoolAPI() *testMpoolAPI { bmsgs: make(map[cid.Cid][]*types.SignedMessage), statenonce: make(map[address.Address]uint64), balance: make(map[address.Address]types.BigInt), + baseFee: types.NewInt(100), } genesis := mock.MkBlock(nil, 1, 1) tma.tipsets = append(tma.tipsets, mock.TipSet(genesis)) @@ -182,7 +185,7 @@ func (tma *testMpoolAPI) LoadTipSet(tsk types.TipSetKey) (*types.TipSet, error) } func (tma *testMpoolAPI) ChainComputeBaseFee(ctx context.Context, ts *types.TipSet) (types.BigInt, error) { - return types.NewInt(100), nil + return tma.baseFee, nil } func assertNonce(t *testing.T, mp *MessagePool, addr address.Address, val uint64) { diff --git a/chain/messagepool/selection_test.go b/chain/messagepool/selection_test.go index a9ead3c0129..235ac342b55 100644 --- a/chain/messagepool/selection_test.go +++ b/chain/messagepool/selection_test.go @@ -728,6 +728,116 @@ func TestPriorityMessageSelection2(t *testing.T) { } } +func TestSkyHighBaseFeeMessageSelection(t *testing.T) { + mp, tma := makeTestMpool() + + w1, err := wallet.NewWallet(wallet.NewMemKeyStore()) + if err != nil { + t.Fatal(err) + } + + a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1) + if err != nil { + t.Fatal(err) + } + + w2, err := wallet.NewWallet(wallet.NewMemKeyStore()) + if err != nil { + t.Fatal(err) + } + + a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1) + if err != nil { + t.Fatal(err) + } + + block := tma.nextBlock() + ts := mock.TipSet(block) + tma.applyBlock(t, block) + + gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin.StorageMarketActorCodeID, M: 2}] + + tma.setBalance(a1, 1) // in FIL + tma.setBalance(a2, 1) // in FIL + + // we create 1 block worth of messages; the selection algorithms should only fill the block + // up to the sky high ratio + tma.baseFee = types.BigAdd(skyHighBaseFeeThreshold, types.NewInt(1_000_000)) + skyHighGasLimit := int64(float64(build.BlockGasLimit) * skyHighBaseFeeGasLimitRatio) + + nMessages := int((build.BlockGasLimit / gasLimit) + 1) + for i := 0; i < nMessages; i++ { + bias := (nMessages - i) / 3 + m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(1+i%3+bias)) + mustAdd(t, mp, m) + } + + // test greedy selection + msgs, err := mp.SelectMessages(ts, 1.0) + if err != nil { + t.Fatal(err) + } + + mGasLimit := int64(0) + for _, m := range msgs { + mGasLimit += m.Message.GasLimit + } + + if mGasLimit > skyHighGasLimit { + t.Fatalf("expected block gas limit to be less than sky high gas limit; got %d, limit is %d", + mGasLimit, skyHighGasLimit) + } + + // test optimal selection + msgs, err = mp.SelectMessages(ts, 0.1) + if err != nil { + t.Fatal(err) + } + + mGasLimit = 0 + for _, m := range msgs { + mGasLimit += m.Message.GasLimit + } + + if mGasLimit > skyHighGasLimit { + t.Fatalf("expected block gas limit to be less than sky high gas limit; got %d, limit is %d", + mGasLimit, skyHighGasLimit) + } + + // test priority selection + mp.cfg.PriorityAddrs = []address.Address{a1} + + msgs, err = mp.SelectMessages(ts, 1.0) + if err != nil { + t.Fatal(err) + } + + mGasLimit = int64(0) + for _, m := range msgs { + mGasLimit += m.Message.GasLimit + } + + if mGasLimit > skyHighGasLimit { + t.Fatalf("expected block gas limit to be less than sky high gas limit; got %d, limit is %d", + mGasLimit, skyHighGasLimit) + } + + msgs, err = mp.SelectMessages(ts, 0.1) + if err != nil { + t.Fatal(err) + } + + mGasLimit = 0 + for _, m := range msgs { + mGasLimit += m.Message.GasLimit + } + + if mGasLimit > skyHighGasLimit { + t.Fatalf("expected block gas limit to be less than sky high gas limit; got %d, limit is %d", + mGasLimit, skyHighGasLimit) + } +} + func TestOptimalMessageSelection1(t *testing.T) { // 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 40ca58237079e6b8d54b7b92ee9e9f6b6596f129 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 4 Sep 2020 17:02:00 +0300 Subject: [PATCH 4/5] optimal selection partitioning should make smaller blocks when in skyhigh mode --- chain/messagepool/selection.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index 236fd825bed..fd15bcaafb0 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -123,6 +123,12 @@ func (mp *MessagePool) selectMessagesOptimal(curTs, ts *types.TipSet, tq float64 partitions := make([][]*msgChain, MaxBlocks) for i := 0; i < MaxBlocks && nextChain < len(chains); i++ { gasLimit := int64(build.BlockGasLimit) + + // if base fee is sky high and we allow negatives, then the blocks will be smaller + if allowNegative { + gasLimit = int64(float64(gasLimit) * skyHighBaseFeeGasLimitRatio) + } + for nextChain < len(chains) { chain := chains[nextChain] nextChain++ From c068bc9e338323413fde1c9f9a1ed98552263e0f Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 4 Sep 2020 18:23:12 +0200 Subject: [PATCH 5/5] Adjust numbers Signed-off-by: Jakub Sztandera --- chain/messagepool/selection.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index fd15bcaafb0..303a8bfac56 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -19,9 +19,8 @@ import ( var ( bigBlockGasLimit = big.NewInt(build.BlockGasLimit) - // TODO adjust these magic numbers with some math - skyHighBaseFeeThreshold = abig.NewInt(10_000_000) - skyHighBaseFeeGasLimitRatio = 0.1 + skyHighBaseFeeThreshold = abig.NewInt(111902962585) + skyHighBaseFeeGasLimitRatio = 0.4 ) const MaxBlocks = 15