Skip to content
Open
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
66 changes: 32 additions & 34 deletions libs/dev-tools/src/lib/services/dr/create-dr-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ const DEFAULT_PAYBACK_ADDRESS = new Uint8Array([]);
const DEFAULT_SEDA_PAYLOAD = new Uint8Array([]);

const defaultDrConfig: DrConfig = {
dr_reveal_size_limit_in_bytes: 24_000,
exec_input_limit_in_bytes: 2_048,
tally_input_limit_in_bytes: 512,
consensus_filter_limit_in_bytes: 512,
memo_limit_in_bytes: 512,
payback_address_limit_in_bytes: 128,
seda_payload_limit_in_bytes: 512,
commitTimeoutInBlocks: 50,
revealTimeoutInBlocks: 5,
backupDelayInBlocks: 5,
drRevealSizeLimitInBytes: 24_000,
execInputLimitInBytes: 2_048,
tallyInputLimitInBytes: 512,
consensusFilterLimitInBytes: 512,
memoLimitInBytes: 512,
paybackAddressLimitInBytes: 128,
sEDAPayloadLimitInBytes: 512,
};

export type PostDataRequestInput = {
Expand Down Expand Up @@ -114,10 +117,10 @@ export function createPostedDataRequest(
);

assert(
input.execInputs.length <= drConfig.exec_input_limit_in_bytes,
`execInputs must be less than ${drConfig.exec_input_limit_in_bytes + 1} bytes, received ${input.execInputs.length}`,
input.execInputs.length <= drConfig.execInputLimitInBytes,
`execInputs must be less than ${drConfig.execInputLimitInBytes + 1} bytes, received ${input.execInputs.length}`,
);
const exec_inputs = base64Encode(input.execInputs);
const exec_inputs = input.execInputs;

const tally_program_id = input.tallyProgramId ?? input.execProgramId;
assert(
Expand All @@ -126,50 +129,49 @@ export function createPostedDataRequest(
);

assert(
input.tallyInputs.length <= drConfig.tally_input_limit_in_bytes,
`tallyInputs must be less than ${drConfig.tally_input_limit_in_bytes + 1} bytes, received ${input.tallyInputs.length}`,
input.tallyInputs.length <= drConfig.tallyInputLimitInBytes,
`tallyInputs must be less than ${drConfig.tallyInputLimitInBytes + 1} bytes, received ${input.tallyInputs.length}`,
);
const tally_inputs = base64Encode(input.tallyInputs);
const tally_inputs = input.tallyInputs;

const replication_factor =
input.replicationFactor ?? DEFAULT_REPLICATION_FACTOR;

const consensFilterBytes = encodeConsensusFilter(input.consensusOptions);
const consensus_filter = encodeConsensusFilter(input.consensusOptions);
assert(
consensFilterBytes.length <= drConfig.consensus_filter_limit_in_bytes,
`consensus_filter must be less than ${drConfig.consensus_filter_limit_in_bytes + 1} bytes, received ${consensFilterBytes.length}`,
consensus_filter.length <= drConfig.consensusFilterLimitInBytes,
`consensus_filter must be less than ${drConfig.consensusFilterLimitInBytes + 1} bytes, received ${consensus_filter.length}`,
);
const consensus_filter = base64Encode(consensFilterBytes);

const exec_gas_limit = input.execGasLimit ?? DEFAULT_EXEC_GAS_LIMIT;
const tally_gas_limit = input.tallyGasLimit ?? DEFAULT_TALLY_GAS_LIMIT;
const exec_gas_limit = BigInt(input.execGasLimit ?? DEFAULT_EXEC_GAS_LIMIT);
const tally_gas_limit = BigInt(
input.tallyGasLimit ?? DEFAULT_TALLY_GAS_LIMIT,
);
const gas_price = (input.gasPrice ?? DEFAULT_GAS_PRICE).toString();

if (input.memo) {
assert(
input.memo.length <= drConfig.memo_limit_in_bytes,
`memo must be less than ${drConfig.memo_limit_in_bytes + 1} bytes, received ${input.memo.length}`,
input.memo.length <= drConfig.memoLimitInBytes,
`memo must be less than ${drConfig.memoLimitInBytes + 1} bytes, received ${input.memo.length}`,
);
}
const memo = base64Encode(input.memo ?? DEFAULT_MEMO);
const memo = input.memo ?? DEFAULT_MEMO;

if (input.paybackAddress) {
assert(
input.paybackAddress.length <= drConfig.payback_address_limit_in_bytes,
`paybackAddress must be less than ${drConfig.payback_address_limit_in_bytes + 1} bytes, received ${input.paybackAddress.length}`,
input.paybackAddress.length <= drConfig.paybackAddressLimitInBytes,
`paybackAddress must be less than ${drConfig.paybackAddressLimitInBytes + 1} bytes, received ${input.paybackAddress.length}`,
);
}
const payback_address = base64Encode(
input.paybackAddress ?? DEFAULT_PAYBACK_ADDRESS,
);
const payback_address = input.paybackAddress ?? DEFAULT_PAYBACK_ADDRESS;

if (input.sedaPayload) {
assert(
input.sedaPayload.length <= drConfig.seda_payload_limit_in_bytes,
`sedaPayload must be less than ${drConfig.seda_payload_limit_in_bytes + 1} bytes, received ${input.sedaPayload.length}`,
input.sedaPayload.length <= drConfig.sEDAPayloadLimitInBytes,
`sedaPayload must be less than ${drConfig.sEDAPayloadLimitInBytes + 1} bytes, received ${input.sedaPayload.length}`,
);
}
const seda_payload = base64Encode(input.sedaPayload ?? DEFAULT_SEDA_PAYLOAD);
const seda_payload = input.sedaPayload ?? DEFAULT_SEDA_PAYLOAD;

return {
payback_address,
Expand All @@ -190,10 +192,6 @@ export function createPostedDataRequest(
};
}

function base64Encode(value: Uint8Array): string {
return Buffer.from(value).toString("base64");
}

function isHexAddress(address: string): boolean {
return !!address.match(/^[0-9a-fA-F]{64}$/);
}
51 changes: 30 additions & 21 deletions libs/dev-tools/src/lib/services/dr/get-data-request-status.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import assert from "node:assert";
import { tryAsync } from "@seda-protocol/utils";
import type { ISigner } from "../signer";
import { createSigningClient } from "../signing-client";
import type { DataRequest } from "./data-request";
import { getDataResult } from "./get-data-result";
import { createCoreQueryClient } from "./query-client";

export type DataRequestStatus =
| "pending"
Expand All @@ -14,19 +16,20 @@ export async function getDataRequestStatus(
signer: ISigner,
dr: DataRequest,
): Promise<{ status: DataRequestStatus }> {
const sigingClientResult = await createSigningClient(signer);
if (sigingClientResult.isErr) {
throw sigingClientResult.error;
}

const { client: sigingClient } = sigingClientResult.value;
const contract = signer.getCoreContractAddress();

const contractDr = await sigingClient.queryContractSmart(contract, {
get_data_request: { dr_id: dr.id },
const coreQueryClient = await createCoreQueryClient({
rpc: signer.getEndpoint(),
});
const response = await tryAsync(
coreQueryClient.DataRequest({
drId: dr.id,
}),
);

if (response.isErr) {
throw response.error;
}

if (contractDr === null) {
if (!response.value.dataRequest) {
const drResult = await getDataResult({ rpc: signer.getEndpoint() }, dr);

if (drResult === null) {
Expand All @@ -36,24 +39,30 @@ export async function getDataRequestStatus(
return { status: "resolved" };
}

const replicationFactor = contractDr?.replication_factor;
assert(
Number.isInteger(replicationFactor),
"Invalid DR response, replication factor is not a number.",
);
const responseDR = response.value.dataRequest;
if (!responseDR.dataRequest) {
throw new Error("Invalid DR response, no data request.");
}
if (!responseDR.dataRequest.replicationFactor) {
throw new Error("Invalid DR response, no replication factor.");
}
assert(
typeof contractDr?.commits === "object",
typeof responseDR?.commits === "object",
"Invalid DR response, no commits map.",
);
assert(
typeof contractDr?.reveals === "object",
typeof responseDR?.reveals === "object",
"Invalid DR response, no reveals map.",
);

const commitments = Object.keys(contractDr.commits).length;
const reveals = Object.keys(contractDr.reveals).length;
const commitments = Object.keys(responseDR.commits).length;
const reveals = Object.keys(responseDR.reveals).length;

const status = getStatus(replicationFactor, commitments, reveals);
const status = getStatus(
responseDR.dataRequest.replicationFactor,
commitments,
reveals,
);

return { status };
}
Expand Down
46 changes: 24 additions & 22 deletions libs/dev-tools/src/lib/services/dr/post-data-request-bundle.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { calculateDrFunds } from "@dev-tools/services/dr/calculate-dr-funds";
import { tryParseSync } from "@seda-protocol/utils";
import * as v from "valibot";
import { sedachain } from "../../../../../proto-messages/gen";
import type { GasOptions } from "../gas-options";
import { getDrConfig } from "../get-dr-config";
import { signAndSendTx } from "../sign-and-send-tx";
Expand All @@ -22,38 +23,39 @@ export async function postDataRequestBundle(
dataRequestInputs: PostDataRequestInput[],
gasOptions?: GasOptions,
): Promise<{ tx: string; drs: DataRequest[] }> {
const drConfig = await getDrConfig(signer);
if (drConfig.isErr) {
throw drConfig.error;
}

const sigingClientResult = await createSigningClient(signer);
if (sigingClientResult.isErr) {
throw sigingClientResult.error;
}

const contract = signer.getCoreContractAddress();
const drConfig = await getDrConfig(sigingClientResult.value.client, signer);
if (drConfig.isErr) {
throw drConfig.error;
}

const { client: sigingClient, address } = sigingClientResult.value;

const messages = dataRequestInputs.map((dataRequestInput) => {
const post_data_request = createPostedDataRequest(
dataRequestInput,
drConfig.value,
);
const req = createPostedDataRequest(dataRequestInput, drConfig.value);
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: {
typeUrl: "/sedachain.core.v1.MsgPostDataRequest",
value: sedachain.core.v1.MsgPostDataRequest.fromPartial({
sender: address,
contract,
funds: [
{ amount: calculateDrFunds(post_data_request), denom: "aseda" },
],
msg: Buffer.from(
JSON.stringify({
post_data_request,
}),
),
},
funds: { amount: calculateDrFunds(req), denom: "aseda" },
version: req.posted_dr.version,
execProgramID: req.posted_dr.exec_program_id,
execInputs: req.posted_dr.exec_inputs,
execGasLimit: req.posted_dr.exec_gas_limit,
tallyProgramID: req.posted_dr.tally_program_id,
tallyInputs: req.posted_dr.tally_inputs,
tallyGasLimit: req.posted_dr.tally_gas_limit,
replicationFactor: req.posted_dr.replication_factor,
consensusFilter: req.posted_dr.consensus_filter,
gasPrice: req.posted_dr.gas_price,
memo: req.posted_dr.memo,
sEDAPayload: req.seda_payload,
paybackAddress: req.payback_address,
}),
};
});

Expand Down
56 changes: 28 additions & 28 deletions libs/dev-tools/src/lib/services/dr/post-data-request.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { sedachain } from "@seda-protocol/proto-messages";
import { tryParseSync } from "@seda-protocol/utils";
import * as v from "valibot";
import type { GasOptions } from "../gas-options";
Expand All @@ -14,50 +15,50 @@ import { DataRequest } from "./data-request";

export const PostDataRequestResponseSchema = v.pipe(
v.object({
dr_id: v.string(),
height: v.pipe(
v.number(),
v.transform((val) => BigInt(val)),
),
drID: v.string(),
height: v.bigint(),
}),
v.transform((val) => new DataRequest(val.dr_id, val.height)),
v.transform((val) => new DataRequest(val.drID, val.height)),
);

export async function postDataRequest(
signer: ISigner,
dataRequestInput: PostDataRequestInput,
gasOptions?: GasOptions,
): Promise<{ tx: string; dr: DataRequest }> {
const drConfig = await getDrConfig(signer);
if (drConfig.isErr) {
throw drConfig.error;
}

const sigingClientResult = await createSigningClient(signer);
if (sigingClientResult.isErr) {
throw sigingClientResult.error;
}

const contract = signer.getCoreContractAddress();
const drConfig = await getDrConfig(sigingClientResult.value.client, signer);
if (drConfig.isErr) {
throw drConfig.error;
}

const { client: sigingClient, address } = sigingClientResult.value;

const post_data_request = createPostedDataRequest(
dataRequestInput,
drConfig.value,
);
const req = createPostedDataRequest(dataRequestInput, drConfig.value);

const message = {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: {
funds: [{ amount: calculateDrFunds(post_data_request), denom: "aseda" }],
typeUrl: "/sedachain.core.v1.MsgPostDataRequest",
value: sedachain.core.v1.MsgPostDataRequest.fromPartial({
sender: address,
contract,
msg: Buffer.from(
JSON.stringify({
post_data_request,
}),
),
},
funds: { amount: calculateDrFunds(req), denom: "aseda" },
version: req.posted_dr.version,
execProgramID: req.posted_dr.exec_program_id,
execInputs: req.posted_dr.exec_inputs,
execGasLimit: req.posted_dr.exec_gas_limit,
tallyProgramID: req.posted_dr.tally_program_id,
tallyInputs: req.posted_dr.tally_inputs,
tallyGasLimit: req.posted_dr.tally_gas_limit,
replicationFactor: req.posted_dr.replication_factor,
consensusFilter: req.posted_dr.consensus_filter,
gasPrice: req.posted_dr.gas_price,
memo: req.posted_dr.memo,
sEDAPayload: req.seda_payload,
paybackAddress: req.payback_address,
}),
};

const response = await signAndSendTx(
Expand All @@ -79,8 +80,7 @@ export async function postDataRequest(
response.value.msgResponses[0],
);

const drResponse = JSON.parse(Buffer.from(messageResponse.data).toString());
const dr = tryParseSync(PostDataRequestResponseSchema, drResponse);
const dr = tryParseSync(PostDataRequestResponseSchema, messageResponse);
if (dr.isErr) {
throw new Error(`Failed to parse DR response: ${dr.error}`);
}
Expand Down
5 changes: 5 additions & 0 deletions libs/dev-tools/src/lib/services/dr/query-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ export async function createBatchingQueryClient(config: QueryConfig) {
const protoRpcClient = await createProtoQueryClient(config);
return new sedachain.batching.v1.QueryClientImpl(protoRpcClient);
}

export async function createCoreQueryClient(config: QueryConfig) {
const protoRpcClient = await createProtoQueryClient(config);
return new sedachain.core.v1.QueryClientImpl(protoRpcClient);
}
Loading