Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 12 commits into from
May 13, 2021
2 changes: 2 additions & 0 deletions cmd/bee/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const (
optionNameSwapFactoryAddress = "swap-factory-address"
optionNameSwapInitialDeposit = "swap-initial-deposit"
optionNameSwapEnable = "swap-enable"
optionNameTransaction = "transaction"
optionNameFullNode = "full-node"
optionNamePostageContractAddress = "postage-stamp-address"
optionNamePriceOracleAddress = "price-oracle-address"
Expand Down Expand Up @@ -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(optionNameTransaction, "", "transaction hash")
anatollupacescu marked this conversation as resolved.
Show resolved Hide resolved
}

func newLogger(cmd *cobra.Command, verbosity string) (logging.Logger, error) {
Expand Down
1 change: 1 addition & 0 deletions cmd/bee/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(optionNameTransaction),
PostageContractAddress: c.config.GetString(optionNamePostageContractAddress),
PriceOracleAddress: c.config.GetString(optionNamePriceOracleAddress),
})
Expand Down
27 changes: 26 additions & 1 deletion pkg/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -127,6 +128,7 @@ type Options struct {
SwapInitialDeposit string
SwapEnable bool
FullNodeMode bool
Transaction string
PostageContractAddress string
PriceOracleAddress string
}
Expand Down Expand Up @@ -273,14 +275,22 @@ 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,
EnableQUIC: o.EnableQUIC,
Standalone: o.Standalone,
WelcomeMessage: o.WelcomeMessage,
FullNode: o.FullNodeMode,
Transaction: txHash,
})
if err != nil {
return nil, fmt.Errorf("p2p service: %w", err)
Expand Down Expand Up @@ -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) (string, error) {
if len(transaction) == 32 {
logger.Info("using the provided transaction hash")
return transaction, nil
}

var txHash common.Hash
key := chequebook.ChequebookDeploymentKey
if err := stateStore.Get(key, &txHash); err != nil {
return "", err
}

return txHash.String(), nil
}
24 changes: 23 additions & 1 deletion pkg/p2p/libp2p/internal/handshake/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
Expand All @@ -59,12 +62,18 @@ type AdvertisableAddressResolver interface {
Resolve(observedAdddress ma.Multiaddr) (ma.Multiaddr, error)
}

type SenderMatcher interface {
Matches(ctx context.Context, tx string, 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 string
networkID uint64
welcomeMessage atomic.Value
receivedHandshakes map[libp2ppeer.ID]struct{}
Expand All @@ -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 string, welcomeMessage string, logger logging.Logger) (*Service, error) {
if len(welcomeMessage) > MaxWelcomeMessageLength {
return nil, ErrWelcomeMessageLength
}
Expand All @@ -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),
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
anatollupacescu marked this conversation as resolved.
Show resolved Hide resolved

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,
Expand Down
67 changes: 57 additions & 10 deletions pkg/p2p/libp2p/internal/handshake/handshake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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, "", testWelcomeMessage, logger)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -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, "", LongMessage, logger)
if err == nil || err.Error() != expectedErr.Error() {
t.Fatal("expected:", expectedErr, "got:", err)
}
Expand Down Expand Up @@ -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, "", "", logger)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -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, "", "", logger)
if err != nil {
t.Fatal(err)
}
Expand All @@ -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, "", "", logger)
if err != nil {
t.Fatal(err)
}
Expand All @@ -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, "", "", logger)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -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, "", "", logger)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -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, "", "", logger)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -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, "", "", logger)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -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, "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, "", "", logger)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -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, string, uint64, swarm.Address) (bool, error) {
return m.v, nil
}
Loading