Extract a new extrinsics factory and nonce cache crate - #498

Merged
murerfel merged 4 commits into
masterfrom
feature/fm-extrinsics-factory-nonce-cache
Nov 10, 2021
Merged

Extract a new extrinsics factory and nonce cache crate#498
murerfel merged 4 commits into
masterfrom
feature/fm-extrinsics-factory-nonce-cache

Conversation

@murerfel

@murerfelmurerfel commented Nov 9, 2021

Copy link
Copy Markdown
Contributor

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

@murerfelmurerfel self-assigned this Nov 9, 2021
Comment threadCargo.toml
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
Copy Markdown
ContributorAuthor

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

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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 😞

@clangenbclangenbNov 10, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

yes, done 👍

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
murerfelforce-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 11a7a2e to 40d8f29CompareNovember 9, 2021 09:54

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
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 masterNovember 9, 2021 11:07
@murerfel
murerfelforce-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 40d8f29 to 709bb97CompareNovember 10, 2021 09:04

@clangenbclangenb left a comment

Copy link
Copy Markdown
Contributor

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);
// }

@clangenbclangenbNov 10, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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.

@clangenbclangenb left a comment

Copy link
Copy Markdown
Contributor

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 masterNov 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 <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch>
echevrier added a commit that referenced this pull request Dec 9, 2021
…ko (#442)
author echevrier <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
Cargo test fix
Sign up for freeto 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.

3 participants

@murerfel@clangenb@haerdib
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Extract a new extrinsics factory and nonce cache crate - #498

Merged
murerfel merged 4 commits into
masterfrom
feature/fm-extrinsics-factory-nonce-cache
Nov 10, 2021
Merged

Extract a new extrinsics factory and nonce cache crate#498
murerfel merged 4 commits into
masterfrom
feature/fm-extrinsics-factory-nonce-cache

Conversation

@murerfel

@murerfelmurerfel commented Nov 9, 2021

Copy link
Copy Markdown
Contributor

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

@murerfelmurerfel self-assigned this Nov 9, 2021
Comment threadCargo.toml
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
Copy Markdown
ContributorAuthor

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

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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 😞

@clangenbclangenbNov 10, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

yes, done 👍

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
murerfelforce-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 11a7a2e to 40d8f29CompareNovember 9, 2021 09:54

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
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 masterNovember 9, 2021 11:07
@murerfel
murerfelforce-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 40d8f29 to 709bb97CompareNovember 10, 2021 09:04

@clangenbclangenb left a comment

Copy link
Copy Markdown
Contributor

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);
// }

@clangenbclangenbNov 10, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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.

@clangenbclangenb left a comment

Copy link
Copy Markdown
Contributor

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 masterNov 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 <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch>
echevrier added a commit that referenced this pull request Dec 9, 2021
…ko (#442)
author echevrier <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
Cargo test fix
Sign up for freeto 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.

3 participants

@murerfel@clangenb@haerdib
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Extract a new extrinsics factory and nonce cache crate - #498

Merged
murerfel merged 4 commits into
masterfrom
feature/fm-extrinsics-factory-nonce-cache
Nov 10, 2021
Merged

Extract a new extrinsics factory and nonce cache crate#498
murerfel merged 4 commits into
masterfrom
feature/fm-extrinsics-factory-nonce-cache

Conversation

@murerfel

@murerfelmurerfel commented Nov 9, 2021

Copy link
Copy Markdown
Contributor

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

@murerfelmurerfel self-assigned this Nov 9, 2021
Comment threadCargo.toml
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
Copy Markdown
ContributorAuthor

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

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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 😞

@clangenbclangenbNov 10, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

yes, done 👍

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
murerfelforce-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 11a7a2e to 40d8f29CompareNovember 9, 2021 09:54

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
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 masterNovember 9, 2021 11:07
@murerfel
murerfelforce-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 40d8f29 to 709bb97CompareNovember 10, 2021 09:04

@clangenbclangenb left a comment

Copy link
Copy Markdown
Contributor

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);
// }

@clangenbclangenbNov 10, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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.

@clangenbclangenb left a comment

Copy link
Copy Markdown
Contributor

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 masterNov 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 <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch>
echevrier added a commit that referenced this pull request Dec 9, 2021
…ko (#442)
author echevrier <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
Cargo test fix
Sign up for freeto 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.

3 participants

@murerfel@clangenb@haerdib
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Extract a new extrinsics factory and nonce cache crate - #498

Merged
murerfel merged 4 commits into
masterfrom
feature/fm-extrinsics-factory-nonce-cache
Nov 10, 2021
Merged

Extract a new extrinsics factory and nonce cache crate#498
murerfel merged 4 commits into
masterfrom
feature/fm-extrinsics-factory-nonce-cache

Conversation

@murerfel

@murerfelmurerfel commented Nov 9, 2021

Copy link
Copy Markdown
Contributor

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

@murerfelmurerfel self-assigned this Nov 9, 2021
Comment threadCargo.toml
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
Copy Markdown
ContributorAuthor

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

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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 😞

@clangenbclangenbNov 10, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

yes, done 👍

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
murerfelforce-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 11a7a2e to 40d8f29CompareNovember 9, 2021 09:54

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
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 masterNovember 9, 2021 11:07
@murerfel
murerfelforce-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 40d8f29 to 709bb97CompareNovember 10, 2021 09:04

@clangenbclangenb left a comment

Copy link
Copy Markdown
Contributor

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);
// }

@clangenbclangenbNov 10, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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.

@clangenbclangenb left a comment

Copy link
Copy Markdown
Contributor

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 masterNov 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 <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch>
echevrier added a commit that referenced this pull request Dec 9, 2021
…ko (#442)
author echevrier <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
Cargo test fix
Sign up for freeto 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.

3 participants

@murerfel@clangenb@haerdib
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Extract a new extrinsics factory and nonce cache crate - #498

Merged
murerfel merged 4 commits into
masterfrom
feature/fm-extrinsics-factory-nonce-cache
Nov 10, 2021
Merged

Extract a new extrinsics factory and nonce cache crate#498
murerfel merged 4 commits into
masterfrom
feature/fm-extrinsics-factory-nonce-cache

Conversation

@murerfel

@murerfelmurerfel commented Nov 9, 2021

Copy link
Copy Markdown
Contributor

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

@murerfelmurerfel self-assigned this Nov 9, 2021
Comment threadCargo.toml
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
Copy Markdown
ContributorAuthor

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

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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 😞

@clangenbclangenbNov 10, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

yes, done 👍

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
murerfelforce-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 11a7a2e to 40d8f29CompareNovember 9, 2021 09:54

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
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 masterNovember 9, 2021 11:07
@murerfel
murerfelforce-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 40d8f29 to 709bb97CompareNovember 10, 2021 09:04

@clangenbclangenb left a comment

Copy link
Copy Markdown
Contributor

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);
// }

@clangenbclangenbNov 10, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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.

@clangenbclangenb left a comment

Copy link
Copy Markdown
Contributor

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 masterNov 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 <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch>
echevrier added a commit that referenced this pull request Dec 9, 2021
…ko (#442)
author echevrier <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
Cargo test fix
Sign up for freeto 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.

3 participants

@murerfel@clangenb@haerdib
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Extract a new extrinsics factory and nonce cache crate - #498

Merged
murerfel merged 4 commits into
masterfrom
feature/fm-extrinsics-factory-nonce-cache
Nov 10, 2021
Merged

Extract a new extrinsics factory and nonce cache crate#498
murerfel merged 4 commits into
masterfrom
feature/fm-extrinsics-factory-nonce-cache

Conversation

@murerfel

@murerfelmurerfel commented Nov 9, 2021

Copy link
Copy Markdown
Contributor

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

@murerfelmurerfel self-assigned this Nov 9, 2021
Comment threadCargo.toml
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
Copy Markdown
ContributorAuthor

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

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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 😞

@clangenbclangenbNov 10, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

yes, done 👍

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
murerfelforce-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 11a7a2e to 40d8f29CompareNovember 9, 2021 09:54

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
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 masterNovember 9, 2021 11:07
@murerfel
murerfelforce-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 40d8f29 to 709bb97CompareNovember 10, 2021 09:04

@clangenbclangenb left a comment

Copy link
Copy Markdown
Contributor

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);
// }

@clangenbclangenbNov 10, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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.

@clangenbclangenb left a comment

Copy link
Copy Markdown
Contributor

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 masterNov 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 <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch>
echevrier added a commit that referenced this pull request Dec 9, 2021
…ko (#442)
author echevrier <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
Cargo test fix
Sign up for freeto 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.

3 participants

@murerfel@clangenb@haerdib
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Extract a new extrinsics factory and nonce cache crate - #498

Merged
murerfel merged 4 commits into
masterfrom
feature/fm-extrinsics-factory-nonce-cache
Nov 10, 2021
Merged

Extract a new extrinsics factory and nonce cache crate#498
murerfel merged 4 commits into
masterfrom
feature/fm-extrinsics-factory-nonce-cache

Conversation

@murerfel

@murerfelmurerfel commented Nov 9, 2021

Copy link
Copy Markdown
Contributor

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

@murerfelmurerfel self-assigned this Nov 9, 2021
Comment threadCargo.toml
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
Copy Markdown
ContributorAuthor

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

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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 😞

@clangenbclangenbNov 10, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

yes, done 👍

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
murerfelforce-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 11a7a2e to 40d8f29CompareNovember 9, 2021 09:54

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
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 masterNovember 9, 2021 11:07
@murerfel
murerfelforce-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 40d8f29 to 709bb97CompareNovember 10, 2021 09:04

@clangenbclangenb left a comment

Copy link
Copy Markdown
Contributor

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);
// }

@clangenbclangenbNov 10, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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.

@clangenbclangenb left a comment

Copy link
Copy Markdown
Contributor

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 masterNov 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 <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch>
echevrier added a commit that referenced this pull request Dec 9, 2021
…ko (#442)
author echevrier <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
Cargo test fix
Sign up for freeto 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.

3 participants

@murerfel@clangenb@haerdib
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Extract a new extrinsics factory and nonce cache crate - #498

Merged
murerfel merged 4 commits into
masterfrom
feature/fm-extrinsics-factory-nonce-cache
Nov 10, 2021
Merged

Extract a new extrinsics factory and nonce cache crate#498
murerfel merged 4 commits into
masterfrom
feature/fm-extrinsics-factory-nonce-cache

Conversation

@murerfel

@murerfelmurerfel commented Nov 9, 2021

Copy link
Copy Markdown
Contributor

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

@murerfelmurerfel self-assigned this Nov 9, 2021
Comment threadCargo.toml
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
Copy Markdown
ContributorAuthor

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

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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 😞

@clangenbclangenbNov 10, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

yes, done 👍

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
murerfelforce-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 11a7a2e to 40d8f29CompareNovember 9, 2021 09:54

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
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 masterNovember 9, 2021 11:07
@murerfel
murerfelforce-pushed the feature/fm-extrinsics-factory-nonce-cache branch from 40d8f29 to 709bb97CompareNovember 10, 2021 09:04

@clangenbclangenb left a comment

Copy link
Copy Markdown
Contributor

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);
// }

@clangenbclangenbNov 10, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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.

@clangenbclangenb left a comment

Copy link
Copy Markdown
Contributor

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 masterNov 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 <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch>
echevrier added a commit that referenced this pull request Dec 9, 2021
…ko (#442)
author echevrier <edith.chevrier@scs.ch> 1633529369 +0200
committer echevrier <edith.chevrier@scs.ch> 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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
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 <edith.chevrier@scs.ch>
Cargo test fix
Sign up for freeto 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.

3 participants

@murerfel@clangenb@haerdib