Skip to content

Conversation

@murerfel
Copy link
Contributor

@murerfel murerfel commented Nov 9, 2021

This is the next refactoring step for #301:

  • Extract the composition of extrinsics to a separate extrinsics-factory crate
  • Introduce a new nonce-cache crate to handle the enclave internal nonce
  • Extend the light client to send extrinsics to the parentchain

This is done because in the sidechain we need to compose and send extrinsics to the parentchain and so we need those parts of the code out of the enclave-runtime.

Closes #429

@murerfel murerfel self-assigned this Nov 9, 2021
Comment on lines +4 to +38
"app-libs/stf",
"cli",
"core/light-client",
"core/direct-rpc-server",
"core/tls-websocket-server",
"core/light-client",
"core/rest-client",
"core/rpc-client",
"core/rpc-server",
"core/tls-websocket-server",
"core-primitives/api-client-extensions",
"core-primitives/enclave-api",
"core-primitives/enclave-api/ffi",
"core-primitives/types",
"core-primitives/extrinsics-factory",
"core-primitives/nonce-cache",
"core-primitives/ocall-api",
"core-primitives/storage-verified",
"core-primitives/teerex-storage",
"core-primitives/settings",
"core-primitives/sgx/crypto",
"core-primitives/sgx/io",
"core-primitives/stf-executor",
"core-primitives/stf-state-handler",
"core-primitives/storage",
"core-primitives/storage-verified",
"core-primitives/teerex-storage",
"core-primitives/test",
"core-primitives/types",
"sidechain/sidechain-crate",
"sidechain/validateer-fetch",
"sidechain/primitives",
"service",
"sidechain/consensus/aura",
"sidechain/consensus/common",
"sidechain/consensus/slots",
"sidechain/primitives",
"sidechain/rpc-handler",
"sidechain/sidechain-crate",
"sidechain/state",
"sidechain/top-pool",
"sidechain/top-pool-rpc-author",
"app-libs/stf",
"service",
"core/rpc-client",
"core/rpc-server",
# "enclave",
"sidechain/validateer-fetch",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-ordered the members alphabetically

@murerfel murerfel requested review from clangenb and haerdib November 9, 2021 09:20
Comment on lines +44 to +49
/// Create extrinsics from opaque calls
///
/// Also increases the nonce counter for each extrinsic that is created.
pub trait CreateExtrinsics {
fn create_extrinsics(&self, calls: &[OpaqueCall]) -> Result<Vec<OpaqueExtrinsic>>;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the new trait to create extrinsics, transforming OpaqueCall into OpaqueExtrinsic

Comment on lines +81 to +107
let mut nonce_lock = self.nonce_cache.load_for_mutation()?;
let mut nonce_value = nonce_lock.0;

let extrinsics_buffer: Vec<OpaqueExtrinsic> = calls
.iter()
.map(|call| {
let xt = compose_extrinsic_offline!(
self.signer.clone(),
call,
nonce_value,
Era::Immortal,
self.genesis_hash,
self.genesis_hash,
RUNTIME_SPEC_VERSION,
RUNTIME_TRANSACTION_VERSION
)
.encode();
nonce_value += 1;
xt
})
.map(|xt| {
OpaqueExtrinsic::from_bytes(&xt)
.expect("A previously encoded extrinsic has valid codec; qed.")
})
.collect();

*nonce_lock = Nonce(nonce_value);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Retrieves the nonce write lock, creates all the extrinsics and increments the nonce value for each of them. Then at the end, write the nonce value back into the cache.
The reason we have a strict nonce lock here, is that we want to prevent any other concurrent process to get or write the nonce while we're incrementing it here.

Comment on lines +135 to +153
// #[test]
// pub fn xts_have_increasing_nonce() {
// let nonce_cache = Arc::new(NonceCache::default());
// nonce_cache.set_nonce(Nonce(34)).unwrap();
// let extrinsics_factory =
// ExtrinsicsFactory::new(test_genesis_hash(), test_account(), nonce_cache);
//
// let opaque_calls =
// [OpaqueCall(vec![3u8; 42]), OpaqueCall(vec![12u8, 78]), OpaqueCall(vec![15u8, 12])];
// let xts: Vec<UncheckedExtrinsicV4<OpaqueCall>> = extrinsics_factory
// .create_extrinsics(&opaque_calls)
// .unwrap()
// .iter()
// .map(|mut x| UncheckedExtrinsicV4::<OpaqueCall>::decode(&mut x))
// .collect();
//
// assert_eq!(xts.len(), opaque_calls.len());
// assert_eq!(xts[0].signature.unwrap().2 .2, 34u128);
// }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to write a test where I create extrinsics and then check if the nonce in the resulting extrinsics is correct. Unfortunately, the nonce in the extrinsic is in a private member that I cannot access here 😞

Copy link
Contributor

@clangenb clangenb Nov 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to be hardcore, you could check the relevant bits in the encoded extrinsics. :D.

Anyhow, we can make the nonce public in the api-client. I don't see anything against that.

Comment on lines +44 to +49
lazy_static! {
/// Global instance of a nonce cache
///
/// Concurrent access is managed internally, using RW locks
pub static ref GLOBAL_NONCE_CACHE: Arc<NonceCache> = Default::default();
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The global nonce cache instance that we can use in the enclave. Lazy initialized.

pub mod error;
pub mod nonce_cache;

pub type NonceValue = u32;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NonceValue is an u32. I think there is an open issue where we want to connect this to another type definition Index?

Also I first tried to use u64 or even u128 to be more safe from overflows - but unfortunately deeper down in the extrinsics code, we use u32, so I'm forced to use u32 here too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I created the issue yesterday because I noticed the hardcoded u32 in PR #487: #495.

I also think we shouldnt define the value here but rather in the settings or wherever we have our node primitves defined. But lets leave this task to the issue itself. This is atleast one step towards that goal, thanks.

Comment on lines +33 to +58
/// Local nonce cache
///
/// stores the nonce internally, protected by a RW lock for concurrent access
#[derive(Default)]
pub struct NonceCache {
nonce_lock: RwLock<Nonce>,
}

impl NonceCache {
pub fn new(nonce_lock: RwLock<Nonce>) -> Self {
NonceCache { nonce_lock }
}
}

impl MutateNonce for NonceCache {
fn load_for_mutation(&self) -> Result<RwLockWriteGuard<'_, Nonce>> {
self.nonce_lock.write().map_err(|_| Error::LockPoisoning)
}
}

impl GetNonce for NonceCache {
fn get_nonce(&self) -> Result<Nonce> {
let nonce_lock = self.nonce_lock.read().map_err(|_| Error::LockPoisoning)?;
Ok(*nonce_lock)
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nonce cache is basically a wrapper around a RwLock, with a bit of convenience added to it.

Comment on lines +106 to +111
fn send_extrinsics<OCallApi: EnclaveOnChainOCallApi>(
&mut self,
ocall_api: &OCallApi,
extrinsics: Vec<OpaqueExtrinsic>,
) -> Result<(), Error>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The light client is extended to have the send_extrinsics method. The OCallApi is passed as parameter, rather than having it as a member of the light client. The reason being, that the light client has to implement Encode and Decode, which will be more tricky with an Arc<OCallApi>.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add some description here? Like that it sends the encoded extrinsics to the parentchain?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, done 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a good design. 👍

Comment on lines -149 to -159
lazy_static! {
/// the enclave's parentchain nonce
///
/// This should be abstracted away better in the future. Currently, this only exists
/// because we produce sidechain blocks faster that parentchain chain blocks. So for now this
/// design suffices. Later, we also need to sync across parallel ecalls that might both result
/// in parentchain xt's. Then we should probably think about how we want to abstract the global
/// nonce.
static ref NONCE: SgxRwLock<u32> = SgxRwLock::new(Default::default());
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the global nonce value here, in favor of the nonce cache.


*nonce += 1;
*nonce_lock = Nonce(nonce_value + 1);
std::mem::drop(nonce_lock);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a small optimization: we drop the nonce write lock early, to minimize the time we're holding the lock.

Comment on lines +650 to +652
let genesis_hash = validator.genesis_hash(validator.num_relays())?;
let extrinsics_factory =
ExtrinsicsFactory::new(genesis_hash, signer, GLOBAL_NONCE_CACHE.clone());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now get the genesis hash from the light client when we crate an extrinsics factory

//! move most parts here to the sidechain crate.
use crate::{execute_top_pool_trusted_calls, prepare_and_send_xts, Result as EnclaveResult};
use crate::{execute_top_pool_trusted_calls, Result as EnclaveResult};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reason we're doing this refactoring: refactor and remove the prepare_and_send_xts function from the enclave-runtime, so we can move this sidechain_impl code back into the sidechain crate.

(We still have the execute_top_pool_trusted_calls function in enclave-runtime - that will be refactored in the next PR)

@murerfel murerfel force-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 11a7a2e to 40d8f29 Compare November 9, 2021 09:54

/// Create extrinsics from opaque calls
///
/// Also increases the nonce counter for each extrinsic that is created.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh, thanks alot for that one ❤️

[features]
default = ["std"]
std = [
"thiserror",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"thiserror",
"thiserror",
"log/std"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done 👍

pub mod error;
pub mod nonce_cache;

pub type NonceValue = u32;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I created the issue yesterday because I noticed the hardcoded u32 in PR #487: #495.

I also think we shouldnt define the value here but rather in the settings or wherever we have our node primitves defined. But lets leave this task to the issue itself. This is atleast one step towards that goal, thanks.

Comment on lines +106 to +111
fn send_extrinsics<OCallApi: EnclaveOnChainOCallApi>(
&mut self,
ocall_api: &OCallApi,
extrinsics: Vec<OpaqueExtrinsic>,
) -> Result<(), Error>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add some description here? Like that it sends the encoded extrinsics to the parentchain?

Base automatically changed from feature/fm-separate-send-xts-send-blocks to master November 9, 2021 11:07
@murerfel murerfel force-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 40d8f29 to 709bb97 Compare November 10, 2021 09:04
Copy link
Contributor

@clangenb clangenb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very good, only some minor questions.

Comment on lines +135 to +153
// #[test]
// pub fn xts_have_increasing_nonce() {
// let nonce_cache = Arc::new(NonceCache::default());
// nonce_cache.set_nonce(Nonce(34)).unwrap();
// let extrinsics_factory =
// ExtrinsicsFactory::new(test_genesis_hash(), test_account(), nonce_cache);
//
// let opaque_calls =
// [OpaqueCall(vec![3u8; 42]), OpaqueCall(vec![12u8, 78]), OpaqueCall(vec![15u8, 12])];
// let xts: Vec<UncheckedExtrinsicV4<OpaqueCall>> = extrinsics_factory
// .create_extrinsics(&opaque_calls)
// .unwrap()
// .iter()
// .map(|mut x| UncheckedExtrinsicV4::<OpaqueCall>::decode(&mut x))
// .collect();
//
// assert_eq!(xts.len(), opaque_calls.len());
// assert_eq!(xts[0].signature.unwrap().2 .2, 34u128);
// }
Copy link
Contributor

@clangenb clangenb Nov 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to be hardcore, you could check the relevant bits in the encoded extrinsics. :D.

Anyhow, we can make the nonce public in the api-client. I don't see anything against that.

Comment on lines +106 to +111
fn send_extrinsics<OCallApi: EnclaveOnChainOCallApi>(
&mut self,
ocall_api: &OCallApi,
extrinsics: Vec<OpaqueExtrinsic>,
) -> Result<(), Error>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a good design. 👍

Comment on lines +726 to 729
let xts = extrinsics_factory.create_extrinsics(calls.as_slice())?;

ocall_api
.send_to_parentchain(extrinsics)
.map_err(|e| Error::Other(format!("Failed to send extrinsics: {}", e).into()))?;
validator.send_extrinsics(on_chain_ocall_api, xts)?;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very nice now. :)


pub type NonceValue = u32;

/// Nonce type (newtype wrapper for u64)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't understand the purpose here. Can you please explain it to me? 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean the purpose of a newtype for the Nonce? My intention was to have a strong type for the nonce. So in cases where we have other u32 values/parameters, it prevents mixing them by accident. Imagine a function that has 2 parameters:

fn do_work(nonce: u32, my_counter: u32);

When I call this function, it can happen that I mix up the two parameters, because their types are interchangeable. If I have a newtype for the nonce, that cannot happen.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I see now that the comment is not correct anymore, since we don't have a u64 😛 An example where comments can get out of sync with the code very easily.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I am much in favor of using strong typing here :). However, the comment was indeed misleading. 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah so you're initial question was regarding the comment? Sorry I misunderstood then 😄 The comment is now fixed in the latest commit.

@murerfel murerfel requested review from clangenb and haerdib November 10, 2021 13:41
Copy link
Contributor

@clangenb clangenb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! We are making some serious progress here. :)

@murerfel murerfel merged commit e959c9c into master Nov 10, 2021
@murerfel murerfel deleted the feature/fm-extrinsics-factory-nonce-cache branch November 10, 2021 13:49
echevrier pushed a commit that referenced this pull request Nov 15, 2021
author echevrier <[email protected]> 1633529369 +0200
committer echevrier <[email protected]> 1636648987 +0100

Exchange Rate Oracle Showcase

Add exchange-oracle crate and implement get_exchange_rate for coinGecko

Clean according review

Call update market data (Exchange rate) from enclave

Rename pallet market to teeracle

cargo fmt

Set MARKET_DATA_UPDATE_INTERVAL to 24h

[GA] bump node artifact

Changes from review

Set substrate-fixed version to 0.5.6 (before update to new substrate)

Changes from review

Rebase

Changes due to review

Rebase to get PR #498 to rework code according review
haerdib pushed a commit that referenced this pull request Nov 17, 2021
…ko (#442)

author echevrier <[email protected]> 1633529369 +0200
committer echevrier <[email protected]> 1636648987 +0100

Exchange Rate Oracle Showcase

Add exchange-oracle crate and implement get_exchange_rate for coinGecko

Clean according review

Call update market data (Exchange rate) from enclave

Rename pallet market to teeracle

cargo fmt

Set MARKET_DATA_UPDATE_INTERVAL to 24h

[GA] bump node artifact

Changes from review

Set substrate-fixed version to 0.5.6 (before update to new substrate)

Changes from review

Rebase

Changes due to review

Rebase to get PR #498 to rework code according review

* Rebase

* cargo clippy and catch error in execute_update_market to prevent that the thread is stopped

* settings for demo

* update CI because this branch will not be merged in master, but in exchange_rate_oracle branch

* changes from review

* changes from review

* Remove reference to light client for oracle

* Set update interval for exchange range to 24h

* Changes review

* Changes review

* Cargo clippy

* Changes from review: improve variable names

Co-authored-by: echevrier <[email protected]>
echevrier added a commit that referenced this pull request Dec 9, 2021
…ko (#442)

author echevrier <[email protected]> 1633529369 +0200
committer echevrier <[email protected]> 1636648987 +0100

Exchange Rate Oracle Showcase

Add exchange-oracle crate and implement get_exchange_rate for coinGecko

Clean according review

Call update market data (Exchange rate) from enclave

Rename pallet market to teeracle

cargo fmt

Set MARKET_DATA_UPDATE_INTERVAL to 24h

[GA] bump node artifact

Changes from review

Set substrate-fixed version to 0.5.6 (before update to new substrate)

Changes from review

Rebase

Changes due to review

Rebase to get PR #498 to rework code according review

* Rebase

* cargo clippy and catch error in execute_update_market to prevent that the thread is stopped

* settings for demo

* update CI because this branch will not be merged in master, but in exchange_rate_oracle branch

* changes from review

* changes from review

* Remove reference to light client for oracle

* Set update interval for exchange range to 24h

* Changes review

* Changes review

* Cargo clippy

* Changes from review: improve variable names

Co-authored-by: echevrier <[email protected]>
echevrier added a commit that referenced this pull request Dec 16, 2021
Add exchange-oracle crate and implement get_exchange_rate for coinGecko

Clean according review

Call update market data (Exchange rate) from enclave

Rename pallet market to teeracle

cargo fmt

Set MARKET_DATA_UPDATE_INTERVAL to 24h

[GA] bump node artifact

Changes from review

Set substrate-fixed version to 0.5.6 (before update to new substrate)

Changes from review

Rebase

Changes due to review

Rebase to get PR #498 to rework code according review

* Rebase

* cargo clippy and catch error in execute_update_market to prevent that the thread is stopped

* settings for demo

* update CI because this branch will not be merged in master, but in exchange_rate_oracle branch

* changes from review

* changes from review

* Remove reference to light client for oracle

* Set update interval for exchange range to 24h

* Changes review

* Changes review

* Cargo clippy

* Changes from review: improve variable names

Co-authored-by: echevrier <[email protected]>

Cargo test fix
echevrier added a commit that referenced this pull request Dec 16, 2021
Add exchange-oracle crate and implement get_exchange_rate for coinGecko

Clean according review

Call update market data (Exchange rate) from enclave

Rename pallet market to teeracle

cargo fmt

Set MARKET_DATA_UPDATE_INTERVAL to 24h

[GA] bump node artifact

Changes from review

Set substrate-fixed version to 0.5.6 (before update to new substrate)

Changes from review

Rebase

Changes due to review

Rebase to get PR #498 to rework code according review

* Rebase

* cargo clippy and catch error in execute_update_market to prevent that the thread is stopped

* settings for demo

* update CI because this branch will not be merged in master, but in exchange_rate_oracle branch

* changes from review

* changes from review

* Remove reference to light client for oracle

* Set update interval for exchange range to 24h

* Changes review

* Changes review

* Cargo clippy

* Changes from review: improve variable names

Co-authored-by: echevrier <[email protected]>

Cargo test fix
echevrier added a commit that referenced this pull request Dec 16, 2021
Add exchange-oracle crate and implement get_exchange_rate for coinGecko

Clean according review

Call update market data (Exchange rate) from enclave

Rename pallet market to teeracle

cargo fmt

Set MARKET_DATA_UPDATE_INTERVAL to 24h

[GA] bump node artifact

Changes from review

Set substrate-fixed version to 0.5.6 (before update to new substrate)

Changes from review

Rebase

Changes due to review

Rebase to get PR #498 to rework code according review

* Rebase

* cargo clippy and catch error in execute_update_market to prevent that the thread is stopped

* settings for demo

* update CI because this branch will not be merged in master, but in exchange_rate_oracle branch

* changes from review

* changes from review

* Remove reference to light client for oracle

* Set update interval for exchange range to 24h

* Changes review

* Changes review

* Cargo clippy

* Changes from review: improve variable names

Co-authored-by: echevrier <[email protected]>

Cargo test fix
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[enclave-runtime] Enclave extrinsic factory.

4 participants