1469 lines
38 KiB
Go
1469 lines
38 KiB
Go
package messagepool
|
|
|
|
import (
|
|
"compress/gzip"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"math/big"
|
|
"math/rand"
|
|
"os"
|
|
"sort"
|
|
"testing"
|
|
|
|
"github.com/filecoin-project/go-address"
|
|
"github.com/ipfs/go-cid"
|
|
"github.com/ipfs/go-datastore"
|
|
logging "github.com/ipfs/go-log/v2"
|
|
|
|
builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin"
|
|
|
|
"github.com/filecoin-project/lotus/build"
|
|
"github.com/filecoin-project/lotus/chain/messagepool/gasguess"
|
|
"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/api"
|
|
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
|
|
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
|
|
)
|
|
|
|
func init() {
|
|
// bump this for the selection tests
|
|
MaxActorPendingMessages = 1000000
|
|
}
|
|
|
|
func makeTestMessage(w *wallet.LocalWallet, from, to address.Address, nonce uint64, gasLimit int64, gasPrice uint64) *types.SignedMessage {
|
|
msg := &types.Message{
|
|
From: from,
|
|
To: to,
|
|
Method: 2,
|
|
Value: types.FromFil(0),
|
|
Nonce: nonce,
|
|
GasLimit: gasLimit,
|
|
GasFeeCap: types.NewInt(100 + gasPrice),
|
|
GasPremium: types.NewInt(gasPrice),
|
|
}
|
|
sig, err := w.WalletSign(context.TODO(), from, msg.Cid().Bytes(), api.MsgMeta{})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return &types.SignedMessage{
|
|
Message: *msg,
|
|
Signature: *sig,
|
|
}
|
|
}
|
|
|
|
func makeTestMpool() (*MessagePool, *testMpoolAPI) {
|
|
tma := newTestMpoolAPI()
|
|
ds := datastore.NewMapDatastore()
|
|
mp, err := New(tma, ds, "test", nil)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return mp, tma
|
|
}
|
|
|
|
func TestMessageChains(t *testing.T) {
|
|
mp, tma := makeTestMpool()
|
|
|
|
// the actors
|
|
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
block := tma.nextBlock()
|
|
ts := mock.TipSet(block)
|
|
|
|
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin2.StorageMarketActorCodeID, M: 2}]
|
|
|
|
tma.setBalance(a1, 1) // in FIL
|
|
|
|
// test chain aggregations
|
|
|
|
// test1: 10 messages from a1 to a2, with increasing gasPerf; it should
|
|
// make a single chain with 10 messages given enough balance
|
|
mset := make(map[uint64]*types.SignedMessage)
|
|
for i := 0; i < 10; i++ {
|
|
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1))
|
|
mset[uint64(i)] = m
|
|
}
|
|
baseFee := types.NewInt(0)
|
|
|
|
chains := mp.createMessageChains(a1, mset, baseFee, ts)
|
|
if len(chains) != 1 {
|
|
t.Fatal("expected a single chain")
|
|
}
|
|
if len(chains[0].msgs) != 10 {
|
|
t.Fatalf("expected 10 messages in the chain but got %d", len(chains[0].msgs))
|
|
}
|
|
for i, m := range chains[0].msgs {
|
|
if m.Message.Nonce != uint64(i) {
|
|
t.Fatalf("expected nonce %d but got %d", i, m.Message.Nonce)
|
|
}
|
|
}
|
|
|
|
// test2 : 10 messages from a1 to a2, with decreasing gasPerf; it should
|
|
// make 10 chains with 1 message each
|
|
mset = make(map[uint64]*types.SignedMessage)
|
|
for i := 0; i < 10; i++ {
|
|
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(10-i))
|
|
mset[uint64(i)] = m
|
|
}
|
|
|
|
chains = mp.createMessageChains(a1, mset, baseFee, ts)
|
|
if len(chains) != 10 {
|
|
t.Fatal("expected 10 chains")
|
|
}
|
|
for i, chain := range chains {
|
|
if len(chain.msgs) != 1 {
|
|
t.Fatalf("expected 1 message in chain %d but got %d", i, len(chain.msgs))
|
|
}
|
|
}
|
|
for i, chain := range chains {
|
|
m := chain.msgs[0]
|
|
if m.Message.Nonce != uint64(i) {
|
|
t.Fatalf("expected nonce %d but got %d", i, m.Message.Nonce)
|
|
}
|
|
}
|
|
|
|
// test3a: 10 messages from a1 to a2, with gasPerf increasing in groups of 3; it should
|
|
// merge them in two chains, one with 9 messages and one with the last message
|
|
mset = make(map[uint64]*types.SignedMessage)
|
|
for i := 0; i < 10; i++ {
|
|
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(1+i%3))
|
|
mset[uint64(i)] = m
|
|
}
|
|
|
|
chains = mp.createMessageChains(a1, mset, baseFee, ts)
|
|
if len(chains) != 2 {
|
|
t.Fatal("expected 1 chain")
|
|
}
|
|
|
|
if len(chains[0].msgs) != 9 {
|
|
t.Fatalf("expected 9 messages in the chain but got %d", len(chains[0].msgs))
|
|
}
|
|
if len(chains[1].msgs) != 1 {
|
|
t.Fatalf("expected 1 messages in the chain but got %d", len(chains[1].msgs))
|
|
}
|
|
nextNonce := 0
|
|
for _, chain := range chains {
|
|
for _, m := range chain.msgs {
|
|
if m.Message.Nonce != uint64(nextNonce) {
|
|
t.Fatalf("expected nonce %d but got %d", nextNonce, m.Message.Nonce)
|
|
}
|
|
nextNonce++
|
|
}
|
|
}
|
|
|
|
// test3b: 10 messages from a1 to a2, with gasPerf decreasing in groups of 3 with a bias for the
|
|
// earlier chains; it should make 4 chains, the first 3 with 3 messages and the last with
|
|
// a single message
|
|
mset = make(map[uint64]*types.SignedMessage)
|
|
for i := 0; i < 10; i++ {
|
|
bias := (12 - i) / 3
|
|
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(1+i%3+bias))
|
|
mset[uint64(i)] = m
|
|
}
|
|
|
|
chains = mp.createMessageChains(a1, mset, baseFee, ts)
|
|
if len(chains) != 4 {
|
|
t.Fatal("expected 4 chains")
|
|
}
|
|
for i, chain := range chains {
|
|
expectedLen := 3
|
|
if i > 2 {
|
|
expectedLen = 1
|
|
}
|
|
if len(chain.msgs) != expectedLen {
|
|
t.Fatalf("expected %d message in chain %d but got %d", expectedLen, i, len(chain.msgs))
|
|
}
|
|
}
|
|
nextNonce = 0
|
|
for _, chain := range chains {
|
|
for _, m := range chain.msgs {
|
|
if m.Message.Nonce != uint64(nextNonce) {
|
|
t.Fatalf("expected nonce %d but got %d", nextNonce, m.Message.Nonce)
|
|
}
|
|
nextNonce++
|
|
}
|
|
}
|
|
|
|
// test chain breaks
|
|
|
|
// test4: 10 messages with non-consecutive nonces; it should make a single chain with just
|
|
// the first message
|
|
mset = make(map[uint64]*types.SignedMessage)
|
|
for i := 0; i < 10; i++ {
|
|
m := makeTestMessage(w1, a1, a2, uint64(i*2), gasLimit, uint64(i+1))
|
|
mset[uint64(i)] = m
|
|
}
|
|
|
|
chains = mp.createMessageChains(a1, mset, baseFee, ts)
|
|
if len(chains) != 1 {
|
|
t.Fatal("expected a single chain")
|
|
}
|
|
if len(chains[0].msgs) != 1 {
|
|
t.Fatalf("expected 1 message in the chain but got %d", len(chains[0].msgs))
|
|
}
|
|
for i, m := range chains[0].msgs {
|
|
if m.Message.Nonce != uint64(i) {
|
|
t.Fatalf("expected nonce %d but got %d", i, m.Message.Nonce)
|
|
}
|
|
}
|
|
|
|
// test5: 10 messages with increasing gasLimit, except for the 6th message which has less than
|
|
// the epoch gasLimit; it should create a single chain with the first 5 messages
|
|
mset = make(map[uint64]*types.SignedMessage)
|
|
for i := 0; i < 10; i++ {
|
|
var m *types.SignedMessage
|
|
if i != 5 {
|
|
m = makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1))
|
|
} else {
|
|
m = makeTestMessage(w1, a1, a2, uint64(i), 1, uint64(i+1))
|
|
}
|
|
mset[uint64(i)] = m
|
|
}
|
|
|
|
chains = mp.createMessageChains(a1, mset, baseFee, ts)
|
|
if len(chains) != 1 {
|
|
t.Fatal("expected a single chain")
|
|
}
|
|
if len(chains[0].msgs) != 5 {
|
|
t.Fatalf("expected 5 message in the chain but got %d", len(chains[0].msgs))
|
|
}
|
|
for i, m := range chains[0].msgs {
|
|
if m.Message.Nonce != uint64(i) {
|
|
t.Fatalf("expected nonce %d but got %d", i, m.Message.Nonce)
|
|
}
|
|
}
|
|
|
|
// test6: one more message than what can fit in a block according to gas limit, with increasing
|
|
// gasPerf; it should create a single chain with the max messages
|
|
maxMessages := int(build.BlockGasLimit / gasLimit)
|
|
nMessages := maxMessages + 1
|
|
|
|
mset = make(map[uint64]*types.SignedMessage)
|
|
for i := 0; i < nMessages; i++ {
|
|
mset[uint64(i)] = makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1))
|
|
}
|
|
|
|
chains = mp.createMessageChains(a1, mset, baseFee, ts)
|
|
if len(chains) != 1 {
|
|
t.Fatal("expected a single chain")
|
|
}
|
|
if len(chains[0].msgs) != maxMessages {
|
|
t.Fatalf("expected %d message in the chain but got %d", maxMessages, len(chains[0].msgs))
|
|
}
|
|
for i, m := range chains[0].msgs {
|
|
if m.Message.Nonce != uint64(i) {
|
|
t.Fatalf("expected nonce %d but got %d", i, m.Message.Nonce)
|
|
}
|
|
}
|
|
|
|
// test5: insufficient balance for all messages
|
|
tma.setBalanceRaw(a1, types.NewInt(uint64((300)*gasLimit+1)))
|
|
|
|
mset = make(map[uint64]*types.SignedMessage)
|
|
for i := 0; i < 10; i++ {
|
|
mset[uint64(i)] = makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1))
|
|
}
|
|
|
|
chains = mp.createMessageChains(a1, mset, baseFee, ts)
|
|
if len(chains) != 1 {
|
|
t.Fatalf("expected a single chain: got %d", len(chains))
|
|
}
|
|
if len(chains[0].msgs) != 2 {
|
|
t.Fatalf("expected %d message in the chain but got %d", 2, len(chains[0].msgs))
|
|
}
|
|
for i, m := range chains[0].msgs {
|
|
if m.Message.Nonce != uint64(i) {
|
|
t.Fatalf("expected nonce %d but got %d", i, m.Message.Nonce)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func TestMessageChainSkipping(t *testing.T) {
|
|
// regression test for chain skip bug
|
|
|
|
mp, tma := makeTestMpool()
|
|
|
|
// the actors
|
|
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
block := tma.nextBlock()
|
|
ts := mock.TipSet(block)
|
|
|
|
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin2.StorageMarketActorCodeID, M: 2}]
|
|
baseFee := types.NewInt(0)
|
|
|
|
tma.setBalance(a1, 1) // in FIL
|
|
tma.setStateNonce(a1, 10)
|
|
|
|
mset := make(map[uint64]*types.SignedMessage)
|
|
for i := 0; i < 20; i++ {
|
|
bias := (20 - i) / 3
|
|
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(1+i%3+bias))
|
|
mset[uint64(i)] = m
|
|
}
|
|
|
|
chains := mp.createMessageChains(a1, mset, baseFee, ts)
|
|
if len(chains) != 4 {
|
|
t.Fatalf("expected 4 chains, got %d", len(chains))
|
|
}
|
|
for i, chain := range chains {
|
|
var expectedLen int
|
|
switch {
|
|
case i == 0:
|
|
expectedLen = 2
|
|
case i > 2:
|
|
expectedLen = 2
|
|
default:
|
|
expectedLen = 3
|
|
}
|
|
if len(chain.msgs) != expectedLen {
|
|
t.Fatalf("expected %d message in chain %d but got %d", expectedLen, i, len(chain.msgs))
|
|
}
|
|
}
|
|
nextNonce := 10
|
|
for _, chain := range chains {
|
|
for _, m := range chain.msgs {
|
|
if m.Message.Nonce != uint64(nextNonce) {
|
|
t.Fatalf("expected nonce %d but got %d", nextNonce, m.Message.Nonce)
|
|
}
|
|
nextNonce++
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func TestBasicMessageSelection(t *testing.T) {
|
|
oldMaxNonceGap := MaxNonceGap
|
|
MaxNonceGap = 1000
|
|
defer func() {
|
|
MaxNonceGap = oldMaxNonceGap
|
|
}()
|
|
|
|
mp, tma := makeTestMpool()
|
|
|
|
// the actors
|
|
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
block := tma.nextBlock()
|
|
ts := mock.TipSet(block)
|
|
tma.applyBlock(t, block)
|
|
|
|
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin2.StorageMarketActorCodeID, M: 2}]
|
|
|
|
tma.setBalance(a1, 1) // in FIL
|
|
tma.setBalance(a2, 1) // in FIL
|
|
|
|
// we create 10 messages from each actor to another, with the first actor paying higher
|
|
// gas prices than the second; we expect message selection to order his messages first
|
|
for i := 0; i < 10; i++ {
|
|
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(2*i+1))
|
|
mustAdd(t, mp, m)
|
|
}
|
|
|
|
for i := 0; i < 10; i++ {
|
|
m := makeTestMessage(w2, a2, a1, uint64(i), gasLimit, uint64(i+1))
|
|
mustAdd(t, mp, m)
|
|
}
|
|
|
|
msgs, err := mp.SelectMessages(context.Background(), ts, 1.0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(msgs) != 20 {
|
|
t.Fatalf("exptected 20 messages, got %d", len(msgs))
|
|
}
|
|
|
|
nextNonce := 0
|
|
for i := 0; i < 10; i++ {
|
|
if msgs[i].Message.From != a1 {
|
|
t.Fatalf("expected message from actor a1")
|
|
}
|
|
if msgs[i].Message.Nonce != uint64(nextNonce) {
|
|
t.Fatalf("expected nonce %d, got %d", msgs[i].Message.Nonce, nextNonce)
|
|
}
|
|
nextNonce++
|
|
}
|
|
|
|
nextNonce = 0
|
|
for i := 10; i < 20; i++ {
|
|
if msgs[i].Message.From != a2 {
|
|
t.Fatalf("expected message from actor a2")
|
|
}
|
|
if msgs[i].Message.Nonce != uint64(nextNonce) {
|
|
t.Fatalf("expected nonce %d, got %d", msgs[i].Message.Nonce, nextNonce)
|
|
}
|
|
nextNonce++
|
|
}
|
|
|
|
// now we make a block with all the messages and advance the chain
|
|
block2 := tma.nextBlock()
|
|
tma.setBlockMessages(block2, msgs...)
|
|
tma.applyBlock(t, block2)
|
|
|
|
// we should have no pending messages in the mpool
|
|
pend, _ := mp.Pending(context.TODO())
|
|
if len(pend) != 0 {
|
|
t.Fatalf("expected no pending messages, but got %d", len(pend))
|
|
}
|
|
|
|
// create a block and advance the chain without applying to the mpool
|
|
msgs = nil
|
|
for i := 10; i < 20; i++ {
|
|
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(2*i+1))
|
|
msgs = append(msgs, m)
|
|
m = makeTestMessage(w2, a2, a1, uint64(i), gasLimit, uint64(i+1))
|
|
msgs = append(msgs, m)
|
|
}
|
|
block3 := tma.nextBlock()
|
|
tma.setBlockMessages(block3, msgs...)
|
|
ts3 := mock.TipSet(block3)
|
|
|
|
// now create another set of messages and add them to the mpool
|
|
for i := 20; i < 30; i++ {
|
|
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(2*i+200))
|
|
mustAdd(t, mp, m)
|
|
m = makeTestMessage(w2, a2, a1, uint64(i), gasLimit, uint64(i+1))
|
|
mustAdd(t, mp, m)
|
|
}
|
|
|
|
// select messages in the last tipset; this should include the missed messages as well as
|
|
// the last messages we added, with the first actor's messages first
|
|
// first we need to update the nonce on the tma
|
|
tma.setStateNonce(a1, 10)
|
|
tma.setStateNonce(a2, 10)
|
|
|
|
msgs, err = mp.SelectMessages(context.Background(), ts3, 1.0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(msgs) != 20 {
|
|
t.Fatalf("expected 20 messages, got %d", len(msgs))
|
|
}
|
|
|
|
nextNonce = 20
|
|
for i := 0; i < 10; i++ {
|
|
if msgs[i].Message.From != a1 {
|
|
t.Fatalf("expected message from actor a1")
|
|
}
|
|
if msgs[i].Message.Nonce != uint64(nextNonce) {
|
|
t.Fatalf("expected nonce %d, got %d", msgs[i].Message.Nonce, nextNonce)
|
|
}
|
|
nextNonce++
|
|
}
|
|
|
|
nextNonce = 20
|
|
for i := 10; i < 20; i++ {
|
|
if msgs[i].Message.From != a2 {
|
|
t.Fatalf("expected message from actor a2")
|
|
}
|
|
if msgs[i].Message.Nonce != uint64(nextNonce) {
|
|
t.Fatalf("expected nonce %d, got %d", msgs[i].Message.Nonce, nextNonce)
|
|
}
|
|
nextNonce++
|
|
}
|
|
}
|
|
|
|
func TestMessageSelectionTrimming(t *testing.T) {
|
|
mp, tma := makeTestMpool()
|
|
|
|
// the actors
|
|
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
block := tma.nextBlock()
|
|
ts := mock.TipSet(block)
|
|
tma.applyBlock(t, block)
|
|
|
|
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin2.StorageMarketActorCodeID, M: 2}]
|
|
|
|
tma.setBalance(a1, 1) // in FIL
|
|
tma.setBalance(a2, 1) // in FIL
|
|
|
|
// make many small chains for the two actors
|
|
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)
|
|
m = makeTestMessage(w2, a2, a1, uint64(i), gasLimit, uint64(1+i%3+bias))
|
|
mustAdd(t, mp, m)
|
|
}
|
|
|
|
msgs, err := mp.SelectMessages(context.Background(), ts, 1.0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := int(build.BlockGasLimit / gasLimit)
|
|
if len(msgs) != expected {
|
|
t.Fatalf("expected %d messages, bug got %d", expected, len(msgs))
|
|
}
|
|
|
|
mGasLimit := int64(0)
|
|
for _, m := range msgs {
|
|
mGasLimit += m.Message.GasLimit
|
|
}
|
|
if mGasLimit > build.BlockGasLimit {
|
|
t.Fatal("selected messages gas limit exceeds block gas limit!")
|
|
}
|
|
|
|
}
|
|
|
|
func TestPriorityMessageSelection(t *testing.T) {
|
|
mp, tma := makeTestMpool()
|
|
|
|
// the actors
|
|
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
block := tma.nextBlock()
|
|
ts := mock.TipSet(block)
|
|
tma.applyBlock(t, block)
|
|
|
|
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin2.StorageMarketActorCodeID, M: 2}]
|
|
|
|
tma.setBalance(a1, 1) // in FIL
|
|
tma.setBalance(a2, 1) // in FIL
|
|
|
|
mp.cfg.PriorityAddrs = []address.Address{a1}
|
|
|
|
nMessages := 10
|
|
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)
|
|
m = makeTestMessage(w2, a2, a1, uint64(i), gasLimit, uint64(1+i%3+bias))
|
|
mustAdd(t, mp, m)
|
|
}
|
|
|
|
msgs, err := mp.SelectMessages(context.Background(), ts, 1.0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(msgs) != 20 {
|
|
t.Fatalf("expected 20 messages but got %d", len(msgs))
|
|
}
|
|
|
|
// messages from a1 must be first
|
|
nextNonce := uint64(0)
|
|
for i := 0; i < 10; i++ {
|
|
m := msgs[i]
|
|
if m.Message.From != a1 {
|
|
t.Fatal("expected messages from a1 before messages from a2")
|
|
}
|
|
if m.Message.Nonce != nextNonce {
|
|
t.Fatalf("expected nonce %d but got %d", nextNonce, m.Message.Nonce)
|
|
}
|
|
nextNonce++
|
|
}
|
|
|
|
nextNonce = 0
|
|
for i := 10; i < 20; i++ {
|
|
m := msgs[i]
|
|
if m.Message.From != a2 {
|
|
t.Fatal("expected messages from a2 after messages from a1")
|
|
}
|
|
if m.Message.Nonce != nextNonce {
|
|
t.Fatalf("expected nonce %d but got %d", nextNonce, m.Message.Nonce)
|
|
}
|
|
nextNonce++
|
|
}
|
|
}
|
|
|
|
func TestPriorityMessageSelection2(t *testing.T) {
|
|
mp, tma := makeTestMpool()
|
|
|
|
// the actors
|
|
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
block := tma.nextBlock()
|
|
ts := mock.TipSet(block)
|
|
tma.applyBlock(t, block)
|
|
|
|
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin2.StorageMarketActorCodeID, M: 2}]
|
|
|
|
tma.setBalance(a1, 1) // in FIL
|
|
tma.setBalance(a2, 1) // in FIL
|
|
|
|
mp.cfg.PriorityAddrs = []address.Address{a1}
|
|
|
|
nMessages := int(2 * build.BlockGasLimit / gasLimit)
|
|
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)
|
|
m = makeTestMessage(w2, a2, a1, uint64(i), gasLimit, uint64(1+i%3+bias))
|
|
mustAdd(t, mp, m)
|
|
}
|
|
|
|
msgs, err := mp.SelectMessages(context.Background(), ts, 1.0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expectedMsgs := int(build.BlockGasLimit / gasLimit)
|
|
if len(msgs) != expectedMsgs {
|
|
t.Fatalf("expected %d messages but got %d", expectedMsgs, len(msgs))
|
|
}
|
|
|
|
// all messages must be from a1
|
|
nextNonce := uint64(0)
|
|
for _, m := range msgs {
|
|
if m.Message.From != a1 {
|
|
t.Fatal("expected messages from a1 before messages from a2")
|
|
}
|
|
if m.Message.Nonce != nextNonce {
|
|
t.Fatalf("expected nonce %d but got %d", nextNonce, m.Message.Nonce)
|
|
}
|
|
nextNonce++
|
|
}
|
|
}
|
|
|
|
func TestPriorityMessageSelection3(t *testing.T) {
|
|
mp, tma := makeTestMpool()
|
|
|
|
// the actors
|
|
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
block := tma.nextBlock()
|
|
ts := mock.TipSet(block)
|
|
tma.applyBlock(t, block)
|
|
|
|
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin2.StorageMarketActorCodeID, M: 2}]
|
|
|
|
tma.setBalance(a1, 1) // in FIL
|
|
tma.setBalance(a2, 1) // in FIL
|
|
|
|
mp.cfg.PriorityAddrs = []address.Address{a1}
|
|
|
|
tma.baseFee = types.NewInt(1000)
|
|
nMessages := 10
|
|
for i := 0; i < nMessages; i++ {
|
|
bias := (nMessages - i) / 3
|
|
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(1000+i%3+bias))
|
|
mustAdd(t, mp, m)
|
|
// messages from a2 have negative performance
|
|
m = makeTestMessage(w2, a2, a1, uint64(i), gasLimit, 100)
|
|
mustAdd(t, mp, m)
|
|
}
|
|
|
|
// test greedy selection
|
|
msgs, err := mp.SelectMessages(context.Background(), ts, 1.0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expectedMsgs := 10
|
|
if len(msgs) != expectedMsgs {
|
|
t.Fatalf("expected %d messages but got %d", expectedMsgs, len(msgs))
|
|
}
|
|
|
|
// all messages must be from a1
|
|
nextNonce := uint64(0)
|
|
for _, m := range msgs {
|
|
if m.Message.From != a1 {
|
|
t.Fatal("expected messages from a1 before messages from a2")
|
|
}
|
|
if m.Message.Nonce != nextNonce {
|
|
t.Fatalf("expected nonce %d but got %d", nextNonce, m.Message.Nonce)
|
|
}
|
|
nextNonce++
|
|
}
|
|
|
|
// test optimal selection
|
|
msgs, err = mp.SelectMessages(context.Background(), ts, 0.1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expectedMsgs = 10
|
|
if len(msgs) != expectedMsgs {
|
|
t.Fatalf("expected %d messages but got %d", expectedMsgs, len(msgs))
|
|
}
|
|
|
|
// all messages must be from a1
|
|
nextNonce = uint64(0)
|
|
for _, m := range msgs {
|
|
if m.Message.From != a1 {
|
|
t.Fatal("expected messages from a1 before messages from a2")
|
|
}
|
|
if m.Message.Nonce != nextNonce {
|
|
t.Fatalf("expected nonce %d but got %d", nextNonce, m.Message.Nonce)
|
|
}
|
|
nextNonce++
|
|
}
|
|
|
|
}
|
|
|
|
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 the start
|
|
mp, tma := makeTestMpool()
|
|
|
|
// the actors
|
|
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
block := tma.nextBlock()
|
|
ts := mock.TipSet(block)
|
|
tma.applyBlock(t, block)
|
|
|
|
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin2.StorageMarketActorCodeID, M: 2}]
|
|
|
|
tma.setBalance(a1, 1) // in FIL
|
|
tma.setBalance(a2, 1) // in FIL
|
|
|
|
nMessages := int(10 * build.BlockGasLimit / gasLimit)
|
|
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)
|
|
}
|
|
|
|
msgs, err := mp.SelectMessages(context.Background(), ts, 0.25)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expectedMsgs := int(build.BlockGasLimit / gasLimit)
|
|
if len(msgs) != expectedMsgs {
|
|
t.Fatalf("expected %d messages, but got %d", expectedMsgs, len(msgs))
|
|
}
|
|
|
|
nextNonce := uint64(0)
|
|
for _, m := range msgs {
|
|
if m.Message.From != a1 {
|
|
t.Fatal("expected message from a1")
|
|
}
|
|
|
|
if m.Message.Nonce != nextNonce {
|
|
t.Fatalf("expected nonce %d but got %d", nextNonce, m.Message.Nonce)
|
|
}
|
|
nextNonce++
|
|
}
|
|
}
|
|
|
|
func TestOptimalMessageSelection2(t *testing.T) {
|
|
// 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
|
|
// messages from the second actor from the start
|
|
mp, tma := makeTestMpool()
|
|
|
|
// the actors
|
|
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a1, err := w1.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a2, err := w2.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
block := tma.nextBlock()
|
|
ts := mock.TipSet(block)
|
|
tma.applyBlock(t, block)
|
|
|
|
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin2.StorageMarketActorCodeID, M: 2}]
|
|
|
|
tma.setBalance(a1, 1) // in FIL
|
|
tma.setBalance(a2, 1) // in FIL
|
|
|
|
nMessages := int(5 * build.BlockGasLimit / gasLimit)
|
|
for i := 0; i < nMessages; i++ {
|
|
bias := (nMessages - i) / 3
|
|
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(200000+i%3+bias))
|
|
mustAdd(t, mp, m)
|
|
m = makeTestMessage(w2, a2, a1, uint64(i), gasLimit, uint64(190000+i%3+bias))
|
|
mustAdd(t, mp, m)
|
|
}
|
|
|
|
msgs, err := mp.SelectMessages(context.Background(), ts, 0.1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expectedMsgs := int(build.BlockGasLimit / gasLimit)
|
|
if len(msgs) != expectedMsgs {
|
|
t.Fatalf("expected %d messages, but got %d", expectedMsgs, len(msgs))
|
|
}
|
|
|
|
var nFrom1, nFrom2 int
|
|
var nextNonce1, nextNonce2 uint64
|
|
for _, m := range msgs {
|
|
if m.Message.From == a1 {
|
|
if m.Message.Nonce != nextNonce1 {
|
|
t.Fatalf("expected nonce %d but got %d", nextNonce1, m.Message.Nonce)
|
|
}
|
|
nextNonce1++
|
|
nFrom1++
|
|
} else {
|
|
if m.Message.Nonce != nextNonce2 {
|
|
t.Fatalf("expected nonce %d but got %d", nextNonce2, m.Message.Nonce)
|
|
}
|
|
nextNonce2++
|
|
nFrom2++
|
|
}
|
|
}
|
|
|
|
if nFrom1 > nFrom2 {
|
|
t.Fatalf("expected more messages from a2 than a1; nFrom1=%d nFrom2=%d", nFrom1, nFrom2)
|
|
}
|
|
}
|
|
|
|
func TestOptimalMessageSelection3(t *testing.T) {
|
|
// 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 depenent merging algorithm should pick
|
|
// messages from the median actor from the start
|
|
mp, tma := makeTestMpool()
|
|
|
|
nActors := 10
|
|
// the actors
|
|
var actors []address.Address
|
|
var wallets []*wallet.LocalWallet
|
|
|
|
for i := 0; i < nActors; i++ {
|
|
w, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a, err := w.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
actors = append(actors, a)
|
|
wallets = append(wallets, w)
|
|
}
|
|
|
|
block := tma.nextBlock()
|
|
ts := mock.TipSet(block)
|
|
tma.applyBlock(t, block)
|
|
|
|
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin2.StorageMarketActorCodeID, M: 2}]
|
|
|
|
for _, a := range actors {
|
|
tma.setBalance(a, 1) // in FIL
|
|
}
|
|
|
|
nMessages := int(build.BlockGasLimit/gasLimit) + 1
|
|
for i := 0; i < nMessages; i++ {
|
|
for j := 0; j < nActors; j++ {
|
|
premium := 500000 + 10000*(nActors-j) + (nMessages+2-i)/(30*nActors) + i%3
|
|
m := makeTestMessage(wallets[j], actors[j], actors[j%nActors], uint64(i), gasLimit, uint64(premium))
|
|
mustAdd(t, mp, m)
|
|
}
|
|
}
|
|
|
|
msgs, err := mp.SelectMessages(context.Background(), ts, 0.1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expectedMsgs := int(build.BlockGasLimit / gasLimit)
|
|
if len(msgs) != expectedMsgs {
|
|
t.Fatalf("expected %d messages, but got %d", expectedMsgs, len(msgs))
|
|
}
|
|
|
|
whoIs := func(a address.Address) int {
|
|
for i, aa := range actors {
|
|
if a == aa {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
nonces := make([]uint64, nActors)
|
|
for _, m := range msgs {
|
|
who := whoIs(m.Message.From)
|
|
if who < 3 {
|
|
t.Fatalf("got message from %dth actor", who)
|
|
}
|
|
|
|
nextNonce := nonces[who]
|
|
if m.Message.Nonce != nextNonce {
|
|
t.Fatalf("expected nonce %d but got %d", nextNonce, m.Message.Nonce)
|
|
}
|
|
nonces[who]++
|
|
}
|
|
}
|
|
|
|
func testCompetitiveMessageSelection(t *testing.T, rng *rand.Rand, getPremium func() uint64) (float64, float64, float64) {
|
|
// in this test we use 300 actors and send 10 blocks of messages.
|
|
// actors send with an randomly distributed premium dictated by the getPremium function.
|
|
// a number of miners select with varying ticket quality and we compare the
|
|
// capacity and rewards of greedy selection -vs- optimal selection
|
|
mp, tma := makeTestMpool()
|
|
|
|
nActors := 300
|
|
// the actors
|
|
var actors []address.Address
|
|
var wallets []*wallet.LocalWallet
|
|
|
|
for i := 0; i < nActors; i++ {
|
|
w, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a, err := w.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
actors = append(actors, a)
|
|
wallets = append(wallets, w)
|
|
}
|
|
|
|
block := tma.nextBlock()
|
|
ts := mock.TipSet(block)
|
|
tma.applyBlock(t, block)
|
|
|
|
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin2.StorageMarketActorCodeID, M: 2}]
|
|
baseFee := types.NewInt(0)
|
|
|
|
for _, a := range actors {
|
|
tma.setBalance(a, 1) // in FIL
|
|
}
|
|
|
|
nMessages := 10 * int(build.BlockGasLimit/gasLimit)
|
|
t.Log("nMessages", nMessages)
|
|
nonces := make([]uint64, nActors)
|
|
for i := 0; i < nMessages; i++ {
|
|
from := rng.Intn(nActors)
|
|
to := rng.Intn(nActors)
|
|
nonce := nonces[from]
|
|
nonces[from]++
|
|
premium := getPremium()
|
|
m := makeTestMessage(wallets[from], actors[from], actors[to], nonce, gasLimit, premium)
|
|
mustAdd(t, mp, m)
|
|
}
|
|
|
|
logging.SetLogLevel("messagepool", "error")
|
|
|
|
// 1. greedy selection
|
|
greedyMsgs, err := mp.selectMessagesGreedy(context.Background(), ts, ts)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
totalGreedyCapacity := 0.0
|
|
totalGreedyReward := 0.0
|
|
totalOptimalCapacity := 0.0
|
|
totalOptimalReward := 0.0
|
|
totalBestTQReward := 0.0
|
|
const runs = 1
|
|
for i := 0; i < runs; i++ {
|
|
// 2. optimal selection
|
|
minersRand := rng.Float64()
|
|
winerProba := noWinnersProb()
|
|
i := 0
|
|
for ; i < MaxBlocks && minersRand > 0; i++ {
|
|
minersRand -= winerProba[i]
|
|
}
|
|
nMiners := i - 1
|
|
if nMiners < 1 {
|
|
nMiners = 1
|
|
}
|
|
|
|
optMsgs := make(map[cid.Cid]*types.SignedMessage)
|
|
bestTq := 0.0
|
|
var bestMsgs []*types.SignedMessage
|
|
for j := 0; j < nMiners; j++ {
|
|
tq := rng.Float64()
|
|
msgs, err := mp.SelectMessages(context.Background(), ts, tq)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if tq > bestTq {
|
|
bestMsgs = msgs
|
|
}
|
|
|
|
for _, m := range msgs {
|
|
optMsgs[m.Cid()] = m
|
|
}
|
|
}
|
|
|
|
totalGreedyCapacity += float64(len(greedyMsgs))
|
|
totalOptimalCapacity += float64(len(optMsgs))
|
|
boost := float64(len(optMsgs)) / float64(len(greedyMsgs))
|
|
|
|
t.Logf("nMiners: %d", nMiners)
|
|
t.Logf("greedy capacity %d, optimal capacity %d (x %.1f )", len(greedyMsgs),
|
|
len(optMsgs), boost)
|
|
if len(greedyMsgs) > len(optMsgs) {
|
|
t.Errorf("greedy capacity higher than optimal capacity; wtf")
|
|
}
|
|
|
|
greedyReward := big.NewInt(0)
|
|
for _, m := range greedyMsgs {
|
|
greedyReward.Add(greedyReward, mp.getGasReward(m, baseFee))
|
|
}
|
|
|
|
optReward := big.NewInt(0)
|
|
for _, m := range optMsgs {
|
|
optReward.Add(optReward, mp.getGasReward(m, baseFee))
|
|
}
|
|
|
|
bestTqReward := big.NewInt(0)
|
|
for _, m := range bestMsgs {
|
|
bestTqReward.Add(bestTqReward, mp.getGasReward(m, baseFee))
|
|
}
|
|
|
|
totalBestTQReward += float64(bestTqReward.Uint64())
|
|
|
|
nMinersBig := big.NewInt(int64(nMiners))
|
|
greedyAvgReward, _ := new(big.Rat).SetFrac(greedyReward, nMinersBig).Float64()
|
|
totalGreedyReward += greedyAvgReward
|
|
optimalAvgReward, _ := new(big.Rat).SetFrac(optReward, nMinersBig).Float64()
|
|
totalOptimalReward += optimalAvgReward
|
|
|
|
boost = optimalAvgReward / greedyAvgReward
|
|
t.Logf("greedy reward: %.0f, optimal reward: %.0f (x %.1f )", greedyAvgReward,
|
|
optimalAvgReward, boost)
|
|
|
|
}
|
|
|
|
capacityBoost := totalOptimalCapacity / totalGreedyCapacity
|
|
rewardBoost := totalOptimalReward / totalGreedyReward
|
|
t.Logf("Average capacity boost: %f", capacityBoost)
|
|
t.Logf("Average reward boost: %f", rewardBoost)
|
|
t.Logf("Average best tq reward: %f", totalBestTQReward/runs/1e12)
|
|
|
|
logging.SetLogLevel("messagepool", "info")
|
|
|
|
return capacityBoost, rewardBoost, totalBestTQReward / runs / 1e12
|
|
}
|
|
|
|
func makeExpPremiumDistribution(rng *rand.Rand) func() uint64 {
|
|
return func() uint64 {
|
|
premium := 20000*math.Exp(-3.*rng.Float64()) + 5000
|
|
return uint64(premium)
|
|
}
|
|
}
|
|
|
|
func makeZipfPremiumDistribution(rng *rand.Rand) func() uint64 {
|
|
zipf := rand.NewZipf(rng, 1.001, 1, 40000)
|
|
return func() uint64 {
|
|
return zipf.Uint64() + 10000
|
|
}
|
|
}
|
|
|
|
func TestCompetitiveMessageSelectionExp(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping in short mode")
|
|
}
|
|
var capacityBoost, rewardBoost, tqReward float64
|
|
seeds := []int64{1947, 1976, 2020, 2100, 10000, 143324, 432432, 131, 32, 45}
|
|
for _, seed := range seeds {
|
|
t.Log("running competitive message selection with Exponential premium distribution and seed", seed)
|
|
rng := rand.New(rand.NewSource(seed))
|
|
cb, rb, tqR := testCompetitiveMessageSelection(t, rng, makeExpPremiumDistribution(rng))
|
|
capacityBoost += cb
|
|
rewardBoost += rb
|
|
tqReward += tqR
|
|
}
|
|
|
|
capacityBoost /= float64(len(seeds))
|
|
rewardBoost /= float64(len(seeds))
|
|
tqReward /= float64(len(seeds))
|
|
t.Logf("Average capacity boost across all seeds: %f", capacityBoost)
|
|
t.Logf("Average reward boost across all seeds: %f", rewardBoost)
|
|
t.Logf("Average reward of best ticket across all seeds: %f", tqReward)
|
|
}
|
|
|
|
func TestCompetitiveMessageSelectionZipf(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping in short mode")
|
|
}
|
|
var capacityBoost, rewardBoost, tqReward float64
|
|
seeds := []int64{1947, 1976, 2020, 2100, 10000, 143324, 432432, 131, 32, 45}
|
|
for _, seed := range seeds {
|
|
t.Log("running competitive message selection with Zipf premium distribution and seed", seed)
|
|
rng := rand.New(rand.NewSource(seed))
|
|
cb, rb, tqR := testCompetitiveMessageSelection(t, rng, makeZipfPremiumDistribution(rng))
|
|
capacityBoost += cb
|
|
rewardBoost += rb
|
|
tqReward += tqR
|
|
}
|
|
|
|
tqReward /= float64(len(seeds))
|
|
capacityBoost /= float64(len(seeds))
|
|
rewardBoost /= float64(len(seeds))
|
|
t.Logf("Average capacity boost across all seeds: %f", capacityBoost)
|
|
t.Logf("Average reward boost across all seeds: %f", rewardBoost)
|
|
t.Logf("Average reward of best ticket across all seeds: %f", tqReward)
|
|
}
|
|
|
|
func TestGasReward(t *testing.T) {
|
|
tests := []struct {
|
|
Premium uint64
|
|
FeeCap uint64
|
|
BaseFee uint64
|
|
GasReward int64
|
|
}{
|
|
{Premium: 100, FeeCap: 200, BaseFee: 100, GasReward: 100},
|
|
{Premium: 100, FeeCap: 200, BaseFee: 210, GasReward: -10 * 3},
|
|
{Premium: 200, FeeCap: 250, BaseFee: 210, GasReward: 40},
|
|
{Premium: 200, FeeCap: 250, BaseFee: 2000, GasReward: -1750 * 3},
|
|
}
|
|
|
|
mp := new(MessagePool)
|
|
for _, test := range tests {
|
|
test := test
|
|
t.Run(fmt.Sprintf("%v", test), func(t *testing.T) {
|
|
msg := &types.SignedMessage{
|
|
Message: types.Message{
|
|
GasLimit: 10,
|
|
GasFeeCap: types.NewInt(test.FeeCap),
|
|
GasPremium: types.NewInt(test.Premium),
|
|
},
|
|
}
|
|
rew := mp.getGasReward(msg, types.NewInt(test.BaseFee))
|
|
if rew.Cmp(big.NewInt(test.GasReward*10)) != 0 {
|
|
t.Errorf("bad reward: expected %d, got %s", test.GasReward*10, rew)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRealWorldSelection(t *testing.T) {
|
|
// 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
|
|
file, err := os.Open("test-messages.json.gz")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
gzr, err := gzip.NewReader(file)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
dec := json.NewDecoder(gzr)
|
|
|
|
var msgs []*types.SignedMessage
|
|
baseNonces := make(map[address.Address]uint64)
|
|
|
|
readLoop:
|
|
for {
|
|
m := new(types.SignedMessage)
|
|
err := dec.Decode(m)
|
|
switch err {
|
|
case nil:
|
|
msgs = append(msgs, m)
|
|
nonce, ok := baseNonces[m.Message.From]
|
|
if !ok || m.Message.Nonce < nonce {
|
|
baseNonces[m.Message.From] = m.Message.Nonce
|
|
}
|
|
|
|
case io.EOF:
|
|
break readLoop
|
|
|
|
default:
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
actorMap := make(map[address.Address]address.Address)
|
|
actorWallets := make(map[address.Address]api.Wallet)
|
|
|
|
for _, m := range msgs {
|
|
baseNonce := baseNonces[m.Message.From]
|
|
|
|
localActor, ok := actorMap[m.Message.From]
|
|
if !ok {
|
|
w, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a, err := w.WalletNew(context.Background(), types.KTSecp256k1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
actorMap[m.Message.From] = a
|
|
actorWallets[a] = w
|
|
localActor = a
|
|
}
|
|
|
|
w, ok := actorWallets[localActor]
|
|
if !ok {
|
|
t.Fatalf("failed to lookup wallet for actor %s", localActor)
|
|
}
|
|
|
|
m.Message.From = localActor
|
|
m.Message.Nonce -= baseNonce
|
|
|
|
sig, err := w.WalletSign(context.TODO(), localActor, m.Message.Cid().Bytes(), api.MsgMeta{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
m.Signature = *sig
|
|
}
|
|
|
|
mp, tma := makeTestMpool()
|
|
|
|
block := tma.nextBlockWithHeight(build.UpgradeBreezeHeight + 10)
|
|
ts := mock.TipSet(block)
|
|
tma.applyBlock(t, block)
|
|
|
|
for _, a := range actorMap {
|
|
tma.setBalance(a, 1000000)
|
|
}
|
|
|
|
tma.baseFee = types.NewInt(800_000_000)
|
|
|
|
sort.Slice(msgs, func(i, j int) bool {
|
|
return msgs[i].Message.Nonce < msgs[j].Message.Nonce
|
|
})
|
|
|
|
// add the messages
|
|
for _, m := range msgs {
|
|
mustAdd(t, mp, m)
|
|
}
|
|
|
|
// do message selection and check block packing
|
|
minGasLimit := int64(0.9 * float64(build.BlockGasLimit))
|
|
|
|
// greedy first
|
|
selected, err := mp.SelectMessages(context.Background(), ts, 1.0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
gasLimit := int64(0)
|
|
for _, m := range selected {
|
|
gasLimit += m.Message.GasLimit
|
|
}
|
|
if gasLimit < minGasLimit {
|
|
t.Fatalf("failed to pack with tq=1.0; packed %d, minimum packing: %d", gasLimit, minGasLimit)
|
|
}
|
|
|
|
// high quality ticket
|
|
selected, err = mp.SelectMessages(context.Background(), ts, .8)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
gasLimit = int64(0)
|
|
for _, m := range selected {
|
|
gasLimit += m.Message.GasLimit
|
|
}
|
|
if gasLimit < minGasLimit {
|
|
t.Fatalf("failed to pack with tq=0.8; packed %d, minimum packing: %d", gasLimit, minGasLimit)
|
|
}
|
|
|
|
// mid quality ticket
|
|
selected, err = mp.SelectMessages(context.Background(), ts, .4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
gasLimit = int64(0)
|
|
for _, m := range selected {
|
|
gasLimit += m.Message.GasLimit
|
|
}
|
|
if gasLimit < minGasLimit {
|
|
t.Fatalf("failed to pack with tq=0.4; packed %d, minimum packing: %d", gasLimit, minGasLimit)
|
|
}
|
|
|
|
// low quality ticket
|
|
selected, err = mp.SelectMessages(context.Background(), ts, .1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
gasLimit = int64(0)
|
|
for _, m := range selected {
|
|
gasLimit += m.Message.GasLimit
|
|
}
|
|
if gasLimit < minGasLimit {
|
|
t.Fatalf("failed to pack with tq=0.1; packed %d, minimum packing: %d", gasLimit, minGasLimit)
|
|
}
|
|
|
|
// very low quality ticket
|
|
selected, err = mp.SelectMessages(context.Background(), ts, .01)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
gasLimit = int64(0)
|
|
for _, m := range selected {
|
|
gasLimit += m.Message.GasLimit
|
|
}
|
|
if gasLimit < minGasLimit {
|
|
t.Fatalf("failed to pack with tq=0.01; packed %d, minimum packing: %d", gasLimit, minGasLimit)
|
|
}
|
|
|
|
}
|