-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from lambdaclass/test-validium
Improving validium script code and testing
- Loading branch information
Showing
7 changed files
with
324 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"tasks": { | ||
"validium": "deno run --allow-read --allow-net --allow-env validium.ts", | ||
"validium": "deno run --allow-read --allow-net --allow-env main.ts", | ||
"test": "deno test --allow-net --allow-read --allow-env tests.ts" | ||
} | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { | ||
ApiPromise, | ||
Keyring, | ||
WsProvider, | ||
} from "https://deno.land/x/[email protected]/api/mod.ts"; | ||
import { API_EXTENSIONS, API_RPC, API_TYPES } from "./api_options.ts"; | ||
import { KeyringPair } from "https://deno.land/x/[email protected]/keyring/types.ts"; | ||
|
||
export function initializeAvailApi(availRpc: string): Promise<ApiPromise> { | ||
return ApiPromise.create({ | ||
provider: new WsProvider(availRpc), | ||
rpc: API_RPC, | ||
types: API_TYPES, | ||
signedExtensions: API_EXTENSIONS, | ||
}); | ||
} | ||
|
||
export function createAccount(suri: string): KeyringPair { | ||
return new Keyring({ type: "sr25519" }).addFromUri(suri); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { submitDataAndVerify } from "./validium.ts"; | ||
|
||
await submitDataAndVerify(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,21 @@ | ||
import { ethers } from "npm:[email protected]"; | ||
import { | ||
assert, | ||
assertEquals, | ||
} from "https://deno.land/[email protected]/assert/mod.ts"; | ||
import { load } from "https://deno.land/[email protected]/dotenv/mod.ts"; | ||
import { assertEquals } from "https://deno.land/[email protected]/assert/mod.ts"; | ||
import ABI from "./abi/availbridge.json" with { type: "json" }; | ||
import { ProofData, submitDataAndVerify, verifyProof } from "./validium.ts"; | ||
|
||
Deno.test("verifyBlobLeaf function should return expected result", async () => { | ||
const env = await load(); | ||
const env = await load(); | ||
|
||
const BRIDGE_ADDRESS = env["DA_BRIDGE_ADDRESS"]; | ||
const ETH_PROVIDER_URL = env["ETH_PROVIDER_URL"]; | ||
const provider = new ethers.providers.JsonRpcProvider(ETH_PROVIDER_URL); | ||
const contractInstance = new ethers.Contract(BRIDGE_ADDRESS, ABI, provider); | ||
Deno.test("Load environment variables", () => { | ||
assert(env["AVAIL_RPC"], "AVAIL_RPC should be defined"); | ||
assert(env["SURI"], "SURI should be defined"); | ||
assert(env["DA_BRIDGE_ADDRESS"], "DA_BRIDGE_ADDRESS should be defined"); | ||
assert(env["BRIDGE_API_URL"], "BRIDGE_API_URL should be defined"); | ||
assert(env["ETH_PROVIDER_URL"], "ETH_PROVIDER_URL should be defined"); | ||
}); | ||
|
||
Deno.test("verifyBlobLeaf function should return expected result", async () => { | ||
const proof = { | ||
blobRoot: | ||
"0xe882a0dd840cc7b99d5f9ff05216be547c7b7d84a61d474353c4d9cb90cb2cdd", | ||
|
@@ -42,19 +47,21 @@ Deno.test("verifyBlobLeaf function should return expected result", async () => { | |
"0xfbab0eb809f03a99ee5dcca7f4131b6b8a8b56eccbee8f439cd33145d2d14e1d", | ||
}; | ||
|
||
const isVerified = await contractInstance.verifyBlobLeaf([ | ||
proof.dataRootProof, | ||
proof.leafProof, | ||
proof.rangeHash, | ||
proof.dataRootIndex, | ||
proof.blobRoot, | ||
proof.bridgeRoot, | ||
proof.leaf, | ||
proof.leafIndex, | ||
]); | ||
|
||
console.log(`Blob validation is: ${isVerified}`); | ||
|
||
const proofData: ProofData = { | ||
dataRootProof: proof.dataRootProof, | ||
leafProof: proof.leafProof, | ||
rangeHash: proof.rangeHash, | ||
dataRootIndex: proof.dataRootIndex, | ||
blobRoot: proof.blobRoot, | ||
bridgeRoot: proof.bridgeRoot, | ||
leaf: proof.leaf, | ||
leafIndex: proof.leafIndex, | ||
}; | ||
const isVerified = await verifyProof(proofData); | ||
const expectedValue = true; | ||
assertEquals(isVerified, expectedValue); | ||
}); | ||
|
||
Deno.test("submitDataAndVerify", async () => { | ||
await submitDataAndVerify(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,10 @@ | ||
import { | ||
ApiPromise, | ||
Keyring, | ||
WsProvider, | ||
} from "https://deno.land/x/[email protected]/api/mod.ts"; | ||
import { API_EXTENSIONS, API_RPC, API_TYPES } from "./api_options.ts"; | ||
import { ApiPromise } from "https://deno.land/x/[email protected]/api/mod.ts"; | ||
import { ISubmittableResult } from "https://deno.land/x/[email protected]/types/types/extrinsic.ts"; | ||
import { ethers } from "npm:[email protected]"; | ||
import { load } from "https://deno.land/[email protected]/dotenv/mod.ts"; | ||
import ABI from "./abi/availbridge.json" with { type: "json" }; | ||
import { KeyringPair } from "https://deno.land/x/[email protected]/keyring/types.ts"; | ||
import { createAccount, initializeAvailApi } from "./helpers.ts"; | ||
|
||
const env = await load(); | ||
|
||
|
@@ -18,24 +14,16 @@ const BRIDGE_ADDRESS = env["DA_BRIDGE_ADDRESS"]; // deployed bridge address | |
const DATA = "a"; // data to send | ||
const BRIDGE_API_URL = env["BRIDGE_API_URL"]; // bridge api url | ||
const ETH_PROVIDER_URL = env["ETH_PROVIDER_URL"]; // eth provider url | ||
const availApi = await ApiPromise.create({ | ||
provider: new WsProvider(AVAIL_RPC), | ||
rpc: API_RPC, | ||
types: API_TYPES, | ||
signedExtensions: API_EXTENSIONS, | ||
}); | ||
const account = new Keyring({ type: "sr25519" }).addFromUri(SURI); | ||
|
||
/** | ||
* ProofData represents a response from the api that holds proof for | ||
* the blob verification. | ||
*/ | ||
// deno-lint-ignore no-unused-vars | ||
class ProofData { | ||
export class ProofData { | ||
// proof of inclusion for the data root | ||
dataRootProof: Array<string> | undefined; | ||
// proof of inclusion of leaf within blob/bridge root | ||
leafProof: string | undefined; | ||
leafProof: Array<string> | undefined; | ||
// abi.encodePacked(startBlock, endBlock) of header range commitment on VectorX | ||
rangeHash: string | undefined; | ||
// index of the data root in the commitment tree | ||
|
@@ -62,7 +50,7 @@ interface SubmitDataResult extends ISubmittableResult { | |
* @param account that is sending transaction | ||
* @returns {Promise<SubmitDataResult>} | ||
*/ | ||
function submitData( | ||
export function submitData( | ||
availApi: ApiPromise, | ||
data: string, | ||
account: KeyringPair, | ||
|
@@ -88,17 +76,9 @@ function submitData( | |
}); | ||
} | ||
|
||
const result: SubmitDataResult = await submitData(availApi, DATA, account); | ||
if (result.isFinalized) { | ||
console.log( | ||
`DA transaction in finalized block: ${result.blockNumber}, transaction index: ${result.txIndex}`, | ||
); | ||
console.log(`result submitData = ${JSON.stringify(result)}`); | ||
} | ||
|
||
// wait until the chain head on the Ethereum network is updated with the block range | ||
// in which the Avail DA transaction is included. | ||
while (true) { | ||
export async function getLastCommittedBlock( | ||
result: SubmitDataResult, | ||
): Promise<number> { | ||
const getHeadRsp = await fetch(BRIDGE_API_URL + "/avl/head"); | ||
if (getHeadRsp.status != 200) { | ||
console.log("Something went wrong fetching the head."); | ||
|
@@ -107,7 +87,7 @@ while (true) { | |
console.log("Headers:", Array.from(getHeadRsp.headers.entries())); | ||
const responseBody = await getHeadRsp.text(); | ||
console.log("Response Body:", responseBody); | ||
break; | ||
Deno.exit(0); | ||
} | ||
const headRsp = await getHeadRsp.json(); | ||
const blockNumber: number = Number(result.blockNumber); | ||
|
@@ -117,43 +97,77 @@ while (true) { | |
blockNumber - lastCommittedBlock | ||
} blocks left`, | ||
); | ||
if (lastCommittedBlock >= blockNumber) { | ||
console.log("Fetching the blob proof."); | ||
const proofResponse = await fetch( | ||
BRIDGE_API_URL + "/eth/proof/" + result.status.asFinalized + "?index=" + | ||
result.txIndex, | ||
); | ||
console.log(proofResponse.url); | ||
if (proofResponse.status != 200) { | ||
console.log("Something went wrong fetching the proof."); | ||
console.log(proofResponse); | ||
break; | ||
} | ||
const proof: ProofData = await proofResponse.json(); | ||
console.log("Proof fetched:"); | ||
console.log(proof); | ||
// call the deployed contract verification function with the inclusion proof. | ||
const provider = new ethers.providers.JsonRpcProvider(ETH_PROVIDER_URL); | ||
const contractInstance = new ethers.Contract(BRIDGE_ADDRESS, ABI, provider); | ||
const isVerified = await contractInstance.verifyBlobLeaf([ | ||
proof.dataRootProof, | ||
proof.leafProof, | ||
proof.rangeHash, | ||
proof.dataRootIndex, | ||
proof.blobRoot, | ||
proof.bridgeRoot, | ||
proof.leaf, | ||
proof.leafIndex, | ||
]); | ||
console.log(`Blob validation is: ${isVerified}`); | ||
break; | ||
return lastCommittedBlock; | ||
} | ||
|
||
export async function getProof(result: SubmitDataResult): Promise<ProofData> { | ||
console.log("Fetching the blob proof."); | ||
const proofResponse = await fetch( | ||
BRIDGE_API_URL + "/eth/proof/" + result.status.asFinalized + "?index=" + | ||
result.txIndex, | ||
); | ||
console.log(proofResponse.url); | ||
if (proofResponse.status != 200) { | ||
console.log("Something went wrong fetching the proof."); | ||
console.log(proofResponse); | ||
Deno.exit(0); | ||
} | ||
const proof: ProofData = await proofResponse.json(); | ||
console.log("Proof fetched:"); | ||
console.log(proof); | ||
return proof; | ||
} | ||
|
||
console.log( | ||
"Waiting to bridge inclusion commitment. This can take a while...", | ||
export async function verifyProof(proof: ProofData): Promise<boolean> { | ||
// call the deployed contract verification function with the inclusion proof. | ||
const provider = new ethers.providers.JsonRpcProvider(ETH_PROVIDER_URL); | ||
const contractInstance = new ethers.Contract( | ||
BRIDGE_ADDRESS, | ||
ABI, | ||
provider, | ||
); | ||
// wait for 1 minute to check again | ||
await new Promise((f) => setTimeout(f, 60 * 1000)); | ||
const isVerified = await contractInstance.verifyBlobLeaf([ | ||
proof.dataRootProof, | ||
proof.leafProof, | ||
proof.rangeHash, | ||
proof.dataRootIndex, | ||
proof.blobRoot, | ||
proof.bridgeRoot, | ||
proof.leaf, | ||
proof.leafIndex, | ||
]); | ||
console.log(`Blob validation is: ${isVerified}`); | ||
return isVerified; | ||
} | ||
|
||
Deno.exit(0); | ||
export async function proofAndVerify(result: SubmitDataResult) { | ||
// wait until the chain head on the Ethereum network is updated with the block range | ||
// in which the Avail DA transaction is included. | ||
while (true) { | ||
const blockNumber: number = Number(result.blockNumber); | ||
const lastCommittedBlock: number = await getLastCommittedBlock(result); | ||
if (lastCommittedBlock >= blockNumber) { | ||
const proof = await getProof(result); | ||
await verifyProof(proof); | ||
} | ||
console.log( | ||
"Waiting to bridge inclusion commitment. This can take a while...", | ||
); | ||
// wait for 1 minute to check again | ||
await new Promise((f) => setTimeout(f, 60 * 1000)); | ||
} | ||
} | ||
|
||
export async function submitDataAndVerify() { | ||
const availApi = await initializeAvailApi(AVAIL_RPC); | ||
const account = createAccount(SURI); | ||
const result: SubmitDataResult = await submitData(availApi, DATA, account); | ||
if (result.isFinalized) { | ||
console.log( | ||
`DA transaction in finalized block: ${result.blockNumber}, transaction index: ${result.txIndex}`, | ||
); | ||
console.log(`result submitData = ${JSON.stringify(result)}`); | ||
} | ||
await proofAndVerify(result); | ||
Deno.exit(0); | ||
} |