Skip to content

Commit

Permalink
Merge branch 'development' into core-prune-inputs
Browse files Browse the repository at this point in the history
* development:
  fix: seed word parsing (#3607)
  feat: add ban peers metric (#3605)
  feat!: expose reason for transaction cancellation for callback in wallet_ffi (#3601)
  test: improve cucumber scenario robustness (#3599)
  feat: use CipherSeed wallet birthday for recovery start point (#3602)
  docs: ignore RFC code blocks (#3603)
  • Loading branch information
sdbondi committed Nov 23, 2021
2 parents 31a5b5b + fff45db commit 6440fa4
Show file tree
Hide file tree
Showing 34 changed files with 513 additions and 184 deletions.
16 changes: 8 additions & 8 deletions RFC/src/RFC-0250_Covenants.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ before being executed.

For instance,

```
```ignore
xor(
filter_output_hash_eq(Hash(0e0411c70df0ea4243a363fcbf161ebe6e2c1f074faf1c6a316a386823c3753c)),
filter_relative_height(10),
Expand All @@ -300,7 +300,7 @@ xor(

is represented in hex bytes as `23 30 01 a8b3f48e39449e89f7ff699b3eb2b080a2479b09a600a19d8ba48d765fe5d47d 35 07 0a`.
Let's unpack that as follows:
```
```ignore
23 // xor - consume two covenant args
30 // filter_output_hash_eq - consume a hash arg
01 // 32-byte hash
Expand Down Expand Up @@ -365,7 +365,7 @@ one or more outputs.

Spend within 10 blocks or burn

```
```ignore
not(filter_relative_height(10))
```

Expand All @@ -377,13 +377,13 @@ the miner.
Output features as detailed in [RFC-310-AssetImplementation] (early draft stages, still to be finalised) contain the
NFT details. This covenant preserves both the covenant protecting the token, and the token itself.

```
```ignore
filter_fields_preserved([field::features, field::covenant])
```

### Side-chain checkpointing

```
```ignore
and(
filter_field_int_eq(field::feature_flags, 16) // SIDECHAIN CHECKPOINT = 16
filter_fields_preserved([field::features, field::covenant, field::script])
Expand All @@ -392,7 +392,7 @@ and(

### Restrict spending to a particular commitment if not spent within 100 blocks

```
```ignore
or(
not(filter_relative_height(100)),
filter_fields_hashed_eq([field::commmitment], Hash(xxxx))
Expand All @@ -401,7 +401,7 @@ or(

### Output must preserve covenant, features and script or be burnt

```
```ignore
xor(
filter_fields_preserved([field::features, field::covenant, field::script]),
and(
Expand All @@ -413,7 +413,7 @@ xor(

### Commission for NFT transfer

```
```ignore
// Must be different outputs
xor(
and(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ impl WalletEventMonitor {
self.trigger_balance_refresh();
notifier.transaction_mined(tx_id);
},
TransactionEvent::TransactionCancelled(tx_id) => {
TransactionEvent::TransactionCancelled(tx_id, _) => {
self.trigger_tx_state_refresh(tx_id).await;
self.trigger_balance_refresh();
notifier.transaction_cancelled(tx_id);
Expand Down
3 changes: 3 additions & 0 deletions base_layer/core/src/base_node/sync/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ pub trait BaseNodeSyncService: Send + Sync + 'static {

#[rpc(method = 8)]
async fn sync_utxos(&self, request: Request<SyncUtxosRequest>) -> Result<Streaming<SyncUtxosResponse>, RpcStatus>;

#[rpc(method = 9)]
async fn get_height_at_time(&self, request: Request<u64>) -> Result<Response<u64>, RpcStatus>;
}

#[cfg(feature = "base_node")]
Expand Down
55 changes: 55 additions & 0 deletions base_layer/core/src/base_node/sync/rpc/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -506,4 +506,59 @@ impl<B: BlockchainBackend + 'static> BaseNodeSyncService for BaseNodeSyncRpcServ

Ok(Streaming::new(rx))
}

async fn get_height_at_time(&self, request: Request<u64>) -> Result<Response<u64>, RpcStatus> {
let requested_epoch_time: u64 = request.into_message();

let tip_header = self
.db()
.fetch_tip_header()
.await
.map_err(RpcStatus::log_internal_error(LOG_TARGET))?;
let mut left_height = 0u64;
let mut right_height = tip_header.height();

while left_height <= right_height {
let mut mid_height = (left_height + right_height) / 2;

if mid_height == 0 {
return Ok(Response::new(0u64));
}
// If the two bounds are adjacent then perform the test between the right and left sides
if left_height == mid_height {
mid_height = right_height;
}

let mid_header = self
.db()
.fetch_header(mid_height)
.await
.map_err(RpcStatus::log_internal_error(LOG_TARGET))?
.ok_or_else(|| {
RpcStatus::not_found(format!("Header not found during search at height {}", mid_height))
})?;
let before_mid_header = self
.db()
.fetch_header(mid_height - 1)
.await
.map_err(RpcStatus::log_internal_error(LOG_TARGET))?
.ok_or_else(|| {
RpcStatus::not_found(format!("Header not found during search at height {}", mid_height - 1))
})?;

if requested_epoch_time < mid_header.timestamp.as_u64() &&
requested_epoch_time >= before_mid_header.timestamp.as_u64()
{
return Ok(Response::new(before_mid_header.height));
} else if mid_height == right_height {
return Ok(Response::new(right_height));
} else if requested_epoch_time <= mid_header.timestamp.as_u64() {
right_height = mid_height;
} else {
left_height = mid_height;
}
}

Ok(Response::new(0u64))
}
}
76 changes: 71 additions & 5 deletions base_layer/core/tests/base_node_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::convert::TryFrom;
use std::{convert::TryFrom, sync::Arc, time::Duration};

use randomx_rs::RandomXFlag;
use tempfile::{tempdir, TempDir};
Expand All @@ -61,6 +61,8 @@ use tari_core::{
},
rpc::{BaseNodeWalletRpcService, BaseNodeWalletService},
state_machine_service::states::{ListeningInfo, StateInfo, StatusInfo},
sync::rpc::BaseNodeSyncRpcService,
BaseNodeSyncService,
},
blocks::ChainBlock,
consensus::{ConsensusManager, ConsensusManagerBuilder, NetworkConsensus},
Expand All @@ -80,14 +82,15 @@ use tari_core::{
};

use crate::helpers::{
block_builders::{chain_block, create_genesis_block_with_coinbase_value},
block_builders::{chain_block, chain_block_with_new_coinbase, create_genesis_block_with_coinbase_value},
nodes::{BaseNodeBuilder, NodeInterfaces},
};

mod helpers;

async fn setup() -> (
BaseNodeWalletRpcService<TempDatabase>,
BaseNodeSyncRpcService<TempDatabase>,
NodeInterfaces,
RpcRequestMock,
ConsensusManager,
Expand Down Expand Up @@ -118,13 +121,15 @@ async fn setup() -> (
});

let request_mock = RpcRequestMock::new(base_node.comms.peer_manager());
let service = BaseNodeWalletRpcService::new(
let wallet_service = BaseNodeWalletRpcService::new(
base_node.blockchain_db.clone().into(),
base_node.mempool_handle.clone(),
base_node.state_machine_handle.clone(),
);
let base_node_service = BaseNodeSyncRpcService::new(base_node.blockchain_db.clone().into());
(
service,
wallet_service,
base_node_service,
base_node,
request_mock,
consensus_manager,
Expand All @@ -138,7 +143,7 @@ async fn setup() -> (
#[allow(clippy::identity_op)]
async fn test_base_node_wallet_rpc() {
// Testing the submit_transaction() and transaction_query() rpc calls
let (service, mut base_node, request_mock, consensus_manager, block0, utxo0, _temp_dir) = setup().await;
let (service, _, mut base_node, request_mock, consensus_manager, block0, utxo0, _temp_dir) = setup().await;

let (txs1, utxos1) = schema_to_transaction(&[txn_schema!(from: vec![utxo0.clone()], to: vec![1 * T, 1 * T])]);
let tx1 = (*txs1[0]).clone();
Expand Down Expand Up @@ -290,3 +295,64 @@ async fn test_base_node_wallet_rpc() {
.any(|u| u.as_transaction_output(&factories).unwrap().commitment == output.commitment));
}
}

#[tokio::test]
async fn test_get_height_at_time() {
let factories = CryptoFactories::default();

let (_, service, base_node, request_mock, consensus_manager, block0, _utxo0, _temp_dir) = setup().await;

let mut prev_block = block0.clone();
let mut times = Vec::new();
times.push(prev_block.header().timestamp);
for _ in 0..10 {
tokio::time::sleep(Duration::from_secs(2)).await;
let new_block = base_node
.blockchain_db
.prepare_new_block(chain_block_with_new_coinbase(&prev_block, vec![], &consensus_manager, &factories).0)
.unwrap();

prev_block = base_node
.blockchain_db
.add_block(Arc::new(new_block))
.unwrap()
.assert_added();
times.push(prev_block.header().timestamp);
}

let req = request_mock.request_with_context(Default::default(), times[0].as_u64() - 100);
let resp = service.get_height_at_time(req).await.unwrap().into_message();
assert_eq!(resp, 0);

let req = request_mock.request_with_context(Default::default(), times[0].as_u64());
let resp = service.get_height_at_time(req).await.unwrap().into_message();
assert_eq!(resp, 0);

let req = request_mock.request_with_context(Default::default(), times[0].as_u64() + 1);
let resp = service.get_height_at_time(req).await.unwrap().into_message();
assert_eq!(resp, 0);

let req = request_mock.request_with_context(Default::default(), times[7].as_u64());
let resp = service.get_height_at_time(req).await.unwrap().into_message();
assert_eq!(resp, 7);

let req = request_mock.request_with_context(Default::default(), times[7].as_u64() - 1);
let resp = service.get_height_at_time(req).await.unwrap().into_message();
assert_eq!(resp, 6);

let req = request_mock.request_with_context(Default::default(), times[7].as_u64() + 1);
let resp = service.get_height_at_time(req).await.unwrap().into_message();
assert_eq!(resp, 7);

let req = request_mock.request_with_context(Default::default(), times[10].as_u64());
let resp = service.get_height_at_time(req).await.unwrap().into_message();
assert_eq!(resp, 10);

let req = request_mock.request_with_context(Default::default(), times[10].as_u64() - 1);
let resp = service.get_height_at_time(req).await.unwrap().into_message();
assert_eq!(resp, 9);

let req = request_mock.request_with_context(Default::default(), times[10].as_u64() + 1);
let resp = service.get_height_at_time(req).await.unwrap().into_message();
assert_eq!(resp, 10);
}
12 changes: 10 additions & 2 deletions base_layer/key_manager/src/cipher_seed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ pub const CIPHER_SEED_MAC_BYTES: usize = 5;
pub struct CipherSeed {
version: u8,
birthday: u16,
pub entropy: [u8; CIPHER_SEED_ENTROPY_BYTES],
entropy: [u8; CIPHER_SEED_ENTROPY_BYTES],
salt: [u8; CIPHER_SEED_SALT_BYTES],
}

Expand All @@ -108,7 +108,7 @@ impl CipherSeed {

pub fn encipher(&self, passphrase: Option<String>) -> Result<Vec<u8>, KeyManagerError> {
let mut plaintext = self.birthday.to_le_bytes().to_vec();
plaintext.append(&mut self.entropy.clone().to_vec());
plaintext.append(&mut self.entropy().clone().to_vec());

let passphrase = passphrase.unwrap_or_else(|| DEFAULT_CIPHER_SEED_PASSPHRASE.to_string());

Expand Down Expand Up @@ -236,6 +236,14 @@ impl CipherSeed {

Ok(())
}

pub fn entropy(&self) -> [u8; CIPHER_SEED_ENTROPY_BYTES] {
self.entropy
}

pub fn birthday(&self) -> u16 {
self.birthday
}
}

impl Drop for CipherSeed {
Expand Down
4 changes: 2 additions & 2 deletions base_layer/key_manager/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ pub enum MnemonicError {
defined natural languages"
)]
UnknownLanguage,
#[error("Only 2048 words for each language was selected to form Mnemonic word lists")]
WordNotFound,
#[error("Word not found: `{0}`")]
WordNotFound(String),
#[error("A mnemonic word does not exist for the requested index")]
IndexOutOfBounds,
#[error("A problem encountered constructing a secret key from bytes or mnemonic sequence: `{0}`")]
Expand Down
2 changes: 1 addition & 1 deletion base_layer/key_manager/src/key_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ where

/// Derive a new private key from master key: derived_key=SHA256(master_key||branch_seed||index)
pub fn derive_key(&self, key_index: u64) -> Result<DerivedKey<K>, ByteArrayError> {
let concatenated = format!("{}{}", self.seed.entropy.to_vec().to_hex(), key_index.to_string());
let concatenated = format!("{}{}", self.seed.entropy().to_vec().to_hex(), key_index.to_string());
match K::from_bytes(D::digest(&concatenated.into_bytes()).as_slice()) {
Ok(k) => Ok(DerivedKey { k, key_index }),
Err(e) => Err(e),
Expand Down
Loading

0 comments on commit 6440fa4

Please sign in to comment.