github.com/xxRanger/go-ethereum@v1.8.23/swarm/storage/feed/request.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package feed 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "hash" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/common/hexutil" 26 "github.com/ethereum/go-ethereum/swarm/storage" 27 "github.com/ethereum/go-ethereum/swarm/storage/feed/lookup" 28 ) 29 30 // Request represents a request to sign or signed feed update message 31 type Request struct { 32 Update // actual content that will be put on the chunk, less signature 33 Signature *Signature 34 idAddr storage.Address // cached chunk address for the update (not serialized, for internal use) 35 binaryData []byte // cached serialized data (does not get serialized again!, for efficiency/internal use) 36 } 37 38 // updateRequestJSON represents a JSON-serialized UpdateRequest 39 type updateRequestJSON struct { 40 ID 41 ProtocolVersion uint8 `json:"protocolVersion"` 42 Data string `json:"data,omitempty"` 43 Signature string `json:"signature,omitempty"` 44 } 45 46 // Request layout 47 // Update bytes 48 // SignatureLength bytes 49 const minimumSignedUpdateLength = minimumUpdateDataLength + signatureLength 50 51 // NewFirstRequest returns a ready to sign request to publish a first feed update 52 func NewFirstRequest(topic Topic) *Request { 53 54 request := new(Request) 55 56 // get the current time 57 now := TimestampProvider.Now().Time 58 request.Epoch = lookup.GetFirstEpoch(now) 59 request.Feed.Topic = topic 60 request.Header.Version = ProtocolVersion 61 62 return request 63 } 64 65 // SetData stores the payload data the feed update will be updated with 66 func (r *Request) SetData(data []byte) { 67 r.data = data 68 r.Signature = nil 69 } 70 71 // IsUpdate returns true if this request models a signed update or otherwise it is a signature request 72 func (r *Request) IsUpdate() bool { 73 return r.Signature != nil 74 } 75 76 // Verify checks that signatures are valid 77 func (r *Request) Verify() (err error) { 78 if len(r.data) == 0 { 79 return NewError(ErrInvalidValue, "Update does not contain data") 80 } 81 if r.Signature == nil { 82 return NewError(ErrInvalidSignature, "Missing signature field") 83 } 84 85 digest, err := r.GetDigest() 86 if err != nil { 87 return err 88 } 89 90 // get the address of the signer (which also checks that it's a valid signature) 91 r.Feed.User, err = getUserAddr(digest, *r.Signature) 92 if err != nil { 93 return err 94 } 95 96 // check that the lookup information contained in the chunk matches the updateAddr (chunk search key) 97 // that was used to retrieve this chunk 98 // if this validation fails, someone forged a chunk. 99 if !bytes.Equal(r.idAddr, r.Addr()) { 100 return NewError(ErrInvalidSignature, "Signature address does not match with update user address") 101 } 102 103 return nil 104 } 105 106 // Sign executes the signature to validate the update message 107 func (r *Request) Sign(signer Signer) error { 108 r.Feed.User = signer.Address() 109 r.binaryData = nil //invalidate serialized data 110 digest, err := r.GetDigest() // computes digest and serializes into .binaryData 111 if err != nil { 112 return err 113 } 114 115 signature, err := signer.Sign(digest) 116 if err != nil { 117 return err 118 } 119 120 // Although the Signer interface returns the public address of the signer, 121 // recover it from the signature to see if they match 122 userAddr, err := getUserAddr(digest, signature) 123 if err != nil { 124 return NewError(ErrInvalidSignature, "Error verifying signature") 125 } 126 127 if userAddr != signer.Address() { // sanity check to make sure the Signer is declaring the same address used to sign! 128 return NewError(ErrInvalidSignature, "Signer address does not match update user address") 129 } 130 131 r.Signature = &signature 132 r.idAddr = r.Addr() 133 return nil 134 } 135 136 // GetDigest creates the feed update digest used in signatures 137 // the serialized payload is cached in .binaryData 138 func (r *Request) GetDigest() (result common.Hash, err error) { 139 hasher := hashPool.Get().(hash.Hash) 140 defer hashPool.Put(hasher) 141 hasher.Reset() 142 dataLength := r.Update.binaryLength() 143 if r.binaryData == nil { 144 r.binaryData = make([]byte, dataLength+signatureLength) 145 if err := r.Update.binaryPut(r.binaryData[:dataLength]); err != nil { 146 return result, err 147 } 148 } 149 hasher.Write(r.binaryData[:dataLength]) //everything except the signature. 150 151 return common.BytesToHash(hasher.Sum(nil)), nil 152 } 153 154 // create an update chunk. 155 func (r *Request) toChunk() (storage.Chunk, error) { 156 157 // Check that the update is signed and serialized 158 // For efficiency, data is serialized during signature and cached in 159 // the binaryData field when computing the signature digest in .getDigest() 160 if r.Signature == nil || r.binaryData == nil { 161 return nil, NewError(ErrInvalidSignature, "toChunk called without a valid signature or payload data. Call .Sign() first.") 162 } 163 164 updateLength := r.Update.binaryLength() 165 166 // signature is the last item in the chunk data 167 copy(r.binaryData[updateLength:], r.Signature[:]) 168 169 chunk := storage.NewChunk(r.idAddr, r.binaryData) 170 return chunk, nil 171 } 172 173 // fromChunk populates this structure from chunk data. It does not verify the signature is valid. 174 func (r *Request) fromChunk(chunk storage.Chunk) error { 175 // for update chunk layout see Request definition 176 177 chunkdata := chunk.Data() 178 179 //deserialize the feed update portion 180 if err := r.Update.binaryGet(chunkdata[:len(chunkdata)-signatureLength]); err != nil { 181 return err 182 } 183 184 // Extract the signature 185 var signature *Signature 186 cursor := r.Update.binaryLength() 187 sigdata := chunkdata[cursor : cursor+signatureLength] 188 if len(sigdata) > 0 { 189 signature = &Signature{} 190 copy(signature[:], sigdata) 191 } 192 193 r.Signature = signature 194 r.idAddr = chunk.Address() 195 r.binaryData = chunkdata 196 197 return nil 198 199 } 200 201 // FromValues deserializes this instance from a string key-value store 202 // useful to parse query strings 203 func (r *Request) FromValues(values Values, data []byte) error { 204 signatureBytes, err := hexutil.Decode(values.Get("signature")) 205 if err != nil { 206 r.Signature = nil 207 } else { 208 if len(signatureBytes) != signatureLength { 209 return NewError(ErrInvalidSignature, "Incorrect signature length") 210 } 211 r.Signature = new(Signature) 212 copy(r.Signature[:], signatureBytes) 213 } 214 err = r.Update.FromValues(values, data) 215 if err != nil { 216 return err 217 } 218 r.idAddr = r.Addr() 219 return err 220 } 221 222 // AppendValues serializes this structure into the provided string key-value store 223 // useful to build query strings 224 func (r *Request) AppendValues(values Values) []byte { 225 if r.Signature != nil { 226 values.Set("signature", hexutil.Encode(r.Signature[:])) 227 } 228 return r.Update.AppendValues(values) 229 } 230 231 // fromJSON takes an update request JSON and populates an UpdateRequest 232 func (r *Request) fromJSON(j *updateRequestJSON) error { 233 234 r.ID = j.ID 235 r.Header.Version = j.ProtocolVersion 236 237 var err error 238 if j.Data != "" { 239 r.data, err = hexutil.Decode(j.Data) 240 if err != nil { 241 return NewError(ErrInvalidValue, "Cannot decode data") 242 } 243 } 244 245 if j.Signature != "" { 246 sigBytes, err := hexutil.Decode(j.Signature) 247 if err != nil || len(sigBytes) != signatureLength { 248 return NewError(ErrInvalidSignature, "Cannot decode signature") 249 } 250 r.Signature = new(Signature) 251 r.idAddr = r.Addr() 252 copy(r.Signature[:], sigBytes) 253 } 254 return nil 255 } 256 257 // UnmarshalJSON takes a JSON structure stored in a byte array and populates the Request object 258 // Implements json.Unmarshaler interface 259 func (r *Request) UnmarshalJSON(rawData []byte) error { 260 var requestJSON updateRequestJSON 261 if err := json.Unmarshal(rawData, &requestJSON); err != nil { 262 return err 263 } 264 return r.fromJSON(&requestJSON) 265 } 266 267 // MarshalJSON takes an update request and encodes it as a JSON structure into a byte array 268 // Implements json.Marshaler interface 269 func (r *Request) MarshalJSON() (rawData []byte, err error) { 270 var signatureString, dataString string 271 if r.Signature != nil { 272 signatureString = hexutil.Encode(r.Signature[:]) 273 } 274 if r.data != nil { 275 dataString = hexutil.Encode(r.data) 276 } 277 278 requestJSON := &updateRequestJSON{ 279 ID: r.ID, 280 ProtocolVersion: r.Header.Version, 281 Data: dataString, 282 Signature: signatureString, 283 } 284 285 return json.Marshal(requestJSON) 286 }