github.com/xxRanger/go-ethereum@v1.8.23/swarm/storage/feed/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 feed
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"hash"
    23  
    24  	"github.com/ethereum/go-ethereum/common"
    25  	"github.com/ethereum/go-ethereum/common/hexutil"
    26  	"github.com/ethereum/go-ethereum/swarm/storage"
    27  	"github.com/ethereum/go-ethereum/swarm/storage/feed/lookup"
    28  )
    29  
    30  // Request represents a request to sign or signed feed update message
    31  type Request struct {
    32  	Update     // actual content that will be put on the chunk, less signature
    33  	Signature  *Signature
    34  	idAddr     storage.Address // cached chunk address for the update (not serialized, for internal use)
    35  	binaryData []byte          // cached serialized data (does not get serialized again!, for efficiency/internal use)
    36  }
    37  
    38  // updateRequestJSON represents a JSON-serialized UpdateRequest
    39  type updateRequestJSON struct {
    40  	ID
    41  	ProtocolVersion uint8  `json:"protocolVersion"`
    42  	Data            string `json:"data,omitempty"`
    43  	Signature       string `json:"signature,omitempty"`
    44  }
    45  
    46  // Request layout
    47  // Update bytes
    48  // SignatureLength bytes
    49  const minimumSignedUpdateLength = minimumUpdateDataLength + signatureLength
    50  
    51  // NewFirstRequest returns a ready to sign request to publish a first feed update
    52  func NewFirstRequest(topic Topic) *Request {
    53  
    54  	request := new(Request)
    55  
    56  	// get the current time
    57  	now := TimestampProvider.Now().Time
    58  	request.Epoch = lookup.GetFirstEpoch(now)
    59  	request.Feed.Topic = topic
    60  	request.Header.Version = ProtocolVersion
    61  
    62  	return request
    63  }
    64  
    65  // SetData stores the payload data the feed update will be updated with
    66  func (r *Request) SetData(data []byte) {
    67  	r.data = data
    68  	r.Signature = nil
    69  }
    70  
    71  // IsUpdate returns true if this request models a signed update or otherwise it is a signature request
    72  func (r *Request) IsUpdate() bool {
    73  	return r.Signature != nil
    74  }
    75  
    76  // Verify checks that signatures are valid
    77  func (r *Request) Verify() (err error) {
    78  	if len(r.data) == 0 {
    79  		return NewError(ErrInvalidValue, "Update does not contain data")
    80  	}
    81  	if r.Signature == nil {
    82  		return NewError(ErrInvalidSignature, "Missing signature field")
    83  	}
    84  
    85  	digest, err := r.GetDigest()
    86  	if err != nil {
    87  		return err
    88  	}
    89  
    90  	// get the address of the signer (which also checks that it's a valid signature)
    91  	r.Feed.User, err = getUserAddr(digest, *r.Signature)
    92  	if err != nil {
    93  		return err
    94  	}
    95  
    96  	// check that the lookup information contained in the chunk matches the updateAddr (chunk search key)
    97  	// that was used to retrieve this chunk
    98  	// if this validation fails, someone forged a chunk.
    99  	if !bytes.Equal(r.idAddr, r.Addr()) {
   100  		return NewError(ErrInvalidSignature, "Signature address does not match with update user address")
   101  	}
   102  
   103  	return nil
   104  }
   105  
   106  // Sign executes the signature to validate the update message
   107  func (r *Request) Sign(signer Signer) error {
   108  	r.Feed.User = signer.Address()
   109  	r.binaryData = nil           //invalidate serialized data
   110  	digest, err := r.GetDigest() // computes digest and serializes into .binaryData
   111  	if err != nil {
   112  		return err
   113  	}
   114  
   115  	signature, err := signer.Sign(digest)
   116  	if err != nil {
   117  		return err
   118  	}
   119  
   120  	// Although the Signer interface returns the public address of the signer,
   121  	// recover it from the signature to see if they match
   122  	userAddr, err := getUserAddr(digest, signature)
   123  	if err != nil {
   124  		return NewError(ErrInvalidSignature, "Error verifying signature")
   125  	}
   126  
   127  	if userAddr != signer.Address() { // sanity check to make sure the Signer is declaring the same address used to sign!
   128  		return NewError(ErrInvalidSignature, "Signer address does not match update user address")
   129  	}
   130  
   131  	r.Signature = &signature
   132  	r.idAddr = r.Addr()
   133  	return nil
   134  }
   135  
   136  // GetDigest creates the feed update digest used in signatures
   137  // the serialized payload is cached in .binaryData
   138  func (r *Request) GetDigest() (result common.Hash, err error) {
   139  	hasher := hashPool.Get().(hash.Hash)
   140  	defer hashPool.Put(hasher)
   141  	hasher.Reset()
   142  	dataLength := r.Update.binaryLength()
   143  	if r.binaryData == nil {
   144  		r.binaryData = make([]byte, dataLength+signatureLength)
   145  		if err := r.Update.binaryPut(r.binaryData[:dataLength]); err != nil {
   146  			return result, err
   147  		}
   148  	}
   149  	hasher.Write(r.binaryData[:dataLength]) //everything except the signature.
   150  
   151  	return common.BytesToHash(hasher.Sum(nil)), nil
   152  }
   153  
   154  // create an update chunk.
   155  func (r *Request) toChunk() (storage.Chunk, error) {
   156  
   157  	// Check that the update is signed and serialized
   158  	// For efficiency, data is serialized during signature and cached in
   159  	// the binaryData field when computing the signature digest in .getDigest()
   160  	if r.Signature == nil || r.binaryData == nil {
   161  		return nil, NewError(ErrInvalidSignature, "toChunk called without a valid signature or payload data. Call .Sign() first.")
   162  	}
   163  
   164  	updateLength := r.Update.binaryLength()
   165  
   166  	// signature is the last item in the chunk data
   167  	copy(r.binaryData[updateLength:], r.Signature[:])
   168  
   169  	chunk := storage.NewChunk(r.idAddr, r.binaryData)
   170  	return chunk, nil
   171  }
   172  
   173  // fromChunk populates this structure from chunk data. It does not verify the signature is valid.
   174  func (r *Request) fromChunk(chunk storage.Chunk) error {
   175  	// for update chunk layout see Request definition
   176  
   177  	chunkdata := chunk.Data()
   178  
   179  	//deserialize the feed update portion
   180  	if err := r.Update.binaryGet(chunkdata[:len(chunkdata)-signatureLength]); err != nil {
   181  		return err
   182  	}
   183  
   184  	// Extract the signature
   185  	var signature *Signature
   186  	cursor := r.Update.binaryLength()
   187  	sigdata := chunkdata[cursor : cursor+signatureLength]
   188  	if len(sigdata) > 0 {
   189  		signature = &Signature{}
   190  		copy(signature[:], sigdata)
   191  	}
   192  
   193  	r.Signature = signature
   194  	r.idAddr = chunk.Address()
   195  	r.binaryData = chunkdata
   196  
   197  	return nil
   198  
   199  }
   200  
   201  // FromValues deserializes this instance from a string key-value store
   202  // useful to parse query strings
   203  func (r *Request) FromValues(values Values, data []byte) error {
   204  	signatureBytes, err := hexutil.Decode(values.Get("signature"))
   205  	if err != nil {
   206  		r.Signature = nil
   207  	} else {
   208  		if len(signatureBytes) != signatureLength {
   209  			return NewError(ErrInvalidSignature, "Incorrect signature length")
   210  		}
   211  		r.Signature = new(Signature)
   212  		copy(r.Signature[:], signatureBytes)
   213  	}
   214  	err = r.Update.FromValues(values, data)
   215  	if err != nil {
   216  		return err
   217  	}
   218  	r.idAddr = r.Addr()
   219  	return err
   220  }
   221  
   222  // AppendValues serializes this structure into the provided string key-value store
   223  // useful to build query strings
   224  func (r *Request) AppendValues(values Values) []byte {
   225  	if r.Signature != nil {
   226  		values.Set("signature", hexutil.Encode(r.Signature[:]))
   227  	}
   228  	return r.Update.AppendValues(values)
   229  }
   230  
   231  // fromJSON takes an update request JSON and populates an UpdateRequest
   232  func (r *Request) fromJSON(j *updateRequestJSON) error {
   233  
   234  	r.ID = j.ID
   235  	r.Header.Version = j.ProtocolVersion
   236  
   237  	var err error
   238  	if j.Data != "" {
   239  		r.data, err = hexutil.Decode(j.Data)
   240  		if err != nil {
   241  			return NewError(ErrInvalidValue, "Cannot decode data")
   242  		}
   243  	}
   244  
   245  	if j.Signature != "" {
   246  		sigBytes, err := hexutil.Decode(j.Signature)
   247  		if err != nil || len(sigBytes) != signatureLength {
   248  			return NewError(ErrInvalidSignature, "Cannot decode signature")
   249  		}
   250  		r.Signature = new(Signature)
   251  		r.idAddr = r.Addr()
   252  		copy(r.Signature[:], sigBytes)
   253  	}
   254  	return nil
   255  }
   256  
   257  // UnmarshalJSON takes a JSON structure stored in a byte array and populates the Request object
   258  // Implements json.Unmarshaler interface
   259  func (r *Request) UnmarshalJSON(rawData []byte) error {
   260  	var requestJSON updateRequestJSON
   261  	if err := json.Unmarshal(rawData, &requestJSON); err != nil {
   262  		return err
   263  	}
   264  	return r.fromJSON(&requestJSON)
   265  }
   266  
   267  // MarshalJSON takes an update request and encodes it as a JSON structure into a byte array
   268  // Implements json.Marshaler interface
   269  func (r *Request) MarshalJSON() (rawData []byte, err error) {
   270  	var signatureString, dataString string
   271  	if r.Signature != nil {
   272  		signatureString = hexutil.Encode(r.Signature[:])
   273  	}
   274  	if r.data != nil {
   275  		dataString = hexutil.Encode(r.data)
   276  	}
   277  
   278  	requestJSON := &updateRequestJSON{
   279  		ID:              r.ID,
   280  		ProtocolVersion: r.Header.Version,
   281  		Data:            dataString,
   282  		Signature:       signatureString,
   283  	}
   284  
   285  	return json.Marshal(requestJSON)
   286  }