Skip to content

Commit

Permalink
fix: ignore greylist for self verification (#2688)
Browse files Browse the repository at this point in the history
  • Loading branch information
ralph-pichler authored Nov 29, 2021
1 parent 4a8b93f commit 07ef74c
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 76 deletions.
2 changes: 1 addition & 1 deletion pkg/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ func NewBee(addr string, publicKey *ecdsa.PublicKey, signer crypto.Signer, netwo

senderMatcher := transaction.NewMatcher(swapBackend, types.NewLondonSigner(big.NewInt(chainID)), stateStore)

_, err = senderMatcher.Matches(p2pCtx, txHash, networkID, swarmAddress)
_, err = senderMatcher.Matches(p2pCtx, txHash, networkID, swarmAddress, true)
if err != nil {
return nil, fmt.Errorf("identity transaction verification failed: %w", err)
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/p2p/libp2p/internal/handshake/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ type AdvertisableAddressResolver interface {
}

type SenderMatcher interface {
Matches(ctx context.Context, tx []byte, networkID uint64, senderOverlay swarm.Address) ([]byte, error)
Matches(ctx context.Context, tx []byte, networkID uint64, senderOverlay swarm.Address, ignoreGreylist bool) ([]byte, error)
}

// Service can perform initiate or handle a handshake between peers.
Expand Down Expand Up @@ -191,7 +191,7 @@ func (s *Service) Handshake(ctx context.Context, stream p2p.Stream, peerMultiadd
return nil, ErrNetworkIDIncompatible
}

blockHash, err := s.senderMatcher.Matches(ctx, resp.Ack.Transaction, s.networkID, overlay)
blockHash, err := s.senderMatcher.Matches(ctx, resp.Ack.Transaction, s.networkID, overlay, false)
if err != nil {
return nil, fmt.Errorf("overlay %v verification failed: %w", overlay, err)
}
Expand Down Expand Up @@ -321,7 +321,7 @@ func (s *Service) Handle(ctx context.Context, stream p2p.Stream, remoteMultiaddr
}
}

blockHash, err := s.senderMatcher.Matches(ctx, ack.Transaction, s.networkID, overlay)
blockHash, err := s.senderMatcher.Matches(ctx, ack.Transaction, s.networkID, overlay, false)
if err != nil {
return nil, fmt.Errorf("overlay %v verification failed: %w", overlay, err)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/p2p/libp2p/internal/handshake/handshake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,7 @@ type MockSenderMatcher struct {
blockHash []byte
}

func (m MockSenderMatcher) Matches(context.Context, []byte, uint64, swarm.Address) ([]byte, error) {
func (m MockSenderMatcher) Matches(context.Context, []byte, uint64, swarm.Address, bool) ([]byte, error) {

if m.v {
return m.blockHash, nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/p2p/libp2p/libp2p_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,6 @@ type MockSenderMatcher struct {
BlockHash []byte
}

func (m MockSenderMatcher) Matches(context.Context, []byte, uint64, swarm.Address) ([]byte, error) {
func (m MockSenderMatcher) Matches(context.Context, []byte, uint64, swarm.Address, bool) ([]byte, error) {
return m.BlockHash, nil
}
81 changes: 23 additions & 58 deletions pkg/transaction/sender_matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,18 @@ func NewMatcher(backend Backend, signer types.Signer, storage storage.StateStore
}
}

func (m *Matcher) Matches(ctx context.Context, tx []byte, networkID uint64, senderOverlay swarm.Address) ([]byte, error) {
func (m *Matcher) greylist(senderOverlay swarm.Address, incomingTx common.Hash, err error) error {
err2 := m.storage.Put(peerOverlayKey(senderOverlay, incomingTx), &overlayVerification{
TimeStamp: m.timeNow(),
Verified: false,
})
if err2 != nil {
return err2
}
return err
}

func (m *Matcher) Matches(ctx context.Context, tx []byte, networkID uint64, senderOverlay swarm.Address, ignoreGreylist bool) ([]byte, error) {
incomingTx := common.BytesToHash(tx)

var val overlayVerification
Expand All @@ -67,31 +77,17 @@ func (m *Matcher) Matches(ctx context.Context, tx []byte, networkID uint64, send
} else if val.Verified {
// add cache invalidation
return val.NextBlockHash, nil
} else if val.TimeStamp.Add(5 * time.Minute).After(m.timeNow()) {
} else if val.TimeStamp.Add(5*time.Minute).After(m.timeNow()) && !ignoreGreylist {
return nil, ErrGreylisted
}

nTx, isPending, err := m.backend.TransactionByHash(ctx, incomingTx)
if err != nil {
err2 := m.storage.Put(peerOverlayKey(senderOverlay, incomingTx), &overlayVerification{
TimeStamp: m.timeNow(),
Verified: false,
})
if err2 != nil {
return nil, err2
}
return nil, fmt.Errorf("%v: %w", err, ErrTransactionNotFound)
return nil, m.greylist(senderOverlay, incomingTx, fmt.Errorf("%v: %w", err, ErrTransactionNotFound))
}

if isPending {
err2 := m.storage.Put(peerOverlayKey(senderOverlay, incomingTx), &overlayVerification{
TimeStamp: m.timeNow(),
Verified: false,
})
if err2 != nil {
return nil, err2
}
return nil, ErrTransactionPending
return nil, m.greylist(senderOverlay, incomingTx, ErrTransactionPending)
}

// if transaction data is exactly one word and starts with 4 0-bytes this is a transaction data type proof
Expand All @@ -104,67 +100,36 @@ func (m *Matcher) Matches(ctx context.Context, tx []byte, networkID uint64, send
} else {
attestedOverlay, err = types.Sender(m.signer, nTx)
if err != nil {
err2 := m.storage.Put(peerOverlayKey(senderOverlay, incomingTx), &overlayVerification{
TimeStamp: m.timeNow(),
Verified: false,
})
if err2 != nil {
return nil, err2
}
return nil, fmt.Errorf("%v: %w", err, ErrTransactionSenderInvalid)
return nil, m.greylist(senderOverlay, incomingTx, ErrTransactionSenderInvalid)
}
}

receipt, err := m.backend.TransactionReceipt(ctx, incomingTx)
if err != nil {
err2 := m.storage.Put(peerOverlayKey(senderOverlay, incomingTx), &overlayVerification{
TimeStamp: m.timeNow(),
Verified: false,
})
if err2 != nil {
return nil, err2
}
return nil, err
return nil, m.greylist(senderOverlay, incomingTx, err)
}

nextBlock, err := m.backend.HeaderByNumber(ctx, big.NewInt(0).Add(receipt.BlockNumber, big.NewInt(1)))
if err != nil {
err2 := m.storage.Put(peerOverlayKey(senderOverlay, incomingTx), &overlayVerification{
TimeStamp: m.timeNow(),
Verified: false,
})
if err2 != nil {
return nil, err2
}
return nil, err
return nil, m.greylist(senderOverlay, incomingTx, err)
}

receiptBlockHash := receipt.BlockHash.Bytes()
nextBlockParentHash := nextBlock.ParentHash.Bytes()
nextBlockHash := nextBlock.Hash().Bytes()

if !bytes.Equal(receiptBlockHash, nextBlockParentHash) {
err2 := m.storage.Put(peerOverlayKey(senderOverlay, incomingTx), &overlayVerification{
TimeStamp: m.timeNow(),
Verified: false,
})
if err2 != nil {
return nil, err2
}
return nil, fmt.Errorf("receipt hash %x does not match block's parent hash %x: %w", receiptBlockHash, nextBlockParentHash, ErrBlockHashMismatch)
return nil, m.greylist(
senderOverlay,
incomingTx,
fmt.Errorf("receipt hash %x does not match block's parent hash %x: %w", receiptBlockHash, nextBlockParentHash, ErrBlockHashMismatch),
)
}

expectedRemoteBzzAddress := crypto.NewOverlayFromEthereumAddress(attestedOverlay.Bytes(), networkID, nextBlockHash)

if !expectedRemoteBzzAddress.Equal(senderOverlay) {
err2 := m.storage.Put(peerOverlayKey(senderOverlay, incomingTx), &overlayVerification{
TimeStamp: m.timeNow(),
Verified: false,
})
if err2 != nil {
return nil, err2
}
return nil, ErrOverlayMismatch
return nil, m.greylist(senderOverlay, incomingTx, ErrOverlayMismatch)
}

err = m.storage.Put(peerOverlayKey(senderOverlay, incomingTx), &overlayVerification{
Expand Down
43 changes: 31 additions & 12 deletions pkg/transaction/sender_matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func TestMatchesSender(t *testing.T) {

matcher := transaction.NewMatcher(backendmock.New(txByHash), nil, statestore.NewStateStore())

_, err := matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}))
_, err := matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}), false)
if !errors.Is(err, transaction.ErrTransactionNotFound) {
t.Fatalf("bad error type, want %v, got %v", transaction.ErrTransactionNotFound, err)
}
Expand All @@ -53,7 +53,7 @@ func TestMatchesSender(t *testing.T) {

matcher := transaction.NewMatcher(backendmock.New(txByHash), nil, statestore.NewStateStore())

_, err := matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}))
_, err := matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}), false)
if !errors.Is(err, transaction.ErrTransactionPending) {
t.Fatalf("bad error type, want %v, got %v", transaction.ErrTransactionPending, err)
}
Expand All @@ -69,7 +69,7 @@ func TestMatchesSender(t *testing.T) {
}
matcher := transaction.NewMatcher(backendmock.New(txByHash), signer, statestore.NewStateStore())

_, err := matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}))
_, err := matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}), false)
if !errors.Is(err, transaction.ErrTransactionSenderInvalid) {
t.Fatalf("bad error type, want %v, got %v", transaction.ErrTransactionSenderInvalid, err)
}
Expand Down Expand Up @@ -103,7 +103,7 @@ func TestMatchesSender(t *testing.T) {

matcher := transaction.NewMatcher(backendmock.New(txByHash, trxReceipt, headerByNum), signer, statestore.NewStateStore())

_, err := matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}))
_, err := matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}), false)
if err == nil {
t.Fatalf("expected no match")
}
Expand Down Expand Up @@ -139,7 +139,7 @@ func TestMatchesSender(t *testing.T) {

senderOverlay := crypto.NewOverlayFromEthereumAddress(signer.addr.Bytes(), 0, nextBlockHeader.Hash().Bytes())

_, err := matcher.Matches(context.Background(), trx, 0, senderOverlay)
_, err := matcher.Matches(context.Background(), trx, 0, senderOverlay, false)
if err != nil {
t.Fatalf("expected match")
}
Expand Down Expand Up @@ -178,14 +178,14 @@ func TestMatchesSender(t *testing.T) {

senderOverlay := crypto.NewOverlayFromEthereumAddress(overlayEth.Bytes(), 0, nextBlockHeader.Hash().Bytes())

_, err := matcher.Matches(context.Background(), trx, 0, senderOverlay)
_, err := matcher.Matches(context.Background(), trx, 0, senderOverlay, false)
if err != nil {
t.Fatalf("expected match. got %v", err)
}

senderOverlay = crypto.NewOverlayFromEthereumAddress(signer.addr.Bytes(), 0, nextBlockHeader.Hash().Bytes())

_, err = matcher.Matches(context.Background(), trx, 0, senderOverlay)
_, err = matcher.Matches(context.Background(), trx, 0, senderOverlay, false)
if err == nil {
t.Fatalf("matched signer for data tx")
}
Expand Down Expand Up @@ -226,12 +226,12 @@ func TestMatchesSender(t *testing.T) {

senderOverlay := crypto.NewOverlayFromEthereumAddress(signer.addr.Bytes(), 0, nextBlockHeader.Hash().Bytes())

_, err := matcher.Matches(context.Background(), trx, 0, senderOverlay)
_, err := matcher.Matches(context.Background(), trx, 0, senderOverlay, false)
if err != nil {
t.Fatalf("expected match")
}

_, err = matcher.Matches(context.Background(), trx, 0, senderOverlay)
_, err = matcher.Matches(context.Background(), trx, 0, senderOverlay, false)
if err != nil {
t.Fatalf("expected match")
}
Expand All @@ -249,20 +249,39 @@ func TestMatchesSender(t *testing.T) {
matcher := transaction.NewMatcher(backendmock.New(txByHash), nil, statestore.NewStateStore())
matcher.SetTime(0)

_, err := matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}))
_, err := matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}), false)
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{}))
_, err = matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}), false)
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{}))
_, err = matcher.Matches(context.Background(), trx, 0, swarm.NewAddress([]byte{}), false)
if !errors.Is(err, transaction.ErrTransactionNotFound) {
t.Fatalf("bad error type, want %v, got %v", transaction.ErrTransactionNotFound, err)
}
})

t.Run("greylisted but ignored", 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{}), false)
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{}), true)
if !errors.Is(err, transaction.ErrTransactionNotFound) {
t.Fatalf("bad error type, want %v, got %v", transaction.ErrTransactionNotFound, err)
}
Expand Down

0 comments on commit 07ef74c

Please sign in to comment.