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 }