Skip to content

Commit

Permalink
feat: envelope (ethersphere#4723)
Browse files Browse the repository at this point in the history
  • Loading branch information
nugaon authored Jul 17, 2024
1 parent 0f7b88d commit bc3cdba
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 0 deletions.
26 changes: 26 additions & 0 deletions openapi/Swarm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,32 @@ paths:
default:
description: Default response

"/envelope/{address}":
post:
summary: "Create postage stamp signature against given chunk address"
tags:
- Envelope
parameters:
- in: header
schema:
$ref: "SwarmCommon.yaml#/components/parameters/SwarmPostageBatchId"
required: true
responses:
"201":
description: Ok
content:
application/json:
schema:
$ref: "SwarmCommon.yaml#/components/schemas/PostEnvelopeResponse"
"400":
$ref: "SwarmCommon.yaml#/components/responses/400"
"402":
$ref: "SwarmCommon.yaml#/components/responses/402"
"500":
$ref: "SwarmCommon.yaml#/components/responses/500"
default:
description: Default response

"/connect/{multiAddress}":
post:
summary: Connect to address
Expand Down
24 changes: 24 additions & 0 deletions openapi/SwarmCommon.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,18 @@ components:
pattern: "^([A-Fa-f0-9]+)$"
example: "cf880b8eeac5093fa27b0825906c600685"

Hex8Bytes:
description: Hexadecimal string representation of 8 bytes
type: string
pattern: "^([0-9a-fA-F]{16})$"
example: "1a2b3c4d5e6f7a8b"

Signature:
description: Hexadecimal string representation of cryptographic signature
type: string
pattern: "^([0-9a-fA-F]{130})$"
example: "1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e"

MultiAddress:
type: string

Expand Down Expand Up @@ -367,6 +379,18 @@ components:
reference:
$ref: "#/components/schemas/SwarmReference"

PostEnvelopeResponse:
type: object
properties:
issuer:
$ref: "#/components/schemas/EthereumAddress"
index:
$ref: "#/components/schemas/Hex8Bytes"
timestamp:
$ref: "#/components/schemas/Hex8Bytes"
signature:
$ref: "#/components/schemas/Signature"

DebugPostageBatchesResponse:
type: object
properties:
Expand Down
91 changes: 91 additions & 0 deletions pkg/api/envelope.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright 2024 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 api

import (
"encoding/hex"
"errors"
"net/http"

"github.com/ethersphere/bee/v2/pkg/jsonhttp"
"github.com/ethersphere/bee/v2/pkg/postage"
"github.com/ethersphere/bee/v2/pkg/swarm"
"github.com/gorilla/mux"
)

type postEnvelopeResponse struct {
Issuer string `json:"issuer"` // Ethereum address of the postage batch owner
Index string `json:"index"` // used index of the Postage Batch
Timestamp string `json:"timestamp"` // timestamp of the postage stamp
Signature string `json:"signature"` // postage stamp signature
}

// envelopePostHandler generates new postage stamp for requested chunk address
func (s *Service) envelopePostHandler(w http.ResponseWriter, r *http.Request) {
logger := s.logger.WithName("post_envelope").Build()

headers := struct {
BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"`
}{}
if response := s.mapStructure(r.Header, &headers); response != nil {
response("invalid header params", logger, w)
return
}

paths := struct {
Address swarm.Address `map:"address" validate:"required"`
}{}
if response := s.mapStructure(mux.Vars(r), &paths); response != nil {
response("invalid path params", logger, w)
return
}

stamper, save, err := s.getStamper(headers.BatchID)
if err != nil {
logger.Debug("get stamper failed", "error", err)
logger.Error(err, "get stamper failed")
switch {
case errors.Is(err, errBatchUnusable) || errors.Is(err, postage.ErrNotUsable):
jsonhttp.UnprocessableEntity(w, "batch not usable yet or does not exist")
case errors.Is(err, postage.ErrNotFound):
jsonhttp.NotFound(w, "batch with id not found")
case errors.Is(err, errInvalidPostageBatch):
jsonhttp.BadRequest(w, "invalid batch id")
default:
jsonhttp.InternalServerError(w, nil)
}
return
}

stamp, err := stamper.Stamp(paths.Address)
if err != nil {
logger.Debug("split write all failed", "error", err)
logger.Error(nil, "split write all failed")
switch {
case errors.Is(err, postage.ErrBucketFull):
jsonhttp.PaymentRequired(w, "batch is overissued")
default:
jsonhttp.InternalServerError(w, "stamping failed")
}
return
}
err = save()
if err != nil {
jsonhttp.InternalServerError(w, "failed to save stamp issuer")
return
}

issuer, err := s.signer.EthereumAddress()
if err != nil {
jsonhttp.InternalServerError(w, "signer ethereum address")
return
}
jsonhttp.Created(w, postEnvelopeResponse{
Issuer: issuer.Hex(),
Index: hex.EncodeToString(stamp.Index()),
Timestamp: hex.EncodeToString(stamp.Timestamp()),
Signature: hex.EncodeToString(stamp.Sig()),
})
}
66 changes: 66 additions & 0 deletions pkg/api/envelope_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2024 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 api_test

import (
"fmt"
"net/http"
"testing"

"github.com/ethersphere/bee/v2/pkg/api"
"github.com/ethersphere/bee/v2/pkg/jsonhttp"
"github.com/ethersphere/bee/v2/pkg/jsonhttp/jsonhttptest"
mockbatchstore "github.com/ethersphere/bee/v2/pkg/postage/batchstore/mock"
mockpost "github.com/ethersphere/bee/v2/pkg/postage/mock"
)

func TestPostEnvelope(t *testing.T) {
t.Parallel()

zeroHex := "0000000000000000000000000000000000000000000000000000000000000000"
envelopeEndpoint := func(chunkAddress string) string { return fmt.Sprintf("/envelope/%s", chunkAddress) }
client, _, _, _ := newTestServer(t, testServerOptions{
Post: mockpost.New(mockpost.WithAcceptAll()),
})

t.Run("ok", func(t *testing.T) {
t.Parallel()

jsonhttptest.Request(t, client, http.MethodPost, envelopeEndpoint(zeroHex), http.StatusCreated,
jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr),
)
})

t.Run("wrong chunk address", func(t *testing.T) {
t.Parallel()

jsonhttptest.Request(t, client, http.MethodPost, envelopeEndpoint("invalid"), http.StatusBadRequest,
jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr),
)
})

t.Run("postage does not exist", func(t *testing.T) {
t.Parallel()
client, _, _, _ := newTestServer(t, testServerOptions{})

jsonhttptest.Request(t, client, http.MethodPost, envelopeEndpoint(zeroHex), http.StatusNotFound,
jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, zeroHex),
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{Message: "batch with id not found", Code: http.StatusNotFound}),
)
})

t.Run("batch unusable", func(t *testing.T) {
t.Parallel()
client, _, _, _ := newTestServer(t, testServerOptions{
Post: mockpost.New(mockpost.WithAcceptAll()),
BatchStore: mockbatchstore.New(),
})

jsonhttptest.Request(t, client, http.MethodPost, envelopeEndpoint(zeroHex), http.StatusUnprocessableEntity,
jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr),
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{Message: "batch not usable yet or does not exist", Code: http.StatusUnprocessableEntity}),
)
})
}
4 changes: 4 additions & 0 deletions pkg/api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@ func (s *Service) mountAPI() {
"HEAD": http.HandlerFunc(s.hasChunkHandler),
})

handle("/envelope/{address}", jsonhttp.MethodHandler{
"POST": http.HandlerFunc(s.envelopePostHandler),
})

handle("/soc/{owner}/{id}", jsonhttp.MethodHandler{
"POST": web.ChainHandlers(
jsonhttp.NewMaxBodyBytesHandler(swarm.ChunkWithSpanSize),
Expand Down

0 comments on commit bc3cdba

Please sign in to comment.