diff --git a/bitacross-worker/bitacross/core/bc-task-processor/src/lib.rs b/bitacross-worker/bitacross/core/bc-task-processor/src/lib.rs index 0278723f6c..64171055bf 100644 --- a/bitacross-worker/bitacross/core/bc-task-processor/src/lib.rs +++ b/bitacross-worker/bitacross/core/bc-task-processor/src/lib.rs @@ -78,7 +78,7 @@ use lc_direct_call::{ handler::{ kill_ceremony, nonce_share, partial_signature_share, sign_bitcoin::{self, SignBitcoinError}, - sign_ethereum, + sign_ethereum, sign_ton, }, CeremonyRoundCall, CeremonyRoundCallSigned, DirectCall, DirectCallSigned, }; @@ -109,6 +109,7 @@ pub struct BitAcrossTaskContext< SIGNINGAK, EKR, BKR, + TKR, S: StfEnclaveSigning, H: HandleState, O: EnclaveOnChainOCallApi, @@ -121,6 +122,7 @@ pub struct BitAcrossTaskContext< SIGNINGAK: AccessKey, EKR: AccessKey, BKR: AccessKey, + TKR: AccessKey, ::KeyType: ShieldingCryptoEncrypt + 'static, Responder: SendRpcResponse, { @@ -128,6 +130,7 @@ pub struct BitAcrossTaskContext< pub signing_key_access: Arc, pub ethereum_key_repository: Arc, pub bitcoin_key_repository: Arc, + pub ton_key_repository: Arc, pub enclave_signer: Arc, pub state_handler: Arc, pub ocall_api: Arc, @@ -145,6 +148,7 @@ impl< SIGNINGAK, EKR, BKR, + TKR, S: StfEnclaveSigning, H: HandleState, O: EnclaveOnChainOCallApi, @@ -152,12 +156,13 @@ impl< ERL: EnclaveRegistryLookup, SRL: SignerRegistryLookup, Responder, - > BitAcrossTaskContext + > BitAcrossTaskContext where SKR: AccessKey + AccessPubkey, SIGNINGAK: AccessKey, EKR: AccessKey, BKR: AccessKey, + TKR: AccessKey, ::KeyType: ShieldingCryptoEncrypt + 'static, H::StateT: SgxExternalitiesTrait, Responder: SendRpcResponse, @@ -168,6 +173,7 @@ where signing_key_access: Arc, ethereum_key_repository: Arc, bitcoin_key_repository: Arc, + ton_key_repository: Arc, enclave_signer: Arc, state_handler: Arc, ocall_api: Arc, @@ -184,6 +190,7 @@ where signing_key_access, ethereum_key_repository, bitcoin_key_repository, + ton_key_repository, enclave_signer, state_handler, ocall_api, @@ -199,8 +206,23 @@ where } #[allow(clippy::type_complexity)] -pub fn run_bit_across_handler_runner( - context: Arc>, +pub fn run_bit_across_handler_runner< + SKR, + SIGNINGAK, + EKR, + BKR, + TKR, + S, + H, + O, + RRL, + ERL, + SRL, + Responder, +>( + context: Arc< + BitAcrossTaskContext, + >, ceremony_commands_thread_count: u8, ceremony_events_thread_count: u8, ) where @@ -208,6 +230,7 @@ pub fn run_bit_across_handler_runner + Send + Sync + 'static, EKR: AccessKey + Send + Sync + 'static, BKR: AccessKey + Send + Sync + 'static, + TKR: AccessKey + Send + Sync + 'static, ::KeyType: ShieldingCryptoEncrypt + ShieldingCryptoDecrypt + 'static, S: StfEnclaveSigning + Send + Sync + 'static, H: HandleState + Send + Sync + 'static, @@ -287,8 +310,10 @@ pub fn run_bit_across_handler_runner( - context: Arc>, +fn handle_ceremony_command( + context: Arc< + BitAcrossTaskContext, + >, ceremony_id: CeremonyId, command: CeremonyCommand, event_threads_pool: ThreadPool, @@ -298,6 +323,7 @@ fn handle_ceremony_command + Send + Sync + 'static, EKR: AccessKey + Send + Sync + 'static, BKR: AccessKey + Send + Sync + 'static, + TKR: AccessKey + Send + Sync + 'static, ::KeyType: ShieldingCryptoEncrypt + ShieldingCryptoDecrypt + 'static, S: StfEnclaveSigning + Send + Sync + 'static, H: HandleState + Send + Sync + 'static, @@ -429,8 +455,10 @@ fn handle_ceremony_command( - context: Arc>, +fn process_command( + context: Arc< + BitAcrossTaskContext, + >, ceremony_id: CeremonyId, command: CeremonyCommand, ) -> Option @@ -439,6 +467,7 @@ where SIGNINGAK: AccessKey + Send + Sync + 'static, EKR: AccessKey + Send + Sync + 'static, BKR: AccessKey + Send + Sync + 'static, + TKR: AccessKey + Send + Sync + 'static, ::KeyType: ShieldingCryptoEncrypt + ShieldingCryptoDecrypt + 'static, S: StfEnclaveSigning + Send + Sync + 'static, H: HandleState + Send + Sync + 'static, @@ -531,15 +560,18 @@ where } #[allow(clippy::type_complexity)] -fn handle_request( +fn handle_request( request: BitAcrossRequest, - context: Arc>, + context: Arc< + BitAcrossTaskContext, + >, ) -> Option<(CeremonyId, CeremonyCommand)> where SKR: AccessKey + AccessPubkey, SIGNINGAK: AccessKey, EKR: AccessKey, BKR: AccessKey, + TKR: AccessKey + Send + Sync + 'static, ::KeyType: ShieldingCryptoEncrypt + ShieldingCryptoDecrypt + 'static, S: StfEnclaveSigning + Send + Sync + 'static, H: HandleState + Send + Sync + 'static, @@ -572,15 +604,18 @@ where } #[allow(clippy::type_complexity)] -fn handle_direct_call( +fn handle_direct_call( request: PlainRequest, - context: Arc>, + context: Arc< + BitAcrossTaskContext, + >, ) -> Result<(Option, Option<(CeremonyId, CeremonyCommand)>), Vec> where SKR: AccessKey + AccessPubkey, SIGNINGAK: AccessKey, EKR: AccessKey, BKR: AccessKey, + TKR: AccessKey + Send + Sync + 'static, ::KeyType: ShieldingCryptoEncrypt + ShieldingCryptoDecrypt + 'static, S: StfEnclaveSigning + Send + Sync + 'static, H: HandleState + Send + Sync + 'static, @@ -652,19 +687,33 @@ where e.encode() }) .map(|r| (Some(BitAcrossProcessingResult::Ok(r.encode())), None)), + DirectCall::SignTon(signer, payload) => sign_ton::handle( + signer, + payload, + context.relayer_registry_lookup.deref(), + context.ton_key_repository.deref(), + ) + .map_err(|e| { + error!("SignTon error: {:?}", e); + e.encode() + }) + .map(|r| (Some(BitAcrossProcessingResult::Ok(r.encode())), None)), } } #[allow(clippy::type_complexity)] -fn handle_ceremony_round_call( +fn handle_ceremony_round_call( request: PlainRequest, - context: Arc>, + context: Arc< + BitAcrossTaskContext, + >, ) -> Result, Vec> where SKR: AccessKey + AccessPubkey, SIGNINGAK: AccessKey, EKR: AccessKey, BKR: AccessKey, + TKR: AccessKey + Send + Sync + 'static, ::KeyType: ShieldingCryptoEncrypt + ShieldingCryptoDecrypt + 'static, S: StfEnclaveSigning + Send + Sync + 'static, H: HandleState + Send + Sync + 'static, diff --git a/bitacross-worker/core-primitives/enclave-api/ffi/src/lib.rs b/bitacross-worker/core-primitives/enclave-api/ffi/src/lib.rs index ba18cbaa7d..339aa7bb97 100644 --- a/bitacross-worker/core-primitives/enclave-api/ffi/src/lib.rs +++ b/bitacross-worker/core-primitives/enclave-api/ffi/src/lib.rs @@ -134,6 +134,13 @@ extern "C" { pair_size: u32, ) -> sgx_status_t; + pub fn get_ton_wallet_pair( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + pair: *mut u8, + pair_size: u32, + ) -> sgx_status_t; + pub fn get_mrenclave( eid: sgx_enclave_id_t, retval: *mut sgx_status_t, diff --git a/bitacross-worker/core-primitives/enclave-api/src/enclave_base.rs b/bitacross-worker/core-primitives/enclave-api/src/enclave_base.rs index b56b5ed0ba..c5749a931d 100644 --- a/bitacross-worker/core-primitives/enclave-api/src/enclave_base.rs +++ b/bitacross-worker/core-primitives/enclave-api/src/enclave_base.rs @@ -78,6 +78,9 @@ pub trait EnclaveBase: Send + Sync + 'static { /// retrieve the eth wallet key pair, only works in non-prod fn get_ethereum_wallet_pair(&self) -> EnclaveResult; + /// retrieve the ton wallet key pair, only works in non-prod + fn get_ton_wallet_pair(&self) -> EnclaveResult; + fn get_fingerprint(&self) -> EnclaveResult; // litentry @@ -112,7 +115,7 @@ mod impl_ffi { use pallet_teebag::EnclaveFingerprint; use sgx_crypto_helper::rsa3072::Rsa3072PubKey; use sgx_types::*; - use sp_core::ed25519; + use sp_core::{ed25519, Pair}; impl EnclaveBase for Enclave { fn init( @@ -378,6 +381,25 @@ mod impl_ffi { .map_err(|e| Error::Other(format!("{:?}", e).into())) } + fn get_ton_wallet_pair(&self) -> EnclaveResult { + let mut retval = sgx_status_t::SGX_SUCCESS; + let mut private_key = [0u8; 32]; + + let result = unsafe { + ffi::get_ton_wallet_pair( + self.eid, + &mut retval, + private_key.as_mut_ptr(), + private_key.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(ed25519::Pair::from_seed(&private_key)) + } + fn get_fingerprint(&self) -> EnclaveResult { let mut retval = sgx_status_t::SGX_SUCCESS; let mut mr_enclave = [0u8; MR_ENCLAVE_SIZE]; diff --git a/bitacross-worker/core-primitives/node-api/metadata/src/metadata_mocks.rs b/bitacross-worker/core-primitives/node-api/metadata/src/metadata_mocks.rs index c4b66a273c..e3fcbc0e35 100644 --- a/bitacross-worker/core-primitives/node-api/metadata/src/metadata_mocks.rs +++ b/bitacross-worker/core-primitives/node-api/metadata/src/metadata_mocks.rs @@ -72,6 +72,7 @@ pub struct NodeMetadataMock { bitacross_remove_relayer: u8, btc_wallet_generated: u8, eth_wallet_generated: u8, + ton_wallet_generated: u8, } impl NodeMetadataMock { @@ -113,6 +114,7 @@ impl NodeMetadataMock { bitacross_remove_relayer: 1u8, btc_wallet_generated: 2u8, eth_wallet_generated: 3u8, + ton_wallet_generated: 4u8, } } } @@ -221,6 +223,10 @@ impl BitAcrossCallIndexes for NodeMetadataMock { fn eth_wallet_generated_indexes(&self) -> Result<[u8; 2]> { Ok([self.bitacross_module, self.eth_wallet_generated]) } + + fn ton_wallet_generated_indexes(&self) -> Result<[u8; 2]> { + Ok([self.bitacross_module, self.ton_wallet_generated]) + } } impl TimestampCallIndexes for NodeMetadataMock { diff --git a/bitacross-worker/core-primitives/node-api/metadata/src/pallet_bitacross.rs b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_bitacross.rs index 914cd18704..0c45043f28 100644 --- a/bitacross-worker/core-primitives/node-api/metadata/src/pallet_bitacross.rs +++ b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_bitacross.rs @@ -24,6 +24,7 @@ pub trait BitAcrossCallIndexes { fn remove_relayer_call_indexes(&self) -> Result<[u8; 2]>; fn btc_wallet_generated_indexes(&self) -> Result<[u8; 2]>; fn eth_wallet_generated_indexes(&self) -> Result<[u8; 2]>; + fn ton_wallet_generated_indexes(&self) -> Result<[u8; 2]>; } impl BitAcrossCallIndexes for NodeMetadata { @@ -42,4 +43,8 @@ impl BitAcrossCallIndexes for NodeMetadata { fn eth_wallet_generated_indexes(&self) -> Result<[u8; 2]> { self.call_indexes(BITACROSS, "eth_wallet_generated") } + + fn ton_wallet_generated_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(BITACROSS, "ton_wallet_generated") + } } diff --git a/bitacross-worker/core-primitives/sgx/crypto/src/ed25519.rs b/bitacross-worker/core-primitives/sgx/crypto/src/ed25519.rs index 153314eb4f..3e1602f42e 100644 --- a/bitacross-worker/core-primitives/sgx/crypto/src/ed25519.rs +++ b/bitacross-worker/core-primitives/sgx/crypto/src/ed25519.rs @@ -34,9 +34,9 @@ pub trait Ed25519Sealing { fn exists(&self) -> bool; - fn create_sealed_if_absent(&self) -> Result<()>; + fn create_sealed_if_absent_or_provided(&self, seed: Option<[u8; 32]>) -> Result<()>; - fn create_sealed(&self) -> Result<()>; + fn create_sealed(&self, seed: Option<[u8; 32]>) -> Result<()>; } impl ToPubkey for ed25519::Pair { @@ -61,15 +61,17 @@ pub mod sgx { use log::*; use sgx_rand::{Rng, StdRng}; use sp_core::{crypto::Pair, ed25519}; - use std::path::PathBuf; + use std::{path::PathBuf, string::String}; /// Gets a repository for an Ed25519 keypair and initializes /// a fresh key pair if it doesn't exist at `path`. pub fn get_ed25519_repository( path: PathBuf, + key_file_prefix: Option, + key: Option<[u8; 32]>, ) -> Result> { - let ed25519_seal = Ed25519Seal::new(path); - ed25519_seal.create_sealed_if_absent()?; + let ed25519_seal = Ed25519Seal::new(path, key_file_prefix); + ed25519_seal.create_sealed_if_absent_or_provided(key)?; let signing_pair = ed25519_seal.unseal_pair()?; Ok(KeyRepository::new(signing_pair, ed25519_seal.into())) } @@ -77,15 +79,20 @@ pub mod sgx { #[derive(Clone, Debug)] pub struct Ed25519Seal { base_path: PathBuf, + key_file_prefix: Option, } impl Ed25519Seal { - pub fn new(base_path: PathBuf) -> Self { - Self { base_path } + pub fn new(base_path: PathBuf, key_file_prefix: Option) -> Self { + Self { base_path, key_file_prefix } } pub fn path(&self) -> PathBuf { - self.base_path.join(SEALED_SIGNER_SEED_FILE) + if let Some(ref prefix) = self.key_file_prefix { + self.base_path.join(prefix.clone() + "_" + SEALED_SIGNER_SEED_FILE) + } else { + self.base_path.join(SEALED_SIGNER_SEED_FILE) + } } } @@ -102,19 +109,26 @@ pub mod sgx { self.path().exists() } - fn create_sealed_if_absent(&self) -> Result<()> { + fn create_sealed_if_absent_or_provided(&self, seed: Option<[u8; 32]>) -> Result<()> { if !self.exists() { info!("Keyfile not found, creating new! {}", self.path().display()); - return self.create_sealed() + return self.create_sealed(seed) + } + if seed.is_some() { + info!("Seed provided, creating new! {}", self.path().display()); + return self.create_sealed(seed) } + Ok(()) } - fn create_sealed(&self) -> Result<()> { - let mut seed = [0u8; 32]; - let mut rand = StdRng::new()?; - rand.fill_bytes(&mut seed); - + fn create_sealed(&self, seed: Option<[u8; 32]>) -> Result<()> { + let seed = seed.unwrap_or_else(|| { + let mut seed = [0u8; 32]; + let mut rand = StdRng::new().unwrap(); + rand.fill_bytes(&mut seed); + seed + }); Ok(seal(&seed, self.path())?) } } @@ -141,24 +155,30 @@ pub mod sgx_tests { use super::sgx::*; use crate::{key_repository::AccessKey, Ed25519Sealing, ToPubkey}; use itp_sgx_temp_dir::TempDir; + use std::string::String; pub fn using_get_ed25519_repository_twice_initializes_key_only_once() { let temp_dir = TempDir::with_prefix("using_get_rsa3072_repository_twice_initializes_key_only_once") .unwrap(); let temp_path = temp_dir.path().to_path_buf(); - let key1 = get_ed25519_repository(temp_path.clone()).unwrap().retrieve_key().unwrap(); - let key2 = get_ed25519_repository(temp_path).unwrap().retrieve_key().unwrap(); + let key1 = get_ed25519_repository(temp_path.clone(), None, None) + .unwrap() + .retrieve_key() + .unwrap(); + let key2 = get_ed25519_repository(temp_path, None, None).unwrap().retrieve_key().unwrap(); assert_eq!(key1.pubkey().unwrap(), key2.pubkey().unwrap()); } + pub fn using_get_ed25519_repository_with_different_prefix_initializes_key_twice() {} + pub fn ed25529_sealing_works() { let temp_dir = TempDir::with_prefix("ed25529_sealing_works").unwrap(); - let seal = Ed25519Seal::new(temp_dir.path().to_path_buf()); + let seal = Ed25519Seal::new(temp_dir.path().to_path_buf(), None::); // Create new sealed keys and unseal them. assert!(!seal.exists()); - seal.create_sealed_if_absent().unwrap(); + seal.create_sealed_if_absent_or_provided(None).unwrap(); let pair = seal.unseal_pair().unwrap(); let pubkey = seal.unseal_pubkey().unwrap(); @@ -166,13 +186,13 @@ pub mod sgx_tests { assert_eq!(pair.pubkey().unwrap(), pubkey); // Should not change anything because the key is already there. - seal.create_sealed_if_absent().unwrap(); + seal.create_sealed_if_absent_or_provided(None).unwrap(); let pair_same = seal.unseal_pair().unwrap(); assert_eq!(pair.pubkey().unwrap(), pair_same.pubkey().unwrap()); // Should overwrite previous keys. - seal.create_sealed().unwrap(); + seal.create_sealed(None).unwrap(); let pair_different = seal.unseal_pair().unwrap(); assert_ne!(pair_different.pubkey().unwrap(), pair.pubkey().unwrap()); diff --git a/bitacross-worker/enclave-runtime/Enclave.edl b/bitacross-worker/enclave-runtime/Enclave.edl index 7b54768ba5..1a645bec79 100644 --- a/bitacross-worker/enclave-runtime/Enclave.edl +++ b/bitacross-worker/enclave-runtime/Enclave.edl @@ -105,6 +105,9 @@ enclave { public sgx_status_t get_ethereum_wallet_pair( [out, size=pair_size] uint8_t* pair, uint32_t pair_size); + public sgx_status_t get_ton_wallet_pair( + [out, size=pair_size] uint8_t* pair, uint32_t pair_size); + public sgx_status_t get_mrenclave( [out, size=mrenclave_size] uint8_t* mrenclave, uint32_t mrenclave_size); diff --git a/bitacross-worker/enclave-runtime/src/initialization/global_components.rs b/bitacross-worker/enclave-runtime/src/initialization/global_components.rs index 8cbeecaf52..6e60d93af5 100644 --- a/bitacross-worker/enclave-runtime/src/initialization/global_components.rs +++ b/bitacross-worker/enclave-runtime/src/initialization/global_components.rs @@ -103,6 +103,7 @@ pub type EnclaveShieldingKeyRepository = KeyRepository; pub type EnclaveBitcoinKeyRepository = KeyRepository; pub type EnclaveEthereumKeyRepository = KeyRepository; +pub type EnclaveTonKeyRepository = KeyRepository; pub type EnclaveStateFileIo = SgxStateFileIo; pub type EnclaveStateSnapshotRepository = StateSnapshotRepository; pub type EnclaveStateObserver = StateObserver; @@ -356,6 +357,10 @@ pub static GLOBAL_ETHEREUM_KEY_REPOSITORY_COMPONENT: ComponentContainer< EnclaveEthereumKeyRepository, > = ComponentContainer::new("Ethereum key repository"); +/// Ton key repository +pub static GLOBAL_TON_KEY_REPOSITORY_COMPONENT: ComponentContainer = + ComponentContainer::new("Ton key repository"); + /// Light client db seal for the Integritee parentchain pub static GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL: ComponentContainer< EnclaveLightClientSeal, diff --git a/bitacross-worker/enclave-runtime/src/initialization/mod.rs b/bitacross-worker/enclave-runtime/src/initialization/mod.rs index dba412f69c..f8da5bab31 100644 --- a/bitacross-worker/enclave-runtime/src/initialization/mod.rs +++ b/bitacross-worker/enclave-runtime/src/initialization/mod.rs @@ -35,8 +35,8 @@ use crate::{ GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, GLOBAL_STATE_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_OBSERVER_COMPONENT, GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL, - GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TOP_POOL_AUTHOR_COMPONENT, - GLOBAL_WEB_SOCKET_SERVER_COMPONENT, + GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TON_KEY_REPOSITORY_COMPONENT, + GLOBAL_TOP_POOL_AUTHOR_COMPONENT, GLOBAL_WEB_SOCKET_SERVER_COMPONENT, }, ocall::OcallApi, rpc::{rpc_response_channel::RpcResponseChannel, worker_api_direct::public_api_rpc_handler}, @@ -95,7 +95,12 @@ use itp_types::{parentchain::ParentchainId, OpaqueCall, ShardIdentifier}; use litentry_macros::if_development_or; use log::*; use sp_core::{crypto::Pair, H256}; -use std::{collections::HashMap, path::PathBuf, string::String, sync::Arc}; +use std::{ + collections::HashMap, + path::PathBuf, + string::{String, ToString}, + sync::Arc, +}; use std::sync::SgxRwLock as RwLock; @@ -109,7 +114,7 @@ pub(crate) fn init_enclave( info!("Ceremony commands thread count: {}", ceremony_commands_thread_count); info!("Ceremony events thread count: {}", ceremony_events_thread_count); - let signing_key_repository = Arc::new(get_ed25519_repository(base_dir.clone())?); + let signing_key_repository = Arc::new(get_ed25519_repository(base_dir.clone(), None, None)?); GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.initialize(signing_key_repository.clone()); let signer = signing_key_repository.retrieve_key()?; @@ -127,6 +132,12 @@ pub(crate) fn init_enclave( let ethereum_key = ethereum_key_repository.retrieve_key()?; info!("[Enclave initialized] Ethereum public key raw : {:?}", ethereum_key.public_bytes()); + let ton_key_repository = + Arc::new(get_ed25519_repository(base_dir.clone(), Some("ton".to_string()), None)?); + GLOBAL_TON_KEY_REPOSITORY_COMPONENT.initialize(ton_key_repository.clone()); + let ton_key = ton_key_repository.retrieve_key()?; + info!("[Enclave initialized] Ton public key raw : {:?}", ton_key.public().0); + let shielding_key_repository = Arc::new(get_rsa3072_repository(base_dir.clone())?); GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.initialize(shielding_key_repository.clone()); @@ -278,12 +289,21 @@ pub(crate) fn finish_enclave_init() -> EnclaveResult<()> { pub(crate) fn init_wallets(base_dir: PathBuf) -> EnclaveResult<()> { if_development_or!( { - println!("Initializing wallets from BTC_KEY and ETH_KEY env variables"); + println!("Initializing wallets from BTC_KEY, ETH_KEY and TON_KEY env variables"); let btc_key: Option<[u8; 32]> = read_key_from_env("BTC_KEY")?; - create_schnorr_repository(base_dir.clone(), "bitcoin", btc_key)?; + if btc_key.is_some() { + create_schnorr_repository(base_dir.clone(), "bitcoin", btc_key)?; + } let eth_key: Option<[u8; 32]> = read_key_from_env("ETH_KEY")?; - create_ecdsa_repository(base_dir, "ethereum", eth_key)?; + if eth_key.is_some() { + create_ecdsa_repository(base_dir.clone(), "ethereum", eth_key)?; + } + + let ton_key: Option<[u8; 32]> = read_key_from_env("TON_KEY")?; + if ton_key.is_some() { + get_ed25519_repository(base_dir, Some("ton".to_string()), ton_key)?; + } }, { println!("Init wallets available in dev mode only!"); @@ -335,8 +355,18 @@ pub(crate) fn publish_wallets() -> EnclaveResult<()> { let ethereum_opaque_call = OpaqueCall::from_tuple(&(ethereum_call, ethereum_key.public_bytes())); + let ton_key_repository = GLOBAL_TON_KEY_REPOSITORY_COMPONENT.get()?; + let ton_key = ton_key_repository.retrieve_key()?; + + let ton_call = metadata_repository + .get_from_metadata(|m| m.ton_wallet_generated_indexes()) + .map_err(|e| Error::Other(e.into()))? + .map_err(|e| Error::Other(format!("{:?}", e).into()))?; + + let ton_opaque_call = OpaqueCall::from_tuple(&(ton_call, ton_key.public().0)); + let xts = extrinsics_factory - .create_extrinsics(&[bitcoin_opaque_call, ethereum_opaque_call], None) + .create_extrinsics(&[bitcoin_opaque_call, ethereum_opaque_call, ton_opaque_call], None) .map_err(|e| Error::Other(e.into()))?; validator_accessor .execute_mut_on_validator(|v| v.send_extrinsics(xts)) @@ -381,6 +411,7 @@ fn run_bit_across_handler( let shielding_key_repository = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get()?; let ethereum_key_repository = GLOBAL_ETHEREUM_KEY_REPOSITORY_COMPONENT.get()?; let bitcoin_key_repository = GLOBAL_BITCOIN_KEY_REPOSITORY_COMPONENT.get()?; + let ton_key_repository = GLOBAL_TON_KEY_REPOSITORY_COMPONENT.get()?; #[allow(clippy::unwrap_used)] let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; @@ -391,11 +422,12 @@ fn run_bit_across_handler( author_api, )); - let stf_task_context = BitAcrossTaskContext::new( + let task_context = BitAcrossTaskContext::new( shielding_key_repository, signing_key, ethereum_key_repository, bitcoin_key_repository, + ton_key_repository, stf_enclave_signer, state_handler, ocall_api, @@ -408,7 +440,7 @@ fn run_bit_across_handler( responder, ); run_bit_across_handler_runner( - Arc::new(stf_task_context), + Arc::new(task_context), ceremony_commands_thread_count, ceremony_events_thread_count, ); diff --git a/bitacross-worker/enclave-runtime/src/lib.rs b/bitacross-worker/enclave-runtime/src/lib.rs index 6de9732e39..5800f628f6 100644 --- a/bitacross-worker/enclave-runtime/src/lib.rs +++ b/bitacross-worker/enclave-runtime/src/lib.rs @@ -63,6 +63,7 @@ use core::ffi::c_int; #[cfg(feature = "development")] use initialization::global_components::{ GLOBAL_BITCOIN_KEY_REPOSITORY_COMPONENT, GLOBAL_ETHEREUM_KEY_REPOSITORY_COMPONENT, + GLOBAL_TON_KEY_REPOSITORY_COMPONENT, }; use itc_parentchain::{ block_import_dispatcher::DispatchBlockImport, @@ -318,6 +319,36 @@ pub unsafe extern "C" fn get_ethereum_wallet_pair(pair: *mut u8, pair_size: u32) ) } +#[no_mangle] +#[cfg_attr(not(feature = "development"), allow(unused_variables))] +pub unsafe extern "C" fn get_ton_wallet_pair(pair: *mut u8, pair_size: u32) -> sgx_status_t { + if_development_or!( + { + let ton_key_repository = match GLOBAL_TON_KEY_REPOSITORY_COMPONENT.get() { + Ok(s) => s, + Err(e) => { + error!("{:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let keypair = match ton_key_repository.retrieve_key() { + Ok(p) => p, + Err(e) => return e.into(), + }; + + let privkey_slice = slice::from_raw_parts_mut(pair, pair_size as usize); + privkey_slice.clone_from_slice(&keypair.seed()); + + sgx_status_t::SGX_SUCCESS + }, + { + error!("Ton wallet can only be retrieved in non-prod"); + sgx_status_t::SGX_ERROR_UNEXPECTED + } + ) +} + #[no_mangle] pub unsafe extern "C" fn set_nonce( nonce: *const u32, diff --git a/bitacross-worker/example/client/definitions.json b/bitacross-worker/example/client/definitions.json index 1e6dc4dffe..11bf91c1c1 100644 --- a/bitacross-worker/example/client/definitions.json +++ b/bitacross-worker/example/client/definitions.json @@ -74,6 +74,10 @@ [ "SignEthereum", "(LitentryIdentity, PrehashedEthereumMessage)" + ], + [ + "SignTon", + "(LitentryIdentity, Vec)" ] ] }, diff --git a/bitacross-worker/example/client/example.go b/bitacross-worker/example/client/example.go index a41cf8005b..1b11f01f54 100644 --- a/bitacross-worker/example/client/example.go +++ b/bitacross-worker/example/client/example.go @@ -181,6 +181,18 @@ func prepareSignEthereumDirectCall(identity map[string]interface{}, prehashedEth } +func prepareSignTonDirectCall(identity map[string]interface{}, payload []byte) map[string]interface{} { + signTonDirectCall := map[string]interface{}{ + "col1": identity, + "col2": utiles.BytesToHex(payload), + } + + return map[string]interface{}{ + "SignTon": signTonDirectCall, + } + +} + func prepareSignBitcoinTaprootSpendableDirectCall(identity map[string]interface{}, bitcoinPayload []byte, merkleRootHash [32]byte) map[string]interface{} { payload := map[string]interface{}{ "TaprootSpendable": map[string]interface{}{ diff --git a/bitacross-worker/litentry/core/direct-call/src/handler/mod.rs b/bitacross-worker/litentry/core/direct-call/src/handler/mod.rs index 712daf571a..4c7a8d3c03 100644 --- a/bitacross-worker/litentry/core/direct-call/src/handler/mod.rs +++ b/bitacross-worker/litentry/core/direct-call/src/handler/mod.rs @@ -19,3 +19,4 @@ pub mod nonce_share; pub mod partial_signature_share; pub mod sign_bitcoin; pub mod sign_ethereum; +pub mod sign_ton; diff --git a/bitacross-worker/litentry/core/direct-call/src/handler/sign_ton.rs b/bitacross-worker/litentry/core/direct-call/src/handler/sign_ton.rs new file mode 100644 index 0000000000..8ab4cee10e --- /dev/null +++ b/bitacross-worker/litentry/core/direct-call/src/handler/sign_ton.rs @@ -0,0 +1,103 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use bc_relayer_registry::RelayerRegistryLookup; +use codec::Encode; +use itp_sgx_crypto::key_repository::AccessKey; +use litentry_primitives::Identity; +use log::error; +use sp_core::{ed25519::Pair as Ed25519Pair, Pair}; +use std::vec::Vec; + +#[derive(Encode, Debug)] +pub enum SignTonError { + InvalidSigner, + SigningError, +} + +pub fn handle>( + signer: Identity, + msg: Vec, + relayer_registry: &RRL, + key_repository: &EKR, +) -> Result<[u8; 64], SignTonError> { + if relayer_registry.contains_key(&signer) { + let key = key_repository.retrieve_key().map_err(|e| { + error!("Could not retrieve ton signing key: {}", e); + SignTonError::SigningError + })?; + let sig = key.sign(&msg); + Ok(sig.into()) + } else { + Err(SignTonError::InvalidSigner) + } +} + +#[cfg(test)] +pub mod test { + use crate::handler::sign_ton::handle; + use bc_relayer_registry::{RelayerRegistry, RelayerRegistryUpdater}; + use itp_sgx_crypto::{ecdsa::Pair as EcdsaPair, mocks::KeyRepositoryMock}; + use k256::{ecdsa::SigningKey, elliptic_curve::rand_core}; + use litentry_primitives::Identity; + use sp_core::{sr25519, Pair}; + + #[test] + pub fn it_should_return_ok_for_relayer_signer() { + //given + let relayer_registry = RelayerRegistry::default(); + let alice_key_pair = sr25519::Pair::from_string("//Alice", None).unwrap(); + let relayer_account = Identity::Substrate(alice_key_pair.public().into()); + relayer_registry.update(relayer_account.clone()).unwrap(); + + let signing_key = Pair::from_seed(&[ + 135, 174, 141, 248, 62, 107, 189, 100, 181, 60, 54, 229, 76, 255, 248, 189, 240, 238, + 171, 149, 56, 144, 67, 122, 222, 52, 26, 118, 79, 121, 33, 37, + ]); + + let key_repository = KeyRepositoryMock::new(signing_key); + + //when + let result = + handle(relayer_account, Default::default(), &relayer_registry, &key_repository); + + //then + assert!(result.is_ok()) + } + + #[test] + pub fn it_should_return_err_for_non_relayer_signer() { + //given + let relayer_registry = RelayerRegistry::default(); + let alice_key_pair = sr25519::Pair::from_string("//Alice", None).unwrap(); + let non_relayer_account = Identity::Substrate(alice_key_pair.public().into()); + + let private = SigningKey::random(&mut rand_core::OsRng); + let signing_key = Pair::from_seed(&[ + 135, 174, 141, 248, 62, 107, 189, 100, 181, 60, 54, 229, 76, 255, 248, 189, 240, 238, + 171, 149, 56, 144, 67, 122, 222, 52, 26, 118, 79, 121, 33, 37, + ]); + + let key_repository = KeyRepositoryMock::new(signing_key); + + //when + let result = + handle(non_relayer_account, Default::default(), &relayer_registry, &key_repository); + + //then + assert!(result.is_err()) + } +} diff --git a/bitacross-worker/litentry/core/direct-call/src/lib.rs b/bitacross-worker/litentry/core/direct-call/src/lib.rs index e21e4f98ce..d2043ec53c 100644 --- a/bitacross-worker/litentry/core/direct-call/src/lib.rs +++ b/bitacross-worker/litentry/core/direct-call/src/lib.rs @@ -25,6 +25,7 @@ use codec::{Decode, Encode}; use itp_stf_primitives::types::KeyPair; use litentry_primitives::{Identity, LitentryMultiSignature, ShardIdentifier}; use sp_io::hashing::blake2_256; +use std::vec::Vec; pub mod handler; @@ -50,6 +51,7 @@ impl DirectCallSigned { pub enum DirectCall { SignBitcoin(Identity, SignBitcoinPayload), SignEthereum(Identity, PrehashedEthereumMessage), + SignTon(Identity, Vec), CheckSignBitcoin(Identity), } @@ -58,6 +60,7 @@ impl DirectCall { match self { Self::SignBitcoin(signer, ..) => signer, Self::SignEthereum(signer, ..) => signer, + Self::SignTon(signer, ..) => signer, Self::CheckSignBitcoin(signer) => signer, } } diff --git a/bitacross-worker/service/src/cli.yml b/bitacross-worker/service/src/cli.yml index c311123924..f3764fbeef 100644 --- a/bitacross-worker/service/src/cli.yml +++ b/bitacross-worker/service/src/cli.yml @@ -165,7 +165,7 @@ subcommands: - wallet: about: Print the bitcoin and ethereum custodian wallet key information, only works in non-prod - init-wallet: - about: Init eth and btc wallets from BTC_KEY and ETH_KEY env variables, only works in non-prod + about: Init eth, btc, ton wallets from BTC_KEY, ETH_KEY, TON_KEY env variables, only works in non-prod - mrenclave: about: Dump mrenclave to stdout. base58 encoded. - init-shard: diff --git a/bitacross-worker/service/src/main_impl.rs b/bitacross-worker/service/src/main_impl.rs index 664dfd800f..f68e11a1ad 100644 --- a/bitacross-worker/service/src/main_impl.rs +++ b/bitacross-worker/service/src/main_impl.rs @@ -54,7 +54,10 @@ use litentry_primitives::extract_tcb_info_from_raw_dcap_quote; use crate::error::ServiceResult; use itc_parentchain::primitives::ParentchainId; use itp_types::parentchain::{AccountId, Balance}; -use sp_core::crypto::{AccountId32, Ss58Codec}; +use sp_core::{ + crypto::{AccountId32, Ss58Codec}, + Pair, +}; use sp_keyring::AccountKeyring; use sp_runtime::MultiSigner; use std::{ @@ -259,6 +262,11 @@ pub(crate) fn main() { let ethereum_keypair = enclave.get_ethereum_wallet_pair().unwrap(); println!("public : 0x{}", hex::encode(ethereum_keypair.public_bytes())); println!("private: 0x{}", hex::encode(ethereum_keypair.private_bytes())); + + println!("Ton wallet:"); + let ton_keypair = enclave.get_ton_wallet_pair().unwrap(); + println!("public : 0x{}", hex::encode(ton_keypair.public().0)); + println!("private: 0x{}", hex::encode(ton_keypair.seed())); } else if let Some(sub_matches) = matches.subcommand_matches("init-wallet") { println!("Initializing wallets"); enclave.init_wallets(config.data_dir().to_str().unwrap()).unwrap(); diff --git a/bitacross-worker/service/src/tests/mocks/enclave_api_mock.rs b/bitacross-worker/service/src/tests/mocks/enclave_api_mock.rs index 5b37ce280d..fb212ce886 100644 --- a/bitacross-worker/service/src/tests/mocks/enclave_api_mock.rs +++ b/bitacross-worker/service/src/tests/mocks/enclave_api_mock.rs @@ -105,6 +105,10 @@ impl EnclaveBase for EnclaveMock { unreachable!() } + fn get_ton_wallet_pair(&self) -> EnclaveResult { + unreachable!() + } + fn get_fingerprint(&self) -> EnclaveResult { Ok([1u8; MR_ENCLAVE_SIZE].into()) } diff --git a/pallets/bitacross/src/custodial_wallet.rs b/pallets/bitacross/src/custodial_wallet.rs index 9ce4e57db7..65870cd4ba 100644 --- a/pallets/bitacross/src/custodial_wallet.rs +++ b/pallets/bitacross/src/custodial_wallet.rs @@ -17,13 +17,15 @@ use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; -pub type PubKey = [u8; 33]; +pub type PubKey33 = [u8; 33]; +pub type PubKey32 = [u8; 32]; /// custodial wallet that each tee worker generates and holds #[derive(Encode, Decode, Clone, Default, Debug, PartialEq, Eq, TypeInfo)] pub struct CustodialWallet { - pub btc: Option, - pub eth: Option, + pub btc: Option, + pub eth: Option, + pub ton: Option, } impl CustodialWallet { @@ -34,4 +36,8 @@ impl CustodialWallet { pub fn has_eth(&self) -> bool { self.eth.is_some() } + + pub fn has_ton(&self) -> bool { + self.ton.is_some() + } } diff --git a/pallets/bitacross/src/lib.rs b/pallets/bitacross/src/lib.rs index e146d32d74..0289fb2a3a 100644 --- a/pallets/bitacross/src/lib.rs +++ b/pallets/bitacross/src/lib.rs @@ -70,8 +70,9 @@ pub mod pallet { AdminSet { new_admin: Option }, RelayerAdded { who: Identity }, RelayerRemoved { who: Identity }, - BtcWalletGenerated { pub_key: PubKey, account_id: T::AccountId }, - EthWalletGenerated { pub_key: PubKey }, + BtcWalletGenerated { pub_key: PubKey33, account_id: T::AccountId }, + EthWalletGenerated { pub_key: PubKey33 }, + TonWalletGenerated { pub_key: PubKey32 }, VaultRemoved { who: T::AccountId }, } @@ -82,6 +83,7 @@ pub mod pallet { UnsupportedRelayerType, BtcWalletAlreadyExist, EthWalletAlreadyExist, + TonWalletAlreadyExist, VaultNotExist, } @@ -166,7 +168,7 @@ pub mod pallet { #[pallet::weight(({195_000_000}, DispatchClass::Normal, Pays::No))] pub fn btc_wallet_generated( origin: OriginFor, - pub_key: PubKey, + pub_key: PubKey33, ) -> DispatchResultWithPostInfo { let tee_account = T::TEECallOrigin::ensure_origin(origin)?; Vault::::try_mutate(tee_account.clone(), |v| { @@ -181,7 +183,7 @@ pub mod pallet { #[pallet::weight(({195_000_000}, DispatchClass::Normal, Pays::No))] pub fn eth_wallet_generated( origin: OriginFor, - pub_key: PubKey, + pub_key: PubKey33, ) -> DispatchResultWithPostInfo { let tee_account = T::TEECallOrigin::ensure_origin(origin)?; Vault::::try_mutate(tee_account, |v| { @@ -192,9 +194,24 @@ pub mod pallet { }) } - // TODO: placeholder #[pallet::call_index(32)] #[pallet::weight(({195_000_000}, DispatchClass::Normal, Pays::No))] + pub fn ton_wallet_generated( + origin: OriginFor, + pub_key: PubKey32, + ) -> DispatchResultWithPostInfo { + let tee_account = T::TEECallOrigin::ensure_origin(origin)?; + Vault::::try_mutate(tee_account, |v| { + ensure!(!v.has_ton(), Error::::TonWalletAlreadyExist); + v.ton = Some(pub_key); + Self::deposit_event(Event::TonWalletGenerated { pub_key }); + Ok(Pays::No.into()) + }) + } + + // TODO: placeholder + #[pallet::call_index(33)] + #[pallet::weight(({195_000_000}, DispatchClass::Normal, Pays::No))] pub fn task_complete(origin: OriginFor) -> DispatchResultWithPostInfo { let _ = T::TEECallOrigin::ensure_origin(origin)?; Ok(Pays::No.into())