430 lines
12 KiB
Go
430 lines
12 KiB
Go
package messagepool
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/filecoin-project/go-address"
|
|
"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/specs-actors/actors/builtin"
|
|
"github.com/filecoin-project/specs-actors/actors/crypto"
|
|
"github.com/ipfs/go-datastore"
|
|
|
|
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
|
|
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
|
|
)
|
|
|
|
func makeTestMessage(w *wallet.Wallet, 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,
|
|
GasPrice: types.NewInt(gasPrice),
|
|
}
|
|
sig, err := w.Sign(context.TODO(), from, msg.Cid().Bytes())
|
|
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")
|
|
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.GenerateKey(crypto.SigTypeBLS)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a2, err := w2.GenerateKey(crypto.SigTypeBLS)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
block := mock.MkBlock(nil, 1, 1)
|
|
ts := mock.TipSet(block)
|
|
|
|
gasLimit := gasguess.Costs[gasguess.CostKey{builtin.StorageMarketActorCodeID, 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
|
|
}
|
|
|
|
chains := mp.createMessageChains(a1, mset, 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, 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, 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, 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, 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, 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, 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(3*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, ts)
|
|
if len(chains) != 1 {
|
|
t.Fatal("expected a single chain")
|
|
}
|
|
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 TestBasicMessageSelection(t *testing.T) {
|
|
mp, tma := makeTestMpool()
|
|
|
|
// the actors
|
|
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a1, err := w1.GenerateKey(crypto.SigTypeBLS)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
a2, err := w2.GenerateKey(crypto.SigTypeBLS)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
block := mock.MkBlock(nil, 1, 1)
|
|
ts := mock.TipSet(block)
|
|
tma.applyBlock(t, block)
|
|
|
|
gasLimit := gasguess.Costs[gasguess.CostKey{builtin.StorageMarketActorCodeID, 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(ts)
|
|
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 := mock.MkBlock(ts, 2, 2)
|
|
tma.setBlockMessages(block2, msgs...)
|
|
tma.applyBlock(t, block2)
|
|
|
|
// we should have no pending messages in the mpool
|
|
pend, ts2 := mp.Pending()
|
|
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 := mock.MkBlock(ts2, 3, 3)
|
|
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+1))
|
|
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(ts3)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(msgs) != 40 {
|
|
t.Fatalf("expected 40 messages, got %d", len(msgs))
|
|
}
|
|
|
|
nextNonce = 10
|
|
for i := 0; i < 20; 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 = 10
|
|
for i := 20; i < 40; 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++
|
|
}
|
|
}
|