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 }