github.com/daragao/go-ethereum@v1.8.14-0.20180809141559-45eaef243198/swarm/storage/mru/handler.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 // Handler is the API for Mutable Resources 18 // It enables creating, updating, syncing and retrieving resources and their update data 19 package mru 20 21 import ( 22 "bytes" 23 "context" 24 "fmt" 25 "sync" 26 "time" 27 "unsafe" 28 29 "github.com/ethereum/go-ethereum/swarm/log" 30 "github.com/ethereum/go-ethereum/swarm/storage" 31 ) 32 33 const chunkSize = 4096 // temporary until we implement FileStore in the resourcehandler 34 35 type Handler struct { 36 chunkStore *storage.NetStore 37 HashSize int 38 resources map[uint64]*resource 39 resourceLock sync.RWMutex 40 storeTimeout time.Duration 41 queryMaxPeriods uint32 42 } 43 44 // HandlerParams pass parameters to the Handler constructor NewHandler 45 // Signer and TimestampProvider are mandatory parameters 46 type HandlerParams struct { 47 QueryMaxPeriods uint32 48 } 49 50 // hashPool contains a pool of ready hashers 51 var hashPool sync.Pool 52 var minimumChunkLength int 53 54 // init initializes the package and hashPool 55 func init() { 56 hashPool = sync.Pool{ 57 New: func() interface{} { 58 return storage.MakeHashFunc(resourceHashAlgorithm)() 59 }, 60 } 61 if minimumMetadataLength < minimumUpdateDataLength { 62 minimumChunkLength = minimumMetadataLength 63 } else { 64 minimumChunkLength = minimumUpdateDataLength 65 } 66 } 67 68 // NewHandler creates a new Mutable Resource API 69 func NewHandler(params *HandlerParams) (*Handler, error) { 70 71 rh := &Handler{ 72 resources: make(map[uint64]*resource), 73 storeTimeout: defaultStoreTimeout, 74 queryMaxPeriods: params.QueryMaxPeriods, 75 } 76 77 for i := 0; i < hasherCount; i++ { 78 hashfunc := storage.MakeHashFunc(resourceHashAlgorithm)() 79 if rh.HashSize == 0 { 80 rh.HashSize = hashfunc.Size() 81 } 82 hashPool.Put(hashfunc) 83 } 84 85 return rh, nil 86 } 87 88 // SetStore sets the store backend for the Mutable Resource API 89 func (h *Handler) SetStore(store *storage.NetStore) { 90 h.chunkStore = store 91 } 92 93 // Validate is a chunk validation method 94 // If it looks like a resource update, the chunk address is checked against the ownerAddr of the update's signature 95 // It implements the storage.ChunkValidator interface 96 func (h *Handler) Validate(chunkAddr storage.Address, data []byte) bool { 97 98 dataLength := len(data) 99 if dataLength < minimumChunkLength { 100 return false 101 } 102 103 //metadata chunks have the first two bytes set to zero 104 if data[0] == 0 && data[1] == 0 && dataLength >= minimumMetadataLength { 105 //metadata chunk 106 rootAddr, _ := metadataHash(data) 107 valid := bytes.Equal(chunkAddr, rootAddr) 108 if !valid { 109 log.Debug(fmt.Sprintf("Invalid root metadata chunk with address: %s", chunkAddr.Hex())) 110 } 111 return valid 112 } 113 114 // if it is not a metadata chunk, check if it is a properly formatted update chunk with 115 // valid signature and proof of ownership of the resource it is trying 116 // to update 117 118 // First, deserialize the chunk 119 var r SignedResourceUpdate 120 if err := r.fromChunk(chunkAddr, data); err != nil { 121 log.Debug("Invalid resource chunk with address %s: %s ", chunkAddr.Hex(), err.Error()) 122 return false 123 } 124 125 // check that the lookup information contained in the chunk matches the updateAddr (chunk search key) 126 // that was used to retrieve this chunk 127 // if this validation fails, someone forged a chunk. 128 if !bytes.Equal(chunkAddr, r.updateHeader.UpdateAddr()) { 129 log.Debug("period,version,rootAddr contained in update chunk do not match updateAddr %s", chunkAddr.Hex()) 130 return false 131 } 132 133 // Verify signatures and that the signer actually owns the resource 134 // If it fails, it means either the signature is not valid, data is corrupted 135 // or someone is trying to update someone else's resource. 136 if err := r.Verify(); err != nil { 137 log.Debug("Invalid signature: %v", err) 138 return false 139 } 140 141 return true 142 } 143 144 // GetContent retrieves the data payload of the last synced update of the Mutable Resource 145 func (h *Handler) GetContent(rootAddr storage.Address) (storage.Address, []byte, error) { 146 rsrc := h.get(rootAddr) 147 if rsrc == nil || !rsrc.isSynced() { 148 return nil, nil, NewError(ErrNotFound, " does not exist or is not synced") 149 } 150 return rsrc.lastKey, rsrc.data, nil 151 } 152 153 // GetLastPeriod retrieves the period of the last synced update of the Mutable Resource 154 func (h *Handler) GetLastPeriod(rootAddr storage.Address) (uint32, error) { 155 rsrc := h.get(rootAddr) 156 if rsrc == nil { 157 return 0, NewError(ErrNotFound, " does not exist") 158 } else if !rsrc.isSynced() { 159 return 0, NewError(ErrNotSynced, " is not synced") 160 } 161 return rsrc.period, nil 162 } 163 164 // GetVersion retrieves the period of the last synced update of the Mutable Resource 165 func (h *Handler) GetVersion(rootAddr storage.Address) (uint32, error) { 166 rsrc := h.get(rootAddr) 167 if rsrc == nil { 168 return 0, NewError(ErrNotFound, " does not exist") 169 } else if !rsrc.isSynced() { 170 return 0, NewError(ErrNotSynced, " is not synced") 171 } 172 return rsrc.version, nil 173 } 174 175 // \TODO should be hashsize * branches from the chosen chunker, implement with FileStore 176 func (h *Handler) chunkSize() int64 { 177 return chunkSize 178 } 179 180 // New creates a new metadata chunk out of the request passed in. 181 func (h *Handler) New(ctx context.Context, request *Request) error { 182 183 // frequency 0 is invalid 184 if request.metadata.Frequency == 0 { 185 return NewError(ErrInvalidValue, "frequency cannot be 0 when creating a resource") 186 } 187 188 // make sure owner is set to something 189 if request.metadata.Owner == zeroAddr { 190 return NewError(ErrInvalidValue, "ownerAddr must be set to create a new metadata chunk") 191 } 192 193 // create the meta chunk and store it in swarm 194 chunk, metaHash, err := request.metadata.newChunk() 195 if err != nil { 196 return err 197 } 198 if request.metaHash != nil && !bytes.Equal(request.metaHash, metaHash) || 199 request.rootAddr != nil && !bytes.Equal(request.rootAddr, chunk.Addr) { 200 return NewError(ErrInvalidValue, "metaHash in UpdateRequest does not match actual metadata") 201 } 202 203 request.metaHash = metaHash 204 request.rootAddr = chunk.Addr 205 206 h.chunkStore.Put(ctx, chunk) 207 log.Debug("new resource", "name", request.metadata.Name, "startTime", request.metadata.StartTime, "frequency", request.metadata.Frequency, "owner", request.metadata.Owner) 208 209 // create the internal index for the resource and populate it with its metadata 210 rsrc := &resource{ 211 resourceUpdate: resourceUpdate{ 212 updateHeader: updateHeader{ 213 UpdateLookup: UpdateLookup{ 214 rootAddr: chunk.Addr, 215 }, 216 }, 217 }, 218 ResourceMetadata: request.metadata, 219 updated: time.Now(), 220 } 221 h.set(chunk.Addr, rsrc) 222 223 return nil 224 } 225 226 // NewUpdateRequest prepares an UpdateRequest structure with all the necessary information to 227 // just add the desired data and sign it. 228 // The resulting structure can then be signed and passed to Handler.Update to be verified and sent 229 func (h *Handler) NewUpdateRequest(ctx context.Context, rootAddr storage.Address) (updateRequest *Request, err error) { 230 231 if rootAddr == nil { 232 return nil, NewError(ErrInvalidValue, "rootAddr cannot be nil") 233 } 234 235 // Make sure we have a cache of the metadata chunk 236 rsrc, err := h.Load(ctx, rootAddr) 237 if err != nil { 238 return nil, err 239 } 240 241 now := TimestampProvider.Now() 242 243 updateRequest = new(Request) 244 updateRequest.period, err = getNextPeriod(rsrc.StartTime.Time, now.Time, rsrc.Frequency) 245 if err != nil { 246 return nil, err 247 } 248 249 if _, err = h.lookup(rsrc, LookupLatestVersionInPeriod(rsrc.rootAddr, updateRequest.period)); err != nil { 250 if err.(*Error).code != ErrNotFound { 251 return nil, err 252 } 253 // not finding updates means that there is a network error 254 // or that the resource really does not have updates in this period. 255 } 256 257 updateRequest.multihash = rsrc.multihash 258 updateRequest.rootAddr = rsrc.rootAddr 259 updateRequest.metaHash = rsrc.metaHash 260 updateRequest.metadata = rsrc.ResourceMetadata 261 262 // if we already have an update for this period then increment version 263 // resource object MUST be in sync for version to be correct, but we checked this earlier in the method already 264 if h.hasUpdate(rootAddr, updateRequest.period) { 265 updateRequest.version = rsrc.version + 1 266 } else { 267 updateRequest.version = 1 268 } 269 270 return updateRequest, nil 271 } 272 273 // Lookup retrieves a specific or latest version of the resource update with metadata chunk at params.Root 274 // Lookup works differently depending on the configuration of `LookupParams` 275 // See the `LookupParams` documentation and helper functions: 276 // `LookupLatest`, `LookupLatestVersionInPeriod` and `LookupVersion` 277 // When looking for the latest update, it starts at the next period after the current time. 278 // upon failure tries the corresponding keys of each previous period until one is found 279 // (or startTime is reached, in which case there are no updates). 280 func (h *Handler) Lookup(ctx context.Context, params *LookupParams) (*resource, error) { 281 282 rsrc := h.get(params.rootAddr) 283 if rsrc == nil { 284 return nil, NewError(ErrNothingToReturn, "resource not loaded") 285 } 286 return h.lookup(rsrc, params) 287 } 288 289 // LookupPrevious returns the resource before the one currently loaded in the resource cache 290 // This is useful where resource updates are used incrementally in contrast to 291 // merely replacing content. 292 // Requires a cached resource object to determine the current state of the resource. 293 func (h *Handler) LookupPrevious(ctx context.Context, params *LookupParams) (*resource, error) { 294 rsrc := h.get(params.rootAddr) 295 if rsrc == nil { 296 return nil, NewError(ErrNothingToReturn, "resource not loaded") 297 } 298 if !rsrc.isSynced() { 299 return nil, NewError(ErrNotSynced, "LookupPrevious requires synced resource.") 300 } else if rsrc.period == 0 { 301 return nil, NewError(ErrNothingToReturn, " not found") 302 } 303 var version, period uint32 304 if rsrc.version > 1 { 305 version = rsrc.version - 1 306 period = rsrc.period 307 } else if rsrc.period == 1 { 308 return nil, NewError(ErrNothingToReturn, "Current update is the oldest") 309 } else { 310 version = 0 311 period = rsrc.period - 1 312 } 313 return h.lookup(rsrc, NewLookupParams(rsrc.rootAddr, period, version, params.Limit)) 314 } 315 316 // base code for public lookup methods 317 func (h *Handler) lookup(rsrc *resource, params *LookupParams) (*resource, error) { 318 319 lp := *params 320 // we can't look for anything without a store 321 if h.chunkStore == nil { 322 return nil, NewError(ErrInit, "Call Handler.SetStore() before performing lookups") 323 } 324 325 var specificperiod bool 326 if lp.period > 0 { 327 specificperiod = true 328 } else { 329 // get the current time and the next period 330 now := TimestampProvider.Now() 331 332 var period uint32 333 period, err := getNextPeriod(rsrc.StartTime.Time, now.Time, rsrc.Frequency) 334 if err != nil { 335 return nil, err 336 } 337 lp.period = period 338 } 339 340 // start from the last possible period, and iterate previous ones 341 // (unless we want a specific period only) until we find a match. 342 // If we hit startTime we're out of options 343 var specificversion bool 344 if lp.version > 0 { 345 specificversion = true 346 } else { 347 lp.version = 1 348 } 349 350 var hops uint32 351 if lp.Limit == 0 { 352 lp.Limit = h.queryMaxPeriods 353 } 354 log.Trace("resource lookup", "period", lp.period, "version", lp.version, "limit", lp.Limit) 355 for lp.period > 0 { 356 if lp.Limit != 0 && hops > lp.Limit { 357 return nil, NewErrorf(ErrPeriodDepth, "Lookup exceeded max period hops (%d)", lp.Limit) 358 } 359 updateAddr := lp.UpdateAddr() 360 chunk, err := h.chunkStore.GetWithTimeout(context.TODO(), updateAddr, defaultRetrieveTimeout) 361 if err == nil { 362 if specificversion { 363 return h.updateIndex(rsrc, chunk) 364 } 365 // check if we have versions > 1. If a version fails, the previous version is used and returned. 366 log.Trace("rsrc update version 1 found, checking for version updates", "period", lp.period, "updateAddr", updateAddr) 367 for { 368 newversion := lp.version + 1 369 updateAddr := lp.UpdateAddr() 370 newchunk, err := h.chunkStore.GetWithTimeout(context.TODO(), updateAddr, defaultRetrieveTimeout) 371 if err != nil { 372 return h.updateIndex(rsrc, chunk) 373 } 374 chunk = newchunk 375 lp.version = newversion 376 log.Trace("version update found, checking next", "version", lp.version, "period", lp.period, "updateAddr", updateAddr) 377 } 378 } 379 if specificperiod { 380 break 381 } 382 log.Trace("rsrc update not found, checking previous period", "period", lp.period, "updateAddr", updateAddr) 383 lp.period-- 384 hops++ 385 } 386 return nil, NewError(ErrNotFound, "no updates found") 387 } 388 389 // Load retrieves the Mutable Resource metadata chunk stored at rootAddr 390 // Upon retrieval it creates/updates the index entry for it with metadata corresponding to the chunk contents 391 func (h *Handler) Load(ctx context.Context, rootAddr storage.Address) (*resource, error) { 392 chunk, err := h.chunkStore.GetWithTimeout(ctx, rootAddr, defaultRetrieveTimeout) 393 if err != nil { 394 return nil, NewError(ErrNotFound, err.Error()) 395 } 396 397 // create the index entry 398 rsrc := &resource{} 399 400 if err := rsrc.ResourceMetadata.binaryGet(chunk.SData); err != nil { // Will fail if this is not really a metadata chunk 401 return nil, err 402 } 403 404 rsrc.rootAddr, rsrc.metaHash = metadataHash(chunk.SData) 405 if !bytes.Equal(rsrc.rootAddr, rootAddr) { 406 return nil, NewError(ErrCorruptData, "Corrupt metadata chunk") 407 } 408 h.set(rootAddr, rsrc) 409 log.Trace("resource index load", "rootkey", rootAddr, "name", rsrc.ResourceMetadata.Name, "starttime", rsrc.ResourceMetadata.StartTime, "frequency", rsrc.ResourceMetadata.Frequency) 410 return rsrc, nil 411 } 412 413 // update mutable resource index map with specified content 414 func (h *Handler) updateIndex(rsrc *resource, chunk *storage.Chunk) (*resource, error) { 415 416 // retrieve metadata from chunk data and check that it matches this mutable resource 417 var r SignedResourceUpdate 418 if err := r.fromChunk(chunk.Addr, chunk.SData); err != nil { 419 return nil, err 420 } 421 log.Trace("resource index update", "name", rsrc.ResourceMetadata.Name, "updatekey", chunk.Addr, "period", r.period, "version", r.version) 422 423 // update our rsrcs entry map 424 rsrc.lastKey = chunk.Addr 425 rsrc.period = r.period 426 rsrc.version = r.version 427 rsrc.updated = time.Now() 428 rsrc.data = make([]byte, len(r.data)) 429 rsrc.multihash = r.multihash 430 copy(rsrc.data, r.data) 431 rsrc.Reader = bytes.NewReader(rsrc.data) 432 log.Debug("resource synced", "name", rsrc.ResourceMetadata.Name, "updateAddr", chunk.Addr, "period", rsrc.period, "version", rsrc.version) 433 h.set(chunk.Addr, rsrc) 434 return rsrc, nil 435 } 436 437 // Update adds an actual data update 438 // Uses the Mutable Resource metadata currently loaded in the resources map entry. 439 // It is the caller's responsibility to make sure that this data is not stale. 440 // Note that a Mutable Resource update cannot span chunks, and thus has a MAX NET LENGTH 4096, INCLUDING update header data and signature. An error will be returned if the total length of the chunk payload will exceed this limit. 441 // Update can only check if the caller is trying to overwrite the very last known version, otherwise it just puts the update 442 // on the network. 443 func (h *Handler) Update(ctx context.Context, r *SignedResourceUpdate) (storage.Address, error) { 444 return h.update(ctx, r) 445 } 446 447 // create and commit an update 448 func (h *Handler) update(ctx context.Context, r *SignedResourceUpdate) (updateAddr storage.Address, err error) { 449 450 // we can't update anything without a store 451 if h.chunkStore == nil { 452 return nil, NewError(ErrInit, "Call Handler.SetStore() before updating") 453 } 454 455 rsrc := h.get(r.rootAddr) 456 if rsrc != nil && rsrc.period != 0 && rsrc.version != 0 && // This is the only cheap check we can do for sure 457 rsrc.period == r.period && rsrc.version >= r.version { // without having to lookup update chunks 458 459 return nil, NewError(ErrInvalidValue, "A former update in this period is already known to exist") 460 } 461 462 chunk, err := r.toChunk() // Serialize the update into a chunk. Fails if data is too big 463 if err != nil { 464 return nil, err 465 } 466 467 // send the chunk 468 h.chunkStore.Put(ctx, chunk) 469 log.Trace("resource update", "updateAddr", r.updateAddr, "lastperiod", r.period, "version", r.version, "data", chunk.SData, "multihash", r.multihash) 470 471 // update our resources map entry if the new update is older than the one we have, if we have it. 472 if rsrc != nil && (r.period > rsrc.period || (rsrc.period == r.period && r.version > rsrc.version)) { 473 rsrc.period = r.period 474 rsrc.version = r.version 475 rsrc.data = make([]byte, len(r.data)) 476 rsrc.updated = time.Now() 477 rsrc.lastKey = r.updateAddr 478 rsrc.multihash = r.multihash 479 copy(rsrc.data, r.data) 480 rsrc.Reader = bytes.NewReader(rsrc.data) 481 } 482 return r.updateAddr, nil 483 } 484 485 // Retrieves the resource index value for the given nameHash 486 func (h *Handler) get(rootAddr storage.Address) *resource { 487 if len(rootAddr) < storage.KeyLength { 488 log.Warn("Handler.get with invalid rootAddr") 489 return nil 490 } 491 hashKey := *(*uint64)(unsafe.Pointer(&rootAddr[0])) 492 h.resourceLock.RLock() 493 defer h.resourceLock.RUnlock() 494 rsrc := h.resources[hashKey] 495 return rsrc 496 } 497 498 // Sets the resource index value for the given nameHash 499 func (h *Handler) set(rootAddr storage.Address, rsrc *resource) { 500 if len(rootAddr) < storage.KeyLength { 501 log.Warn("Handler.set with invalid rootAddr") 502 return 503 } 504 hashKey := *(*uint64)(unsafe.Pointer(&rootAddr[0])) 505 h.resourceLock.Lock() 506 defer h.resourceLock.Unlock() 507 h.resources[hashKey] = rsrc 508 } 509 510 // Checks if we already have an update on this resource, according to the value in the current state of the resource index 511 func (h *Handler) hasUpdate(rootAddr storage.Address, period uint32) bool { 512 rsrc := h.get(rootAddr) 513 return rsrc != nil && rsrc.period == period 514 }