Skip to content

Commit

Permalink
feat(cactus-connector-fabric): add get tx receipt by tx id
Browse files Browse the repository at this point in the history
Authored-by: Eduardo Vasques <eduardovasques10@tecnico.ulisboa.pt>
Signed-off-by: Rafael Belchior <rafael.belchior@tecnico.ulisboa.pt>
  • Loading branch information
RafaelAPB authored and raynatopedrajeta committed Jan 8, 2025
1 parent 8ee9c62 commit 3bee9e8
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 37 deletions.
36 changes: 36 additions & 0 deletions .github/dast/ generate-jwt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const fs = require("fs");
const { SignJWT } = require("jose");
const crypto = require("crypto");

// Path to the config file
const configFilePath = "./.config.json";

// Load and parse the config file
const config = JSON.parse(fs.readFileSync(configFilePath, "utf8"));

// Extract audience and issuer from the config
const audience = config.authorizationConfigJson.expressJwtOptions.audience;
const issuer = config.authorizationConfigJson.expressJwtOptions.issuer;
const secret = config.authorizationConfigJson.expressJwtOptions.secret;

// Log audience and issuer
console.log(`Audience: ${audience}`);
console.log(`Issuer: ${issuer}`);

// Generate a symmetric key for signing (HS256 uses a shared secret, not a public/private key pair)
const jwtKeyPair = {
privateKey: crypto.createSecretKey(secret),
};

// Example of how to generate a JWT using `audience`, `issuer`, and `HS256`
async function generateJWT() {
const jwt = await new SignJWT({ scope: "read:health" })
.setProtectedHeader({ alg: "HS256" })
.setIssuer(issuer)
.setAudience(audience)
.sign(jwtKeyPair.privateKey);

console.log(`Generated JWT: ${jwt}`);
}

generateJWT();
99 changes: 67 additions & 32 deletions .github/workflows/.dast-nuclei-cmd-api-server.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
name: DAST_Scan_Nuclei

env:
NODEJS_VERSION: v18.18.2

on:
push:
branches: [main, dev]

pull_request:
branches: [main, dev]

jobs:
nuclei-scan:
runs-on: ubuntu-22.04
Expand All @@ -25,30 +25,30 @@ jobs:
libvcx \
indy-cli \
&& sudo rm -f /etc/apt/sources.list.d/sovrin.list*
- name: Set up NodeJS ${{ env.NODEJS_VERSION }}
uses: actions/setup-node@v4.0.3
with:
node-version: ${{ env.NODEJS_VERSION }}

- name: Install jq
run: sudo apt update && sudo apt install -y jq

- name: Verify jq
run: jq --version

- uses: actions/checkout@v4.1.7

- uses: actions/setup-go@v4.0.0
with:
go-version: 1.23

- run: go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@v3.3.5

- run: nuclei --version

- run: npm run configure

- name: Create URLs file for Nuclei
run: |
echo https://localhost:4000/ > urls.txt
Expand All @@ -73,58 +73,93 @@ jobs:
echo https://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/get-prometheus-exporter-metrics
echo https://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/get-besu-record
} >> urls.txt
- run: yarn generate-api-server-config

- run: jq '.authorizationProtocol = "NONE"' .config.json > .config2.json && mv .config2.json .config.json
#- run: jq '.authorizationProtocol = "NONE"' .config.json > .config2.json && mv .config2.json .config.json

# Delete the first and the second items in the array (remove keychain and manual consortium plugins)
- run: jq 'del(.plugins[0,1])' .config.json > .config2.json && mv .config2.json .config.json

- name: Install Keychain manual plugin into the API server
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

- name: Install Fabric connector into the API server
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/Admin@org1.example.com/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

- name: Install Besu connector into the API server
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

- name: Run Besu all-in-one image
run: |
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
until curl --fail -X POST --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' localhost:8545; do sleep 5; done
- name: Print API Server Config File - ./.config.json
run: cat .config.json

- name: Print Nuclei Config File - ./.nuclei-config.yaml
run: cat .nuclei-config.yaml

- name: Print Nuclei URL List File - ./urls.txt
run: cat urls.txt
#------------
- name: Generate Audience and Issuer
id: generate_ids
run: |

Check failure on line 109 in .github/workflows/.dast-nuclei-cmd-api-server.yaml

View workflow job for this annotation

GitHub Actions / ActionLint / Lint_GitHub_Actions

shellcheck reported issue in this script: SC2086:info:1:31: Double quote to prevent globbing and word splitting

Check failure on line 109 in .github/workflows/.dast-nuclei-cmd-api-server.yaml

View workflow job for this annotation

GitHub Actions / ActionLint / Lint_GitHub_Actions

shellcheck reported issue in this script: SC2086:info:2:29: Double quote to prevent globbing and word splitting
echo "audience=$(uuidgen)" >> $GITHUB_ENV
echo "issuer=$(uuidgen)" >> $GITHUB_ENV
- name: Generate RSA Keys
run: |
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -in private_key.pem -pubout -out public_key.pem
- name: Display Results
run: |
echo "Audience: ${{ env.audience }}"
echo "Issuer: ${{ env.issuer }}"
echo "Public Key:"
cat public_key.pem
echo "Private Key:"
cat private_key.pem
- run: jq '.expressJwtOptions.secret = "-----BEGIN PUBLIC KEY-----\n$(cat public_key.pem)\n-----END PUBLIC KEY-----" |
.expressJwtOptions.algorithms = ["RS256"] |
.expressJwtOptions.issuer = "${{ env.issuer }}" |
.expressJwtOptions.audience = "${{ env.audience }}"' .config.json > .config2.json && mv .config2.json .config.json

- name: Generate Auth Bearer Token
run: |

Check failure on line 133 in .github/workflows/.dast-nuclei-cmd-api-server.yaml

View workflow job for this annotation

GitHub Actions / ActionLint / Lint_GitHub_Actions

shellcheck reported issue in this script: SC2002:style:2:19: Useless cat. Consider 'cmd < file | ..' or 'cmd file | ..' instead

Check failure on line 133 in .github/workflows/.dast-nuclei-cmd-api-server.yaml

View workflow job for this annotation

GitHub Actions / ActionLint / Lint_GitHub_Actions

shellcheck reported issue in this script: SC2086:info:6:15: Double quote to prevent globbing and word splitting

Check failure on line 133 in .github/workflows/.dast-nuclei-cmd-api-server.yaml

View workflow job for this annotation

GitHub Actions / ActionLint / Lint_GitHub_Actions

shellcheck reported issue in this script: SC2086:info:6:23: Double quote to prevent globbing and word splitting
HEADER_B64=$(echo '{"alg":"RS256"}' | openssl base64 -e -A | tr -d '=' | tr '/+' '_-')
PAYLOAD_B64=$(cat '{"scope":"read:health","iss":"${{ env.issuer }}","aud":"${{ env.audience }}"}' | openssl base64 -e -A | tr -d '=' | tr '/+' '_-')
SIGNATURE=$(echo -n "$HEADER_B64.$PAYLOAD_B64" | openssl dgst -sha256 -sign cat private_key.pem | openssl base64 -e -A | tr -d '=' | tr '/+' '_-')
JWT="$HEADER_B64.$PAYLOAD_B64.$SIGNATURE"
echo dast_jwt=$JWT >> $GITHUB_ENV
- name: Start API Server & Run DAST
uses: BerniWittmann/background-server-action@v1.1.0
env:
# Needed because the wait-on syntax otherwise keeps thinking that
# there is a problem due to our self signed certificates on the
# test instance of the API server
NODE_TLS_REJECT_UNAUTHORIZED: 0
# Needed because the wait-on syntax otherwise keeps thinking that
# there is a problem due to our self signed certificates on the
# test instance of the API server
NODE_TLS_REJECT_UNAUTHORIZED: 0
with:
build: yarn --version
start: yarn start:api-server
command: "nuclei -version"
command-windows: echo "The project build is not supported on the Windows operating system. Please use Linux or macOS"
wait-on: "https://localhost:4000/api/v1/api-server/healthcheck"
# wait for 10 minutes for the server to respond
wait-on-timeout: 120

wait-on-command: |
curl -X GET https://localhost:4000/api/v1/api-server/healthcheck -k -H "Authorization: Bearer ${{ env.dast_jwt }}"
- name: Run the dast nuclei scan
run: "nuclei -list=urls.txt -dast -severity=high,critical -sarif-export ~/nuclei.sarif -output=nuclei.log"

- name: GitHub Workflow artifacts
uses: actions/upload-artifact@v3.0.0
with:
name: nuclei.log
path: nuclei.log
path: nuclei.log
36 changes: 36 additions & 0 deletions .github/workflows/generate-jwt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const fs = require("fs");
import { SignJWT, exportSPKI, generateKeyPair } from "jose";
const crypto = require("crypto");

// Path to the config file
const configFilePath = "./.config.json";

// Load and parse the config file
const config = JSON.parse(fs.readFileSync(configFilePath, "utf8"));

// Extract audience and issuer from the config
const audience = config.authorizationConfigJson.expressJwtOptions.audience;
const issuer = config.authorizationConfigJson.expressJwtOptions.issuer;
const secret = config.authorizationConfigJson.expressJwtOptions.secret;

// Log audience and issuer
console.log(`Audience: ${audience}`);
console.log(`Issuer: ${issuer}`);

// Generate a symmetric key for signing (HS256 uses a shared secret, not a public/private key pair)
const jwtKeyPair = {
privateKey: crypto.createSecretKey(secret),
};

// Example of how to generate a JWT using `audience`, `issuer`, and `HS256`
async function generateJWT() {
const jwt = await new SignJWT({ scope: "read:health" })
.setProtectedHeader({ alg: "HS256" })
.setIssuer(issuer)
.setAudience(audience)
.sign(jwtKeyPair.privateKey);

console.log(`Generated JWT: ${jwt}`);
}

generateJWT();
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export async function getTransactionReceiptByTxID(
if (!extensionNsRwset.rwset) continue;

const rwset = extensionNsRwset.rwset;
if (!rwset.writes) continue;
if (!rwset.writes || rwset.writes.length === 0) continue;
const rwsetWrite = rwset.writes;
if (!rwsetWrite[0].key) continue;
const rwsetKey = rwsetWrite[0].key;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ import {
formatCactiFullBlockResponse,
formatCactiTransactionsBlockResponse,
} from "./get-block/cacti-block-formatters";

import { GetBlockEndpointV1 } from "./get-block/get-block-endpoint-v1";
import { GetChainInfoEndpointV1 } from "./get-chain-info/get-chain-info-endpoint-v1";
import { querySystemChainCode } from "./common/query-system-chain-code";
Expand All @@ -158,10 +159,17 @@ import {
} from "./common/utils";
import { findAndReplaceFabricLoggingSpec } from "./common/find-and-replace-fabric-logging-spec";
import { deployContractGoSourceImplFabricV256 } from "./deploy-contract-go-source/deploy-contract-go-source-impl-fabric-v2-5-6";
import { Observable, ReplaySubject } from "rxjs";

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

export interface IRunTxReqWithTxId {
request: RunTransactionRequest;
transactionId: string;
timestamp: Date;
}

/**
* Constant value holding the default $GOPATH in the Fabric CLI container as
* observed on fabric deployments that are produced by the official examples
Expand Down Expand Up @@ -229,6 +237,7 @@ export class PluginLedgerConnectorFabric
private readonly certStore: CertDatastore;
private readonly sshDebugOn: boolean;
private runningWatchBlocksMonitors = new Set<WatchBlocksV1Endpoint>();
private txSubject: ReplaySubject<IRunTxReqWithTxId> = new ReplaySubject();

public get className(): string {
return PluginLedgerConnectorFabric.CLASS_NAME;
Expand Down Expand Up @@ -295,18 +304,26 @@ export class PluginLedgerConnectorFabric
);
}


this.sshDebugOn = opts.sshDebugOn === true;
if (this.opts.sshConfig) {
this.sshConfig = this.opts.sshConfig;

if (this.sshDebugOn) {
this.sshConfig = this.enableSshDebugLogs(this.sshConfig);
}
} else if (this.opts.sshConfigB64) {
const sshConfigBuffer = Buffer.from(this.opts.sshConfigB64, "base64");
const sshConfigString = sshConfigBuffer.toString("utf-8");
this.sshConfig = JSON.parse(sshConfigString);

if (this.sshDebugOn) {
this.sshConfig = this.enableSshDebugLogs(this.sshConfig);
}
} else {
throw new Error("Cannot instantiate Fabric connector without SSH config");
}
if (this.sshDebugOn) {
this.sshConfig = this.enableSshDebugLogs(this.sshConfig);
// throw new Error("Cannot instantiate Fabric connector without SSH config");
this.sshConfig = {}
console.log("The check for sshConfig has been temporarily removed")
}

this.signCallback = opts.signCallback;
Expand Down Expand Up @@ -339,6 +356,10 @@ export class PluginLedgerConnectorFabric
return `@hyperledger/cactus-plugin-ledger-connector-fabric`;
}

public getTxSubjectObservable(): Observable<IRunTxReqWithTxId> {
return this.txSubject.asObservable();
}

public async onPluginInit(): Promise<unknown> {
return;
}
Expand Down Expand Up @@ -1178,6 +1199,7 @@ export class PluginLedgerConnectorFabric
): Promise<RunTransactionResponse> {
const fnTag = `${this.className}#transact()`;
this.log.debug("%s ENTER", fnTag);

const {
channelName,
contractName,
Expand Down Expand Up @@ -1247,6 +1269,7 @@ export class PluginLedgerConnectorFabric
const transactionProposal = await contract.createTransaction(fnName);
transactionProposal.setEndorsingPeers(endorsingTargets);
out = await transactionProposal.setTransient(transientMap).submit();
transactionId = transactionProposal.getTransactionId();
break;
}
default: {
Expand All @@ -1255,6 +1278,17 @@ export class PluginLedgerConnectorFabric
}
}

// create IRunTxReqWithTxId for transaction monitoring
const receiptData: IRunTxReqWithTxId = {
request: req,
transactionId: transactionId == "" ? uuidv4() : transactionId,
timestamp: new Date(),
};
this.log.debug(
`IRunTxReqWithTxId created with ID: ${receiptData.transactionId}`,
);
this.txSubject.next(receiptData);

const res: RunTransactionResponse = {
functionOutput: this.convertToTransactionResponseType(
out,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export {
PluginLedgerConnectorFabric,
IPluginLedgerConnectorFabricOptions,
SignPayloadCallback,
IRunTxReqWithTxId,
} from "./plugin-ledger-connector-fabric";

import { IPluginFactoryOptions } from "@hyperledger/cactus-core-api";
Expand Down
Loading

0 comments on commit 3bee9e8

Please sign in to comment.