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

feat(PE-5751): add blockheight and sortkey eval filters #12

Merged
merged 22 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f094f15
chore(tests): add tests for blockheight and sortkey, break tests into…
atticusofsparta Feb 29, 2024
d4bc32f
Merge branch 'develop' into PE-5751-add-blockheight-sortkey-filters
atticusofsparta Mar 1, 2024
619c193
fix(tests): update with new names on methods
atticusofsparta Mar 1, 2024
7d14edb
fix(types): remove unnecesssary empty defaults
atticusofsparta Mar 1, 2024
5c80242
fix(types): remove any type
atticusofsparta Mar 1, 2024
f8ff552
fix(types): make props nullable on certain read apis
Mar 4, 2024
ae890c8
fix(tests): update tests to use younger contract, add evalParams config
Mar 5, 2024
1fcb3e6
fix(tests): update gateways test
Mar 5, 2024
2b28675
fix(validity util): isBlockheight check more strict
Mar 5, 2024
855da2d
feat(contract): create new contract classes that impelement both warp…
Mar 6, 2024
72910ce
Merge remote-tracking branch 'origin/develop' into reccomendations
Mar 6, 2024
6eb7ef5
feat(ant): create ant contract class for interacting with ant contracts
Mar 6, 2024
a58e5b4
chore: make husky files executable
Mar 6, 2024
8119e5e
chore: remove ant contract implementations for now
Mar 6, 2024
6eee06f
Merge remote-tracking branch 'origin/develop' into reccomendations
Mar 6, 2024
a961f6f
chore: remove all ant state implementations
Mar 7, 2024
8164eef
chore: update types, interfaces and add docker-compose for tests
Mar 8, 2024
a3d9801
chore: update github action to run against local servcie, formatting
Mar 8, 2024
1c0897e
chore: pin warp to 1.4.29 to avoid build issues
Mar 8, 2024
d484544
chore: move type files up and cleanup exports
Mar 8, 2024
86b00a6
chore: update tests
Mar 8, 2024
56ebb08
fix(recs): modify the interfaces for contracts and implement with war…
dtfiedler Mar 8, 2024
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
47 changes: 35 additions & 12 deletions src/common/caches/arns-remote-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
ArNSStateResponse,
Gateway,
HTTPClient,
ReadInteractionFilters,
} from '../../types/index.js';
import { NotFound } from '../error.js';
import { AxiosHTTPService } from '../http.js';
Expand Down Expand Up @@ -58,32 +59,44 @@ export class ArNSRemoteCache implements ArIOContract {
}
}

async getGateway({ address }: { address: string }) {
async getGateway({
address,
blockHeight,
sortKey,
}: { address: string } & ReadInteractionFilters) {
this.logger.debug(`Fetching gateway ${address}`);
const gateway = await this.getGateways().then((gateways) => {
if (gateways[address] === undefined) {
throw new NotFound(`Gateway not found: ${address}`);
}
return gateways[address];
});
const gateway = await this.getGateways({ blockHeight, sortKey }).then(
(gateways) => {
if (gateways[address] === undefined) {
throw new NotFound(`Gateway not found: ${address}`);
}
return gateways[address];
},
);
return gateway;
}

async getGateways() {
async getGateways({ blockHeight, sortKey }: ReadInteractionFilters) {
this.logger.debug(`Fetching gateways`);
const { result } = await this.http.get<
ArNSStateResponse<'result', Record<string, Gateway>>
>({
endpoint: `/contract/${this.contractTxId.toString()}/read/gateways`,
params: { blockHeight, sortKey: sortKey?.toString() },
});
return result;
}

async getBalance({ address }: { address: string }) {
async getBalance({
address,
blockHeight,
sortKey,
}: { address: string } & ReadInteractionFilters) {
this.logger.debug(`Fetching balance for ${address}`);
const { result } = await this.http
.get<ArNSStateResponse<'result', number>>({
endpoint: `/contract/${this.contractTxId.toString()}/state/balances/${address}`,
params: { blockHeight, sortKey: sortKey?.toString() },
})
.catch((e) => {
if (e instanceof NotFound) {
Expand All @@ -94,32 +107,42 @@ export class ArNSRemoteCache implements ArIOContract {
return result;
}

async getBalances() {
async getBalances({ blockHeight, sortKey }: ReadInteractionFilters) {
this.logger.debug(`Fetching balances`);
const { result } = await this.http.get<
ArNSStateResponse<'result', Record<string, number>>
>({
endpoint: `/contract/${this.contractTxId.toString()}/state/balances`,
params: { blockHeight, sortKey: sortKey?.toString() },
});
return result;
}

async getArNSRecord({ domain }: { domain: string }): Promise<ArNSNameData> {
async getArNSRecord({
domain,
blockHeight,
sortKey,
}: { domain: string } & ReadInteractionFilters): Promise<ArNSNameData> {
this.logger.debug(`Fetching record for ${domain}`);
const { result } = await this.http.get<
ArNSStateResponse<'result', ArNSNameData>
>({
endpoint: `/contract/${this.contractTxId.toString()}/state/records/${domain}`,
params: { blockHeight, sortKey: sortKey?.toString() },
});
return result;
}

async getArNSRecords(): Promise<Record<string, ArNSNameData>> {
async getArNSRecords({
blockHeight,
sortKey,
}: ReadInteractionFilters): Promise<Record<string, ArNSNameData>> {
this.logger.debug(`Fetching all records`);
const { result } = await this.http.get<
ArNSStateResponse<'result', Record<string, ArNSNameData>>
>({
endpoint: `/contract/${this.contractTxId.toString()}/state/records`,
params: { blockHeight, sortKey: sortKey?.toString() },
});
return result;
}
Expand Down
3 changes: 3 additions & 0 deletions src/common/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,19 @@ export class AxiosHTTPService implements HTTPClient {
signal,
allowedStatuses = [200, 202],
headers,
params,
}: {
endpoint: string;
signal?: AbortSignal;
allowedStatuses?: number[];
headers?: Record<string, string>;
params?: Record<string, unknown>;
}): Promise<T> {
this.logger.debug(`Get request to endpoint: ${endpoint}`);
const { status, statusText, data } = await this.axios.get<T>(endpoint, {
headers,
signal,
params,
});

if (!allowedStatuses.includes(status)) {
Expand Down
4 changes: 4 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
*/

export const ARWEAVE_TX_REGEX = new RegExp('^[a-zA-Z0-9_-]{43}$');
// sortkey: padded blockheight to 12, JS timestamp, hash of transactionID + block hash. Timestamp only applicable to L2 and normally is all zeros.
export const SORT_KEY_REGEX = new RegExp(
'^[0-9]{12},[0-9]{13},[a-fA-F0-9]{64}$',
);
export const ARNS_TESTNET_REGISTRY_TX =
process.env.ARNS_REGISTRY_TX ?? 'bLAgYxAdX2Ry-nt6aH2ixgvJXbpsEYm28NgJgyqfs-U';

Expand Down
35 changes: 29 additions & 6 deletions src/types/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,37 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { SmartWeaveSortKey } from '../utils/index.js';
import { ArNSNameData, Gateway } from './contract-state.js';

export type EvaluationFilters = {
blockHeight?: number;
sortKey?: SmartWeaveSortKey; // should be tested against regex for validity
dtfiedler marked this conversation as resolved.
Show resolved Hide resolved
};

// TODO: extend type with other read filters (e.g max eval time)
export type ReadInteractionFilters = EvaluationFilters;

// TODO: extend with additional methods
export interface ArIOContract {
getGateway({ address }: { address: WalletAddress }): Promise<Gateway>;
getGateways(): Promise<Record<WalletAddress, Gateway>>;
getBalance({ address }: { address: WalletAddress }): Promise<number>;
getBalances(): Promise<Record<WalletAddress, number>>;
getArNSRecord({ domain }: { domain: string }): Promise<ArNSNameData>;
getArNSRecords(): Promise<Record<string, ArNSNameData>>;
getGateway(
props: { address: WalletAddress } & ReadInteractionFilters,
): Promise<Gateway>;
getGateways(
props: ReadInteractionFilters,
): Promise<Record<WalletAddress, Gateway>>;
getBalance(
props: { address: WalletAddress } & ReadInteractionFilters,
): Promise<number>;
getBalances(
props: ReadInteractionFilters,
): Promise<Record<WalletAddress, number>>;
getArNSRecord(
props: { domain: string } & ReadInteractionFilters,
): Promise<ArNSNameData>;
getArNSRecords(
prop: ReadInteractionFilters,
): Promise<Record<string, ArNSNameData>>;
}

/* eslint-disable @typescript-eslint/no-explicit-any */
Expand All @@ -45,11 +66,13 @@ export interface HTTPClient {
signal,
headers,
allowedStatuses,
params,
}: {
endpoint: string;
signal?: AbortSignal;
headers?: Record<string, string>;
allowedStatuses?: number[];
params?: Record<string, unknown>;
}): Promise<T>;
// TODO: add post method
// post<T>({
Expand Down
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
*/
export * from './arweave.js';
export * from './http-client.js';
export * from './smartweave/index.js';
13 changes: 13 additions & 0 deletions src/utils/smartweave/evaluation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { SmartWeaveSortKey } from './evaluation.js';

describe(`Smartweave eval utils`, () => {
it(`should throw on a bad sort key`, async () => {
const sortKey = '123,456,abc';
const error = await (async () => new SmartWeaveSortKey(sortKey))().catch(
(e) => e,
);

expect(error).toBeInstanceOf(Error);
expect(error.message).toContain(sortKey);
});
});
49 changes: 49 additions & 0 deletions src/utils/smartweave/evaluation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Copyright (C) 2022-2024 Permanent Data Solutions, Inc. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { SORT_KEY_REGEX } from '../../constants.js';

export class SmartWeaveSortKey {
dtfiedler marked this conversation as resolved.
Show resolved Hide resolved
private _sortKey: string;
constructor(sortKey: string) {
if (!SmartWeaveSortKey.validate(sortKey)) {
throw new Error(`Invalid sort key: ${sortKey}`);
}

this._sortKey = sortKey;
}

static validate(sortKey: string): boolean {
return SORT_KEY_REGEX.test(sortKey);
}

toString(): string {
return this._sortKey;
}

parts(): string[] {
return this._sortKey.split(',');
}
blockHeight(): number {
return parseInt(this.parts()[0]);
}
timestamp(): number {
return parseInt(this.parts()[1]);
}
hash(): string {
return this.parts()[2];
}
}
17 changes: 17 additions & 0 deletions src/utils/smartweave/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Copyright (C) 2022-2024 Permanent Data Solutions, Inc. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export * from './evaluation.js';
2 changes: 0 additions & 2 deletions tests/ar-io.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,5 @@ describe('ArIO Client', () => {
const arioClient = new ArIO();

expect(arioClient).toBeInstanceOf(ArIO);
expect(arioClient.testnet).toBeDefined();
expect(arioClient.devnet).toBeDefined();
});
});
57 changes: 0 additions & 57 deletions tests/arns-remote-cache.test.ts

This file was deleted.

Loading
Loading