github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/swarm/storage/mru/metadata.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 mru 18 19 import ( 20 "encoding/binary" 21 "hash" 22 23 "github.com/FusionFoundation/efsn/common" 24 "github.com/FusionFoundation/efsn/swarm/storage" 25 ) 26 27 // ResourceMetadata encapsulates the immutable information about a mutable resource :) 28 // once serialized into a chunk, the resource can be retrieved by knowing its content-addressed rootAddr 29 type ResourceMetadata struct { 30 StartTime Timestamp // time at which the resource starts to be valid 31 Frequency uint64 // expected update frequency for the resource 32 Name string // name of the resource, for the reference of the user or to disambiguate resources with same starttime, frequency, owneraddr 33 Owner common.Address // public address of the resource owner 34 } 35 36 const frequencyLength = 8 // sizeof(uint64) 37 const nameLengthLength = 1 38 39 // Resource metadata chunk layout: 40 // 4 prefix bytes (chunkPrefixLength). The first two set to zero. The second two indicate the length 41 // Timestamp: timestampLength bytes 42 // frequency: frequencyLength bytes 43 // name length: nameLengthLength bytes 44 // name (variable length, can be empty, up to 255 bytes) 45 // ownerAddr: common.AddressLength 46 const minimumMetadataLength = chunkPrefixLength + timestampLength + frequencyLength + nameLengthLength + common.AddressLength 47 48 // binaryGet populates the resource metadata from a byte array 49 func (r *ResourceMetadata) binaryGet(serializedData []byte) error { 50 if len(serializedData) < minimumMetadataLength { 51 return NewErrorf(ErrInvalidValue, "Metadata chunk to deserialize is too short. Expected at least %d. Got %d.", minimumMetadataLength, len(serializedData)) 52 } 53 54 // first two bytes must be set to zero to indicate metadata chunks, so enforce this. 55 if serializedData[0] != 0 || serializedData[1] != 0 { 56 return NewError(ErrCorruptData, "Invalid metadata chunk") 57 } 58 59 cursor := 2 60 metadataLength := int(binary.LittleEndian.Uint16(serializedData[cursor : cursor+2])) // metadataLength does not include the 4 prefix bytes 61 if metadataLength+chunkPrefixLength != len(serializedData) { 62 return NewErrorf(ErrCorruptData, "Incorrect declared metadata length. Expected %d, got %d.", metadataLength+chunkPrefixLength, len(serializedData)) 63 } 64 65 cursor += 2 66 67 if err := r.StartTime.binaryGet(serializedData[cursor : cursor+timestampLength]); err != nil { 68 return err 69 } 70 cursor += timestampLength 71 72 r.Frequency = binary.LittleEndian.Uint64(serializedData[cursor : cursor+frequencyLength]) 73 cursor += frequencyLength 74 75 nameLength := int(serializedData[cursor]) 76 if nameLength+minimumMetadataLength > len(serializedData) { 77 return NewErrorf(ErrInvalidValue, "Metadata chunk to deserialize is too short when decoding resource name. Expected at least %d. Got %d.", nameLength+minimumMetadataLength, len(serializedData)) 78 } 79 cursor++ 80 r.Name = string(serializedData[cursor : cursor+nameLength]) 81 cursor += nameLength 82 83 copy(r.Owner[:], serializedData[cursor:]) 84 cursor += common.AddressLength 85 if cursor != len(serializedData) { 86 return NewErrorf(ErrInvalidValue, "Metadata chunk has leftover data after deserialization. %d left to read", len(serializedData)-cursor) 87 } 88 return nil 89 } 90 91 // binaryPut encodes the metadata into a byte array 92 func (r *ResourceMetadata) binaryPut(serializedData []byte) error { 93 metadataChunkLength := r.binaryLength() 94 if len(serializedData) != metadataChunkLength { 95 return NewErrorf(ErrInvalidValue, "Need a slice of exactly %d bytes to serialize this metadata, but got a slice of size %d.", metadataChunkLength, len(serializedData)) 96 } 97 98 // root chunk has first two bytes both set to 0, which distinguishes from update bytes 99 // therefore, skip the first two bytes of a zero-initialized array. 100 cursor := 2 101 binary.LittleEndian.PutUint16(serializedData[cursor:cursor+2], uint16(metadataChunkLength-chunkPrefixLength)) // metadataLength does not include the 4 prefix bytes 102 cursor += 2 103 104 r.StartTime.binaryPut(serializedData[cursor : cursor+timestampLength]) 105 cursor += timestampLength 106 107 binary.LittleEndian.PutUint64(serializedData[cursor:cursor+frequencyLength], r.Frequency) 108 cursor += frequencyLength 109 110 // Encode the name string as a 1 byte length followed by the encoded string. 111 // Longer strings will be truncated. 112 nameLength := len(r.Name) 113 if nameLength > 255 { 114 nameLength = 255 115 } 116 serializedData[cursor] = uint8(nameLength) 117 cursor++ 118 copy(serializedData[cursor:cursor+nameLength], []byte(r.Name[:nameLength])) 119 cursor += nameLength 120 121 copy(serializedData[cursor:cursor+common.AddressLength], r.Owner[:]) 122 cursor += common.AddressLength 123 124 return nil 125 } 126 127 func (r *ResourceMetadata) binaryLength() int { 128 return minimumMetadataLength + len(r.Name) 129 } 130 131 // serializeAndHash returns the root chunk addr and metadata hash that help identify and ascertain ownership of this resource 132 // returns the serialized metadata as a byproduct of having to hash it. 133 func (r *ResourceMetadata) serializeAndHash() (rootAddr, metaHash []byte, chunkData []byte, err error) { 134 135 chunkData = make([]byte, r.binaryLength()) 136 if err := r.binaryPut(chunkData); err != nil { 137 return nil, nil, nil, err 138 } 139 rootAddr, metaHash = metadataHash(chunkData) 140 return rootAddr, metaHash, chunkData, nil 141 142 } 143 144 // creates a metadata chunk out of a resourceMetadata structure 145 func (metadata *ResourceMetadata) newChunk() (chunk storage.Chunk, metaHash []byte, err error) { 146 // the metadata chunk contains a timestamp of when the resource starts to be valid 147 // and also how frequently it is expected to be updated 148 // from this we know at what time we should look for updates, and how often 149 // it also contains the name of the resource, so we know what resource we are working with 150 151 // the key (rootAddr) of the metadata chunk is content-addressed 152 // if it wasn't we couldn't replace it later 153 // resolving this relationship is left up to external agents (for example ENS) 154 rootAddr, metaHash, chunkData, err := metadata.serializeAndHash() 155 if err != nil { 156 return nil, nil, err 157 } 158 159 // make the chunk and send it to swarm 160 chunk = storage.NewChunk(rootAddr, chunkData) 161 162 return chunk, metaHash, nil 163 } 164 165 // metadataHash returns the metadata chunk root address and metadata hash 166 // that help identify and ascertain ownership of this resource 167 // We compute it as rootAddr = H(ownerAddr, H(metadata)) 168 // Where H() is SHA3 169 // metadata are all the metadata fields, except ownerAddr 170 // ownerAddr is the public address of the resource owner 171 // Update chunks must carry a rootAddr reference and metaHash in order to be verified 172 // This way, a node that receives an update can check the signature, recover the public address 173 // and check the ownership by computing H(ownerAddr, metaHash) and comparing it to the rootAddr 174 // the resource is claiming to update without having to lookup the metadata chunk. 175 // see verifyResourceOwnerhsip in signedupdate.go 176 func metadataHash(chunkData []byte) (rootAddr, metaHash []byte) { 177 hasher := hashPool.Get().(hash.Hash) 178 defer hashPool.Put(hasher) 179 hasher.Reset() 180 hasher.Write(chunkData[:len(chunkData)-common.AddressLength]) 181 metaHash = hasher.Sum(nil) 182 hasher.Reset() 183 hasher.Write(metaHash) 184 hasher.Write(chunkData[len(chunkData)-common.AddressLength:]) 185 rootAddr = hasher.Sum(nil) 186 return 187 }