diff --git a/packages/cacti-ledger-browser/README.md b/packages/cacti-ledger-browser/README.md index 950cbafbd1..b396c9d9e3 100644 --- a/packages/cacti-ledger-browser/README.md +++ b/packages/cacti-ledger-browser/README.md @@ -1,6 +1,6 @@ # `@hyperledger/cacti-ledger-browser` -This component allows viewing ledger data in Supabase or other postgreSQL compatible database. The data is fed to supabase by persistence plugins for each ledgers. +This component allows viewing ledger data in Supabase or other PostgreSQL compatible database. The data is fed to supabase by persistence plugins for each ledgers. ## Summary @@ -8,9 +8,6 @@ This component allows viewing ledger data in Supabase or other postgreSQL compat - [Summary](#summary) - [Remarks](#remarks) - [Getting Started](#getting-started) - - [Prerequisites using yarn](#prerequisites-using-yarn) - - [Alternative Prerequisites using npm](#alternative-prerequisites-using-npm) - - [Usage](#usage) - [Contributing](#contributing) - [License](#license) - [Acknowledgments](#acknowledgments) @@ -22,35 +19,9 @@ This component allows viewing ledger data in Supabase or other postgreSQL compat ## Getting Started -Clone the git repository on your local machine. Follow these instructions that will get you a copy of the project up and running on your local machine for development and testing purposes. +Clone the git repository on your local machine. -### Prerequisites using yarn - -In the root of the project, execute the command to install and build the dependencies. It will also build this GUI front-end component: - -```sh -yarn run build -``` - -### Alternative Prerequisites using npm - -In the root of the project, execute the command to install and build the dependencies. It will also build this GUI front-end component: - -```sh -npm install -``` - -### Usage - -- Run Supabase instance (see documentation for detailed instructions). For development purposes, you can use our image located in `tools/docker/supabase-all-in-one`. -- Run one or more persistence plugins: - - [Ethereum](../cacti-plugin-persistence-ethereum) - - [Fabric] (../cacti-plugin-persistence-fabric) -- Edit Supabase configuration files, set correct supabase API URL and service_role key. - - ./src/main/typescript/common/supabase-client.tsx - - ./src/main/typescript/common/queries.ts -- Execute `yarn run start` or `npm start` in this package directory. -- The running application address: http://localhost:3001/ (can be changed in [Vite configuration](./vite.config.ts)) +See [docs/docs/cactus/ledger-browser/setup.md](../../docs/docs/cactus/ledger-browser/setup.md) for detailed information on how to setup and use this package. ## Contributing diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/index.tsx b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/index.tsx index 29e3a84703..abd26442d8 100644 --- a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/index.tsx +++ b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/index.tsx @@ -4,6 +4,7 @@ import Dashboard from "./pages/Dashboard/Dashboard"; import Blocks from "./pages/Blocks/Blocks"; import Transactions from "./pages/Transactions/Transactions"; import TransactionDetails from "./pages/TransactionDetails/TransactionDetails"; +import Discovery from "./pages/Discovery/Discovery"; import { AppInstancePersistencePluginOptions, AppDefinition, @@ -56,11 +57,19 @@ const fabricBrowserAppDefinition: AppDefinition = { title: "Dashboard", url: "/", }, + { + title: "Discovery", + url: "/discovery", + }, ], routes: [ { element: , }, + { + path: "discovery", + element: , + }, { path: "blocks", element: , diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/Discovery.tsx b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/Discovery.tsx new file mode 100644 index 0000000000..78c0f9c320 --- /dev/null +++ b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/Discovery.tsx @@ -0,0 +1,91 @@ +import React from "react"; +import { useQuery } from "@tanstack/react-query"; +import Typography from "@mui/material/Typography"; +import Box from "@mui/material/Box"; +import List from "@mui/material/List/List"; +import ListItem from "@mui/material/ListItem/ListItem"; +import ListItemButton from "@mui/material/ListItemButton/ListItemButton"; +import ListItemText from "@mui/material/ListItemText/ListItemText"; +import DoubleArrowIcon from "@mui/icons-material/DoubleArrow"; +import ListItemIcon from "@mui/material/ListItemIcon/ListItemIcon"; +import CircularProgress from "@mui/material/CircularProgress/CircularProgress"; +import Divider from "@mui/material/Divider/Divider"; + +import { DiscoveryMSP } from "../../supabase-types"; +import PageTitle from "../../../../components/ui/PageTitle"; +import { useNotification } from "../../../../common/context/NotificationContext"; +import { fabricDiscoveryMSPs } from "../../queries"; +import DiscoveryDetails from "./DiscoveryDetails"; + +function Discovery() { + const { showNotification } = useNotification(); + const [selectedMSP, setSelectedMSP] = React.useState< + DiscoveryMSP | undefined + >(undefined); + const { isError, isPending, data, error } = useQuery(fabricDiscoveryMSPs()); + + React.useEffect(() => { + isError && + showNotification(`Could not fetch discovery MSPs: ${error}`, "error"); + }, [isError]); + + const msps = data ?? []; + + return ( + + Discovery + + + + + Select MSP + + + {isPending && ( + + + + )} + + + {msps && ( + + {msps.map((msp) => ( + + setSelectedMSP(msp)} + selected={msp.id === (selectedMSP?.id ?? "")} + sx={(theme) => ({ + "&.Mui-selected": { + backgroundColor: theme.palette.primary.main, + color: "white", + }, + })} + > + + + + + + + ))} + + )} + + + + + {} + + + ); +} + +export default Discovery; diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/DiscoveryDetails.tsx b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/DiscoveryDetails.tsx new file mode 100644 index 0000000000..bdde8b056c --- /dev/null +++ b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/DiscoveryDetails.tsx @@ -0,0 +1,127 @@ +import React from "react"; +import { useQuery } from "@tanstack/react-query"; +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography/Typography"; +import Card from "@mui/material/Card/Card"; +import CardContent from "@mui/material/CardContent/CardContent"; +import List from "@mui/material/List/List"; +import ListItem from "@mui/material/ListItem/ListItem"; +import ListItemText from "@mui/material/ListItemText/ListItemText"; +import CircularProgress from "@mui/material/CircularProgress/CircularProgress"; + +import { useNotification } from "../../../../common/context/NotificationContext"; +import { fabricDiscoveryNodes } from "../../queries"; +import { DiscoveryMSP } from "../../supabase-types"; +import PeerCardButton from "./PeerCardButton"; +import OrdererCardButton from "./OrdererCardButton"; + +function NothingSelectedMessage() { + return ( + + + Select MSP on the left to display ledger components. + + + ); +} + +function LoadingSpinner() { + return ( + + + + ); +} + +interface DiscoveryDetailsProps { + msp: DiscoveryMSP | undefined; +} + +function DiscoveryDetails({ msp }: DiscoveryDetailsProps) { + const { showNotification } = useNotification(); + const { isError, isPending, data, error } = useQuery( + fabricDiscoveryNodes(msp?.id), + ); + React.useEffect(() => { + isError && + showNotification( + `Could not fetch ledger components for MSP ${msp?.name ?? "(Unknown)"}: ${error}`, + "error", + ); + }, [isError]); + + if (!msp) { + return ; + } + + if (isPending) { + return ; + } + + const mspOUString = JSON.parse(msp.organizational_unit_identifiers).join( + ", ", + ); + + const peers = data?.peers ?? []; + const orderers = data?.orderers ?? []; + + return ( + + + + + {msp.name} ({msp.mspid}) + + + + + + + + + + + + + + Peers + + + {peers.map((p) => ( + + ))} + {peers.length === 0 && ( + No peers defined for this MSP + )} + + + + Orderers + + + {orderers.map((o) => ( + + ))} + {orderers.length === 0 && ( + No orderers defined for this MSP + )} + + + ); +} + +export default DiscoveryDetails; diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/OrdererCardButton.tsx b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/OrdererCardButton.tsx new file mode 100644 index 0000000000..f0f9ab3d42 --- /dev/null +++ b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/OrdererCardButton.tsx @@ -0,0 +1,39 @@ +import * as React from "react"; +import Button from "@mui/material/Button"; +import Dialog from "@mui/material/Dialog"; +import DialogTitle from "@mui/material/DialogTitle"; +import DialogContent from "@mui/material/DialogContent"; + +import OrdererDetailsBox from "./OrdererDetailsBox"; +import { DiscoveryOrderer } from "../../supabase-types"; + +interface OrdererCardButtonProps { + orderer: DiscoveryOrderer; +} + +export default function OrdererCardButton({ orderer }: OrdererCardButtonProps) { + const [openDialog, setOpenDialog] = React.useState(false); + + return ( + <> + + setOpenDialog(false)} + open={openDialog} + > + Orderer Details + + + + + + ); +} diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/OrdererDetailsBox.tsx b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/OrdererDetailsBox.tsx new file mode 100644 index 0000000000..1af55511b0 --- /dev/null +++ b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/OrdererDetailsBox.tsx @@ -0,0 +1,33 @@ +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +import { styled } from "@mui/material/styles"; +import { DiscoveryOrderer } from "../../supabase-types"; +import StackedRowItems from "../../../../components/ui/StackedRowItems"; + +const ListHeaderTypography = styled(Typography)(({ theme }) => ({ + color: theme.palette.secondary.main, + fontWeight: "bold", +})); + +export interface OrdererDetailsBoxProps { + orderer: DiscoveryOrderer; +} + +export default function OrdererDetailsBox({ orderer }: OrdererDetailsBoxProps) { + return ( + + + Name: + {orderer.name} + + + Host: + {orderer.host} + + + Port: + {orderer.port} + + + ); +} diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/PeerCardButton.tsx b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/PeerCardButton.tsx new file mode 100644 index 0000000000..acc2cefc6a --- /dev/null +++ b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/PeerCardButton.tsx @@ -0,0 +1,39 @@ +import * as React from "react"; +import Button from "@mui/material/Button"; +import Dialog from "@mui/material/Dialog"; +import DialogTitle from "@mui/material/DialogTitle"; +import DialogContent from "@mui/material/DialogContent"; + +import PeerDetailsBox from "./PeerDetailsBox"; +import { DiscoveryPeer } from "../../supabase-types"; + +interface PeerCardButtonProps { + peer: DiscoveryPeer; +} + +export default function PeerCardButton({ peer }: PeerCardButtonProps) { + const [openDialog, setOpenDialog] = React.useState(false); + + return ( + <> + + setOpenDialog(false)} + open={openDialog} + > + Peer Details + + + + + + ); +} diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/PeerDetailsBox.tsx b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/PeerDetailsBox.tsx new file mode 100644 index 0000000000..4610bb1ce7 --- /dev/null +++ b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/Discovery/PeerDetailsBox.tsx @@ -0,0 +1,57 @@ +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +import List from "@mui/material/List/List"; +import ListItem from "@mui/material/ListItem/ListItem"; +import ListItemText from "@mui/material/ListItemText/ListItemText"; +import { styled } from "@mui/material/styles"; +import { DiscoveryPeer, DiscoveryPeerChaincodes } from "../../supabase-types"; +import StackedRowItems from "../../../../components/ui/StackedRowItems"; + +const ListHeaderTypography = styled(Typography)(({ theme }) => ({ + color: theme.palette.secondary.main, + fontWeight: "bold", +})); + +export interface PeerDetailsBoxProps { + peer: DiscoveryPeer; +} + +export default function PeerDetailsBox({ peer }: PeerDetailsBoxProps) { + const parsedChaincodes: DiscoveryPeerChaincodes[] = JSON.parse( + peer.chaincodes, + ); + + return ( + + + Name: + {peer.name} + + + Endpoint: + {peer.endpoint} + + + Ledger Height: + {peer.ledger_height} + + + + Chaincodes + + + {parsedChaincodes.map((cc) => { + return ( + + + + ); + })} + + + ); +} diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/queries.ts b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/queries.ts index 5c4e62606b..24ccd711d6 100644 --- a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/queries.ts +++ b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/queries.ts @@ -6,6 +6,9 @@ import { SupabaseClient, createClient } from "@supabase/supabase-js"; import { queryOptions } from "@tanstack/react-query"; import { + DiscoveryMSP, + DiscoveryOrderer, + DiscoveryPeer, FabricBlock, FabricCertificate, FabricTransaction, @@ -211,3 +214,68 @@ export function fabricActionEndorsements(actionId: string) { }, }); } + +/** + * Get all MSPs discovered on current fabric channel. + */ +export function fabricDiscoveryMSPs() { + const [supabase, supabaseQueryKey] = useSupabaseClient(); + const tableName = "discovery_msp"; + + return queryOptions({ + queryKey: [supabaseQueryKey, tableName], + queryFn: async () => { + const { data, error } = await supabase.from(tableName).select(); + + if (error) { + throw new Error(`Could not get discovery MSPs: ${error.message}`); + } + + return data as DiscoveryMSP[]; + }, + }); +} + +/** + * Get all peers and orderers for specified msp. + * + * @param mspId: Database ID of the MSP (i.e. uuid in `id` field) + */ +export function fabricDiscoveryNodes(mspId?: string) { + const [supabase, supabaseQueryKey] = useSupabaseClient(); + + return queryOptions({ + queryKey: [supabaseQueryKey, "fabricDiscoveryNodes", mspId], + queryFn: async () => { + // Fetch peers + const { data: peers, error: peerError } = await supabase + .from("discovery_peers") + .select() + .match({ discovery_msp_id: mspId }); + + if (peerError) { + throw new Error( + `Could not get peers of MSP ID ${mspId}: ${peerError.message}`, + ); + } + + // Fetch orderers + const { data: orderers, error: ordererError } = await supabase + .from("discovery_orderers") + .select() + .match({ discovery_msp_id: mspId }); + + if (ordererError) { + throw new Error( + `Could not get orderers of MSP ID ${mspId}: ${ordererError.message}`, + ); + } + + return { + peers: peers as DiscoveryPeer[], + orderers: orderers as DiscoveryOrderer[], + }; + }, + enabled: !!mspId, + }); +} diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/supabase-types.ts b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/supabase-types.ts index 4c16f694ff..6ab49b6798 100644 --- a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/supabase-types.ts +++ b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/supabase-types.ts @@ -55,3 +55,33 @@ export interface FabricCertificate { valid_from: string; valid_to: string; } + +export interface DiscoveryMSP { + id: string; + mspid: string; + name: string; + organizational_unit_identifiers: string; + admins: string; +} + +export interface DiscoveryPeer { + id: string; + endpoint: string; + name: string; + chaincodes: string; + ledger_height: number; + discovery_msp_id: string; +} + +export interface DiscoveryPeerChaincodes { + name: string; + version: string; +} + +export interface DiscoveryOrderer { + id: string; + host: string; + name: string; + port: number; + discovery_msp_id: string; +} diff --git a/packages/cacti-ledger-browser/src/main/typescript/pages/home/AddApplicationPopupCard.tsx b/packages/cacti-ledger-browser/src/main/typescript/pages/home/AddApplicationPopupCard.tsx index b01b45bbc4..76ab2a3a9b 100644 --- a/packages/cacti-ledger-browser/src/main/typescript/pages/home/AddApplicationPopupCard.tsx +++ b/packages/cacti-ledger-browser/src/main/typescript/pages/home/AddApplicationPopupCard.tsx @@ -63,7 +63,7 @@ export default function AddApplicationPopupCard() { sx={{ display: "flex", flexDirection: "column", - width: 400, + width: "400px", minHeight: 250, backgroundColor: theme.palette.grey[100], }} diff --git a/packages/cacti-ledger-browser/src/main/typescript/pages/home/AppCard.tsx b/packages/cacti-ledger-browser/src/main/typescript/pages/home/AppCard.tsx index 3517da884f..400660bac8 100644 --- a/packages/cacti-ledger-browser/src/main/typescript/pages/home/AppCard.tsx +++ b/packages/cacti-ledger-browser/src/main/typescript/pages/home/AppCard.tsx @@ -122,7 +122,7 @@ export default function AppCard({ appConfig }: AppCardProps) { sx={{ display: "flex", flexDirection: "column", - width: 400, + width: "400px", }} > diff --git a/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/.openapi-generator/FILES b/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/.openapi-generator/FILES index 9bb625d942..aead169ff7 100644 --- a/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/.openapi-generator/FILES +++ b/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/.openapi-generator/FILES @@ -6,6 +6,7 @@ client.go configuration.go go.mod go.sum +model_discover_network_response_v1.go model_error_exception_response_v1.go model_status_response_v1.go model_tracked_operation_v1.go diff --git a/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/README.md b/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/README.md index 118dbf4097..c5f7539662 100644 --- a/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/README.md +++ b/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/README.md @@ -77,11 +77,13 @@ All URIs are relative to *http://localhost* Class | Method | HTTP request | Description ------------ | ------------- | ------------- | ------------- +*DefaultApi* | [**DiscoverNetworkV1**](docs/DefaultApi.md#discovernetworkv1) | **Post** /api/v1/plugins/@hyperledger/cactus-plugin-persistence-fabric/discover-network | Refresh Fabric network structure in the database through discovery. *DefaultApi* | [**GetStatusV1**](docs/DefaultApi.md#getstatusv1) | **Get** /api/v1/plugins/@hyperledger/cactus-plugin-persistence-fabric/status | Get the status of persistence plugin for fabric ## Documentation For Models + - [DiscoverNetworkResponseV1](docs/DiscoverNetworkResponseV1.md) - [ErrorExceptionResponseV1](docs/ErrorExceptionResponseV1.md) - [StatusResponseV1](docs/StatusResponseV1.md) - [TrackedOperationV1](docs/TrackedOperationV1.md) diff --git a/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/api/openapi.yaml b/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/api/openapi.yaml index 050a9d59bb..b9cbd91ba2 100644 --- a/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/api/openapi.yaml +++ b/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/api/openapi.yaml @@ -32,6 +32,28 @@ paths: http: verbLowerCase: get path: /api/v1/plugins/@hyperledger/cactus-plugin-persistence-fabric/status + /api/v1/plugins/@hyperledger/cactus-plugin-persistence-fabric/discover-network: + post: + operationId: discoverNetworkV1 + parameters: [] + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/DiscoverNetworkResponseV1' + description: OK + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorExceptionResponseV1' + description: Internal Server Error + summary: Refresh Fabric network structure in the database through discovery. + x-hyperledger-cacti: + http: + verbLowerCase: post + path: /api/v1/plugins/@hyperledger/cactus-plugin-persistence-fabric/discover-network components: schemas: TrackedOperationV1: @@ -99,6 +121,21 @@ components: - operationsRunning - webServicesRegistered type: object + DiscoverNetworkResponseV1: + example: + message: message + status: true + properties: + status: + nullable: false + type: boolean + message: + nullable: false + type: string + required: + - message + - status + type: object ErrorExceptionResponseV1: properties: message: diff --git a/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/api_default.go b/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/api_default.go index dcc4a4252f..b1f5c77670 100644 --- a/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/api_default.go +++ b/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/api_default.go @@ -22,6 +22,113 @@ import ( // DefaultApiService DefaultApi service type DefaultApiService service +type ApiDiscoverNetworkV1Request struct { + ctx context.Context + ApiService *DefaultApiService +} + +func (r ApiDiscoverNetworkV1Request) Execute() (*DiscoverNetworkResponseV1, *http.Response, error) { + return r.ApiService.DiscoverNetworkV1Execute(r) +} + +/* +DiscoverNetworkV1 Refresh Fabric network structure in the database through discovery. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiDiscoverNetworkV1Request +*/ +func (a *DefaultApiService) DiscoverNetworkV1(ctx context.Context) ApiDiscoverNetworkV1Request { + return ApiDiscoverNetworkV1Request{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return DiscoverNetworkResponseV1 +func (a *DefaultApiService) DiscoverNetworkV1Execute(r ApiDiscoverNetworkV1Request) (*DiscoverNetworkResponseV1, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *DiscoverNetworkResponseV1 + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "DefaultApiService.DiscoverNetworkV1") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/api/v1/plugins/@hyperledger/cactus-plugin-persistence-fabric/discover-network" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 500 { + var v ErrorExceptionResponseV1 + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + type ApiGetStatusV1Request struct { ctx context.Context ApiService *DefaultApiService diff --git a/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/model_discover_network_response_v1.go b/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/model_discover_network_response_v1.go new file mode 100644 index 0000000000..aa92eb1153 --- /dev/null +++ b/packages/cactus-plugin-persistence-fabric/src/main/go/generated/openapi/go-client/model_discover_network_response_v1.go @@ -0,0 +1,144 @@ +/* +Hyperledger Cactus Plugin - Persistence Fabric + +Synchronizes state of an fabric ledger into a DB that can later be viewed in GUI + +API version: 2.1.0 +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package cactus-plugin-persistence-fabric + +import ( + "encoding/json" +) + +// checks if the DiscoverNetworkResponseV1 type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &DiscoverNetworkResponseV1{} + +// DiscoverNetworkResponseV1 struct for DiscoverNetworkResponseV1 +type DiscoverNetworkResponseV1 struct { + Status bool `json:"status"` + Message string `json:"message"` +} + +// NewDiscoverNetworkResponseV1 instantiates a new DiscoverNetworkResponseV1 object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewDiscoverNetworkResponseV1(status bool, message string) *DiscoverNetworkResponseV1 { + this := DiscoverNetworkResponseV1{} + this.Status = status + this.Message = message + return &this +} + +// NewDiscoverNetworkResponseV1WithDefaults instantiates a new DiscoverNetworkResponseV1 object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewDiscoverNetworkResponseV1WithDefaults() *DiscoverNetworkResponseV1 { + this := DiscoverNetworkResponseV1{} + return &this +} + +// GetStatus returns the Status field value +func (o *DiscoverNetworkResponseV1) GetStatus() bool { + if o == nil { + var ret bool + return ret + } + + return o.Status +} + +// GetStatusOk returns a tuple with the Status field value +// and a boolean to check if the value has been set. +func (o *DiscoverNetworkResponseV1) GetStatusOk() (*bool, bool) { + if o == nil { + return nil, false + } + return &o.Status, true +} + +// SetStatus sets field value +func (o *DiscoverNetworkResponseV1) SetStatus(v bool) { + o.Status = v +} + +// GetMessage returns the Message field value +func (o *DiscoverNetworkResponseV1) GetMessage() string { + if o == nil { + var ret string + return ret + } + + return o.Message +} + +// GetMessageOk returns a tuple with the Message field value +// and a boolean to check if the value has been set. +func (o *DiscoverNetworkResponseV1) GetMessageOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Message, true +} + +// SetMessage sets field value +func (o *DiscoverNetworkResponseV1) SetMessage(v string) { + o.Message = v +} + +func (o DiscoverNetworkResponseV1) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o DiscoverNetworkResponseV1) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["status"] = o.Status + toSerialize["message"] = o.Message + return toSerialize, nil +} + +type NullableDiscoverNetworkResponseV1 struct { + value *DiscoverNetworkResponseV1 + isSet bool +} + +func (v NullableDiscoverNetworkResponseV1) Get() *DiscoverNetworkResponseV1 { + return v.value +} + +func (v *NullableDiscoverNetworkResponseV1) Set(val *DiscoverNetworkResponseV1) { + v.value = val + v.isSet = true +} + +func (v NullableDiscoverNetworkResponseV1) IsSet() bool { + return v.isSet +} + +func (v *NullableDiscoverNetworkResponseV1) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableDiscoverNetworkResponseV1(val *DiscoverNetworkResponseV1) *NullableDiscoverNetworkResponseV1 { + return &NullableDiscoverNetworkResponseV1{value: val, isSet: true} +} + +func (v NullableDiscoverNetworkResponseV1) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableDiscoverNetworkResponseV1) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/packages/cactus-plugin-persistence-fabric/src/main/json/openapi.json b/packages/cactus-plugin-persistence-fabric/src/main/json/openapi.json index a0438776ed..0e505e4998 100644 --- a/packages/cactus-plugin-persistence-fabric/src/main/json/openapi.json +++ b/packages/cactus-plugin-persistence-fabric/src/main/json/openapi.json @@ -73,6 +73,20 @@ } } }, + "DiscoverNetworkResponseV1": { + "type": "object", + "required": ["status", "message"], + "properties": { + "status": { + "type": "boolean", + "nullable": false + }, + "message": { + "type": "string", + "nullable": false + } + } + }, "ErrorExceptionResponseV1": { "type": "object", "required": ["message", "error"], @@ -124,6 +138,41 @@ } } } + }, + "/api/v1/plugins/@hyperledger/cactus-plugin-persistence-fabric/discover-network": { + "post": { + "operationId": "discoverNetworkV1", + "summary": "Refresh Fabric network structure in the database through discovery.", + "x-hyperledger-cacti": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-persistence-fabric/discover-network" + } + }, + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DiscoverNetworkResponseV1" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExceptionResponseV1" + } + } + } + } + } + } } } } diff --git a/packages/cactus-plugin-persistence-fabric/src/main/json/openapi.tpl.json b/packages/cactus-plugin-persistence-fabric/src/main/json/openapi.tpl.json index a0438776ed..b3eb37a447 100644 --- a/packages/cactus-plugin-persistence-fabric/src/main/json/openapi.tpl.json +++ b/packages/cactus-plugin-persistence-fabric/src/main/json/openapi.tpl.json @@ -14,7 +14,10 @@ "TrackedOperationV1": { "description": "Persistence plugin operation that is tracked and returned in status report.", "type": "object", - "required": ["startAt", "operation"], + "required": [ + "startAt", + "operation" + ], "properties": { "startAt": { "type": "string", @@ -73,9 +76,29 @@ } } }, + "DiscoverNetworkResponseV1": { + "type": "object", + "required": [ + "status", + "message" + ], + "properties": { + "status": { + "type": "boolean", + "nullable": false + }, + "message": { + "type": "string", + "nullable": false + } + } + }, "ErrorExceptionResponseV1": { "type": "object", - "required": ["message", "error"], + "required": [ + "message", + "error" + ], "properties": { "message": { "type": "string", @@ -124,6 +147,41 @@ } } } + }, + "/api/v1/plugins/@hyperledger/cactus-plugin-persistence-fabric/discover-network": { + "post": { + "operationId": "discoverNetworkV1", + "summary": "Refresh Fabric network structure in the database through discovery.", + "x-hyperledger-cacti": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cactus-plugin-persistence-fabric/discover-network" + } + }, + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DiscoverNetworkResponseV1" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExceptionResponseV1" + } + } + } + } + } + } } } -} +} \ No newline at end of file diff --git a/packages/cactus-plugin-persistence-fabric/src/main/sql/schema.sql b/packages/cactus-plugin-persistence-fabric/src/main/sql/schema.sql index 56930d05aa..aca76e6f1a 100644 --- a/packages/cactus-plugin-persistence-fabric/src/main/sql/schema.sql +++ b/packages/cactus-plugin-persistence-fabric/src/main/sql/schema.sql @@ -1,7 +1,7 @@ --- Clean start: --- DROP SCHEMA fabric CASCADE; -CREATE SCHEMA fabric; +CREATE SCHEMA IF NOT EXISTS fabric; ALTER SCHEMA fabric OWNER TO postgres; @@ -152,6 +152,75 @@ ALTER TABLE fabric.transaction_action_endorsement OWNER TO postgres; ALTER TABLE ONLY fabric.transaction_action_endorsement ADD CONSTRAINT transaction_action_endorsements_pkey PRIMARY KEY (id); +-- Table: fabric.discovery_msp + +-- DROP TABLE IF EXISTS fabric.discovery_msp; + +CREATE TABLE IF NOT EXISTS fabric.discovery_msp +( + id uuid DEFAULT gen_random_uuid() NOT NULL, + mspid text COLLATE pg_catalog."default" NOT NULL, + name text COLLATE pg_catalog."default" NOT NULL, + organizational_unit_identifiers text COLLATE pg_catalog."default" NOT NULL, + admins text COLLATE pg_catalog."default" NOT NULL, + CONSTRAINT discovery_msp_pkey PRIMARY KEY (id) +); + +ALTER TABLE IF EXISTS fabric.discovery_msp + OWNER to postgres; + +GRANT ALL ON TABLE fabric.discovery_msp TO anon; +GRANT ALL ON TABLE fabric.discovery_msp TO authenticated; +GRANT ALL ON TABLE fabric.discovery_msp TO postgres; +GRANT ALL ON TABLE fabric.discovery_msp TO service_role; + +-- Table: fabric.discovery_orderers + +-- DROP TABLE IF EXISTS fabric.discovery_orderers; + +CREATE TABLE IF NOT EXISTS fabric.discovery_orderers +( + id uuid DEFAULT gen_random_uuid() NOT NULL, + name text COLLATE pg_catalog."default" NOT NULL, + host text COLLATE pg_catalog."default" NOT NULL, + port integer NOT NULL, + discovery_msp_id uuid NOT NULL, + CONSTRAINT discovery_orderers_pkey PRIMARY KEY (id), + CONSTRAINT discovery_orderers_discovery_msp_id_fkey FOREIGN KEY (discovery_msp_id) REFERENCES fabric.discovery_msp(id) +); + +ALTER TABLE IF EXISTS fabric.discovery_orderers + OWNER to postgres; + +GRANT ALL ON TABLE fabric.discovery_orderers TO anon; +GRANT ALL ON TABLE fabric.discovery_orderers TO authenticated; +GRANT ALL ON TABLE fabric.discovery_orderers TO postgres; +GRANT ALL ON TABLE fabric.discovery_orderers TO service_role; + +-- Table: fabric.discovery_peers + +-- DROP TABLE IF EXISTS fabric.discovery_peers; + +CREATE TABLE IF NOT EXISTS fabric.discovery_peers +( + id uuid DEFAULT gen_random_uuid() NOT NULL, + name text COLLATE pg_catalog."default" NOT NULL, + endpoint text COLLATE pg_catalog."default" NOT NULL, + ledger_height BIGINT NOT NULL, + chaincodes text COLLATE pg_catalog."default" NOT NULL, + discovery_msp_id uuid NOT NULL, + CONSTRAINT discovery_peers_pkey PRIMARY KEY (id), + CONSTRAINT discovery_peers_discovery_msp_id_fkey FOREIGN KEY (discovery_msp_id) REFERENCES fabric.discovery_msp(id) +); + +ALTER TABLE IF EXISTS fabric.discovery_peers + OWNER to postgres; + +GRANT ALL ON TABLE fabric.discovery_peers TO anon; +GRANT ALL ON TABLE fabric.discovery_peers TO authenticated; +GRANT ALL ON TABLE fabric.discovery_peers TO postgres; +GRANT ALL ON TABLE fabric.discovery_peers TO service_role; + -- FUNCTION: fabric.get_missing_blocks_in_range(integer, integer) -- DROP FUNCTION IF EXISTS fabric.get_missing_blocks_in_range(integer, integer); diff --git a/packages/cactus-plugin-persistence-fabric/src/main/typescript/db-client/db-client.ts b/packages/cactus-plugin-persistence-fabric/src/main/typescript/db-client/db-client.ts index 11c5ffb7c8..b98b975a84 100644 --- a/packages/cactus-plugin-persistence-fabric/src/main/typescript/db-client/db-client.ts +++ b/packages/cactus-plugin-persistence-fabric/src/main/typescript/db-client/db-client.ts @@ -18,6 +18,10 @@ import { FullBlockTransactionActionV1, FullBlockTransactionEndorsementV1, FullBlockTransactionEventV1, + GetDiscoveryResultsResponseV1, + GetDiscoveryResultsResponseV1MspsValue, + GetDiscoveryResultsResponseV1OrderersValueEndpointsInner, + GetDiscoveryResultsResponseV1PeersByMSPValuePeersInner, } from "@hyperledger/cactus-plugin-ledger-connector-fabric/src/main/typescript/generated/openapi/typescript-axios/api"; import { Database as DatabaseSchemaType } from "./database.types"; @@ -500,4 +504,145 @@ export default class PostgresDatabaseClient { return queryResponse.rows; } + + /** + * Insert discovered MSP to the database. + */ + private async insertDiscoveryMsp( + msp: GetDiscoveryResultsResponseV1MspsValue, + ): Promise { + this.log.debug(`Insert to fabric.discovery_msp MspID ${msp.id})`); + + const txInsertResponse = await this.client.query( + `INSERT INTO + fabric.discovery_msp("mspid", "name", "organizational_unit_identifiers", "admins") + VALUES ($1, $2, $3, $4) + RETURNING id;`, + [ + msp.id, + msp.name, + JSON.stringify(msp.organizationalUnitIdentifiers), + msp.admins, + ], + ); + if (txInsertResponse.rowCount !== 1) { + throw new Error(`MSP ${msp.id} was not inserted into the DB`); + } + + return txInsertResponse.rows[0].id; + } + + /** + * Insert discovered orderer to the database. + */ + private async insertDiscoveryOrderer( + discoveryMSPId: string, + orderer: GetDiscoveryResultsResponseV1OrderersValueEndpointsInner, + ): Promise { + this.log.debug( + `Insert to fabric.discovery_orderers orderer ${orderer.name})`, + ); + + const txInsertResponse = await this.client.query( + `INSERT INTO + fabric.discovery_orderers("name", "host", "port", "discovery_msp_id") + VALUES ($1, $2, $3, $4) + RETURNING id;`, + [orderer.name, orderer.host, orderer.port, discoveryMSPId], + ); + if (txInsertResponse.rowCount !== 1) { + throw new Error(`Orderer ${orderer.name} was not inserted into the DB`); + } + + return txInsertResponse.rows[0].id; + } + + /** + * Insert discovered peer to the database. + */ + private async insertDiscoveryPeer( + discoveryMSPId: string, + peer: GetDiscoveryResultsResponseV1PeersByMSPValuePeersInner, + ): Promise { + this.log.debug(`Insert to fabric.discovery_peers peer ${peer.name})`); + + const txInsertResponse = await this.client.query( + `INSERT INTO + fabric.discovery_peers("name", "endpoint", "ledger_height", "chaincodes", "discovery_msp_id") + VALUES ($1, $2, $3, $4, $5) + RETURNING id;`, + [ + peer.name, + peer.endpoint, + peer.ledgerHeight, + JSON.stringify(peer.chaincodes), + discoveryMSPId, + ], + ); + if (txInsertResponse.rowCount !== 1) { + throw new Error(`Peer ${peer.name} was not inserted into the DB`); + } + + return txInsertResponse.rows[0].id; + } + + /** + * Insert fabric discovery results to the database. + * @param discoveryResults response from getDiscoveryResults endpoint of fabric connector. + */ + public async insertDiscoveryResults( + discoveryResults: GetDiscoveryResultsResponseV1, + ): Promise { + this.assertConnected(); + + this.log.debug("Insert discovery results started"); + + try { + await this.client.query("BEGIN"); + + const discoveryMSPIds = new Map(); + + // Insert MSPs + for (const msp of Object.values(discoveryResults.msps)) { + const discoveryMSPId = await this.insertDiscoveryMsp(msp); + discoveryMSPIds.set(msp.id, discoveryMSPId); + } + + // Insert Orderers + for (const mspId of Object.keys(discoveryResults.orderers)) { + for (const orderer of discoveryResults.orderers[mspId].endpoints) { + const discoveryMSPId = discoveryMSPIds.get(mspId); + if (!discoveryMSPId) { + this.log.warn( + `Unexpected Error! Could not find ${discoveryMSPId} in MSP inserted to the database`, + ); + continue; + } + await this.insertDiscoveryOrderer(discoveryMSPId, orderer); + } + } + + // Insert Peers + for (const mspId of Object.keys(discoveryResults.peersByMSP)) { + for (const peer of discoveryResults.peersByMSP[mspId].peers) { + const discoveryMSPId = discoveryMSPIds.get(mspId); + if (!discoveryMSPId) { + this.log.warn( + `Unexpected Error! Could not find ${discoveryMSPId} in MSP inserted to the database`, + ); + continue; + } + await this.insertDiscoveryPeer(discoveryMSPId, peer); + } + } + + await this.client.query("COMMIT"); + } catch (err: unknown) { + await this.client.query("ROLLBACK"); + this.log.warn("insertDiscoveryResults() exception:", err); + throw new Error( + "Could not insert discovery results into the database - transaction reverted", + ); + } + } } diff --git a/packages/cactus-plugin-persistence-fabric/src/main/typescript/generated/openapi/typescript-axios/api.ts b/packages/cactus-plugin-persistence-fabric/src/main/typescript/generated/openapi/typescript-axios/api.ts index 76733ba783..049b408cfc 100644 --- a/packages/cactus-plugin-persistence-fabric/src/main/typescript/generated/openapi/typescript-axios/api.ts +++ b/packages/cactus-plugin-persistence-fabric/src/main/typescript/generated/openapi/typescript-axios/api.ts @@ -23,6 +23,25 @@ import type { RequestArgs } from './base'; // @ts-ignore import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError } from './base'; +/** + * + * @export + * @interface DiscoverNetworkResponseV1 + */ +export interface DiscoverNetworkResponseV1 { + /** + * + * @type {boolean} + * @memberof DiscoverNetworkResponseV1 + */ + 'status': boolean; + /** + * + * @type {string} + * @memberof DiscoverNetworkResponseV1 + */ + 'message': string; +} /** * * @export @@ -111,6 +130,36 @@ export interface TrackedOperationV1 { */ export const DefaultApiAxiosParamCreator = function (configuration?: Configuration) { return { + /** + * + * @summary Refresh Fabric network structure in the database through discovery. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + discoverNetworkV1: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cactus-plugin-persistence-fabric/discover-network`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * * @summary Get the status of persistence plugin for fabric @@ -151,6 +200,16 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati export const DefaultApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = DefaultApiAxiosParamCreator(configuration) return { + /** + * + * @summary Refresh Fabric network structure in the database through discovery. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async discoverNetworkV1(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.discoverNetworkV1(options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * * @summary Get the status of persistence plugin for fabric @@ -171,6 +230,15 @@ export const DefaultApiFp = function(configuration?: Configuration) { export const DefaultApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = DefaultApiFp(configuration) return { + /** + * + * @summary Refresh Fabric network structure in the database through discovery. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + discoverNetworkV1(options?: any): AxiosPromise { + return localVarFp.discoverNetworkV1(options).then((request) => request(axios, basePath)); + }, /** * * @summary Get the status of persistence plugin for fabric @@ -190,6 +258,17 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa * @extends {BaseAPI} */ export class DefaultApi extends BaseAPI { + /** + * + * @summary Refresh Fabric network structure in the database through discovery. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public discoverNetworkV1(options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).discoverNetworkV1(options).then((request) => request(this.axios, this.basePath)); + } + /** * * @summary Get the status of persistence plugin for fabric diff --git a/packages/cactus-plugin-persistence-fabric/src/main/typescript/plugin-persistence-fabric.ts b/packages/cactus-plugin-persistence-fabric/src/main/typescript/plugin-persistence-fabric.ts index 9857561734..fe07ce3511 100644 --- a/packages/cactus-plugin-persistence-fabric/src/main/typescript/plugin-persistence-fabric.ts +++ b/packages/cactus-plugin-persistence-fabric/src/main/typescript/plugin-persistence-fabric.ts @@ -30,6 +30,7 @@ import { import OAS from "../json/openapi.json"; import { StatusEndpointV1 } from "./web-services/status-endpoint-v1"; +import { DiscoverNetworkEndpointV1 } from "./web-services/discover-network-endpoint-v1"; import PostgresDatabaseClient from "./db-client/db-client"; import { StatusResponseV1, @@ -369,6 +370,13 @@ export class PluginPersistenceFabric }); endpoints.push(endpoint); } + { + const endpoint = new DiscoverNetworkEndpointV1({ + connector: this, + logLevel: this.options.logLevel, + }); + endpoints.push(endpoint); + } this.endpoints = endpoints; log.info(`Instantiated web services for plugin ${pkgName} OK`, { @@ -407,6 +415,9 @@ export class PluginPersistenceFabric // Synchronize the current DB state this.lastSeenBlock = await this.syncAll(); + // Update fabric network structure + await this.discoverNetwork(); + const blocksObservable = this.apiClient.watchBlocksV1({ channelName: this.channelName, gatewayOptions: this.gatewayOptions, @@ -531,4 +542,33 @@ export class PluginPersistenceFabric this.removeTrackedOperation(operationId); } } + + /** + * Synchronize current ledger structure (discovery results) with the database. + * Discovery should be enabled on the conenctor for this method to work. + * Any errors are ignored (i.e. this discovery is optional), but warning message will be printed. + */ + public async discoverNetwork() { + const operationId = uuidv4(); + this.addTrackedOperation(operationId, "discoverNetwork"); + + try { + this.log.info("discoverNetwork() started..."); + + const discoveryResultsResponse = + await this.apiClient.getDiscoveryResultsV1({ + channelName: this.channelName, + gatewayOptions: this.gatewayOptions, + }); + + const discoveryResults = discoveryResultsResponse.data; + + await this.dbClient.insertDiscoveryResults(discoveryResults); + this.log.info("Fabric network updated."); + } catch (err) { + this.log.warn("Could not update discovery results:", err); + } finally { + this.removeTrackedOperation(operationId); + } + } } diff --git a/packages/cactus-plugin-persistence-fabric/src/main/typescript/web-services/discover-network-endpoint-v1.ts b/packages/cactus-plugin-persistence-fabric/src/main/typescript/web-services/discover-network-endpoint-v1.ts new file mode 100644 index 0000000000..eb02c8c332 --- /dev/null +++ b/packages/cactus-plugin-persistence-fabric/src/main/typescript/web-services/discover-network-endpoint-v1.ts @@ -0,0 +1,110 @@ +/** + * OpenAPI endpoint (POST) for refreshing network structure in the database. + */ + +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, + IAsyncProvider, +} from "@hyperledger/cactus-common"; +import type { + IEndpointAuthzOptions, + IExpressRequestHandler, + IWebServiceEndpoint, +} from "@hyperledger/cactus-core-api"; +import { + handleRestEndpointException, + registerWebServiceEndpoint, +} from "@hyperledger/cactus-core"; + +import { PluginPersistenceFabric } from "../plugin-persistence-fabric"; +import OAS from "../../json/openapi.json"; + +import type { Express, Request, Response } from "express"; + +export interface IDiscoverNetworkEndpointV1Options { + logLevel?: LogLevelDesc; + connector: PluginPersistenceFabric; +} + +/** + * OpenAPI endpoint (GET) for reading status of the persistence plugin. + */ +export class DiscoverNetworkEndpointV1 implements IWebServiceEndpoint { + public static readonly CLASS_NAME = "DiscoverNetworkEndpointV1"; + + private readonly log: Logger; + + public get className(): string { + return DiscoverNetworkEndpointV1.CLASS_NAME; + } + + constructor(public readonly options: IDiscoverNetworkEndpointV1Options) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + Checks.truthy(options.connector, `${fnTag} arg options.connector`); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + + public getOasPath(): any { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cactus-plugin-persistence-fabric/discover-network" + ]; + } + + public getPath(): string { + const apiPath = this.getOasPath(); + return apiPath.post["x-hyperledger-cacti"].http.path; + } + + public getVerbLowerCase(): string { + const apiPath = this.getOasPath(); + return apiPath.post["x-hyperledger-cacti"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.getOasPath().get.operationId; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public async handleRequest(_req: Request, res: Response): Promise { + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + this.log.debug(reqTag); + + try { + await this.options.connector.discoverNetwork(); + res.status(200).json({ + status: true, + message: "Discovery done.", + }); + } catch (ex) { + const errorMsg = `Crash while serving ${reqTag}`; + handleRestEndpointException({ errorMsg, log: this.log, error: ex, res }); + } + } +} diff --git a/packages/cactus-plugin-persistence-fabric/src/test/sql/insert-test-data.sql b/packages/cactus-plugin-persistence-fabric/src/test/sql/insert-test-data.sql index f61fc68623..8bdb6a0c87 100644 --- a/packages/cactus-plugin-persistence-fabric/src/test/sql/insert-test-data.sql +++ b/packages/cactus-plugin-persistence-fabric/src/test/sql/insert-test-data.sql @@ -548,6 +548,115 @@ j8Fxno3p3EAmocwmZyu4wxnVmqfKdhs= ' ); +-- +-- Data for Name: discovery_msp; Type: TABLE DATA; Schema: fabric; Owner: postgres +-- +INSERT INTO + fabric.discovery_msp ( + id, + mspid, + name, + organizational_unit_identifiers, + admins + ) +VALUES + ( + '75ecbf61-5c81-4928-a2c3-316c136610bf', + 'OrdererMSP', + 'OrdererMSP', + '[]', + '' + ); + +INSERT INTO + fabric.discovery_msp ( + id, + mspid, + name, + organizational_unit_identifiers, + admins + ) +VALUES + ( + '38f053a5-f3f4-49fc-96a1-6d0dacc21837', + 'Org1MSP', + 'Org1MSP', + '[]', + '' + ); + +INSERT INTO + fabric.discovery_msp ( + id, + mspid, + name, + organizational_unit_identifiers, + admins + ) +VALUES + ( + 'acd8eab1-59a0-4a89-a4f4-4d222ed2c3f2', + 'Org2MSP', + 'Org2MSP', + '[]', + '' + ); + +-- +-- Data for Name: discovery_orderers; Type: TABLE DATA; Schema: fabric; Owner: postgres +-- +INSERT INTO + fabric.discovery_orderers (id, name, host, port, discovery_msp_id) +VALUES + ( + 'd165280d-93c1-4710-9829-88da847b6431', + 'orderer.example.com:7050', + 'orderer.example.com', + 7050, + '75ecbf61-5c81-4928-a2c3-316c136610bf' + ); + +-- +-- Data for Name: discovery_peers; Type: TABLE DATA; Schema: fabric; Owner: postgres +-- +INSERT INTO + fabric.discovery_peers ( + id, + name, + endpoint, + ledger_height, + chaincodes, + discovery_msp_id + ) +VALUES + ( + '9819add6-c50d-467f-8fe8-eb1c4089ba9a', + 'peer0.org1.example.com:7051', + 'peer0.org1.example.com:7051', + 7, + '[{"name":"basic","version":"1"},{"name":"_lifecycle","version":"1"}]', + '38f053a5-f3f4-49fc-96a1-6d0dacc21837' + ); + +INSERT INTO + fabric.discovery_peers ( + id, + name, + endpoint, + ledger_height, + chaincodes, + discovery_msp_id + ) +VALUES + ( + 'a3969e81-cb5b-4b99-be9c-4ca7e9384130', + 'peer0.org2.example.com:9051', + 'peer0.org2.example.com:9051', + 7, + '[{"name":"basic","version":"1"},{"name":"_lifecycle","version":"1"}]', + 'acd8eab1-59a0-4a89-a4f4-4d222ed2c3f2' + ); + -- -- Data for Name: transaction; Type: TABLE DATA; Schema: fabric; Owner: postgres -- diff --git a/packages/cactus-plugin-persistence-fabric/src/test/typescript/integration/persistence-fabrc-postgresql-db-client.test.ts b/packages/cactus-plugin-persistence-fabric/src/test/typescript/integration/persistence-fabrc-postgresql-db-client.test.ts index 0885997c4e..71d089651f 100644 --- a/packages/cactus-plugin-persistence-fabric/src/test/typescript/integration/persistence-fabrc-postgresql-db-client.test.ts +++ b/packages/cactus-plugin-persistence-fabric/src/test/typescript/integration/persistence-fabrc-postgresql-db-client.test.ts @@ -25,7 +25,11 @@ import { import PostgresDatabaseClient from "../../../main/typescript/db-client/db-client"; import "jest-extended"; -import { invalidSampleBlock, sampleBlock } from "./sample-block"; +import { + invalidSampleBlock, + sampleBlock, + sampleDiscoveryResults, +} from "./sample-data"; // Logger setup const log: Logger = LoggerProvider.getOrCreate({ @@ -98,6 +102,27 @@ describe("Fabric persistence PostgreSQL PostgresDatabaseClient tests", () => { return response.rows; } + async function getDiscoveryMSPs() { + const response = await dbClient.client.query( + "SELECT * FROM fabric.discovery_msp", + ); + return response.rows; + } + + async function getDiscoveryPeers() { + const response = await dbClient.client.query( + "SELECT p.*, m.mspid FROM fabric.discovery_peers p JOIN fabric.discovery_msp m ON p.discovery_msp_id = m.id;", + ); + return response.rows; + } + + async function getDiscoveryOrderers() { + const response = await dbClient.client.query( + "SELECT o.*, m.mspid FROM fabric.discovery_orderers o JOIN fabric.discovery_msp m ON o.discovery_msp_id = m.id;", + ); + return response.rows; + } + ////////////////////////////////// // Environment Setup ////////////////////////////////// @@ -153,6 +178,9 @@ describe("Fabric persistence PostgreSQL PostgresDatabaseClient tests", () => { "transaction", "transaction_action", "transaction_action_endorsement", + "discovery_msp", + "discovery_orderers", + "discovery_peers", ].sort(), ); @@ -377,4 +405,38 @@ describe("Fabric persistence PostgreSQL PostgresDatabaseClient tests", () => { .sort(); expect(missingBlocksNumbers).toEqual([1, 2, 4, 5]); }); + + test("insertDiscoveryResults updates ledger structure", async () => { + await dbClient.insertDiscoveryResults(sampleDiscoveryResults); + + // Assert MSPs + const dbMsps = await getDiscoveryMSPs(); + expect(dbMsps.length).toEqual(3); + for (const msp of dbMsps) { + expect(msp.mspid).toBeTruthy(); + expect(msp.name).toBeTruthy(); + } + + // Assert Peers + const dbPeers = await getDiscoveryPeers(); + expect(dbPeers.length).toEqual(2); + for (const peer of dbPeers) { + expect(peer.name).toBeTruthy(); + expect(peer.endpoint).toBeTruthy(); + expect(peer.ledger_height).toBeTruthy(); + expect(peer.chaincodes).toBeTruthy(); + expect(peer.discovery_msp_id).toBeTruthy(); + expect(peer.mspid).toBeTruthy(); + } + + // Assert Orderers + const dbOrderers = await getDiscoveryOrderers(); + expect(dbOrderers.length).toEqual(1); + const dbOrderer = dbOrderers[0]; + expect(dbOrderer.name).toBeTruthy(); + expect(dbOrderer.host).toBeTruthy(); + expect(dbOrderer.port).toBeTruthy(); + expect(dbOrderer.discovery_msp_id).toBeTruthy(); + expect(dbOrderer.mspid).toBeTruthy(); + }); }); diff --git a/packages/cactus-plugin-persistence-fabric/src/test/typescript/integration/persistence-fabric-functional.test.ts b/packages/cactus-plugin-persistence-fabric/src/test/typescript/integration/persistence-fabric-functional.test.ts index 94f77b6531..161a4462f4 100644 --- a/packages/cactus-plugin-persistence-fabric/src/test/typescript/integration/persistence-fabric-functional.test.ts +++ b/packages/cactus-plugin-persistence-fabric/src/test/typescript/integration/persistence-fabric-functional.test.ts @@ -420,4 +420,53 @@ describe("Fabric persistence plugin tests", () => { }, testTimeout, ); + + test( + "Discovery network pushes current ledger structure to the database", + async () => { + await persistence.discoverNetwork(); + + const allInsertCalls = ( + dbClientInstance.insertDiscoveryResults as jest.Mock + ).mock.calls; + expect(allInsertCalls.length).toBe(1); + const discoveryInsertArg = allInsertCalls[0][0]; + + // Assert required fields provided + expect(Object.keys(discoveryInsertArg.msps).length).toBeGreaterThan(0); + expect(Object.keys(discoveryInsertArg.orderers).length).toBeGreaterThan( + 0, + ); + expect(Object.keys(discoveryInsertArg.peersByMSP).length).toBeGreaterThan( + 0, + ); + expect(discoveryInsertArg.timestamp).toBeTruthy(); + }, + testTimeout, + ); + + test( + "Network discovery is called when starting block monitoring", + async () => { + try { + await new Promise((resolve, reject) => { + ( + dbClientInstance.getMissingBlocksInRange as jest.Mock + ).mockReturnValue([]); + + ( + dbClientInstance.insertDiscoveryResults as jest.Mock + ).mockImplementation((discoveryResults) => resolve(discoveryResults)); + + persistence.startMonitor((err) => { + reject(err); + }); + log.debug("Persistence plugin block monitoring started."); + }); + } finally { + persistence.stopMonitor(); + } + }, + testTimeout, + ); }); diff --git a/packages/cactus-plugin-persistence-fabric/src/test/typescript/integration/sample-block.ts b/packages/cactus-plugin-persistence-fabric/src/test/typescript/integration/sample-block.ts deleted file mode 100644 index b474e0d55e..0000000000 --- a/packages/cactus-plugin-persistence-fabric/src/test/typescript/integration/sample-block.ts +++ /dev/null @@ -1,106 +0,0 @@ -export const sampleBlock = { - blockNumber: 3, - blockHash: - "0xe6656aa10d9f73fde8db5b0ce6bba9a2bc3118c72ef9d69b53b9f7c512139d60", - previousBlockHash: - "0xf8c0f8c4d4ae2f3c2140a1e63d940734d27549fa8829e1c5c994bcc3182f0caa", - transactionCount: 1, - cactiTransactionsEvents: [ - { - hash: "4affb3661c2a8075e52e8e6826e1768616bb1f8c588b1baa54368a3996a54de8", - channelId: "mychannel", - timestamp: "2024-06-10T10:55:26.036Z", - protocolVersion: 0, - transactionType: "ENDORSER_TRANSACTION", - epoch: 0, - actions: [ - { - functionName: "MyFunctionName", - functionArgs: ["foo", "bar"], - chaincodeId: "myChaincode", - creator: { - mspid: "Org1MSP", - cert: { - serialNumber: "16C8C9A05A2B7EFA6ED794F28A2FBCE6DED1C86C", - subject: - "C=US\nST=North Carolina\nO=Hyperledger\nOU=admin\nCN=org1admin", - issuer: - "C=US\nST=North Carolina\nL=Durham\nO=org1.example.com\nCN=ca.org1.example.com", - subjectAltName: "DNS:9071369d9d11", - validFrom: "Jun 10 10:50:00 2024 GMT", - validTo: "Jun 10 10:55:00 2025 GMT", - pem: "-----BEGIN CERTIFICATE-----\nMIICqTCCAlCgAwIBAgIUFsjJoForfvpu15Tyii+85t7RyGwwCgYIKoZIzj0EAwIw\ncDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQH\nEwZEdXJoYW0xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjQwNjEwMTA1MDAwWhcNMjUwNjEwMTA1NTAw\nWjBgMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExFDASBgNV\nBAoTC0h5cGVybGVkZ2VyMQ4wDAYDVQQLEwVhZG1pbjESMBAGA1UEAxMJb3JnMWFk\nbWluMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEaCjVDW8X3Fpa7lXTrjNACJG\nmslK1ppx9uzh9Fqk2lLN7GxcJSi2hcIyTK9+udwbRynDHl1HgMG/fLBfqrkCNKOB\n1zCB1DAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUTw/b\nSs21vEgoQbb2wnwXF4DkCTEwHwYDVR0jBBgwFoAUgN29gMPVb3dfnq0ngxTg67qy\niQkwFwYDVR0RBBAwDoIMOTA3MTM2OWQ5ZDExMFsGCCoDBAUGBwgBBE97ImF0dHJz\nIjp7ImhmLkFmZmlsaWF0aW9uIjoiIiwiaGYuRW5yb2xsbWVudElEIjoib3JnMWFk\nbWluIiwiaGYuVHlwZSI6ImFkbWluIn19MAoGCCqGSM49BAMCA0cAMEQCIGzNQ3Ut\niHpsKZzzIadYTY7TlC7FliD+XI89FyzM2RqoAiALJ2yU42wNnfrRuByQQN9cHz1j\nArKZknDfP6HYxUS0RQ==\n-----END CERTIFICATE-----\n", - }, - }, - endorsements: [ - { - signer: { - mspid: "Org1MSP", - cert: { - serialNumber: "3D697828B3244EDC75A95CCC30FC5013B904F6E5", - subject: - "C=US\nST=North Carolina\nO=Hyperledger\nOU=peer\nCN=peer0", - issuer: - "C=US\nST=North Carolina\nL=Durham\nO=org1.example.com\nCN=ca.org1.example.com", - subjectAltName: "DNS:9071369d9d11", - validFrom: "Jun 10 10:50:00 2024 GMT", - validTo: "Jun 10 10:55:00 2025 GMT", - pem: "-----BEGIN CERTIFICATE-----\nMIICnzCCAkagAwIBAgIUPWl4KLMkTtx1qVzMMPxQE7kE9uUwCgYIKoZIzj0EAwIw\ncDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQH\nEwZEdXJoYW0xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjQwNjEwMTA1MDAwWhcNMjUwNjEwMTA1NTAw\nWjBbMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExFDASBgNV\nBAoTC0h5cGVybGVkZ2VyMQ0wCwYDVQQLEwRwZWVyMQ4wDAYDVQQDEwVwZWVyMDBZ\nMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOBN1m+Sd4tJgk7cj/2tjncS0DDaZrpB\nXScgGyyvFu7WvUNAX5huTiUcP6RPnfQ2op1fgaPvHwVWQ4sLwU3wYqSjgdIwgc8w\nDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFOWzZpC41lih\n5kCb9Dhd/w626Ve7MB8GA1UdIwQYMBaAFIDdvYDD1W93X56tJ4MU4Ou6sokJMBcG\nA1UdEQQQMA6CDDkwNzEzNjlkOWQxMTBWBggqAwQFBgcIAQRKeyJhdHRycyI6eyJo\nZi5BZmZpbGlhdGlvbiI6IiIsImhmLkVucm9sbG1lbnRJRCI6InBlZXIwIiwiaGYu\nVHlwZSI6InBlZXIifX0wCgYIKoZIzj0EAwIDRwAwRAIgCNafIs0XRatMvyu1Mj62\n4LVXfIgyolfaFaOZaFtjJdYCIA4bciJH/vMOdbxoAbNr7B83P1GEfHLdmd2yy7D1\nVi3u\n-----END CERTIFICATE-----\n", - }, - }, - signature: - "0x304402205f576c57e2c29806c7636e6ed4d9b02e842ccbbd01dd333c8716efa927e74bac022079be4c059a36fba7ef9a767275e7d8e0f020a6898d930a9d9f2ab93a5e0d8a9b", - }, - ], - }, - ], - }, - ], -}; - -export const invalidSampleBlock = { - blockNumber: 3, - blockHash: - "0xe6656aa10d9f73fde8db5b0ce6bba9a2bc3118c72ef9d69b53b9f7c512139d60", - previousBlockHash: - "0xf8c0f8c4d4ae2f3c2140a1e63d940734d27549fa8829e1c5c994bcc3182f0caa", - transactionCount: 1, - cactiTransactionsEvents: [ - { - hash: "4affb3661c2a8075e52e8e6826e1768616bb1f8c588b1baa54368a3996a54de8", - channelId: "mychannel", - timestamp: "2024-06-10T10:55:26.036Z", - protocolVersion: 0, - type: "ENDORSER_TRANSACTION", - epoch: 0, - actions: [ - { - functionName: "MyFunctionName", - functionArgs: ["foo", "bar"], - chaincodeId: "myChaincode", - creator: { - mspid: "Org1MSP", - cert: { - serialNumber: "16C8C9A05A2B7EFA6ED794F28A2FBCE6DED1C86C", - subject: - "C=US\nST=North Carolina\nO=Hyperledger\nOU=admin\nCN=org1admin", - issuer: - "C=US\nST=North Carolina\nL=Durham\nO=org1.example.com\nCN=ca.org1.example.com", - subjectAltName: "DNS:9071369d9d11", - validFrom: "Jun 10 10:50:00 2024 GMT", - validTo: "Jun 10 10:55:00 2025 GMT", - pem: "-----BEGIN CERTIFICATE-----\nMIICqTCCAlCgAwIBAgIUFsjJoForfvpu15Tyii+85t7RyGwwCgYIKoZIzj0EAwIw\ncDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQH\nEwZEdXJoYW0xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjQwNjEwMTA1MDAwWhcNMjUwNjEwMTA1NTAw\nWjBgMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExFDASBgNV\nBAoTC0h5cGVybGVkZ2VyMQ4wDAYDVQQLEwVhZG1pbjESMBAGA1UEAxMJb3JnMWFk\nbWluMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEaCjVDW8X3Fpa7lXTrjNACJG\nmslK1ppx9uzh9Fqk2lLN7GxcJSi2hcIyTK9+udwbRynDHl1HgMG/fLBfqrkCNKOB\n1zCB1DAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUTw/b\nSs21vEgoQbb2wnwXF4DkCTEwHwYDVR0jBBgwFoAUgN29gMPVb3dfnq0ngxTg67qy\niQkwFwYDVR0RBBAwDoIMOTA3MTM2OWQ5ZDExMFsGCCoDBAUGBwgBBE97ImF0dHJz\nIjp7ImhmLkFmZmlsaWF0aW9uIjoiIiwiaGYuRW5yb2xsbWVudElEIjoib3JnMWFk\nbWluIiwiaGYuVHlwZSI6ImFkbWluIn19MAoGCCqGSM49BAMCA0cAMEQCIGzNQ3Ut\niHpsKZzzIadYTY7TlC7FliD+XI89FyzM2RqoAiALJ2yU42wNnfrRuByQQN9cHz1j\nArKZknDfP6HYxUS0RQ==\n-----END CERTIFICATE-----\n", - }, - }, - endorsements: [ - { - signer: { - foo: "Org1MSP", - }, - }, - ], - }, - ], - }, - ], -}; diff --git a/packages/cactus-plugin-persistence-fabric/src/test/typescript/integration/sample-data.ts b/packages/cactus-plugin-persistence-fabric/src/test/typescript/integration/sample-data.ts new file mode 100644 index 0000000000..379420c8b5 --- /dev/null +++ b/packages/cactus-plugin-persistence-fabric/src/test/typescript/integration/sample-data.ts @@ -0,0 +1,209 @@ +export const sampleBlock = { + blockNumber: 3, + blockHash: + "0xe6656aa10d9f73fde8db5b0ce6bba9a2bc3118c72ef9d69b53b9f7c512139d60", + previousBlockHash: + "0xf8c0f8c4d4ae2f3c2140a1e63d940734d27549fa8829e1c5c994bcc3182f0caa", + transactionCount: 1, + cactiTransactionsEvents: [ + { + hash: "4affb3661c2a8075e52e8e6826e1768616bb1f8c588b1baa54368a3996a54de8", + channelId: "mychannel", + timestamp: "2024-06-10T10:55:26.036Z", + protocolVersion: 0, + transactionType: "ENDORSER_TRANSACTION", + epoch: 0, + actions: [ + { + functionName: "MyFunctionName", + functionArgs: ["foo", "bar"], + chaincodeId: "myChaincode", + creator: { + mspid: "Org1MSP", + cert: { + serialNumber: "16C8C9A05A2B7EFA6ED794F28A2FBCE6DED1C86C", + subject: + "C=US\nST=North Carolina\nO=Hyperledger\nOU=admin\nCN=org1admin", + issuer: + "C=US\nST=North Carolina\nL=Durham\nO=org1.example.com\nCN=ca.org1.example.com", + subjectAltName: "DNS:9071369d9d11", + validFrom: "Jun 10 10:50:00 2024 GMT", + validTo: "Jun 10 10:55:00 2025 GMT", + pem: "-----BEGIN CERTIFICATE-----\nMIICqTCCAlCgAwIBAgIUFsjJoForfvpu15Tyii+85t7RyGwwCgYIKoZIzj0EAwIw\ncDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQH\nEwZEdXJoYW0xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjQwNjEwMTA1MDAwWhcNMjUwNjEwMTA1NTAw\nWjBgMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExFDASBgNV\nBAoTC0h5cGVybGVkZ2VyMQ4wDAYDVQQLEwVhZG1pbjESMBAGA1UEAxMJb3JnMWFk\nbWluMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEaCjVDW8X3Fpa7lXTrjNACJG\nmslK1ppx9uzh9Fqk2lLN7GxcJSi2hcIyTK9+udwbRynDHl1HgMG/fLBfqrkCNKOB\n1zCB1DAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUTw/b\nSs21vEgoQbb2wnwXF4DkCTEwHwYDVR0jBBgwFoAUgN29gMPVb3dfnq0ngxTg67qy\niQkwFwYDVR0RBBAwDoIMOTA3MTM2OWQ5ZDExMFsGCCoDBAUGBwgBBE97ImF0dHJz\nIjp7ImhmLkFmZmlsaWF0aW9uIjoiIiwiaGYuRW5yb2xsbWVudElEIjoib3JnMWFk\nbWluIiwiaGYuVHlwZSI6ImFkbWluIn19MAoGCCqGSM49BAMCA0cAMEQCIGzNQ3Ut\niHpsKZzzIadYTY7TlC7FliD+XI89FyzM2RqoAiALJ2yU42wNnfrRuByQQN9cHz1j\nArKZknDfP6HYxUS0RQ==\n-----END CERTIFICATE-----\n", + }, + }, + endorsements: [ + { + signer: { + mspid: "Org1MSP", + cert: { + serialNumber: "3D697828B3244EDC75A95CCC30FC5013B904F6E5", + subject: + "C=US\nST=North Carolina\nO=Hyperledger\nOU=peer\nCN=peer0", + issuer: + "C=US\nST=North Carolina\nL=Durham\nO=org1.example.com\nCN=ca.org1.example.com", + subjectAltName: "DNS:9071369d9d11", + validFrom: "Jun 10 10:50:00 2024 GMT", + validTo: "Jun 10 10:55:00 2025 GMT", + pem: "-----BEGIN CERTIFICATE-----\nMIICnzCCAkagAwIBAgIUPWl4KLMkTtx1qVzMMPxQE7kE9uUwCgYIKoZIzj0EAwIw\ncDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQH\nEwZEdXJoYW0xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjQwNjEwMTA1MDAwWhcNMjUwNjEwMTA1NTAw\nWjBbMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExFDASBgNV\nBAoTC0h5cGVybGVkZ2VyMQ0wCwYDVQQLEwRwZWVyMQ4wDAYDVQQDEwVwZWVyMDBZ\nMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOBN1m+Sd4tJgk7cj/2tjncS0DDaZrpB\nXScgGyyvFu7WvUNAX5huTiUcP6RPnfQ2op1fgaPvHwVWQ4sLwU3wYqSjgdIwgc8w\nDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFOWzZpC41lih\n5kCb9Dhd/w626Ve7MB8GA1UdIwQYMBaAFIDdvYDD1W93X56tJ4MU4Ou6sokJMBcG\nA1UdEQQQMA6CDDkwNzEzNjlkOWQxMTBWBggqAwQFBgcIAQRKeyJhdHRycyI6eyJo\nZi5BZmZpbGlhdGlvbiI6IiIsImhmLkVucm9sbG1lbnRJRCI6InBlZXIwIiwiaGYu\nVHlwZSI6InBlZXIifX0wCgYIKoZIzj0EAwIDRwAwRAIgCNafIs0XRatMvyu1Mj62\n4LVXfIgyolfaFaOZaFtjJdYCIA4bciJH/vMOdbxoAbNr7B83P1GEfHLdmd2yy7D1\nVi3u\n-----END CERTIFICATE-----\n", + }, + }, + signature: + "0x304402205f576c57e2c29806c7636e6ed4d9b02e842ccbbd01dd333c8716efa927e74bac022079be4c059a36fba7ef9a767275e7d8e0f020a6898d930a9d9f2ab93a5e0d8a9b", + }, + ], + }, + ], + }, + ], +}; + +export const invalidSampleBlock = { + blockNumber: 3, + blockHash: + "0xe6656aa10d9f73fde8db5b0ce6bba9a2bc3118c72ef9d69b53b9f7c512139d60", + previousBlockHash: + "0xf8c0f8c4d4ae2f3c2140a1e63d940734d27549fa8829e1c5c994bcc3182f0caa", + transactionCount: 1, + cactiTransactionsEvents: [ + { + hash: "4affb3661c2a8075e52e8e6826e1768616bb1f8c588b1baa54368a3996a54de8", + channelId: "mychannel", + timestamp: "2024-06-10T10:55:26.036Z", + protocolVersion: 0, + type: "ENDORSER_TRANSACTION", + epoch: 0, + actions: [ + { + functionName: "MyFunctionName", + functionArgs: ["foo", "bar"], + chaincodeId: "myChaincode", + creator: { + mspid: "Org1MSP", + cert: { + serialNumber: "16C8C9A05A2B7EFA6ED794F28A2FBCE6DED1C86C", + subject: + "C=US\nST=North Carolina\nO=Hyperledger\nOU=admin\nCN=org1admin", + issuer: + "C=US\nST=North Carolina\nL=Durham\nO=org1.example.com\nCN=ca.org1.example.com", + subjectAltName: "DNS:9071369d9d11", + validFrom: "Jun 10 10:50:00 2024 GMT", + validTo: "Jun 10 10:55:00 2025 GMT", + pem: "-----BEGIN CERTIFICATE-----\nMIICqTCCAlCgAwIBAgIUFsjJoForfvpu15Tyii+85t7RyGwwCgYIKoZIzj0EAwIw\ncDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQH\nEwZEdXJoYW0xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjQwNjEwMTA1MDAwWhcNMjUwNjEwMTA1NTAw\nWjBgMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExFDASBgNV\nBAoTC0h5cGVybGVkZ2VyMQ4wDAYDVQQLEwVhZG1pbjESMBAGA1UEAxMJb3JnMWFk\nbWluMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEaCjVDW8X3Fpa7lXTrjNACJG\nmslK1ppx9uzh9Fqk2lLN7GxcJSi2hcIyTK9+udwbRynDHl1HgMG/fLBfqrkCNKOB\n1zCB1DAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUTw/b\nSs21vEgoQbb2wnwXF4DkCTEwHwYDVR0jBBgwFoAUgN29gMPVb3dfnq0ngxTg67qy\niQkwFwYDVR0RBBAwDoIMOTA3MTM2OWQ5ZDExMFsGCCoDBAUGBwgBBE97ImF0dHJz\nIjp7ImhmLkFmZmlsaWF0aW9uIjoiIiwiaGYuRW5yb2xsbWVudElEIjoib3JnMWFk\nbWluIiwiaGYuVHlwZSI6ImFkbWluIn19MAoGCCqGSM49BAMCA0cAMEQCIGzNQ3Ut\niHpsKZzzIadYTY7TlC7FliD+XI89FyzM2RqoAiALJ2yU42wNnfrRuByQQN9cHz1j\nArKZknDfP6HYxUS0RQ==\n-----END CERTIFICATE-----\n", + }, + }, + endorsements: [ + { + signer: { + foo: "Org1MSP", + }, + }, + ], + }, + ], + }, + ], +}; + +export const sampleDiscoveryResults = { + msps: { + Org2MSP: { + id: "Org2MSP", + name: "Org2MSP", + organizationalUnitIdentifiers: [], + rootCerts: + "-----BEGIN CERTIFICATE-----\nMIICHjCCAcWgAwIBAgIUfFNit/ZNZY2SIbGN6ekejAdRoikwCgYIKoZIzj0EAwIw\nbDELMAkGA1UEBhMCVUsxEjAQBgNVBAgTCUhhbXBzaGlyZTEQMA4GA1UEBxMHSHVy\nc2xleTEZMBcGA1UEChMQb3JnMi5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eub3Jn\nMi5leGFtcGxlLmNvbTAeFw0yNTA0MDIwODI2MDBaFw00MDAzMjkwODI2MDBaMGwx\nCzAJBgNVBAYTAlVLMRIwEAYDVQQIEwlIYW1wc2hpcmUxEDAOBgNVBAcTB0h1cnNs\nZXkxGTAXBgNVBAoTEG9yZzIuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2NhLm9yZzIu\nZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARofvaM3udqgShD\njgunnP4hBLepJAdJJXVaF4gGwjGN7u0OJsUYefwoaZsIieJ+fJkpk+KWfV6esw5l\nopNQ7k0/o0UwQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAd\nBgNVHQ4EFgQUngIcE6RHSr5A0IcqEyqz6Gi27l4wCgYIKoZIzj0EAwIDRwAwRAIg\nWSvPOIUDa/rTJPRR9t54UzsY9mHHlxaTwFSTLTPuKdoCIG369U7uOHBCkSFiIe9s\nxQAnpvZCCd+l0XXJlx7h0a2M\n-----END CERTIFICATE-----\n", + intermediateCerts: "", + admins: "", + tlsRootCerts: + "-----BEGIN CERTIFICATE-----\nMIICHjCCAcWgAwIBAgIUfFNit/ZNZY2SIbGN6ekejAdRoikwCgYIKoZIzj0EAwIw\nbDELMAkGA1UEBhMCVUsxEjAQBgNVBAgTCUhhbXBzaGlyZTEQMA4GA1UEBxMHSHVy\nc2xleTEZMBcGA1UEChMQb3JnMi5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eub3Jn\nMi5leGFtcGxlLmNvbTAeFw0yNTA0MDIwODI2MDBaFw00MDAzMjkwODI2MDBaMGwx\nCzAJBgNVBAYTAlVLMRIwEAYDVQQIEwlIYW1wc2hpcmUxEDAOBgNVBAcTB0h1cnNs\nZXkxGTAXBgNVBAoTEG9yZzIuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2NhLm9yZzIu\nZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARofvaM3udqgShD\njgunnP4hBLepJAdJJXVaF4gGwjGN7u0OJsUYefwoaZsIieJ+fJkpk+KWfV6esw5l\nopNQ7k0/o0UwQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAd\nBgNVHQ4EFgQUngIcE6RHSr5A0IcqEyqz6Gi27l4wCgYIKoZIzj0EAwIDRwAwRAIg\nWSvPOIUDa/rTJPRR9t54UzsY9mHHlxaTwFSTLTPuKdoCIG369U7uOHBCkSFiIe9s\nxQAnpvZCCd+l0XXJlx7h0a2M\n-----END CERTIFICATE-----\n", + tlsIntermediateCerts: "", + }, + OrdererMSP: { + id: "OrdererMSP", + name: "OrdererMSP", + organizationalUnitIdentifiers: [], + rootCerts: + "-----BEGIN CERTIFICATE-----\nMIICCjCCAbGgAwIBAgIUHxMC6sxNcaZZCyez3FqdrC2KbnUwCgYIKoZIzj0EAwIw\nYjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQHEwhOZXcg\nWW9yazEUMBIGA1UEChMLZXhhbXBsZS5jb20xFzAVBgNVBAMTDmNhLmV4YW1wbGUu\nY29tMB4XDTI1MDQwMjA4MjYwMFoXDTQwMDMyOTA4MjYwMFowYjELMAkGA1UEBhMC\nVVMxETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQHEwhOZXcgWW9yazEUMBIGA1UE\nChMLZXhhbXBsZS5jb20xFzAVBgNVBAMTDmNhLmV4YW1wbGUuY29tMFkwEwYHKoZI\nzj0CAQYIKoZIzj0DAQcDQgAEgSssN7HxPWwg2+vhERmNqt5jtmUn9dJpdm1nEBVY\nqRDEpnsssIo7O0riIeywxFnhXSPTDr83mHX5ROFLONh/CKNFMEMwDgYDVR0PAQH/\nBAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFDs3WE+sRoD6Gdh5\n9mzTpZ5RkOdGMAoGCCqGSM49BAMCA0cAMEQCIAjAssqpRqiuPjRwxWDvMYxx9kBy\nMZbalwUjZ+xwbyG4AiAyQuYy6rpCw/IczpJdqZAbwwkYrUYiJd1kecwwnXxu8w==\n-----END CERTIFICATE-----\n", + intermediateCerts: "", + admins: "", + tlsRootCerts: + "-----BEGIN CERTIFICATE-----\nMIICCjCCAbGgAwIBAgIUHxMC6sxNcaZZCyez3FqdrC2KbnUwCgYIKoZIzj0EAwIw\nYjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQHEwhOZXcg\nWW9yazEUMBIGA1UEChMLZXhhbXBsZS5jb20xFzAVBgNVBAMTDmNhLmV4YW1wbGUu\nY29tMB4XDTI1MDQwMjA4MjYwMFoXDTQwMDMyOTA4MjYwMFowYjELMAkGA1UEBhMC\nVVMxETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQHEwhOZXcgWW9yazEUMBIGA1UE\nChMLZXhhbXBsZS5jb20xFzAVBgNVBAMTDmNhLmV4YW1wbGUuY29tMFkwEwYHKoZI\nzj0CAQYIKoZIzj0DAQcDQgAEgSssN7HxPWwg2+vhERmNqt5jtmUn9dJpdm1nEBVY\nqRDEpnsssIo7O0riIeywxFnhXSPTDr83mHX5ROFLONh/CKNFMEMwDgYDVR0PAQH/\nBAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFDs3WE+sRoD6Gdh5\n9mzTpZ5RkOdGMAoGCCqGSM49BAMCA0cAMEQCIAjAssqpRqiuPjRwxWDvMYxx9kBy\nMZbalwUjZ+xwbyG4AiAyQuYy6rpCw/IczpJdqZAbwwkYrUYiJd1kecwwnXxu8w==\n-----END CERTIFICATE-----\n", + tlsIntermediateCerts: "", + }, + Org1MSP: { + id: "Org1MSP", + name: "Org1MSP", + organizationalUnitIdentifiers: [], + rootCerts: + "-----BEGIN CERTIFICATE-----\nMIICJjCCAc2gAwIBAgIUKlBnHkZhAX6EvIRadEAc5fcVZdUwCgYIKoZIzj0EAwIw\ncDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQH\nEwZEdXJoYW0xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjUwNDAyMDgyNjAwWhcNNDAwMzI5MDgyNjAw\nWjBwMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExDzANBgNV\nBAcTBkR1cmhhbTEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEcMBoGA1UEAxMT\nY2Eub3JnMS5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABH/R\nyYrvRTkbEi1o/VRUMqi9WPfFh2ANpzgq+ax1OJufEpa6duDXNOPV7+jJ8jrZGSsG\ndB7HSVxmQ4YJM7dFcn+jRTBDMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAG\nAQH/AgEBMB0GA1UdDgQWBBQpN+0sYu6en762EX2o1ls4QWjgfzAKBggqhkjOPQQD\nAgNHADBEAiB4tKn8Tv5h6cLy69UIT/kECozoWE8T41OQSism/nW8hAIgEMPR2sbU\nvu6ZKkeg4vmfDjqXl/A5e74Mwf+A0+FnooE=\n-----END CERTIFICATE-----\n", + intermediateCerts: "", + admins: "", + tlsRootCerts: + "-----BEGIN CERTIFICATE-----\nMIICJjCCAc2gAwIBAgIUKlBnHkZhAX6EvIRadEAc5fcVZdUwCgYIKoZIzj0EAwIw\ncDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQH\nEwZEdXJoYW0xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjUwNDAyMDgyNjAwWhcNNDAwMzI5MDgyNjAw\nWjBwMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExDzANBgNV\nBAcTBkR1cmhhbTEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEcMBoGA1UEAxMT\nY2Eub3JnMS5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABH/R\nyYrvRTkbEi1o/VRUMqi9WPfFh2ANpzgq+ax1OJufEpa6duDXNOPV7+jJ8jrZGSsG\ndB7HSVxmQ4YJM7dFcn+jRTBDMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAG\nAQH/AgEBMB0GA1UdDgQWBBQpN+0sYu6en762EX2o1ls4QWjgfzAKBggqhkjOPQQD\nAgNHADBEAiB4tKn8Tv5h6cLy69UIT/kECozoWE8T41OQSism/nW8hAIgEMPR2sbU\nvu6ZKkeg4vmfDjqXl/A5e74Mwf+A0+FnooE=\n-----END CERTIFICATE-----\n", + tlsIntermediateCerts: "", + }, + }, + orderers: { + OrdererMSP: { + endpoints: [ + { + host: "orderer.example.com", + port: 7050, + name: "orderer.example.com:7050", + }, + ], + }, + }, + peersByMSP: { + Org2MSP: { + peers: [ + { + mspid: "Org2MSP", + endpoint: "peer0.org2.example.com:9051", + name: "peer0.org2.example.com:9051", + ledgerHeight: 15, + chaincodes: [ + { + name: "basic", + version: "1", + }, + { + name: "copyAssetTrade", + version: "1", + }, + { + name: "_lifecycle", + version: "1", + }, + ], + }, + ], + }, + Org1MSP: { + peers: [ + { + mspid: "Org1MSP", + endpoint: "peer0.org1.example.com:7051", + name: "peer0.org1.example.com:7051", + ledgerHeight: 15, + chaincodes: [ + { + name: "basic", + version: "1", + }, + { + name: "copyAssetTrade", + version: "1", + }, + { + name: "_lifecycle", + version: "1", + }, + ], + }, + ], + }, + }, + timestamp: 1743584187645, +}; diff --git a/tools/docker/supabase-all-in-one/Dockerfile b/tools/docker/supabase-all-in-one/Dockerfile index 955b2ba879..1526003d87 100644 --- a/tools/docker/supabase-all-in-one/Dockerfile +++ b/tools/docker/supabase-all-in-one/Dockerfile @@ -56,6 +56,8 @@ COPY ./src/healthcheck.sh /bin/healthcheck RUN chmod +x /bin/healthcheck HEALTHCHECK --interval=5s --timeout=5s --start-period=45s --retries=90 CMD /bin/healthcheck +# Studio +EXPOSE 3000 # Supabase EXPOSE 8000 # Postgres diff --git a/tools/docker/supabase-all-in-one/docker-compose.yml b/tools/docker/supabase-all-in-one/docker-compose.yml index 4e2607e8fd..e700284312 100644 --- a/tools/docker/supabase-all-in-one/docker-compose.yml +++ b/tools/docker/supabase-all-in-one/docker-compose.yml @@ -12,4 +12,3 @@ services: - "3000:3000" # Supabase Studio - "8000:8000" # Supabase API - "5432:5432" # Postgres - network_mode: host