mirror of
https://github.com/filecoin-project/lotus.git
synced 2025-08-24 01:08:42 +08:00
304 lines
7.9 KiB
Go
304 lines
7.9 KiB
Go
package index
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
pseudo "math/rand"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ipfs/go-cid"
|
|
mh "github.com/multiformats/go-multihash"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/filecoin-project/go-address"
|
|
"github.com/filecoin-project/go-state-types/abi"
|
|
"github.com/filecoin-project/go-state-types/crypto"
|
|
|
|
"github.com/filecoin-project/lotus/chain/actors/adt"
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
"github.com/filecoin-project/lotus/chain/types/ethtypes"
|
|
)
|
|
|
|
func TestGetCidFromHash(t *testing.T) {
|
|
seed := time.Now().UnixNano()
|
|
t.Logf("seed: %d", seed)
|
|
rng := pseudo.New(pseudo.NewSource(seed))
|
|
ctx := context.Background()
|
|
|
|
s, _, _ := setupWithHeadIndexed(t, 10, rng)
|
|
|
|
ethTxHash := ethtypes.EthHash([32]byte{1})
|
|
msgCid := randomCid(t, rng)
|
|
|
|
// read from empty db -> ErrNotFound
|
|
c, err := s.GetCidFromHash(ctx, ethTxHash)
|
|
require.Error(t, err)
|
|
require.True(t, errors.Is(err, ErrNotFound))
|
|
require.EqualValues(t, cid.Undef, c)
|
|
|
|
// insert and read
|
|
insertEthTxHash(t, s, ethTxHash, msgCid)
|
|
c, err = s.GetCidFromHash(ctx, ethTxHash)
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, msgCid, c)
|
|
|
|
// look up some other hash -> fails
|
|
c, err = s.GetCidFromHash(ctx, ethtypes.EthHash([32]byte{2}))
|
|
require.Error(t, err)
|
|
require.True(t, errors.Is(err, ErrNotFound))
|
|
require.EqualValues(t, cid.Undef, c)
|
|
}
|
|
|
|
func TestGetMsgInfo(t *testing.T) {
|
|
ctx := context.Background()
|
|
seed := time.Now().UnixNano()
|
|
t.Logf("seed: %d", seed)
|
|
rng := pseudo.New(pseudo.NewSource(seed))
|
|
s, _, _ := setupWithHeadIndexed(t, 10, rng)
|
|
|
|
t.Run("message exists", func(t *testing.T) {
|
|
msgCid := randomCid(t, rng)
|
|
msgCidBytes := msgCid.Bytes()
|
|
tsKeyCid := randomCid(t, rng)
|
|
|
|
insertTipsetMessage(t, s, tipsetMessage{
|
|
tipsetKeyCid: tsKeyCid.Bytes(),
|
|
height: uint64(1),
|
|
reverted: false,
|
|
messageCid: msgCidBytes,
|
|
messageIndex: 1,
|
|
})
|
|
|
|
mi, err := s.GetMsgInfo(ctx, msgCid)
|
|
require.NoError(t, err)
|
|
require.Equal(t, msgCid, mi.Message)
|
|
require.Equal(t, tsKeyCid, mi.TipSet)
|
|
require.Equal(t, abi.ChainEpoch(1), mi.Epoch)
|
|
})
|
|
|
|
t.Run("message not found", func(t *testing.T) {
|
|
nonExistentMsgCid := randomCid(t, rng)
|
|
mi, err := s.GetMsgInfo(ctx, nonExistentMsgCid)
|
|
require.Error(t, err)
|
|
require.ErrorIs(t, err, ErrNotFound)
|
|
require.Nil(t, mi)
|
|
})
|
|
}
|
|
|
|
func setupWithHeadIndexed(t *testing.T, headHeight abi.ChainEpoch, rng *pseudo.Rand) (*SqliteIndexer, *types.TipSet, *dummyChainStore) {
|
|
head := fakeTipSet(t, rng, headHeight, []cid.Cid{})
|
|
d := newDummyChainStore()
|
|
d.SetHeaviestTipSet(head)
|
|
|
|
s, err := NewSqliteIndexer(":memory:", d, 0, false, 0)
|
|
require.NoError(t, err)
|
|
insertHead(t, s, head, headHeight)
|
|
|
|
return s, head, d
|
|
}
|
|
|
|
func insertHead(t *testing.T, s *SqliteIndexer, head *types.TipSet, height abi.ChainEpoch) {
|
|
headKeyBytes, err := toTipsetKeyCidBytes(head)
|
|
require.NoError(t, err)
|
|
|
|
insertTipsetMessage(t, s, tipsetMessage{
|
|
tipsetKeyCid: headKeyBytes,
|
|
height: uint64(height),
|
|
reverted: false,
|
|
messageCid: nil,
|
|
messageIndex: -1,
|
|
})
|
|
}
|
|
|
|
func insertEthTxHash(t *testing.T, s *SqliteIndexer, ethTxHash ethtypes.EthHash, messageCid cid.Cid) {
|
|
msgCidBytes := messageCid.Bytes()
|
|
|
|
res, err := s.stmts.insertEthTxHashStmt.Exec(ethTxHash.String(), msgCidBytes)
|
|
require.NoError(t, err)
|
|
rowsAffected, err := res.RowsAffected()
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(1), rowsAffected)
|
|
}
|
|
|
|
type dummyChainStore struct {
|
|
mu sync.RWMutex
|
|
|
|
heightToTipSet map[abi.ChainEpoch]*types.TipSet
|
|
messagesForTipset map[*types.TipSet][]types.ChainMsg
|
|
keyToTipSet map[types.TipSetKey]*types.TipSet
|
|
tipsetCidToTipset map[cid.Cid]*types.TipSet
|
|
|
|
heaviestTipSet *types.TipSet
|
|
messagesForBlock func(ctx context.Context, b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error)
|
|
actorStore func(ctx context.Context) adt.Store
|
|
}
|
|
|
|
func newDummyChainStore() *dummyChainStore {
|
|
return &dummyChainStore{
|
|
heightToTipSet: make(map[abi.ChainEpoch]*types.TipSet),
|
|
messagesForTipset: make(map[*types.TipSet][]types.ChainMsg),
|
|
keyToTipSet: make(map[types.TipSetKey]*types.TipSet),
|
|
tipsetCidToTipset: make(map[cid.Cid]*types.TipSet),
|
|
}
|
|
}
|
|
|
|
func (d *dummyChainStore) MessagesForTipset(ctx context.Context, ts *types.TipSet) ([]types.ChainMsg, error) {
|
|
d.mu.RLock()
|
|
defer d.mu.RUnlock()
|
|
|
|
msgs, ok := d.messagesForTipset[ts]
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
return msgs, nil
|
|
}
|
|
|
|
func (d *dummyChainStore) GetHeaviestTipSet() *types.TipSet {
|
|
d.mu.RLock()
|
|
defer d.mu.RUnlock()
|
|
return d.heaviestTipSet
|
|
}
|
|
|
|
func (d *dummyChainStore) GetTipSetByCid(_ context.Context, tsKeyCid cid.Cid) (*types.TipSet, error) {
|
|
d.mu.RLock()
|
|
defer d.mu.RUnlock()
|
|
if _, ok := d.tipsetCidToTipset[tsKeyCid]; !ok {
|
|
return nil, errors.New("not found")
|
|
}
|
|
return d.tipsetCidToTipset[tsKeyCid], nil
|
|
}
|
|
|
|
func (d *dummyChainStore) SetTipSetByCid(t *testing.T, ts *types.TipSet) {
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
tsKeyCid, err := ts.Key().Cid()
|
|
require.NoError(t, err)
|
|
d.tipsetCidToTipset[tsKeyCid] = ts
|
|
}
|
|
|
|
func (d *dummyChainStore) GetTipSetFromKey(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) {
|
|
d.mu.RLock()
|
|
defer d.mu.RUnlock()
|
|
|
|
return d.keyToTipSet[tsk], nil
|
|
}
|
|
|
|
func (d *dummyChainStore) MessagesForBlock(ctx context.Context, b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) {
|
|
d.mu.RLock()
|
|
defer d.mu.RUnlock()
|
|
if d.messagesForBlock != nil {
|
|
return d.messagesForBlock(ctx, b)
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (d *dummyChainStore) ActorStore(ctx context.Context) adt.Store {
|
|
d.mu.RLock()
|
|
defer d.mu.RUnlock()
|
|
if d.actorStore != nil {
|
|
return d.actorStore(ctx)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *dummyChainStore) GetTipsetByHeight(ctx context.Context, h abi.ChainEpoch, _ *types.TipSet, prev bool) (*types.TipSet, error) {
|
|
d.mu.RLock()
|
|
defer d.mu.RUnlock()
|
|
|
|
ts, ok := d.heightToTipSet[h]
|
|
if !ok {
|
|
return nil, errors.New("tipset not found")
|
|
}
|
|
return ts, nil
|
|
}
|
|
|
|
func (d *dummyChainStore) IsStoringEvents() bool {
|
|
return true
|
|
}
|
|
|
|
// Setter methods to configure the mock
|
|
|
|
func (d *dummyChainStore) SetMessagesForTipset(ts *types.TipSet, msgs []types.ChainMsg) {
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
d.messagesForTipset[ts] = msgs
|
|
}
|
|
|
|
func (d *dummyChainStore) SetHeaviestTipSet(ts *types.TipSet) {
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
d.heaviestTipSet = ts
|
|
}
|
|
|
|
func (d *dummyChainStore) SetTipsetByHeightAndKey(h abi.ChainEpoch, tsk types.TipSetKey, ts *types.TipSet) {
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
d.heightToTipSet[h] = ts
|
|
d.keyToTipSet[tsk] = ts
|
|
}
|
|
|
|
func randomIDAddr(tb testing.TB, rng *pseudo.Rand) address.Address {
|
|
tb.Helper()
|
|
addr, err := address.NewIDAddress(uint64(rng.Int63()))
|
|
require.NoError(tb, err)
|
|
return addr
|
|
}
|
|
|
|
func randomCid(tb testing.TB, rng *pseudo.Rand) cid.Cid {
|
|
tb.Helper()
|
|
cb := cid.V1Builder{Codec: cid.Raw, MhType: mh.IDENTITY}
|
|
c, err := cb.Sum(randomBytes(10, rng))
|
|
require.NoError(tb, err)
|
|
return c
|
|
}
|
|
|
|
func randomBytes(n int, rng *pseudo.Rand) []byte {
|
|
buf := make([]byte, n)
|
|
rng.Read(buf)
|
|
return buf
|
|
}
|
|
|
|
func fakeTipSet(tb testing.TB, rng *pseudo.Rand, h abi.ChainEpoch, parents []cid.Cid) *types.TipSet {
|
|
tb.Helper()
|
|
ts, err := types.NewTipSet([]*types.BlockHeader{
|
|
{
|
|
Height: h,
|
|
Miner: randomIDAddr(tb, rng),
|
|
|
|
Parents: parents,
|
|
|
|
Ticket: &types.Ticket{VRFProof: []byte{byte(h % 2)}},
|
|
|
|
ParentStateRoot: randomCid(tb, rng),
|
|
Messages: randomCid(tb, rng),
|
|
ParentMessageReceipts: randomCid(tb, rng),
|
|
|
|
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
|
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
|
},
|
|
{
|
|
Height: h,
|
|
Miner: randomIDAddr(tb, rng),
|
|
|
|
Parents: parents,
|
|
|
|
Ticket: &types.Ticket{VRFProof: []byte{byte((h + 1) % 2)}},
|
|
|
|
ParentStateRoot: randomCid(tb, rng),
|
|
Messages: randomCid(tb, rng),
|
|
ParentMessageReceipts: randomCid(tb, rng),
|
|
|
|
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
|
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
|
},
|
|
})
|
|
|
|
require.NoError(tb, err)
|
|
|
|
return ts
|
|
}
|