Commit 982d73c3 authored by Ralph Pichler's avatar Ralph Pichler Committed by GitHub

feat: add greylist to sender matcher (#2161)

parent 55518ade
...@@ -4,6 +4,18 @@ ...@@ -4,6 +4,18 @@
package transaction package transaction
import "time"
var ( var (
StoredTransactionKey = storedTransactionKey StoredTransactionKey = storedTransactionKey
) )
func (s *Matcher) SetTimeNow(f func() time.Time) {
s.timeNow = f
}
func (s *Matcher) SetTime(k int64) {
s.SetTimeNow(func() time.Time {
return time.Unix(k, 0)
})
}
...@@ -19,28 +19,30 @@ type Matcher struct { ...@@ -19,28 +19,30 @@ type Matcher struct {
backend Backend backend Backend
storage storage.StateStorer storage storage.StateStorer
signer types.Signer signer types.Signer
timeNow func() time.Time
} }
const ( const (
overlayPrefix = "verified_overlay_" overlayPrefix = "verified_overlay_"
) )
func peerOverlayKey(peer swarm.Address) string { func peerOverlayKey(peer swarm.Address, txHash common.Hash) string {
return fmt.Sprintf("%s%s", overlayPrefix, peer.String()) return fmt.Sprintf("%s%s_%s", overlayPrefix, peer.String(), txHash.String())
} }
var ( var (
ErrTransactionNotFound = errors.New("transaction not found") ErrTransactionNotFound = errors.New("transaction not found")
ErrTransactionPending = errors.New("transaction in pending status") ErrTransactionPending = errors.New("transaction in pending status")
ErrTransactionSenderInvalid = errors.New("invalid transaction sender") ErrTransactionSenderInvalid = errors.New("invalid transaction sender")
ErrGreylisted = errors.New("overlay and transaction greylisted")
ErrBlockHashMismatch = errors.New("block hash mismatch") ErrBlockHashMismatch = errors.New("block hash mismatch")
ErrOverlayMismatch = errors.New("overlay mismatch") ErrOverlayMismatch = errors.New("overlay mismatch")
) )
type overlayVerification struct { type overlayVerification struct {
nextBlockHash []byte NextBlockHash []byte
verified bool Verified bool
timeStamp time.Time TimeStamp time.Time
} }
func NewMatcher(backend Backend, signer types.Signer, storage storage.StateStorer) *Matcher { func NewMatcher(backend Backend, signer types.Signer, storage storage.StateStorer) *Matcher {
...@@ -48,23 +50,34 @@ func NewMatcher(backend Backend, signer types.Signer, storage storage.StateStore ...@@ -48,23 +50,34 @@ func NewMatcher(backend Backend, signer types.Signer, storage storage.StateStore
storage: storage, storage: storage,
backend: backend, backend: backend,
signer: signer, signer: signer,
timeNow: time.Now,
} }
} }
func (m *Matcher) Matches(ctx context.Context, tx []byte, networkID uint64, senderOverlay swarm.Address) ([]byte, error) { func (m *Matcher) Matches(ctx context.Context, tx []byte, networkID uint64, senderOverlay swarm.Address) ([]byte, error) {
var val overlayVerification incomingTx := common.BytesToHash(tx)
err := m.storage.Get(peerOverlayKey(senderOverlay), &val) var val overlayVerification
if err != nil && !errors.Is(err, storage.ErrNotFound) { err := m.storage.Get(peerOverlayKey(senderOverlay, incomingTx), &val)
return nil, err if err != nil {
} else if val.verified { if !errors.Is(err, storage.ErrNotFound) {
return nil, err
}
} else if val.Verified {
// add cache invalidation // add cache invalidation
return val.nextBlockHash, nil return val.NextBlockHash, nil
} else if val.TimeStamp.Add(5 * time.Minute).After(m.timeNow()) {
return nil, ErrGreylisted
} }
incomingTx := common.BytesToHash(tx) err = m.storage.Put(peerOverlayKey(senderOverlay, incomingTx), &overlayVerification{
TimeStamp: m.timeNow(),
Verified: false,
})
if err != nil {
return nil, err
}
nTx, isPending, err := m.backend.TransactionByHash(ctx, incomingTx) nTx, isPending, err := m.backend.TransactionByHash(ctx, incomingTx)
if err != nil { if err != nil {
...@@ -104,10 +117,10 @@ func (m *Matcher) Matches(ctx context.Context, tx []byte, networkID uint64, send ...@@ -104,10 +117,10 @@ func (m *Matcher) Matches(ctx context.Context, tx []byte, networkID uint64, send
return nil, ErrOverlayMismatch return nil, ErrOverlayMismatch
} }
err = m.storage.Put(peerOverlayKey(senderOverlay), &overlayVerification{ err = m.storage.Put(peerOverlayKey(senderOverlay, incomingTx), &overlayVerification{
timeStamp: time.Now(), TimeStamp: m.timeNow(),
verified: true, Verified: true,
nextBlockHash: nextBlockHash, NextBlockHash: nextBlockHash,
}) })
if err != nil { if err != nil {
......
...@@ -137,6 +137,83 @@ func TestMatchesSender(t *testing.T) { ...@@ -137,6 +137,83 @@ func TestMatchesSender(t *testing.T) {
t.Fatalf("expected match") t.Fatalf("expected match")
} }
}) })
t.Run("cached", func(t *testing.T) {
trxBlock := common.HexToHash("0x2")
nextBlockHeader := &types.Header{
ParentHash: trxBlock,
}
calls := 0
trxReceipt := backendmock.WithTransactionReceiptFunc(func(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
calls++
return &types.Receipt{
BlockNumber: big.NewInt(0),
BlockHash: trxBlock,
}, nil
})
headerByNum := backendmock.WithHeaderbyNumberFunc(func(ctx context.Context, number *big.Int) (*types.Header, error) {
calls++
return nextBlockHeader, nil
})
txByHash := backendmock.WithTransactionByHashFunc(func(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
calls++
return signedTx, false, nil
})
signer := &mockSigner{
addr: common.HexToAddress("0xff"),
}
matcher := transaction.NewMatcher(backendmock.New(trxReceipt, headerByNum, txByHash), signer, statestore.NewStateStore())
senderOverlay := crypto.NewOverlayFromEthereumAddress(signer.addr.Bytes(), 0, nextBlockHeader.Hash().Bytes())
_, err := matcher.Matches(context.Background(), trx, 0, senderOverlay)
if err != nil {
t.Fatalf("expected match")
}
_, err = matcher.Matches(context.Background(), trx, 0, senderOverlay)
if err != nil {
t.Fatalf("expected match")
}
if calls != 3 {
t.Fatal("too many calls")
}
})
t.Run("greylisted", func(t *testing.T) {
txByHash := backendmock.WithTransactionByHashFunc(func(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
return nil, false, errors.New("transaction not found by hash")
})
matcher := transaction.NewMatcher(backendmock.New(txByHash), nil, statestore.NewStateStore())
matcher.SetTime(0)
_, err := matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}))
if !errors.Is(err, transaction.ErrTransactionNotFound) {
t.Fatalf("bad error type, want %v, got %v", transaction.ErrTransactionNotFound, err)
}
_, err = matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}))
if !errors.Is(err, transaction.ErrGreylisted) {
t.Fatalf("bad error type, want %v, got %v", transaction.ErrGreylisted, err)
}
// greylist expires
matcher.SetTime(5 * 60)
_, err = matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}))
if !errors.Is(err, transaction.ErrTransactionNotFound) {
t.Fatalf("bad error type, want %v, got %v", transaction.ErrTransactionNotFound, err)
}
})
} }
type mockSigner struct { type mockSigner struct {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment