Unverified Commit b5410205 authored by Hanwen Cheng's avatar Hanwen Cheng Committed by GitHub
Browse files

feat: enable generate address with seed (#590)



* feat: enable generate address with seed

* Update src/utils/native.ts
Co-Authored-By: Joseph Mark's avatarJoseph Mark <sjeohp@gmail.com>

* Update src/utils/native.ts
Co-Authored-By: Joseph Mark's avatarJoseph Mark <sjeohp@gmail.com>
Co-authored-by: Joseph Mark's avatarJoseph Mark <sjeohp@gmail.com>
parent 2d274fdf
Pipeline #87305 failed with stages
in 4 minutes
......@@ -199,16 +199,36 @@ public class EthkeyBridge extends ReactContextBaseJavaModule {
}
@ReactMethod
public void substrateSignWithRef(double seed_ref, String message, Promise promise) {
public void substrateSignWithRef(double seed_ref, String suriSuffix, String message, Promise promise) {
try {
String s = substrateBrainwalletSignWithRef(Double.doubleToRawLongBits(seed_ref), message);
String s = ethkeySubstrateBrainwalletSignWithRef(Double.doubleToRawLongBits(seed_ref), suriSuffix, message);
promise.resolve(s);
} catch (Exception e) {
promise.reject("invalid substrate phrase", "invalid substrate phrase");
}
}
private static native String ethkeyBrainwalletAddress(String seed);
@ReactMethod
public void brainWalletAddressWithRef(double seedRef, Promise promise) {
try {
String s = ethkeyBrainWalletAddressWithRef(Double.doubleToRawLongBits(seedRef));
promise.resolve(s);
} catch (Exception e) {
promise.reject("invalid substrate phrase", "invalid substrate phrase");
}
}
@ReactMethod
public void substrateAddressWithRef(double seedRef, String suriSuffix, int prefix, Promise promise) {
try {
String substrateAddress = ethkeySubstrateWalletAddressWithRef(Double.doubleToRawLongBits(seedRef), suriSuffix, prefix));
promise.resolve(substrateAddress);
} catch (Exception e) {
promise.reject("invalid suri suffix or prefix", "invalid suri suffix or prefix");
}
}
private static native String ethkeyBrainwalletAddress(String seed);
private static native String ethkeyBrainwalletBIP39Address(String seed);
private static native String ethkeyBrainwalletSign(String seed, String message);
private static native String ethkeyRlpItem(String data, int position);
......@@ -224,8 +244,10 @@ public class EthkeyBridge extends ReactContextBaseJavaModule {
private static native String substrateBrainwalletAddress(String seed, int prefix);
private static native String substrateBrainwalletSign(String seed, String message);
private static native boolean schnorrkelVerify(String seed, String message, String signature);
private static native long ethkeyDecryptDataRef(String data, String password);
private static native void ethkeyDestroyDataRef(long data_ref);
private static native String ethkeyBrainwalletSignWithRef(long seed_ref, String message);
private static native String substrateBrainwalletSignWithRef(long seed_ref, String message);
private static native long ethkeyDecryptDataRef(String data, String password);
private static native void ethkeyDestroyDataRef(long data_ref);
private static native String ethkeyBrainwalletSignWithRef(long seed_ref, String message);
private static native String ethkeySubstrateBrainwalletSignWithRef(long seed_ref, String suriSuffix, String message)
private static native String ethkeySubstrateWalletAddressWithRef(long seedRef, String suriSuffix, int prefix);
private static native String ethkeyBrainWalletAddressWithRef(long seedRef);
}
......@@ -33,7 +33,7 @@
+ (BOOL)requiresMainQueueSetup
{
return YES;
return YES;
}
RCT_EXTERN_METHOD(brainWalletAddress:(NSString*)seed resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
......@@ -48,13 +48,14 @@ RCT_EXTERN_METHOD(encryptData:(NSString*)data password:(NSString*)password resol
RCT_EXTERN_METHOD(decryptData:(NSString*)data password:(NSString*)password resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(qrCode:(NSString*)data resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(qrCodeHex:(NSString*)data resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(substrateAddress:(NSString*)seed version:(NSUInteger*)version resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(substrateAddress:(NSString*)seed prefix:(NSUInteger*)prefix resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(substrateSign:(NSString*)seed message:(NSString*)message resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(blake2b:(NSString*)data resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(schnorrkelVerify: (NSString*)seed message:(NSString*)message signature:(NSString*)signature resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(decryptDataRef:(NSString*)data password:(NSString*)password resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(destroyDataRef:(int64_t)dataRef resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(brainWalletSignWithRef:(int64_t)seedRef message:(NSString*)message resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(substrateSignWithRef:(int64_t)seedRef message:(NSString*)message resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(substrateSignWithRef:(int64_t)seedRef suriSuffix:(NSString*)suriSuffix message:(NSString*)message resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(substrateAddressWithRef:(int64_t)seedRef suriSuffix:(NSString*)suriSuffix prefix:(NSUInteger*)prefix resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(brainWalletAddressWithRef:(int64_t)seedRef resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
@end
......@@ -178,10 +178,10 @@ class EthkeyBridge: NSObject {
}
}
@objc func substrateAddress(_ seed: String, version: UInt32, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
@objc func substrateAddress(_ seed: String, prefix: UInt32, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
var error: UInt32 = 0
var seed_ptr = seed.asPtr()
let address_rust_str = substrate_brainwallet_address(&error, &seed_ptr, version)
let address_rust_str = substrate_brainwallet_address(&error, &seed_ptr, prefix)
let address_rust_str_ptr = rust_string_ptr(address_rust_str)
let address = String.fromStringPtr(ptr: address_rust_str_ptr!.pointee)
rust_string_ptr_destroy(address_rust_str_ptr)
......@@ -247,14 +247,37 @@ class EthkeyBridge: NSObject {
resolve(signature)
}
@objc func substrateSignWithRef(_ seed_ref: Int64, message: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
@objc func substrateSignWithRef(_ seed_ref: Int64, suri_suffix: String, message: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
var error: UInt32 = 0
var message_ptr = message.asPtr()
let signature_rust_str = substrate_brainwallet_sign_with_ref(&error, seed_ref, &message_ptr)
var suri_suffix_ptr = suri_suffix.asPtr()
let signature_rust_str = substrate_brainwallet_sign_with_ref(&error, seed_ref, &suri_suffix_ptr, &message_ptr)
let signature_rust_str_ptr = rust_string_ptr(signature_rust_str)
let signature = String.fromStringPtr(ptr: signature_rust_str_ptr!.pointee)
rust_string_ptr_destroy(signature_rust_str_ptr)
rust_string_destroy(signature_rust_str)
resolve(signature)
}
@objc func brainWalletAddressWithRef(_ seed_ref: Int64, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
var error: UInt32 = 0
let brain_wallet_address_str = brain_wallet_address_with_ref(&error, seed_ref)
let brain_wallett_address_ptr = rust_string_ptr(brain_wallet_address_str)
let brain_wallet_address = String.fromStringPtr(ptr: brain_wallett_address_ptr!.pointee)
rust_string_ptr_destroy(brain_wallett_address_ptr)
rust_string_destroy(brain_wallet_address_str)
resolve(brain_wallet_address)
}
@objc func substrateAddressWithRef(_ seed_ref: Int64, suri_suffix: String, prefix: UInt32, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
var error: UInt32 = 0
var suri_suffix_ptr = suri_suffix.asPtr()
let substrate_address_str = substrate_address_with_ref(&error, seed_ref, &suri_suffix_ptr, prefix)
let substrate_address_ptr = rust_string_ptr(substrate_address_str)
let substrate_address = String.fromStringPtr(ptr: substrate_address_ptr!.pointee)
rust_string_ptr_destroy(substrate_address_ptr)
rust_string_destroy(substrate_address_str)
resolve(substrate_address)
}
}
......@@ -8,17 +8,17 @@
"yarn": "^1.3.2"
},
"scripts": {
"android": "yarn run build-rust-android && npx react-native run-android",
"android": "yarn run build-rust:android && npx react-native run-android",
"build-prod:ios": "NODE_OPTIONS=--max_old_space_size=8192 npx react-native bundle --dev false --entry-file index.js --bundle-output ios/main.jsbundle --platform ios --assets-dest='./ios'",
"build-rust-ios": "(cd rust/signer && make ios)",
"build-rust-android": "(cd rust/signer && make android)",
"build-rust-all": "yarn build-rust-ios && yarn build-rust-android",
"build-rust:ios": "(cd rust/signer && make ios)",
"build-rust:android": "(cd rust/signer && make android)",
"build-rust": "yarn build-rust:ios && yarn build-rust:android",
"build:ios": "yarn install && (cd ios && pod install) && yarn build-rust-ios",
"build:android": "yarn install && yarn buil-rust-android",
"clean": "watchman watch-del-all && rm -rf /tmp/metro-bundler-cache-* && rm -rf /tmp/haste-map-react-native-packager-* && rm -rf node_modules/ && yarn cache clean --force && rm -rf ./android/app/build && rm -rf ./ios/build && yarn clean-rust && cd ios && pod deintegrate && cd ..",
"clean-rust": "(cd rust/signer && cargo clean)",
"commit": "commit-wizard",
"ios": "yarn run build-rust-ios && npx react-native run-ios",
"ios": "yarn run build-rust:ios && npx react-native run-ios",
"lint": "npx eslint . --ext .js,.jsx,.ts,.tsx --ignore-path .gitignore",
"lint:fix": "npx eslint . --ext .js,.jsx,.ts,.tsx --fix --ignore-path .gitignore",
"start": "NODE_OPTIONS=--max_old_space_size=8192 npx react-native start",
......
......@@ -85,4 +85,8 @@ void destroy_data_ref(unsigned* error, int64_t data_ref);
struct rust_string* ethkey_brainwallet_sign_with_ref(unsigned* error, int64_t seed_ref, const struct rust_string_ptr* message);
struct rust_string* substrate_brainwallet_sign_with_ref(unsigned* error, int64_t seed_ref, const struct rust_string_ptr* data);
struct rust_string* substrate_brainwallet_sign_with_ref(unsigned* error, int64_t seed_ref, const struct rust_string_ptr* suri_suffix, const struct rust_string_ptr* data);
struct rust_string* substrate_address_with_ref(unsigned* error, int64_t seed_ref, const struct rust_string_ptr* suri_suffix, const unsigned prefix);
struct rust_string* brain_wallet_address_with_ref(unsigned* error, int64_t seed_ref);
......@@ -258,11 +258,12 @@ export! {
Some(signature.to_hex())
}
@Java_io_parity_signer_EthkeyBridge_substrateBrainwalletSignWithRef
fn substrate_brainwallet_sign_with_ref(seed_ref: i64, message: &str) -> Option<String> {
@Java_io_parity_signer_EthkeyBridge_ethkeySubstrateBrainwalletSignWithRef
fn substrate_brainwallet_sign_with_ref(seed_ref: i64, suri_suffix: &str, message: &str) -> Option<String> {
let seed = unsafe { Box::from_raw(seed_ref as *mut String) };
let suri = format!("{}{}", &seed, suri_suffix);
let keypair = sr25519::KeyPair::from_suri(&seed)?;
let keypair = sr25519::KeyPair::from_suri(&suri)?;
let message: Vec<u8> = message.from_hex().ok()?;
let signature = keypair.sign(&message);
......@@ -271,16 +272,37 @@ export! {
Some(signature.to_hex())
}
@Java_io_parity_signer_EthkeyBridge_ethkeySubstrateWalletAddressWithRef
fn substrate_address_with_ref(seed_ref: i64, suri_suffix: &str, prefix: u8) -> Option<String> {
let seed = unsafe { Box::from_raw(seed_ref as *mut String) };
let suri = format!("{}{}", &seed, suri_suffix);
let keypair = sr25519::KeyPair::from_suri(&suri)?;
let _ = Box::into_raw(seed) as i64;
Some(keypair.ss58_address(prefix))
}
@Java_io_parity_signer_EthkeyBridge_ethkeyBrainWalletAddressWithRef
fn brain_wallet_address_with_ref(seed_ref: i64) -> Option<String> {
let seed = unsafe { Box::from_raw(seed_ref as *mut String) };
let address = ethkey_brainwallet_address(&seed);
// so that the reference remains valid
let _ = Box::into_raw(seed) as i64;
Some(address)
}
}
#[cfg(test)]
mod tests {
use super::*;
static SURI: &str = "grant jaguar wish bench exact find voice habit tank pony state salmon";
static DERIVED_SURI: &str = "grant jaguar wish bench exact find voice habit tank pony state salmon//hard/soft/0";
static SEED_PHRASE: &str = "grant jaguar wish bench exact find voice habit tank pony state salmon";
static SURI_SUFFIX: &str = "//hard/soft/0";
static ENCRYPTED_SEED: &str = "{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"47b4b75d13045ff7569da858e234f7ea\"},\"ciphertext\":\"ca1cf5387822b70392c4aeec729676f91ab00a795d7593fb7e52ecc333dbc4a1acbedc744b5d8d519c714e194bd741995244c8128bfdce6c184d6bda4ca136ed265eedcee9\",\"kdf\":\"pbkdf2\",\"kdfparams\":{\"c\":10240,\"dklen\":32,\"prf\":\"hmac-sha256\",\"salt\":\"b4a2d1edd1a70fe2eb48d7aff15c19e234f6aa211f5142dddb05a59af12b3381\"},\"mac\":\"b38a54eb382f2aa1a8be2f7b86fe040fe112d0f42fea03fac186dccdd7ae3eb9\"}";
static PIN: &str = "000000";
static SUBSTRATE_ADDRESS: &str = "5D4kaJXj5HVoBw2tFFsDj56BjZdPhXKxgGxZuKk4K3bKqHZ6";
static ETHEREUM_ADDRESS: &str = "bip39:f85f35e47e976650641ecd1a644e33edccc9cab1";
#[test]
fn test_random_phrase() {
......@@ -320,25 +342,25 @@ mod tests {
// Secret seed: 0xb139e4050f80172b44957ef9d1755ef5c96c296d63b8a2b50025bf477bd95224
// Public key (hex): 0x944eeb240615f4a94f673f240a256584ba178e22dd7b67503a753968e2f95761
let expected = "5FRAPSnpgmnXAnmPVv68fT6o7ntTvaZmkTED8jDttnXs9k4n";
let generated = substrate_brainwallet_address(SURI, 42).unwrap();
let generated = substrate_brainwallet_address(SEED_PHRASE, 42).unwrap();
assert_eq!(expected, generated);
}
#[test]
fn test_substrate_brainwallet_address_suri() {
let expected = "5D4kaJXj5HVoBw2tFFsDj56BjZdPhXKxgGxZuKk4K3bKqHZ6";
let generated = substrate_brainwallet_address(DERIVED_SURI, 42).unwrap();
let suri = format!("{}{}", SEED_PHRASE, SURI_SUFFIX);
let generated = substrate_brainwallet_address(&suri, 42).unwrap();
assert_eq!(expected, generated);
assert_eq!(SUBSTRATE_ADDRESS, generated);
}
#[test]
fn test_substrate_sign() {
let msg: String = b"Build The Future".to_hex();
let signature = substrate_brainwallet_sign(SURI, &msg).unwrap();
let signature = substrate_brainwallet_sign(SEED_PHRASE, &msg).unwrap();
let is_valid = schnorrkel_verify(SURI, &msg, &signature).unwrap();
let is_valid = schnorrkel_verify(SEED_PHRASE, &msg, &signature).unwrap();
assert!(is_valid);
}
......@@ -347,8 +369,9 @@ mod tests {
fn test_substrate_sign_with_ref() {
let msg: String = b"Build The Future".to_hex();
let data_pointer = decrypt_data_ref(ENCRYPTED_SEED, String::from(PIN)).unwrap();
let signature_by_ref = substrate_brainwallet_sign_with_ref(data_pointer, &msg).unwrap();
let is_valid = schnorrkel_verify(SURI, &msg, &signature_by_ref).unwrap();
let signature_by_ref = substrate_brainwallet_sign_with_ref(data_pointer, SURI_SUFFIX, &msg).unwrap();
let suri = format!("{}{}", SEED_PHRASE, SURI_SUFFIX);
let is_valid = schnorrkel_verify(&suri, &msg, &signature_by_ref).unwrap();
destroy_data_ref(data_pointer);
assert!(is_valid);
}
......@@ -357,6 +380,22 @@ mod tests {
#[test]
fn decrypt_with_ref() {
let decrypted_result = decrypt_data(ENCRYPTED_SEED, String::from(PIN)).unwrap();
assert_eq!(SURI, decrypted_result);
assert_eq!(SEED_PHRASE, decrypted_result);
}
#[test]
fn test_generate_substrate_address() {
let data_pointer = decrypt_data_ref(ENCRYPTED_SEED, String::from(PIN)).unwrap();
let address = substrate_wallet_address_with_ref(data_pointer, SURI_SUFFIX, 42).unwrap();
destroy_data_ref(data_pointer);
assert_eq!(address, SUBSTRATE_ADDRESS);
}
#[test]
fn test_generate_ethereum_address() {
let data_pointer = decrypt_data_ref(ENCRYPTED_SEED, String::from(PIN)).unwrap();
let address = brain_wallet_address_with_ref(data_pointer).unwrap();
destroy_data_ref(data_pointer);
assert_eq!(address, ETHEREUM_ADDRESS);
}
}
......@@ -186,6 +186,30 @@ export class SeedRef {
);
}
trySubstrateAddress(suriSuffix: string, prefix: number): Promise<string> {
if (!this.valid) {
throw new Error('a seed reference has not been created');
}
return EthkeyBridge.substrateAddressWithRef(
this.dataRef,
suriSuffix,
prefix
).then((address: string) => {
return address;
});
}
tryBrainWalletAddress(): Promise<string> {
if (!this.valid) {
throw new Error('a seed reference has not been created');
}
return EthkeyBridge.brainWalletAddressWithRef(this.dataRef).then(
(address: string) => {
return address;
}
);
}
// Destroy the decrypted seed. Must be called before this leaves scope or
// memory will leak.
tryDestroy(): Promise<SeedRef> {
......@@ -210,11 +234,11 @@ export class SeedRef {
}
// Use a reference returned by decryptDataRef to sign a message
trySubstrateSign(message: string): Promise<string> {
trySubstrateSign(suriSuffix: string, message: string): Promise<string> {
if (!this.valid) {
// Seed reference was never created or was already destroyed.
throw new Error('cannot sign with an invalid seed reference');
}
return EthkeyBridge.substrateSignWithRef(this.dataRef, message);
return EthkeyBridge.substrateSignWithRef(this.dataRef, suriSuffix, message);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment