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 (
optionNameSwapFactoryAddress = "swap-factory-address"
optionNameSwapInitialDeposit = "swap-initial-deposit"
optionNameSwapEnable = "swap-enable"
optionNameTransactionHash = "transaction"
optionNameFullNode = "full-node"
optionNamePostageContractAddress = "postage-stamp-address"
optionNamePriceOracleAddress = "price-oracle-address"
......@@ -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().String(optionNamePostageContractAddress, "", "postage stamp contract 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) {
......
......@@ -148,6 +148,7 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz
SwapInitialDeposit: c.config.GetString(optionNameSwapInitialDeposit),
SwapEnable: c.config.GetBool(optionNameSwapEnable),
FullNodeMode: fullNode,
Transaction: c.config.GetString(optionNameTransactionHash),
PostageContractAddress: c.config.GetString(optionNamePostageContractAddress),
PriceOracleAddress: c.config.GetString(optionNamePriceOracleAddress),
})
......
......@@ -21,6 +21,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethersphere/bee/pkg/accounting"
"github.com/ethersphere/bee/pkg/addressbook"
......@@ -127,6 +128,7 @@ type Options struct {
SwapInitialDeposit string
SwapEnable bool
FullNodeMode bool
Transaction string
PostageContractAddress string
PriceOracleAddress string
}
......@@ -273,7 +275,14 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
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,
NATAddr: o.NATAddr,
EnableWS: o.EnableWS,
......@@ -281,6 +290,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
Standalone: o.Standalone,
WelcomeMessage: o.WelcomeMessage,
FullNode: o.FullNodeMode,
Transaction: txHash,
})
if err != nil {
return nil, fmt.Errorf("p2p service: %w", err)
......@@ -781,3 +791,18 @@ func (e *multiError) add(err error) {
func (e *multiError) hasErrors() bool {
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 (
// ErrInvalidSyn is returned if observable address in ack is not a valid..
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 = fmt.Errorf("handshake welcome message longer than maximum of %d characters", MaxWelcomeMessageLength)
)
......@@ -59,12 +62,18 @@ type AdvertisableAddressResolver interface {
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.
type Service struct {
signer crypto.Signer
advertisableAddresser AdvertisableAddressResolver
senderMatcher SenderMatcher
overlay swarm.Address
fullNode bool
transaction []byte
networkID uint64
welcomeMessage atomic.Value
receivedHandshakes map[libp2ppeer.ID]struct{}
......@@ -89,7 +98,7 @@ func (i *Info) LightString() string {
}
// 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 {
return nil, ErrWelcomeMessageLength
}
......@@ -100,6 +109,8 @@ func New(signer crypto.Signer, advertisableAddresser AdvertisableAddressResolver
overlay: overlay,
networkID: networkID,
fullNode: fullNode,
transaction: transaction,
senderMatcher: isSender,
receivedHandshakes: make(map[libp2ppeer.ID]struct{}),
logger: logger,
Notifiee: new(network.NoopNotifiee),
......@@ -171,6 +182,7 @@ func (s *Service) Handshake(ctx context.Context, stream p2p.Stream, peerMultiadd
},
NetworkID: s.networkID,
FullNode: s.fullNode,
Transaction: s.transaction,
WelcomeMessage: welcomeMessage,
}); err != nil {
return nil, fmt.Errorf("write ack message: %w", err)
......@@ -250,6 +262,7 @@ func (s *Service) Handle(ctx context.Context, stream p2p.Stream, remoteMultiaddr
},
NetworkID: s.networkID,
FullNode: s.fullNode,
Transaction: s.transaction,
WelcomeMessage: welcomeMessage,
},
}); err != nil {
......@@ -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())
}
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{
BzzAddress: remoteBzzAddress,
FullNode: ack.FullNode,
......
......@@ -19,6 +19,7 @@ import (
"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/protobuf"
"github.com/ethersphere/bee/pkg/swarm"
libp2ppeer "github.com/libp2p/go-libp2p-core/peer"
ma "github.com/multiformats/go-multiaddr"
......@@ -91,8 +92,9 @@ func TestHandshake(t *testing.T) {
}
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 {
t.Fatal(err)
}
......@@ -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."
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() {
t.Fatal("expected:", expectedErr, "got:", err)
}
......@@ -368,7 +370,7 @@ func TestHandshake(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 {
t.Fatal(err)
}
......@@ -425,7 +427,7 @@ func TestHandshake(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 {
t.Fatal(err)
}
......@@ -444,7 +446,7 @@ func TestHandshake(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 {
t.Fatal(err)
}
......@@ -471,7 +473,7 @@ func TestHandshake(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 {
t.Fatal(err)
}
......@@ -500,7 +502,7 @@ func TestHandshake(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 {
t.Fatal(err)
}
......@@ -539,7 +541,7 @@ func TestHandshake(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 {
t.Fatal(err)
}
......@@ -601,7 +603,7 @@ func TestHandshake(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 {
t.Fatal(err)
}
......@@ -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) {
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 {
t.Fatal(err)
}
......@@ -693,3 +732,11 @@ func (a *AdvertisableAddresserMock) Resolve(observedAdddress ma.Multiaddr) (ma.M
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 {
Address *BzzAddress `protobuf:"bytes,1,opt,name=Address,proto3" json:"Address,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"`
Transaction []byte `protobuf:"bytes,4,opt,name=Transaction,proto3" json:"Transaction,omitempty"`
WelcomeMessage string `protobuf:"bytes,99,opt,name=WelcomeMessage,proto3" json:"WelcomeMessage,omitempty"`
}
......@@ -127,6 +128,13 @@ func (m *Ack) GetFullNode() bool {
return false
}
func (m *Ack) GetTransaction() []byte {
if m != nil {
return m.Transaction
}
return nil
}
func (m *Ack) GetWelcomeMessage() string {
if m != nil {
return m.WelcomeMessage
......@@ -256,26 +264,28 @@ func init() {
func init() { proto.RegisterFile("handshake.proto", fileDescriptor_a77305914d5d202f) }
var fileDescriptor_a77305914d5d202f = []byte{
// 303 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcf, 0x48, 0xcc, 0x4b,
0x29, 0xce, 0x48, 0xcc, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x84, 0x0b, 0x28,
0x19, 0x72, 0x31, 0x07, 0x57, 0xe6, 0x09, 0x69, 0x71, 0x09, 0xf8, 0x27, 0x15, 0xa7, 0x16, 0x95,
0xa5, 0xa6, 0x84, 0xe6, 0xa5, 0xa4, 0x16, 0xe5, 0x24, 0x56, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0xf0,
0x04, 0x61, 0x88, 0x2b, 0xcd, 0x60, 0xe4, 0x62, 0x76, 0x4c, 0xce, 0x16, 0xd2, 0xe7, 0x62, 0x77,
0x4c, 0x49, 0x29, 0x4a, 0x2d, 0x2e, 0x06, 0x2b, 0xe5, 0x36, 0x12, 0xd5, 0x43, 0x58, 0xe4, 0x54,
0x55, 0x05, 0x95, 0x0c, 0x82, 0xa9, 0x12, 0x92, 0xe1, 0xe2, 0xf4, 0x4b, 0x2d, 0x29, 0xcf, 0x2f,
0xca, 0xf6, 0x74, 0x91, 0x60, 0x52, 0x60, 0xd4, 0x60, 0x09, 0x42, 0x08, 0x08, 0x49, 0x71, 0x71,
0xb8, 0x95, 0xe6, 0xe4, 0xf8, 0xe5, 0xa7, 0xa4, 0x4a, 0x30, 0x2b, 0x30, 0x6a, 0x70, 0x04, 0xc1,
0xf9, 0x42, 0x6a, 0x5c, 0x7c, 0xe1, 0xa9, 0x39, 0xc9, 0xf9, 0xb9, 0xa9, 0xbe, 0xa9, 0xc5, 0xc5,
0x89, 0xe9, 0xa9, 0x12, 0xc9, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x68, 0xa2, 0x4a, 0x3e, 0x5c, 0x6c,
0xc1, 0x95, 0x79, 0x20, 0xc7, 0x29, 0x80, 0xfd, 0x05, 0x75, 0x18, 0x1f, 0x92, 0xc3, 0x82, 0x2b,
0xf3, 0x82, 0xc0, 0x5e, 0x56, 0x00, 0xfb, 0x02, 0xec, 0x0e, 0x54, 0x15, 0x8e, 0xc9, 0xd9, 0x41,
0x20, 0x29, 0xa5, 0x04, 0x2e, 0x2e, 0x84, 0x37, 0x40, 0xee, 0x43, 0x0b, 0x1a, 0x38, 0x1f, 0xe4,
0xb3, 0xe0, 0xcc, 0xf4, 0xbc, 0xc4, 0x92, 0xd2, 0xa2, 0x54, 0xb0, 0x89, 0x3c, 0x41, 0x08, 0x01,
0x21, 0x09, 0x2e, 0x76, 0xff, 0x32, 0x88, 0x46, 0x66, 0xb0, 0x1c, 0x8c, 0xeb, 0x24, 0x73, 0xe2,
0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70,
0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x4c, 0x05, 0x49, 0x49, 0x6c, 0xe0, 0xd8,
0x32, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x30, 0x28, 0x02, 0x6f, 0xc0, 0x01, 0x00, 0x00,
// 324 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x91, 0xdf, 0x4a, 0xf3, 0x30,
0x18, 0xc6, 0x97, 0x75, 0xec, 0xcf, 0xbb, 0xb1, 0xef, 0x23, 0x20, 0x14, 0x19, 0x25, 0xf4, 0x40,
0x8a, 0x07, 0x13, 0xf5, 0x0a, 0x36, 0x44, 0x10, 0x74, 0x83, 0x54, 0x11, 0x3c, 0x32, 0x6b, 0xc3,
0x36, 0x5a, 0x93, 0x91, 0x74, 0x93, 0xee, 0x2a, 0xbc, 0x1e, 0xaf, 0xc0, 0xc3, 0x1d, 0x7a, 0x28,
0xdb, 0x8d, 0x48, 0xe3, 0xd6, 0xce, 0x79, 0xf8, 0xfe, 0x9e, 0x27, 0xc9, 0xf3, 0xbc, 0x81, 0x7f,
0x13, 0x26, 0x42, 0x3d, 0x61, 0x11, 0xef, 0xce, 0x94, 0x4c, 0x24, 0x6e, 0xe4, 0xc0, 0x3d, 0x07,
0xcb, 0x4f, 0x05, 0x3e, 0x85, 0xff, 0xc3, 0x91, 0xe6, 0x6a, 0xc1, 0xc3, 0x07, 0x11, 0x72, 0x15,
0xb3, 0xd4, 0x46, 0x04, 0x79, 0x2d, 0xfa, 0x87, 0xbb, 0xef, 0x08, 0xac, 0x5e, 0x10, 0xe1, 0x33,
0xa8, 0xf5, 0xc2, 0x50, 0x71, 0xad, 0x8d, 0xb5, 0x79, 0x71, 0xd4, 0x2d, 0x1e, 0xea, 0x2f, 0x97,
0x5b, 0x91, 0xee, 0x5c, 0xb8, 0x03, 0x8d, 0x01, 0x4f, 0x5e, 0xa5, 0x8a, 0x6e, 0xae, 0xec, 0x32,
0x41, 0x5e, 0x85, 0x16, 0x00, 0x1f, 0x43, 0xfd, 0x7a, 0x1e, 0xc7, 0x03, 0x19, 0x72, 0xdb, 0x22,
0xc8, 0xab, 0xd3, 0x7c, 0xc6, 0x04, 0x9a, 0xf7, 0x8a, 0x09, 0xcd, 0x82, 0x64, 0x2a, 0x85, 0x5d,
0x31, 0xc9, 0xf6, 0x11, 0x3e, 0x81, 0xf6, 0x23, 0x8f, 0x03, 0xf9, 0xc2, 0xef, 0xb8, 0xd6, 0x6c,
0xcc, 0xed, 0x80, 0x20, 0xaf, 0x41, 0x0f, 0xa8, 0x7b, 0x0b, 0x55, 0x3f, 0x15, 0x59, 0x7c, 0x62,
0x9a, 0x6f, 0xa3, 0xb7, 0xf7, 0xa2, 0xfb, 0xa9, 0xa0, 0x66, 0x29, 0xc4, 0xf4, 0x34, 0x49, 0x7f,
0x3b, 0x7a, 0x41, 0x44, 0x33, 0xc9, 0x7d, 0x06, 0x28, 0x8a, 0x66, 0x0d, 0x0e, 0x96, 0x97, 0xcf,
0x59, 0x77, 0x7f, 0x3a, 0x16, 0x2c, 0x99, 0x2b, 0x6e, 0x6e, 0x6c, 0xd1, 0x02, 0x60, 0x1b, 0x6a,
0xc3, 0xc5, 0xcf, 0x41, 0xcb, 0x68, 0xbb, 0xb1, 0xdf, 0xf9, 0x58, 0x3b, 0x68, 0xb5, 0x76, 0xd0,
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) {
......@@ -337,6 +347,13 @@ func (m *Ack) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i--
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 {
i--
if m.FullNode {
......@@ -498,6 +515,10 @@ func (m *Ack) Size() (n int) {
if m.FullNode {
n += 2
}
l = len(m.Transaction)
if l > 0 {
n += 1 + l + sovHandshake(uint64(l))
}
l = len(m.WelcomeMessage)
if l > 0 {
n += 2 + l + sovHandshake(uint64(l))
......@@ -740,6 +761,40 @@ func (m *Ack) Unmarshal(dAtA []byte) error {
}
}
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:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field WelcomeMessage", wireType)
......
......@@ -16,6 +16,7 @@ message Ack {
BzzAddress Address = 1;
uint64 NetworkID = 2;
bool FullNode = 3;
bytes Transaction = 4;
string WelcomeMessage = 99;
}
......
......@@ -84,9 +84,10 @@ type Options struct {
Standalone bool
FullNode bool
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)
if err != nil {
return nil, fmt.Errorf("address: %w", err)
......@@ -209,7 +210,7 @@ func New(ctx context.Context, signer beecrypto.Signer, networkID uint64, overlay
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 {
return nil, fmt.Errorf("handshake service: %w", err)
}
......
......@@ -13,6 +13,7 @@ import (
"testing"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethersphere/bee/pkg/addressbook"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/logging"
......@@ -28,6 +29,7 @@ type libp2pServiceOpts struct {
Logger logging.Logger
Addressbook addressbook.Interface
PrivateKey *ecdsa.PrivateKey
MockPeerKey *ecdsa.PrivateKey
libp2pOpts libp2p.Options
}
......@@ -69,7 +71,12 @@ func newService(t *testing.T, networkID uint64, o libp2pServiceOpts) (s *libp2p.
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 {
t.Fatal(err)
}
......@@ -150,3 +157,9 @@ func serviceUnderlayAddress(t *testing.T, s *libp2p.Service) multiaddr.Multiaddr
}
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 (
const (
chequebookKey = "swap_chequebook"
chequebookDeploymentKey = "swap_chequebook_transaction_deployment"
ChequebookDeploymentKey = "swap_chequebook_transaction_deployment"
balanceCheckBackoffDuration = 20 * time.Second
balanceCheckMaxRetries = 10
......@@ -124,7 +124,7 @@ func Init(
}
var txHash common.Hash
err = stateStore.Get(chequebookDeploymentKey, &txHash)
err = stateStore.Get(ChequebookDeploymentKey, &txHash)
if err != nil && err != storage.ErrNotFound {
return nil, err
}
......@@ -145,7 +145,7 @@ func Init(
logger.Infof("deploying new chequebook in transaction %x", txHash)
err = stateStore.Put(chequebookDeploymentKey, txHash)
err = stateStore.Put(ChequebookDeploymentKey, txHash)
if err != nil {
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