Commit 89251900 authored by Anatolie Lupacescu's avatar Anatolie Lupacescu Committed by GitHub

feat: validate peer address using proof-of-identity transaction hash (#1655)

parent 0b0bb9e8
...@@ -58,6 +58,7 @@ const ( ...@@ -58,6 +58,7 @@ const (
optionNameSwapFactoryAddress = "swap-factory-address" optionNameSwapFactoryAddress = "swap-factory-address"
optionNameSwapInitialDeposit = "swap-initial-deposit" optionNameSwapInitialDeposit = "swap-initial-deposit"
optionNameSwapEnable = "swap-enable" optionNameSwapEnable = "swap-enable"
optionNameTransactionHash = "transaction"
optionNameFullNode = "full-node" optionNameFullNode = "full-node"
optionNamePostageContractAddress = "postage-stamp-address" optionNamePostageContractAddress = "postage-stamp-address"
optionNamePriceOracleAddress = "price-oracle-address" optionNamePriceOracleAddress = "price-oracle-address"
...@@ -230,6 +231,7 @@ func (c *command) setAllFlags(cmd *cobra.Command) { ...@@ -230,6 +231,7 @@ func (c *command) setAllFlags(cmd *cobra.Command) {
cmd.Flags().Bool(optionNameFullNode, false, "cause the node to start in full mode") cmd.Flags().Bool(optionNameFullNode, false, "cause the node to start in full mode")
cmd.Flags().String(optionNamePostageContractAddress, "", "postage stamp contract address") cmd.Flags().String(optionNamePostageContractAddress, "", "postage stamp contract address")
cmd.Flags().String(optionNamePriceOracleAddress, "", "price oracle address") cmd.Flags().String(optionNamePriceOracleAddress, "", "price oracle address")
cmd.Flags().String(optionNameTransactionHash, "", "proof-of-identity transaction hash")
} }
func newLogger(cmd *cobra.Command, verbosity string) (logging.Logger, error) { func newLogger(cmd *cobra.Command, verbosity string) (logging.Logger, error) {
......
...@@ -148,6 +148,7 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz ...@@ -148,6 +148,7 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz
SwapInitialDeposit: c.config.GetString(optionNameSwapInitialDeposit), SwapInitialDeposit: c.config.GetString(optionNameSwapInitialDeposit),
SwapEnable: c.config.GetBool(optionNameSwapEnable), SwapEnable: c.config.GetBool(optionNameSwapEnable),
FullNodeMode: fullNode, FullNodeMode: fullNode,
Transaction: c.config.GetString(optionNameTransactionHash),
PostageContractAddress: c.config.GetString(optionNamePostageContractAddress), PostageContractAddress: c.config.GetString(optionNamePostageContractAddress),
PriceOracleAddress: c.config.GetString(optionNamePriceOracleAddress), PriceOracleAddress: c.config.GetString(optionNamePriceOracleAddress),
}) })
......
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethersphere/bee/pkg/accounting" "github.com/ethersphere/bee/pkg/accounting"
"github.com/ethersphere/bee/pkg/addressbook" "github.com/ethersphere/bee/pkg/addressbook"
...@@ -127,6 +128,7 @@ type Options struct { ...@@ -127,6 +128,7 @@ type Options struct {
SwapInitialDeposit string SwapInitialDeposit string
SwapEnable bool SwapEnable bool
FullNodeMode bool FullNodeMode bool
Transaction string
PostageContractAddress string PostageContractAddress string
PriceOracleAddress string PriceOracleAddress string
} }
...@@ -273,7 +275,14 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey, ...@@ -273,7 +275,14 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
lightNodes := lightnode.NewContainer() lightNodes := lightnode.NewContainer()
p2ps, err := libp2p.New(p2pCtx, signer, networkID, swarmAddress, addr, addressbook, stateStore, lightNodes, logger, tracer, libp2p.Options{ txHash, err := getTxHash(stateStore, logger, o.Transaction)
if err != nil {
return nil, errors.New("no transaction hash provided or found")
}
senderMatcher := transaction.NewMatcher(swapBackend, types.NewEIP155Signer(big.NewInt(chainID)))
p2ps, err := libp2p.New(p2pCtx, signer, networkID, swarmAddress, addr, addressbook, stateStore, lightNodes, senderMatcher, logger, tracer, libp2p.Options{
PrivateKey: libp2pPrivateKey, PrivateKey: libp2pPrivateKey,
NATAddr: o.NATAddr, NATAddr: o.NATAddr,
EnableWS: o.EnableWS, EnableWS: o.EnableWS,
...@@ -281,6 +290,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey, ...@@ -281,6 +290,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
Standalone: o.Standalone, Standalone: o.Standalone,
WelcomeMessage: o.WelcomeMessage, WelcomeMessage: o.WelcomeMessage,
FullNode: o.FullNodeMode, FullNode: o.FullNodeMode,
Transaction: txHash,
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("p2p service: %w", err) return nil, fmt.Errorf("p2p service: %w", err)
...@@ -781,3 +791,18 @@ func (e *multiError) add(err error) { ...@@ -781,3 +791,18 @@ func (e *multiError) add(err error) {
func (e *multiError) hasErrors() bool { func (e *multiError) hasErrors() bool {
return len(e.errors) > 0 return len(e.errors) > 0
} }
func getTxHash(stateStore storage.StateStorer, logger logging.Logger, transaction string) ([]byte, error) {
if len(transaction) == 32 {
logger.Info("using the provided transaction hash")
return []byte(transaction), nil
}
var txHash common.Hash
key := chequebook.ChequebookDeploymentKey
if err := stateStore.Get(key, &txHash); err != nil {
return nil, err
}
return txHash.Bytes(), nil
}
...@@ -50,6 +50,9 @@ var ( ...@@ -50,6 +50,9 @@ var (
// ErrInvalidSyn is returned if observable address in ack is not a valid.. // ErrInvalidSyn is returned if observable address in ack is not a valid..
ErrInvalidSyn = errors.New("invalid syn") ErrInvalidSyn = errors.New("invalid syn")
// ErrAddressNotFound is returned if observable address in ack is not a valid..
ErrAddressNotFound = errors.New("address not found")
// ErrWelcomeMessageLength is returned if the welcome message is longer than the maximum length // ErrWelcomeMessageLength is returned if the welcome message is longer than the maximum length
ErrWelcomeMessageLength = fmt.Errorf("handshake welcome message longer than maximum of %d characters", MaxWelcomeMessageLength) ErrWelcomeMessageLength = fmt.Errorf("handshake welcome message longer than maximum of %d characters", MaxWelcomeMessageLength)
) )
...@@ -59,12 +62,18 @@ type AdvertisableAddressResolver interface { ...@@ -59,12 +62,18 @@ type AdvertisableAddressResolver interface {
Resolve(observedAdddress ma.Multiaddr) (ma.Multiaddr, error) Resolve(observedAdddress ma.Multiaddr) (ma.Multiaddr, error)
} }
type SenderMatcher interface {
Matches(ctx context.Context, tx []byte, networkID uint64, senderOverlay swarm.Address) (bool, error)
}
// Service can perform initiate or handle a handshake between peers. // Service can perform initiate or handle a handshake between peers.
type Service struct { type Service struct {
signer crypto.Signer signer crypto.Signer
advertisableAddresser AdvertisableAddressResolver advertisableAddresser AdvertisableAddressResolver
senderMatcher SenderMatcher
overlay swarm.Address overlay swarm.Address
fullNode bool fullNode bool
transaction []byte
networkID uint64 networkID uint64
welcomeMessage atomic.Value welcomeMessage atomic.Value
receivedHandshakes map[libp2ppeer.ID]struct{} receivedHandshakes map[libp2ppeer.ID]struct{}
...@@ -89,7 +98,7 @@ func (i *Info) LightString() string { ...@@ -89,7 +98,7 @@ func (i *Info) LightString() string {
} }
// New creates a new handshake Service. // New creates a new handshake Service.
func New(signer crypto.Signer, advertisableAddresser AdvertisableAddressResolver, overlay swarm.Address, networkID uint64, fullNode bool, welcomeMessage string, logger logging.Logger) (*Service, error) { func New(signer crypto.Signer, advertisableAddresser AdvertisableAddressResolver, isSender SenderMatcher, overlay swarm.Address, networkID uint64, fullNode bool, transaction []byte, welcomeMessage string, logger logging.Logger) (*Service, error) {
if len(welcomeMessage) > MaxWelcomeMessageLength { if len(welcomeMessage) > MaxWelcomeMessageLength {
return nil, ErrWelcomeMessageLength return nil, ErrWelcomeMessageLength
} }
...@@ -100,6 +109,8 @@ func New(signer crypto.Signer, advertisableAddresser AdvertisableAddressResolver ...@@ -100,6 +109,8 @@ func New(signer crypto.Signer, advertisableAddresser AdvertisableAddressResolver
overlay: overlay, overlay: overlay,
networkID: networkID, networkID: networkID,
fullNode: fullNode, fullNode: fullNode,
transaction: transaction,
senderMatcher: isSender,
receivedHandshakes: make(map[libp2ppeer.ID]struct{}), receivedHandshakes: make(map[libp2ppeer.ID]struct{}),
logger: logger, logger: logger,
Notifiee: new(network.NoopNotifiee), Notifiee: new(network.NoopNotifiee),
...@@ -171,6 +182,7 @@ func (s *Service) Handshake(ctx context.Context, stream p2p.Stream, peerMultiadd ...@@ -171,6 +182,7 @@ func (s *Service) Handshake(ctx context.Context, stream p2p.Stream, peerMultiadd
}, },
NetworkID: s.networkID, NetworkID: s.networkID,
FullNode: s.fullNode, FullNode: s.fullNode,
Transaction: s.transaction,
WelcomeMessage: welcomeMessage, WelcomeMessage: welcomeMessage,
}); err != nil { }); err != nil {
return nil, fmt.Errorf("write ack message: %w", err) return nil, fmt.Errorf("write ack message: %w", err)
...@@ -250,6 +262,7 @@ func (s *Service) Handle(ctx context.Context, stream p2p.Stream, remoteMultiaddr ...@@ -250,6 +262,7 @@ func (s *Service) Handle(ctx context.Context, stream p2p.Stream, remoteMultiaddr
}, },
NetworkID: s.networkID, NetworkID: s.networkID,
FullNode: s.fullNode, FullNode: s.fullNode,
Transaction: s.transaction,
WelcomeMessage: welcomeMessage, WelcomeMessage: welcomeMessage,
}, },
}); err != nil { }); err != nil {
...@@ -271,6 +284,15 @@ func (s *Service) Handle(ctx context.Context, stream p2p.Stream, remoteMultiaddr ...@@ -271,6 +284,15 @@ func (s *Service) Handle(ctx context.Context, stream p2p.Stream, remoteMultiaddr
s.logger.Infof("greeting \"%s\" from peer: %s", ack.WelcomeMessage, remoteBzzAddress.Overlay.String()) s.logger.Infof("greeting \"%s\" from peer: %s", ack.WelcomeMessage, remoteBzzAddress.Overlay.String())
} }
matchesSender, err := s.senderMatcher.Matches(ctx, ack.Transaction, s.networkID, remoteBzzAddress.Overlay)
if err != nil {
return nil, err
}
if !matchesSender {
return nil, fmt.Errorf("given address is not registered on Ethereum: %v: %w", remoteBzzAddress.Overlay, ErrAddressNotFound)
}
return &Info{ return &Info{
BzzAddress: remoteBzzAddress, BzzAddress: remoteBzzAddress,
FullNode: ack.FullNode, FullNode: ack.FullNode,
......
...@@ -19,6 +19,7 @@ import ( ...@@ -19,6 +19,7 @@ import (
"github.com/ethersphere/bee/pkg/p2p/libp2p/internal/handshake/mock" "github.com/ethersphere/bee/pkg/p2p/libp2p/internal/handshake/mock"
"github.com/ethersphere/bee/pkg/p2p/libp2p/internal/handshake/pb" "github.com/ethersphere/bee/pkg/p2p/libp2p/internal/handshake/pb"
"github.com/ethersphere/bee/pkg/p2p/protobuf" "github.com/ethersphere/bee/pkg/p2p/protobuf"
"github.com/ethersphere/bee/pkg/swarm"
libp2ppeer "github.com/libp2p/go-libp2p-core/peer" libp2ppeer "github.com/libp2p/go-libp2p-core/peer"
ma "github.com/multiformats/go-multiaddr" ma "github.com/multiformats/go-multiaddr"
...@@ -91,8 +92,9 @@ func TestHandshake(t *testing.T) { ...@@ -91,8 +92,9 @@ func TestHandshake(t *testing.T) {
} }
aaddresser := &AdvertisableAddresserMock{} aaddresser := &AdvertisableAddresserMock{}
senderMatcher := &MockSenderMatcher{v: true}
handshakeService, err := handshake.New(signer1, aaddresser, node1Info.BzzAddress.Overlay, networkID, true, testWelcomeMessage, logger) handshakeService, err := handshake.New(signer1, aaddresser, senderMatcher, node1Info.BzzAddress.Overlay, networkID, true, nil, testWelcomeMessage, logger)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -160,7 +162,7 @@ func TestHandshake(t *testing.T) { ...@@ -160,7 +162,7 @@ func TestHandshake(t *testing.T) {
const LongMessage = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi consectetur urna ut lorem sollicitudin posuere. Donec sagittis laoreet sapien." const LongMessage = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi consectetur urna ut lorem sollicitudin posuere. Donec sagittis laoreet sapien."
expectedErr := handshake.ErrWelcomeMessageLength expectedErr := handshake.ErrWelcomeMessageLength
_, err := handshake.New(signer1, aaddresser, node1Info.BzzAddress.Overlay, networkID, true, LongMessage, logger) _, err := handshake.New(signer1, aaddresser, senderMatcher, node1Info.BzzAddress.Overlay, networkID, true, nil, LongMessage, logger)
if err == nil || err.Error() != expectedErr.Error() { if err == nil || err.Error() != expectedErr.Error() {
t.Fatal("expected:", expectedErr, "got:", err) t.Fatal("expected:", expectedErr, "got:", err)
} }
...@@ -368,7 +370,7 @@ func TestHandshake(t *testing.T) { ...@@ -368,7 +370,7 @@ func TestHandshake(t *testing.T) {
}) })
t.Run("Handle - OK", func(t *testing.T) { t.Run("Handle - OK", func(t *testing.T) {
handshakeService, err := handshake.New(signer1, aaddresser, node1Info.BzzAddress.Overlay, networkID, true, "", logger) handshakeService, err := handshake.New(signer1, aaddresser, senderMatcher, node1Info.BzzAddress.Overlay, networkID, true, nil, "", logger)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -425,7 +427,7 @@ func TestHandshake(t *testing.T) { ...@@ -425,7 +427,7 @@ func TestHandshake(t *testing.T) {
}) })
t.Run("Handle - read error ", func(t *testing.T) { t.Run("Handle - read error ", func(t *testing.T) {
handshakeService, err := handshake.New(signer1, aaddresser, node1Info.BzzAddress.Overlay, networkID, true, "", logger) handshakeService, err := handshake.New(signer1, aaddresser, senderMatcher, node1Info.BzzAddress.Overlay, networkID, true, nil, "", logger)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -444,7 +446,7 @@ func TestHandshake(t *testing.T) { ...@@ -444,7 +446,7 @@ func TestHandshake(t *testing.T) {
}) })
t.Run("Handle - write error ", func(t *testing.T) { t.Run("Handle - write error ", func(t *testing.T) {
handshakeService, err := handshake.New(signer1, aaddresser, node1Info.BzzAddress.Overlay, networkID, true, "", logger) handshakeService, err := handshake.New(signer1, aaddresser, senderMatcher, node1Info.BzzAddress.Overlay, networkID, true, nil, "", logger)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -471,7 +473,7 @@ func TestHandshake(t *testing.T) { ...@@ -471,7 +473,7 @@ func TestHandshake(t *testing.T) {
}) })
t.Run("Handle - ack read error ", func(t *testing.T) { t.Run("Handle - ack read error ", func(t *testing.T) {
handshakeService, err := handshake.New(signer1, aaddresser, node1Info.BzzAddress.Overlay, networkID, true, "", logger) handshakeService, err := handshake.New(signer1, aaddresser, senderMatcher, node1Info.BzzAddress.Overlay, networkID, true, nil, "", logger)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -500,7 +502,7 @@ func TestHandshake(t *testing.T) { ...@@ -500,7 +502,7 @@ func TestHandshake(t *testing.T) {
}) })
t.Run("Handle - networkID mismatch ", func(t *testing.T) { t.Run("Handle - networkID mismatch ", func(t *testing.T) {
handshakeService, err := handshake.New(signer1, aaddresser, node1Info.BzzAddress.Overlay, networkID, true, "", logger) handshakeService, err := handshake.New(signer1, aaddresser, senderMatcher, node1Info.BzzAddress.Overlay, networkID, true, nil, "", logger)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -539,7 +541,7 @@ func TestHandshake(t *testing.T) { ...@@ -539,7 +541,7 @@ func TestHandshake(t *testing.T) {
}) })
t.Run("Handle - duplicate handshake", func(t *testing.T) { t.Run("Handle - duplicate handshake", func(t *testing.T) {
handshakeService, err := handshake.New(signer1, aaddresser, node1Info.BzzAddress.Overlay, networkID, true, "", logger) handshakeService, err := handshake.New(signer1, aaddresser, senderMatcher, node1Info.BzzAddress.Overlay, networkID, true, nil, "", logger)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -601,7 +603,7 @@ func TestHandshake(t *testing.T) { ...@@ -601,7 +603,7 @@ func TestHandshake(t *testing.T) {
}) })
t.Run("Handle - invalid ack", func(t *testing.T) { t.Run("Handle - invalid ack", func(t *testing.T) {
handshakeService, err := handshake.New(signer1, aaddresser, node1Info.BzzAddress.Overlay, networkID, true, "", logger) handshakeService, err := handshake.New(signer1, aaddresser, senderMatcher, node1Info.BzzAddress.Overlay, networkID, true, nil, "", logger)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -635,8 +637,45 @@ func TestHandshake(t *testing.T) { ...@@ -635,8 +637,45 @@ func TestHandshake(t *testing.T) {
} }
}) })
t.Run("Handle - transaction is not on the blockchain", func(t *testing.T) {
sbMock := &MockSenderMatcher{v: false}
handshakeService, err := handshake.New(signer1, aaddresser, sbMock, node1Info.BzzAddress.Overlay, networkID, true, []byte("0xff"), "", logger)
if err != nil {
t.Fatal(err)
}
var buffer1 bytes.Buffer
var buffer2 bytes.Buffer
stream1 := mock.NewStream(&buffer1, &buffer2)
stream2 := mock.NewStream(&buffer2, &buffer1)
w := protobuf.NewWriter(stream2)
if err := w.WriteMsg(&pb.Syn{
ObservedUnderlay: node1maBinary,
}); err != nil {
t.Fatal(err)
}
if err := w.WriteMsg(&pb.Ack{
Address: &pb.BzzAddress{
Underlay: node2maBinary,
Overlay: node2BzzAddress.Overlay.Bytes(),
Signature: node2BzzAddress.Signature,
},
NetworkID: networkID,
FullNode: true,
}); err != nil {
t.Fatal(err)
}
_, err = handshakeService.Handle(context.Background(), stream1, node2AddrInfo.Addrs[0], node2AddrInfo.ID)
if !errors.Is(err, handshake.ErrAddressNotFound) {
t.Fatalf("expected error %v, got %v", handshake.ErrAddressNotFound, err)
}
})
t.Run("Handle - advertisable error", func(t *testing.T) { t.Run("Handle - advertisable error", func(t *testing.T) {
handshakeService, err := handshake.New(signer1, aaddresser, node1Info.BzzAddress.Overlay, networkID, true, "", logger) handshakeService, err := handshake.New(signer1, aaddresser, senderMatcher, node1Info.BzzAddress.Overlay, networkID, true, nil, "", logger)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -693,3 +732,11 @@ func (a *AdvertisableAddresserMock) Resolve(observedAdddress ma.Multiaddr) (ma.M ...@@ -693,3 +732,11 @@ func (a *AdvertisableAddresserMock) Resolve(observedAdddress ma.Multiaddr) (ma.M
return observedAdddress, nil return observedAdddress, nil
} }
type MockSenderMatcher struct {
v bool
}
func (m MockSenderMatcher) Matches(context.Context, []byte, uint64, swarm.Address) (bool, error) {
return m.v, nil
}
...@@ -70,6 +70,7 @@ type Ack struct { ...@@ -70,6 +70,7 @@ type Ack struct {
Address *BzzAddress `protobuf:"bytes,1,opt,name=Address,proto3" json:"Address,omitempty"` Address *BzzAddress `protobuf:"bytes,1,opt,name=Address,proto3" json:"Address,omitempty"`
NetworkID uint64 `protobuf:"varint,2,opt,name=NetworkID,proto3" json:"NetworkID,omitempty"` NetworkID uint64 `protobuf:"varint,2,opt,name=NetworkID,proto3" json:"NetworkID,omitempty"`
FullNode bool `protobuf:"varint,3,opt,name=FullNode,proto3" json:"FullNode,omitempty"` FullNode bool `protobuf:"varint,3,opt,name=FullNode,proto3" json:"FullNode,omitempty"`
Transaction []byte `protobuf:"bytes,4,opt,name=Transaction,proto3" json:"Transaction,omitempty"`
WelcomeMessage string `protobuf:"bytes,99,opt,name=WelcomeMessage,proto3" json:"WelcomeMessage,omitempty"` WelcomeMessage string `protobuf:"bytes,99,opt,name=WelcomeMessage,proto3" json:"WelcomeMessage,omitempty"`
} }
...@@ -127,6 +128,13 @@ func (m *Ack) GetFullNode() bool { ...@@ -127,6 +128,13 @@ func (m *Ack) GetFullNode() bool {
return false return false
} }
func (m *Ack) GetTransaction() []byte {
if m != nil {
return m.Transaction
}
return nil
}
func (m *Ack) GetWelcomeMessage() string { func (m *Ack) GetWelcomeMessage() string {
if m != nil { if m != nil {
return m.WelcomeMessage return m.WelcomeMessage
...@@ -256,26 +264,28 @@ func init() { ...@@ -256,26 +264,28 @@ func init() {
func init() { proto.RegisterFile("handshake.proto", fileDescriptor_a77305914d5d202f) } func init() { proto.RegisterFile("handshake.proto", fileDescriptor_a77305914d5d202f) }
var fileDescriptor_a77305914d5d202f = []byte{ var fileDescriptor_a77305914d5d202f = []byte{
// 303 bytes of a gzipped FileDescriptorProto // 324 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcf, 0x48, 0xcc, 0x4b, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x91, 0xdf, 0x4a, 0xf3, 0x30,
0x29, 0xce, 0x48, 0xcc, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x84, 0x0b, 0x28, 0x18, 0xc6, 0x97, 0x75, 0xec, 0xcf, 0xbb, 0xb1, 0xef, 0x23, 0x20, 0x14, 0x19, 0x25, 0xf4, 0x40,
0x19, 0x72, 0x31, 0x07, 0x57, 0xe6, 0x09, 0x69, 0x71, 0x09, 0xf8, 0x27, 0x15, 0xa7, 0x16, 0x95, 0x8a, 0x07, 0x13, 0xf5, 0x0a, 0x36, 0x44, 0x10, 0x74, 0x83, 0x54, 0x11, 0x3c, 0x32, 0x6b, 0xc3,
0xa5, 0xa6, 0x84, 0xe6, 0xa5, 0xa4, 0x16, 0xe5, 0x24, 0x56, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0xf0, 0x36, 0x5a, 0x93, 0x91, 0x74, 0x93, 0xee, 0x2a, 0xbc, 0x1e, 0xaf, 0xc0, 0xc3, 0x1d, 0x7a, 0x28,
0x04, 0x61, 0x88, 0x2b, 0xcd, 0x60, 0xe4, 0x62, 0x76, 0x4c, 0xce, 0x16, 0xd2, 0xe7, 0x62, 0x77, 0xdb, 0x8d, 0x48, 0xe3, 0xd6, 0xce, 0x79, 0xf8, 0xfe, 0x9e, 0x27, 0xc9, 0xf3, 0xbc, 0x81, 0x7f,
0x4c, 0x49, 0x29, 0x4a, 0x2d, 0x2e, 0x06, 0x2b, 0xe5, 0x36, 0x12, 0xd5, 0x43, 0x58, 0xe4, 0x54, 0x13, 0x26, 0x42, 0x3d, 0x61, 0x11, 0xef, 0xce, 0x94, 0x4c, 0x24, 0x6e, 0xe4, 0xc0, 0x3d, 0x07,
0x55, 0x05, 0x95, 0x0c, 0x82, 0xa9, 0x12, 0x92, 0xe1, 0xe2, 0xf4, 0x4b, 0x2d, 0x29, 0xcf, 0x2f, 0xcb, 0x4f, 0x05, 0x3e, 0x85, 0xff, 0xc3, 0x91, 0xe6, 0x6a, 0xc1, 0xc3, 0x07, 0x11, 0x72, 0x15,
0xca, 0xf6, 0x74, 0x91, 0x60, 0x52, 0x60, 0xd4, 0x60, 0x09, 0x42, 0x08, 0x08, 0x49, 0x71, 0x71, 0xb3, 0xd4, 0x46, 0x04, 0x79, 0x2d, 0xfa, 0x87, 0xbb, 0xef, 0x08, 0xac, 0x5e, 0x10, 0xe1, 0x33,
0xb8, 0x95, 0xe6, 0xe4, 0xf8, 0xe5, 0xa7, 0xa4, 0x4a, 0x30, 0x2b, 0x30, 0x6a, 0x70, 0x04, 0xc1, 0xa8, 0xf5, 0xc2, 0x50, 0x71, 0xad, 0x8d, 0xb5, 0x79, 0x71, 0xd4, 0x2d, 0x1e, 0xea, 0x2f, 0x97,
0xf9, 0x42, 0x6a, 0x5c, 0x7c, 0xe1, 0xa9, 0x39, 0xc9, 0xf9, 0xb9, 0xa9, 0xbe, 0xa9, 0xc5, 0xc5, 0x5b, 0x91, 0xee, 0x5c, 0xb8, 0x03, 0x8d, 0x01, 0x4f, 0x5e, 0xa5, 0x8a, 0x6e, 0xae, 0xec, 0x32,
0x89, 0xe9, 0xa9, 0x12, 0xc9, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x68, 0xa2, 0x4a, 0x3e, 0x5c, 0x6c, 0x41, 0x5e, 0x85, 0x16, 0x00, 0x1f, 0x43, 0xfd, 0x7a, 0x1e, 0xc7, 0x03, 0x19, 0x72, 0xdb, 0x22,
0xc1, 0x95, 0x79, 0x20, 0xc7, 0x29, 0x80, 0xfd, 0x05, 0x75, 0x18, 0x1f, 0x92, 0xc3, 0x82, 0x2b, 0xc8, 0xab, 0xd3, 0x7c, 0xc6, 0x04, 0x9a, 0xf7, 0x8a, 0x09, 0xcd, 0x82, 0x64, 0x2a, 0x85, 0x5d,
0xf3, 0x82, 0xc0, 0x5e, 0x56, 0x00, 0xfb, 0x02, 0xec, 0x0e, 0x54, 0x15, 0x8e, 0xc9, 0xd9, 0x41, 0x31, 0xc9, 0xf6, 0x11, 0x3e, 0x81, 0xf6, 0x23, 0x8f, 0x03, 0xf9, 0xc2, 0xef, 0xb8, 0xd6, 0x6c,
0x20, 0x29, 0xa5, 0x04, 0x2e, 0x2e, 0x84, 0x37, 0x40, 0xee, 0x43, 0x0b, 0x1a, 0x38, 0x1f, 0xe4, 0xcc, 0xed, 0x80, 0x20, 0xaf, 0x41, 0x0f, 0xa8, 0x7b, 0x0b, 0x55, 0x3f, 0x15, 0x59, 0x7c, 0x62,
0xb3, 0xe0, 0xcc, 0xf4, 0xbc, 0xc4, 0x92, 0xd2, 0xa2, 0x54, 0xb0, 0x89, 0x3c, 0x41, 0x08, 0x01, 0x9a, 0x6f, 0xa3, 0xb7, 0xf7, 0xa2, 0xfb, 0xa9, 0xa0, 0x66, 0x29, 0xc4, 0xf4, 0x34, 0x49, 0x7f,
0x21, 0x09, 0x2e, 0x76, 0xff, 0x32, 0x88, 0x46, 0x66, 0xb0, 0x1c, 0x8c, 0xeb, 0x24, 0x73, 0xe2, 0x3b, 0x7a, 0x41, 0x44, 0x33, 0xc9, 0x7d, 0x06, 0x28, 0x8a, 0x66, 0x0d, 0x0e, 0x96, 0x97, 0xcf,
0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0x59, 0x77, 0x7f, 0x3a, 0x16, 0x2c, 0x99, 0x2b, 0x6e, 0x6e, 0x6c, 0xd1, 0x02, 0x60, 0x1b, 0x6a,
0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x4c, 0x05, 0x49, 0x49, 0x6c, 0xe0, 0xd8, 0xc3, 0xc5, 0xcf, 0x41, 0xcb, 0x68, 0xbb, 0xb1, 0xdf, 0xf9, 0x58, 0x3b, 0x68, 0xb5, 0x76, 0xd0,
0x32, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x30, 0x28, 0x02, 0x6f, 0xc0, 0x01, 0x00, 0x00, 0xd7, 0xda, 0x41, 0x6f, 0x1b, 0xa7, 0xb4, 0xda, 0x38, 0xa5, 0xcf, 0x8d, 0x53, 0x7a, 0x2a, 0xcf,
0x46, 0xa3, 0xaa, 0xf9, 0xcf, 0xcb, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd1, 0x48, 0x2c, 0x4a,
0xe2, 0x01, 0x00, 0x00,
} }
func (m *Syn) Marshal() (dAtA []byte, err error) { func (m *Syn) Marshal() (dAtA []byte, err error) {
...@@ -337,6 +347,13 @@ func (m *Ack) MarshalToSizedBuffer(dAtA []byte) (int, error) { ...@@ -337,6 +347,13 @@ func (m *Ack) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i-- i--
dAtA[i] = 0x9a dAtA[i] = 0x9a
} }
if len(m.Transaction) > 0 {
i -= len(m.Transaction)
copy(dAtA[i:], m.Transaction)
i = encodeVarintHandshake(dAtA, i, uint64(len(m.Transaction)))
i--
dAtA[i] = 0x22
}
if m.FullNode { if m.FullNode {
i-- i--
if m.FullNode { if m.FullNode {
...@@ -498,6 +515,10 @@ func (m *Ack) Size() (n int) { ...@@ -498,6 +515,10 @@ func (m *Ack) Size() (n int) {
if m.FullNode { if m.FullNode {
n += 2 n += 2
} }
l = len(m.Transaction)
if l > 0 {
n += 1 + l + sovHandshake(uint64(l))
}
l = len(m.WelcomeMessage) l = len(m.WelcomeMessage)
if l > 0 { if l > 0 {
n += 2 + l + sovHandshake(uint64(l)) n += 2 + l + sovHandshake(uint64(l))
...@@ -740,6 +761,40 @@ func (m *Ack) Unmarshal(dAtA []byte) error { ...@@ -740,6 +761,40 @@ func (m *Ack) Unmarshal(dAtA []byte) error {
} }
} }
m.FullNode = bool(v != 0) m.FullNode = bool(v != 0)
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Transaction", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowHandshake
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthHandshake
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthHandshake
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Transaction = append(m.Transaction[:0], dAtA[iNdEx:postIndex]...)
if m.Transaction == nil {
m.Transaction = []byte{}
}
iNdEx = postIndex
case 99: case 99:
if wireType != 2 { if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field WelcomeMessage", wireType) return fmt.Errorf("proto: wrong wireType = %d for field WelcomeMessage", wireType)
......
...@@ -16,6 +16,7 @@ message Ack { ...@@ -16,6 +16,7 @@ message Ack {
BzzAddress Address = 1; BzzAddress Address = 1;
uint64 NetworkID = 2; uint64 NetworkID = 2;
bool FullNode = 3; bool FullNode = 3;
bytes Transaction = 4;
string WelcomeMessage = 99; string WelcomeMessage = 99;
} }
......
...@@ -84,9 +84,10 @@ type Options struct { ...@@ -84,9 +84,10 @@ type Options struct {
Standalone bool Standalone bool
FullNode bool FullNode bool
WelcomeMessage string WelcomeMessage string
Transaction []byte
} }
func New(ctx context.Context, signer beecrypto.Signer, networkID uint64, overlay swarm.Address, addr string, ab addressbook.Putter, storer storage.StateStorer, lightNodes *lightnode.Container, logger logging.Logger, tracer *tracing.Tracer, o Options) (*Service, error) { func New(ctx context.Context, signer beecrypto.Signer, networkID uint64, overlay swarm.Address, addr string, ab addressbook.Putter, storer storage.StateStorer, lightNodes *lightnode.Container, swapBackend handshake.SenderMatcher, logger logging.Logger, tracer *tracing.Tracer, o Options) (*Service, error) {
host, port, err := net.SplitHostPort(addr) host, port, err := net.SplitHostPort(addr)
if err != nil { if err != nil {
return nil, fmt.Errorf("address: %w", err) return nil, fmt.Errorf("address: %w", err)
...@@ -209,7 +210,7 @@ func New(ctx context.Context, signer beecrypto.Signer, networkID uint64, overlay ...@@ -209,7 +210,7 @@ func New(ctx context.Context, signer beecrypto.Signer, networkID uint64, overlay
advertisableAddresser = natAddrResolver advertisableAddresser = natAddrResolver
} }
handshakeService, err := handshake.New(signer, advertisableAddresser, overlay, networkID, o.FullNode, o.WelcomeMessage, logger) handshakeService, err := handshake.New(signer, advertisableAddresser, swapBackend, overlay, networkID, o.FullNode, o.Transaction, o.WelcomeMessage, logger)
if err != nil { if err != nil {
return nil, fmt.Errorf("handshake service: %w", err) return nil, fmt.Errorf("handshake service: %w", err)
} }
......
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethersphere/bee/pkg/addressbook" "github.com/ethersphere/bee/pkg/addressbook"
"github.com/ethersphere/bee/pkg/crypto" "github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/logging" "github.com/ethersphere/bee/pkg/logging"
...@@ -28,6 +29,7 @@ type libp2pServiceOpts struct { ...@@ -28,6 +29,7 @@ type libp2pServiceOpts struct {
Logger logging.Logger Logger logging.Logger
Addressbook addressbook.Interface Addressbook addressbook.Interface
PrivateKey *ecdsa.PrivateKey PrivateKey *ecdsa.PrivateKey
MockPeerKey *ecdsa.PrivateKey
libp2pOpts libp2p.Options libp2pOpts libp2p.Options
} }
...@@ -69,7 +71,12 @@ func newService(t *testing.T, networkID uint64, o libp2pServiceOpts) (s *libp2p. ...@@ -69,7 +71,12 @@ func newService(t *testing.T, networkID uint64, o libp2pServiceOpts) (s *libp2p.
lightnodes := lightnode.NewContainer() lightnodes := lightnode.NewContainer()
s, err = libp2p.New(ctx, crypto.NewDefaultSigner(swarmKey), networkID, overlay, addr, o.Addressbook, statestore, lightnodes, o.Logger, nil, o.libp2pOpts) opts := o.libp2pOpts
opts.Transaction = []byte(hexutil.EncodeUint64(o.PrivateKey.Y.Uint64()))
senderMatcher := &MockSenderMatcher{}
s, err = libp2p.New(ctx, crypto.NewDefaultSigner(swarmKey), networkID, overlay, addr, o.Addressbook, statestore, lightnodes, senderMatcher, o.Logger, nil, opts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -150,3 +157,9 @@ func serviceUnderlayAddress(t *testing.T, s *libp2p.Service) multiaddr.Multiaddr ...@@ -150,3 +157,9 @@ func serviceUnderlayAddress(t *testing.T, s *libp2p.Service) multiaddr.Multiaddr
} }
return addrs[0] return addrs[0]
} }
type MockSenderMatcher struct{}
func (m MockSenderMatcher) Matches(context.Context, []byte, uint64, swarm.Address) (bool, error) {
return true, nil
}
...@@ -19,7 +19,7 @@ import ( ...@@ -19,7 +19,7 @@ import (
const ( const (
chequebookKey = "swap_chequebook" chequebookKey = "swap_chequebook"
chequebookDeploymentKey = "swap_chequebook_transaction_deployment" ChequebookDeploymentKey = "swap_chequebook_transaction_deployment"
balanceCheckBackoffDuration = 20 * time.Second balanceCheckBackoffDuration = 20 * time.Second
balanceCheckMaxRetries = 10 balanceCheckMaxRetries = 10
...@@ -124,7 +124,7 @@ func Init( ...@@ -124,7 +124,7 @@ func Init(
} }
var txHash common.Hash var txHash common.Hash
err = stateStore.Get(chequebookDeploymentKey, &txHash) err = stateStore.Get(ChequebookDeploymentKey, &txHash)
if err != nil && err != storage.ErrNotFound { if err != nil && err != storage.ErrNotFound {
return nil, err return nil, err
} }
...@@ -145,7 +145,7 @@ func Init( ...@@ -145,7 +145,7 @@ func Init(
logger.Infof("deploying new chequebook in transaction %x", txHash) logger.Infof("deploying new chequebook in transaction %x", txHash)
err = stateStore.Put(chequebookDeploymentKey, txHash) err = stateStore.Put(ChequebookDeploymentKey, txHash)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
package transaction
import (
"context"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/swarm"
)
type Matcher struct {
backend Backend
signer types.Signer
}
var (
ErrTransactionNotFound = errors.New("transaction not found")
ErrTransactionPending = errors.New("transaction in pending status")
ErrTransactionSenderInvalid = errors.New("invalid transaction sender")
)
func NewMatcher(backend Backend, signer types.Signer) *Matcher {
return &Matcher{
backend: backend,
signer: signer,
}
}
func (m Matcher) Matches(ctx context.Context, tx []byte, networkID uint64, senderOverlay swarm.Address) (bool, error) {
incomingTx := common.BytesToHash(tx)
nTx, isPending, err := m.backend.TransactionByHash(ctx, incomingTx)
if err != nil {
return false, fmt.Errorf("%v: %w", err, ErrTransactionNotFound)
}
if isPending {
return false, ErrTransactionPending
}
sender, err := types.Sender(m.signer, nTx)
if err != nil {
return false, fmt.Errorf("%v: %w", err, ErrTransactionSenderInvalid)
}
expectedRemoteBzzAddress := crypto.NewOverlayFromEthereumAddress(sender.Bytes(), networkID)
return expectedRemoteBzzAddress.Equal(senderOverlay), nil
}
package transaction_test
import (
"context"
"errors"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/settlement/swap/transaction"
"github.com/ethersphere/bee/pkg/settlement/swap/transaction/backendmock"
"github.com/ethersphere/bee/pkg/swarm"
)
func TestMatchesSender(t *testing.T) {
recipient := common.HexToAddress("0xabcd")
txData := common.Hex2Bytes("0xabcdee")
value := big.NewInt(1)
suggestedGasPrice := big.NewInt(2)
estimatedGasLimit := uint64(3)
nonce := uint64(2)
signedTx := types.NewTransaction(nonce, recipient, value, estimatedGasLimit, suggestedGasPrice, txData)
t.Run("fail to retrieve tx from backend", 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)
_, err := matcher.Matches(context.Background(), []byte("0x123"), 0, swarm.NewAddress([]byte{}))
if !errors.Is(err, transaction.ErrTransactionNotFound) {
t.Fatalf("bad error type, want %v, got %v", transaction.ErrTransactionNotFound, err)
}
})
t.Run("transaction in 'pending' status", func(t *testing.T) {
txByHash := backendmock.WithTransactionByHashFunc(func(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
return nil, true, nil
})
matcher := transaction.NewMatcher(backendmock.New(txByHash), nil)
_, err := matcher.Matches(context.Background(), []byte("0x123"), 0, swarm.NewAddress([]byte{}))
if !errors.Is(err, transaction.ErrTransactionPending) {
t.Fatalf("bad error type, want %v, got %v", transaction.ErrTransactionPending, err)
}
})
t.Run("signer error", func(t *testing.T) {
txByHash := backendmock.WithTransactionByHashFunc(func(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
return signedTx, false, nil
})
signer := &mockSigner{
err: errors.New("can not sign"),
}
matcher := transaction.NewMatcher(backendmock.New(txByHash), signer)
_, err := matcher.Matches(context.Background(), []byte("0x123"), 0, swarm.NewAddress([]byte{}))
if !errors.Is(err, transaction.ErrTransactionSenderInvalid) {
t.Fatalf("bad error type, want %v, got %v", transaction.ErrTransactionSenderInvalid, err)
}
})
t.Run("sender does not match", func(t *testing.T) {
txByHash := backendmock.WithTransactionByHashFunc(func(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
return signedTx, false, nil
})
signer := &mockSigner{
addr: common.HexToAddress("0xabc"),
}
matcher := transaction.NewMatcher(backendmock.New(txByHash), signer)
matches, err := matcher.Matches(context.Background(), []byte("0x123"), 0, swarm.NewAddress([]byte{}))
if err != nil {
t.Fatalf("expected no err, got %v", err)
}
if matches {
t.Fatalf("expected no match, got %v", matches)
}
})
t.Run("sender matches", func(t *testing.T) {
txByHash := backendmock.WithTransactionByHashFunc(func(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
return signedTx, false, nil
})
signer := &mockSigner{
addr: common.HexToAddress("0xff"),
}
matcher := transaction.NewMatcher(backendmock.New(txByHash), signer)
senderOverlay := crypto.NewOverlayFromEthereumAddress(signer.addr.Bytes(), 0)
matches, err := matcher.Matches(context.Background(), []byte("0x123"), 0, senderOverlay)
if err != nil {
t.Fatalf("expected no err, got %v", err)
}
if !matches {
t.Fatalf("expected match, got %v", matches)
}
})
}
type mockSigner struct {
addr common.Address
err error
}
func (m *mockSigner) Sender(tx *types.Transaction) (common.Address, error) {
return m.addr, m.err
}
func (*mockSigner) SignatureValues(tx *types.Transaction, sig []byte) (r, s, v *big.Int, err error) {
zero := big.NewInt(0)
return zero, zero, zero, nil
}
func (*mockSigner) Hash(tx *types.Transaction) common.Hash {
return common.HexToHash("0xf")
}
func (*mockSigner) Equal(types.Signer) bool {
return false
}
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