-
Notifications
You must be signed in to change notification settings - Fork 337
/
sender_matcher.go
146 lines (123 loc) · 4.28 KB
/
sender_matcher.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package transaction
import (
"bytes"
"context"
"errors"
"fmt"
"math/big"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/storage"
"github.com/ethersphere/bee/pkg/swarm"
)
type Matcher struct {
backend Backend
storage storage.StateStorer
signer types.Signer
timeNow func() time.Time
}
const (
overlayPrefix = "verified_overlay_"
)
func peerOverlayKey(peer swarm.Address, txHash common.Hash) string {
return fmt.Sprintf("%s%s_%s", overlayPrefix, peer.ByteString(), txHash.String())
}
var (
ErrTransactionNotFound = errors.New("transaction not found")
ErrTransactionPending = errors.New("transaction in pending status")
ErrTransactionSenderInvalid = errors.New("invalid transaction sender")
ErrGreylisted = errors.New("overlay and transaction greylisted")
ErrBlockHashMismatch = errors.New("block hash mismatch")
ErrOverlayMismatch = errors.New("overlay mismatch")
)
type overlayVerification struct {
NextBlockHash []byte
Verified bool
TimeStamp time.Time
}
func NewMatcher(backend Backend, signer types.Signer, storage storage.StateStorer) *Matcher {
return &Matcher{
storage: storage,
backend: backend,
signer: signer,
timeNow: time.Now,
}
}
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
err := m.storage.Get(peerOverlayKey(senderOverlay, incomingTx), &val)
if err != nil {
if !errors.Is(err, storage.ErrNotFound) {
return nil, err
}
} else if val.Verified {
// add cache invalidation
return val.NextBlockHash, nil
} 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 {
return nil, m.greylist(senderOverlay, incomingTx, fmt.Errorf("%v: %w", err, ErrTransactionNotFound))
}
if isPending {
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
// we check for the starting 0-bytes so we don't mistake a 28 byte data solidity call for this
// otherwise this is considered as a signer based proof. a transaction can be only one of the two.
var attestedOverlay common.Address
txData := nTx.Data()
if len(txData) == 32 && bytes.Equal(txData[0:4], []byte{0, 0, 0, 0}) {
attestedOverlay = common.BytesToAddress(nTx.Data())
} else {
attestedOverlay, err = types.Sender(m.signer, nTx)
if err != nil {
return nil, m.greylist(senderOverlay, incomingTx, ErrTransactionSenderInvalid)
}
}
receipt, err := m.backend.TransactionReceipt(ctx, incomingTx)
if err != nil {
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 {
return nil, m.greylist(senderOverlay, incomingTx, err)
}
receiptBlockHash := receipt.BlockHash.Bytes()
nextBlockParentHash := nextBlock.ParentHash.Bytes()
nextBlockHash := nextBlock.Hash().Bytes()
if !bytes.Equal(receiptBlockHash, nextBlockParentHash) {
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) {
return nil, m.greylist(senderOverlay, incomingTx, ErrOverlayMismatch)
}
err = m.storage.Put(peerOverlayKey(senderOverlay, incomingTx), &overlayVerification{
TimeStamp: m.timeNow(),
Verified: true,
NextBlockHash: nextBlockHash,
})
if err != nil {
return nil, err
}
return nextBlockHash, nil
}