Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor publish #4

Merged
merged 3 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
## Addresses

### On Sepolia
XAPI: `0xC32BDE0002D71fD9a6C64eED65bdFD6e72F4Deba`
XAPI Consumer Example: `0xc6F64212F06F34e3d25bCb8D2C498387c46Fe7cb`
XAPI: `0xcedBd5b942c37870D172fEF4FC1C568C0aB8c038`
XAPI Consumer Example: `0xcd3627925b1396104126Fa400715A01C09703D66`

### On Near testnet
ORMP Aggregator: `ormpaggregator.guantong.testnet`
Expand Down
4 changes: 2 additions & 2 deletions aggregator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"clear": "near contract call-function as-transaction ormpaggregator.guantong.testnet _clear_state json-args {} prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' sign-as ormpaggregator.guantong.testnet network-config testnet sign-with-legacy-keychain send",
"test": "$npm_execpath run build && ava -- ./build/ormp_aggregator.wasm",
"publish-test": "near contract call-function as-transaction ormpaggregator.guantong.testnet publish json-args {} prepaid-gas '300.0 Tgas' attached-deposit '0 NEAR' sign-as ormpaggregator.guantong.testnet network-config testnet sign-with-legacy-keychain send",
"publish-external": "near contract call-function as-transaction ormpaggregator.guantong.testnet publish_external json-args '{\"request_id\": \"6277101735386680763835789423207666416102355444464034512855\"}' prepaid-gas '300.0 Tgas' attached-deposit '0 NEAR' sign-as guantong.testnet network-config testnet sign-with-legacy-keychain send",
"report": "near contract call-function as-transaction ormpaggregator.guantong.testnet report json-args '{\"request_id\":\"6277101735386680763835789423207666416102355444464034512855\",\"nonce\":\"1\",\"answers\":[{\"data_source_name\":\"test-source\",\"result\":\"test-result\"}],\"reporter_required\":{\"quorum\":1,\"threshold\":1},\"reward_address\":\"0x9f123456\"}' prepaid-gas '300.0 Tgas' attached-deposit '0 NEAR' sign-as guantong.testnet network-config testnet sign-with-legacy-keychain send",
"publish-external": "near contract call-function as-transaction ormpaggregator.guantong.testnet publish_external json-args '{\"request_id\": \"6277101735386680763835789423207666416102355444464034512856\",\"nonce\":\"3\",\"gas_limit\":\"1\",\"max_fee_per_gas\":\"1\",\"max_priority_fee_per_gas\":\"1\"}' prepaid-gas '300.0 Tgas' attached-deposit '0 NEAR' sign-as guantong.testnet network-config testnet sign-with-legacy-keychain send",
"report": "near contract call-function as-transaction ormpaggregator.guantong.testnet report json-args '{\"request_id\":\"6277101735386680763835789423207666416102355444464034512856\",\"answers\":[{\"data_source_name\":\"test-source\",\"result\":\"test-result\"}],\"reward_address\":\"0x9f123456\"}' prepaid-gas '300.0 Tgas' attached-deposit '0 NEAR' sign-as guantong.testnet network-config testnet sign-with-legacy-keychain send",
"ava": "ava"
},
"dependencies": {
Expand Down
86 changes: 44 additions & 42 deletions aggregator/src/abstract/aggregator.abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,10 @@ export enum RequestMethod {
export class PublishChainConfig {
chain_id: ChainId;
xapi_address: string;
gas_limit: string;
max_fee_per_gas: string;
max_priority_fee_per_gas: string;

constructor({ chain_id, xapi_address, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }: { chain_id: ChainId, xapi_address: string, gas_limit: string, max_fee_per_gas: string, max_priority_fee_per_gas: string }) {
constructor({ chain_id, xapi_address }: { chain_id: ChainId, xapi_address: string }) {
this.chain_id = chain_id;
this.xapi_address = xapi_address;
this.gas_limit = gas_limit;
this.max_fee_per_gas = max_fee_per_gas;
this.max_priority_fee_per_gas = max_priority_fee_per_gas;
}
}

Expand Down Expand Up @@ -94,11 +88,20 @@ class PublishData {
chain_config: PublishChainConfig;
signature: string;

constructor({ request_id, response, chain_config, signature }: { request_id: RequestId, response: Response, chain_config: PublishChainConfig, signature: string }) {
nonce: string;
gas_limit: string;
max_fee_per_gas: string;
max_priority_fee_per_gas: string;

constructor({ request_id, response, chain_config, signature, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }: { request_id: RequestId, response: Response, chain_config: PublishChainConfig, signature: string, nonce: string, gas_limit: string, max_fee_per_gas: string, max_priority_fee_per_gas: string }) {
this.request_id = request_id;
this.response = response;
this.chain_config = chain_config;
this.signature = signature;
this.nonce = nonce;
this.gas_limit = gas_limit;
this.max_fee_per_gas = max_fee_per_gas;
this.max_priority_fee_per_gas = max_priority_fee_per_gas;
}
}

Expand All @@ -125,6 +128,7 @@ export class ReporterRequired {

export class Response {
request_id: RequestId;
chain_id: ChainId;
valid_reporters: AccountId[];
// EVM address to distribute rewards
reporter_reward_addresses: string[];
Expand All @@ -136,8 +140,6 @@ export class Response {

// 👇 These values should be aggregated from reporter's answer
result: string;
nonce: string;
chain_id: ChainId;

constructor(request_id: RequestId) {
this.request_id = request_id;
Expand All @@ -150,19 +152,12 @@ export class Report {
request_id: RequestId;
reporter: AccountId;
timestamp: Timestamp;
chain_id: ChainId;
// Evm address to withdraw rewards on target chain
reward_address: string;
// Because cross-chain transactions may fail, we need to rely on the reporter to report nonce instead of maintaining the self-increment.
nonce: string;
reporter_required: ReporterRequired;
answers: Answer[];
constructor({ request_id, chain_id, nonce, answers, reporter_required, reward_address }: { request_id: RequestId, chain_id: ChainId, nonce: string, answers: Answer[], reporter_required: ReporterRequired, reward_address: string }) {
constructor({ request_id, answers, reward_address }: { request_id: RequestId, answers: Answer[], reward_address: string }) {
this.request_id = request_id;
this.chain_id = chain_id;
this.nonce = nonce;
this.answers = answers;
this.reporter_required = reporter_required;
this.reward_address = reward_address;
this.timestamp = near.blockTimestamp().toString();
this.reporter = near.signerAccountId();
Expand Down Expand Up @@ -277,9 +272,6 @@ export abstract class Aggregator extends ContractBase {
_set_publish_chain_config(publish_chain_config: PublishChainConfig): void {
this._assert_operator();
assert(publish_chain_config.chain_id != null, "chain_id can't be null.");
assert(publish_chain_config.gas_limit != null, "gas_limit can't be null.");
assert(publish_chain_config.max_fee_per_gas != null, "max_fee_per_gas can't be null.");
assert(publish_chain_config.max_priority_fee_per_gas != null, "max_priority_fee_per_gas can't be null.");
assert(publish_chain_config.xapi_address != null, "xapi_address can't be null.");
this.publish_chain_config_lookup.set(publish_chain_config.chain_id.toString(), publish_chain_config);
}
Expand Down Expand Up @@ -324,21 +316,15 @@ export abstract class Aggregator extends ContractBase {
return this.response_lookup.get(request_id);
}

abstract report({ request_id, nonce, answers, reporter_required, reward_address }: { request_id: RequestId, nonce: string, answers: Answer[], reporter_required: ReporterRequired, reward_address: string }): NearPromise;
_report({ request_id, nonce, answers, reporter_required, reward_address }: { request_id: RequestId, nonce: string, answers: Answer[], reporter_required: ReporterRequired, reward_address: string }): NearPromise {
abstract report({ request_id, answers, reward_address }: { request_id: RequestId, answers: Answer[], reward_address: string }): NearPromise;
_report({ request_id, answers, reward_address }: { request_id: RequestId, answers: Answer[], reward_address: string }): NearPromise {
assert(request_id != null, "request_id is null");
assert(nonce != null, "nonce is null");
assert(answers != null && answers.length > 0, "answers is empty");
assert(reward_address != null, "reward_address is null");

const _chain_id = (BigInt(request_id) >> BigInt(192)).toString();

const __report = new Report({
request_id,
chain_id: _chain_id,
nonce,
answers,
reporter_required,
reward_address
});

Expand Down Expand Up @@ -368,6 +354,8 @@ export abstract class Aggregator extends ContractBase {
if (_response.status == RequestStatus[RequestStatus.FETCHING] && BigInt(_response.started_at) + BigInt(this.timeout) < near.blockTimestamp()) {
_response.status = RequestStatus[RequestStatus.TIMEOUT];
new TimeoutEvent(_response).emit();
this.response_lookup.set(request_id, _response);
return;
}

// Only fetching request can accept reports.
Expand Down Expand Up @@ -462,13 +450,26 @@ export abstract class Aggregator extends ContractBase {
}
}

abstract publish_external({ request_id }: { request_id: RequestId }): NearPromise;
_publish({ request_id, promise_index }: { request_id: RequestId, promise_index: number }): NearPromise {
abstract publish_external({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }: { request_id: RequestId, nonce: string, gas_limit: string, max_fee_per_gas: string, max_priority_fee_per_gas: string }): NearPromise;
_publish({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }: { request_id: RequestId, nonce: string, gas_limit: string, max_fee_per_gas: string, max_priority_fee_per_gas: string }): NearPromise {
assert(nonce != null, "nonce can't be null.");
assert(gas_limit != null, "gas_limit can't be null.");
assert(max_fee_per_gas != null, "max_fee_per_gas can't be null.");
assert(max_priority_fee_per_gas != null, "max_priority_fee_per_gas can't be null.");

const _response = this.response_lookup.get(request_id);
assert(_response != null, `Response for ${request_id} does not exist`);
assert(_response.status == RequestStatus[RequestStatus.AGGREGATED] || _response.status == RequestStatus[RequestStatus.PUBLISHED], `Response status is ${_response.status}, can't be published`);

assert(_response.status == RequestStatus[RequestStatus.AGGREGATED], `Response status is ${_response.status}, can't be published`);
// Update timeout status if necessary.
if (BigInt(_response.started_at) + BigInt(this.timeout) < near.blockTimestamp()) {
_response.status = RequestStatus[RequestStatus.TIMEOUT];
new TimeoutEvent(_response).emit();
this.response_lookup.set(request_id, _response);
return;
}

_response.chain_id = (BigInt(request_id) >> BigInt(192)).toString();
const _chain_config = this.publish_chain_config_lookup.get(_response.chain_id.toString());
assert(_chain_config != null, `Chain config for ${_response.chain_id} does not exist`);

Expand All @@ -491,10 +492,10 @@ export abstract class Aggregator extends ContractBase {

const payload = ethereumTransaction({
chainId: BigInt(_response.chain_id),
nonce: BigInt(_response.nonce),
maxPriorityFeePerGas: BigInt(_chain_config.max_priority_fee_per_gas),
maxFeePerGas: BigInt(_chain_config.max_fee_per_gas),
gasLimit: BigInt(_chain_config.gas_limit),
nonce: BigInt(nonce),
maxPriorityFeePerGas: BigInt(max_priority_fee_per_gas),
maxFeePerGas: BigInt(max_fee_per_gas),
gasLimit: BigInt(gas_limit),
to: _chain_config.xapi_address,
value: BigInt(0),
data: function_call_data_bytes,
Expand All @@ -521,7 +522,7 @@ export abstract class Aggregator extends ContractBase {
NearPromise.new(near.currentAccountId())
.functionCall(
"publish_callback",
JSON.stringify({ request_id: request_id, promise_index: promise_index }),
JSON.stringify({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }),
BigInt(0),
// Beware of the 300T cap with mpc gas
BigInt(ONE_TERA_GAS * BigInt(25))
Expand All @@ -530,16 +531,17 @@ export abstract class Aggregator extends ContractBase {
return promise.asReturn();
}

abstract publish_callback({ request_id, promise_index }: { request_id: RequestId, promise_index: number }): void
_publish_callback({ request_id, promise_index }: { request_id: RequestId, promise_index: number }): void {
const _result = this._promise_result({ promise_index: promise_index });
near.log(`publish_callback ${request_id}, ${_result.success}, ${_result.result}, promise_index: ${promise_index}`);
abstract publish_callback({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }: { request_id: RequestId, nonce: string, gas_limit: string, max_fee_per_gas: string, max_priority_fee_per_gas: string }): void
_publish_callback({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }: { request_id: RequestId, nonce: string, gas_limit: string, max_fee_per_gas: string, max_priority_fee_per_gas: string }): void {
const _result = this._promise_result({ promise_index: 0 });
near.log(`publish_callback ${request_id}, ${_result.success}, ${_result.result}`);
const _response = this.response_lookup.get(request_id);
if (_result.success) {
_response.status = RequestStatus[RequestStatus.PUBLISHED];
const _chain_config = this.publish_chain_config_lookup.get(_response.chain_id.toString());
new PublishEvent(new PublishData({
request_id, response: _response, chain_config: _chain_config, signature: _result.result
request_id, response: _response, chain_config: _chain_config, signature: _result.result,
nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas
})).emit();
this.response_lookup.set(request_id, _response);
}
Expand Down
27 changes: 14 additions & 13 deletions aggregator/src/ormp.aggregator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class OrmpAggregator extends Aggregator {
}

_aggregate({ request_id, top_staked }: { request_id: RequestId, top_staked: Staked[] }): boolean {
// filter invalid reporter
// 1. Filter invalid reports via top staked reporters
const _reporters = this.report_lookup.get(request_id).map(r => r.reporter);
const _valid_reporters = _reporters.filter(reporter =>
top_staked.some(staked => staked.account_id === reporter)
Expand All @@ -42,35 +42,36 @@ class OrmpAggregator extends Aggregator {

const _each_reporter_report = [];
const _each_reporter_result = new Map<string, string>();

// 2. Aggregate from multi datasources of one report.
_valid_reports.forEach(report => {
const _result = this._aggregate_answer(report.answers.map(r => r.result)).result;
_each_reporter_result.set(report.reporter, _result);
_each_reporter_report.push(`${_result} | ${report.nonce} | ${report.chain_id}`);
_each_reporter_report.push(_result);
});

// 3. Aggregate from multi reports
const most_common_report = this._aggregate_answer(_each_reporter_report);
near.log("most_common_report: ", most_common_report);

const [result, nonce, chain_id] = most_common_report.result.split(' | ');
const result = most_common_report.result;
assert(_valid_reporters.length >= this.reporter_required.quorum, `Quorum: required ${this.reporter_required.quorum}, but got ${_valid_reporters.length}`);
assert(most_common_report.count >= this.reporter_required.threshold, `Threshold: required ${this.reporter_required.threshold}, but got ${most_common_report.count}`)

const _response = this.response_lookup.get(request_id);

_response.valid_reporters = [];
_response.reporter_reward_addresses = []
// filter most common reporter
// 4. Filter most common reporter, only most common report will be rewarded
for (const _report of _valid_reports) {
const key = `${_each_reporter_result.get(_report.reporter)} | ${_report.nonce} | ${_report.chain_id}`;
const key = _each_reporter_result.get(_report.reporter);
if (key === most_common_report.result) {
_response.reporter_reward_addresses.push(_report.reward_address);
_response.valid_reporters.push(_report.reporter);
}
}

_response.result = result;
_response.nonce = nonce;
_response.chain_id = chain_id;
this.response_lookup.set(request_id, _response);
return true;
}
Expand Down Expand Up @@ -103,13 +104,13 @@ class OrmpAggregator extends Aggregator {
/// Calls

@call({})
publish_external({ request_id }: { request_id: RequestId; }): NearPromise {
return super._publish({ request_id, promise_index: 0 });
publish_external({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }: { request_id: RequestId; nonce: string; gas_limit: string; max_fee_per_gas: string; max_priority_fee_per_gas: string; }): NearPromise {
return super._publish({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas });
}

@call({ privateFunction: true })
publish_callback({ request_id, promise_index }: { request_id: RequestId; promise_index: number; }): void {
super._publish_callback({ request_id, promise_index });
publish_callback({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }: { request_id: RequestId; nonce: string; gas_limit: string; max_fee_per_gas: string; max_priority_fee_per_gas: string; }): void {
super._publish_callback({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas });
}

@call({ privateFunction: true })
Expand All @@ -118,8 +119,8 @@ class OrmpAggregator extends Aggregator {
}

@call({ payableFunction: true })
report({ request_id, nonce, answers, reporter_required, reward_address }: { request_id: RequestId; nonce: string; answers: Answer[]; reporter_required: ReporterRequired; reward_address: string }): NearPromise {
return super._report({ request_id, nonce, answers, reporter_required, reward_address });
report({ request_id, answers, reward_address }: { request_id: RequestId; answers: Answer[]; reward_address: string; }): NearPromise {
return super._report({ request_id, answers, reward_address });
}

@call({ payableFunction: true })
Expand Down
3 changes: 1 addition & 2 deletions xapi-consumer/contracts/ConsumerExample.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ contract ConsumerExample is IXAPIConsumer{

function makeRequest(string memory aggregator) external payable {
string memory requestData = "{'hello':'world'}";
ReporterRequired memory reporterRequired = ReporterRequired(1, 1);
uint256 requestId = xapi.makeRequest{value: msg.value}(requestData, this.fulfillCallback.selector, aggregator, reporterRequired);
uint256 requestId = xapi.makeRequest{value: msg.value}(requestData, this.fulfillCallback.selector, aggregator);
emit RequestMade(requestId, requestData);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"ConsumerExampleModule#ConsumerExample": "0xc6F64212F06F34e3d25bCb8D2C498387c46Fe7cb"
"ConsumerExampleModule#ConsumerExample": "0xcd3627925b1396104126Fa400715A01C09703D66"
}
2 changes: 1 addition & 1 deletion xapi-consumer/ignition/modules/ConsumerExample.deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules");
module.exports = buildModule("ConsumerExampleModule", (m) => {
const deployer = m.getAccount(0);

const consumer = m.contract("ConsumerExample", ["0xC32BDE0002D71fD9a6C64eED65bdFD6e72F4Deba"], {
const consumer = m.contract("ConsumerExample", ["0xcedBd5b942c37870D172fEF4FC1C568C0aB8c038"], {
from: deployer
});

Expand Down
2 changes: 1 addition & 1 deletion xapi-consumer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"build": "npx hardhat compile",
"deploy": "npx hardhat ignition deploy ./ignition/modules/ConsumerExample.deploy.js --network sepolia",
"console": "npx hardhat console --network sepolia",
"verify": "npx hardhat verify --network sepolia 0xc6F64212F06F34e3d25bCb8D2C498387c46Fe7cb '0xC32BDE0002D71fD9a6C64eED65bdFD6e72F4Deba'"
"verify": "npx hardhat verify --network sepolia 0xcd3627925b1396104126Fa400715A01C09703D66 '0xcedBd5b942c37870D172fEF4FC1C568C0aB8c038'"
},
"dependencies": {
"xapi": "file:../xapi"
Expand Down
2 changes: 1 addition & 1 deletion xapi/contracts/XAPI.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ contract XAPI is IXAPI, Ownable2Step {
for (uint256 i = 0; i < response.reporters.length; i++) {
rewards[response.reporters[i]] += aggregatorConfig.perReporterFee;
}
rewards[aggregatorConfig.fulfillAddress] += aggregatorConfig.publishFee;
rewards[aggregatorConfig.rewardAddress] += aggregatorConfig.publishFee;

(bool success,) =
request.callbackContract.call(abi.encodeWithSelector(request.callbackFunction, requestId, response));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"XAPIModule#XAPI": "0xcedBd5b942c37870D172fEF4FC1C568C0aB8c038"
}
Loading