Skip to content

Commit 3bee9e8

Browse files
RafaelAPBraynatopedrajeta
authored andcommitted
feat(cactus-connector-fabric): add get tx receipt by tx id
Authored-by: Eduardo Vasques <[email protected]> Signed-off-by: Rafael Belchior <[email protected]>
1 parent 8ee9c62 commit 3bee9e8

File tree

7 files changed

+215
-37
lines changed

7 files changed

+215
-37
lines changed

.github/dast/ generate-jwt.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const fs = require("fs");
2+
const { SignJWT } = require("jose");
3+
const crypto = require("crypto");
4+
5+
// Path to the config file
6+
const configFilePath = "./.config.json";
7+
8+
// Load and parse the config file
9+
const config = JSON.parse(fs.readFileSync(configFilePath, "utf8"));
10+
11+
// Extract audience and issuer from the config
12+
const audience = config.authorizationConfigJson.expressJwtOptions.audience;
13+
const issuer = config.authorizationConfigJson.expressJwtOptions.issuer;
14+
const secret = config.authorizationConfigJson.expressJwtOptions.secret;
15+
16+
// Log audience and issuer
17+
console.log(`Audience: ${audience}`);
18+
console.log(`Issuer: ${issuer}`);
19+
20+
// Generate a symmetric key for signing (HS256 uses a shared secret, not a public/private key pair)
21+
const jwtKeyPair = {
22+
privateKey: crypto.createSecretKey(secret),
23+
};
24+
25+
// Example of how to generate a JWT using `audience`, `issuer`, and `HS256`
26+
async function generateJWT() {
27+
const jwt = await new SignJWT({ scope: "read:health" })
28+
.setProtectedHeader({ alg: "HS256" })
29+
.setIssuer(issuer)
30+
.setAudience(audience)
31+
.sign(jwtKeyPair.privateKey);
32+
33+
console.log(`Generated JWT: ${jwt}`);
34+
}
35+
36+
generateJWT();
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
name: DAST_Scan_Nuclei
2-
2+
33
env:
44
NODEJS_VERSION: v18.18.2
5-
5+
66
on:
77
push:
88
branches: [main, dev]
9-
9+
1010
pull_request:
1111
branches: [main, dev]
12-
12+
1313
jobs:
1414
nuclei-scan:
1515
runs-on: ubuntu-22.04
@@ -25,30 +25,30 @@ jobs:
2525
libvcx \
2626
indy-cli \
2727
&& sudo rm -f /etc/apt/sources.list.d/sovrin.list*
28-
28+
2929
- name: Set up NodeJS ${{ env.NODEJS_VERSION }}
3030
uses: actions/[email protected]
3131
with:
3232
node-version: ${{ env.NODEJS_VERSION }}
33-
33+
3434
- name: Install jq
3535
run: sudo apt update && sudo apt install -y jq
36-
36+
3737
- name: Verify jq
3838
run: jq --version
39-
39+
4040
- uses: actions/[email protected]
41-
41+
4242
- uses: actions/[email protected]
4343
with:
4444
go-version: 1.23
45-
45+
4646
- run: go install -v github.com/projectdiscovery/nuclei/v3/cmd/[email protected]
47-
47+
4848
- run: nuclei --version
49-
49+
5050
- run: npm run configure
51-
51+
5252
- name: Create URLs file for Nuclei
5353
run: |
5454
echo https://localhost:4000/ > urls.txt
@@ -73,58 +73,93 @@ jobs:
7373
echo https://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/get-prometheus-exporter-metrics
7474
echo https://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/get-besu-record
7575
} >> urls.txt
76-
76+
7777
- run: yarn generate-api-server-config
78-
79-
- run: jq '.authorizationProtocol = "NONE"' .config.json > .config2.json && mv .config2.json .config.json
80-
78+
79+
#- run: jq '.authorizationProtocol = "NONE"' .config.json > .config2.json && mv .config2.json .config.json
80+
8181
# Delete the first and the second items in the array (remove keychain and manual consortium plugins)
8282
- run: jq 'del(.plugins[0,1])' .config.json > .config2.json && mv .config2.json .config.json
83-
83+
8484
- name: Install Keychain manual plugin into the API server
8585
run: jq '.plugins += [{ "packageName":"@hyperledger/cactus-plugin-keychain-memory","type":"org.hyperledger.cactus.plugin_import_type.LOCAL","action":"org.hyperledger.cactus.plugin_import_action.INSTALL","options":{"packageSrc":"/home/runner/work/cacti/cacti/packages/cactus-plugin-keychain-memory/","instanceId":"0daacd05-d1cd-4eab-9332-4ad1aff4b909","keychainId":"d29d728e-eaa0-4e2d-b187-d132242b0d9a"}}]' .config.json > .config2.json && mv .config2.json .config.json
86-
86+
8787
- name: Install Fabric connector into the API server
8888
run: jq '.plugins += [{ "packageName":"@hyperledger/cactus-plugin-ledger-connector-fabric", "type":"org.hyperledger.cactus.plugin_import_type.LOCAL", "action":"org.hyperledger.cactus.plugin_import_action.INSTALL", "options":{ "packageSrc":"/home/runner/work/cacti/cacti/packages/cactus-plugin-ledger-connector-fabric/", "instanceId":"some-unique-fabric-connector-instance-id", "peerBinary":"/fabric-samples/bin/peer", "connectionProfile":"{}", "dockerBinary":"usr/local/bin/docker","cliContainerEnv":{"CORE_PEER_LOCALMSPID":"Org1MSP","CORE_PEER_ADDRESS":"peer0.org1.example.com:7051","CORE_PEER_MSPCONFIGPATH":"/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp","CORE_PEER_TLS_ROOTCERT_FILE":"/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt","ORDERER_TLS_ROOTCERT_FILE":"/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem"},"discoveryOptions":{"enabled":true,"asLocalhost":true}}}] ' .config.json > .config2.json && mv .config2.json .config.json
89-
89+
9090
- name: Install Besu connector into the API server
9191
run: jq '.plugins += [{"packageName":"@hyperledger/cactus-plugin-ledger-connector-besu","type":"org.hyperledger.cactus.plugin_import_type.LOCAL","action":"org.hyperledger.cactus.plugin_import_action.INSTALL","options":{"packageSrc":"/home/runner/work/cacti/cacti/packages/cactus-plugin-ledger-connector-besu/", "rpcApiHttpHost":"http://127.0.0.1:8545", "rpcApiWsHost":"ws://127.0.0.1:8546", "instanceId":"some-unique-besu-connector-instance-id"}}]' .config.json > .config2.json && mv .config2.json .config.json
92-
92+
9393
- name: Run Besu all-in-one image
9494
run: |
9595
docker run -d -p 0.0.0.0:8545:8545/tcp -p 0.0.0.0:8546:8546/tcp -p 0.0.0.0:8888:8888/tcp -p 0.0.0.0:9001:9001/tcp -p 0.0.0.0:9545:9545/tcp ghcr.io/hyperledger/cactus-besu-all-in-one:v2.0.0-rc.7
9696
until curl --fail -X POST --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' localhost:8545; do sleep 5; done
97-
97+
9898
- name: Print API Server Config File - ./.config.json
9999
run: cat .config.json
100-
100+
101101
- name: Print Nuclei Config File - ./.nuclei-config.yaml
102102
run: cat .nuclei-config.yaml
103-
103+
104104
- name: Print Nuclei URL List File - ./urls.txt
105105
run: cat urls.txt
106+
#------------
107+
- name: Generate Audience and Issuer
108+
id: generate_ids
109+
run: |
110+
echo "audience=$(uuidgen)" >> $GITHUB_ENV
111+
echo "issuer=$(uuidgen)" >> $GITHUB_ENV
112+
113+
- name: Generate RSA Keys
114+
run: |
115+
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
116+
openssl rsa -in private_key.pem -pubout -out public_key.pem
117+
118+
- name: Display Results
119+
run: |
120+
echo "Audience: ${{ env.audience }}"
121+
echo "Issuer: ${{ env.issuer }}"
122+
echo "Public Key:"
123+
cat public_key.pem
124+
echo "Private Key:"
125+
cat private_key.pem
126+
127+
- run: jq '.expressJwtOptions.secret = "-----BEGIN PUBLIC KEY-----\n$(cat public_key.pem)\n-----END PUBLIC KEY-----" |
128+
.expressJwtOptions.algorithms = ["RS256"] |
129+
.expressJwtOptions.issuer = "${{ env.issuer }}" |
130+
.expressJwtOptions.audience = "${{ env.audience }}"' .config.json > .config2.json && mv .config2.json .config.json
131+
132+
- name: Generate Auth Bearer Token
133+
run: |
134+
HEADER_B64=$(echo '{"alg":"RS256"}' | openssl base64 -e -A | tr -d '=' | tr '/+' '_-')
135+
PAYLOAD_B64=$(cat '{"scope":"read:health","iss":"${{ env.issuer }}","aud":"${{ env.audience }}"}' | openssl base64 -e -A | tr -d '=' | tr '/+' '_-')
136+
137+
SIGNATURE=$(echo -n "$HEADER_B64.$PAYLOAD_B64" | openssl dgst -sha256 -sign cat private_key.pem | openssl base64 -e -A | tr -d '=' | tr '/+' '_-')
138+
JWT="$HEADER_B64.$PAYLOAD_B64.$SIGNATURE"
139+
echo dast_jwt=$JWT >> $GITHUB_ENV
106140
107141
- name: Start API Server & Run DAST
108142
uses: BerniWittmann/[email protected]
109143
env:
110-
# Needed because the wait-on syntax otherwise keeps thinking that
111-
# there is a problem due to our self signed certificates on the
112-
# test instance of the API server
113-
NODE_TLS_REJECT_UNAUTHORIZED: 0
144+
# Needed because the wait-on syntax otherwise keeps thinking that
145+
# there is a problem due to our self signed certificates on the
146+
# test instance of the API server
147+
NODE_TLS_REJECT_UNAUTHORIZED: 0
114148
with:
115149
build: yarn --version
116150
start: yarn start:api-server
117151
command: "nuclei -version"
118152
command-windows: echo "The project build is not supported on the Windows operating system. Please use Linux or macOS"
119-
wait-on: "https://localhost:4000/api/v1/api-server/healthcheck"
120153
# wait for 10 minutes for the server to respond
121154
wait-on-timeout: 120
122-
155+
wait-on-command: |
156+
curl -X GET https://localhost:4000/api/v1/api-server/healthcheck -k -H "Authorization: Bearer ${{ env.dast_jwt }}"
157+
123158
- name: Run the dast nuclei scan
124159
run: "nuclei -list=urls.txt -dast -severity=high,critical -sarif-export ~/nuclei.sarif -output=nuclei.log"
125-
160+
126161
- name: GitHub Workflow artifacts
127162
uses: actions/[email protected]
128163
with:
129164
name: nuclei.log
130-
path: nuclei.log
165+
path: nuclei.log

.github/workflows/generate-jwt.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const fs = require("fs");
2+
import { SignJWT, exportSPKI, generateKeyPair } from "jose";
3+
const crypto = require("crypto");
4+
5+
// Path to the config file
6+
const configFilePath = "./.config.json";
7+
8+
// Load and parse the config file
9+
const config = JSON.parse(fs.readFileSync(configFilePath, "utf8"));
10+
11+
// Extract audience and issuer from the config
12+
const audience = config.authorizationConfigJson.expressJwtOptions.audience;
13+
const issuer = config.authorizationConfigJson.expressJwtOptions.issuer;
14+
const secret = config.authorizationConfigJson.expressJwtOptions.secret;
15+
16+
// Log audience and issuer
17+
console.log(`Audience: ${audience}`);
18+
console.log(`Issuer: ${issuer}`);
19+
20+
// Generate a symmetric key for signing (HS256 uses a shared secret, not a public/private key pair)
21+
const jwtKeyPair = {
22+
privateKey: crypto.createSecretKey(secret),
23+
};
24+
25+
// Example of how to generate a JWT using `audience`, `issuer`, and `HS256`
26+
async function generateJWT() {
27+
const jwt = await new SignJWT({ scope: "read:health" })
28+
.setProtectedHeader({ alg: "HS256" })
29+
.setIssuer(issuer)
30+
.setAudience(audience)
31+
.sign(jwtKeyPair.privateKey);
32+
33+
console.log(`Generated JWT: ${jwt}`);
34+
}
35+
36+
generateJWT();

packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/common/get-transaction-receipt-by-tx-id.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ export async function getTransactionReceiptByTxID(
157157
if (!extensionNsRwset.rwset) continue;
158158

159159
const rwset = extensionNsRwset.rwset;
160-
if (!rwset.writes) continue;
160+
if (!rwset.writes || rwset.writes.length === 0) continue;
161161
const rwsetWrite = rwset.writes;
162162
if (!rwsetWrite[0].key) continue;
163163
const rwsetKey = rwsetWrite[0].key;

packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/plugin-ledger-connector-fabric.ts

+38-4
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ import {
147147
formatCactiFullBlockResponse,
148148
formatCactiTransactionsBlockResponse,
149149
} from "./get-block/cacti-block-formatters";
150+
150151
import { GetBlockEndpointV1 } from "./get-block/get-block-endpoint-v1";
151152
import { GetChainInfoEndpointV1 } from "./get-chain-info/get-chain-info-endpoint-v1";
152153
import { querySystemChainCode } from "./common/query-system-chain-code";
@@ -158,10 +159,17 @@ import {
158159
} from "./common/utils";
159160
import { findAndReplaceFabricLoggingSpec } from "./common/find-and-replace-fabric-logging-spec";
160161
import { deployContractGoSourceImplFabricV256 } from "./deploy-contract-go-source/deploy-contract-go-source-impl-fabric-v2-5-6";
162+
import { Observable, ReplaySubject } from "rxjs";
161163

162164
const { loadFromConfig } = require("fabric-network/lib/impl/ccp/networkconfig");
163165
assertFabricFunctionIsAvailable(loadFromConfig, "loadFromConfig");
164166

167+
export interface IRunTxReqWithTxId {
168+
request: RunTransactionRequest;
169+
transactionId: string;
170+
timestamp: Date;
171+
}
172+
165173
/**
166174
* Constant value holding the default $GOPATH in the Fabric CLI container as
167175
* observed on fabric deployments that are produced by the official examples
@@ -229,6 +237,7 @@ export class PluginLedgerConnectorFabric
229237
private readonly certStore: CertDatastore;
230238
private readonly sshDebugOn: boolean;
231239
private runningWatchBlocksMonitors = new Set<WatchBlocksV1Endpoint>();
240+
private txSubject: ReplaySubject<IRunTxReqWithTxId> = new ReplaySubject();
232241

233242
public get className(): string {
234243
return PluginLedgerConnectorFabric.CLASS_NAME;
@@ -295,18 +304,26 @@ export class PluginLedgerConnectorFabric
295304
);
296305
}
297306

307+
298308
this.sshDebugOn = opts.sshDebugOn === true;
299309
if (this.opts.sshConfig) {
300310
this.sshConfig = this.opts.sshConfig;
311+
312+
if (this.sshDebugOn) {
313+
this.sshConfig = this.enableSshDebugLogs(this.sshConfig);
314+
}
301315
} else if (this.opts.sshConfigB64) {
302316
const sshConfigBuffer = Buffer.from(this.opts.sshConfigB64, "base64");
303317
const sshConfigString = sshConfigBuffer.toString("utf-8");
304318
this.sshConfig = JSON.parse(sshConfigString);
319+
320+
if (this.sshDebugOn) {
321+
this.sshConfig = this.enableSshDebugLogs(this.sshConfig);
322+
}
305323
} else {
306-
throw new Error("Cannot instantiate Fabric connector without SSH config");
307-
}
308-
if (this.sshDebugOn) {
309-
this.sshConfig = this.enableSshDebugLogs(this.sshConfig);
324+
// throw new Error("Cannot instantiate Fabric connector without SSH config");
325+
this.sshConfig = {}
326+
console.log("The check for sshConfig has been temporarily removed")
310327
}
311328

312329
this.signCallback = opts.signCallback;
@@ -339,6 +356,10 @@ export class PluginLedgerConnectorFabric
339356
return `@hyperledger/cactus-plugin-ledger-connector-fabric`;
340357
}
341358

359+
public getTxSubjectObservable(): Observable<IRunTxReqWithTxId> {
360+
return this.txSubject.asObservable();
361+
}
362+
342363
public async onPluginInit(): Promise<unknown> {
343364
return;
344365
}
@@ -1178,6 +1199,7 @@ export class PluginLedgerConnectorFabric
11781199
): Promise<RunTransactionResponse> {
11791200
const fnTag = `${this.className}#transact()`;
11801201
this.log.debug("%s ENTER", fnTag);
1202+
11811203
const {
11821204
channelName,
11831205
contractName,
@@ -1247,6 +1269,7 @@ export class PluginLedgerConnectorFabric
12471269
const transactionProposal = await contract.createTransaction(fnName);
12481270
transactionProposal.setEndorsingPeers(endorsingTargets);
12491271
out = await transactionProposal.setTransient(transientMap).submit();
1272+
transactionId = transactionProposal.getTransactionId();
12501273
break;
12511274
}
12521275
default: {
@@ -1255,6 +1278,17 @@ export class PluginLedgerConnectorFabric
12551278
}
12561279
}
12571280

1281+
// create IRunTxReqWithTxId for transaction monitoring
1282+
const receiptData: IRunTxReqWithTxId = {
1283+
request: req,
1284+
transactionId: transactionId == "" ? uuidv4() : transactionId,
1285+
timestamp: new Date(),
1286+
};
1287+
this.log.debug(
1288+
`IRunTxReqWithTxId created with ID: ${receiptData.transactionId}`,
1289+
);
1290+
this.txSubject.next(receiptData);
1291+
12581292
const res: RunTransactionResponse = {
12591293
functionOutput: this.convertToTransactionResponseType(
12601294
out,

packages/cactus-plugin-ledger-connector-fabric/src/main/typescript/public-api.ts

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export {
99
PluginLedgerConnectorFabric,
1010
IPluginLedgerConnectorFabricOptions,
1111
SignPayloadCallback,
12+
IRunTxReqWithTxId,
1213
} from "./plugin-ledger-connector-fabric";
1314

1415
import { IPluginFactoryOptions } from "@hyperledger/cactus-core-api";

0 commit comments

Comments
 (0)