building first draft of passport circuit with arkworks

This commit is contained in:
0xturboblitz
2023-09-28 18:31:26 +02:00
parent 1901b7be50
commit 7dcdb8c1ff
14 changed files with 3528 additions and 81 deletions

View File

@@ -31,14 +31,14 @@ import {
DEFAULT_ADDRESS,
LOCAL_IP,
} from '@env';
import {PassportData} from './types/passportData';
import {arraysAreEqual, dataHashesObjToArray} from './utils/utils';
import {computeAndCheckEContent} from './utils/computeEContent';
import {DataHash, PassportData} from './types/passportData';
import {arraysAreEqual, bytesToBigDecimal, dataHashesObjToArray, formatAndConcatenateDataHashes, formatMrz, splitToWords} from './utils/utils';
import {hash, toUnsignedByte} from './utils/computeEContent';
console.log('DEFAULT_PNUMBER', DEFAULT_PNUMBER);
const CACHE_DATA_IN_LOCAL_SERVER = true;
const SKIP_SCAN = false;
const SKIP_SCAN = true;
function App(): JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
@@ -67,13 +67,17 @@ function App(): JSX.Element {
if (SKIP_SCAN && passportData === null) {
console.log('skipping scan step...');
fetch(`${LOCAL_IP}/passportData`)
try {
fetch(`${LOCAL_IP}/passportData`)
.then(response => response.json())
.then(data => {
console.log('passport data fetched');
setPassportData(data);
setStep('scanCompleted');
});
} catch(e) {
console.log('error fetching passport data', e);
}
}
async function handleResponse(response: any) {
@@ -148,21 +152,52 @@ function App(): JSX.Element {
return;
}
// 1. Compute the eContent from the mrzInfo and the dataGroupHashes
const computedEContent = computeAndCheckEContent(passportData);
// 1. TODO check signature to make sure the proof will work
if (!arraysAreEqual(computedEContent, passportData.eContent)) {
throw new Error(
'computed eContent does not match the one from passportData',
);
// 2. Format all the data as inputs for the circuit
const formattedMrz = formatMrz(passportData.mrz);
const mrzHash = hash(formatMrz(passportData.mrz));
const concatenatedDataHashes = formatAndConcatenateDataHashes(
mrzHash,
passportData.dataGroupHashes as DataHash[],
);
const reveal_bitmap = Array.from({ length: 88 }, (_, i) => (i >= 16 && i <= 22) ? '1' : '0');
const inputs = {
mrz: Array.from(formattedMrz).map(byte => String(byte)),
reveal_bitmap: Array.from(reveal_bitmap).map(byte => String(byte)),
dataHashes: Array.from(concatenatedDataHashes.map(toUnsignedByte)).map(byte => String(byte)),
eContentBytes: Array.from(passportData.eContent.map(toUnsignedByte)).map(byte => String(byte)),
signature: splitToWords(
BigInt(bytesToBigDecimal(passportData.encryptedDigest)),
BigInt(64),
BigInt(32)
),
pubkey: splitToWords(
BigInt(passportData.modulus),
BigInt(64),
BigInt(32)
),
address,
}
// 3. Generate a proof of passport
// 2. TODO : check RSA signature to make sure the proof will work
// Did not manage to do it cuz js crypto libs suck
// 3. Format all the data as inputs for the circuit
// 4. Generate a proof of passport
const start = Date.now();
NativeModules.RNPassportReader.provePassport(inputs, (err: any, res: any) => {
const end = Date.now();
if (err) {
console.error(err);
setProofResult(
"res:" + err.toString() + ' time elapsed: ' + (end - start) + 'ms',
);
} else {
console.log(res);
setProofResult(
"res:" + res.toString() + ' time elapsed: ' + (end - start) + 'ms',
);
}
});
};
const handleMint = () => {
@@ -271,19 +306,17 @@ function App(): JSX.Element {
) : null}
{step === 'proofGenerated' ? (
<View style={styles.sectionContainer}>
<Text style={styles.header}>Zero-knowledge proof generated</Text>
<Text style={styles.sectionDescription}>Zero-knowledge proof generated</Text>
<Text style={styles.header}>You can now mint your SBT</Text>
<Button title="Mint Proof of Passport" onPress={handleMint} />
</View>
) : null}
</View>
<View style={styles.sectionContainer}>
<View style={{...styles.sectionContainer, marginTop: 80}}>
<Button title="Call rust though java" onPress={callRustLib} />
<Text style={styles.header}>{result}</Text>
</View>
<View style={styles.sectionContainer}>
<Button title="Prove" onPress={proveRust} />
<Text style={styles.header}>{proofResult}</Text>
<Text style={styles.sectionDescription}>{result}</Text>
<Button title="Test sample proof with arkworks" onPress={proveRust} />
<Text style={styles.sectionDescription}>{proofResult}</Text>
</View>
</ScrollView>
</SafeAreaView>

View File

@@ -1,21 +1,48 @@
To build libhalo2_passport.so, run in /script:
# Embassy App
Only Android right now, under heavy development
#### Requirements
Install `nodejs v18`
#### Installation
```bash
yarn
```
#### Add circuit build
Go to this [repo](https://github.com/0xturboblitz/circom-embassy) and build the circuit.
Then, place `passport.r1cs` and `passport.wasm` in ark-circom-passport/passport/
#### Build native lib
In /script, run:
```
./build_rust.sh
```
It will cross-compile using the config in app/android/react-native-passport-reader/android/build.gradle
This will build the libhalo2_circom_passport.so lib and copy it to the desired place to be used by the app.
The config used is in android/react-native-passport-reader/android/build.gradle.
You can go there to change the profile (`debug` or `release`)
You'll potentially need to set the rust-toolchain rust version as global default. Example:
```
rustup default 1.67.0
```
And install the targets like:
And install the targets like this:
```
rustup target add aarch64-linux-android
```
Then:
yarn start => a or i
To run the server:
```
yarn start
```
Then press `a` for android or `i` for iOS

View File

@@ -1 +1,2 @@
node_modules/
android/src/main/jniLibs/arm64/libark_circom_passport.so

View File

@@ -56,9 +56,9 @@ dependencies {
apply plugin: 'org.mozilla.rust-android-gradle.rust-android'
cargo {
module = "../../../ark-circom-rsa" // this works
module = "../../../ark-circom-passport" // this works
// module = "/Users/turboblitz/code/my-code/passport-sbt/app/halo2-passport"
libname = "ark_circom_rsa" // Or whatever matches Cargo.toml's [package] name.
libname = "ark_circom_passport" // Or whatever matches Cargo.toml's [package] name.
// targets = ["arm", "x86"] // failing
targets = ["arm64", "darwin-aarch64"] // Those work
apiLevel = 29

View File

@@ -92,6 +92,7 @@ import com.google.gson.Gson;
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReadableNativeMap
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
@@ -571,7 +572,34 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
// Return the result to JavaScript through the callback
callback.invoke(null, resultFromProof)
}
external fun provePassport(
mrz: List<String>,
reveal_bitmap: List<String>,
dataHashes: List<String>,
eContentBytes: List<String>,
signature: List<String>,
pubkey: List<String>,
address: String
): Int
@ReactMethod
fun provePassport(inputs: ReadableMap, callback: Callback) {
Log.d(TAG, "inputsaaa: " + inputs.toString())
val mrz = inputs.getArray("mrz")?.toArrayList()?.map { it as String } ?: listOf()
val reveal_bitmap = inputs.getArray("reveal_bitmap")?.toArrayList()?.map { it as String } ?: listOf()
val data_hashes = inputs.getArray("dataHashes")?.toArrayList()?.map { it as String } ?: listOf()
val e_content_bytes = inputs.getArray("eContentBytes")?.toArrayList()?.map { it as String } ?: listOf()
val signature = inputs.getArray("signature")?.toArrayList()?.map { it as String } ?: listOf()
val pubkey = inputs.getArray("pubkey")?.toArrayList()?.map { it as String } ?: listOf()
val address = inputs.getString("address") ?: ""
val resultFromProof = provePassport(mrz, reveal_bitmap, data_hashes, e_content_bytes, signature, pubkey, address)
// Return the result to JavaScript through the callback
callback.invoke(null, resultFromProof)
}
companion object {
private val TAG = RNPassportReaderModule::class.java.simpleName
@@ -582,7 +610,7 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
private const val KEY_IS_SUPPORTED = "isSupported"
var instance: RNPassportReaderModule? = null
init {
System.loadLibrary("ark_circom_rsa")
System.loadLibrary("ark_circom_passport")
}
}
}

3
app/ark-circom-passport/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/target
rsa
passport

3027
app/ark-circom-passport/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
[package]
name = "ark_circom_passport"
version = "0.1.0"
edition = "2021"
[lib]
name = "ark_circom_passport"
path = "src/passport.rs"
crate-type = ["cdylib"]
[dependencies]
ark-circom = { git = "https://github.com/0xturboblitz/circom-compat.git" }
ark-bn254 = { version = "=0.4.0" }
ark-groth16 = { version = "=0.4.0", default-features = false, features = ["parallel"] }
ark-std = { version = "=0.4.0", default-features = false, features = ["parallel"] }
ark-crypto-primitives = { version = "=0.4.0" }
color-eyre = "=0.6.2"
jni = "0.18" # Choose the version that best fits your needs
log = "0.4"
android_logger = "0.8"

View File

@@ -0,0 +1,3 @@
[toolchain]
channel = "stable"
version = "1.67.0"

View File

@@ -0,0 +1,286 @@
use ark_circom::{CircomBuilder, CircomConfig};
use ark_std::rand::thread_rng;
use color_eyre::Result;
use std::os::raw::c_int;
use ark_bn254::Bn254;
use ark_crypto_primitives::snark::SNARK;
use ark_groth16::Groth16;
use std::time::Instant;
use std::convert::TryInto;
type GrothBn = Groth16<Bn254>;
extern crate jni;
use jni::objects::{JClass, JObject, JValue, JString};
use jni::JNIEnv;
use log::Level;
use android_logger::Config;
#[no_mangle]
pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_callRustCode(
env: JNIEnv,
_: JClass,
) -> jni::sys::jstring {
android_logger::init_once(Config::default().with_min_level(Level::Trace));
log::warn!("log before imports");
let current_dir = std::env::current_dir().unwrap();
let path_str = current_dir.to_str().unwrap();
let output = env.new_string(path_str).expect("Couldn't create java string!");
output.into_inner()
}
#[no_mangle]
pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_proveRSAInRust(
_: JNIEnv,
_: JClass,
) -> c_int {
fn run() -> Result<u128, Box<dyn std::error::Error>> {
println!("log before imports");
const MAIN_WASM: &'static [u8] = include_bytes!("../rsa/main.wasm");
const MAIN_R1CS: &'static [u8] = include_bytes!("../rsa/main.r1cs");
let cfg = CircomConfig::<Bn254>::from_bytes(MAIN_WASM, MAIN_R1CS)?;
let mut builder = CircomBuilder::new(cfg);
let signature: [u128; 32] = [
4993543337487904319, 5039260395924778555,
16044715263198697509, 6517674227143205114,
9783381675666809188, 7797234981612410535,
9712659746244703685, 8223984644219552691,
5746171858797010138, 16352708903743190663,
11557514992480971638, 13495509591487042457,
11156826800435483355, 7934676927345641909,
17671838456179191719, 15427313345670295171,
3979639931302305273, 10870708508897347751,
17325747030660864416, 4196229958717243275,
8295837152932404523, 5206285193355768709,
16500962385150574058, 45927554409508738,
11056427006453546685, 3610340837562714815,
2914954158206709664, 9941999032204203280,
3682966980231699250, 1089954850805856847,
12801803660741250853, 6643401487810361365
];
let modulus: [u128; 32] = [
14637485623069577853, 7482098129440337882,
9329095990282353414, 13124250581866537330,
18349306516477384309, 3633589540637627345,
756443621693602880, 9532268969225926567,
10797289495421403158, 8716880397646489088,
16390100705849925925, 4946748147388408397,
5159237052852568257, 4383482229078465345,
17440536203309797881, 9244726556354794825,
13954964489103323762, 12859274108738823253,
15430872548874177827, 8078236913810864353,
13311543254088155939, 6627932043456339426,
10937476704429447948, 4860889415451015006,
4549761793924050171, 1117773587704762559,
13984923195668836033, 5179232650854575709,
16174751231280536837, 9625446134615655537,
6169436660688221259, 13128400207083283532
];
let base_message: [u128; 32] = [
3626324085499461436, 15137430623782848370,
13410089559264023318, 7272337899472972005,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0
];
for &elem in signature.iter() {
builder.push_input("signature", elem);
}
for &elem in modulus.iter() {
builder.push_input("modulus", elem);
}
for &elem in base_message.iter() {
builder.push_input("base_message", elem);
}
// create an empty instance for setting it up
let circom = builder.setup();
let mut rng = thread_rng();
let params = GrothBn::generate_random_parameters_with_reduction(circom, &mut rng)?;
let circom = builder.build()?;
println!("circuit built");
let inputs = circom.get_public_inputs().unwrap();
let start1 = Instant::now();
let proof = GrothBn::prove(&params, circom, &mut rng)?;
let duration1 = start1.elapsed();
println!("proof generated. Took: {:?}", duration1);
let start2 = Instant::now();
let pvk = GrothBn::process_vk(&params.vk).unwrap();
let verified = GrothBn::verify_with_processed_vk(&pvk, &inputs, &proof)?;
let duration2 = start2.elapsed();
println!("proof verified. Took: {:?}", duration2);
assert!(verified);
Ok(duration1.as_millis())
}
match run() {
Ok(elapsed_millis) => elapsed_millis as i32, // Assuming the elapsed time will fit in an i32
Err(_) => -1, // return -1 or some other error code when there's an error
}
}
#[no_mangle]
pub extern "C" fn Java_io_tradle_nfc_RNPassportReaderModule_provePassport(
env: JNIEnv,
_: JClass,
mrz: JObject,
reveal_bitmap: JObject,
data_hashes: JObject,
e_content_bytes: JObject,
signature: JObject,
pubkey: JObject,
address: JString,
) -> c_int {
log::warn!("formatting inputsaaaa...");
fn run_proof(
mrz: JObject,
reveal_bitmap: JObject,
data_hashes: JObject,
e_content_bytes: JObject,
signature: JObject,
pubkey: JObject,
address: JString,
env: JNIEnv
) -> Result<u128, Box<dyn std::error::Error>> {
android_logger::init_once(Config::default().with_min_level(Level::Trace));
log::warn!("formatting inputs...");
log::warn!("mrz_veccccccc");
let mrz_vec: Vec<String> = java_arraylist_to_rust_vec(&env, mrz)?;
let reveal_bitmap_vec: Vec<String> = java_arraylist_to_rust_vec(&env, reveal_bitmap)?;
let data_hashes_vec: Vec<String> = java_arraylist_to_rust_vec(&env, data_hashes)?;
let e_content_bytes_vec: Vec<String> = java_arraylist_to_rust_vec(&env, e_content_bytes)?;
let signature_vec: Vec<String> = java_arraylist_to_rust_vec(&env, signature)?;
let pubkey_vec: Vec<String> = java_arraylist_to_rust_vec(&env, pubkey)?;
let address_str: String = env.get_string(address)?.into();
log::warn!("mrz_vec {:?}", mrz_vec);
log::warn!("reveal_bitmap_vec {:?}", reveal_bitmap_vec);
log::warn!("data_hashes_vec {:?}", data_hashes_vec);
log::warn!("e_content_bytes_vec {:?}", e_content_bytes_vec);
log::warn!("signature_vec {:?}", signature_vec);
log::warn!("pubkey_vec {:?}", pubkey_vec);
log::warn!("address_str {:?}", address_str);
log::warn!("loading circuit...");
const MAIN_WASM: &'static [u8] = include_bytes!("../passport/passport.wasm");
const MAIN_R1CS: &'static [u8] = include_bytes!("../passport/passport.r1cs");
log::warn!("circuit loaded");
let cfg = CircomConfig::<Bn254>::from_bytes(MAIN_WASM, MAIN_R1CS)?;
let mut builder = CircomBuilder::new(cfg);
mrz_vec.iter()
.filter_map(|s| s.parse::<u128>().ok())
.for_each(|n| builder.push_input("mrz", n));
reveal_bitmap_vec.iter()
.filter_map(|s| s.parse::<u128>().ok())
.for_each(|n| builder.push_input("reveal_bitmap", n));
data_hashes_vec.iter()
.filter_map(|s| s.parse::<u128>().ok())
.for_each(|n| builder.push_input("dataHashes", n));
e_content_bytes_vec.iter()
.filter_map(|s| s.parse::<u128>().ok())
.for_each(|n| builder.push_input("eContentBytes", n));
signature_vec.iter()
.filter_map(|s| s.parse::<u128>().ok())
.for_each(|n| builder.push_input("signature", n));
pubkey_vec.iter()
.filter_map(|s| s.parse::<u128>().ok())
.for_each(|n| builder.push_input("pubkey", n));
// create an empty instance for setting it up
let circom = builder.setup();
let mut rng = thread_rng();
let params = GrothBn::generate_random_parameters_with_reduction(circom, &mut rng)?;
let circom = builder.build()?;
println!("circuit built");
let inputs = circom.get_public_inputs().unwrap();
let start1 = Instant::now();
let proof = GrothBn::prove(&params, circom, &mut rng)?;
let duration1 = start1.elapsed();
println!("proof generated. Took: {:?}", duration1);
let start2 = Instant::now();
let pvk = GrothBn::process_vk(&params.vk).unwrap();
let verified = GrothBn::verify_with_processed_vk(&pvk, &inputs, &proof)?;
let duration2 = start2.elapsed();
println!("proof verified. Took: {:?}", duration2);
assert!(verified);
Ok(duration1.as_millis())
}
match run_proof(
mrz,
reveal_bitmap,
data_hashes,
e_content_bytes,
signature,
pubkey,
address,
env
) {
Ok(elapsed_millis) => elapsed_millis as i32, // Assuming the elapsed time will fit in an i32
Err(_) => -1, // return -1 or some other error code when there's an error
}
}
fn java_arraylist_to_rust_vec(env: &JNIEnv, java_list: JObject) -> Result<Vec<String>, jni::errors::Error> {
let size = env.call_method(java_list, "size", "()I", &[])?.i()? as i32;
let mut vec = Vec::with_capacity(size.try_into().unwrap());
for i in 0..size {
let java_string = env.call_method(java_list, "get", "(I)Ljava/lang/Object;", &[JValue::from(i)])?.l()?;
let rust_string: String = env.get_string(java_string.into())?.into();
vec.push(rust_string);
}
Ok(vec)
}
// -message:"BpfCoordinator" -message:"MATCH" -message:"maximum" -message:"exception" level:WARN

View File

@@ -6,4 +6,12 @@ cd ../android
cd ..
mkdir -p android/react-native-passport-reader/android/src/main/jniLibs/arm64/
cp ark-circom-rsa/target/aarch64-linux-android/release/libark_circom_rsa.so android/react-native-passport-reader/android/src/main/jniLibs/arm64/
if [ -f "ark-circom-passport/target/aarch64-linux-android/release/libark_circom_passport.so" ]; then
echo copied release version
cp ark-circom-passport/target/aarch64-linux-android/release/libark_circom_passport.so android/react-native-passport-reader/android/src/main/jniLibs/arm64/
elif [ -f "ark-circom-passport/target/aarch64-linux-android/debug/libark_circom_passport.so" ]; then
echo copied debug version
cp ark-circom-passport/target/aarch64-linux-android/debug/libark_circom_passport.so android/react-native-passport-reader/android/src/main/jniLibs/arm64/
fi

View File

@@ -11,9 +11,10 @@ app.post('/post', (req: Request, res: Response) => {
const data = req.body;
fs.writeFile('passportData.json', JSON.stringify(data, null, 2), err => {
if (err) {
console.log(err);
console.log("err posting new passportData", err);
res.status(500).json({message: 'An error occurred while writing file'});
} else {
console.log("File written successfully");
res.json({message: 'File written successfully'});
}
});
@@ -22,9 +23,11 @@ app.post('/post', (req: Request, res: Response) => {
app.get('/passportData', (req: Request, res: Response) => {
fs.readFile('passportData.json', (err, data) => {
if (err) {
console.log("err fetching passportData", err);
console.log(err);
res.status(500).json({message: 'An error occurred while reading file'});
} else {
console.log("passportData fetched");
res.json(JSON.parse(data.toString()));
}
});

View File

@@ -9,36 +9,6 @@ import {
} from './utils';
import {sha256} from 'js-sha256';
export function computeAndCheckEContent(passportData: PassportData) {
const mrz = assembleMrz(passportData.mrzInfo);
const dataHashes = passportData.dataGroupHashes;
const mrzHash = hash(formatMrz(mrz));
if (!arraysAreEqual(mrzHash, dataHashes[0][1])) {
throw new Error('MRZ hash does not match data group hash 1');
}
const concatenatedDataHashes = formatAndConcatenateDataHashes(
mrzHash,
dataHashes,
);
if (
!arraysAreEqual(
concatenatedDataHashes,
passportData.contentBytes.content.string,
)
) {
throw new Error('Concatenated data hashes do not match content bytes');
}
const concatenatedDataHashesHashDigest = hash(concatenatedDataHashes);
const timeOfSignature = findTimeOfSignature(passportData.eContentDecomposed);
return assembleEContent(concatenatedDataHashesHashDigest, timeOfSignature);
}
// hash logic here because the one in utils.ts only works with node
export function hash(bytesArray: number[]) {
let unsignedBytesArray = bytesArray.map(toUnsignedByte);
@@ -46,7 +16,7 @@ export function hash(bytesArray: number[]) {
return hexToSignedBytes(hash);
}
function hexToSignedBytes(hexString: string) {
export function hexToSignedBytes(hexString: string) {
let bytes = [];
for (let i = 0; i < hexString.length - 1; i += 2) {
let byte = parseInt(hexString.substr(i, 2), 16);
@@ -55,6 +25,6 @@ function hexToSignedBytes(hexString: string) {
return bytes;
}
function toUnsignedByte(signedByte: number) {
export function toUnsignedByte(signedByte: number) {
return signedByte < 0 ? signedByte + 256 : signedByte;
}

View File

@@ -109,22 +109,20 @@ export function formatAndConcatenateDataHashes(
// Let's replace the first array with the MRZ hash
dataHashes.shift();
dataHashes.unshift([1, mrzHash]);
// Concaténons les dataHashes :
const concatenatedDataHashes: number[] = [].concat(
...dataHashes.map((dataHash: any) => {
dataHash[1].unshift(...[48, 37, 2, 1, dataHash[0], 4, 32]);
return dataHash[1];
}),
);
let concat: number[] = []
// Starting sequence. Should be the same for everybody, but not sure
concatenatedDataHashes.unshift(
...[
48, -126, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, -122, 72, 1, 101, 3, 4, 2, 1,
48, -126, 1, 17,
],
);
return concatenatedDataHashes;
concat.push(...[
48, -126, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, -122, 72, 1, 101, 3, 4, 2, 1,
48, -126, 1, 17,
])
for(const dataHash of dataHashes) {
concat.push(...[48, 37, 2, 1, dataHash[0], 4, 32, ...dataHash[1]])
}
return concat;
}
export function assembleEContent(
@@ -160,3 +158,41 @@ export function assembleEContent(
constructedEContent.push(...messageDigest);
return constructedEContent;
}
export const toBinaryString = (byte: any) => {
const binary = (parseInt(byte, 10) & 0xFF).toString(2).padStart(8, '0');
return binary;
};
export function splitToWords(
number: bigint,
wordsize: bigint,
numberElement: bigint
) {
let t = number
const words: string[] = []
for (let i = BigInt(0); i < numberElement; ++i) {
const baseTwo = BigInt(2)
words.push(`${t % BigInt(Math.pow(Number(baseTwo), Number(wordsize)))}`)
t = BigInt(t / BigInt(Math.pow(Number(BigInt(2)), Number(wordsize))))
}
if (!(t == BigInt(0))) {
throw `Number ${number} does not fit in ${(
wordsize * numberElement
).toString()} bits`
}
return words
}
export function bytesToBigDecimal(arr: number[]): string {
let result = BigInt(0);
for (let i = 0; i < arr.length; i++) {
result = result * BigInt(256) + BigInt(arr[i] & 0xff);
}
return result.toString();
}
export function hexToDecimal(hex: string): string {
return BigInt(`0x${hex}`).toString();
}