Skip to content

Commit

Permalink
Merge pull request #817 from iotaledger/test/rest-api
Browse files Browse the repository at this point in the history
Test REST API with the Docker test framework
  • Loading branch information
muXxer authored Mar 12, 2024
2 parents 35acece + 6a74c6b commit 8144a57
Show file tree
Hide file tree
Showing 21 changed files with 1,175 additions and 221 deletions.
28 changes: 20 additions & 8 deletions components/restapi/core/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

func congestionByAccountAddress(c echo.Context) (*api.CongestionResponse, error) {
commitmentID, err := httpserver.ParseCommitmentIDQueryParam(c, api.ParameterCommitmentID)
queryCommitmentID, err := httpserver.ParseCommitmentIDQueryParam(c, api.ParameterCommitmentID)
if err != nil {
return nil, err
}
Expand All @@ -26,8 +26,19 @@ func congestionByAccountAddress(c echo.Context) (*api.CongestionResponse, error)
if workScore != 0 {
workScores = append(workScores, workScore)
}
maxCommittableAge := deps.RequestHandler.CommittedAPI().ProtocolParameters().MaxCommittableAge()
latestCommittedSlot := deps.RequestHandler.GetLatestCommitment().Slot()
if queryCommitmentID != iotago.EmptyCommitmentID {
if latestCommittedSlot >= maxCommittableAge && queryCommitmentID.Slot()+maxCommittableAge < latestCommittedSlot {
return nil, ierrors.Wrapf(echo.ErrBadRequest, "invalid commitmentID, target slot index older than allowed (%d<%d)", queryCommitmentID.Slot(), latestCommittedSlot-maxCommittableAge)
}

if queryCommitmentID.Slot() > latestCommittedSlot {
return nil, ierrors.Wrapf(echo.ErrBadRequest, "invalid commitmentID, slot %d is not committed yet, latest committed slot: %d", queryCommitmentID.Slot(), latestCommittedSlot)
}
}

commitment, err := deps.RequestHandler.GetCommitmentByID(commitmentID)
queryCommittment, err := deps.RequestHandler.GetCommitmentByID(queryCommitmentID)
if err != nil {
return nil, err
}
Expand All @@ -43,7 +54,7 @@ func congestionByAccountAddress(c echo.Context) (*api.CongestionResponse, error)
return nil, ierrors.Wrapf(httpserver.ErrInvalidParameter, "address %s is not an account address", c.Param(api.ParameterBech32Address))
}

return deps.RequestHandler.CongestionByAccountAddress(accountAddress, commitment, workScores...)
return deps.RequestHandler.CongestionByAccountAddress(accountAddress, queryCommittment, workScores...)
}

func validators(c echo.Context) (*api.ValidatorsResponse, error) {
Expand All @@ -61,10 +72,6 @@ func validators(c echo.Context) (*api.ValidatorsResponse, error) {
}
}

// do not respond to really old requests
if requestedSlot+iotago.SlotIndex(restapi.ParamsRestAPI.MaxRequestedSlotAge) < latestCommittedSlot {
return nil, ierrors.Wrapf(echo.ErrBadRequest, "request is too old, request started at %d, latest committed slot index is %d", requestedSlot, latestCommittedSlot)
}
slotRange := uint32(requestedSlot) / restapi.ParamsRestAPI.RequestsMemoryCacheGranularity

return deps.RequestHandler.Validators(slotRange, cursorIndex, pageSize)
Expand All @@ -86,14 +93,14 @@ func validatorByAccountAddress(c echo.Context) (*api.ValidatorResponse, error) {
}

func rewardsByOutputID(c echo.Context) (*api.ManaRewardsResponse, error) {
var err error
outputID, err := httpserver.ParseOutputIDParam(c, api.ParameterOutputID)
if err != nil {
return nil, ierrors.Wrapf(err, "failed to parse output ID %s", c.Param(api.ParameterOutputID))
}

var slot iotago.SlotIndex
if len(c.QueryParam(api.ParameterSlot)) > 0 {
var err error
slot, err = httpserver.ParseSlotQueryParam(c, api.ParameterSlot)
if err != nil {
return nil, ierrors.Wrapf(err, "failed to parse slot index %s", c.Param(api.ParameterSlot))
Expand All @@ -113,6 +120,7 @@ func rewardsByOutputID(c echo.Context) (*api.ManaRewardsResponse, error) {

func selectedCommittee(c echo.Context) (*api.CommitteeResponse, error) {
var epoch iotago.EpochIndex

if len(c.QueryParam(api.ParameterEpoch)) == 0 {
// by default we return current epoch
epoch = deps.RequestHandler.CommittedAPI().TimeProvider().CurrentEpoch()
Expand All @@ -122,6 +130,10 @@ func selectedCommittee(c echo.Context) (*api.CommitteeResponse, error) {
if err != nil {
return nil, err
}
currentEpoch := deps.RequestHandler.CommittedAPI().TimeProvider().CurrentEpoch()
if epoch > currentEpoch {
return nil, ierrors.Wrapf(echo.ErrBadRequest, "provided epoch %d is from the future, current epoch: %d", epoch, currentEpoch)
}
}

return deps.RequestHandler.SelectedCommittee(epoch)
Expand Down
4 changes: 2 additions & 2 deletions components/restapi/core/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ func configure() error {
}

return responseByHeader(c, resp)
})
}, checkNodeSynced())

routeGroup.GET(api.EndpointWithEchoParameters(api.CoreEndpointOutputWithMetadata), func(c echo.Context) error {
resp, err := outputWithMetadataFromOutputID(c)
Expand All @@ -231,7 +231,7 @@ func configure() error {
}

return responseByHeader(c, resp)
})
}, checkNodeSynced())

routeGroup.GET(api.EndpointWithEchoParameters(api.CoreEndpointTransactionsIncludedBlock), func(c echo.Context) error {
block, err := blockFromTransactionID(c)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ require (
github.com/iotaledger/hive.go/stringify v0.0.0-20240307102857-7e23a3c59bd2
github.com/iotaledger/inx-app v1.0.0-rc.3.0.20240307101848-db58eb9353ec
github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022
github.com/iotaledger/iota.go/v4 v4.0.0-20240307091827-db3c503615a6
github.com/iotaledger/iota.go/v4 v4.0.0-20240307175623-0904c71fcb38
github.com/labstack/echo/v4 v4.11.4
github.com/labstack/gommon v0.4.2
github.com/libp2p/go-libp2p v0.33.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,8 @@ github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022 h1:I178Sa
github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022/go.mod h1:jTFxIWiMUdAwO263jlJCSWcNLqEkgYEVOFXfjp5aNJM=
github.com/iotaledger/iota-crypto-demo v0.0.0-20240216103559-27ca8dffd1e7 h1:t6k4MqiUov0FrBb2o2JhKlOVSdlPbIQWM8ivYHL0G0g=
github.com/iotaledger/iota-crypto-demo v0.0.0-20240216103559-27ca8dffd1e7/go.mod h1:do+N3LpeDEi9qselEC4XcjqGoRc7cWGiqBtIeBOKEMs=
github.com/iotaledger/iota.go/v4 v4.0.0-20240307091827-db3c503615a6 h1:dzOaHlQDJecS3SDqwaUDQfTYjZDvwqRdn1/6bCjFpgM=
github.com/iotaledger/iota.go/v4 v4.0.0-20240307091827-db3c503615a6/go.mod h1:8UQOTI7CC5R/3TurawUFuBZbkb37RzW8m4q8Hp7ct30=
github.com/iotaledger/iota.go/v4 v4.0.0-20240307175623-0904c71fcb38 h1:NizJ3CALLCcJowtAtkNuDlpE4gd4qjaWZkp/kTZfeYk=
github.com/iotaledger/iota.go/v4 v4.0.0-20240307175623-0904c71fcb38/go.mod h1:8UQOTI7CC5R/3TurawUFuBZbkb37RzW8m4q8Hp7ct30=
github.com/ipfs/boxo v0.18.0 h1:MOL9/AgoV3e7jlVMInicaSdbgralfqSsbkc31dZ9tmw=
github.com/ipfs/boxo v0.18.0/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80=
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
Expand Down
10 changes: 9 additions & 1 deletion pkg/requesthandler/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/labstack/echo/v4"

"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/kvstore"
"github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/iota-core/pkg/core/account"
"github.com/iotaledger/iota-core/pkg/model"
Expand Down Expand Up @@ -100,6 +101,10 @@ func (r *RequestHandler) ValidatorByAccountAddress(accountAddress *iotago.Accoun
func (r *RequestHandler) RewardsByOutputID(outputID iotago.OutputID, slot iotago.SlotIndex) (*api.ManaRewardsResponse, error) {
utxoOutput, err := r.protocol.Engines.Main.Get().Ledger.Output(outputID)
if err != nil {
if ierrors.Is(err, kvstore.ErrKeyNotFound) {
return nil, ierrors.Wrapf(echo.ErrNotFound, "output %s not found", outputID.ToHex())
}

return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get output %s from ledger: %s", outputID.ToHex(), err)
}

Expand Down Expand Up @@ -156,16 +161,19 @@ func (r *RequestHandler) RewardsByOutputID(outputID iotago.OutputID, slot iotago
delegationEnd,
claimingEpoch,
)
default:
return nil, ierrors.Wrapf(echo.ErrBadRequest, "output %s is neither a delegation output nor account", outputID.ToHex())
}

if err != nil {
return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to calculate reward for output %s: %s", outputID.ToHex(), err)
}

latestCommittedEpochPoolRewards, poolRewardExists, err := r.protocol.Engines.Main.Get().SybilProtection.PoolRewardsForAccount(stakingPoolValidatorAccountID)

if err != nil {
return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to retrieve pool rewards for account %s: %s", stakingPoolValidatorAccountID.ToHex(), err)
}

if !poolRewardExists {
latestCommittedEpochPoolRewards = 0
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/requesthandler/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/labstack/echo/v4"

"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/kvstore"
"github.com/iotaledger/inx-app/pkg/httpserver"
iotago "github.com/iotaledger/iota.go/v4"
"github.com/iotaledger/iota.go/v4/api"
Expand All @@ -23,6 +24,10 @@ func (r *RequestHandler) BlockByID(blockID iotago.BlockID) (*iotago.Block, error
func (r *RequestHandler) BlockMetadataByBlockID(blockID iotago.BlockID) (*api.BlockMetadataResponse, error) {
blockMetadata, err := r.protocol.Engines.Main.Get().BlockRetainer.BlockMetadata(blockID)
if err != nil {
if ierrors.Is(err, kvstore.ErrKeyNotFound) {
return nil, ierrors.Wrapf(echo.ErrNotFound, "block not found: %s", blockID.ToHex())
}

return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get block metadata %s: %s", blockID.ToHex(), err)
}

Expand Down
10 changes: 5 additions & 5 deletions pkg/requesthandler/commitments.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func (r *RequestHandler) GetCommitmentBySlot(slot iotago.SlotIndex) (*model.Comm

commitment, err := r.protocol.Engines.Main.Get().Storage.Commitments().Load(slot)
if err != nil {
return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to load commitment, slot: %d, error: %w", slot, err)
return nil, ierrors.Join(echo.ErrInternalServerError, ierrors.Wrapf(err, "failed to load commitment, slot: %d", slot))
}

return commitment, nil
Expand All @@ -38,11 +38,11 @@ func (r *RequestHandler) GetCommitmentByID(commitmentID iotago.CommitmentID) (*m

commitment, err := r.protocol.Engines.Main.Get().Storage.Commitments().Load(commitmentID.Slot())
if err != nil {
return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to load commitment, commitmentID: %s, slot: %d, error: %w", commitmentID, commitmentID.Slot(), err)
return nil, ierrors.Join(echo.ErrInternalServerError, ierrors.Wrapf(err, "failed to load commitment, commitmentID: %s, slot: %d", commitmentID, commitmentID.Slot()))
}

if commitment.ID() != commitmentID {
return nil, ierrors.Wrapf(echo.ErrBadRequest, "commitment in the store for slot %d does not match the given commitmentID (%s != %s)", commitmentID.Slot(), commitment.ID(), commitmentID)
return nil, ierrors.Join(echo.ErrBadRequest, ierrors.Wrapf(err, "commitment in the store for slot %d does not match the given commitmentID (%s != %s)", commitmentID.Slot(), commitment.ID(), commitmentID))
}

return commitment, nil
Expand All @@ -55,7 +55,7 @@ func (r *RequestHandler) GetLatestCommitment() *model.Commitment {
func (r *RequestHandler) GetUTXOChanges(commitmentID iotago.CommitmentID) (*api.UTXOChangesResponse, error) {
diffs, err := r.protocol.Engines.Main.Get().Ledger.SlotDiffs(commitmentID.Slot())
if err != nil {
return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get slot diffs, commitmentID: %s, slot: %d, error: %w", commitmentID, commitmentID.Slot(), err)
return nil, ierrors.Join(echo.ErrInternalServerError, ierrors.Wrapf(err, "failed to get slot diffs, commitmentID: %s, slot: %d", commitmentID, commitmentID.Slot()))
}

createdOutputs := make(iotago.OutputIDs, len(diffs.Outputs))
Expand All @@ -79,7 +79,7 @@ func (r *RequestHandler) GetUTXOChanges(commitmentID iotago.CommitmentID) (*api.
func (r *RequestHandler) GetUTXOChangesFull(commitmentID iotago.CommitmentID) (*api.UTXOChangesFullResponse, error) {
diffs, err := r.protocol.Engines.Main.Get().Ledger.SlotDiffs(commitmentID.Slot())
if err != nil {
return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get slot diffs, commitmentID: %s, slot: %d, error: %w", commitmentID, commitmentID.Slot(), err)
return nil, ierrors.Join(echo.ErrInternalServerError, ierrors.Wrapf(err, "failed to get slot diffs, commitmentID: %s, slot: %d", commitmentID, commitmentID.Slot()))
}

createdOutputs := make([]*api.OutputWithID, len(diffs.Outputs))
Expand Down
13 changes: 13 additions & 0 deletions pkg/requesthandler/utxo.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/labstack/echo/v4"

"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/kvstore"
"github.com/iotaledger/iota-core/pkg/protocol/engine/utxoledger"
iotago "github.com/iotaledger/iota.go/v4"
"github.com/iotaledger/iota.go/v4/api"
Expand All @@ -12,6 +13,10 @@ import (
func (r *RequestHandler) OutputFromOutputID(outputID iotago.OutputID) (*api.OutputResponse, error) {
output, err := r.protocol.Engines.Main.Get().Ledger.Output(outputID)
if err != nil {
if ierrors.Is(err, kvstore.ErrKeyNotFound) {
return nil, ierrors.Wrapf(echo.ErrNotFound, "output %s not found in the Ledger", outputID.ToHex())
}

return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get output %s from the Ledger: %s", outputID.ToHex(), err)
}

Expand All @@ -24,6 +29,10 @@ func (r *RequestHandler) OutputFromOutputID(outputID iotago.OutputID) (*api.Outp
func (r *RequestHandler) OutputMetadataFromOutputID(outputID iotago.OutputID) (*api.OutputMetadata, error) {
output, spent, err := r.protocol.Engines.Main.Get().Ledger.OutputOrSpent(outputID)
if err != nil {
if ierrors.Is(err, kvstore.ErrKeyNotFound) {
return nil, ierrors.Wrapf(echo.ErrNotFound, "output %s not found in the Ledger", outputID.ToHex())
}

return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get output %s from the Ledger: %s", outputID.ToHex(), err)
}

Expand All @@ -37,6 +46,10 @@ func (r *RequestHandler) OutputMetadataFromOutputID(outputID iotago.OutputID) (*
func (r *RequestHandler) OutputWithMetadataFromOutputID(outputID iotago.OutputID) (*api.OutputWithMetadataResponse, error) {
output, spent, err := r.protocol.Engines.Main.Get().Ledger.OutputOrSpent(outputID)
if err != nil {
if ierrors.Is(err, kvstore.ErrKeyNotFound) {
return nil, ierrors.Wrapf(echo.ErrNotFound, "output %s not found in the Ledger", outputID.ToHex())
}

return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get output %s from the Ledger: %s", outputID.ToHex(), err)
}

Expand Down
23 changes: 11 additions & 12 deletions pkg/retainer/blockretainer/block_retainer.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,18 @@ func (r *BlockRetainer) getBlockMetadata(blockID iotago.BlockID) (*slotstore.Blo
return nil, err
}

data, found := store.BlockMetadata(blockID)
if !found {
return nil, ierrors.Errorf("block %s not found", blockID.String())
data, err := store.BlockMetadata(blockID)
if err != nil {
return nil, ierrors.Wrapf(err, "block %s not found", blockID.String())
}

return data, nil
}

func (r *BlockRetainer) BlockMetadata(blockID iotago.BlockID) (*api.BlockMetadataResponse, error) {
blockStatus := r.blockState(blockID)
if blockStatus == api.BlockStateUnknown {
return nil, ierrors.Errorf("block %s not found", blockID.ToHex())
blockStatus, err := r.blockState(blockID)
if err != nil {
return nil, ierrors.Wrapf(err, "block %s not found", blockID.ToHex())
}

// we do not expose accepted flag
Expand All @@ -130,25 +130,24 @@ func (r *BlockRetainer) BlockMetadata(blockID iotago.BlockID) (*api.BlockMetadat
}, nil
}

func (r *BlockRetainer) blockState(blockID iotago.BlockID) api.BlockState {
func (r *BlockRetainer) blockState(blockID iotago.BlockID) (api.BlockState, error) {
blockMetadata, err := r.getBlockMetadata(blockID)
if err != nil {
r.errorHandler(ierrors.Wrapf(err, "could not get block data for slot %d", blockID.Slot()))
return api.BlockStateUnknown
return api.BlockStateUnknown, err
}

switch blockMetadata.State {
case api.BlockStatePending, api.BlockStateDropped:
if blockID.Slot() <= r.latestCommittedSlotFunc() {
return api.BlockStateOrphaned
return api.BlockStateOrphaned, nil
}
case api.BlockStateAccepted, api.BlockStateConfirmed:
if blockID.Slot() <= r.finalizedSlotFunc() {
return api.BlockStateFinalized
return api.BlockStateFinalized, nil
}
}

return blockMetadata.State
return blockMetadata.State, nil
}

// OnBlockBooked triggers storing block in the retainer on block booked event.
Expand Down
6 changes: 3 additions & 3 deletions pkg/storage/prunable/slotstore/block_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,11 @@ func (r *BlockMetadataStore) StoreBlockDropped(blockID iotago.BlockID) error {
return r.blockMetadataStore.Set(blockID, blockMetadata)
}

func (r *BlockMetadataStore) BlockMetadata(blockID iotago.BlockID) (*BlockMetadata, bool) {
func (r *BlockMetadataStore) BlockMetadata(blockID iotago.BlockID) (*BlockMetadata, error) {
blockMetadata, err := r.blockMetadataStore.Get(blockID)
if err != nil {
return nil, false
return nil, err
}

return blockMetadata, true
return blockMetadata, nil
}
8 changes: 4 additions & 4 deletions tools/docker-network/tests/committeerotation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,10 @@ func Test_Delegation(t *testing.T) {
account := d.CreateAccount()

// delegate all faucet funds to V2, V2 should replace V3
delegationStartEpoch := d.DelegateToValidator(account.ID, d.Node("V2"))
d.AssertCommittee(delegationStartEpoch+1, d.AccountsFromNodes(d.Nodes("V1", "V2", "V4")...))
_, delegationOutput := d.DelegateToValidator(account.ID, d.Node("V2").AccountAddress(t))
d.AssertCommittee(delegationOutput.StartEpoch+1, d.AccountsFromNodes(d.Nodes("V1", "V2", "V4")...))

// delegate all faucet funds to V3, V3 should replace V1
delegationStartEpoch = d.DelegateToValidator(account.ID, d.Node("V3"))
d.AssertCommittee(delegationStartEpoch+1, d.AccountsFromNodes(d.Nodes("V2", "V3", "V4")...))
_, delegationOutput = d.DelegateToValidator(account.ID, d.Node("V3").AccountAddress(t))
d.AssertCommittee(delegationOutput.StartEpoch+1, d.AccountsFromNodes(d.Nodes("V2", "V3", "V4")...))
}
Loading

0 comments on commit 8144a57

Please sign in to comment.