Skip to content
Snippets Groups Projects
Unverified Commit 256f8326 authored by Acha Bill's avatar Acha Bill Committed by GitHub
Browse files

Validate web API requests (#508)


* Adjust inbox worker pool capacity to default

* Adjust inbox worker pool capacity to default (#505)

* validate api requests

* release cachedInputs

Co-authored-by: default avatarAngelo Capossele <angelocapossele@gmail.com>

* add payload size validation to sendPayload api

* Apply suggestions from code review

* validate transaction signatures

* refactor: move validation to packages

* update ValidateTransactionToAttach

* define errors

* Apply suggestions from code review

* fixes from codereview

Co-authored-by: default avatarjonastheis <mail@jonastheis.de>
Co-authored-by: default avatarWolfgang Welz <welzwo@gmail.com>
Co-authored-by: default avatarAngelo Capossele <angelocapossele@gmail.com>
Co-authored-by: default avatarLuca Moser <moser.luca@gmail.com>
parent efcefdcc
No related branches found
No related tags found
No related merge requests found
......@@ -21,6 +21,12 @@ import (
"github.com/iotaledger/goshimmer/packages/binary/storageprefix"
)
var (
// ErrTransactionDoesNotSpendAllFunds is returned if a transaction does not spend all of its inputs.
ErrTransactionDoesNotSpendAllFunds = errors.New("transaction does not spend all funds from inputs")
// ErrInvalidTransactionSignature is returned if the signature of a transaction is invalid.
ErrInvalidTransactionSignature = errors.New("invalid transaction signatures")
)
// Tangle represents the value tangle that consists out of value payloads.
// It is an independent ontology, that lives inside the tangle.
type Tangle struct {
......@@ -1577,6 +1583,27 @@ func (tangle *Tangle) getCachedOutputsFromTransactionInputs(tx *transaction.Tran
return
}
// ValidateTransactionToAttach checks that the given transaction spends all funds from its inputs and
// that its the signature is valid.
func (tangle *Tangle) ValidateTransactionToAttach(tx *transaction.Transaction) (err error) {
_, cachedInputs, consumedBalances, _, err := tangle.retrieveConsumedInputDetails(tx)
defer cachedInputs.Release()
if err != nil {
return
}
if !tangle.checkTransactionOutputs(consumedBalances, tx.Outputs()) {
err = ErrTransactionDoesNotSpendAllFunds
return
}
if !tx.SignaturesValid() {
err = ErrInvalidTransactionSignature
return
}
return
}
// retrieveConsumedInputDetails retrieves the details of the consumed inputs of a transaction.
func (tangle *Tangle) retrieveConsumedInputDetails(tx *transaction.Transaction) (inputsSolid bool, cachedInputs CachedOutputs, consumedBalances map[balance.Color]int64, consumedBranches branchmanager.BranchIds, err error) {
cachedInputs = tangle.getCachedOutputsFromTransactionInputs(tx)
consumedBalances = make(map[balance.Color]int64)
......
......@@ -15,6 +15,11 @@ import (
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address/signaturescheme"
)
var (
// ErrMaxDataPayloadSizeExceeded is returned if the data payload size is exceeded.
ErrMaxDataPayloadSizeExceeded = errors.New("maximum data payload size exceeded")
)
// region IMPLEMENT Transaction ////////////////////////////////////////////////////////////////////////////////////////////
// Transaction represents a value transfer for IOTA. It consists out of a number of inputs, a number of outputs and their
......@@ -311,7 +316,7 @@ func (transaction *Transaction) SetDataPayload(data []byte) error {
defer transaction.dataPayloadMutex.Unlock()
if len(data) > MaxDataPayloadSize {
return fmt.Errorf("maximum dataPayload size of %d bytes exceeded", MaxDataPayloadSize)
return fmt.Errorf("%w: %d", ErrMaxDataPayloadSizeExceeded, MaxDataPayloadSize)
}
transaction.dataPayload = data
return nil
......@@ -382,8 +387,7 @@ func (transaction *Transaction) UnmarshalObjectStorageValue(bytes []byte) (consu
return
}
if dataPayloadSize > MaxDataPayloadSize {
err = fmt.Errorf("data payload size of %d bytes exceeds maximum limit of %d bytes",
dataPayloadSize, MaxDataPayloadSize)
err = fmt.Errorf("%w: %d", ErrMaxDataPayloadSizeExceeded, MaxDataPayloadSize)
return
}
......
package payload
import (
"errors"
"fmt"
"sync"
"github.com/iotaledger/hive.go/stringify"
......@@ -11,6 +13,11 @@ import (
"github.com/iotaledger/hive.go/marshalutil"
)
var (
// ErrMaximumPayloadSizeExceeded is returned if the payload exceeds the maximum size.
ErrMaximumPayloadSizeExceeded = errors.New("maximum payload size exceeded")
)
// Payload is a collective beacon payload.
type Payload struct {
header.Header
......@@ -28,6 +35,9 @@ type Payload struct {
bytesMutex sync.RWMutex
}
// MaxCollectiveBeaconPayloadSize defines the maximum size of a collective beacon payload.
const MaxCollectiveBeaconPayloadSize = 64 * 1024
// New creates a new collective beacon payload.
func New(instanceID uint32, round uint64, prevSignature, signature, dpk []byte) *Payload {
return &Payload{
......@@ -41,11 +51,15 @@ func New(instanceID uint32, round uint64, prevSignature, signature, dpk []byte)
// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
func Parse(marshalUtil *marshalutil.MarshalUtil) (*Payload, error) {
if payload, err := marshalUtil.Parse(func(data []byte) (interface{}, int, error) { return FromBytes(data) }); err != nil {
return &Payload{}, err
} else {
return payload.(*Payload), nil
unmarshalledPayload, err := marshalUtil.Parse(func(data []byte) (interface{}, int, error) { return FromBytes(data) })
if err != nil {
return nil, err
}
_payload := unmarshalledPayload.(*Payload)
if len(_payload.bytes) > MaxCollectiveBeaconPayloadSize {
return nil, fmt.Errorf("%w: %d", ErrMaximumPayloadSizeExceeded, MaxCollectiveBeaconPayloadSize)
}
return _payload, nil
}
// FromBytes parses the marshaled version of a Payload into an object.
......
......@@ -14,6 +14,9 @@ type Data struct {
data []byte
}
// MaxDataPayloadSize defines the maximum size of a data payload.
const MaxDataPayloadSize = 64 * 1024
// NewData creates new data payload.
func NewData(data []byte) *Data {
return &Data{
......
package payload
import (
"errors"
"fmt"
"github.com/iotaledger/hive.go/marshalutil"
)
var (
// ErrMaximumPayloadSizeExceeded is returned if the payload exceeds the maximum size.
ErrMaximumPayloadSizeExceeded = errors.New("maximum payload size exceeded")
)
const (
// ObjectName defines the name of the data object.
ObjectName = "data"
......@@ -45,6 +53,11 @@ func FromBytes(bytes []byte) (result Payload, consumedBytes int, err error) {
return
}
if payloadSize > MaxDataPayloadSize {
err = fmt.Errorf("%w: %d", ErrMaximumPayloadSizeExceeded, MaxDataPayloadSize)
return
}
marshalUtil.ReadSeek(marshalUtil.ReadOffset() - marshalutil.UINT32_SIZE*2)
payloadBytes, err := marshalUtil.ReadBytes(int(payloadSize) + 8)
if err != nil {
......
package data
import (
"fmt"
"net/http"
"sync"
......@@ -44,7 +45,12 @@ func broadcastData(c echo.Context) error {
return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
}
//TODO: to check max payload size allowed, if exceeding return an error
dataPayload := payload.NewData(request.Data)
if len(dataPayload.Bytes()) > payload.MaxDataPayloadSize {
err := fmt.Errorf("%w: %d", payload.ErrMaximumPayloadSizeExceeded, payload.MaxDataPayloadSize)
return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
}
msg, err := issuer.IssuePayload(payload.NewData(request.Data))
if err != nil {
return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
......
......@@ -18,11 +18,10 @@ func Handler(c echo.Context) error {
return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
}
//TODO: to check max payload size allowed, if exceeding return an error
marshalUtil := marshalutil.New(request.Payload)
parsedPayload, err := payload.Parse(marshalUtil)
if err != nil {
return c.JSON(http.StatusBadRequest, Response{Error: "not a valid Collective Beacon payload"})
return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
}
msg, err := issuer.IssuePayload(parsedPayload)
......
......@@ -17,11 +17,9 @@ func sendPayload(c echo.Context) error {
return c.JSON(http.StatusBadRequest, MsgResponse{Error: err.Error()})
}
//TODO: to check max payload size allowed, if exceeding return an error
parsedPayload, _, err := payload.FromBytes(request.Payload)
if err != nil {
return c.JSON(http.StatusBadRequest, MsgResponse{Error: "not a valid payload"})
return c.JSON(http.StatusBadRequest, MsgResponse{Error: err.Error()})
}
msg, err := issuer.IssuePayload(parsedPayload)
......
......@@ -22,6 +22,11 @@ func Handler(c echo.Context) error {
return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
}
err = valuetransfers.Tangle().ValidateTransactionToAttach(tx)
if err != nil {
return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
}
// Prepare value payload and send the message to tangle
payload := valuetransfers.ValueObjectFactory().IssueTransaction(tx)
_, err = issuer.IssuePayload(payload)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment