github.com/ethersphere/bee/v2@v2.2.0/pkg/bzz/address.go (about)

     1  // Copyright 2020 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package bzz exposes the data structure and operations
     6  // necessary on the bzz.Address type which used in the handshake
     7  // protocol, address-book and hive protocol.
     8  package bzz
     9  
    10  import (
    11  	"bytes"
    12  	"encoding/base64"
    13  	"encoding/binary"
    14  	"encoding/json"
    15  	"errors"
    16  	"fmt"
    17  
    18  	"github.com/ethereum/go-ethereum/common"
    19  	"github.com/ethersphere/bee/v2/pkg/crypto"
    20  	"github.com/ethersphere/bee/v2/pkg/swarm"
    21  
    22  	ma "github.com/multiformats/go-multiaddr"
    23  )
    24  
    25  var ErrInvalidAddress = errors.New("invalid address")
    26  
    27  // Address represents the bzz address in swarm.
    28  // It consists of a peers underlay (physical) address, overlay (topology) address and signature.
    29  // Signature is used to verify the `Overlay/Underlay` pair, as it is based on `underlay|networkID`, signed with the public key of Overlay address
    30  type Address struct {
    31  	Underlay        ma.Multiaddr
    32  	Overlay         swarm.Address
    33  	Signature       []byte
    34  	Nonce           []byte
    35  	EthereumAddress []byte
    36  }
    37  
    38  type addressJSON struct {
    39  	Overlay   string `json:"overlay"`
    40  	Underlay  string `json:"underlay"`
    41  	Signature string `json:"signature"`
    42  	Nonce     string `json:"transaction"`
    43  }
    44  
    45  func NewAddress(signer crypto.Signer, underlay ma.Multiaddr, overlay swarm.Address, networkID uint64, nonce []byte) (*Address, error) {
    46  	underlayBinary, err := underlay.MarshalBinary()
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  
    51  	signature, err := signer.Sign(generateSignData(underlayBinary, overlay.Bytes(), networkID))
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	return &Address{
    57  		Underlay:  underlay,
    58  		Overlay:   overlay,
    59  		Signature: signature,
    60  		Nonce:     nonce,
    61  	}, nil
    62  }
    63  
    64  func ParseAddress(underlay, overlay, signature, nonce []byte, validateOverlay bool, networkID uint64) (*Address, error) {
    65  	recoveredPK, err := crypto.Recover(signature, generateSignData(underlay, overlay, networkID))
    66  	if err != nil {
    67  		return nil, ErrInvalidAddress
    68  	}
    69  
    70  	if validateOverlay {
    71  		recoveredOverlay, err := crypto.NewOverlayAddress(*recoveredPK, networkID, nonce)
    72  		if err != nil {
    73  			return nil, ErrInvalidAddress
    74  		}
    75  		if !bytes.Equal(recoveredOverlay.Bytes(), overlay) {
    76  			return nil, ErrInvalidAddress
    77  		}
    78  	}
    79  
    80  	multiUnderlay, err := ma.NewMultiaddrBytes(underlay)
    81  	if err != nil {
    82  		return nil, ErrInvalidAddress
    83  	}
    84  
    85  	ethAddress, err := crypto.NewEthereumAddress(*recoveredPK)
    86  	if err != nil {
    87  		return nil, fmt.Errorf("extract blockchain address: %w: %w", err, ErrInvalidAddress)
    88  	}
    89  
    90  	return &Address{
    91  		Underlay:        multiUnderlay,
    92  		Overlay:         swarm.NewAddress(overlay),
    93  		Signature:       signature,
    94  		Nonce:           nonce,
    95  		EthereumAddress: ethAddress,
    96  	}, nil
    97  }
    98  
    99  func generateSignData(underlay, overlay []byte, networkID uint64) []byte {
   100  	networkIDBytes := make([]byte, 8)
   101  	binary.BigEndian.PutUint64(networkIDBytes, networkID)
   102  	signData := append([]byte("bee-handshake-"), underlay...)
   103  	signData = append(signData, overlay...)
   104  	return append(signData, networkIDBytes...)
   105  }
   106  
   107  func (a *Address) Equal(b *Address) bool {
   108  	if a == nil || b == nil {
   109  		return a == b
   110  	}
   111  
   112  	return a.Overlay.Equal(b.Overlay) && multiaddrEqual(a.Underlay, b.Underlay) && bytes.Equal(a.Signature, b.Signature) && bytes.Equal(a.Nonce, b.Nonce)
   113  }
   114  
   115  func multiaddrEqual(a, b ma.Multiaddr) bool {
   116  	if a == nil || b == nil {
   117  		return a == b
   118  	}
   119  
   120  	return a.Equal(b)
   121  }
   122  
   123  func (a *Address) MarshalJSON() ([]byte, error) {
   124  	return json.Marshal(&addressJSON{
   125  		Overlay:   a.Overlay.String(),
   126  		Underlay:  a.Underlay.String(),
   127  		Signature: base64.StdEncoding.EncodeToString(a.Signature),
   128  		Nonce:     common.Bytes2Hex(a.Nonce),
   129  	})
   130  }
   131  
   132  func (a *Address) UnmarshalJSON(b []byte) error {
   133  	v := &addressJSON{}
   134  	err := json.Unmarshal(b, v)
   135  	if err != nil {
   136  		return err
   137  	}
   138  
   139  	addr, err := swarm.ParseHexAddress(v.Overlay)
   140  	if err != nil {
   141  		return err
   142  	}
   143  
   144  	a.Overlay = addr
   145  
   146  	m, err := ma.NewMultiaddr(v.Underlay)
   147  	if err != nil {
   148  		return err
   149  	}
   150  
   151  	a.Underlay = m
   152  	a.Signature, err = base64.StdEncoding.DecodeString(v.Signature)
   153  	a.Nonce = common.Hex2Bytes(v.Nonce)
   154  	return err
   155  }
   156  
   157  func (a *Address) String() string {
   158  	return fmt.Sprintf("[Underlay: %v, Overlay %v, Signature %x, Transaction %x]", a.Underlay, a.Overlay, a.Signature, a.Nonce)
   159  }
   160  
   161  // ShortString returns shortened versions of bzz address in a format: [Overlay, Underlay]
   162  // It can be used for logging
   163  func (a *Address) ShortString() string {
   164  	return fmt.Sprintf("[Overlay: %s, Underlay: %s]", a.Overlay.String(), a.Underlay.String())
   165  }