utilities.go 3.59 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
// Copyright 2021 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package headerutils

import (
	"encoding/binary"
	"errors"
	"github.com/ethersphere/bee/pkg/p2p"
	"github.com/ethersphere/bee/pkg/swarm"
)

const (
	priceFieldName  = "price"
	targetFieldName = "target"
	indexFieldName  = "index"
)

var (
	// ErrFieldLength denotes p2p.Header having malformed field length in bytes
	ErrFieldLength = errors.New("field length error")
	// ErrNoIndexHeader denotes p2p.Header lacking specified field
	ErrNoIndexHeader = errors.New("no index header")
	// ErrNoTargetHeader denotes p2p.Header lacking specified field
	ErrNoTargetHeader = errors.New("no target header")
	// ErrNoPriceHeader denotes p2p.Header lacking specified field
	ErrNoPriceHeader = errors.New("no price header")
)

// Headers, utility functions

func MakePricingHeaders(chunkPrice uint64, addr swarm.Address) (p2p.Headers, error) {

	chunkPriceInBytes := make([]byte, 8)

	binary.BigEndian.PutUint64(chunkPriceInBytes, chunkPrice)

	headers := p2p.Headers{
		priceFieldName:  chunkPriceInBytes,
		targetFieldName: addr.Bytes(),
	}

	return headers, nil
}

func MakePricingResponseHeaders(chunkPrice uint64, addr swarm.Address, index uint8) (p2p.Headers, error) {

	chunkPriceInBytes := make([]byte, 8)
	chunkIndexInBytes := make([]byte, 1)

	binary.BigEndian.PutUint64(chunkPriceInBytes, chunkPrice)
	chunkIndexInBytes[0] = index

	headers := p2p.Headers{
		priceFieldName:  chunkPriceInBytes,
		targetFieldName: addr.Bytes(),
		indexFieldName:  chunkIndexInBytes,
	}

	return headers, nil
}

// ParsePricingHeaders used by responder to read address and price from stream headers
// Returns an error if no target field attached or the contents of it are not readable
func ParsePricingHeaders(receivedHeaders p2p.Headers) (swarm.Address, uint64, error) {

	target, err := ParseTargetHeader(receivedHeaders)
	if err != nil {
		return swarm.ZeroAddress, 0, err
	}
	price, err := ParsePriceHeader(receivedHeaders)
	if err != nil {
		return swarm.ZeroAddress, 0, err
	}
	return target, price, nil
}

// ParsePricingResponseHeaders used by requester to read address, price and index from response headers
// Returns an error if any fields are missing or target is unreadable
func ParsePricingResponseHeaders(receivedHeaders p2p.Headers) (swarm.Address, uint64, uint8, error) {
	target, err := ParseTargetHeader(receivedHeaders)
	if err != nil {
		return swarm.ZeroAddress, 0, 0, err
	}
	price, err := ParsePriceHeader(receivedHeaders)
	if err != nil {
		return swarm.ZeroAddress, 0, 0, err
	}
	index, err := ParseIndexHeader(receivedHeaders)
	if err != nil {
		return swarm.ZeroAddress, 0, 0, err
	}

	return target, price, index, nil
}

func ParseIndexHeader(receivedHeaders p2p.Headers) (uint8, error) {
	if receivedHeaders[indexFieldName] == nil {
		return 0, ErrNoIndexHeader
	}

	if len(receivedHeaders[indexFieldName]) != 1 {
		return 0, ErrFieldLength
	}

	index := receivedHeaders[indexFieldName][0]
	return index, nil
}

func ParseTargetHeader(receivedHeaders p2p.Headers) (swarm.Address, error) {
	if receivedHeaders[targetFieldName] == nil {
		return swarm.ZeroAddress, ErrNoTargetHeader
	}

	target := swarm.NewAddress(receivedHeaders[targetFieldName])

	return target, nil
}

func ParsePriceHeader(receivedHeaders p2p.Headers) (uint64, error) {
	if receivedHeaders[priceFieldName] == nil {
		return 0, ErrNoPriceHeader
	}

	if len(receivedHeaders[priceFieldName]) != 8 {
		return 0, ErrFieldLength
	}

	receivedPrice := binary.BigEndian.Uint64(receivedHeaders[priceFieldName])
	return receivedPrice, nil
}