diff --git a/dapps/valuetransfers/packages/tangle/tangle.go b/dapps/valuetransfers/packages/tangle/tangle.go index 7c91dec1711e6dd251325a46b6987a493af1cd99..15b192b9e28ee01fda6d36a261fa940017a39fe9 100644 --- a/dapps/valuetransfers/packages/tangle/tangle.go +++ b/dapps/valuetransfers/packages/tangle/tangle.go @@ -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) diff --git a/dapps/valuetransfers/packages/transaction/transaction.go b/dapps/valuetransfers/packages/transaction/transaction.go index 3b234bdf0bec0bff067f85aa5ca4fae2b3e7bb5b..760ded1db4a41edd801eb03722a6f0aea8d105fa 100644 --- a/dapps/valuetransfers/packages/transaction/transaction.go +++ b/dapps/valuetransfers/packages/transaction/transaction.go @@ -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 } diff --git a/packages/binary/drng/subtypes/collectiveBeacon/payload/payload.go b/packages/binary/drng/subtypes/collectiveBeacon/payload/payload.go index 2c41c2c8d710406aab97aac21fa4c3c9f43ea630..d53be6f1bf2689d305e90a2319f046f665842dd4 100644 --- a/packages/binary/drng/subtypes/collectiveBeacon/payload/payload.go +++ b/packages/binary/drng/subtypes/collectiveBeacon/payload/payload.go @@ -1,6 +1,8 @@ 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. diff --git a/packages/binary/messagelayer/payload/data.go b/packages/binary/messagelayer/payload/data.go index 9db9cc640b6e9f5114f94a5de8d042e640cc866a..6092956c7f1a7d42f5c6c7b274924d2c68b72038 100644 --- a/packages/binary/messagelayer/payload/data.go +++ b/packages/binary/messagelayer/payload/data.go @@ -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{ diff --git a/packages/binary/messagelayer/payload/payload.go b/packages/binary/messagelayer/payload/payload.go index f70f87578a42015d56bf590921ab93969ab2b5de..a0282c323f6617809ffcfd0cd652a26f742375fb 100644 --- a/packages/binary/messagelayer/payload/payload.go +++ b/packages/binary/messagelayer/payload/payload.go @@ -1,9 +1,17 @@ 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 { diff --git a/plugins/webapi/data/plugin.go b/plugins/webapi/data/plugin.go index a13ecce6b59ef7223fc9055afb646367e530cec0..605ad8d8f36f83c78080cf1017dccdadaea336db 100644 --- a/plugins/webapi/data/plugin.go +++ b/plugins/webapi/data/plugin.go @@ -1,6 +1,7 @@ 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()}) diff --git a/plugins/webapi/drng/collectivebeacon/handler.go b/plugins/webapi/drng/collectivebeacon/handler.go index 591dc4820ca55c0fa61e6f6ba458ed1f9cc10a03..3c21953c861e2b7ef011a772633d2b601a615b2b 100644 --- a/plugins/webapi/drng/collectivebeacon/handler.go +++ b/plugins/webapi/drng/collectivebeacon/handler.go @@ -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) diff --git a/plugins/webapi/message/sendPayload.go b/plugins/webapi/message/sendPayload.go index 159ffe16f0cc7cd0dcc015e39b09cd005fe5955b..cffc572be8a36141ec6f2086edcb1d1d4ec26a3d 100644 --- a/plugins/webapi/message/sendPayload.go +++ b/plugins/webapi/message/sendPayload.go @@ -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) diff --git a/plugins/webapi/value/sendtransaction/handler.go b/plugins/webapi/value/sendtransaction/handler.go index 8a65571533c113d60d7a74a9e7108acaa4e34d65..dda59ab8c3d80fbe5c3b69a08dd82254acc81b6c 100644 --- a/plugins/webapi/value/sendtransaction/handler.go +++ b/plugins/webapi/value/sendtransaction/handler.go @@ -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)