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