github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/swarm/storage/mru/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 mru
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  
    23  	"github.com/FusionFoundation/efsn/common"
    24  	"github.com/FusionFoundation/efsn/common/hexutil"
    25  	"github.com/FusionFoundation/efsn/swarm/storage"
    26  )
    27  
    28  // updateRequestJSON represents a JSON-serialized UpdateRequest
    29  type updateRequestJSON struct {
    30  	Name      string `json:"name,omitempty"`
    31  	Frequency uint64 `json:"frequency,omitempty"`
    32  	StartTime uint64 `json:"startTime,omitempty"`
    33  	Owner     string `json:"ownerAddr,omitempty"`
    34  	RootAddr  string `json:"rootAddr,omitempty"`
    35  	MetaHash  string `json:"metaHash,omitempty"`
    36  	Version   uint32 `json:"version,omitempty"`
    37  	Period    uint32 `json:"period,omitempty"`
    38  	Data      string `json:"data,omitempty"`
    39  	Multihash bool   `json:"multiHash"`
    40  	Signature string `json:"signature,omitempty"`
    41  }
    42  
    43  // Request represents an update and/or resource create message
    44  type Request struct {
    45  	SignedResourceUpdate
    46  	metadata ResourceMetadata
    47  	isNew    bool
    48  }
    49  
    50  var zeroAddr = common.Address{}
    51  
    52  // NewCreateUpdateRequest returns a ready to sign request to create and initialize a resource with data
    53  func NewCreateUpdateRequest(metadata *ResourceMetadata) (*Request, error) {
    54  
    55  	request, err := NewCreateRequest(metadata)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	// get the current time
    61  	now := TimestampProvider.Now().Time
    62  
    63  	request.version = 1
    64  	request.period, err = getNextPeriod(metadata.StartTime.Time, now, metadata.Frequency)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	return request, nil
    69  }
    70  
    71  // NewCreateRequest returns a request to create a new resource
    72  func NewCreateRequest(metadata *ResourceMetadata) (request *Request, err error) {
    73  	if metadata.StartTime.Time == 0 { // get the current time
    74  		metadata.StartTime = TimestampProvider.Now()
    75  	}
    76  
    77  	if metadata.Owner == zeroAddr {
    78  		return nil, NewError(ErrInvalidValue, "OwnerAddr is not set")
    79  	}
    80  
    81  	request = &Request{
    82  		metadata: *metadata,
    83  	}
    84  	request.rootAddr, request.metaHash, _, err = request.metadata.serializeAndHash()
    85  	request.isNew = true
    86  	return request, nil
    87  }
    88  
    89  // Frequency returns the resource's expected update frequency
    90  func (r *Request) Frequency() uint64 {
    91  	return r.metadata.Frequency
    92  }
    93  
    94  // Name returns the resource human-readable name
    95  func (r *Request) Name() string {
    96  	return r.metadata.Name
    97  }
    98  
    99  // Multihash returns true if the resource data should be interpreted as a multihash
   100  func (r *Request) Multihash() bool {
   101  	return r.multihash
   102  }
   103  
   104  // Period returns in which period the resource will be published
   105  func (r *Request) Period() uint32 {
   106  	return r.period
   107  }
   108  
   109  // Version returns the resource version to publish
   110  func (r *Request) Version() uint32 {
   111  	return r.version
   112  }
   113  
   114  // RootAddr returns the metadata chunk address
   115  func (r *Request) RootAddr() storage.Address {
   116  	return r.rootAddr
   117  }
   118  
   119  // StartTime returns the time that the resource was/will be created at
   120  func (r *Request) StartTime() Timestamp {
   121  	return r.metadata.StartTime
   122  }
   123  
   124  // Owner returns the resource owner's address
   125  func (r *Request) Owner() common.Address {
   126  	return r.metadata.Owner
   127  }
   128  
   129  // Sign executes the signature to validate the resource and sets the owner address field
   130  func (r *Request) Sign(signer Signer) error {
   131  	if r.metadata.Owner != zeroAddr && r.metadata.Owner != signer.Address() {
   132  		return NewError(ErrInvalidSignature, "Signer does not match current owner of the resource")
   133  	}
   134  
   135  	if err := r.SignedResourceUpdate.Sign(signer); err != nil {
   136  		return err
   137  	}
   138  	r.metadata.Owner = signer.Address()
   139  	return nil
   140  }
   141  
   142  // SetData stores the payload data the resource will be updated with
   143  func (r *Request) SetData(data []byte, multihash bool) {
   144  	r.data = data
   145  	r.multihash = multihash
   146  	r.signature = nil
   147  	if !r.isNew {
   148  		r.metadata.Frequency = 0 // mark as update
   149  	}
   150  }
   151  
   152  func (r *Request) IsNew() bool {
   153  	return r.metadata.Frequency > 0 && (r.period <= 1 || r.version <= 1)
   154  }
   155  
   156  func (r *Request) IsUpdate() bool {
   157  	return r.signature != nil
   158  }
   159  
   160  // fromJSON takes an update request JSON and populates an UpdateRequest
   161  func (r *Request) fromJSON(j *updateRequestJSON) error {
   162  
   163  	r.version = j.Version
   164  	r.period = j.Period
   165  	r.multihash = j.Multihash
   166  	r.metadata.Name = j.Name
   167  	r.metadata.Frequency = j.Frequency
   168  	r.metadata.StartTime.Time = j.StartTime
   169  
   170  	if err := decodeHexArray(r.metadata.Owner[:], j.Owner, "ownerAddr"); err != nil {
   171  		return err
   172  	}
   173  
   174  	var err error
   175  	if j.Data != "" {
   176  		r.data, err = hexutil.Decode(j.Data)
   177  		if err != nil {
   178  			return NewError(ErrInvalidValue, "Cannot decode data")
   179  		}
   180  	}
   181  
   182  	var declaredRootAddr storage.Address
   183  	var declaredMetaHash []byte
   184  
   185  	declaredRootAddr, err = decodeHexSlice(j.RootAddr, storage.AddressLength, "rootAddr")
   186  	if err != nil {
   187  		return err
   188  	}
   189  	declaredMetaHash, err = decodeHexSlice(j.MetaHash, 32, "metaHash")
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	if r.IsNew() {
   195  		// for new resource creation, rootAddr and metaHash are optional because
   196  		// we can derive them from the content itself.
   197  		// however, if the user sent them, we check them for consistency.
   198  
   199  		r.rootAddr, r.metaHash, _, err = r.metadata.serializeAndHash()
   200  		if err != nil {
   201  			return err
   202  		}
   203  		if j.RootAddr != "" && !bytes.Equal(declaredRootAddr, r.rootAddr) {
   204  			return NewError(ErrInvalidValue, "rootAddr does not match resource metadata")
   205  		}
   206  		if j.MetaHash != "" && !bytes.Equal(declaredMetaHash, r.metaHash) {
   207  			return NewError(ErrInvalidValue, "metaHash does not match resource metadata")
   208  		}
   209  
   210  	} else {
   211  		//Update message
   212  		r.rootAddr = declaredRootAddr
   213  		r.metaHash = declaredMetaHash
   214  	}
   215  
   216  	if j.Signature != "" {
   217  		sigBytes, err := hexutil.Decode(j.Signature)
   218  		if err != nil || len(sigBytes) != signatureLength {
   219  			return NewError(ErrInvalidSignature, "Cannot decode signature")
   220  		}
   221  		r.signature = new(Signature)
   222  		r.updateAddr = r.UpdateAddr()
   223  		copy(r.signature[:], sigBytes)
   224  	}
   225  	return nil
   226  }
   227  
   228  func decodeHexArray(dst []byte, src, name string) error {
   229  	bytes, err := decodeHexSlice(src, len(dst), name)
   230  	if err != nil {
   231  		return err
   232  	}
   233  	if bytes != nil {
   234  		copy(dst, bytes)
   235  	}
   236  	return nil
   237  }
   238  
   239  func decodeHexSlice(src string, expectedLength int, name string) (bytes []byte, err error) {
   240  	if src != "" {
   241  		bytes, err = hexutil.Decode(src)
   242  		if err != nil || len(bytes) != expectedLength {
   243  			return nil, NewErrorf(ErrInvalidValue, "Cannot decode %s", name)
   244  		}
   245  	}
   246  	return bytes, nil
   247  }
   248  
   249  // UnmarshalJSON takes a JSON structure stored in a byte array and populates the Request object
   250  // Implements json.Unmarshaler interface
   251  func (r *Request) UnmarshalJSON(rawData []byte) error {
   252  	var requestJSON updateRequestJSON
   253  	if err := json.Unmarshal(rawData, &requestJSON); err != nil {
   254  		return err
   255  	}
   256  	return r.fromJSON(&requestJSON)
   257  }
   258  
   259  // MarshalJSON takes an update request and encodes it as a JSON structure into a byte array
   260  // Implements json.Marshaler interface
   261  func (r *Request) MarshalJSON() (rawData []byte, err error) {
   262  	var signatureString, dataHashString, rootAddrString, metaHashString string
   263  	if r.signature != nil {
   264  		signatureString = hexutil.Encode(r.signature[:])
   265  	}
   266  	if r.data != nil {
   267  		dataHashString = hexutil.Encode(r.data)
   268  	}
   269  	if r.rootAddr != nil {
   270  		rootAddrString = hexutil.Encode(r.rootAddr)
   271  	}
   272  	if r.metaHash != nil {
   273  		metaHashString = hexutil.Encode(r.metaHash)
   274  	}
   275  	var ownerAddrString string
   276  	if r.metadata.Frequency == 0 {
   277  		ownerAddrString = ""
   278  	} else {
   279  		ownerAddrString = hexutil.Encode(r.metadata.Owner[:])
   280  	}
   281  
   282  	requestJSON := &updateRequestJSON{
   283  		Name:      r.metadata.Name,
   284  		Frequency: r.metadata.Frequency,
   285  		StartTime: r.metadata.StartTime.Time,
   286  		Version:   r.version,
   287  		Period:    r.period,
   288  		Owner:     ownerAddrString,
   289  		Data:      dataHashString,
   290  		Multihash: r.multihash,
   291  		Signature: signatureString,
   292  		RootAddr:  rootAddrString,
   293  		MetaHash:  metaHashString,
   294  	}
   295  
   296  	return json.Marshal(requestJSON)
   297  }