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  }