Skip to content

Commit

Permalink
feat: add eth_batch_personal_sign api (#64)
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoguang1010 authored Feb 22, 2024
1 parent 5aa5b15 commit 09287f6
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 10 deletions.
27 changes: 27 additions & 0 deletions token-core/tcx-eth/src/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,25 @@ impl EthRecoverAddressInput {
}
}

pub fn batch_personal_sign(keystore: &mut Keystore, data: Vec<String>) -> Result<Vec<String>> {
let mut signatures = vec![];
for val in data.iter() {
let message = hash_message(utf8_or_hex_to_bytes(&val)?);
let mut signature = keystore.secp256k1_ecdsa_sign_recoverable(&message, "")?;
signature[64] = signature[64] + 27;
signatures.push(signature.to_0x_hex());
}
Ok(signatures)
}

#[cfg(test)]
mod test {
use crate::transaction::{
AccessList, EthMessageInput, EthMessageOutput, EthRecoverAddressInput, EthTxInput,
EthTxOutput, SignatureType,
};

use crate::signer::batch_personal_sign;
use tcx_constants::{CurveType, TEST_MNEMONIC, TEST_PASSWORD};
use tcx_keystore::{Keystore, MessageSigner, Metadata, SignatureParameters, TransactionSigner};

Expand Down Expand Up @@ -669,4 +681,19 @@ mod test {
println!("{}", output.address);
assert_eq!(output.address, "0xed54a7c1d8634bb589f24bb7f05a5554b36f9618");
}

#[test]
fn test_batch_personal_sign() {
let mut keystore =
private_key_store("a392604efc2fad9c0b3da43b5f698a2e3f270f170d859912be0d54742275c5f6");

let test_data = vec![
"Hello imToken".to_string(),
"0xef678007d18427e6022059dbc264f27507cd1ffc".to_string(),
];

let result = batch_personal_sign(&mut keystore, test_data).unwrap();
assert_eq!(result[0], "0x1be38ff0ab0e6d97cba73cf61421f0641628be8ee91dcb2f73315e7fdf4d0e2770b0cb3cc7350426798d43f0fb05602664a28bb2c9fcf46a07fa1c8c4e322ec01b".to_string());
assert_eq!(result[1], "0xb12a1c9d3a7bb722d952366b06bd48cb35bdf69065dee92351504c3716a782493c697de7b5e59579bdcc624aa277f8be5e7f42dc65fe7fcd4cc68fef29ff28c21b".to_string());
}
}
13 changes: 13 additions & 0 deletions token-core/tcx-proto/src/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,16 @@ message ExportMnemonicParam {
}
}

message EthBatchPersonalSignParam {
string id = 1;
oneof key {
string password = 2;
string derivedKey = 3;
}
repeated string data = 4;
}

message EthBatchPersonalSignResult {
repeated string signatures = 1;
}

27 changes: 27 additions & 0 deletions token-core/tcx/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,33 @@ pub mod export_mnemonic_param {
DerivedKey(::prost::alloc::string::String),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EthBatchPersonalSignParam {
#[prost(string, tag = "1")]
pub id: ::prost::alloc::string::String,
#[prost(string, repeated, tag = "4")]
pub data: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
#[prost(oneof = "eth_batch_personal_sign_param::Key", tags = "2, 3")]
pub key: ::core::option::Option<eth_batch_personal_sign_param::Key>,
}
/// Nested message and enum types in `EthBatchPersonalSignParam`.
pub mod eth_batch_personal_sign_param {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Key {
#[prost(string, tag = "2")]
Password(::prost::alloc::string::String),
#[prost(string, tag = "3")]
DerivedKey(::prost::alloc::string::String),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EthBatchPersonalSignResult {
#[prost(string, repeated, tag = "1")]
pub signatures: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
}
/// FUNCTION: create_keystore(CreateKeystoreParam): KeystoreResult
///
/// create a new hd keystore
Expand Down
27 changes: 23 additions & 4 deletions token-core/tcx/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ use crate::api::{
self, export_private_key_param, wallet_key_param, AccountResponse, BackupResult,
CreateKeystoreParam, DecryptDataFromIpfsParam, DecryptDataFromIpfsResult, DeriveAccountsParam,
DeriveAccountsResult, DeriveSubAccountsParam, DeriveSubAccountsResult, DerivedKeyResult,
EncryptDataToIpfsParam, EncryptDataToIpfsResult, ExistsJsonParam, ExistsKeystoreResult,
ExistsMnemonicParam, ExistsPrivateKeyParam, ExportJsonParam, ExportJsonResult,
ExportMnemonicParam, ExportMnemonicResult, ExportPrivateKeyParam, ExportPrivateKeyResult,
GeneralResult, GetExtendedPublicKeysParam, GetExtendedPublicKeysResult, GetPublicKeysParam,
EncryptDataToIpfsParam, EncryptDataToIpfsResult, EthBatchPersonalSignParam,
EthBatchPersonalSignResult, ExistsJsonParam, ExistsKeystoreResult, ExistsMnemonicParam,
ExistsPrivateKeyParam, ExportJsonParam, ExportJsonResult, ExportMnemonicParam,
ExportMnemonicResult, ExportPrivateKeyParam, ExportPrivateKeyResult, GeneralResult,
GetExtendedPublicKeysParam, GetExtendedPublicKeysResult, GetPublicKeysParam,
GetPublicKeysResult, ImportJsonParam, ImportMnemonicParam, ImportPrivateKeyParam,
ImportPrivateKeyResult, KeystoreResult, MnemonicToPublicKeyParam, MnemonicToPublicKeyResult,
ScanKeystoresResult, SignAuthenticationMessageParam, SignAuthenticationMessageResult,
Expand All @@ -57,6 +58,7 @@ use tcx_constants::coin_info::coin_info_from_param;
use tcx_constants::{CoinInfo, CurveType};
use tcx_crypto::aes::cbc::encrypt_pkcs7;
use tcx_crypto::KDF_ROUNDS;
use tcx_eth::signer::batch_personal_sign;
use tcx_keystore::{MessageSigner, TransactionSigner};

use tcx_primitive::Ss58Codec;
Expand Down Expand Up @@ -1213,6 +1215,23 @@ pub(crate) fn sign_bls_to_execution_change(data: &[u8]) -> Result<Vec<u8>> {
encode_message(result)
}

impl_to_key!(crate::api::eth_batch_personal_sign_param::Key);
pub(crate) fn eth_batch_personal_sign(data: &[u8]) -> Result<Vec<u8>> {
let param: EthBatchPersonalSignParam = EthBatchPersonalSignParam::decode(data)?;

let mut map = KEYSTORE_MAP.write();
let keystore: &mut Keystore = match map.get_mut(&param.id) {
Some(keystore) => Ok(keystore),
_ => Err(anyhow!("{}", "wallet_not_found")),
}?;

let mut keystore = KeystoreGuard::unlock(keystore, param.key.clone().unwrap().into())?;

let signatures = batch_personal_sign(keystore.keystore_mut(), param.data)?;

encode_message(EthBatchPersonalSignResult { signatures })
}

#[cfg(test)]
mod tests {
use tcx_constants::CurveType;
Expand Down
62 changes: 56 additions & 6 deletions token-core/tcx/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ use std::result;
use crate::error_handling::{landingpad, LAST_ERROR};
use crate::handler::{
create_keystore, decrypt_data_from_ipfs, delete_keystore, derive_accounts, derive_sub_accounts,
encode_message, encrypt_data_to_ipfs, exists_json, exists_mnemonic, exists_private_key,
export_json, export_mnemonic, export_private_key, get_derived_key, get_extended_public_keys,
get_public_keys, import_json, import_mnemonic, import_private_key, mnemonic_to_public,
sign_authentication_message, sign_hashes, sign_message, sign_tx, unlock_then_crash,
verify_password,
encode_message, encrypt_data_to_ipfs, eth_batch_personal_sign, exists_json, exists_mnemonic,
exists_private_key, export_json, export_mnemonic, export_private_key, get_derived_key,
get_extended_public_keys, get_public_keys, import_json, import_mnemonic, import_private_key,
mnemonic_to_public, sign_authentication_message, sign_hashes, sign_message, sign_tx,
unlock_then_crash, verify_password,
};
use crate::migration::{migrate_keystore, scan_legacy_keystores};

Expand Down Expand Up @@ -119,6 +119,9 @@ pub unsafe extern "C" fn call_tcx_api(hex_str: *const c_char) -> *const c_char {
"sign_bls_to_execution_change" => {
landingpad(|| sign_bls_to_execution_change(&action.param.unwrap().value))
}
"eth_batch_personal_sign" => {
landingpad(|| eth_batch_personal_sign(&action.param.unwrap().value))
}
_ => landingpad(|| Err(anyhow!("unsupported_method"))),
};
match reply {
Expand Down Expand Up @@ -185,7 +188,8 @@ mod tests {
export_private_key_param, migrate_keystore_param, sign_param, BackupResult,
CreateKeystoreParam, DecryptDataFromIpfsParam, DecryptDataFromIpfsResult,
DeriveAccountsParam, DeriveAccountsResult, DeriveSubAccountsParam, DeriveSubAccountsResult,
DerivedKeyResult, EncryptDataToIpfsParam, EncryptDataToIpfsResult, ExistsJsonParam,
DerivedKeyResult, EncryptDataToIpfsParam, EncryptDataToIpfsResult,
EthBatchPersonalSignParam, EthBatchPersonalSignResult, ExistsJsonParam,
ExistsKeystoreResult, ExistsMnemonicParam, ExistsPrivateKeyParam, ExportJsonParam,
ExportJsonResult, ExportMnemonicResult, ExportPrivateKeyParam, ExportPrivateKeyResult,
GeneralResult, GetExtendedPublicKeysParam, GetExtendedPublicKeysResult, GetPublicKeysParam,
Expand Down Expand Up @@ -4874,4 +4878,50 @@ mod tests {
}
})
}

#[test]
#[serial]
fn test_eth_batch_personal_sign_by_private_key() {
run_test(|| {
let wallet = import_default_pk_store();
let param = EthBatchPersonalSignParam {
id: wallet.id.to_string(),
key: Some(api::eth_batch_personal_sign_param::Key::Password(
TEST_PASSWORD.to_owned(),
)),
data: vec![
"Hello imToken".to_string(),
"0xef678007d18427e6022059dbc264f27507cd1ffc".to_string(),
],
};
let sign_result = call_api("eth_batch_personal_sign", param).unwrap();
let ret: EthBatchPersonalSignResult =
EthBatchPersonalSignResult::decode(sign_result.as_slice()).unwrap();
assert_eq!(ret.signatures[0], "0x1be38ff0ab0e6d97cba73cf61421f0641628be8ee91dcb2f73315e7fdf4d0e2770b0cb3cc7350426798d43f0fb05602664a28bb2c9fcf46a07fa1c8c4e322ec01b".to_string());
assert_eq!(ret.signatures[1], "0xb12a1c9d3a7bb722d952366b06bd48cb35bdf69065dee92351504c3716a782493c697de7b5e59579bdcc624aa277f8be5e7f42dc65fe7fcd4cc68fef29ff28c21b".to_string());
});
}

#[test]
#[serial]
fn test_eth_batch_personal_sign_by_hd() {
run_test(|| {
let wallet = import_default_wallet();
let param = EthBatchPersonalSignParam {
id: wallet.id.to_string(),
key: Some(api::eth_batch_personal_sign_param::Key::Password(
TEST_PASSWORD.to_owned(),
)),
data: vec![
"Hello imToken".to_string(),
"0xef678007d18427e6022059dbc264f27507cd1ffc".to_string(),
],
};
let sign_result = call_api("eth_batch_personal_sign", param).unwrap();
let ret: EthBatchPersonalSignResult =
EthBatchPersonalSignResult::decode(sign_result.as_slice()).unwrap();
assert_eq!(ret.signatures[0], "0xb270b1e5ee1345c693b7b4fd7f5287f6b6372059c89590dfcadc4edf94ec9293296d3e205495f1468953a4f893cd6798b66301a51571fe2a419e75d5755b5bc91c".to_string());
assert_eq!(ret.signatures[1], "0x19671e4c92847629fa5bbba59e70402f54366e61db0555984a83cc512413fc462d6be1508f1accdee6ad0040ed7401ac2db33d17e1aa4e66bedcb9f75c250cfa1b".to_string());
});
}
}

0 comments on commit 09287f6

Please sign in to comment.