Skip to content

Commit

Permalink
feat: readProofParamsFile & adapt get proof params interface
Browse files Browse the repository at this point in the history
  • Loading branch information
nom4dv3 committed Feb 25, 2024
1 parent 8048808 commit 70ccb0e
Show file tree
Hide file tree
Showing 6 changed files with 445 additions and 62 deletions.
27 changes: 6 additions & 21 deletions packages/cle-cli/src/commands/prove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import to from 'await-to-js'
import prompts from 'prompts'
import * as cleApi from '@ora-io/cle-api'
import type { Input } from 'zkwasm-toolchain'
import { convertToMd5, generateDspHubParams, loadJsonRpcProviderUrl, loadYamlFromPath, logLoadingAnimation, taskPrettyPrint, validateProvider } from '../utils'
import { convertToMd5, generateDspHubParams, loadJsonRpcProviderUrl, loadYamlFromPath, logLoadingAnimation, taskPrettyPrint, validateProvider, writeProofParamsFile } from '../utils'
import { logger } from '../logger'
import type { UserConfig } from '../config'
import { parseTemplateTag } from '../tag'
Expand Down Expand Up @@ -228,31 +228,16 @@ async function proveMode(userPrivateKey: string, md5: string, privateInputStr: s
return
}
loading.stopAndClear()
if (
result.instances === null
&& result.batch_instances === null
&& result.proof === null
&& result.aux === null
) {
if (!result.proofParams) {
logger.warn('[-] PROOF NOT FOUND')
return
}

// write proof to file as txt
// proof file name
const outputProofFile = parseTemplateTag(outputProofFilePath, {
...TAGS,
taskId: result.taskId || '',
taskId: taskId || '',
})

logger.info(`[+] Proof written to ${outputProofFile}.\n`)

fs.writeFileSync(
outputProofFile,
`Instances:\n${result.instances // TODO: checkout how to return/write 2-dim instances
}\n\nBatched Instances:\n${result.batch_instances
}\n\nProof transcripts:\n${result.proof
}\n\nAux data:\n${result.aux
}${result.extra ? '\n\nExtra data:\n' : ''}${result.extra
}\n`,
)
// write proof to file as txt
writeProofParamsFile(outputProofFile, result.proofParams)
}
47 changes: 8 additions & 39 deletions packages/cle-cli/src/commands/verify.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import fs from 'node:fs'
import type { ProofParams } from '@ora-io/cle-api'
import { Error, constants, verify as verifyApi } from '@ora-io/cle-api'
import { ethers } from 'ethers'
import { ZkWasmUtil } from '@ora-io/zkwasm-service-helper'
import { logger } from '../logger'
import { loadJsonRpcProviderUrl, loadYamlFromPath, logDivider } from '../utils'
import { loadJsonRpcProviderUrl, loadYamlFromPath, logDivider, readProofParamsFile } from '../utils'
import type { UserConfig } from '../config'
import { parseTemplateTag } from '../tag'
import { TAGS } from '../constants'
Expand All @@ -31,7 +28,13 @@ export async function verify(options: VerifyOptions) {
// Get deployed verification contract address.
// TODO: I reused this func to save code, but the naming is a bit misleading, fix it later.
// const verifierAddress = loadJsonRpcProviderUrl(cleYaml, AggregatorVerifierAddress, false)
const proofParams = getVerifyProofParamsByProofFile(taskId, outputProofFilePath)

const proofFilPath = parseTemplateTag(outputProofFilePath, {
...TAGS,
taskId: taskId || '',
})

const proofParams = readProofParamsFile(proofFilPath)
const network = cleYaml.decidePublishNetwork()
if (!network) {
logger.error('[-] ERROR: Failed to get network')
Expand All @@ -57,37 +60,3 @@ export async function verify(options: VerifyOptions) {

return verifyResult
}

function getVerifyProofParamsByProofFile(taskId: string, outputProofFilePath: string): ProofParams {
const outputProofFile = parseTemplateTag(outputProofFilePath, {
...TAGS,
taskId: taskId || '',
})

if (!fs.existsSync(outputProofFile)) {
logger.error('[-] ERROR: Failed to get proof file')
process.exit(1)
}

const content = fs.readFileSync(outputProofFile, 'utf-8')
const regex = /Instances:\n(.*?)\n\nBatched Instances:\n(.*?)\n\nProof transcripts:\n(.*?)\n\nAux data:\n(.*?)\n\n/s
const matches = content.match(regex)

if (matches) {
const instancesValue = matches[1].trim().split('\n')
const batchedInstancesValue = matches[2].trim().split('\n')
const proofTranscriptsValue = matches[3].trim().split('\n')
const auxDataValue = matches[4].trim().split('\n')

return {
instances: [ZkWasmUtil.hexStringsToBytes(instancesValue, 32)], // TODO: checkout how to read/return 2-dim instances
batch_instances: ZkWasmUtil.hexStringsToBytes(batchedInstancesValue, 32),
aggregate_proof: ZkWasmUtil.hexStringsToBytes(proofTranscriptsValue, 32),
aux: ZkWasmUtil.hexStringsToBytes(auxDataValue, 32),
}
}
else {
logger.error('[-] ERROR: Failed to get proof file')
process.exit(1)
}
}
73 changes: 72 additions & 1 deletion packages/cle-cli/src/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import fs from 'fs'
import { Md5 } from 'ts-md5'

import { ZkWasmUtil } from '@ora-io/zkwasm-service-helper'
import type { ProofParams } from '@ora-io/cle-api/index'
import { logger } from '../logger'
/**
* Convert hex string to Uint8Array
* @param hexString
Expand Down Expand Up @@ -28,3 +31,71 @@ export function convertToMd5(value: Uint8Array): string {
return ''
return hash.toString()
}

export function writeProofParamsFile(outputFilePath: string, proofParams: ProofParams) {
let instancesStr = ''
proofParams.instances.forEach((ins: Uint8Array, idx) => { instancesStr += `[${idx}]\n${toHexStringBytes32Reverse(ins)}` }) // 2 dim
const batchInstancesStr = toHexStringBytes32Reverse(proofParams.batch_instances)
const proofStr = toHexStringBytes32Reverse(proofParams.aggregate_proof)
const auxStr = toHexStringBytes32Reverse(proofParams.aux)
const extraStr = proofParams.extra ? toHexStringBytes32Reverse(proofParams.extra) : null

fs.writeFileSync(
outputFilePath,
`Instances:\n${instancesStr // TODO: checkout how to return/write 2-dim instances
}\n\nBatched Instances:\n${batchInstancesStr
}\n\nProof transcripts:\n${proofStr
}\n\nAux data:\n${auxStr
// }${extraStr ? '\n\nExtra data:\n' : ''}${extraStr
}\n\nExtra data:\n${extraStr
}\n`,
)
logger.info(`[+] Proof written to ${outputFilePath}.\n`)
}

export function readProofParamsFile(outputFilePath: string): ProofParams {
if (!fs.existsSync(outputFilePath)) {
logger.error(`[-] ERROR: Proof file ${outputFilePath} doesn't exist.`)
process.exit(1)
}

const content = fs.readFileSync(outputFilePath, 'utf-8')
const regex = /Instances:\n(.*?)\n\nBatched Instances:\n(.*?)\n\nProof transcripts:\n(.*?)\n\nAux data:\n(.*?)\n\n/s
const matches = content.match(regex)

if (matches) {
const instancesValue = matches[1].trim().split(/\[\d+\]/)// .filter(item => item !== '');
if (instancesValue[0] === '')
instancesValue.shift()
const batchedInstancesValue = matches[2].trim().split('\n')
const proofTranscriptsValue = matches[3].trim().split('\n')
const auxDataValue = matches[4].trim().split('\n')
return {
instances: instancesValue.map((iv) => {
const ivt = iv.trim()
return (ivt.length > 0) ? ZkWasmUtil.hexStringsToBytes(ivt.split('\n'), 32) : new Uint8Array(0)
}),
batch_instances: ZkWasmUtil.hexStringsToBytes(batchedInstancesValue, 32),
aggregate_proof: ZkWasmUtil.hexStringsToBytes(proofTranscriptsValue, 32),
aux: ZkWasmUtil.hexStringsToBytes(auxDataValue, 32),
}
}
else {
logger.error(`[-] ERROR: Can't parse proof file ${outputFilePath}.`)
process.exit(1)
}
}

/**
* Reverse Uint8Array to string
* @param arr
* @returns
*/
export function toHexStringBytes32Reverse(arr: Uint8Array) {
let result = ''
for (let i = 0; i < arr.length / 32; i++) {
result
+= `0x${toHexString(arr.slice(i * 32, (i + 1) * 32).reverse())}\n`
}
return result
}
177 changes: 177 additions & 0 deletions test/fixtures/utils/proof_0000.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
Instances:
[0]
0x00000000000000000000000000000000000000000000000000000000004de8c9
0x0000000000000000000000000000000000000000000000000000000000000020
0x00000000000000000000000000000000000000000000000000000000266240c0
[1]
0x0000000000000000000000000000000000000000000000000000000aaaaaaaaa
[2]
0x0000000000000000000000000000000000000000000000000000000aaaaaaaaa
[3]
[4]
[5]


Batched Instances:
0x0000000000fb3e2c3bcdc79c8c382730561bccfe651693d568d0bb5661ce920e
0x00000000003202cf17301674a5c570080a17800000000000000000220eeffe9a
0x0000000000000000000000000002d5d6dd940a40cf08303e009b6560de5053e4
0x000000000055261ccaa9b0c5171e33908dace85db2277c70ca5ba636120731e2
0x0000000000d736671de57a82b2c697f511ef6000000000000000002c1f35855e
0x0000000000000000000000000001c568e9c0f1e914566ce641405f0bbfd61eda


Proof transcripts:
0x187b08eceb0ff6b4322d5ba30e4f1709feb4cfaed8f5f92e407174d921736171
0x2d419229cc376498dd45e39cf4e616d8430cfc2a480e6574f2c2785639002aa6
0x07afeaa2a53ebbd52a56e034d40c79e8718806e7e5e5822c75f765b985c4c185
0x1b22fc0b958e031ba6d87af74132567df9fb8a69ecab9aebfc639257cff76e3f
0x167b32e53877759a70b56098605e762d33bc5d0040920f8425af285990c00491
0x2b95a63eede1a9e09db49ba0fa3e6e9d72c490115626aca2e2e680951eedb76a
0x1e8315b4b17f3cab2ec58baf386e5555e7cc57d2fc866d9d40773fdc08fbe977
0x2463c40ebad84b518cd5f68f0d4bab1aea17a62fb26bfb5226d55b6b008d55b7
0x06a8c5c470ad04b70b178ca813fe6ab3dd8911a6cbf1a826146db959d60dd8a8
0x080a62e7a3a21a2c4c31409a21198359d5b1c60219103ae298a61e3854aebb01
0x18973fe8c8a1ba771bc273f4d3d024b5e4c2efcc22265b9b25a70cae2b2c798a
0x14e9d7add8b695c8a62fa7bf08cb7660fe6ecbb4c0e0c018a1bbc3b27093dc39
0x26427ef8ffc5bf88ea00ac66ab348862dec24ae35a301abda0d2e24b1267eb13
0x0cbb5760e9e6e6441c7cf3cb86f8f7bf11085476621e2827455f9fa0a28a6f6b
0x0969a68b635ff7db79af41785ac5ba8c657bf1ff737baf313599d5fff6b2b563
0x276aad1c7da0087fd4a26fba8524a414d87d142b2b49f513853549097039dee8
0x15b55fd09ab70d69cea6c715998d857e2273e20aa97518e52e3192eec65b3f90
0x1be8083993d517b24aa671c5755d54b22680d1297c314bac8e721b7530fa7980
0x2e8a6ecb869e0c880a9661bddc3b350d348e92818eff54599c9ac647481aa577
0x002e49c9141f5f54cec05c97aea495171e446e7e3aba28d7ae600430c89dbe77
0x2ab4b42ebe144529df8e8b139eb5e6a721ae03a78cb7045ae377bfea87c19499
0x064d5d4d4b5956cd31bb4c0696b64eec6642b07104b2a25ad041bebef1b1972d
0x2541579c2dcd7450e7dc5717b1de4e91427db75b68bc4a026485f9b6ee6b0123
0x09f5a623e62d16c13a9d230a57db7c42eb4d6e13da3686f65bd126c4fc91f5c4
0x223e346d840618e9bc6beca5f3e84fa57c4908e6c0056e2e33378806b3ae218e
0x208a3e6b55bb34562cd04defd11285ffc74762bf2a3fd99a431eb84b073ffc44
0x291990bc3d8bf3fa5767c70b443164af4a12cf9f190ba4729948a7cb0b861052
0x00f505217bd370ba1b74b277e577ae8fde70f5e9c814584ffb05be2caff437c6
0x0ec65e6674e1ffdbef9e53332c171a7fe77128cf49bd9bbb6086cb506070badc
0x265da5b3368b2a2d09416545678c23dcfa5b21f34162f81fbdefd3e2f5c20a9e
0x2964b35055dd613a8f118cdd2d5226eac65c0f8150c071fa27c2d3cb473ad19e
0x2c1875f88edf7feffd8704da56491435b517e923b81b7f5ac43fee97ceb9d22a
0x08e38c1f18113e4cac5e4e2fb44c0a6bb7d1973372a70f8d0561ab225ca1f242
0x00235accc7ffb49c70954f291c747d261289a8128cc0e7cb5bb988141fb9a262
0x0452d68ba89613d684f26279d0691a3238f9ea24bd5270deed6e50e8575e8c84
0x0f654230f4c12cbd4f755cb9e67f54c5caef67f3a2b9b0c971cd7aa53688784a
0x05e6b642c2887e962684f48f28255d7e9bb79c9c35df0f4fd186b82c7630c46c
0x1da30ee749c9cf3d6e6ba0acfbd8a7ea6552ceb8db2e48d662788299170c1ba5
0x098976e4ed0c35d899defd4347eef2d8577feb512894d9f2069244fd6dbcb091
0x1ef9b4d6cdf9acba5d0cabff0ff5b2ec10f222731028a6495252d03186d38a5d
0x28c092990f9a2e3bbcb27119c823715713b469919f1cac5c4d3fe7b71326e749
0x09fb766084d1c5ed63a0ba3311b01dac2ff2f83e36b23c86203b1287108836e0
0x19b27dfc470e6bd14ebf6cf58ba45da9ec0f04bfb426d7e886c3ef36b2a0f39a
0x144b774672046711277040c50371dddb1c30256f60c85e89f2819442698fc7f7
0x00d18d41e4e135174bd8e2938643fe7f593ed4eea55ad3072deb49f23a14e484
0x1acd22ff590f0b2c815f6b1621f923c6cdbb8ae51034e8a7b99e30e83b21b36a
0x0c193daf2bed22b6f6ce19e9d936e752612baf78901f3cf8d84ed96364dd7506
0x1470b2dbad86769d7bc4710a4c4390c9300a82f1c983657af6cb60c5692eff69
0x1ebb31a5cf24fa18e8991ef1c1f5e702a9aa59f2a9bf69ccd7cca5211f9f5d57
0x15f83bd97cb2a44bdab5bde1f0771c516a3d8b80b4628008ab09d26b87227983
0x04ea3e8b3d37a2375eb0396108dec4ac5c9ae989af24b90f99f5cd0817d79f21
0x1398ba2ec37852e88f7ec120770084c421a3e6c768c11b5f0bc070c94e9b12b2
0x16291a2a90ab4009521812db354e33a6fb17e814d6eec3697bf27af65f19d032
0x04d8844034515cda7da7bdb15459d7b719bc45f9a8a559dc030ed5deafc327ba
0x106b3bfc2c6a1dfb686efdd3e7309268619194f57831d4c93c66c520612f4d7d
0x13860435df5cb0edff8c18aff96fcd67f283db42630ab9dee75d94f624c65f3c
0x2a521ce51b6ff3951b40ac885e4447a39b6c09f423e9c3a7da95e446d0f16863
0x211c509237e66a42d9127d9917dee24bc853b3ccd2646e597e14961b2f8fb4bf
0x2cf316d3a993c7be3d677d7c6e4caed2b9629dc8881cc9ec418ef56b1ba8d4c4
0x06df38ac9c56ed160509d1d57e77c8154a9194a13bbb3ab81bc1764b17252881
0x0bea34ed3415435bbaaa437ac8ed8654574326a12cc0106bd46b201bdd14b796
0x2f351a605d0dcb0ff94eb5adfc3e1bd76fdb0af4abedb80f9ccaf3da494e375a
0x1067d272864bc2e9bc2ad86e056481ce3a30788b1df6ca7e1efa0a2d3840d297
0x0611186541ba581ad4e88f6e01508ac67858b0c3b5246cc1b2e7e2f2cc7ab6ff
0x19bc4fb87a503c7130202cc2a4bf41168298a47acb893a7b4d9165ef6ab8e9fc
0x1e53dca11af16103b72abc1d8513a51f826490922161ff7e4ac3905f108830e8
0x2198b2bd177fcc1d64f9daa3a55acc09d1f3d5eda95dbfe09557ad29d1f484fa
0x2d995beea512cfc307f6a836ba58aa1ddd947ce5b9187f9f11d939fa5efe2e82
0x261822bdf9a3400ca351a845df163e6de06f83ece0ce139b2a1c4a55c88771e8
0x0975b27a4f98ca16afce198dbf55e31e241757f26cd225303e1e067230cc7a65
0x05e0468504c889bc3fb7d69a40f50db2c0e8006859fe1e2f376767a76cbb1d92
0x2b9f05bffd70f720a3a34635656cfa0f776b0e17ea0791686dfde3b6bd43da1e
0x0474dab545d88019644c9089348ab463d82019d6a8a7fe5964ded4d5c1624d1f
0x10ca6292c2756c3215e943f625ad57c3a32bbb366d0347942e951cb60c185a6b
0x065f1195de926ca1c55e70dd8e103d66065555012f49fa68c8c7f0d2e6156d3e
0x2339cf74c86f3582a174ff0b866a8aaca7514c2f755074bbeda92a28f7b7046f
0x0d6bb3e6a28ef2ca0c442f7c6bc0604b733d36f124bc914ee991bd9523dce09d
0x1548314a7aa4e4c7d2762e250659e5c2852db3fef435950ceed0dca08116de9f
0x29b5dfa885a507a3896c589a82e2a5c17f9b2446092ccb3a145d9712578dea9e
0x25273310fc4a2763471323dc7e8745c24c125207c762cbd2d28633731782bce6
0x0e5f5561855fdc0975dd53fef95155bd0ce37c506103a55f1534e6b4481f9cb7
0x22fcabe99a4c546f4575169a24f88f560627c735dad9c39a65a34ea7d8735187
0x2e6c08f9e3d75eed9af94488d8def4d18c2974dda405c0685afad2ecb5a67908
0x0b9434d94fc1ae1488b9adf889febb74d41cc181f9a7fc62e0eccbd70a6eb595
0x2243990371d14ae5022d6860385d7160c3176ff1b26e20197b07c83bd2a05126
0x0722c0255ca92f09f45ef0370a30ea388c82782fdfeee52dd02d1043e9fc55e6
0x0695dc2df95c805f860052427222fa6c8ea1f62ee14880b4cb7fd2484cf4f1a4
0x14da5ae2b01c412ffa67c62fb11a16a7df0ad6f0bc6631e2778ae5d46b750859
0x1871c6dd4491400f843f329424fab21807ed4d5eff69e4b13ec13aee21f341ea
0x0c15c59a078e1288ed23f4337edba0f07d24a4c9e0580f37b10c04475caa7c4a
0x1ca15153faedefb1552ed200f349c8ca34ff27f74ef92e9d408d22261a19c5db
0x0429ee5e0bc9137f6a6fde17eaacf66d0a8c7ddcb74317e6f435ea2e136c0f02
0x0038f9bc8dda084fb7a9b010d207327d2b444ffeee7f57bbaf913e1db036bf4a
0x05922e4cea9c06e980842753606f599d846e0b88f6fa14e055175b9780273509
0x06a71db9788cc0cc48e5b475721a6fd1ee9968092706b0e10a37e066ccd74feb
0x0f7d807d2294c8d4c7ba8b82209cff2d1871d9f7d865fd4fc430a68f0f01b303
0x133d9c5cd426dc163cf02bee10e60391f16ce561d31f76be3b98d2f138eea487
0x0ccf5bd594c356e26433282b92df843fc86c78e8a2bae9298c50618df6cc3345
0x01d12a12b857352b2a3e7accf27357acc6b17110d992d7b548c5cb1d966da9ae
0x1b30966d98cf787298c62b2bd223b09b9caf1fd0628fc45397eb4fefb37c2ff5
0x106f4deebd0ac09739d1b42aaca9fb6b776e7fa69f17403fd545db186c3ab0f0
0x27414ff19b6e63f3dd7468973a6115c24622415d6b86b077d3da291dba2b10ed
0x11fefcd7576fe96fc538a8865765f82d153ae5e9194c31dd748d850ec6f0c264
0x0b4b344ebf6471fd8cd977e2d75947f9258f4da4621e00af4da189f8227a7644
0x2779edb713d748e7579211307d59e9cc1386612a1f56726131808705e5787f17
0x2821f432e2cbde8ddae4419dd7e0f8422459411802472745085688367345a458
0x1f1e574fe31cd86285765d6085138867fd4fcf7a59d21ffbcfd1a2e0525ed9d7
0x2fcea12cf09fc5e763f926e847addcfa53eafa650e018d74a1eb089e99cdedb0
0x29f5343066e9ccae8d3e45a7eb2948241c87b2bf975cb8ee4cddac9e302563c2
0x0e520d84d6476102d384220494e28cf57cb3b2c6fce32e6b22c21b30dea38488
0x046aad74b081c7a88178ede5a3c86969d50971eb964fbf3d6e795d86b872004a
0x2dc569e67f81b7822ca321317523f8924dd97a9d90fb22c71df10a2c5004956c
0x084fdf96c2984d23d30810f6dbb5901118f6ff445b65efc2fef596b9362f377f
0x0079a1b5f4a1af966ed7aa271514cab189f9fc36176282fe858c5eb74d655905
0x2453fe6f3ff82870aa060cbdf476b5bf175aed0ca5d7c50b6057b44ae3ab1804
0x061bfbb9c99564296ee65d6a5bd5ec127fab6286b76a46e60c17e94070637c98
0x077e2f43344da116b98f5e1307de3e5c9dce867da12134dabc3925f0e5382c98
0x055ee9ef419d690d63fb204784911aa3b5b677332d7169826d821f190a8dde47
0x1dff8cbfa7ce1071f0960a7fa85de34a3800c9d09088fe482323d5b6b0b0b8d8
0x298672ab373a592bcf7a1be3384f04ae917a08b1e9fa00fbe6f096ac1755b57f
0x2d246369f450d7d7677a09cad14c1fee58e2d20f956c251b55a6fb36e5ad53f8
0x23a508452d7e25449a378352e1e317750125d6571bd84357afdf0e061c49e62b
0x264836b6578d84fe7a142b297e7221ee10c975f59c58266b9ee62efc7b0fadf7
0x2daeff3f93f8ece903c6b0ecc19d7be27d608b860a4cbd52e127ac46b6643b7e
0x0c26f99d587915e2ce893453a771a2f837ac5801ab77162c241801150896abb1
0x0dab114c968fca8d94a95268ac43a03ceabb90acabd19398fac27b230fe71059
0x06b724db3ab0e8686689fda0c5455ff05f60095b9d7e3604da4c12a7125e7c86
0x2eb9f7b365dab6bc0c4fb5190bbbdf8d4d213f572aa9d45e52bf294ea107abb2
0x1ee6a77317c4f60e635858be29df60258de3e1c937732ec12ea1d70ec6b47a1d
0x2005a611bf7a04244f5597af4283757ef24da5a9f4adcb57abb4cc4abf817fd3
0x1d01de1a086ac402721946df31ffb65718899a96fc449b564f34e98a7570bc4c
0x0f03ab535bf800a05eeb236bbe185f231264aa7ae85248b39c98d7c7ca09a835
0x1ca78b8f4600127e51667e794182d5c0f8a415d9eab24f1e0b853ccc7934fe5d
0x1c373ec6a1f7978491af5125717d9a91bd467e1ff5c523c84e5b31c5244916dd
0x21410defa516b819b33fa47fb93b8a4174e095391caece5ea88a8b385505f131
0x0fd8816cfb844dcf5933ca7d63cf75c34de60d145baafb77a8aa511a529a017e
0x2c9f2a7ee3fdab7d6ecc010d1b2b4c60b442bfce4630468826588e7cc8bb4ba9
0x225800d0a312e58855846c2b4c328c0c0909ab93f14968da3b019d993eed8462


Aux data:
0x2e6b053dbaacaa04ba671575646fff52cc8e091085fd7c55918cada54c8c8640
0x07cdb58fbdd0c50a612438e96e0a313fa90a0e14c7d73b7226dbb5eae1083e43
0x29b7fb014e453237326a28a062fc11822cb9b74bdc946f8592955fd4ac3446e6
0x021ac9f1dd109040730cb7d7e66fad705ea8660d7276f4093280c782bb02cda7
0x0e6934980da1b02d764a0e7d08fffe8dff7ce5ae42594aae42d04b016318a65c
0x209786bc5e0b8ae548c48d6aa7b7f93ce8809800b453993289a9b75f04d0cbd7
0x2e0c1576f663fcbd6123f87f0d883d6219e94586f6c4be20a622bdef3ef7db13
0x1e977f0827370e1cb2d5ca255cb6c7c652bc3a0e278cd2192d082b56c289773d


Extra data:
null
Loading

0 comments on commit 70ccb0e

Please sign in to comment.