Skip to content

Commit

Permalink
feat(sdk): add support for proxy_http_fetch
Browse files Browse the repository at this point in the history
  • Loading branch information
FranklinWaller committed Aug 29, 2024
1 parent 1a01479 commit d56c739
Show file tree
Hide file tree
Showing 19 changed files with 373 additions and 34 deletions.
Binary file modified bun.lockb
Binary file not shown.
19 changes: 15 additions & 4 deletions libs/as-sdk-integration-tests/assembly/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Console, httpFetch, HttpFetchOptions, Process } from '../../as-sdk/assembly/index';
import { Console, httpFetch, Process } from '../../as-sdk/assembly/index';
import { testProxyHttpFetch } from './proxy-http';
import { testTallyVmReveals, testTallyVmRevealsFiltered } from './tally';
import { testTallyVmHttp, testTallyVmMode } from './vm-tests';

Expand All @@ -16,8 +17,12 @@ if (args === 'testHttpRejection') {
testTallyVmReveals();
} else if (args === 'testTallyVmRevealsFiltered') {
testTallyVmRevealsFiltered();
} else if (args === 'testProxyHttpFetch') {
testProxyHttpFetch();
}

Process.exit_with_message(1, "No argument given");

export function testHttpRejection(): void {
const response = httpFetch('example.com/');
const rejected = response.rejected;
Expand All @@ -35,10 +40,16 @@ export function testHttpRejection(): void {
export function testHttpSuccess(): void {
const response = httpFetch('https://jsonplaceholder.typicode.com/todos/1');
const fulfilled = response.fulfilled;
const rejected = response.rejected;


if (fulfilled !== null) {
Process.exit_with_result(0, fulfilled.bytes);
} else {
Process.exit_with_message(31, 'My custom test failed');
Process.exit_with_result(0, fulfilled.bytes.value);
}

if (rejected !== null) {
Process.exit_with_result(1, rejected.bytes.value);
}

Process.exit_with_message(20, 'Something went wrong..');
}
22 changes: 22 additions & 0 deletions libs/as-sdk-integration-tests/assembly/proxy-http.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Process, proxyHttpFetch } from "../../as-sdk/assembly";


export function testProxyHttpFetch(): void {
const response = proxyHttpFetch('http://localhost:5384/proxy/planets/1');
const fulfilledResponse = response.fulfilled;
const rejectedResponse = response.rejected;

if (fulfilledResponse) {
const result = String.UTF8.decode(fulfilledResponse.bytes.value.buffer);

Process.exit_with_message(0, result);
}

if (rejectedResponse) {
const result = String.UTF8.decode(rejectedResponse.bytes.value.buffer);

Process.exit_with_message(1, result);
}

Process.exit_with_message(20, "Something went wrong..");
}
2 changes: 1 addition & 1 deletion libs/as-sdk-integration-tests/assembly/vm-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ export function testTallyVmHttp(): void {
}

if (rejected !== null) {
Process.exit_with_result(0, rejected.bytes);
Process.exit_with_result(0, rejected.bytes.value);
}
}
61 changes: 61 additions & 0 deletions libs/as-sdk-integration-tests/src/proxy-http.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { expect, describe, it, mock, beforeEach } from 'bun:test';
import { executeDrWasm } from '@seda/dev-tools';
import { readFile } from 'node:fs/promises';
import { Response } from 'node-fetch';

const mockHttpFetch = mock();

describe('ProxyHttp', () => {
beforeEach(() => {
mockHttpFetch.mockReset();
});

it.skip('should allow proxy_http_fetch which have a valid signature', async () => {
const wasmBinary = await readFile(
'dist/libs/as-sdk-integration-tests/debug.wasm'
);

const mockResponse = new Response('"Tatooine"', {
headers: {
'x-seda-signature': '93c67407c95f7d8252d8a28f5a637d57f2088376fcf34751d3ca04324e74d8185d11fe3fb23532f610158393b5678aeda82a56898fa95e0ca4d483e7aa472715',
'x-seda-publickey': '02100efce2a783cc7a3fbf9c5d15d4cc6e263337651312f21a35d30c16cb38f4c3'
},
});

mockHttpFetch.mockResolvedValue(mockResponse);

const result = await executeDrWasm(
wasmBinary,
Buffer.from('testProxyHttpFetch'),
mockHttpFetch
);

expect(result.exitCode).toBe(0);
expect(result.result).toEqual(new TextEncoder().encode('"Tatooine"'));
});

it.skip('should reject if the proxy_http_fetch has an invalid signature', async () => {
const wasmBinary = await readFile(
'dist/libs/as-sdk-integration-tests/debug.wasm'
);

const mockResponse = new Response('"Tatooine"', {
statusText: 'mock_ok',
headers: {
'x-seda-signature': '83c67407c95f7d8252d8a28f5a637d57f2088376fcf34751d3ca04324e74d8185d11fe3fb23532f610158393b5678aeda82a56898fa95e0ca4d483e7aa472715',
'x-seda-publickey': '02100efce2a783cc7a3fbf9c5d15d4cc6e263337651312f21a35d30c16cb38f4c3'
},
});

mockHttpFetch.mockResolvedValue(mockResponse);

const result = await executeDrWasm(
wasmBinary,
Buffer.from('testProxyHttpFetch'),
mockHttpFetch
);

expect(result.exitCode).toBe(1);
expect(result.result).toEqual(new TextEncoder().encode('Invalid signature'));
});
});
5 changes: 5 additions & 0 deletions libs/as-sdk/assembly/bindings/seda_v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ export declare function http_fetch(
action_length: u32
): u32;

export declare function proxy_http_fetch(
action_ptr: usize,
action_length: u32
): u32;

export declare function call_result_write(
result: usize,
result_length: u32
Expand Down
34 changes: 34 additions & 0 deletions libs/as-sdk/assembly/bytes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { JSON } from "json-as";
import { decodeHex, encodeHex } from "./hex";

@json
class InnerBytes {
type: string = "hex";
value!: string;
}

export class Bytes {
type: string = "hex";
value: Uint8Array;

constructor(value: Uint8Array) {
this.value = value;
}

__SERIALIZE(): string {
const inner = new InnerBytes();
inner.value = encodeHex(this.value);

return JSON.stringify(inner);
}

__INITIALIZE(): void {}

__DESERIALIZE(data: string, _key_start: i32, _key_end: i32, _outerLoopIndex: i32, _numberValueIndex: i32): bool {
const innerBytes = JSON.parse<InnerBytes>(data);
const buffer = decodeHex(innerBytes.value);
this.value = buffer;

return true;
}
}
19 changes: 19 additions & 0 deletions libs/as-sdk/assembly/hex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export function encodeHex(array: Uint8Array): string {
let hex = ''

for (let i = 0; i < array.length; i++) {
hex += array[i].toString(16)
}

return hex
}

export function decodeHex(data: string): Uint8Array {
let array = new Uint8Array(data.length >>> 1)

for (let i = 0; i < data.length >>> 1; ++i) {
array.fill(i32(parseInt('0x' + data.substr(i * 2, 2), 16)), i, i + 1)
}

return array;
}
41 changes: 26 additions & 15 deletions libs/as-sdk/assembly/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,36 @@ import { JSON } from 'json-as/assembly';
import { call_result_write, http_fetch } from './bindings/seda_v1';
import { jsonArrToUint8Array, uint8arrayToJsonArray } from './json-utils';
import { PromiseStatus, FromBuffer } from './promise';
import { Bytes } from './bytes';

@json
class InnerResponse {
export class InnerHttpResponse {
bytes!: u8[];
content_length!: i64;
status!: i64;
url!: string;
headers!: Map<string, string>;
}

@json
export class HttpResponseDisplay {
type: string = "HttpResponseDisplay";
bytes!: Bytes;
contentLength!: i64;
url!: string;
status!: i64;
headers!: Map<string, string>;
}

/**
* Response of an httpFetch call
*/
@json
export class HttpResponse implements FromBuffer<HttpResponse> {
/**
* Raw result of the HTTP fetch. (usually not used)
*/
public result: Uint8Array | null = null;
/**
* The response body result. This can be used to convert to JSON, text, etc.
*/
public bytes: Uint8Array = new Uint8Array(0);
public bytes: Bytes = new Bytes(new Uint8Array(0));
/**
* The length of the content
*/
Expand All @@ -42,12 +49,11 @@ export class HttpResponse implements FromBuffer<HttpResponse> {
*/
public headers: Map<string, string> = new Map();

fromBuffer(buffer: Uint8Array): HttpResponse {
static fromInner(value: InnerHttpResponse): HttpResponse {
const response = new HttpResponse();
const value = JSON.parse<InnerResponse>(String.UTF8.decode(buffer.buffer));


if (value.bytes) {
response.bytes = jsonArrToUint8Array(<u8[]>value.bytes);
response.bytes.value = jsonArrToUint8Array(<u8[]>value.bytes);
}

if (value.content_length) {
Expand Down Expand Up @@ -76,15 +82,20 @@ export class HttpResponse implements FromBuffer<HttpResponse> {
}
}

response.result = buffer;
return response;
}

fromBuffer(buffer: Uint8Array): HttpResponse {
const value = JSON.parse<InnerHttpResponse>(String.UTF8.decode(buffer.buffer));

return HttpResponse.fromInner(value);
}

toString(): string {
const response = new InnerResponse();
const response = new HttpResponseDisplay();

response.bytes = uint8arrayToJsonArray(this.bytes);
response.content_length = this.contentLength;
response.bytes = this.bytes;
response.contentLength = this.contentLength;
response.headers = this.headers;
response.status = this.status;
response.url = this.url;
Expand Down Expand Up @@ -131,7 +142,7 @@ export class HttpFetchOptions {
}

@json
class HttpFetch {
export class HttpFetch {
url: string;
options: HttpFetchOptions;

Expand Down
6 changes: 6 additions & 0 deletions libs/as-sdk/assembly/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@ export {
HttpResponse,
} from './http';

export {
proxyHttpFetch,
} from "./proxy-http";

// Export library so consumers don't need to reimport it themselves
export { JSON } from 'json-as/assembly';
export { PromiseStatus } from './promise';
export { Process, Tally };
export { RevealBody } from './tally';
export { Console } from './console';
export { Bytes } from './bytes';
export { decodeHex, encodeHex } from './hex';
9 changes: 2 additions & 7 deletions libs/as-sdk/assembly/process.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { VM_MODE_TALLY, VM_MODE_DR, VM_MODE_ENV_KEY } from './vm-modes';
import { wasi_process } from '@assemblyscript/wasi-shim/assembly/wasi_process';
import { execution_result } from './bindings/seda_v1';
import { decodeHex } from './hex';

export default class Process {
/**
Expand Down Expand Up @@ -48,14 +49,8 @@ export default class Process {
static getInputs(): Uint8Array {
// Data at index 0 is the dr/tally inputs encoded as hex
const data = Process.args().at(1);
const array = new Uint8Array(data.length >>> 1)

// Decodes the hex string into a buffer
for (let i = 0; i < data.length >>> 1; ++i) {
array.fill(i32(parseInt('0x' + data.substr(i * 2, 2), 16)), i, i + 1)
}

return array;
return decodeHex(data);
}

/**
Expand Down
26 changes: 26 additions & 0 deletions libs/as-sdk/assembly/proxy-http.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { JSON } from 'json-as/assembly';
import { HttpFetchOptions, HttpFetch, HttpResponse } from "./http";
import { call_result_write, proxy_http_fetch } from './bindings/seda_v1';
import { PromiseStatus } from './promise';

export function proxyHttpFetch(url: string, options: HttpFetchOptions = new HttpFetchOptions()): PromiseStatus<HttpResponse, HttpResponse> {
const action = new HttpFetch(url, options);
const actionStr = JSON.stringify(action);

const buffer = String.UTF8.encode(actionStr);
const utf8ptr = changetype<usize>(buffer);

const responseLength = proxy_http_fetch(utf8ptr, buffer.byteLength);
const responseBuffer = new ArrayBuffer(responseLength);
const responseBufferPtr = changetype<usize>(responseBuffer);

call_result_write(responseBufferPtr, responseLength);

const response = String.UTF8.decode(responseBuffer);

return PromiseStatus.fromStr(
response,
new HttpResponse(),
new HttpResponse(),
);
}
5 changes: 4 additions & 1 deletion libs/vm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"type": "module",
"version": "0.0.3",
"dependencies": {
"@wasmer/wasi": "^1.2.2"
"@wasmer/wasi": "^1.2.2",
"true-myth": "^7.3.0",
"@noble/secp256k1": "2.1.0",
"@noble/hashes": "1.4.0"
}
}
8 changes: 8 additions & 0 deletions libs/vm/src/services/keccak256.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { keccak_256 } from "@noble/hashes/sha3";

export function keccak256(input: Buffer): Buffer {
const hasher = keccak_256.create();
hasher.update(input);

return Buffer.from(hasher.digest());
}
Loading

0 comments on commit d56c739

Please sign in to comment.