Skip to content

Commit

Permalink
feat(satp-hermes): add oracle (wip)
Browse files Browse the repository at this point in the history
Signed-off-by: Rafael Belchior <rafael.belchior@tecnico.ulisboa.pt>
  • Loading branch information
RafaelAPB committed Feb 21, 2025
1 parent 641cd50 commit 8a1c47e
Show file tree
Hide file tree
Showing 28 changed files with 918 additions and 87 deletions.
4 changes: 3 additions & 1 deletion packages/cactus-plugin-satp-hermes/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@
"generate-sdk:go": "openapi-generator-cli generate -i ./src/main/yml/bol/openapi-blo-bundled.yml -g go -o ./src/main/go/generated/gateway-client --additional-properties=packageName=generated,generateInterfaces=true,packageVersion=v0.0.1,moduleName=github.com/hyperledger/cacti/packages/cactus-plugin-satp-hermes/src/main/go/generated --git-user-id hyperledger --git-repo-id cacti/packages/cactus-plugin-satp-hermes/src/main/go/generated",
"generate-sdk:typescript-axios-bol": "yarn bundle-openapi-yaml && yarn bundle-openapi-json && openapi-generator-cli generate -i ./src/main/yml/bol/openapi-blo-bundled.yml -g typescript-axios -o ./src/main/typescript/generated/gateway-client/typescript-axios/ --reserved-words-mappings protected=protected --enable-post-process-file",
"lint": "run-p 'lint:*'",
"lint:eslint": "eslint './src/**/*.{js,ts}' --quiet --fix && cspell \"*/*/src/**/*.{js,ts}\"",
"lint-code": "run-s format:eslint format:prettier",
"format:eslint": "eslint '**/*.{js,ts}' --quiet --fix",
"format:prettier": "prettier --write --config ../../.prettierrc.js \"**/{openapi.json,*.ts,*.js}\"",
"lint:oapi": "vacuum lint -d -e ./src/main/yml/bol/openapi-blo-bundled.yml",
"lint:protobuf": "buf lint --path src/main/proto --verbose",
"preinstall": "curl -L https://foundry.paradigm.xyz | bash && foundryup",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ export async function executeGetIntegrations(
logger.error(`${fnTag}, Error getting status: ${error.message}`);
throw error;
}
logger.error(`${fnTag}, Unexpected error: ${error.message}`);
throw new Error("An unexpected error occurred while obtaining status.");
logger.error(`${fnTag}, Unexpected error: ${error.message}`);
throw new Error("An unexpected error occurred while obtaining status.");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,9 @@ export class BLODispatcher {
}

private getTargetGatewayClient(id: string) {
const channels: [string, { toGatewayID: string }][] = Array.from(this.orchestrator.getChannels());
const channels: [string, { toGatewayID: string }][] = Array.from(
this.orchestrator.getChannels(),
);
const filtered = channels.filter((ch) => {
return ch[0] === id && ch[1].toGatewayID === id;
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// interfaces api1(http) with gateway. calls oracle-execute-task

// todo load endpoints in the dispatcher
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// todo implement service by calling internal compomnents (public functions of oracle manager)
/*
GOAL
Goal is to implement an oracle that allows data transfers, keeping accountability. gateway notarizes data (produces a proof with bungee) and persists the data on target + notarization
IDEA:
instead of Register data transfer task,that is ran on specific conditions, do a one-time data transfer task
input parameters include
4. if event name, then also event fields 5. if function endponit, which method and parameters
6. session id (for data transfer)
7. (optional) callbackurl for notifications
smart contract addresses, functions, and parameters for source and target chains, etc
returns an ID (of data transfer task, taskID) to be used in the status endpoint;
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// interfaces api1(http) with gateway. calls oracle-get-status

// todo load endpoints in the dispatcher
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// todo implement service by calling internal compomnents (public functions of oracle manager)
/*
user provides the status id given by register endpoint, and gets a status
TBD define data model for status, suggestion: NOT_FOUND, INVALID, PENDING, SUCCESS, FAILED
returns a status and useful data to confirm such status (eg bungee notarizations, tx hashes, etc)
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// interfaces api1(http) with gateway. calls oracle-register-task

// todo load endpoints in the dispatcher
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// todo implement service by calling internal compomnents (public functions of oracle manager)
/*
GOAL
Goal is to implement an oracle that allows data transfers, keeping accountability. gateway notarizes data (produces a proof with bungee) and persists the data on target + notarization
IDEA:
Register data transfer task (automatic data transfer, needs trigger conditions)ORexecute data transfer task (perform one data transfer at current time)
General Config includes:
1. Source and target chain id
2. Source and target chain contract id
3. Source chain contract event name or function endpoint
4. if event name, then also event fields 5. if function endponit, which method and parameters
6. session id (for data transfer)
7. (optional) callbackurl for notifications
config for automatic data transfer
8. trigger conditions (eg fire event, return value on certain function call changes, time,
returns an ID (of data transfer task, taskID) to be used in the status endpoint;
*/
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ import {
saveSignature,
SessionType,
} from "../../session-utils";
import { type Asset, createAssetId } from "../../../cross-chain-mechanisms/satp-bridge/types/asset";
import {
type Asset,
createAssetId,
} from "../../../cross-chain-mechanisms/satp-bridge/types/asset";
import {
SATPService,
SATPServiceType,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//// filepath: /Users/rafaelapb/Projects/blockchain-integration-framework/packages/cactus-plugin-satp-hermes/src/main/typescript/cross-chain-mechanisms/oracle/oracle-abstract.ts
import type {
LogLevelDesc,
} from "@hyperledger/cactus-common";
import type { PluginBungeeHermes } from "@hyperledger/cactus-plugin-bungee-hermes";
import type { TransactionResponse } from "../../types/blockchain-interaction";

/**
* Common interface options for all Oracles.
*/
export interface OracleAbstractOptions {
bungee: PluginBungeeHermes;
logLevel?: LogLevelDesc;
}

/**
* Data structure for updating the ledger with some payload.
*/
export interface UpdateOracleEntryBase {
header: {
targetChainId: string;
sequenceNumber: number;
};
payload: string;
}

/**
* Data structure for reading from the ledger (method name, contract, etc.).
*/
export interface ReadEntryArgsBase {
chainId: string;
contractId: string;
methodName: string;
params?: any[];
}

export abstract class OracleAbstract {
protected readonly config: any;
public network: string;

constructor(options: OracleAbstractOptions) {
this.network = this.config?.network?.id || "";
}

/**
* Method to update the ledger with some payload.
* Must be implemented by subclass (Fabric/EVM, etc.).
*/
public abstract updateEntry(
entry: UpdateOracleEntryBase,
): Promise<{ transactionResponse: TransactionResponse; proof: any }>;

/**
* Method to read from the ledger (e.g., query a method).
* Must be implemented by subclass.
*/
public abstract readEntry(
args: ReadEntryArgsBase,
): Promise<{ callOutput: any; proof: any }>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import {
OracleAbstract,
type OracleAbstractOptions,
type UpdateOracleEntryBase,
type ReadEntryArgsBase,
} from "./oracle-abstract";
import type { TransactionResponse } from "../../types/blockchain-interaction";
import {
EthContractInvocationType,
PluginLedgerConnectorBesu,
type Web3SigningCredential,
} from "@hyperledger/cactus-plugin-ledger-connector-besu";
import safeStableStringify from "safe-stable-stringify";
import {
type Logger,
LoggerProvider,
type LogLevelDesc,
} from "@hyperledger/cactus-common";
import type {
IPluginBungeeHermesOptions,
PluginBungeeHermes,
} from "@hyperledger/cactus-plugin-bungee-hermes";
import type { IPluginLedgerConnectorEthereumOptions } from "@hyperledger/cactus-plugin-ledger-connector-ethereum";

export interface UpdateEVMOracleTransactionConfig {
contractName: string;
keychainId: string;
signingCredential: Web3SigningCredential;
gas: number;
network: {
id: string;
ledgerType: string;
};
options: any;
bungeeOptions: IPluginBungeeHermesOptions;
}

export interface UpdateEVMOracleEntry extends UpdateOracleEntryBase {}
export interface ReadEntryArgs extends ReadEntryArgsBase {}

export interface OracleEVMOptions extends OracleAbstractOptions {
oracleConfig: UpdateEVMOracleTransactionConfig;
connectorConfig: IPluginLedgerConnectorEthereumOptions;
}
export class OracleEVM extends OracleAbstract {
public static CLASS_NAME = "OracleAbstract";
private readonly connector: PluginLedgerConnectorBesu;
protected readonly config: UpdateEVMOracleTransactionConfig;
private readonly logger: Logger;
private readonly bungee: PluginBungeeHermes;

constructor(options: OracleEVMOptions, level?: LogLevelDesc) {
super({
bungee: options.bungee,
logLevel: level || options.logLevel,
});
const label = OracleEVM.CLASS_NAME;
this.logger = LoggerProvider.getOrCreate({ label, level: level || "INFO" });
this.config = options.oracleConfig;
this.connector = new PluginLedgerConnectorBesu(this.config.options);
this.network = this.config.network.id;
this.bungee = options.bungee;
}

public async updateEntry(
entry: UpdateEVMOracleEntry,
): Promise<{ transactionResponse: TransactionResponse; proof: any }> {
const fnTag = `${OracleEVM.CLASS_NAME}#updateEntry`;
this.logger.debug(
`${fnTag}: Updating entry with header: ${safeStableStringify(entry.header)}`,
);

const response = (await this.connector.invokeContract({
contractName: this.config.contractName,
keychainId: this.config.keychainId,
invocationType: EthContractInvocationType.Send,
methodName: "UpdateEVMOracleEntry",
params: [
entry.header.targetChainId,
entry.header.sequenceNumber.toString(),
entry.payload,
],
signingCredential: this.config.signingCredential,
gas: this.config.gas,
})) as {
success: boolean;
out: { transactionReceipt: { transactionHash?: string } };
};

if (!response.success) {
throw new Error(`${fnTag}: EVM transaction failed`);
}

const transactionResponse: TransactionResponse = {
transactionId: response.out.transactionReceipt.transactionHash ?? "",
transactionReceipt:
safeStableStringify(response.out.transactionReceipt) ?? "",
};

const networkDetails = {
signingCredential: this.config.signingCredential,
contractName: this.config.contractName,
connectorApiPath: this.network,
keychainId: this.config.keychainId,
contractAddress: "",
participant: "",
};

const snapshot = await this.bungee.generateSnapshot(
[],
"ORACLE_EVM",
networkDetails,
);
const proof = this.bungee.generateView(
snapshot,
"0",
Number.MAX_SAFE_INTEGER.toString(),
undefined,
);

return { transactionResponse, proof };
}

public async readEntry(
args: ReadEntryArgs,
): Promise<{ callOutput: any; proof: any }> {
const fnTag = `${OracleEVM.CLASS_NAME}#readEntry`;
this.logger.debug(
`${fnTag}: Reading entry with args: ${safeStableStringify(args)}`,
);

const response = (await this.connector.invokeContract({
contractName: args.contractId,
keychainId: this.config.keychainId,
invocationType: EthContractInvocationType.Call,
methodName: args.methodName,
params: args.params || [],
signingCredential: this.config.signingCredential,
gas: this.config.gas,
})) as { success: boolean; callOutput: any };

if (!response.success) {
throw new Error(`${fnTag}: EVM read transaction failed`);
}

const networkDetails = {
signingCredential: this.config.signingCredential,
contractName: this.config.contractName,
connectorApiPath: this.network,
keychainId: this.config.keychainId,
contractAddress: "",
participant: "",
};

const snapshot = await this.bungee.generateSnapshot(
[],
"ORACLE_EVM",
networkDetails,
);
const proof = this.bungee.generateView(
snapshot,
"0",
Number.MAX_SAFE_INTEGER.toString(),
undefined,
);

return { callOutput: response.callOutput, proof };
}
}
Loading

0 comments on commit 8a1c47e

Please sign in to comment.