github.com/Ethersocial/go-esn@v0.3.7/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/ethersocial/go-esn/common" 25 "github.com/ethersocial/go-esn/common/hexutil" 26 "github.com/ethersocial/go-esn/swarm/storage" 27 "github.com/ethersocial/go-esn/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(updateAddr storage.Address, chunkdata []byte) error { 175 // for update chunk layout see Request definition 176 177 //deserialize the feed update portion 178 if err := r.Update.binaryGet(chunkdata[:len(chunkdata)-signatureLength]); err != nil { 179 return err 180 } 181 182 // Extract the signature 183 var signature *Signature 184 cursor := r.Update.binaryLength() 185 sigdata := chunkdata[cursor : cursor+signatureLength] 186 if len(sigdata) > 0 { 187 signature = &Signature{} 188 copy(signature[:], sigdata) 189 } 190 191 r.Signature = signature 192 r.idAddr = updateAddr 193 r.binaryData = chunkdata 194 195 return nil 196 197 } 198 199 // FromValues deserializes this instance from a string key-value store 200 // useful to parse query strings 201 func (r *Request) FromValues(values Values, data []byte) error { 202 signatureBytes, err := hexutil.Decode(values.Get("signature")) 203 if err != nil { 204 r.Signature = nil 205 } else { 206 if len(signatureBytes) != signatureLength { 207 return NewError(ErrInvalidSignature, "Incorrect signature length") 208 } 209 r.Signature = new(Signature) 210 copy(r.Signature[:], signatureBytes) 211 } 212 err = r.Update.FromValues(values, data) 213 if err != nil { 214 return err 215 } 216 r.idAddr = r.Addr() 217 return err 218 } 219 220 // AppendValues serializes this structure into the provided string key-value store 221 // useful to build query strings 222 func (r *Request) AppendValues(values Values) []byte { 223 if r.Signature != nil { 224 values.Set("signature", hexutil.Encode(r.Signature[:])) 225 } 226 return r.Update.AppendValues(values) 227 } 228 229 // fromJSON takes an update request JSON and populates an UpdateRequest 230 func (r *Request) fromJSON(j *updateRequestJSON) error { 231 232 r.ID = j.ID 233 r.Header.Version = j.ProtocolVersion 234 235 var err error 236 if j.Data != "" { 237 r.data, err = hexutil.Decode(j.Data) 238 if err != nil { 239 return NewError(ErrInvalidValue, "Cannot decode data") 240 } 241 } 242 243 if j.Signature != "" { 244 sigBytes, err := hexutil.Decode(j.Signature) 245 if err != nil || len(sigBytes) != signatureLength { 246 return NewError(ErrInvalidSignature, "Cannot decode signature") 247 } 248 r.Signature = new(Signature) 249 r.idAddr = r.Addr() 250 copy(r.Signature[:], sigBytes) 251 } 252 return nil 253 } 254 255 // UnmarshalJSON takes a JSON structure stored in a byte array and populates the Request object 256 // Implements json.Unmarshaler interface 257 func (r *Request) UnmarshalJSON(rawData []byte) error { 258 var requestJSON updateRequestJSON 259 if err := json.Unmarshal(rawData, &requestJSON); err != nil { 260 return err 261 } 262 return r.fromJSON(&requestJSON) 263 } 264 265 // MarshalJSON takes an update request and encodes it as a JSON structure into a byte array 266 // Implements json.Marshaler interface 267 func (r *Request) MarshalJSON() (rawData []byte, err error) { 268 var signatureString, dataString string 269 if r.Signature != nil { 270 signatureString = hexutil.Encode(r.Signature[:]) 271 } 272 if r.data != nil { 273 dataString = hexutil.Encode(r.data) 274 } 275 276 requestJSON := &updateRequestJSON{ 277 ID: r.ID, 278 ProtocolVersion: r.Header.Version, 279 Data: dataString, 280 Signature: signatureString, 281 } 282 283 return json.Marshal(requestJSON) 284 }