From 21a1777f79056554deda085a22160a5f96ad5a9a Mon Sep 17 00:00:00 2001 From: muXxer Date: Fri, 22 Mar 2024 13:40:39 +0100 Subject: [PATCH 1/4] Add transaction endpoint --- components/restapi/core/component.go | 13 +++++++-- components/restapi/core/transaction.go | 30 ++++++++++++++++++--- go.mod | 2 +- go.sum | 4 +-- pkg/requesthandler/blocks.go | 12 ++++++++- pkg/tests/booker_test.go | 2 +- pkg/tests/reward_test.go | 2 +- tools/docker-network/tests/api_core.go | 2 +- tools/docker-network/tests/api_core_test.go | 19 ++++++++++--- tools/gendoc/go.mod | 2 +- tools/gendoc/go.sum | 5 ++-- 11 files changed, 73 insertions(+), 20 deletions(-) diff --git a/components/restapi/core/component.go b/components/restapi/core/component.go index 8a01f8a51..8b35c025c 100644 --- a/components/restapi/core/component.go +++ b/components/restapi/core/component.go @@ -223,13 +223,22 @@ func configure() error { return responseByHeader(c, resp) }, checkNodeSynced()) + routeGroup.GET(api.EndpointWithEchoParameters(api.CoreEndpointTransaction), func(c echo.Context) error { + resp, err := transactionFromTransactionID(c) + if err != nil { + return err + } + + return responseByHeader(c, resp) + }) + routeGroup.GET(api.EndpointWithEchoParameters(api.CoreEndpointTransactionsIncludedBlock), func(c echo.Context) error { - block, err := blockFromTransactionID(c) + resp, err := blockFromTransactionID(c) if err != nil { return err } - return responseByHeader(c, block) + return responseByHeader(c, resp) }) routeGroup.GET(api.EndpointWithEchoParameters(api.CoreEndpointTransactionsIncludedBlockMetadata), func(c echo.Context) error { diff --git a/components/restapi/core/transaction.go b/components/restapi/core/transaction.go index b14e5000e..801d8f9ae 100644 --- a/components/restapi/core/transaction.go +++ b/components/restapi/core/transaction.go @@ -21,7 +21,7 @@ func blockIDFromTransactionID(c echo.Context) (iotago.BlockID, error) { func blockFromTransactionID(c echo.Context) (*iotago.Block, error) { blockID, err := blockIDFromTransactionID(c) if err != nil { - return nil, ierrors.WithMessagef(echo.ErrBadRequest, "failed to get block ID by transaction ID: %w", err) + return nil, ierrors.Wrap(err, "failed to get block ID by transaction ID") } return deps.RequestHandler.BlockFromBlockID(blockID) @@ -30,16 +30,40 @@ func blockFromTransactionID(c echo.Context) (*iotago.Block, error) { func blockMetadataFromTransactionID(c echo.Context) (*api.BlockMetadataResponse, error) { blockID, err := blockIDFromTransactionID(c) if err != nil { - return nil, ierrors.WithMessagef(echo.ErrBadRequest, "failed to get block ID by transaction ID: %w", err) + return nil, ierrors.Wrap(err, "failed to get block ID by transaction ID") } return deps.RequestHandler.BlockMetadataFromBlockID(blockID) } +func transactionFromTransactionID(c echo.Context) (*iotago.Transaction, error) { + txID, err := httpserver.ParseTransactionIDParam(c, api.ParameterTransactionID) + if err != nil { + return nil, ierrors.Wrap(err, "failed to parse transaction ID") + } + + blockID, err := deps.RequestHandler.BlockIDFromTransactionID(txID) + if err != nil { + return nil, ierrors.Wrap(err, "failed to get block ID by transaction ID") + } + + block, err := deps.RequestHandler.ModelBlockFromBlockID(blockID) + if err != nil { + return nil, ierrors.Wrap(err, "failed to get block by block ID") + } + + tx, isTransaction := block.SignedTransaction() + if !isTransaction { + return nil, ierrors.WithMessagef(echo.ErrBadRequest, "block %s does not contain a transaction", blockID) + } + + return tx.Transaction, nil +} + func transactionMetadataFromTransactionID(c echo.Context) (*api.TransactionMetadataResponse, error) { txID, err := httpserver.ParseTransactionIDParam(c, api.ParameterTransactionID) if err != nil { - return nil, ierrors.Wrapf(err, "failed to parse transaction ID %s", c.Param(api.ParameterTransactionID)) + return nil, ierrors.Wrap(err, "failed to parse transaction ID") } return deps.RequestHandler.TransactionMetadataFromTransactionID(txID) diff --git a/go.mod b/go.mod index 4baa4e884..7d7dd094d 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/iotaledger/hive.go/stringify v0.0.0-20240320122938-13a946cf3c7a 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-20240322111205-845f859ca28c + github.com/iotaledger/iota.go/v4 v4.0.0-20240322114706-82a1f8a8b70c github.com/labstack/echo/v4 v4.11.4 github.com/labstack/gommon v0.4.2 github.com/libp2p/go-libp2p v0.33.1 diff --git a/go.sum b/go.sum index c02bf6332..aeb46c9e1 100644 --- a/go.sum +++ b/go.sum @@ -325,8 +325,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-20240320124000-d02f37a4fdff h1:Do8fakxvFaj7dLckoo/z+mRyBdZo8QvT8HcgnQlG2Sg= github.com/iotaledger/iota-crypto-demo v0.0.0-20240320124000-d02f37a4fdff/go.mod h1:aVEutEWFnhDNJBxtVuzy2BeTN+8FAlnR83k7hKV0CFE= -github.com/iotaledger/iota.go/v4 v4.0.0-20240322111205-845f859ca28c h1:xnaAczQXgcm4FL/z6q/r5WqUsyxqsUcs/hEdikQjjQ0= -github.com/iotaledger/iota.go/v4 v4.0.0-20240322111205-845f859ca28c/go.mod h1:qn/63CB0/jE1em6ewqDSiz+ovS+E/os7K5b7g2pmJFg= +github.com/iotaledger/iota.go/v4 v4.0.0-20240322114706-82a1f8a8b70c h1:0uqpCv2txjbVi1E5AFvXkUGmTMiEX1nPzmTFH1Bfk6c= +github.com/iotaledger/iota.go/v4 v4.0.0-20240322114706-82a1f8a8b70c/go.mod h1:qn/63CB0/jE1em6ewqDSiz+ovS+E/os7K5b7g2pmJFg= 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= diff --git a/pkg/requesthandler/blocks.go b/pkg/requesthandler/blocks.go index 3f80278e1..b80d4e1f1 100644 --- a/pkg/requesthandler/blocks.go +++ b/pkg/requesthandler/blocks.go @@ -7,16 +7,26 @@ import ( "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/kvstore" + "github.com/iotaledger/iota-core/pkg/model" iotago "github.com/iotaledger/iota.go/v4" "github.com/iotaledger/iota.go/v4/api" ) -func (r *RequestHandler) BlockFromBlockID(blockID iotago.BlockID) (*iotago.Block, error) { +func (r *RequestHandler) ModelBlockFromBlockID(blockID iotago.BlockID) (*model.Block, error) { block, exists := r.protocol.Engines.Main.Get().Block(blockID) if !exists { return nil, ierrors.WithMessagef(echo.ErrNotFound, "block %s not found", blockID) } + return block, nil +} + +func (r *RequestHandler) BlockFromBlockID(blockID iotago.BlockID) (*iotago.Block, error) { + block, err := r.ModelBlockFromBlockID(blockID) + if err != nil { + return nil, err + } + return block.ProtocolBlock(), nil } diff --git a/pkg/tests/booker_test.go b/pkg/tests/booker_test.go index 7b6b78e26..333fb3e4c 100644 --- a/pkg/tests/booker_test.go +++ b/pkg/tests/booker_test.go @@ -871,7 +871,7 @@ func Test_BlockWithInvalidTransactionGetsBooked(t *testing.T) { ts.AssertBlocksInCacheConfirmed(ts.Blocks("block1"), true, ts.Nodes()...) ts.AssertTransactionsExist([]*iotago.Transaction{tx1.Transaction}, true, ts.Nodes()...) - ts.AssertTransactionFailure(lo.PanicOnErr(tx1.ID()), iotago.ErrIssuerFeatureNotUnlocked, ts.Nodes()...) + ts.AssertTransactionFailure(tx1.MustID(), iotago.ErrIssuerFeatureNotUnlocked, ts.Nodes()...) ts.AssertTransactionsInCacheAccepted([]*iotago.Transaction{tx1.Transaction}, false, ts.Nodes()...) ts.CommitUntilSlot(block1Slot, vblock3.ID()) diff --git a/pkg/tests/reward_test.go b/pkg/tests/reward_test.go index a82fa3736..3d8801186 100644 --- a/pkg/tests/reward_test.go +++ b/pkg/tests/reward_test.go @@ -250,7 +250,7 @@ func Test_RewardInputCannotPointToNFTOutput(t *testing.T) { ts.Wait(node1, node2) ts.AssertTransactionsExist([]*iotago.Transaction{tx2.Transaction}, true, node1, node2) - signedTx2ID := lo.PanicOnErr(tx2.ID()) + signedTx2ID := tx2.MustID() ts.AssertTransactionFailure(signedTx2ID, iotago.ErrRewardInputReferenceInvalid, node1, node2) } diff --git a/tools/docker-network/tests/api_core.go b/tools/docker-network/tests/api_core.go index bafa2d80e..c27c29335 100644 --- a/tools/docker-network/tests/api_core.go +++ b/tools/docker-network/tests/api_core.go @@ -238,7 +238,7 @@ func (d *DockerTestFramework) prepareAssets(totalAssetsNum int) (coreAPIAssets, latestSlot = lo.Max[iotago.SlotIndex](latestSlot, blockSlot, valueBlockSlot, delegationOutputID.CreationSlot(), secondAttachment.Slot()) fmt.Printf("Assets for slot %d\n: dataBlock: %s block: %s\ntx: %s\nbasic output: %s, faucet output: %s\n delegation output: %s\n", - valueBlockSlot, block.MustID().String(), valueBlock.MustID().String(), lo.PanicOnErr(signedTx.ID()).String(), + valueBlockSlot, block.MustID().String(), valueBlock.MustID().String(), signedTx.MustID().String(), basicOutputID.String(), faucetOutput.ID.String(), delegationOutputID.String()) } diff --git a/tools/docker-network/tests/api_core_test.go b/tools/docker-network/tests/api_core_test.go index cb086c930..e8149a421 100644 --- a/tools/docker-network/tests/api_core_test.go +++ b/tools/docker-network/tests/api_core_test.go @@ -12,7 +12,6 @@ import ( "github.com/stretchr/testify/require" - "github.com/iotaledger/hive.go/lo" iotago "github.com/iotaledger/iota.go/v4" "github.com/iotaledger/iota.go/v4/api" "github.com/iotaledger/iota.go/v4/nodeclient" @@ -282,11 +281,23 @@ func Test_CoreAPI(t *testing.T) { }) }, }, + { + name: "Test_TransactionByID", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachTransaction(t, func(t *testing.T, transaction *iotago.SignedTransaction, firstAttachmentID iotago.BlockID) { + txID := transaction.Transaction.MustID() + resp, err := d.wallet.Clients[nodeAlias].TransactionByID(context.Background(), txID) + require.NoError(t, err) + require.NotNil(t, resp) + require.EqualValues(t, txID, resp.MustID()) + }) + }, + }, { name: "Test_TransactionsIncludedBlock", testFunc: func(t *testing.T, nodeAlias string) { assetsPerSlot.forEachTransaction(t, func(t *testing.T, transaction *iotago.SignedTransaction, firstAttachmentID iotago.BlockID) { - resp, err := d.wallet.Clients[nodeAlias].TransactionIncludedBlock(context.Background(), lo.PanicOnErr(transaction.Transaction.ID())) + resp, err := d.wallet.Clients[nodeAlias].TransactionIncludedBlock(context.Background(), transaction.Transaction.MustID()) require.NoError(t, err) require.NotNil(t, resp) require.EqualValues(t, firstAttachmentID, resp.MustID()) @@ -297,7 +308,7 @@ func Test_CoreAPI(t *testing.T) { name: "Test_TransactionsIncludedBlockMetadata", testFunc: func(t *testing.T, nodeAlias string) { assetsPerSlot.forEachTransaction(t, func(t *testing.T, transaction *iotago.SignedTransaction, firstAttachmentID iotago.BlockID) { - resp, err := d.wallet.Clients[nodeAlias].TransactionIncludedBlockMetadata(context.Background(), lo.PanicOnErr(transaction.Transaction.ID())) + resp, err := d.wallet.Clients[nodeAlias].TransactionIncludedBlockMetadata(context.Background(), transaction.Transaction.MustID()) require.NoError(t, err) require.NotNil(t, resp) require.EqualValues(t, api.BlockStateFinalized, resp.BlockState) @@ -309,7 +320,7 @@ func Test_CoreAPI(t *testing.T) { name: "Test_TransactionsMetadata", testFunc: func(t *testing.T, nodeAlias string) { assetsPerSlot.forEachTransaction(t, func(t *testing.T, transaction *iotago.SignedTransaction, firstAttachmentID iotago.BlockID) { - resp, err := d.wallet.Clients[nodeAlias].TransactionMetadata(context.Background(), lo.PanicOnErr(transaction.Transaction.ID())) + resp, err := d.wallet.Clients[nodeAlias].TransactionMetadata(context.Background(), transaction.Transaction.MustID()) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, api.TransactionStateFinalized, resp.TransactionState) diff --git a/tools/gendoc/go.mod b/tools/gendoc/go.mod index b32e6a76a..ebacca7bb 100644 --- a/tools/gendoc/go.mod +++ b/tools/gendoc/go.mod @@ -75,7 +75,7 @@ require ( github.com/iotaledger/inx-app v1.0.0-rc.3.0.20240307101848-db58eb9353ec // indirect github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022 // indirect github.com/iotaledger/iota-crypto-demo v0.0.0-20240320124000-d02f37a4fdff // indirect - github.com/iotaledger/iota.go/v4 v4.0.0-20240322111205-845f859ca28c // indirect + github.com/iotaledger/iota.go/v4 v4.0.0-20240322114706-82a1f8a8b70c // indirect github.com/ipfs/boxo v0.18.0 // indirect github.com/ipfs/go-cid v0.4.1 // indirect github.com/ipfs/go-datastore v0.6.0 // indirect diff --git a/tools/gendoc/go.sum b/tools/gendoc/go.sum index be3d93d88..06afa2cf9 100644 --- a/tools/gendoc/go.sum +++ b/tools/gendoc/go.sum @@ -329,9 +329,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-20240320124000-d02f37a4fdff h1:Do8fakxvFaj7dLckoo/z+mRyBdZo8QvT8HcgnQlG2Sg= github.com/iotaledger/iota-crypto-demo v0.0.0-20240320124000-d02f37a4fdff/go.mod h1:aVEutEWFnhDNJBxtVuzy2BeTN+8FAlnR83k7hKV0CFE= -github.com/iotaledger/iota.go/v4 v4.0.0-20240321174445-4e586367e5bd h1:GD6XJA52pPknOsMTBNXC/6VB/vtxrKVW2CDdeLRt0eQ= -github.com/iotaledger/iota.go/v4 v4.0.0-20240321174445-4e586367e5bd/go.mod h1:qn/63CB0/jE1em6ewqDSiz+ovS+E/os7K5b7g2pmJFg= -github.com/iotaledger/iota.go/v4 v4.0.0-20240322111205-845f859ca28c/go.mod h1:qn/63CB0/jE1em6ewqDSiz+ovS+E/os7K5b7g2pmJFg= +github.com/iotaledger/iota.go/v4 v4.0.0-20240322114706-82a1f8a8b70c h1:0uqpCv2txjbVi1E5AFvXkUGmTMiEX1nPzmTFH1Bfk6c= +github.com/iotaledger/iota.go/v4 v4.0.0-20240322114706-82a1f8a8b70c/go.mod h1:qn/63CB0/jE1em6ewqDSiz+ovS+E/os7K5b7g2pmJFg= 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= From 8b071fa7bf6682f64f70865e72fed0989bf3f0f5 Mon Sep 17 00:00:00 2001 From: muXxer Date: Fri, 22 Mar 2024 13:43:15 +0100 Subject: [PATCH 2/4] Disable debugAPI per default, but enable it in the local docker network --- components/debugapi/params.go | 2 +- config_defaults.json | 2 +- documentation/configuration.md | 4 ++-- tools/docker-network/.env | 1 + 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/components/debugapi/params.go b/components/debugapi/params.go index 59fbadc82..98032899d 100644 --- a/components/debugapi/params.go +++ b/components/debugapi/params.go @@ -7,7 +7,7 @@ import ( // ParametersDebugAPI contains the definition of configuration parameters used by the debug API. type ParametersDebugAPI struct { // Enabled whether the DebugAPI component is enabled. - Enabled bool `default:"true" usage:"whether the DebugAPI component is enabled"` + Enabled bool `default:"false" usage:"whether the DebugAPI component is enabled"` Database struct { Path string `default:"testnet/debug" usage:"the path to the database folder"` diff --git a/config_defaults.json b/config_defaults.json index 36735f1b3..87212bda4 100644 --- a/config_defaults.json +++ b/config_defaults.json @@ -74,7 +74,7 @@ } }, "debugAPI": { - "enabled": true, + "enabled": false, "db": { "path": "testnet/debug", "maxOpenDBs": 2, diff --git a/documentation/configuration.md b/documentation/configuration.md index d126d442c..418652ad6 100644 --- a/documentation/configuration.md +++ b/documentation/configuration.md @@ -235,7 +235,7 @@ Example: | Name | Description | Type | Default value | | ------------------ | ----------------------------------------- | ------- | ------------- | -| enabled | Whether the DebugAPI component is enabled | boolean | true | +| enabled | Whether the DebugAPI component is enabled | boolean | false | | [db](#debugapi_db) | Configuration for db | object | | ### Db @@ -258,7 +258,7 @@ Example: ```json { "debugAPI": { - "enabled": true, + "enabled": false, "db": { "path": "testnet/debug", "maxOpenDBs": 2, diff --git a/tools/docker-network/.env b/tools/docker-network/.env index ca94e4ea8..09fececc7 100644 --- a/tools/docker-network/.env +++ b/tools/docker-network/.env @@ -9,6 +9,7 @@ COMMON_CONFIG=" --db.path=/app/data/database --protocol.snapshot.path=/app/data/snapshot.bin --restAPI.publicRoutes=/health,/api/routes,/api/core/v3/info,/api/core/v3/network*,/api/core/v3/blocks*,/api/core/v3/transactions*,/api/core/v3/commitments*,/api/core/v3/outputs*,/api/core/v3/accounts*,/api/core/v3/validators*,/api/core/v3/rewards*,/api/core/v3/committee*,/api/debug/v2/*,/api/indexer/v2/*,/api/mqtt/v2,/api/blockissuer/v1/*,/api/management/v1/* +--debugAPI.enabled=true " AUTOPEERING_CONFIG=" From 4317d0e73b672605dd37ca6fe24496ee05d2331f Mon Sep 17 00:00:00 2001 From: muXxer Date: Fri, 22 Mar 2024 16:54:36 +0100 Subject: [PATCH 3/4] Fixed some error returns in API and INX --- components/inx/server_commitments.go | 4 +- components/inx/server_node.go | 39 ++++++++++++------- components/inx/server_utxo.go | 13 ++++++- components/restapi/core/transaction.go | 2 +- pkg/requesthandler/commitments.go | 31 +++++++-------- tools/docker-network/tests/api_core_test.go | 14 +++---- .../tests/api_management_test.go | 1 + 7 files changed, 62 insertions(+), 42 deletions(-) diff --git a/components/inx/server_commitments.go b/components/inx/server_commitments.go index 104a0ef30..8721e313d 100644 --- a/components/inx/server_commitments.go +++ b/components/inx/server_commitments.go @@ -36,7 +36,7 @@ func (s *Server) ListenToCommitments(req *inx.SlotRangeRequest, srv inx.INX_List return status.Errorf(codes.NotFound, "commitment slot %d not found", slot) } - return err + return status.Errorf(codes.Internal, "failed to load commitment for slot %d: %s", slot, err.Error()) } if err := srv.Send(inxCommitment(commitment)); err != nil { @@ -178,7 +178,7 @@ func (s *Server) ReadCommitment(_ context.Context, req *inx.CommitmentRequest) ( return nil, status.Errorf(codes.NotFound, "commitment slot %d not found", req.GetCommitmentSlot()) } - return nil, err + return nil, status.Errorf(codes.Internal, "failed to load commitment for slot %d: %s", commitmentSlot, err.Error()) } if req.GetCommitmentId() != nil { diff --git a/components/inx/server_node.go b/components/inx/server_node.go index c6cfbaeca..e7f05db04 100644 --- a/components/inx/server_node.go +++ b/components/inx/server_node.go @@ -4,32 +4,41 @@ import ( "context" "time" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/iotaledger/hive.go/ierrors" + "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/runtime/workerpool" inx "github.com/iotaledger/inx/go" "github.com/iotaledger/iota-core/pkg/protocol/engine/syncmanager" ) -func inxNodeStatus(status *syncmanager.SyncStatus) *inx.NodeStatus { - finalizedCommitment, err := deps.Protocol.Engines.Main.Get().Storage.Commitments().Load(status.LatestFinalizedSlot) +func inxNodeStatus(syncStatus *syncmanager.SyncStatus) (*inx.NodeStatus, error) { + finalizedCommitment, err := deps.Protocol.Engines.Main.Get().Storage.Commitments().Load(syncStatus.LatestFinalizedSlot) if err != nil { - return nil + if ierrors.Is(err, kvstore.ErrKeyNotFound) { + return nil, status.Errorf(codes.NotFound, "finalized commitment (slot %d) not found", syncStatus.LatestFinalizedSlot) + } + + return nil, status.Errorf(codes.Internal, "failed to load finalized commitment (slot %d): %s", syncStatus.LatestFinalizedSlot, err.Error()) } return &inx.NodeStatus{ - IsHealthy: status.NodeSynced, - IsBootstrapped: status.NodeBootstrapped, - LastAcceptedBlockSlot: uint32(status.LastAcceptedBlockSlot), - LastConfirmedBlockSlot: uint32(status.LastConfirmedBlockSlot), - LatestCommitment: inxCommitment(status.LatestCommitment), + IsHealthy: syncStatus.NodeSynced, + IsBootstrapped: syncStatus.NodeBootstrapped, + LastAcceptedBlockSlot: uint32(syncStatus.LastAcceptedBlockSlot), + LastConfirmedBlockSlot: uint32(syncStatus.LastConfirmedBlockSlot), + LatestCommitment: inxCommitment(syncStatus.LatestCommitment), LatestFinalizedCommitment: inxCommitment(finalizedCommitment), - PruningEpoch: uint32(status.LastPrunedEpoch), - HasPruned: status.HasPruned, - } + PruningEpoch: uint32(syncStatus.LastPrunedEpoch), + HasPruned: syncStatus.HasPruned, + }, nil } func (s *Server) ReadNodeStatus(context.Context, *inx.NoParams) (*inx.NodeStatus, error) { - return inxNodeStatus(deps.Protocol.Engines.Main.Get().SyncManager.SyncStatus()), nil + return inxNodeStatus(deps.Protocol.Engines.Main.Get().SyncManager.SyncStatus()) } func (s *Server) ListenToNodeStatus(req *inx.NodeStatusRequest, srv inx.INX_ListenToNodeStatusServer) error { @@ -56,7 +65,11 @@ func (s *Server) ListenToNodeStatus(req *inx.NodeStatusRequest, srv inx.INX_List lastUpdateTimer = nil } - nodeStatus := inxNodeStatus(status) + nodeStatus, err := inxNodeStatus(status) + if err != nil { + Component.LogErrorf("failed to convert sync status to inx node status: %s", err.Error()) + return + } // Use cool-down if the node is syncing if coolDownDuration > 0 && !nodeStatus.GetIsHealthy() { diff --git a/components/inx/server_utxo.go b/components/inx/server_utxo.go index dbfc81223..193c179ae 100644 --- a/components/inx/server_utxo.go +++ b/components/inx/server_utxo.go @@ -7,6 +7,7 @@ import ( "google.golang.org/grpc/status" "github.com/iotaledger/hive.go/ierrors" + "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/runtime/workerpool" inx "github.com/iotaledger/inx/go" @@ -204,7 +205,11 @@ func (s *Server) ListenToLedgerUpdates(req *inx.SlotRangeRequest, srv inx.INX_Li createLedgerUpdatePayloadAndSend := func(slot iotago.SlotIndex, outputs utxoledger.Outputs, spents utxoledger.Spents) error { commitment, err := deps.Protocol.Engines.Main.Get().Storage.Commitments().Load(slot) if err != nil { - return status.Errorf(codes.NotFound, "commitment for slot %d not found", slot) + if ierrors.Is(err, kvstore.ErrKeyNotFound) { + return status.Errorf(codes.NotFound, "commitment for slot %d not found", slot) + } + + return status.Errorf(codes.Internal, "failed to get commitment for slot %d: %s", slot, err.Error()) } // Send Begin @@ -248,7 +253,11 @@ func (s *Server) ListenToLedgerUpdates(req *inx.SlotRangeRequest, srv inx.INX_Li for currentSlot := startSlot; currentSlot <= endSlot; currentSlot++ { stateDiff, err := deps.Protocol.Engines.Main.Get().Ledger.SlotDiffs(currentSlot) if err != nil { - return status.Errorf(codes.NotFound, "ledger update for slot %d not found", currentSlot) + if ierrors.Is(err, kvstore.ErrKeyNotFound) { + return status.Errorf(codes.NotFound, "ledger update for slot %d not found", currentSlot) + } + + return status.Errorf(codes.Internal, "failed to get ledger update for slot %d: %s", currentSlot, err.Error()) } if err := createLedgerUpdatePayloadAndSend(stateDiff.Slot, stateDiff.Outputs, stateDiff.Spents); err != nil { diff --git a/components/restapi/core/transaction.go b/components/restapi/core/transaction.go index 801d8f9ae..ddd415831 100644 --- a/components/restapi/core/transaction.go +++ b/components/restapi/core/transaction.go @@ -54,7 +54,7 @@ func transactionFromTransactionID(c echo.Context) (*iotago.Transaction, error) { tx, isTransaction := block.SignedTransaction() if !isTransaction { - return nil, ierrors.WithMessagef(echo.ErrBadRequest, "block %s does not contain a transaction", blockID) + return nil, ierrors.WithMessagef(echo.ErrInternalServerError, "block %s does not contain a transaction", blockID) } return tx.Transaction, nil diff --git a/pkg/requesthandler/commitments.go b/pkg/requesthandler/commitments.go index 545d3aa40..77b997f2a 100644 --- a/pkg/requesthandler/commitments.go +++ b/pkg/requesthandler/commitments.go @@ -17,7 +17,7 @@ func (r *RequestHandler) GetCommitmentBySlot(slot iotago.SlotIndex) (*model.Comm latest := r.protocol.Engines.Main.Get().SyncManager.LatestCommitment() if slot > latest.Slot() { - return nil, ierrors.WithMessagef(echo.ErrBadRequest, "commitment is from a future slot (%d > %d)", slot, latest.Slot()) + return nil, ierrors.WithMessagef(echo.ErrNotFound, "commitment is from a future slot (%d > %d)", slot, latest.Slot()) } commitment, err := r.protocol.Engines.Main.Get().Storage.Commitments().Load(slot) @@ -37,29 +37,18 @@ func (r *RequestHandler) GetCommitmentBySlot(slot iotago.SlotIndex) (*model.Comm // GetCommitmentByID returns the commitment for the given commitmentID. If commitmentID is empty, the latest commitment is returned. func (r *RequestHandler) GetCommitmentByID(commitmentID iotago.CommitmentID) (*model.Commitment, error) { - latest := r.protocol.Engines.Main.Get().SyncManager.LatestCommitment() if commitmentID == iotago.EmptyCommitmentID { - return latest, nil - } - - if commitmentID.Slot() > latest.Slot() { - return nil, ierrors.WithMessagef(echo.ErrBadRequest, "commitment ID (%s) is from a future slot (%d > %d)", commitmentID, commitmentID.Slot(), latest.Slot()) + // this returns the latest commitment in the case that the commitmentID is empty + return r.protocol.Engines.Main.Get().SyncManager.LatestCommitment(), nil } - commitment, err := r.protocol.Engines.Main.Get().Storage.Commitments().Load(commitmentID.Slot()) + commitment, err := r.GetCommitmentBySlot(commitmentID.Slot()) if err != nil { - if ierrors.Is(err, permanent.ErrCommitmentBeforeGenesis) { - return nil, ierrors.Chain(httpserver.ErrInvalidParameter, err) - } - if ierrors.Is(err, kvstore.ErrKeyNotFound) { - return nil, ierrors.WithMessagef(echo.ErrNotFound, "commitment not found, commitmentID: %s, slot: %d", commitmentID, commitmentID.Slot()) - } - - return nil, ierrors.WithMessagef(echo.ErrInternalServerError, "failed to load commitment, commitmentID: %s, slot: %d, error: %w", commitmentID, commitmentID.Slot(), err) + return nil, err } if commitment.ID() != commitmentID { - return nil, ierrors.WithMessagef(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.WithMessagef(echo.ErrInternalServerError, "commitment in the store for slot %d does not match the given commitmentID (%s != %s)", commitmentID.Slot(), commitment.ID(), commitmentID) } return commitment, nil @@ -120,6 +109,10 @@ func (r *RequestHandler) GetUTXOChangesFullBySlot(slot iotago.SlotIndex) (*api.U func (r *RequestHandler) getUTXOChanges(commitmentID iotago.CommitmentID) (*api.UTXOChangesResponse, error) { diffs, err := r.protocol.Engines.Main.Get().Ledger.SlotDiffs(commitmentID.Slot()) if err != nil { + if ierrors.Is(err, kvstore.ErrKeyNotFound) { + return nil, ierrors.WithMessagef(echo.ErrNotFound, "slot diffs not found, commitmentID: %s, slot: %d", commitmentID, commitmentID.Slot()) + } + return nil, ierrors.WithMessagef(echo.ErrInternalServerError, "failed to get slot diffs, commitmentID: %s, slot: %d, error: %w", commitmentID, commitmentID.Slot(), err) } @@ -144,6 +137,10 @@ 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 { + if ierrors.Is(err, kvstore.ErrKeyNotFound) { + return nil, ierrors.WithMessagef(echo.ErrNotFound, "slot diffs not found, commitmentID: %s, slot: %d", commitmentID, commitmentID.Slot()) + } + return nil, ierrors.WithMessagef(echo.ErrInternalServerError, "failed to get slot diffs, commitmentID: %s, slot: %d, error: %w", commitmentID, commitmentID.Slot(), err) } diff --git a/tools/docker-network/tests/api_core_test.go b/tools/docker-network/tests/api_core_test.go index e8149a421..562d355dd 100644 --- a/tools/docker-network/tests/api_core_test.go +++ b/tools/docker-network/tests/api_core_test.go @@ -489,7 +489,7 @@ func Test_CoreAPI_BadRequests(t *testing.T) { slot := iotago.SlotIndex(1000_000_000) resp, err := d.wallet.Clients[nodeAlias].CommitmentBySlot(context.Background(), slot) require.Error(t, err) - require.True(t, isStatusCode(err, http.StatusBadRequest)) + require.True(t, isStatusCode(err, http.StatusNotFound)) require.Nil(t, resp) }, }, @@ -499,7 +499,7 @@ func Test_CoreAPI_BadRequests(t *testing.T) { committmentID := tpkg.RandCommitmentID() resp, err := d.wallet.Clients[nodeAlias].CommitmentByID(context.Background(), committmentID) require.Error(t, err) - require.True(t, isStatusCode(err, http.StatusBadRequest)) + require.True(t, isStatusCode(err, http.StatusNotFound)) require.Nil(t, resp) }, }, @@ -532,7 +532,7 @@ func Test_CoreAPI_BadRequests(t *testing.T) { slot := iotago.SlotIndex(1000_000_000) resp, err := d.wallet.Clients[nodeAlias].CommitmentUTXOChangesBySlot(context.Background(), slot) require.Error(t, err) - require.True(t, isStatusCode(err, http.StatusBadRequest)) + require.True(t, isStatusCode(err, http.StatusNotFound)) require.Nil(t, resp) }, }, @@ -543,7 +543,7 @@ func Test_CoreAPI_BadRequests(t *testing.T) { resp, err := d.wallet.Clients[nodeAlias].CommitmentUTXOChangesFullBySlot(context.Background(), slot) require.Error(t, err) - require.True(t, isStatusCode(err, http.StatusBadRequest)) + require.True(t, isStatusCode(err, http.StatusNotFound)) require.Nil(t, resp) }, }, @@ -586,7 +586,7 @@ func Test_CoreAPI_BadRequests(t *testing.T) { txID := tpkg.RandTransactionID() resp, err := d.wallet.Clients[nodeAlias].TransactionIncludedBlock(context.Background(), txID) require.Error(t, err) - require.True(t, isStatusCode(err, http.StatusBadRequest)) + require.True(t, isStatusCode(err, http.StatusNotFound)) require.Nil(t, resp) }, }, @@ -597,7 +597,7 @@ func Test_CoreAPI_BadRequests(t *testing.T) { resp, err := d.wallet.Clients[nodeAlias].TransactionIncludedBlockMetadata(context.Background(), txID) require.Error(t, err) - require.True(t, isStatusCode(err, http.StatusBadRequest)) + require.True(t, isStatusCode(err, http.StatusNotFound)) require.Nil(t, resp) }, }, @@ -619,7 +619,7 @@ func Test_CoreAPI_BadRequests(t *testing.T) { commitmentID := tpkg.RandCommitmentID() resp, err := d.wallet.Clients[nodeAlias].Congestion(context.Background(), accountAddress, 0, commitmentID) require.Error(t, err) - require.True(t, isStatusCode(err, http.StatusBadRequest)) + require.True(t, isStatusCode(err, http.StatusNotFound)) require.Nil(t, resp) }, }, diff --git a/tools/docker-network/tests/api_management_test.go b/tools/docker-network/tests/api_management_test.go index 46583e910..da548cbfd 100644 --- a/tools/docker-network/tests/api_management_test.go +++ b/tools/docker-network/tests/api_management_test.go @@ -14,6 +14,7 @@ import ( ) func getContextWithTimeout(duration time.Duration) context.Context { + //nolint:lostcancel ctx, _ := context.WithTimeout(context.Background(), duration) return ctx } From b259203d47e6b0202dcfefb51d34de23d02485b1 Mon Sep 17 00:00:00 2001 From: muXxer Date: Fri, 22 Mar 2024 17:12:44 +0100 Subject: [PATCH 4/4] Return 404 if output is not found --- pkg/requesthandler/transaction.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/requesthandler/transaction.go b/pkg/requesthandler/transaction.go index 8e26961d2..c5d4f8481 100644 --- a/pkg/requesthandler/transaction.go +++ b/pkg/requesthandler/transaction.go @@ -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/retainer/txretainer" iotago "github.com/iotaledger/iota.go/v4" @@ -16,6 +17,10 @@ func (r *RequestHandler) BlockIDFromTransactionID(transactionID iotago.Transacti output, spent, err := r.protocol.Engines.Main.Get().Ledger.OutputOrSpent(outputID) if err != nil { + if ierrors.Is(err, kvstore.ErrKeyNotFound) { + return iotago.EmptyBlockID, ierrors.WithMessagef(echo.ErrNotFound, "output %s not found", outputID.ToHex()) + } + return iotago.EmptyBlockID, ierrors.WithMessagef(echo.ErrInternalServerError, "failed to get output %s: %w", outputID.ToHex(), err) }