Master merge 2.0.0 (#2927)

* create 2.0.0 branch

* fix humansize version

* update grin.yml version

* PoW HardFork (#2866)

* allow version 2 blocks for next 6 months

* add cuckarood.rs with working tests

* switch cuckaroo to cuckarood at right heights

* reorder to reduce conditions

* remove _ prefix on used args; fix typo

* Make Valid Header Version dependant on ChainType

* Rustfmt

* Add tests, uncomment header v2

* Rustfmt

* Add FLOONET_FIRST_HARD_FORK height and simplify logic

* assume floonet stays closer to avg 60s block time

* move floonet hf forward by half a day

* update version in new block when previous no longer valid

* my next commit:-)

* micro optimization

* Support new Bulletproof rewind scheme (#2848)

* Update keychain with new rewind scheme

* Refactor: proof builder trait

* Update tests, cleanup

* rustfmt

* Move conversion of SwitchCommitmentType

* Add proof build trait to tx builders

* Cache hashes in proof builders

* Proof builder tests

* Add ViewKey struct

* Fix some warnings

* Zeroize proof builder secrets on drop

* Modify mine_block to use wallet V2 API (#2892)

* update mine_block to use V2 wallet API

* rustfmt

* Add version endpoint to node API, rename pool/push (#2897)

* add node version API, tweak pool/push parameter

* rustfmt

* Upate version api call (#2899)

* Update version number for next (potential) release

* zeroize: Upgrade to v0.9 (#2914)

* zeroize: Upgrade to v0.9

* missed Cargo.lock

* [PENDING APPROVAL] put phase outs of C32 and beyond on hold (#2714)

* put phase outs of C32 and beyond on hold

* update tests for phaseouts on hold

* Don't wait for p2p-server thread (#2917)

Currently p2p.stop() stops and wait for all peers to exit, that's
basically all we need. However we also run a TCP listener in this thread
which is blocked on `accept` most of the time. We do an attempt to stop
it but it would work only if we get an incoming connection during the
shutdown, which is a week guarantee.

This fix remove joining to p2p-server thread, it stops all peers and
makes an attempt to stop the listener.

Fixes [#2906]

* rustfmt
This commit is contained in:
Yeastplume 2019-06-27 09:19:17 +01:00 committed by GitHub
parent fd6fe35777
commit 1609b041b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 2456 additions and 844 deletions

988
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[package]
name = "grin"
version = "1.1.1-beta.1"
version = "2.0.0-beta.2"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format."
license = "Apache-2.0"
@ -32,13 +32,13 @@ term = "0.5"
failure = "0.1"
failure_derive = "0.1"
grin_api = { path = "./api", version = "1.1.1-beta.1" }
grin_config = { path = "./config", version = "1.1.1-beta.1" }
grin_core = { path = "./core", version = "1.1.1-beta.1" }
grin_keychain = { path = "./keychain", version = "1.1.1-beta.1" }
grin_p2p = { path = "./p2p", version = "1.1.1-beta.1" }
grin_servers = { path = "./servers", version = "1.1.1-beta.1" }
grin_util = { path = "./util", version = "1.1.1-beta.1" }
grin_api = { path = "./api", version = "2.0.0-beta.2" }
grin_config = { path = "./config", version = "2.0.0-beta.2" }
grin_core = { path = "./core", version = "2.0.0-beta.2" }
grin_keychain = { path = "./keychain", version = "2.0.0-beta.2" }
grin_p2p = { path = "./p2p", version = "2.0.0-beta.2" }
grin_servers = { path = "./servers", version = "2.0.0-beta.2" }
grin_util = { path = "./util", version = "2.0.0-beta.2" }
[target.'cfg(windows)'.dependencies]
cursive = { version = "0.12", default-features = false, features = ["pancurses-backend"] }
@ -52,5 +52,5 @@ cursive = "0.12"
built = "0.3"
[dev-dependencies]
grin_chain = { path = "./chain", version = "1.1.1-beta.1" }
grin_store = { path = "./store", version = "1.1.1-beta.1" }
grin_chain = { path = "./chain", version = "2.0.0-beta.2" }
grin_store = { path = "./store", version = "2.0.0-beta.2" }

View file

@ -1,6 +1,6 @@
[package]
name = "grin_api"
version = "1.1.1-beta.1"
version = "2.0.0-beta.2"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "APIs for grin, a simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format."
license = "Apache-2.0"
@ -30,9 +30,9 @@ futures = "0.1.21"
rustls = "0.13"
url = "1.7.0"
grin_core = { path = "../core", version = "1.1.1-beta.1" }
grin_chain = { path = "../chain", version = "1.1.1-beta.1" }
grin_p2p = { path = "../p2p", version = "1.1.1-beta.1" }
grin_pool = { path = "../pool", version = "1.1.1-beta.1" }
grin_store = { path = "../store", version = "1.1.1-beta.1" }
grin_util = { path = "../util", version = "1.1.1-beta.1" }
grin_core = { path = "../core", version = "2.0.0-beta.2" }
grin_chain = { path = "../chain", version = "2.0.0-beta.2" }
grin_p2p = { path = "../p2p", version = "2.0.0-beta.2" }
grin_pool = { path = "../pool", version = "2.0.0-beta.2" }
grin_store = { path = "../store", version = "2.0.0-beta.2" }
grin_util = { path = "../util", version = "2.0.0-beta.2" }

View file

@ -19,6 +19,7 @@ mod pool_api;
mod server_api;
mod transactions_api;
mod utils;
mod version_api;
use self::blocks_api::BlockHandler;
use self::blocks_api::HeaderHandler;
@ -32,9 +33,10 @@ use self::peers_api::PeersConnectedHandler;
use self::pool_api::PoolInfoHandler;
use self::pool_api::PoolPushHandler;
use self::server_api::IndexHandler;
use self::server_api::StatusHandler;
use self::server_api::KernelDownloadHandler;
use self::server_api::StatusHandler;
use self::transactions_api::TxHashSetHandler;
use self::version_api::VersionHandler;
use crate::auth::{BasicAuthMiddleware, GRIN_BASIC_REALM};
use crate::chain;
use crate::p2p;
@ -104,12 +106,13 @@ pub fn build_router(
"get txhashset/outputs?start_index=1&max=100".to_string(),
"get txhashset/merkleproof?n=1".to_string(),
"get pool".to_string(),
"post pool/push".to_string(),
"post pool/push_tx".to_string(),
"post peers/a.b.c.d:p/ban".to_string(),
"post peers/a.b.c.d:p/unban".to_string(),
"get peers/all".to_string(),
"get peers/connected".to_string(),
"get peers/a.b.c.d".to_string(),
"get version".to_string(),
];
let index_handler = IndexHandler { list: route_list };
@ -157,6 +160,9 @@ pub fn build_router(
let peer_handler = PeerHandler {
peers: Arc::downgrade(&peers),
};
let version_handler = VersionHandler {
chain: Arc::downgrade(&chain),
};
let mut router = Router::new();
@ -171,9 +177,10 @@ pub fn build_router(
router.add_route("/v1/status", Arc::new(status_handler))?;
router.add_route("/v1/kerneldownload", Arc::new(kernel_download_handler))?;
router.add_route("/v1/pool", Arc::new(pool_info_handler))?;
router.add_route("/v1/pool/push", Arc::new(pool_push_handler))?;
router.add_route("/v1/pool/push_tx", Arc::new(pool_push_handler))?;
router.add_route("/v1/peers/all", Arc::new(peers_all_handler))?;
router.add_route("/v1/peers/connected", Arc::new(peers_connected_handler))?;
router.add_route("/v1/peers/**", Arc::new(peer_handler))?;
router.add_route("/v1/version", Arc::new(version_handler))?;
Ok(router)
}

View file

@ -53,7 +53,7 @@ struct TxWrapper {
}
/// Push new transaction to our local transaction pool.
/// POST /v1/pool/push
/// POST /v1/pool/push_tx
pub struct PoolPushHandler {
pub tx_pool: Weak<RwLock<pool::TransactionPool>>,
}

View file

@ -0,0 +1,49 @@
// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::utils::w;
use crate::chain;
use crate::rest::*;
use crate::router::{Handler, ResponseFuture};
use crate::types::Version;
use crate::web::*;
use hyper::{Body, Request};
use std::sync::Weak;
const CRATE_VERSION: &'static str = env!("CARGO_PKG_VERSION");
/// Version handler. Get running node API version
/// GET /v1/version
pub struct VersionHandler {
pub chain: Weak<chain::Chain>,
}
impl VersionHandler {
fn get_version(&self) -> Result<Version, Error> {
let head = w(&self.chain)?
.head_header()
.map_err(|e| ErrorKind::Internal(format!("can't get head: {}", e)))?;
Ok(Version {
node_version: CRATE_VERSION.to_owned(),
block_header_version: head.version.into(),
})
}
}
impl Handler for VersionHandler {
fn get(&self, _req: Request<Body>) -> ResponseFuture {
result_to_response(self.get_version())
}
}

View file

@ -34,6 +34,15 @@ macro_rules! no_dup {
};
}
/// API Version Information
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Version {
/// Current node API Version (api crate version)
pub node_version: String,
/// Block header version
pub block_header_version: u16,
}
/// The state of the current fork tip
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Tip {

View file

@ -1,6 +1,6 @@
[package]
name = "grin_chain"
version = "1.1.1-beta.1"
version = "2.0.0-beta.2"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Chain implementation for grin, a simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format."
license = "Apache-2.0"
@ -23,10 +23,10 @@ lru-cache = "0.1"
lazy_static = "1"
regex = "1"
grin_core = { path = "../core", version = "1.1.1-beta.1" }
grin_keychain = { path = "../keychain", version = "1.1.1-beta.1" }
grin_store = { path = "../store", version = "1.1.1-beta.1" }
grin_util = { path = "../util", version = "1.1.1-beta.1" }
grin_core = { path = "../core", version = "2.0.0-beta.2" }
grin_keychain = { path = "../keychain", version = "2.0.0-beta.2" }
grin_store = { path = "../store", version = "2.0.0-beta.2" }
grin_util = { path = "../util", version = "2.0.0-beta.2" }
[dev-dependencies]
env_logger = "0.5"

View file

@ -76,7 +76,14 @@ fn data_files() {
let prev = chain.head_header().unwrap();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
let pk = ExtKeychainPath::new(1, n as u32, 0, 0, 0).to_identifier();
let reward = libtx::reward::output(&keychain, &pk, 0, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&pk,
0,
false,
)
.unwrap();
let mut b =
core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward)
.unwrap();
@ -154,7 +161,8 @@ fn _prepare_block_nosum(
let key_id = ExtKeychainPath::new(1, diff as u32, 0, 0, 0).to_identifier();
let fees = txs.iter().map(|tx| tx.fee()).sum();
let reward = libtx::reward::output(kc, &key_id, fees, false).unwrap();
let reward =
libtx::reward::output(kc, &libtx::ProofBuilder::new(kc), &key_id, fees, false).unwrap();
let mut b = match core::core::Block::new(
prev,
txs.into_iter().cloned().collect(),

View file

@ -19,7 +19,7 @@ use self::core::core::verifier_cache::LruVerifierCache;
use self::core::core::{Block, BlockHeader, OutputIdentifier, Transaction};
use self::core::genesis;
use self::core::global::ChainTypes;
use self::core::libtx::{self, build, reward};
use self::core::libtx::{self, build, reward, ProofBuilder};
use self::core::pow::Difficulty;
use self::core::{consensus, global, pow};
use self::keychain::{ExtKeychain, ExtKeychainPath, Keychain};
@ -106,7 +106,14 @@ fn mine_genesis_reward_chain() {
let mut genesis = genesis::genesis_dev();
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let key_id = keychain::ExtKeychain::derive_key_id(0, 1, 0, 0, 0);
let reward = reward::output(&keychain, &key_id, 0, false).unwrap();
let reward = reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
0,
false,
)
.unwrap();
genesis = genesis.with_reward(reward.0, reward.1);
let tmp_chain_dir = ".grin.tmp";
@ -143,7 +150,9 @@ where
let prev = chain.head_header().unwrap();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
let pk = ExtKeychainPath::new(1, n as u32, 0, 0, 0).to_identifier();
let reward = libtx::reward::output(keychain, &pk, 0, false).unwrap();
let reward =
libtx::reward::output(keychain, &libtx::ProofBuilder::new(keychain), &pk, 0, false)
.unwrap();
let mut b =
core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward)
.unwrap();
@ -401,6 +410,7 @@ fn spend_in_fork_and_compact() {
let chain = setup(".grin6", pow::mine_genesis_block().unwrap());
let prev = chain.head_header().unwrap();
let kc = ExtKeychain::from_random_seed(false).unwrap();
let pb = ProofBuilder::new(&kc);
let mut fork_head = prev;
@ -434,6 +444,7 @@ fn spend_in_fork_and_compact() {
build::with_fee(20000),
],
&kc,
&pb,
)
.unwrap();
@ -451,6 +462,7 @@ fn spend_in_fork_and_compact() {
build::with_fee(20000),
],
&kc,
&pb,
)
.unwrap();
@ -540,7 +552,14 @@ fn output_header_mappings() {
let prev = chain.head_header().unwrap();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
let pk = ExtKeychainPath::new(1, n as u32, 0, 0, 0).to_identifier();
let reward = libtx::reward::output(&keychain, &pk, 0, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&pk,
0,
false,
)
.unwrap();
reward_outputs.push(reward.0.clone());
let mut b =
core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward)
@ -643,7 +662,8 @@ where
let key_id = ExtKeychainPath::new(1, diff as u32, 0, 0, 0).to_identifier();
let fees = txs.iter().map(|tx| tx.fee()).sum();
let reward = libtx::reward::output(kc, &key_id, fees, false).unwrap();
let reward =
libtx::reward::output(kc, &libtx::ProofBuilder::new(kc), &key_id, fees, false).unwrap();
let mut b = match core::core::Block::new(
prev,
txs.into_iter().cloned().collect(),

View file

@ -60,7 +60,14 @@ fn test_various_store_indices() {
setup_chain(&genesis, chain_store.clone()).unwrap();
let reward = libtx::reward::output(&keychain, &key_id, 0, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
0,
false,
)
.unwrap();
let block = Block::new(&genesis.header, vec![], Difficulty::min(), reward).unwrap();
let block_hash = block.hash();

View file

@ -16,7 +16,7 @@ use self::chain::types::NoopAdapter;
use self::chain::ErrorKind;
use self::core::core::verifier_cache::LruVerifierCache;
use self::core::global::{self, ChainTypes};
use self::core::libtx::{self, build};
use self::core::libtx::{self, build, ProofBuilder};
use self::core::pow::Difficulty;
use self::core::{consensus, pow};
use self::keychain::{ExtKeychain, ExtKeychainPath, Keychain};
@ -59,13 +59,14 @@ fn test_coinbase_maturity() {
let prev = chain.head_header().unwrap();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier();
let key_id3 = ExtKeychainPath::new(1, 3, 0, 0, 0).to_identifier();
let key_id4 = ExtKeychainPath::new(1, 4, 0, 0, 0).to_identifier();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
let reward = libtx::reward::output(&keychain, &key_id1, 0, false).unwrap();
let reward = libtx::reward::output(&keychain, &builder, &key_id1, 0, false).unwrap();
let mut block = core::core::Block::new(&prev, vec![], Difficulty::min(), reward).unwrap();
block.header.timestamp = prev.timestamp + Duration::seconds(60);
block.header.pow.secondary_scaling = next_header_info.secondary_scaling;
@ -104,12 +105,13 @@ fn test_coinbase_maturity() {
build::with_fee(2),
],
&keychain,
&builder,
)
.unwrap();
let txs = vec![coinbase_txn.clone()];
let fees = txs.iter().map(|tx| tx.fee()).sum();
let reward = libtx::reward::output(&keychain, &key_id3, fees, false).unwrap();
let reward = libtx::reward::output(&keychain, &builder, &key_id3, fees, false).unwrap();
let mut block = core::core::Block::new(&prev, txs, Difficulty::min(), reward).unwrap();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
block.header.timestamp = prev.timestamp + Duration::seconds(60);
@ -141,10 +143,11 @@ fn test_coinbase_maturity() {
let prev = chain.head_header().unwrap();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
let reward = libtx::reward::output(&keychain, &key_id1, 0, false).unwrap();
let reward = libtx::reward::output(&keychain, &builder, &key_id1, 0, false).unwrap();
let mut block =
core::core::Block::new(&prev, vec![], Difficulty::min(), reward).unwrap();
@ -185,12 +188,13 @@ fn test_coinbase_maturity() {
build::with_fee(2),
],
&keychain,
&builder,
)
.unwrap();
let txs = vec![coinbase_txn.clone()];
let fees = txs.iter().map(|tx| tx.fee()).sum();
let reward = libtx::reward::output(&keychain, &key_id3, fees, false).unwrap();
let reward = libtx::reward::output(&keychain, &builder, &key_id3, fees, false).unwrap();
let mut block = core::core::Block::new(&prev, txs, Difficulty::min(), reward).unwrap();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
block.header.timestamp = prev.timestamp + Duration::seconds(60);
@ -222,9 +226,10 @@ fn test_coinbase_maturity() {
let prev = chain.head_header().unwrap();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let pk = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
let reward = libtx::reward::output(&keychain, &pk, 0, false).unwrap();
let reward = libtx::reward::output(&keychain, &builder, &pk, 0, false).unwrap();
let mut block =
core::core::Block::new(&prev, vec![], Difficulty::min(), reward).unwrap();
let next_header_info =
@ -254,7 +259,7 @@ fn test_coinbase_maturity() {
let txs = vec![coinbase_txn];
let fees = txs.iter().map(|tx| tx.fee()).sum();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
let reward = libtx::reward::output(&keychain, &key_id4, fees, false).unwrap();
let reward = libtx::reward::output(&keychain, &builder, &key_id4, fees, false).unwrap();
let mut block = core::core::Block::new(&prev, txs, Difficulty::min(), reward).unwrap();
block.header.timestamp = prev.timestamp + Duration::seconds(60);

View file

@ -1,6 +1,6 @@
[package]
name = "grin_config"
version = "1.1.1-beta.1"
version = "2.0.0-beta.2"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Configuration for grin, a simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format."
license = "Apache-2.0"
@ -16,10 +16,10 @@ serde_derive = "1"
toml = "0.4"
dirs = "1.0.3"
grin_core = { path = "../core", version = "1.1.1-beta.1" }
grin_servers = { path = "../servers", version = "1.1.1-beta.1" }
grin_p2p = { path = "../p2p", version = "1.1.1-beta.1" }
grin_util = { path = "../util", version = "1.1.1-beta.1" }
grin_core = { path = "../core", version = "2.0.0-beta.2" }
grin_servers = { path = "../servers", version = "2.0.0-beta.2" }
grin_p2p = { path = "../p2p", version = "2.0.0-beta.2" }
grin_util = { path = "../util", version = "2.0.0-beta.2" }
[dev-dependencies]
pretty_assertions = "0.5.1"

View file

@ -1,6 +1,6 @@
[package]
name = "grin_core"
version = "1.1.1-beta.1"
version = "2.0.0-beta.2"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Chain implementation for grin, a simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format."
license = "Apache-2.0"
@ -27,9 +27,10 @@ siphasher = "0.2"
uuid = { version = "0.6", features = ["serde", "v4"] }
log = "0.4"
chrono = { version = "0.4.4", features = ["serde"] }
zeroize = "0.9"
grin_keychain = { path = "../keychain", version = "1.1.1-beta.1" }
grin_util = { path = "../util", version = "1.1.1-beta.1" }
grin_keychain = { path = "../keychain", version = "2.0.0-beta.2" }
grin_util = { path = "../util", version = "2.0.0-beta.2" }
[dev-dependencies]
serde_json = "1"

View file

@ -127,22 +127,42 @@ pub const MAX_BLOCK_WEIGHT: usize = 40_000;
/// Fork every 6 months.
pub const HARD_FORK_INTERVAL: u64 = YEAR_HEIGHT / 2;
/// Floonet first hard fork height, set to happen around 2019-06-20
pub const FLOONET_FIRST_HARD_FORK: u64 = 185_040;
/// Check whether the block version is valid at a given height, implements
/// 6 months interval scheduled hard forks for the first 2 years.
pub fn valid_header_version(height: u64, version: HeaderVersion) -> bool {
// uncomment below as we go from hard fork to hard fork
if height < HARD_FORK_INTERVAL {
version == HeaderVersion::default()
/* } else if height < 2 * HARD_FORK_INTERVAL {
version == 2
} else if height < 3 * HARD_FORK_INTERVAL {
version == 3
} else if height < 4 * HARD_FORK_INTERVAL {
version == 4
} else if height >= 5 * HARD_FORK_INTERVAL {
version > 4 */
} else {
false
let chain_type = global::CHAIN_TYPE.read().clone();
match chain_type {
global::ChainTypes::Floonet => {
if height < FLOONET_FIRST_HARD_FORK {
version == HeaderVersion::default()
// add branches one by one as we go from hard fork to hard fork
// } else if height < FLOONET_SECOND_HARD_FORK {
} else if height < 2 * HARD_FORK_INTERVAL {
version == HeaderVersion::new(2)
} else {
false
}
}
// everything else just like mainnet
_ => {
if height < HARD_FORK_INTERVAL {
version == HeaderVersion::default()
} else if height < 2 * HARD_FORK_INTERVAL {
version == HeaderVersion::new(2)
// uncomment branches one by one as we go from hard fork to hard fork
/*} else if height < 3 * HARD_FORK_INTERVAL {
version == HeaderVersion::new(3)
} else if height < 4 * HARD_FORK_INTERVAL {
version == HeaderVersion::new(4)
} else {
version > HeaderVersion::new(4) */
} else {
false
}
}
}
}
@ -163,14 +183,14 @@ pub const DIFFICULTY_DAMP_FACTOR: u64 = 3;
pub const AR_SCALE_DAMP_FACTOR: u64 = 13;
/// Compute weight of a graph as number of siphash bits defining the graph
/// Must be made dependent on height to phase out smaller size over the years
/// This can wait until end of 2019 at latest
/// Must be made dependent on height to phase out C31 in early 2020
/// Later phase outs are on hold for now
pub fn graph_weight(height: u64, edge_bits: u8) -> u64 {
let mut xpr_edge_bits = edge_bits as u64;
let bits_over_min = edge_bits.saturating_sub(global::min_edge_bits());
let expiry_height = (1 << bits_over_min) * YEAR_HEIGHT;
if height >= expiry_height {
if edge_bits < 32 && height >= expiry_height {
xpr_edge_bits = xpr_edge_bits.saturating_sub(1 + (height - expiry_height) / WEEK_HEIGHT);
}
@ -362,23 +382,29 @@ mod test {
// 2 years in, 31 still at 0, 32 starts decreasing
assert_eq!(graph_weight(2 * YEAR_HEIGHT, 31), 0);
assert_eq!(graph_weight(2 * YEAR_HEIGHT, 32), 512 * 31);
assert_eq!(graph_weight(2 * YEAR_HEIGHT, 32), 512 * 32);
assert_eq!(graph_weight(2 * YEAR_HEIGHT, 33), 1024 * 33);
// 32 loses one factor per week
assert_eq!(graph_weight(2 * YEAR_HEIGHT + WEEK_HEIGHT, 32), 512 * 30);
// 32 phaseout on hold
assert_eq!(graph_weight(2 * YEAR_HEIGHT + WEEK_HEIGHT, 32), 512 * 32);
assert_eq!(graph_weight(2 * YEAR_HEIGHT + WEEK_HEIGHT, 31), 0);
assert_eq!(graph_weight(2 * YEAR_HEIGHT + 30 * WEEK_HEIGHT, 32), 512);
assert_eq!(graph_weight(2 * YEAR_HEIGHT + 31 * WEEK_HEIGHT, 32), 0);
assert_eq!(
graph_weight(2 * YEAR_HEIGHT + 30 * WEEK_HEIGHT, 32),
512 * 32
);
assert_eq!(
graph_weight(2 * YEAR_HEIGHT + 31 * WEEK_HEIGHT, 32),
512 * 32
);
// 3 years in, nothing changes
assert_eq!(graph_weight(3 * YEAR_HEIGHT, 31), 0);
assert_eq!(graph_weight(3 * YEAR_HEIGHT, 32), 0);
assert_eq!(graph_weight(3 * YEAR_HEIGHT, 32), 512 * 32);
assert_eq!(graph_weight(3 * YEAR_HEIGHT, 33), 1024 * 33);
// 4 years in, 33 starts starts decreasing
// 4 years in, still on hold
assert_eq!(graph_weight(4 * YEAR_HEIGHT, 31), 0);
assert_eq!(graph_weight(4 * YEAR_HEIGHT, 32), 0);
assert_eq!(graph_weight(4 * YEAR_HEIGHT, 33), 1024 * 32);
assert_eq!(graph_weight(4 * YEAR_HEIGHT, 32), 512 * 32);
assert_eq!(graph_weight(4 * YEAR_HEIGHT, 33), 1024 * 33);
}
}

View file

@ -178,6 +178,13 @@ impl Default for HeaderVersion {
}
}
// self-conscious increment function courtesy of Jasper
impl HeaderVersion {
fn next(&self) -> Self {
Self(self.0 + 1)
}
}
impl HeaderVersion {
/// Constructor taking the provided version.
pub fn new(version: u16) -> HeaderVersion {
@ -565,6 +572,13 @@ impl Block {
vec![],
)?;
let height = prev.height + 1;
let mut version = prev.version;
if !consensus::valid_header_version(height, version) {
version = version.next();
}
let now = Utc::now().timestamp();
let timestamp = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(now, 0), Utc);
@ -573,7 +587,8 @@ impl Block {
// Caller must validate the block as necessary.
Block {
header: BlockHeader {
height: prev.height + 1,
version,
height,
timestamp,
prev_hash: prev.hash(),
total_kernel_offset,

View file

@ -1499,14 +1499,16 @@ mod test {
use super::*;
use crate::core::hash::Hash;
use crate::core::id::{ShortId, ShortIdentifiable};
use crate::keychain::{ExtKeychain, Keychain};
use crate::keychain::{ExtKeychain, Keychain, SwitchCommitmentType};
use crate::util::secp;
#[test]
fn test_kernel_ser_deser() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let commit = keychain.commit(5, &key_id).unwrap();
let commit = keychain
.commit(5, &key_id, &SwitchCommitmentType::Regular)
.unwrap();
// just some bytes for testing ser/deser
let sig = secp::Signature::from_raw_data(&[0; 64]).unwrap();
@ -1552,10 +1554,14 @@ mod test {
let keychain = ExtKeychain::from_seed(&[0; 32], false).unwrap();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let commit = keychain.commit(1003, &key_id).unwrap();
let commit = keychain
.commit(1003, &key_id, &SwitchCommitmentType::Regular)
.unwrap();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let commit_2 = keychain.commit(1003, &key_id).unwrap();
let commit_2 = keychain
.commit(1003, &key_id, &SwitchCommitmentType::Regular)
.unwrap();
assert!(commit == commit_2);
}
@ -1564,7 +1570,9 @@ mod test {
fn input_short_id() {
let keychain = ExtKeychain::from_seed(&[0; 32], false).unwrap();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let commit = keychain.commit(5, &key_id).unwrap();
let commit = keychain
.commit(5, &key_id, &SwitchCommitmentType::Regular)
.unwrap();
let input = Input {
features: OutputFeatures::Plain,

View file

@ -16,13 +16,16 @@
//! having to pass them all over the place, but aren't consensus values.
//! should be used sparingly.
use crate::consensus::HeaderInfo;
use crate::consensus::{
graph_weight, BASE_EDGE_BITS, BLOCK_TIME_SEC, COINBASE_MATURITY, CUT_THROUGH_HORIZON,
DAY_HEIGHT, DEFAULT_MIN_EDGE_BITS, DIFFICULTY_ADJUST_WINDOW, INITIAL_DIFFICULTY,
MAX_BLOCK_WEIGHT, PROOFSIZE, SECOND_POW_EDGE_BITS, STATE_SYNC_THRESHOLD,
graph_weight, valid_header_version, HeaderInfo, BASE_EDGE_BITS, BLOCK_TIME_SEC,
COINBASE_MATURITY, CUT_THROUGH_HORIZON, DAY_HEIGHT, DEFAULT_MIN_EDGE_BITS,
DIFFICULTY_ADJUST_WINDOW, INITIAL_DIFFICULTY, MAX_BLOCK_WEIGHT, PROOFSIZE,
SECOND_POW_EDGE_BITS, STATE_SYNC_THRESHOLD,
};
use crate::core::block::HeaderVersion;
use crate::pow::{
self, new_cuckaroo_ctx, new_cuckarood_ctx, new_cuckatoo_ctx, EdgeType, PoWContext,
};
use crate::pow::{self, new_cuckaroo_ctx, new_cuckatoo_ctx, EdgeType, PoWContext};
/// An enum collecting sets of parameters used throughout the
/// code wherever mining is needed. This should allow for
/// different sets of parameters for different purposes,
@ -144,7 +147,7 @@ pub fn set_mining_mode(mode: ChainTypes) {
/// Return either a cuckoo context or a cuckatoo context
/// Single change point
pub fn create_pow_context<T>(
_height: u64,
height: u64,
edge_bits: u8,
proof_size: usize,
max_sols: u32,
@ -154,13 +157,19 @@ where
{
let chain_type = CHAIN_TYPE.read().clone();
match chain_type {
// Mainnet has Cuckaroo29 for AR and Cuckatoo30+ for AF
ChainTypes::Mainnet if edge_bits == 29 => new_cuckaroo_ctx(edge_bits, proof_size),
ChainTypes::Mainnet => new_cuckatoo_ctx(edge_bits, proof_size, max_sols),
// Mainnet has Cuckaroo(d)29 for AR and Cuckatoo31+ for AF
ChainTypes::Mainnet if edge_bits > 29 => new_cuckatoo_ctx(edge_bits, proof_size, max_sols),
ChainTypes::Mainnet if valid_header_version(height, HeaderVersion::new(2)) => {
new_cuckarood_ctx(edge_bits, proof_size)
}
ChainTypes::Mainnet => new_cuckaroo_ctx(edge_bits, proof_size),
// Same for Floonet
ChainTypes::Floonet if edge_bits == 29 => new_cuckaroo_ctx(edge_bits, proof_size),
ChainTypes::Floonet => new_cuckatoo_ctx(edge_bits, proof_size, max_sols),
ChainTypes::Floonet if edge_bits > 29 => new_cuckatoo_ctx(edge_bits, proof_size, max_sols),
ChainTypes::Floonet if valid_header_version(height, HeaderVersion::new(2)) => {
new_cuckarood_ctx(edge_bits, proof_size)
}
ChainTypes::Floonet => new_cuckaroo_ctx(edge_bits, proof_size),
// Everything else is Cuckatoo only
_ => new_cuckatoo_ctx(edge_bits, proof_size, max_sols),

View file

@ -36,6 +36,7 @@ extern crate log;
use failure;
#[macro_use]
extern crate failure_derive;
extern crate zeroize;
#[macro_use]
pub mod macros;

View file

@ -21,6 +21,7 @@ use crate::libtx::error::{Error, ErrorKind};
use crate::util::secp::key::{PublicKey, SecretKey};
use crate::util::secp::pedersen::Commitment;
use crate::util::secp::{self, aggsig, Message, Secp256k1, Signature};
use grin_keychain::SwitchCommitmentType;
/// Creates a new secure nonce (as a SecretKey), guaranteed to be usable during
/// aggsig creation.
@ -231,15 +232,17 @@ pub fn verify_partial_sig(
/// use core::libtx::{aggsig, proof};
/// use core::core::transaction::{kernel_sig_msg, KernelFeatures};
/// use core::core::{Output, OutputFeatures};
/// use keychain::{Keychain, ExtKeychain};
/// use keychain::{Keychain, ExtKeychain, SwitchCommitmentType};
///
/// let secp = Secp256k1::with_caps(ContextFlag::Commit);
/// let keychain = ExtKeychain::from_random_seed(false).unwrap();
/// let fees = 10_000;
/// let value = reward(fees);
/// let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
/// let commit = keychain.commit(value, &key_id).unwrap();
/// let rproof = proof::create(&keychain, value, &key_id, commit, None).unwrap();
/// let switch = &SwitchCommitmentType::Regular;
/// let commit = keychain.commit(value, &key_id, switch).unwrap();
/// let builder = proof::ProofBuilder::new(&keychain);
/// let rproof = proof::create(&keychain, &builder, value, &key_id, switch, commit, None).unwrap();
/// let output = Output {
/// features: OutputFeatures::Coinbase,
/// commit: commit,
@ -266,7 +269,7 @@ pub fn sign_from_key_id<K>(
where
K: Keychain,
{
let skey = k.derive_key(value, key_id)?;
let skey = k.derive_key(value, key_id, &SwitchCommitmentType::Regular)?; // TODO: proper support for different switch commitment schemes
let sig = aggsig::sign_single(secp, &msg, &skey, s_nonce, None, None, blind_sum, None)?;
Ok(sig)
}
@ -296,7 +299,7 @@ where
/// use util::secp::{ContextFlag, Secp256k1};
/// use core::core::transaction::{kernel_sig_msg, KernelFeatures};
/// use core::core::{Output, OutputFeatures};
/// use keychain::{Keychain, ExtKeychain};
/// use keychain::{Keychain, ExtKeychain, SwitchCommitmentType};
///
/// // Create signature
/// let secp = Secp256k1::with_caps(ContextFlag::Commit);
@ -304,8 +307,10 @@ where
/// let fees = 10_000;
/// let value = reward(fees);
/// let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
/// let commit = keychain.commit(value, &key_id).unwrap();
/// let rproof = proof::create(&keychain, value, &key_id, commit, None).unwrap();
/// let switch = &SwitchCommitmentType::Regular;
/// let commit = keychain.commit(value, &key_id, switch).unwrap();
/// let builder = proof::ProofBuilder::new(&keychain);
/// let rproof = proof::create(&keychain, &builder, value, &key_id, switch, commit, None).unwrap();
/// let output = Output {
/// features: OutputFeatures::Coinbase,
/// commit: commit,

View file

@ -27,33 +27,42 @@
use crate::core::{Input, Output, OutputFeatures, Transaction, TxKernel};
use crate::keychain::{BlindSum, BlindingFactor, Identifier, Keychain};
use crate::libtx::{aggsig, proof, Error};
use crate::libtx::proof::{self, ProofBuild};
use crate::libtx::{aggsig, Error};
use grin_keychain::SwitchCommitmentType;
/// Context information available to transaction combinators.
pub struct Context<'a, K>
pub struct Context<'a, K, B>
where
K: Keychain,
B: ProofBuild,
{
/// The keychain used for key derivation
pub keychain: &'a K,
/// The bulletproof builder
pub builder: &'a B,
}
/// Function type returned by the transaction combinators. Transforms a
/// (Transaction, BlindSum) pair into another, provided some context.
pub type Append<K> = dyn for<'a> Fn(
&'a mut Context<'_, K>,
pub type Append<K, B> = dyn for<'a> Fn(
&'a mut Context<'_, K, B>,
(Transaction, TxKernel, BlindSum),
) -> (Transaction, TxKernel, BlindSum);
/// Adds an input with the provided value and blinding key to the transaction
/// being built.
fn build_input<K>(value: u64, features: OutputFeatures, key_id: Identifier) -> Box<Append<K>>
fn build_input<K, B>(value: u64, features: OutputFeatures, key_id: Identifier) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
Box::new(
move |build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
let commit = build.keychain.commit(value, &key_id).unwrap();
let commit = build
.keychain
.commit(value, &key_id, &SwitchCommitmentType::Regular)
.unwrap(); // TODO: proper support for different switch commitment schemes
let input = Input::new(features, commit);
(
tx.with_input(input),
@ -66,9 +75,10 @@ where
/// Adds an input with the provided value and blinding key to the transaction
/// being built.
pub fn input<K>(value: u64, key_id: Identifier) -> Box<Append<K>>
pub fn input<K, B>(value: u64, key_id: Identifier) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
debug!(
"Building input (spending regular output): {}, {}",
@ -78,9 +88,10 @@ where
}
/// Adds a coinbase input spending a coinbase output.
pub fn coinbase_input<K>(value: u64, key_id: Identifier) -> Box<Append<K>>
pub fn coinbase_input<K, B>(value: u64, key_id: Identifier) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
debug!("Building input (spending coinbase): {}, {}", value, key_id);
build_input(value, OutputFeatures::Coinbase, key_id)
@ -88,17 +99,30 @@ where
/// Adds an output with the provided value and key identifier from the
/// keychain.
pub fn output<K>(value: u64, key_id: Identifier) -> Box<Append<K>>
pub fn output<K, B>(value: u64, key_id: Identifier) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
Box::new(
move |build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
let commit = build.keychain.commit(value, &key_id).unwrap();
// TODO: proper support for different switch commitment schemes
let switch = &SwitchCommitmentType::Regular;
let commit = build.keychain.commit(value, &key_id, switch).unwrap();
debug!("Building output: {}, {:?}", value, commit);
let rproof = proof::create(build.keychain, value, &key_id, commit, None).unwrap();
let rproof = proof::create(
build.keychain,
build.builder,
value,
&key_id,
switch,
commit,
None,
)
.unwrap();
(
tx.with_output(Output {
@ -114,9 +138,10 @@ where
}
/// Sets the fee on the transaction being built.
pub fn with_fee<K>(fee: u64) -> Box<Append<K>>
pub fn with_fee<K, B>(fee: u64) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
Box::new(
move |_build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
@ -126,9 +151,10 @@ where
}
/// Sets the lock_height on the transaction being built.
pub fn with_lock_height<K>(lock_height: u64) -> Box<Append<K>>
pub fn with_lock_height<K, B>(lock_height: u64) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
Box::new(
move |_build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
@ -140,9 +166,10 @@ where
/// Adds a known excess value on the transaction being built. Usually used in
/// combination with the initial_tx function when a new transaction is built
/// by adding to a pre-existing one.
pub fn with_excess<K>(excess: BlindingFactor) -> Box<Append<K>>
pub fn with_excess<K, B>(excess: BlindingFactor) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
Box::new(
move |_build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
@ -152,9 +179,10 @@ where
}
/// Sets a known tx "offset". Used in final step of tx construction.
pub fn with_offset<K>(offset: BlindingFactor) -> Box<Append<K>>
pub fn with_offset<K, B>(offset: BlindingFactor) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
Box::new(
move |_build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
@ -166,9 +194,10 @@ where
/// Sets an initial transaction to add to when building a new transaction.
/// We currently only support building a tx with a single kernel with
/// build::transaction()
pub fn initial_tx<K>(mut tx: Transaction) -> Box<Append<K>>
pub fn initial_tx<K, B>(mut tx: Transaction) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
assert_eq!(tx.kernels().len(), 1);
let kern = tx.kernels_mut().remove(0);
@ -189,14 +218,16 @@ where
/// let (tx2, _) = build::transaction(vec![initial_tx(tx1), with_excess(sum),
/// output_rand(2)], keychain).unwrap();
///
pub fn partial_transaction<K>(
elems: Vec<Box<Append<K>>>,
pub fn partial_transaction<K, B>(
elems: Vec<Box<Append<K, B>>>,
keychain: &K,
builder: &B,
) -> Result<(Transaction, BlindingFactor), Error>
where
K: Keychain,
B: ProofBuild,
{
let mut ctx = Context { keychain };
let mut ctx = Context { keychain, builder };
let (tx, kern, sum) = elems.iter().fold(
(Transaction::empty(), TxKernel::empty(), BlindSum::new()),
|acc, elem| elem(&mut ctx, acc),
@ -212,11 +243,16 @@ where
}
/// Builds a complete transaction.
pub fn transaction<K>(elems: Vec<Box<Append<K>>>, keychain: &K) -> Result<Transaction, Error>
pub fn transaction<K, B>(
elems: Vec<Box<Append<K, B>>>,
keychain: &K,
builder: &B,
) -> Result<Transaction, Error>
where
K: Keychain,
B: ProofBuild,
{
let mut ctx = Context { keychain };
let mut ctx = Context { keychain, builder };
let (mut tx, mut kern, sum) = elems.iter().fold(
(Transaction::empty(), TxKernel::empty(), BlindSum::new()),
|acc, elem| elem(&mut ctx, acc),
@ -260,6 +296,7 @@ mod test {
use crate::core::transaction::Weighting;
use crate::core::verifier_cache::{LruVerifierCache, VerifierCache};
use crate::keychain::{ExtKeychain, ExtKeychainPath};
use crate::libtx::ProofBuilder;
fn verifier_cache() -> Arc<RwLock<dyn VerifierCache>> {
Arc::new(RwLock::new(LruVerifierCache::new()))
@ -268,6 +305,7 @@ mod test {
#[test]
fn blind_simple_tx() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier();
let key_id3 = ExtKeychainPath::new(1, 3, 0, 0, 0).to_identifier();
@ -282,6 +320,7 @@ mod test {
with_fee(2),
],
&keychain,
&builder,
)
.unwrap();
@ -291,6 +330,7 @@ mod test {
#[test]
fn blind_simple_tx_with_offset() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier();
let key_id3 = ExtKeychainPath::new(1, 3, 0, 0, 0).to_identifier();
@ -305,6 +345,7 @@ mod test {
with_fee(2),
],
&keychain,
&builder,
)
.unwrap();
@ -314,6 +355,7 @@ mod test {
#[test]
fn blind_simpler_tx() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier();
@ -322,6 +364,7 @@ mod test {
let tx = transaction(
vec![input(6, key_id1), output(2, key_id2), with_fee(4)],
&keychain,
&builder,
)
.unwrap();

View file

@ -31,6 +31,7 @@ pub mod secp_ser;
use crate::consensus;
use crate::core::Transaction;
pub use self::proof::ProofBuilder;
pub use crate::libtx::error::{Error, ErrorKind};
const DEFAULT_BASE_FEE: u64 = consensus::MILLI_GRIN;

View file

@ -14,31 +14,47 @@
//! Rangeproof library functions
use crate::keychain::{Identifier, Keychain};
use crate::blake2::blake2b::blake2b;
use crate::keychain::extkey_bip32::BIP32GrinHasher;
use crate::keychain::{Identifier, Keychain, SwitchCommitmentType, ViewKey};
use crate::libtx::error::{Error, ErrorKind};
use crate::util::secp::key::SecretKey;
use crate::util::secp::pedersen::{Commitment, ProofInfo, ProofMessage, RangeProof};
use crate::util::secp::pedersen::{Commitment, ProofMessage, RangeProof};
use crate::util::secp::{self, Secp256k1};
use crate::zeroize::Zeroize;
use std::convert::TryFrom;
/// Create a bulletproof
pub fn create<K>(
pub fn create<K, B>(
k: &K,
b: &B,
amount: u64,
key_id: &Identifier,
switch: &SwitchCommitmentType,
_commit: Commitment,
extra_data: Option<Vec<u8>>,
) -> Result<RangeProof, Error>
where
K: Keychain,
B: ProofBuild,
{
let commit = k.commit(amount, key_id)?;
let skey = k.derive_key(amount, key_id)?;
let nonce = k
.create_nonce(&commit)
.map_err(|e| ErrorKind::RangeProof(e.to_string()))?;
let message = ProofMessage::from_bytes(&key_id.serialize_path());
Ok(k.secp()
.bullet_proof(amount, skey, nonce, extra_data, Some(message)))
// TODO: proper support for different switch commitment schemes
// The new bulletproof scheme encodes and decodes it, but
// it is not supported at the wallet level (yet).
let secp = k.secp();
let commit = k.commit(amount, key_id, switch)?;
let skey = k.derive_key(amount, key_id, switch)?;
let rewind_nonce = b.rewind_nonce(secp, &commit)?;
let private_nonce = b.private_nonce(secp, &commit)?;
let message = b.proof_message(secp, key_id, switch)?;
Ok(secp.bullet_proof(
amount,
skey,
rewind_nonce,
private_nonce,
extra_data,
Some(message),
))
}
/// Verify a proof
@ -55,35 +71,689 @@ pub fn verify(
}
}
/// Rewind a rangeproof to retrieve the amount
pub fn rewind<K>(
k: &K,
/// Rewind a rangeproof to retrieve the amount, derivation path and switch commitment type
pub fn rewind<B>(
secp: &Secp256k1,
b: &B,
commit: Commitment,
extra_data: Option<Vec<u8>>,
proof: RangeProof,
) -> Result<ProofInfo, Error>
) -> Result<Option<(u64, Identifier, SwitchCommitmentType)>, Error>
where
B: ProofBuild,
{
let nonce = b
.rewind_nonce(secp, &commit)
.map_err(|e| ErrorKind::RangeProof(e.to_string()))?;
let info = secp.rewind_bullet_proof(commit, nonce, extra_data, proof);
if info.is_err() {
return Ok(None);
}
let info = info.unwrap();
let amount = info.value;
let check = b
.check_output(secp, &commit, amount, info.message)
.map_err(|e| ErrorKind::RangeProof(e.to_string()))?;
Ok(check.map(|(id, switch)| (amount, id, switch)))
}
/// Used for building proofs and checking if the output belongs to the wallet
pub trait ProofBuild {
/// Create a BP nonce that will allow to rewind the derivation path and flags
fn rewind_nonce(&self, secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error>;
/// Create a BP nonce that blinds the private key
fn private_nonce(&self, secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error>;
/// Create a BP message
fn proof_message(
&self,
secp: &Secp256k1,
id: &Identifier,
switch: &SwitchCommitmentType,
) -> Result<ProofMessage, Error>;
/// Check if the output belongs to this keychain
fn check_output(
&self,
secp: &Secp256k1,
commit: &Commitment,
amount: u64,
message: ProofMessage,
) -> Result<Option<(Identifier, SwitchCommitmentType)>, Error>;
}
/// The new, more flexible proof builder
pub struct ProofBuilder<'a, K>
where
K: Keychain,
{
let nonce = k
.create_nonce(&commit)
.map_err(|e| ErrorKind::RangeProof(e.to_string()))?;
let proof_message = k
.secp()
.rewind_bullet_proof(commit, nonce, extra_data, proof);
let proof_info = match proof_message {
Ok(p) => p,
Err(_) => ProofInfo {
success: false,
value: 0,
message: ProofMessage::empty(),
blinding: SecretKey([0; secp::constants::SECRET_KEY_SIZE]),
mlen: 0,
min: 0,
max: 0,
exp: 0,
mantissa: 0,
},
};
return Ok(proof_info);
keychain: &'a K,
rewind_hash: Vec<u8>,
private_hash: Vec<u8>,
}
impl<'a, K> ProofBuilder<'a, K>
where
K: Keychain,
{
/// Creates a new instance of this proof builder
pub fn new(keychain: &'a K) -> Self {
let private_root_key = keychain
.derive_key(0, &K::root_key_id(), &SwitchCommitmentType::None)
.unwrap();
let private_hash = blake2b(32, &[], &private_root_key.0).as_bytes().to_vec();
let public_root_key = keychain
.public_root_key()
.serialize_vec(keychain.secp(), true);
let rewind_hash = blake2b(32, &[], &public_root_key[..]).as_bytes().to_vec();
Self {
keychain,
rewind_hash,
private_hash,
}
}
fn nonce(&self, commit: &Commitment, private: bool) -> Result<SecretKey, Error> {
let hash = if private {
&self.private_hash
} else {
&self.rewind_hash
};
let res = blake2b(32, &commit.0, hash);
SecretKey::from_slice(self.keychain.secp(), res.as_bytes()).map_err(|e| {
ErrorKind::RangeProof(format!("Unable to create nonce: {:?}", e).to_string()).into()
})
}
}
impl<'a, K> ProofBuild for ProofBuilder<'a, K>
where
K: Keychain,
{
fn rewind_nonce(&self, _secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error> {
self.nonce(commit, false)
}
fn private_nonce(&self, _secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error> {
self.nonce(commit, true)
}
/// Message bytes:
/// 0: reserved for future use
/// 1: wallet type (0 for standard)
/// 2: switch commitment type
/// 3: path depth
/// 4-19: derivation path
fn proof_message(
&self,
_secp: &Secp256k1,
id: &Identifier,
switch: &SwitchCommitmentType,
) -> Result<ProofMessage, Error> {
let mut msg = [0; 20];
msg[2] = u8::from(switch);
let id_bytes = id.to_bytes();
for i in 0..17 {
msg[i + 3] = id_bytes[i];
}
Ok(ProofMessage::from_bytes(&msg))
}
fn check_output(
&self,
_secp: &Secp256k1,
commit: &Commitment,
amount: u64,
message: ProofMessage,
) -> Result<Option<(Identifier, SwitchCommitmentType)>, Error> {
if message.len() != 20 {
return Ok(None);
}
let msg = message.as_bytes();
let exp: [u8; 2] = [0; 2];
if msg[..2] != exp {
return Ok(None);
}
let switch = match SwitchCommitmentType::try_from(msg[2]) {
Ok(s) => s,
Err(_) => return Ok(None),
};
let depth = u8::min(msg[3], 4);
let id = Identifier::from_serialized_path(depth, &msg[4..]);
let commit_exp = self.keychain.commit(amount, &id, &switch)?;
match commit == &commit_exp {
true => Ok(Some((id, switch))),
false => Ok(None),
}
}
}
impl<'a, K> Zeroize for ProofBuilder<'a, K>
where
K: Keychain,
{
fn zeroize(&mut self) {
self.rewind_hash.zeroize();
self.private_hash.zeroize();
}
}
impl<'a, K> Drop for ProofBuilder<'a, K>
where
K: Keychain,
{
fn drop(&mut self) {
self.zeroize();
}
}
/// The legacy proof builder, used before the first hard fork
pub struct LegacyProofBuilder<'a, K>
where
K: Keychain,
{
keychain: &'a K,
root_hash: Vec<u8>,
}
impl<'a, K> LegacyProofBuilder<'a, K>
where
K: Keychain,
{
/// Creates a new instance of this proof builder
pub fn new(keychain: &'a K) -> Self {
Self {
keychain,
root_hash: keychain
.derive_key(0, &K::root_key_id(), &SwitchCommitmentType::Regular)
.unwrap()
.0
.to_vec(),
}
}
fn nonce(&self, commit: &Commitment) -> Result<SecretKey, Error> {
let res = blake2b(32, &commit.0, &self.root_hash);
SecretKey::from_slice(self.keychain.secp(), res.as_bytes()).map_err(|e| {
ErrorKind::RangeProof(format!("Unable to create nonce: {:?}", e).to_string()).into()
})
}
}
impl<'a, K> ProofBuild for LegacyProofBuilder<'a, K>
where
K: Keychain,
{
fn rewind_nonce(&self, _secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error> {
self.nonce(commit)
}
fn private_nonce(&self, _secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error> {
self.nonce(commit)
}
/// Message bytes:
/// 0-3: 0
/// 4-19: derivation path
/// All outputs with this scheme are assumed to use regular switch commitments
fn proof_message(
&self,
_secp: &Secp256k1,
id: &Identifier,
_switch: &SwitchCommitmentType,
) -> Result<ProofMessage, Error> {
let mut msg = [0; 20];
let id_ser = id.serialize_path();
for i in 0..16 {
msg[i + 4] = id_ser[i];
}
Ok(ProofMessage::from_bytes(&msg))
}
fn check_output(
&self,
_secp: &Secp256k1,
commit: &Commitment,
amount: u64,
message: ProofMessage,
) -> Result<Option<(Identifier, SwitchCommitmentType)>, Error> {
if message.len() != 20 {
return Ok(None);
}
let msg = message.as_bytes();
let id = Identifier::from_serialized_path(3, &msg[4..]);
let exp: [u8; 4] = [0; 4];
if msg[..4] != exp {
return Ok(None);
}
let commit_exp = self
.keychain
.commit(amount, &id, &SwitchCommitmentType::Regular)?;
match commit == &commit_exp {
true => Ok(Some((id, SwitchCommitmentType::Regular))),
false => Ok(None),
}
}
}
impl<'a, K> Zeroize for LegacyProofBuilder<'a, K>
where
K: Keychain,
{
fn zeroize(&mut self) {
self.root_hash.zeroize();
}
}
impl<'a, K> Drop for LegacyProofBuilder<'a, K>
where
K: Keychain,
{
fn drop(&mut self) {
self.zeroize();
}
}
impl ProofBuild for ViewKey {
fn rewind_nonce(&self, secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error> {
let res = blake2b(32, &commit.0, &self.rewind_hash);
SecretKey::from_slice(secp, res.as_bytes()).map_err(|e| {
ErrorKind::RangeProof(format!("Unable to create nonce: {:?}", e).to_string()).into()
})
}
fn private_nonce(&self, _secp: &Secp256k1, _commit: &Commitment) -> Result<SecretKey, Error> {
unimplemented!();
}
fn proof_message(
&self,
_secp: &Secp256k1,
_id: &Identifier,
_switch: &SwitchCommitmentType,
) -> Result<ProofMessage, Error> {
unimplemented!();
}
fn check_output(
&self,
secp: &Secp256k1,
commit: &Commitment,
amount: u64,
message: ProofMessage,
) -> Result<Option<(Identifier, SwitchCommitmentType)>, Error> {
if message.len() != 20 {
return Ok(None);
}
let msg = message.as_bytes();
let exp: [u8; 2] = [0; 2];
if msg[..2] != exp {
return Ok(None);
}
let switch = match SwitchCommitmentType::try_from(msg[2]) {
Ok(s) => s,
Err(_) => return Ok(None),
};
let depth = u8::min(msg[3], 4);
let id = Identifier::from_serialized_path(depth, &msg[4..]);
let path = id.to_path();
if self.depth > path.depth {
return Ok(None);
}
// For non-root key, check child number of current depth
if self.depth > 0
&& path.depth > 0
&& self.child_number != path.path[self.depth as usize - 1]
{
return Ok(None);
}
let mut key = self.clone();
let mut hasher = BIP32GrinHasher::new(self.is_floo);
for i in self.depth..path.depth {
let child_number = path.path[i as usize];
if child_number.is_hardened() {
return Ok(None);
}
key = key.ckd_pub(&secp, &mut hasher, child_number)?;
}
let pub_key = key.commit(secp, amount, &switch)?;
if commit.to_pubkey(&secp)? == pub_key {
Ok(Some((id, switch)))
} else {
Ok(None)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::keychain::ExtKeychain;
use grin_keychain::ChildNumber;
use rand::{thread_rng, Rng};
#[test]
fn legacy_builder() {
let rng = &mut thread_rng();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = LegacyProofBuilder::new(&keychain);
let amount = rng.gen();
let id = ExtKeychain::derive_key_id(3, rng.gen(), rng.gen(), rng.gen(), 0);
let switch = SwitchCommitmentType::Regular;
let commit = keychain.commit(amount, &id, &switch).unwrap();
let proof = create(
&keychain,
&builder,
amount,
&id,
&switch,
commit.clone(),
None,
)
.unwrap();
assert!(verify(&keychain.secp(), commit.clone(), proof.clone(), None).is_ok());
let rewind = rewind(keychain.secp(), &builder, commit, None, proof).unwrap();
assert!(rewind.is_some());
let (r_amount, r_id, r_switch) = rewind.unwrap();
assert_eq!(r_amount, amount);
assert_eq!(r_id, id);
assert_eq!(r_switch, switch);
}
#[test]
fn builder() {
let rng = &mut thread_rng();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let amount = rng.gen();
let id = ExtKeychain::derive_key_id(3, rng.gen(), rng.gen(), rng.gen(), 0);
// With switch commitment
let commit_a = {
let switch = SwitchCommitmentType::Regular;
let commit = keychain.commit(amount, &id, &switch).unwrap();
let proof = create(
&keychain,
&builder,
amount,
&id,
&switch,
commit.clone(),
None,
)
.unwrap();
assert!(verify(&keychain.secp(), commit.clone(), proof.clone(), None).is_ok());
let rewind = rewind(keychain.secp(), &builder, commit.clone(), None, proof).unwrap();
assert!(rewind.is_some());
let (r_amount, r_id, r_switch) = rewind.unwrap();
assert_eq!(r_amount, amount);
assert_eq!(r_id, id);
assert_eq!(r_switch, switch);
commit
};
// Without switch commitment
let commit_b = {
let switch = SwitchCommitmentType::None;
let commit = keychain.commit(amount, &id, &switch).unwrap();
let proof = create(
&keychain,
&builder,
amount,
&id,
&switch,
commit.clone(),
None,
)
.unwrap();
assert!(verify(&keychain.secp(), commit.clone(), proof.clone(), None).is_ok());
let rewind = rewind(keychain.secp(), &builder, commit.clone(), None, proof).unwrap();
assert!(rewind.is_some());
let (r_amount, r_id, r_switch) = rewind.unwrap();
assert_eq!(r_amount, amount);
assert_eq!(r_id, id);
assert_eq!(r_switch, switch);
commit
};
// The resulting pedersen commitments should be different
assert_ne!(commit_a, commit_b);
}
#[test]
fn view_key() {
// TODO
/*let rng = &mut thread_rng();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let mut hasher = keychain.hasher();
let view_key = ViewKey::create(&keychain, keychain.master.clone(), &mut hasher, false).unwrap();
assert_eq!(builder.rewind_hash, view_key.rewind_hash);
let amount = rng.gen();
//let id = ExtKeychain::derive_key_id(3, rng.gen::<u16>() as u32, rng.gen::<u16>() as u32, rng.gen::<u16>() as u32, 0);
let id = ExtKeychain::derive_key_id(0, 0, 0, 0, 0);
let switch = SwitchCommitmentType::Regular;
println!("commit_0 = {:?}", keychain.commit(amount, &id, &SwitchCommitmentType::None).unwrap().0.to_vec());
let commit = keychain.commit(amount, &id, &switch).unwrap();
// Generate proof with ProofBuilder..
let proof = create(&keychain, &builder, amount, &id, &switch, commit.clone(), None).unwrap();
// ..and rewind with ViewKey
let rewind = rewind(keychain.secp(), &view_key, commit.clone(), None, proof);
assert!(rewind.is_ok());
let rewind = rewind.unwrap();
assert!(rewind.is_some());
let (r_amount, r_id, r_switch) = rewind.unwrap();
assert_eq!(r_amount, amount);
assert_eq!(r_id, id);
assert_eq!(r_switch, switch);*/
}
#[test]
fn view_key_no_switch() {
let rng = &mut thread_rng();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let mut hasher = keychain.hasher();
let view_key =
ViewKey::create(&keychain, keychain.master.clone(), &mut hasher, false).unwrap();
assert_eq!(builder.rewind_hash, view_key.rewind_hash);
let amount = rng.gen();
let id = ExtKeychain::derive_key_id(
3,
rng.gen::<u16>() as u32,
rng.gen::<u16>() as u32,
rng.gen::<u16>() as u32,
0,
);
let switch = SwitchCommitmentType::None;
let commit = keychain.commit(amount, &id, &switch).unwrap();
// Generate proof with ProofBuilder..
let proof = create(
&keychain,
&builder,
amount,
&id,
&switch,
commit.clone(),
None,
)
.unwrap();
// ..and rewind with ViewKey
let rewind = rewind(keychain.secp(), &view_key, commit.clone(), None, proof);
assert!(rewind.is_ok());
let rewind = rewind.unwrap();
assert!(rewind.is_some());
let (r_amount, r_id, r_switch) = rewind.unwrap();
assert_eq!(r_amount, amount);
assert_eq!(r_id, id);
assert_eq!(r_switch, switch);
}
#[test]
fn view_key_hardened() {
let rng = &mut thread_rng();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let mut hasher = keychain.hasher();
let view_key =
ViewKey::create(&keychain, keychain.master.clone(), &mut hasher, false).unwrap();
assert_eq!(builder.rewind_hash, view_key.rewind_hash);
let amount = rng.gen();
let id = ExtKeychain::derive_key_id(
3,
rng.gen::<u16>() as u32,
u32::max_value() - 2,
rng.gen::<u16>() as u32,
0,
);
let switch = SwitchCommitmentType::None;
let commit = keychain.commit(amount, &id, &switch).unwrap();
// Generate proof with ProofBuilder..
let proof = create(
&keychain,
&builder,
amount,
&id,
&switch,
commit.clone(),
None,
)
.unwrap();
// ..and rewind with ViewKey
let rewind = rewind(keychain.secp(), &view_key, commit.clone(), None, proof);
assert!(rewind.is_ok());
let rewind = rewind.unwrap();
assert!(rewind.is_none());
}
#[test]
fn view_key_child() {
let rng = &mut thread_rng();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let mut hasher = keychain.hasher();
let view_key =
ViewKey::create(&keychain, keychain.master.clone(), &mut hasher, false).unwrap();
assert_eq!(builder.rewind_hash, view_key.rewind_hash);
// Same child
{
let child_view_key = view_key
.ckd_pub(
keychain.secp(),
&mut hasher,
ChildNumber::from_normal_idx(10),
)
.unwrap();
assert_eq!(child_view_key.depth, 1);
let amount = rng.gen();
let id = ExtKeychain::derive_key_id(
3,
10,
rng.gen::<u16>() as u32,
rng.gen::<u16>() as u32,
0,
);
let switch = SwitchCommitmentType::None;
let commit = keychain.commit(amount, &id, &switch).unwrap();
// Generate proof with ProofBuilder..
let proof = create(
&keychain,
&builder,
amount,
&id,
&switch,
commit.clone(),
None,
)
.unwrap();
// ..and rewind with child ViewKey
let rewind = rewind(
keychain.secp(),
&child_view_key,
commit.clone(),
None,
proof,
);
assert!(rewind.is_ok());
let rewind = rewind.unwrap();
assert!(rewind.is_some());
let (r_amount, r_id, r_switch) = rewind.unwrap();
assert_eq!(r_amount, amount);
assert_eq!(r_id, id);
assert_eq!(r_switch, switch);
}
// Different child
{
let child_view_key = view_key
.ckd_pub(
keychain.secp(),
&mut hasher,
ChildNumber::from_normal_idx(11),
)
.unwrap();
assert_eq!(child_view_key.depth, 1);
let amount = rng.gen();
let id = ExtKeychain::derive_key_id(
3,
10,
rng.gen::<u16>() as u32,
rng.gen::<u16>() as u32,
0,
);
let switch = SwitchCommitmentType::None;
let commit = keychain.commit(amount, &id, &switch).unwrap();
// Generate proof with ProofBuilder..
let proof = create(
&keychain,
&builder,
amount,
&id,
&switch,
commit.clone(),
None,
)
.unwrap();
// ..and rewind with child ViewKey
let rewind = rewind(
keychain.secp(),
&child_view_key,
commit.clone(),
None,
proof,
);
assert!(rewind.is_ok());
let rewind = rewind.unwrap();
assert!(rewind.is_none());
}
}
}

View file

@ -19,25 +19,33 @@ use crate::core::transaction::kernel_sig_msg;
use crate::core::{KernelFeatures, Output, OutputFeatures, TxKernel};
use crate::keychain::{Identifier, Keychain};
use crate::libtx::error::Error;
use crate::libtx::{aggsig, proof};
use crate::libtx::{
aggsig,
proof::{self, ProofBuild},
};
use crate::util::{secp, static_secp_instance};
use grin_keychain::SwitchCommitmentType;
/// output a reward output
pub fn output<K>(
pub fn output<K, B>(
keychain: &K,
builder: &B,
key_id: &Identifier,
fees: u64,
test_mode: bool,
) -> Result<(Output, TxKernel), Error>
where
K: Keychain,
B: ProofBuild,
{
let value = reward(fees);
let commit = keychain.commit(value, key_id)?;
// TODO: proper support for different switch commitment schemes
let switch = &SwitchCommitmentType::Regular;
let commit = keychain.commit(value, key_id, switch)?;
trace!("Block reward - Pedersen Commit is: {:?}", commit,);
let rproof = proof::create(keychain, value, key_id, commit, None)?;
let rproof = proof::create(keychain, builder, value, key_id, switch, commit, None)?;
let output = Output {
features: OutputFeatures::Coinbase,

View file

@ -34,6 +34,7 @@ use num;
#[macro_use]
mod common;
pub mod cuckaroo;
pub mod cuckarood;
pub mod cuckatoo;
mod error;
#[allow(dead_code)]
@ -49,6 +50,7 @@ use chrono::prelude::{DateTime, NaiveDateTime, Utc};
pub use self::common::EdgeType;
pub use self::types::*;
pub use crate::pow::cuckaroo::{new_cuckaroo_ctx, CuckarooContext};
pub use crate::pow::cuckarood::{new_cuckarood_ctx, CuckaroodContext};
pub use crate::pow::cuckatoo::{new_cuckatoo_ctx, CuckatooContext};
pub use crate::pow::error::Error;

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Common types and traits for cuckoo/cuckatoo family of solvers
//! Common types and traits for cuckoo family of solvers
use crate::blake2::blake2b::blake2b;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};

View file

@ -43,7 +43,7 @@ where
Ok(Box::new(CuckarooContext { params }))
}
/// Cuckatoo cycle context. Only includes the verifier for now.
/// Cuckaroo cycle context. Only includes the verifier for now.
pub struct CuckarooContext<T>
where
T: EdgeType,
@ -84,7 +84,8 @@ where
if n > 0 && nonces[n] <= nonces[n - 1] {
return Err(ErrorKind::Verification("edges not ascending".to_owned()))?;
}
let edge = to_edge!(T, siphash_block(&self.params.siphash_keys, nonces[n]));
// 21 is standard siphash rotation constant
let edge = to_edge!(T, siphash_block(&self.params.siphash_keys, nonces[n], 21));
uvs[2 * n] = to_u64!(edge & self.params.edge_mask);
uvs[2 * n + 1] = to_u64!((edge >> 32) & self.params.edge_mask);
xor0 ^= uvs[2 * n];

195
core/src/pow/cuckarood.rs Normal file
View file

@ -0,0 +1,195 @@
// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Implementation of Cuckarood Cycle, based on Cuckoo Cycle designed by
//! John Tromp. Ported to Rust from https://github.com/tromp/cuckoo.
//!
//! Cuckarood is a variation of Cuckaroo that's tweaked at the first HardFork
//! to maintain ASIC-Resistance, as introduced in
//! https://www.grin-forum.org/t/mid-july-pow-hardfork-cuckaroo29-cuckarood29
//! It uses a tweaked siphash round in which the rotation by 21 is replaced by
//! a rotation by 25, halves the number of graph nodes in each partition,
//! and requires cycles to alternate between even- and odd-indexed edges.
use crate::global;
use crate::pow::common::{CuckooParams, EdgeType};
use crate::pow::error::{Error, ErrorKind};
use crate::pow::siphash::siphash_block;
use crate::pow::{PoWContext, Proof};
/// Instantiate a new CuckaroodContext as a PowContext. Note that this can't
/// be moved in the PoWContext trait as this particular trait needs to be
/// convertible to an object trait.
pub fn new_cuckarood_ctx<T>(
edge_bits: u8,
proof_size: usize,
) -> Result<Box<dyn PoWContext<T>>, Error>
where
T: EdgeType + 'static,
{
let params = CuckooParams::new(edge_bits, proof_size)?;
Ok(Box::new(CuckaroodContext { params }))
}
/// Cuckarood cycle context. Only includes the verifier for now.
pub struct CuckaroodContext<T>
where
T: EdgeType,
{
params: CuckooParams<T>,
}
impl<T> PoWContext<T> for CuckaroodContext<T>
where
T: EdgeType,
{
fn set_header_nonce(
&mut self,
header: Vec<u8>,
nonce: Option<u32>,
_solve: bool,
) -> Result<(), Error> {
self.params.reset_header_nonce(header, nonce)
}
fn find_cycles(&mut self) -> Result<Vec<Proof>, Error> {
unimplemented!()
}
fn verify(&self, proof: &Proof) -> Result<(), Error> {
if proof.proof_size() != global::proofsize() {
return Err(ErrorKind::Verification("wrong cycle length".to_owned()))?;
}
let nonces = &proof.nonces;
let mut uvs = vec![0u64; 2 * proof.proof_size()];
let mut ndir = vec![0usize; 2];
let mut xor0: u64 = 0;
let mut xor1: u64 = 0;
let nodemask = self.params.edge_mask >> 1;
for n in 0..proof.proof_size() {
let dir = (nonces[n] & 1) as usize;
if ndir[dir] >= proof.proof_size() / 2 {
return Err(ErrorKind::Verification("edges not balanced".to_owned()))?;
}
if nonces[n] > to_u64!(self.params.edge_mask) {
return Err(ErrorKind::Verification("edge too big".to_owned()))?;
}
if n > 0 && nonces[n] <= nonces[n - 1] {
return Err(ErrorKind::Verification("edges not ascending".to_owned()))?;
}
let edge = to_edge!(T, siphash_block(&self.params.siphash_keys, nonces[n], 25));
let idx = 4 * ndir[dir] + 2 * dir;
uvs[idx] = to_u64!(edge & nodemask);
uvs[idx + 1] = to_u64!((edge >> 32) & nodemask);
xor0 ^= uvs[idx];
xor1 ^= uvs[idx + 1];
ndir[dir] += 1;
}
if xor0 | xor1 != 0 {
return Err(ErrorKind::Verification(
"endpoints don't match up".to_owned(),
))?;
}
let mut n = 0;
let mut i = 0;
let mut j;
loop {
// follow cycle
j = i;
for k in (((i % 4) ^ 2)..(2 * self.params.proof_size)).step_by(4) {
if uvs[k] == uvs[i] {
// find reverse edge endpoint identical to one at i
if j != i {
return Err(ErrorKind::Verification("branch in cycle".to_owned()))?;
}
j = k;
}
}
if j == i {
return Err(ErrorKind::Verification("cycle dead ends".to_owned()))?;
}
i = j ^ 1;
n += 1;
if i == 0 {
break;
}
}
if n == self.params.proof_size {
Ok(())
} else {
Err(ErrorKind::Verification("cycle too short".to_owned()))?
}
}
}
#[cfg(test)]
mod test {
use super::*;
// empty header, nonce 64
static V1_19_HASH: [u64; 4] = [
0x89f81d7da5e674df,
0x7586b93105a5fd13,
0x6fbe212dd4e8c001,
0x8800c93a8431f938,
];
static V1_19_SOL: [u64; 42] = [
0xa00, 0x3ffb, 0xa474, 0xdc27, 0x182e6, 0x242cc, 0x24de4, 0x270a2, 0x28356, 0x2951f,
0x2a6ae, 0x2c889, 0x355c7, 0x3863b, 0x3bd7e, 0x3cdbc, 0x3ff95, 0x430b6, 0x4ba1a, 0x4bd7e,
0x4c59f, 0x4f76d, 0x52064, 0x5378c, 0x540a3, 0x5af6b, 0x5b041, 0x5e9d3, 0x64ec7, 0x6564b,
0x66763, 0x66899, 0x66e80, 0x68e4e, 0x69133, 0x6b20a, 0x6c2d7, 0x6fd3b, 0x79a8a, 0x79e29,
0x7ae52, 0x7defe,
];
// empty header, nonce 15
static V2_29_HASH: [u64; 4] = [
0xe2f917b2d79492ed,
0xf51088eaaa3a07a0,
0xaf4d4288d36a4fa8,
0xc8cdfd30a54e0581,
];
static V2_29_SOL: [u64; 42] = [
0x1a9629, 0x1fb257, 0x5dc22a, 0xf3d0b0, 0x200c474, 0x24bd68f, 0x48ad104, 0x4a17170,
0x4ca9a41, 0x55f983f, 0x6076c91, 0x6256ffc, 0x63b60a1, 0x7fd5b16, 0x985bff8, 0xaae71f3,
0xb71f7b4, 0xb989679, 0xc09b7b8, 0xd7601da, 0xd7ab1b6, 0xef1c727, 0xf1e702b, 0xfd6d961,
0xfdf0007, 0x10248134, 0x114657f6, 0x11f52612, 0x12887251, 0x13596b4b, 0x15e8d831,
0x16b4c9e5, 0x17097420, 0x1718afca, 0x187fc40c, 0x19359788, 0x1b41d3f1, 0x1bea25a7,
0x1d28df0f, 0x1ea6c4a0, 0x1f9bf79f, 0x1fa005c6,
];
#[test]
fn cuckarood19_29_vectors() {
let mut ctx19 = new_impl::<u64>(19, 42);
ctx19.params.siphash_keys = V1_19_HASH.clone();
assert!(ctx19
.verify(&Proof::new(V1_19_SOL.to_vec().clone()))
.is_ok());
assert!(ctx19.verify(&Proof::zero(42)).is_err());
let mut ctx29 = new_impl::<u64>(29, 42);
ctx29.params.siphash_keys = V2_29_HASH.clone();
assert!(ctx29
.verify(&Proof::new(V2_29_SOL.to_vec().clone()))
.is_ok());
assert!(ctx29.verify(&Proof::zero(42)).is_err());
}
fn new_impl<T>(edge_bits: u8, proof_size: usize) -> CuckaroodContext<T>
where
T: EdgeType,
{
let params = CuckooParams::new(edge_bits, proof_size).unwrap();
CuckaroodContext { params }
}
}

View file

@ -32,14 +32,14 @@ macro_rules! rotl {
/// a nonce
pub fn siphash24(v: &[u64; 4], nonce: u64) -> u64 {
let mut siphash = SipHash24::new(v);
siphash.hash(nonce);
siphash.hash(nonce, 21); // 21 is standard rotation constant
siphash.digest()
}
/// Builds a block of siphash values by repeatedly hashing from the nonce
/// truncated to its closest block start, up to the end of the block. Returns
/// the resulting hash at the nonce's position.
pub fn siphash_block(v: &[u64; 4], nonce: u64) -> u64 {
pub fn siphash_block(v: &[u64; 4], nonce: u64, rot_e: u8) -> u64 {
// beginning of the block of hashes
let nonce0 = nonce & !SIPHASH_BLOCK_MASK;
let mut nonce_hash = 0;
@ -47,7 +47,7 @@ pub fn siphash_block(v: &[u64; 4], nonce: u64) -> u64 {
// repeated hashing over the whole block
let mut siphash = SipHash24::new(v);
for n in nonce0..(nonce0 + SIPHASH_BLOCK_SIZE) {
siphash.hash(n);
siphash.hash(n, rot_e);
if n == nonce {
nonce_hash = siphash.digest();
}
@ -80,16 +80,16 @@ impl SipHash24 {
}
/// One siphash24 hashing, consisting of 2 and then 4 rounds
pub fn hash(&mut self, nonce: u64) {
pub fn hash(&mut self, nonce: u64, rot_e: u8) {
self.3 ^= nonce;
self.round();
self.round();
self.round(rot_e);
self.round(rot_e);
self.0 ^= nonce;
self.2 ^= 0xff;
for _ in 0..4 {
self.round();
self.round(rot_e);
}
}
@ -98,7 +98,7 @@ impl SipHash24 {
(self.0 ^ self.1) ^ (self.2 ^ self.3)
}
fn round(&mut self) {
fn round(&mut self, rot_e: u8) {
self.0 = self.0.wrapping_add(self.1);
self.2 = self.2.wrapping_add(self.3);
rotl!(self.1, 13);
@ -109,7 +109,7 @@ impl SipHash24 {
self.2 = self.2.wrapping_add(self.1);
self.0 = self.0.wrapping_add(self.3);
rotl!(self.1, 17);
rotl!(self.3, 21);
rotl!(self.3, rot_e);
self.1 ^= self.2;
self.3 ^= self.0;
rotl!(self.2, 32);
@ -130,8 +130,8 @@ mod test {
#[test]
fn hash_block() {
assert_eq!(siphash_block(&[1, 2, 3, 4], 10), 1182162244994096396);
assert_eq!(siphash_block(&[1, 2, 3, 4], 123), 11303676240481718781);
assert_eq!(siphash_block(&[9, 7, 6, 7], 12), 4886136884237259030);
assert_eq!(siphash_block(&[1, 2, 3, 4], 10, 21), 1182162244994096396);
assert_eq!(siphash_block(&[1, 2, 3, 4], 123, 21), 11303676240481718781);
assert_eq!(siphash_block(&[9, 7, 6, 7], 12, 21), 4886136884237259030);
}
}

View file

@ -25,6 +25,7 @@ use crate::core::core::{
Block, BlockHeader, CompactBlock, HeaderVersion, KernelFeatures, OutputFeatures,
};
use crate::core::libtx::build::{self, input, output, with_fee};
use crate::core::libtx::ProofBuilder;
use crate::core::{global, ser};
use crate::keychain::{BlindingFactor, ExtKeychain, Keychain};
use crate::util::secp;
@ -45,6 +46,7 @@ fn verifier_cache() -> Arc<RwLock<dyn VerifierCache>> {
#[allow(dead_code)]
fn too_large_block() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let max_out = global::max_block_weight() / BLOCK_OUTPUT_WEIGHT;
let mut pks = vec![];
@ -59,12 +61,12 @@ fn too_large_block() {
let now = Instant::now();
parts.append(&mut vec![input(500000, pks.pop().unwrap()), with_fee(2)]);
let tx = build::transaction(parts, &keychain).unwrap();
let tx = build::transaction(parts, &keychain, &builder).unwrap();
println!("Build tx: {}", now.elapsed().as_secs());
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![&tx], &keychain, &prev, &key_id);
let b = new_block(vec![&tx], &keychain, &builder, &prev, &key_id);
assert!(b
.validate(&BlindingFactor::zero(), verifier_cache())
.is_err());
@ -86,6 +88,7 @@ fn very_empty_block() {
// builds a block with a tx spending another and check that cut_through occurred
fn block_with_cut_through() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let key_id2 = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
@ -94,17 +97,19 @@ fn block_with_cut_through() {
let mut btx2 = build::transaction(
vec![input(7, key_id1), output(5, key_id2.clone()), with_fee(2)],
&keychain,
&builder,
)
.unwrap();
// spending tx2 - reuse key_id2
let mut btx3 = txspend1i1o(5, &keychain, key_id2.clone(), key_id3);
let mut btx3 = txspend1i1o(5, &keychain, &builder, key_id2.clone(), key_id3);
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(
vec![&mut btx1, &mut btx2, &mut btx3],
&keychain,
&builder,
&prev,
&key_id,
);
@ -120,9 +125,10 @@ fn block_with_cut_through() {
#[test]
fn empty_block_with_coinbase_is_valid() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![], &keychain, &prev, &key_id);
let b = new_block(vec![], &keychain, &builder, &prev, &key_id);
assert_eq!(b.inputs().len(), 0);
assert_eq!(b.outputs().len(), 1);
@ -157,9 +163,10 @@ fn empty_block_with_coinbase_is_valid() {
// additionally verifying the merkle_inputs_outputs also fails
fn remove_coinbase_output_flag() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let mut b = new_block(vec![], &keychain, &prev, &key_id);
let mut b = new_block(vec![], &keychain, &builder, &prev, &key_id);
assert!(b.outputs()[0].is_coinbase());
b.outputs_mut()[0].features = OutputFeatures::Plain;
@ -179,9 +186,10 @@ fn remove_coinbase_output_flag() {
// invalidates the block and specifically it causes verify_coinbase to fail
fn remove_coinbase_kernel_flag() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let mut b = new_block(vec![], &keychain, &prev, &key_id);
let mut b = new_block(vec![], &keychain, &builder, &prev, &key_id);
assert!(b.kernels()[0].is_coinbase());
b.kernels_mut()[0].features = KernelFeatures::Plain;
@ -220,9 +228,10 @@ fn serialize_deserialize_header_version() {
#[test]
fn serialize_deserialize_block_header() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![], &keychain, &prev, &key_id);
let b = new_block(vec![], &keychain, &builder, &prev, &key_id);
let header1 = b.header;
let mut vec = Vec::new();
@ -237,9 +246,10 @@ fn serialize_deserialize_block_header() {
fn serialize_deserialize_block() {
let tx1 = tx1i2o();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
let b = new_block(vec![&tx1], &keychain, &builder, &prev, &key_id);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b).expect("serialization failed");
@ -255,9 +265,10 @@ fn serialize_deserialize_block() {
#[test]
fn empty_block_serialized_size() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![], &keychain, &prev, &key_id);
let b = new_block(vec![], &keychain, &builder, &prev, &key_id);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b).expect("serialization failed");
let target_len = 1_265;
@ -267,10 +278,11 @@ fn empty_block_serialized_size() {
#[test]
fn block_single_tx_serialized_size() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let tx1 = tx1i2o();
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
let b = new_block(vec![&tx1], &keychain, &builder, &prev, &key_id);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b).expect("serialization failed");
let target_len = 2_847;
@ -280,9 +292,10 @@ fn block_single_tx_serialized_size() {
#[test]
fn empty_compact_block_serialized_size() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![], &keychain, &prev, &key_id);
let b = new_block(vec![], &keychain, &builder, &prev, &key_id);
let cb: CompactBlock = b.into();
let mut vec = Vec::new();
ser::serialize(&mut vec, &cb).expect("serialization failed");
@ -293,10 +306,11 @@ fn empty_compact_block_serialized_size() {
#[test]
fn compact_block_single_tx_serialized_size() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let tx1 = tx1i2o();
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
let b = new_block(vec![&tx1], &keychain, &builder, &prev, &key_id);
let cb: CompactBlock = b.into();
let mut vec = Vec::new();
ser::serialize(&mut vec, &cb).expect("serialization failed");
@ -307,6 +321,7 @@ fn compact_block_single_tx_serialized_size() {
#[test]
fn block_10_tx_serialized_size() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
global::set_mining_mode(global::ChainTypes::Mainnet);
let mut txs = vec![];
@ -316,7 +331,7 @@ fn block_10_tx_serialized_size() {
}
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(txs.iter().collect(), &keychain, &prev, &key_id);
let b = new_block(txs.iter().collect(), &keychain, &builder, &prev, &key_id);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b).expect("serialization failed");
let target_len = 17_085;
@ -326,6 +341,7 @@ fn block_10_tx_serialized_size() {
#[test]
fn compact_block_10_tx_serialized_size() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let mut txs = vec![];
for _ in 0..10 {
@ -334,7 +350,7 @@ fn compact_block_10_tx_serialized_size() {
}
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(txs.iter().collect(), &keychain, &prev, &key_id);
let b = new_block(txs.iter().collect(), &keychain, &builder, &prev, &key_id);
let cb: CompactBlock = b.into();
let mut vec = Vec::new();
ser::serialize(&mut vec, &cb).expect("serialization failed");
@ -345,10 +361,11 @@ fn compact_block_10_tx_serialized_size() {
#[test]
fn compact_block_hash_with_nonce() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let tx = tx1i2o();
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![&tx], &keychain, &prev, &key_id);
let b = new_block(vec![&tx], &keychain, &builder, &prev, &key_id);
let cb1: CompactBlock = b.clone().into();
let cb2: CompactBlock = b.clone().into();
@ -375,10 +392,11 @@ fn compact_block_hash_with_nonce() {
#[test]
fn convert_block_to_compact_block() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let tx1 = tx1i2o();
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
let b = new_block(vec![&tx1], &keychain, &builder, &prev, &key_id);
let cb: CompactBlock = b.clone().into();
assert_eq!(cb.out_full().len(), 1);
@ -398,9 +416,10 @@ fn convert_block_to_compact_block() {
#[test]
fn hydrate_empty_compact_block() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![], &keychain, &prev, &key_id);
let b = new_block(vec![], &keychain, &builder, &prev, &key_id);
let cb: CompactBlock = b.clone().into();
let hb = Block::hydrate_from(cb, vec![]).unwrap();
assert_eq!(hb.header, b.header);
@ -411,10 +430,11 @@ fn hydrate_empty_compact_block() {
#[test]
fn serialize_deserialize_compact_block() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let tx1 = tx1i2o();
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
let b = new_block(vec![&tx1], &keychain, &builder, &prev, &key_id);
let mut cb1: CompactBlock = b.into();
@ -437,6 +457,7 @@ fn serialize_deserialize_compact_block() {
#[test]
fn same_amount_outputs_copy_range_proof() {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = keychain::ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let key_id2 = keychain::ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
@ -449,6 +470,7 @@ fn same_amount_outputs_copy_range_proof() {
with_fee(1),
],
&keychain,
&builder,
)
.unwrap();
@ -468,6 +490,7 @@ fn same_amount_outputs_copy_range_proof() {
kernels.clone(),
)],
&keychain,
&builder,
&prev,
&key_id,
);
@ -484,6 +507,7 @@ fn same_amount_outputs_copy_range_proof() {
#[test]
fn wrong_amount_range_proof() {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = keychain::ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let key_id2 = keychain::ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
@ -496,6 +520,7 @@ fn wrong_amount_range_proof() {
with_fee(1),
],
&keychain,
&builder,
)
.unwrap();
let tx2 = build::transaction(
@ -506,6 +531,7 @@ fn wrong_amount_range_proof() {
with_fee(1),
],
&keychain,
&builder,
)
.unwrap();
@ -525,6 +551,7 @@ fn wrong_amount_range_proof() {
kernels.clone(),
)],
&keychain,
&builder,
&prev,
&key_id,
);

View file

@ -21,6 +21,7 @@ use grin_core::core::{
};
use grin_core::libtx::{
build::{self, input, output, with_fee},
proof::{ProofBuild, ProofBuilder},
reward,
};
use grin_core::pow::Difficulty;
@ -29,6 +30,7 @@ use grin_keychain as keychain;
// utility producing a transaction with 2 inputs and a single outputs
pub fn tx2i1o() -> Transaction {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = keychain::ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let key_id2 = keychain::ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
@ -41,6 +43,7 @@ pub fn tx2i1o() -> Transaction {
with_fee(2),
],
&keychain,
&builder,
)
.unwrap()
}
@ -48,12 +51,14 @@ pub fn tx2i1o() -> Transaction {
// utility producing a transaction with a single input and output
pub fn tx1i1o() -> Transaction {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = keychain::ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let key_id2 = keychain::ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
build::transaction(
vec![input(5, key_id1), output(3, key_id2), with_fee(2)],
&keychain,
&builder,
)
.unwrap()
}
@ -63,6 +68,7 @@ pub fn tx1i1o() -> Transaction {
// Note: this tx has an "offset" kernel
pub fn tx1i2o() -> Transaction {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = keychain::ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let key_id2 = keychain::ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
@ -75,23 +81,26 @@ pub fn tx1i2o() -> Transaction {
with_fee(2),
],
&keychain,
&builder,
)
.unwrap()
}
// utility to create a block without worrying about the key or previous
// header
pub fn new_block<K>(
pub fn new_block<K, B>(
txs: Vec<&Transaction>,
keychain: &K,
builder: &B,
previous_header: &BlockHeader,
key_id: &Identifier,
) -> Block
where
K: Keychain,
B: ProofBuild,
{
let fees = txs.iter().map(|tx| tx.fee()).sum();
let reward_output = reward::output(keychain, &key_id, fees, false).unwrap();
let reward_output = reward::output(keychain, builder, &key_id, fees, false).unwrap();
Block::new(
&previous_header,
txs.into_iter().cloned().collect(),
@ -103,13 +112,21 @@ where
// utility producing a transaction that spends an output with the provided
// value and blinding key
pub fn txspend1i1o<K>(v: u64, keychain: &K, key_id1: Identifier, key_id2: Identifier) -> Transaction
pub fn txspend1i1o<K, B>(
v: u64,
keychain: &K,
builder: &B,
key_id1: Identifier,
key_id2: Identifier,
) -> Transaction
where
K: Keychain,
B: ProofBuild,
{
build::transaction(
vec![input(v, key_id1), output(3, key_id2), with_fee(2)],
keychain,
builder,
)
.unwrap()
}

View file

@ -618,38 +618,75 @@ fn test_secondary_pow_scale() {
#[test]
fn hard_forks() {
assert!(valid_header_version(0, HeaderVersion::new(1)));
assert!(valid_header_version(10, HeaderVersion::new(1)));
assert!(!valid_header_version(10, HeaderVersion::new(2)));
assert!(valid_header_version(
YEAR_HEIGHT / 2 - 1,
HeaderVersion::new(1)
));
// v2 not active yet
assert!(!valid_header_version(
YEAR_HEIGHT / 2,
HeaderVersion::new(2)
));
assert!(!valid_header_version(
YEAR_HEIGHT / 2,
HeaderVersion::new(1)
));
assert!(!valid_header_version(YEAR_HEIGHT, HeaderVersion::new(1)));
assert!(!valid_header_version(
YEAR_HEIGHT / 2 + 1,
HeaderVersion::new(2)
));
// Tests for mainnet chain type.
{
global::set_mining_mode(global::ChainTypes::Mainnet);
assert_eq!(global::is_floonet(), false);
assert!(valid_header_version(0, HeaderVersion::new(1)));
assert!(valid_header_version(10, HeaderVersion::new(1)));
assert!(!valid_header_version(10, HeaderVersion::new(2)));
assert!(valid_header_version(
YEAR_HEIGHT / 2 - 1,
HeaderVersion::new(1)
));
assert!(valid_header_version(YEAR_HEIGHT / 2, HeaderVersion::new(2)));
assert!(valid_header_version(
YEAR_HEIGHT / 2 + 1,
HeaderVersion::new(2)
));
assert!(!valid_header_version(
YEAR_HEIGHT / 2,
HeaderVersion::new(1)
));
assert!(!valid_header_version(YEAR_HEIGHT, HeaderVersion::new(1)));
// v3 not active yet
assert!(!valid_header_version(YEAR_HEIGHT, HeaderVersion::new(3)));
assert!(!valid_header_version(YEAR_HEIGHT, HeaderVersion::new(2)));
assert!(!valid_header_version(YEAR_HEIGHT, HeaderVersion::new(1)));
assert!(!valid_header_version(
YEAR_HEIGHT * 3 / 2,
HeaderVersion::new(2)
));
assert!(!valid_header_version(
YEAR_HEIGHT + 1,
HeaderVersion::new(2)
));
}
// Tests for floonet chain type.
{
global::set_mining_mode(global::ChainTypes::Floonet);
assert_eq!(global::is_floonet(), true);
assert!(valid_header_version(0, HeaderVersion::new(1)));
assert!(valid_header_version(10, HeaderVersion::new(1)));
assert!(!valid_header_version(10, HeaderVersion::new(2)));
assert!(valid_header_version(
FLOONET_FIRST_HARD_FORK - 1,
HeaderVersion::new(1)
));
assert!(valid_header_version(
FLOONET_FIRST_HARD_FORK,
HeaderVersion::new(2)
));
assert!(valid_header_version(
FLOONET_FIRST_HARD_FORK + 1,
HeaderVersion::new(2)
));
assert!(!valid_header_version(
FLOONET_FIRST_HARD_FORK,
HeaderVersion::new(1)
));
assert!(!valid_header_version(YEAR_HEIGHT, HeaderVersion::new(1)));
// v3 not active yet
assert!(!valid_header_version(YEAR_HEIGHT, HeaderVersion::new(3)));
assert!(!valid_header_version(YEAR_HEIGHT, HeaderVersion::new(2)));
assert!(!valid_header_version(YEAR_HEIGHT, HeaderVersion::new(1)));
assert!(!valid_header_version(
YEAR_HEIGHT * 3 / 2,
HeaderVersion::new(2)
));
assert!(!valid_header_version(
YEAR_HEIGHT + 1,
HeaderVersion::new(2)
));
}
}
// #[test]
// fn hard_fork_2() {
// assert!(valid_header_version(0, 1));
// assert!(valid_header_version(10, 1));
// assert!(valid_header_version(10, 2));
// assert!(valid_header_version(250_000, 1));
// assert!(!valid_header_version(250_001, 1));
// assert!(!valid_header_version(500_000, 1));
// assert!(valid_header_version(250_001, 2));
// assert!(valid_header_version(500_000, 2));
// assert!(!valid_header_version(500_001, 2));
// }

View file

@ -24,6 +24,7 @@ use self::core::core::{aggregate, deaggregate, KernelFeatures, Output, Transacti
use self::core::libtx::build::{
self, initial_tx, input, output, with_excess, with_fee, with_lock_height,
};
use self::core::libtx::ProofBuilder;
use self::core::ser;
use self::keychain::{BlindingFactor, ExtKeychain, Keychain};
use self::util::static_secp_instance;
@ -75,18 +76,15 @@ fn tx_double_ser_deser() {
#[test]
#[should_panic(expected = "Keychain Error")]
fn test_zero_commit_fails() {
let mut keychain = ExtKeychain::from_random_seed(false).unwrap();
keychain.set_use_switch_commits(false);
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
// blinding should fail as signing with a zero r*G shouldn't work
build::transaction(
vec![
input(10, key_id1.clone()),
output(9, key_id1.clone()),
with_fee(1),
],
vec![input(10, key_id1.clone()), output(10, key_id1.clone())],
&keychain,
&builder,
)
.unwrap();
}
@ -98,6 +96,7 @@ fn verifier_cache() -> Arc<RwLock<dyn VerifierCache>> {
#[test]
fn build_tx_kernel() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let key_id2 = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
@ -111,6 +110,7 @@ fn build_tx_kernel() {
with_fee(2),
],
&keychain,
&builder,
)
.unwrap();
@ -350,6 +350,7 @@ fn basic_transaction_deaggregation() {
#[test]
fn hash_output() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let key_id2 = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
@ -362,6 +363,7 @@ fn hash_output() {
with_fee(1),
],
&keychain,
&builder,
)
.unwrap();
let h = tx.outputs()[0].hash();
@ -407,6 +409,7 @@ fn tx_hash_diff() {
#[test]
fn tx_build_exchange() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let key_id2 = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
@ -419,9 +422,12 @@ fn tx_build_exchange() {
// Alice builds her transaction, with change, which also produces the sum
// of blinding factors before they're obscured.
let (tx, sum) =
build::partial_transaction(vec![in1, in2, output(1, key_id3), with_fee(2)], &keychain)
.unwrap();
let (tx, sum) = build::partial_transaction(
vec![in1, in2, output(1, key_id3), with_fee(2)],
&keychain,
&builder,
)
.unwrap();
(tx, sum)
};
@ -436,6 +442,7 @@ fn tx_build_exchange() {
output(4, key_id4),
],
&keychain,
&builder,
)
.unwrap();
@ -447,11 +454,12 @@ fn tx_build_exchange() {
#[test]
fn reward_empty_block() {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let previous_header = BlockHeader::default();
let b = new_block(vec![], &keychain, &previous_header, &key_id);
let b = new_block(vec![], &keychain, &builder, &previous_header, &key_id);
b.cut_through()
.unwrap()
@ -462,6 +470,7 @@ fn reward_empty_block() {
#[test]
fn reward_with_tx_block() {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let vc = verifier_cache();
@ -471,7 +480,13 @@ fn reward_with_tx_block() {
let previous_header = BlockHeader::default();
let block = new_block(vec![&mut tx1], &keychain, &previous_header, &key_id);
let block = new_block(
vec![&mut tx1],
&keychain,
&builder,
&previous_header,
&key_id,
);
block
.cut_through()
.unwrap()
@ -482,6 +497,7 @@ fn reward_with_tx_block() {
#[test]
fn simple_block() {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let vc = verifier_cache();
@ -493,6 +509,7 @@ fn simple_block() {
let b = new_block(
vec![&mut tx1, &mut tx2],
&keychain,
&builder,
&previous_header,
&key_id,
);
@ -503,7 +520,7 @@ fn simple_block() {
#[test]
fn test_block_with_timelocked_tx() {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let key_id2 = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
@ -520,12 +537,19 @@ fn test_block_with_timelocked_tx() {
with_lock_height(1),
],
&keychain,
&builder,
)
.unwrap();
let previous_header = BlockHeader::default();
let b = new_block(vec![&tx1], &keychain, &previous_header, &key_id3.clone());
let b = new_block(
vec![&tx1],
&keychain,
&builder,
&previous_header,
&key_id3.clone(),
);
b.validate(&BlindingFactor::zero(), vc.clone()).unwrap();
// now try adding a timelocked tx where lock height is greater than current
@ -538,11 +562,18 @@ fn test_block_with_timelocked_tx() {
with_lock_height(2),
],
&keychain,
&builder,
)
.unwrap();
let previous_header = BlockHeader::default();
let b = new_block(vec![&tx1], &keychain, &previous_header, &key_id3.clone());
let b = new_block(
vec![&tx1],
&keychain,
&builder,
&previous_header,
&key_id3.clone(),
);
match b.validate(&BlindingFactor::zero(), vc.clone()) {
Err(KernelLockHeight(height)) => {

View file

@ -27,8 +27,10 @@ use grin_keychain as keychain;
fn test_output_ser_deser() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let commit = keychain.commit(5, &key_id).unwrap();
let proof = proof::create(&keychain, 5, &key_id, commit, None).unwrap();
let switch = &keychain::SwitchCommitmentType::Regular;
let commit = keychain.commit(5, &key_id, switch).unwrap();
let builder = proof::ProofBuilder::new(&keychain);
let proof = proof::create(&keychain, &builder, 5, &key_id, switch, commit, None).unwrap();
let out = Output {
features: OutputFeatures::Plain,

View file

@ -17,7 +17,7 @@ pub mod common;
use self::core::core::verifier_cache::{LruVerifierCache, VerifierCache};
use self::core::core::{Output, OutputFeatures};
use self::core::libtx::proof;
use self::keychain::{ExtKeychain, Keychain};
use self::keychain::{ExtKeychain, Keychain, SwitchCommitmentType};
use self::util::RwLock;
use grin_core as core;
use grin_keychain as keychain;
@ -34,8 +34,10 @@ fn test_verifier_cache_rangeproofs() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let commit = keychain.commit(5, &key_id).unwrap();
let proof = proof::create(&keychain, 5, &key_id, commit, None).unwrap();
let switch = &SwitchCommitmentType::Regular;
let commit = keychain.commit(5, &key_id, switch).unwrap();
let builder = proof::ProofBuilder::new(&keychain);
let proof = proof::create(&keychain, &builder, 5, &key_id, switch, commit, None).unwrap();
let out = Output {
features: OutputFeatures::Plain,

View file

@ -1,6 +1,6 @@
[package]
name = "grin_keychain"
version = "1.1.1-beta.1"
version = "2.0.0-beta.2"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Chain implementation for grin, a simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format."
license = "Apache-2.0"
@ -19,7 +19,7 @@ serde_derive = "1"
serde_json = "1"
uuid = { version = "0.6", features = ["serde", "v4"] }
lazy_static = "1"
zeroize = "0.8.0"
zeroize = "0.9"
digest = "0.7"
hmac = "0.6"
@ -27,4 +27,4 @@ ripemd160 = "0.7"
sha2 = "0.7"
pbkdf2 = "0.2"
grin_util = { path = "../util", version = "1.1.1-beta.1" }
grin_util = { path = "../util", version = "2.0.0-beta.2" }

View file

@ -149,7 +149,7 @@ impl BIP32Hasher for BIP32GrinHasher {
}
/// Extended private key
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ExtendedPrivKey {
/// The network this key is to be used on
pub network: [u8; 4],
@ -399,7 +399,7 @@ impl ExtendedPrivKey {
where
H: BIP32Hasher,
{
let mut sk: ExtendedPrivKey = *self;
let mut sk: ExtendedPrivKey = self.clone();
for cnum in cnums {
sk = sk.ckd_priv(secp, hasher, *cnum)?;
}

View file

@ -17,22 +17,33 @@
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use crate::blake2;
use crate::blake2::blake2b::blake2b;
use crate::extkey_bip32::{BIP32GrinHasher, ExtendedPrivKey};
use crate::types::{BlindSum, BlindingFactor, Error, ExtKeychainPath, Identifier, Keychain};
use crate::util::secp::key::SecretKey;
use crate::extkey_bip32::{BIP32GrinHasher, ExtendedPrivKey, ExtendedPubKey};
use crate::types::{
BlindSum, BlindingFactor, Error, ExtKeychainPath, Identifier, Keychain, SwitchCommitmentType,
};
use crate::util::secp::key::{PublicKey, SecretKey};
use crate::util::secp::pedersen::Commitment;
use crate::util::secp::{self, Message, Secp256k1, Signature};
#[derive(Clone, Debug)]
pub struct ExtKeychain {
secp: Secp256k1,
master: ExtendedPrivKey,
use_switch_commits: bool,
pub master: ExtendedPrivKey,
hasher: BIP32GrinHasher,
}
impl ExtKeychain {
pub fn pub_root_key(&mut self) -> ExtendedPubKey {
ExtendedPubKey::from_private(&self.secp, &self.master, &mut self.hasher)
}
pub fn hasher(&self) -> BIP32GrinHasher {
self.hasher.clone()
}
}
impl Keychain for ExtKeychain {
fn from_seed(seed: &[u8], is_floo: bool) -> Result<ExtKeychain, Error> {
let mut h = BIP32GrinHasher::new(is_floo);
@ -41,7 +52,6 @@ impl Keychain for ExtKeychain {
let keychain = ExtKeychain {
secp: secp,
master: master,
use_switch_commits: true,
hasher: h,
};
Ok(keychain)
@ -54,7 +64,6 @@ impl Keychain for ExtKeychain {
let keychain = ExtKeychain {
secp: secp,
master: master,
use_switch_commits: true,
hasher: h,
};
Ok(keychain)
@ -63,7 +72,7 @@ impl Keychain for ExtKeychain {
/// For testing - probably not a good idea to use outside of tests.
fn from_random_seed(is_floo: bool) -> Result<ExtKeychain, Error> {
let seed: String = thread_rng().sample_iter(&Alphanumeric).take(16).collect();
let seed = blake2::blake2b::blake2b(32, &[], seed.as_bytes());
let seed = blake2b(32, &[], seed.as_bytes());
ExtKeychain::from_seed(seed.as_bytes(), is_floo)
}
@ -75,22 +84,39 @@ impl Keychain for ExtKeychain {
ExtKeychainPath::new(depth, d1, d2, d3, d4).to_identifier()
}
fn derive_key(&self, amount: u64, id: &Identifier) -> Result<SecretKey, Error> {
fn public_root_key(&self) -> PublicKey {
let mut hasher = self.hasher.clone();
ExtendedPubKey::from_private(&self.secp, &self.master, &mut hasher).public_key
}
fn derive_key(
&self,
amount: u64,
id: &Identifier,
switch: &SwitchCommitmentType,
) -> Result<SecretKey, Error> {
let mut h = self.hasher.clone();
let p = id.to_path();
let mut ext_key = self.master;
let mut ext_key = self.master.clone();
for i in 0..p.depth {
ext_key = ext_key.ckd_priv(&self.secp, &mut h, p.path[i as usize])?;
}
match self.use_switch_commits {
true => Ok(self.secp.blind_switch(amount, ext_key.secret_key)?),
false => Ok(ext_key.secret_key),
match *switch {
SwitchCommitmentType::Regular => {
Ok(self.secp.blind_switch(amount, ext_key.secret_key)?)
}
SwitchCommitmentType::None => Ok(ext_key.secret_key),
}
}
fn commit(&self, amount: u64, id: &Identifier) -> Result<Commitment, Error> {
let key = self.derive_key(amount, id)?;
fn commit(
&self,
amount: u64,
id: &Identifier,
switch: &SwitchCommitmentType,
) -> Result<Commitment, Error> {
let key = self.derive_key(amount, id, switch)?;
let commit = self.secp.commit(amount, key)?;
Ok(commit)
}
@ -100,7 +126,11 @@ impl Keychain for ExtKeychain {
.positive_key_ids
.iter()
.filter_map(|k| {
let res = self.derive_key(k.value, &Identifier::from_path(&k.ext_keychain_path));
let res = self.derive_key(
k.value,
&Identifier::from_path(&k.ext_keychain_path),
&k.switch,
);
if let Ok(s) = res {
Some(s)
} else {
@ -113,7 +143,11 @@ impl Keychain for ExtKeychain {
.negative_key_ids
.iter()
.filter_map(|k| {
let res = self.derive_key(k.value, &Identifier::from_path(&k.ext_keychain_path));
let res = self.derive_key(
k.value,
&Identifier::from_path(&k.ext_keychain_path),
&k.switch,
);
if let Ok(s) = res {
Some(s)
} else {
@ -122,37 +156,32 @@ impl Keychain for ExtKeychain {
})
.collect();
pos_keys.extend(
&blind_sum
.positive_blinding_factors
.iter()
.filter_map(|b| b.secret_key(&self.secp).ok())
.collect::<Vec<SecretKey>>(),
);
let keys = blind_sum
.positive_blinding_factors
.iter()
.filter_map(|b| b.secret_key(&self.secp).ok().clone())
.collect::<Vec<SecretKey>>();
pos_keys.extend(keys);
neg_keys.extend(
&blind_sum
.negative_blinding_factors
.iter()
.filter_map(|b| b.secret_key(&self.secp).ok())
.collect::<Vec<SecretKey>>(),
);
let keys = blind_sum
.negative_blinding_factors
.iter()
.filter_map(|b| b.secret_key(&self.secp).ok().clone())
.collect::<Vec<SecretKey>>();
neg_keys.extend(keys);
let sum = self.secp.blind_sum(pos_keys, neg_keys)?;
Ok(BlindingFactor::from_secret_key(sum))
}
fn create_nonce(&self, commit: &Commitment) -> Result<SecretKey, Error> {
// hash(commit|wallet root secret key (m)) as nonce
let root_key = self.derive_key(0, &Self::root_key_id())?;
let res = blake2::blake2b::blake2b(32, &commit.0, &root_key.0[..]);
let res = res.as_bytes();
SecretKey::from_slice(&self.secp, &res)
.map_err(|e| Error::RangeProof(format!("Unable to create nonce: {:?}", e).to_string()))
}
fn sign(&self, msg: &Message, amount: u64, id: &Identifier) -> Result<Signature, Error> {
let skey = self.derive_key(amount, id)?;
fn sign(
&self,
msg: &Message,
amount: u64,
id: &Identifier,
switch: &SwitchCommitmentType,
) -> Result<Signature, Error> {
let skey = self.derive_key(amount, id, switch)?;
let sig = self.secp.sign(msg, &skey)?;
Ok(sig)
}
@ -167,10 +196,6 @@ impl Keychain for ExtKeychain {
Ok(sig)
}
fn set_use_switch_commits(&mut self, value: bool) {
self.use_switch_commits = value;
}
fn secp(&self) -> &Secp256k1 {
&self.secp
}
@ -182,11 +207,13 @@ mod test {
use crate::types::{BlindSum, BlindingFactor, ExtKeychainPath, Keychain};
use crate::util::secp;
use crate::util::secp::key::SecretKey;
use crate::SwitchCommitmentType;
#[test]
fn test_key_derivation() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let secp = keychain.secp();
let switch = &SwitchCommitmentType::None;
let path = ExtKeychainPath::new(1, 1, 0, 0, 0);
let key_id = path.to_identifier();
@ -196,10 +223,10 @@ mod test {
// now create a zero commitment using the key on the keychain associated with
// the key_id
let commit = keychain.commit(0, &key_id).unwrap();
let commit = keychain.commit(0, &key_id, switch).unwrap();
// now check we can use our key to verify a signature from this zero commitment
let sig = keychain.sign(&msg, 0, &key_id).unwrap();
let sig = keychain.sign(&msg, 0, &key_id, switch).unwrap();
secp.verify_from_commit(&msg, &sig, &commit).unwrap();
}
@ -235,9 +262,9 @@ mod test {
// create commitments for secret keys 1, 2 and 3
// all committing to the value 0 (which is what we do for tx_kernels)
let commit_1 = keychain.secp.commit(0, skey1).unwrap();
let commit_2 = keychain.secp.commit(0, skey2).unwrap();
let commit_3 = keychain.secp.commit(0, skey3).unwrap();
let commit_1 = keychain.secp.commit(0, skey1.clone()).unwrap();
let commit_2 = keychain.secp.commit(0, skey2.clone()).unwrap();
let commit_3 = keychain.secp.commit(0, skey3.clone()).unwrap();
// now sum commitments for keys 1 and 2
let sum = keychain

View file

@ -25,14 +25,19 @@ extern crate serde_derive;
#[macro_use]
extern crate lazy_static;
extern crate sha2;
mod base58;
pub mod extkey_bip32;
pub mod mnemonic;
mod types;
pub mod view_key;
pub mod keychain;
pub use crate::extkey_bip32::ChildNumber;
pub use crate::keychain::ExtKeychain;
pub use crate::types::{
BlindSum, BlindingFactor, Error, ExtKeychainPath, Identifier, Keychain, IDENTIFIER_SIZE,
BlindSum, BlindingFactor, Error, ExtKeychainPath, Identifier, Keychain, SwitchCommitmentType,
IDENTIFIER_SIZE,
};
pub use crate::view_key::ViewKey;

View file

@ -14,6 +14,7 @@
use rand::thread_rng;
use std::cmp::min;
use std::convert::TryFrom;
use std::io::Cursor;
use std::ops::Add;
/// Keychain trait and its main supporting types. The Identifier is a
@ -129,9 +130,12 @@ impl Identifier {
}
pub fn to_value_path(&self, value: u64) -> ValueExtKeychainPath {
// TODO: proper support for different switch commitment schemes
// For now it is assumed all outputs are using the regular switch commitment scheme
ValueExtKeychainPath {
value,
ext_keychain_path: self.to_path(),
switch: SwitchCommitmentType::Regular,
}
}
@ -229,6 +233,7 @@ impl fmt::Display for Identifier {
}
#[derive(Default, Clone, PartialEq, Serialize, Deserialize, Zeroize)]
#[zeroize(drop)]
pub struct BlindingFactor([u8; SECRET_KEY_SIZE]);
// Dummy `Debug` implementation that prevents secret leakage.
@ -318,7 +323,7 @@ impl BlindingFactor {
// use blind_sum to subtract skey_1 from our key (to give k = k1 + k2)
let skey = self.secret_key(secp)?;
let skey_2 = secp.blind_sum(vec![skey], vec![skey_1])?;
let skey_2 = secp.blind_sum(vec![skey], vec![skey_1.clone()])?;
let blind_1 = BlindingFactor::from_secret_key(skey_1);
let blind_2 = BlindingFactor::from_secret_key(skey_2);
@ -443,11 +448,12 @@ impl ExtKeychainPath {
}
}
/// Wrapper for amount + path
/// Wrapper for amount + switch + path
#[derive(Copy, Clone, PartialEq, Eq, Debug, Deserialize)]
pub struct ValueExtKeychainPath {
pub value: u64,
pub ext_keychain_path: ExtKeychainPath,
pub switch: SwitchCommitmentType,
}
pub trait Keychain: Sync + Send + Clone {
@ -467,16 +473,61 @@ pub trait Keychain: Sync + Send + Clone {
/// Derives a key id from the depth of the keychain and the values at each
/// depth level. See `KeychainPath` for more information.
fn derive_key_id(depth: u8, d1: u32, d2: u32, d3: u32, d4: u32) -> Identifier;
fn derive_key(&self, amount: u64, id: &Identifier) -> Result<SecretKey, Error>;
fn commit(&self, amount: u64, id: &Identifier) -> Result<Commitment, Error>;
/// The public root key
fn public_root_key(&self) -> PublicKey;
fn derive_key(
&self,
amount: u64,
id: &Identifier,
switch: &SwitchCommitmentType,
) -> Result<SecretKey, Error>;
fn commit(
&self,
amount: u64,
id: &Identifier,
switch: &SwitchCommitmentType,
) -> Result<Commitment, Error>;
fn blind_sum(&self, blind_sum: &BlindSum) -> Result<BlindingFactor, Error>;
fn create_nonce(&self, commit: &Commitment) -> Result<SecretKey, Error>;
fn sign(&self, msg: &Message, amount: u64, id: &Identifier) -> Result<Signature, Error>;
fn sign(
&self,
msg: &Message,
amount: u64,
id: &Identifier,
switch: &SwitchCommitmentType,
) -> Result<Signature, Error>;
fn sign_with_blinding(&self, _: &Message, _: &BlindingFactor) -> Result<Signature, Error>;
fn set_use_switch_commits(&mut self, value: bool);
fn secp(&self) -> &Secp256k1;
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
pub enum SwitchCommitmentType {
None,
Regular,
}
impl TryFrom<u8> for SwitchCommitmentType {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(SwitchCommitmentType::None),
1 => Ok(SwitchCommitmentType::Regular),
_ => Err(()),
}
}
}
impl From<&SwitchCommitmentType> for u8 {
fn from(switch: &SwitchCommitmentType) -> Self {
match *switch {
SwitchCommitmentType::None => 0,
SwitchCommitmentType::Regular => 1,
}
}
}
#[cfg(test)]
mod test {
use rand::thread_rng;
@ -519,7 +570,7 @@ mod test {
fn split_blinding_factor() {
let secp = Secp256k1::new();
let skey_in = SecretKey::new(&secp, &mut thread_rng());
let blind = BlindingFactor::from_secret_key(skey_in);
let blind = BlindingFactor::from_secret_key(skey_in.clone());
let split = blind.split(&secp).unwrap();
// split a key, sum the split keys and confirm the sum matches the original key

195
keychain/src/view_key.rs Normal file
View file

@ -0,0 +1,195 @@
use crate::blake2::blake2b::blake2b;
use byteorder::{BigEndian, ByteOrder};
//use crate::sha2::{Digest, Sha256};
use super::extkey_bip32::{
BIP32Hasher, ChainCode, ChildNumber, Error as BIP32Error, ExtendedPrivKey, ExtendedPubKey,
Fingerprint,
};
use super::types::{Error, Keychain};
use crate::util::secp::constants::GENERATOR_PUB_J_RAW;
use crate::util::secp::ffi;
use crate::util::secp::key::{PublicKey, SecretKey};
use crate::util::secp::Secp256k1;
use crate::SwitchCommitmentType;
/*const VERSION_FLOO_NS: [u8;4] = [0x03, 0x27, 0x3E, 0x4B];
const VERSION_FLOO: [u8;4] = [0x03, 0x27, 0x3E, 0x4B];
const VERSION_MAIN_NS: [u8;4] = [0x03, 0x3C, 0x08, 0xDF];
const VERSION_MAIN: [u8;4] = [0x03, 0x3C, 0x08, 0xDF];*/
/// Key that can be used to scan the chain for owned outputs
/// This is a public key, meaning it cannot be used to spend those outputs
/// At the moment only depth 0 keys can be used
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ViewKey {
/// Whether this view key is meant for floonet or not
pub is_floo: bool,
/// How many derivations this key is from the master (which is 0)
pub depth: u8,
/// Fingerprint of the parent key
parent_fingerprint: Fingerprint,
/// Child number of the key used to derive from parent (0 for master)
pub child_number: ChildNumber,
/// Public key
public_key: PublicKey,
/// Switch public key, required to view outputs that use switch commitment
switch_public_key: Option<PublicKey>,
/// Chain code
chain_code: ChainCode,
/// Hash used to generate rewind nonce
pub rewind_hash: Vec<u8>,
}
impl ViewKey {
pub fn create<K, H>(
keychain: &K,
ext_key: ExtendedPrivKey,
hasher: &mut H,
is_floo: bool,
) -> Result<Self, Error>
where
K: Keychain,
H: BIP32Hasher,
{
let secp = keychain.secp();
let ExtendedPubKey {
network: _,
depth,
parent_fingerprint,
child_number,
public_key,
chain_code,
} = ExtendedPubKey::from_private(secp, &ext_key, hasher);
let mut switch_public_key = PublicKey(ffi::PublicKey(GENERATOR_PUB_J_RAW));
switch_public_key.mul_assign(secp, &ext_key.secret_key)?;
let switch_public_key = Some(switch_public_key);
let rewind_hash = Self::rewind_hash(secp, keychain.public_root_key());
Ok(Self {
is_floo,
depth,
parent_fingerprint,
child_number,
public_key,
switch_public_key,
chain_code,
rewind_hash,
})
}
fn rewind_hash(secp: &Secp256k1, public_root_key: PublicKey) -> Vec<u8> {
let ser = public_root_key.serialize_vec(secp, true);
blake2b(32, &[], &ser[..]).as_bytes().to_vec()
}
fn ckd_pub_tweak<H>(
&self,
secp: &Secp256k1,
hasher: &mut H,
i: ChildNumber,
) -> Result<(SecretKey, ChainCode), Error>
where
H: BIP32Hasher,
{
match i {
ChildNumber::Hardened { .. } => Err(BIP32Error::CannotDeriveFromHardenedKey.into()),
ChildNumber::Normal { index: n } => {
hasher.init_sha512(&self.chain_code[..]);
hasher.append_sha512(&self.public_key.serialize_vec(secp, true)[..]);
let mut be_n = [0; 4];
BigEndian::write_u32(&mut be_n, n);
hasher.append_sha512(&be_n);
let result = hasher.result_sha512();
let secret_key = SecretKey::from_slice(secp, &result[..32])?;
let chain_code = ChainCode::from(&result[32..]);
Ok((secret_key, chain_code))
}
}
}
pub fn ckd_pub<H>(
&self,
secp: &Secp256k1,
hasher: &mut H,
i: ChildNumber,
) -> Result<Self, Error>
where
H: BIP32Hasher,
{
let (secret_key, chain_code) = self.ckd_pub_tweak(secp, hasher, i)?;
let mut public_key = self.public_key.clone();
public_key.add_exp_assign(secp, &secret_key)?;
let switch_public_key = match &self.switch_public_key {
Some(p) => {
let mut j = PublicKey(ffi::PublicKey(GENERATOR_PUB_J_RAW));
j.mul_assign(secp, &secret_key)?;
Some(PublicKey::from_combination(secp, vec![p, &j])?)
}
None => None,
};
Ok(Self {
is_floo: self.is_floo,
depth: self.depth + 1,
parent_fingerprint: self.fingerprint(secp, hasher),
child_number: i,
public_key,
switch_public_key,
chain_code,
rewind_hash: self.rewind_hash.clone(),
})
}
pub fn commit(
&self,
secp: &Secp256k1,
amount: u64,
switch: &SwitchCommitmentType,
) -> Result<PublicKey, Error> {
let value_key = secp.commit_value(amount)?.to_pubkey(secp)?;
let pub_key = PublicKey::from_combination(secp, vec![&self.public_key, &value_key])?;
match *switch {
SwitchCommitmentType::None => Ok(pub_key),
SwitchCommitmentType::Regular => {
// TODO: replace this whole block by a libsecp function
/*let switch_pub = self.switch_public_key.ok_or(Error::SwitchCommitment)?;
let switch_ser: Vec<u8> = switch_pub.serialize_vec(secp, true)[..].to_vec();
let mut commit_ser: Vec<u8> = pub_key.serialize_vec(secp, true)[..].to_vec();
commit_ser[0] += 6; // This only works sometimes
let mut hasher = Sha256::new();
hasher.input(&commit_ser);
hasher.input(&switch_ser);
let blind = SecretKey::from_slice(secp, &hasher.result()[..])?;
let mut pub_key = pub_key;
pub_key.add_exp_assign(secp, &blind)?;
Ok(pub_key)*/
Err(Error::SwitchCommitment)
}
}
}
fn identifier<H>(&self, secp: &Secp256k1, hasher: &mut H) -> [u8; 20]
where
H: BIP32Hasher,
{
let sha2_res = hasher.sha_256(&self.public_key.serialize_vec(secp, true)[..]);
hasher.ripemd_160(&sha2_res)
}
fn fingerprint<H>(&self, secp: &Secp256k1, hasher: &mut H) -> Fingerprint
where
H: BIP32Hasher,
{
Fingerprint::from(&self.identifier(secp, hasher)[0..4])
}
}

View file

@ -1,6 +1,6 @@
[package]
name = "grin_p2p"
version = "1.1.1-beta.1"
version = "2.0.0-beta.2"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Chain implementation for grin, a simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format."
license = "Apache-2.0"
@ -22,10 +22,10 @@ tempfile = "3.0.5"
log = "0.4"
chrono = { version = "0.4.4", features = ["serde"] }
grin_core = { path = "../core", version = "1.1.1-beta.1" }
grin_store = { path = "../store", version = "1.1.1-beta.1" }
grin_util = { path = "../util", version = "1.1.1-beta.1" }
grin_chain = { path = "../chain", version = "1.1.1-beta.1" }
grin_core = { path = "../core", version = "2.0.0-beta.2" }
grin_store = { path = "../store", version = "2.0.0-beta.2" }
grin_util = { path = "../util", version = "2.0.0-beta.2" }
grin_chain = { path = "../chain", version = "2.0.0-beta.2" }
[dev-dependencies]
grin_pool = { path = "../pool", version = "1.1.1-beta.1" }
grin_pool = { path = "../pool", version = "2.0.0-beta.2" }

View file

@ -13,7 +13,7 @@
// limitations under the License.
use crate::conn::{Message, MessageHandler, Response, Tracker};
use crate::core::core::{self, hash::Hash, hash::Hashed, CompactBlock};
use crate::core::core::{self, hash::Hash, CompactBlock};
use crate::msg::{
BanReason, GetPeerAddrs, Headers, KernelDataResponse, Locator, PeerAddrs, Ping, Pong,

View file

@ -1,6 +1,6 @@
[package]
name = "grin_pool"
version = "1.1.1-beta.1"
version = "2.0.0-beta.2"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Chain implementation for grin, a simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format."
license = "Apache-2.0"
@ -19,10 +19,10 @@ chrono = "0.4.4"
failure = "0.1"
failure_derive = "0.1"
grin_core = { path = "../core", version = "1.1.1-beta.1" }
grin_keychain = { path = "../keychain", version = "1.1.1-beta.1" }
grin_store = { path = "../store", version = "1.1.1-beta.1" }
grin_util = { path = "../util", version = "1.1.1-beta.1" }
grin_core = { path = "../core", version = "2.0.0-beta.2" }
grin_keychain = { path = "../keychain", version = "2.0.0-beta.2" }
grin_store = { path = "../store", version = "2.0.0-beta.2" }
grin_util = { path = "../util", version = "2.0.0-beta.2" }
[dev-dependencies]
grin_chain = { path = "../chain", version = "1.1.1-beta.1" }
grin_chain = { path = "../chain", version = "2.0.0-beta.2" }

View file

@ -47,7 +47,14 @@ fn test_transaction_pool_block_building() {
let height = prev_header.height + 1;
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
let fee = txs.iter().map(|x| x.fee()).sum();
let reward = libtx::reward::output(&keychain, &key_id, fee, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
fee,
false,
)
.unwrap();
let mut block = Block::new(&prev_header, txs, Difficulty::min(), reward).unwrap();
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).

View file

@ -51,7 +51,14 @@ fn test_block_building_max_weight() {
let height = prev_header.height + 1;
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
let fee = txs.iter().map(|x| x.fee()).sum();
let reward = libtx::reward::output(&keychain, &key_id, fee, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
fee,
false,
)
.unwrap();
let mut block = Block::new(&prev_header, txs, Difficulty::min(), reward).unwrap();
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).

View file

@ -45,7 +45,14 @@ fn test_transaction_pool_block_reconciliation() {
let header = {
let height = 1;
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
let reward = libtx::reward::output(&keychain, &key_id, 0, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
0,
false,
)
.unwrap();
let genesis = BlockHeader::default();
let mut block = Block::new(&genesis, vec![], Difficulty::min(), reward).unwrap();
@ -65,7 +72,14 @@ fn test_transaction_pool_block_reconciliation() {
let block = {
let key_id = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
let fees = initial_tx.fee();
let reward = libtx::reward::output(&keychain, &key_id, fees, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
fees,
false,
)
.unwrap();
let mut block =
Block::new(&header, vec![initial_tx], Difficulty::min(), reward).unwrap();
@ -159,7 +173,14 @@ fn test_transaction_pool_block_reconciliation() {
let block = {
let key_id = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
let fees = block_txs.iter().map(|tx| tx.fee()).sum();
let reward = libtx::reward::output(&keychain, &key_id, fees, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
fees,
false,
)
.unwrap();
let mut block = Block::new(&header, block_txs, Difficulty::min(), reward).unwrap();
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).

View file

@ -195,7 +195,7 @@ where
tx_elements.push(libtx::build::with_fee(fees as u64));
libtx::build::transaction(tx_elements, keychain).unwrap()
libtx::build::transaction(tx_elements, keychain, &libtx::ProofBuilder::new(keychain)).unwrap()
}
pub fn test_transaction<K>(
@ -225,7 +225,7 @@ where
}
tx_elements.push(libtx::build::with_fee(fees as u64));
libtx::build::transaction(tx_elements, keychain).unwrap()
libtx::build::transaction(tx_elements, keychain, &libtx::ProofBuilder::new(keychain)).unwrap()
}
pub fn test_source() -> TxSource {

View file

@ -44,7 +44,14 @@ fn test_the_transaction_pool() {
let header = {
let height = 1;
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
let reward = libtx::reward::output(&keychain, &key_id, 0, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
0,
false,
)
.unwrap();
let block = Block::new(&BlockHeader::default(), vec![], Difficulty::min(), reward).unwrap();
chain.update_db_for_block(&block);
@ -246,7 +253,14 @@ fn test_the_transaction_pool() {
let header = {
let height = 1;
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
let reward = libtx::reward::output(&keychain, &key_id, 0, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
0,
false,
)
.unwrap();
let block =
Block::new(&BlockHeader::default(), vec![], Difficulty::min(), reward).unwrap();

View file

@ -1,6 +1,6 @@
[package]
name = "grin_servers"
version = "1.1.1-beta.1"
version = "2.0.0-beta.2"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format."
license = "Apache-2.0"
@ -25,11 +25,11 @@ serde_json = "1"
chrono = "0.4.4"
tokio = "0.1.11"
grin_api = { path = "../api", version = "1.1.1-beta.1" }
grin_chain = { path = "../chain", version = "1.1.1-beta.1" }
grin_core = { path = "../core", version = "1.1.1-beta.1" }
grin_keychain = { path = "../keychain", version = "1.1.1-beta.1" }
grin_p2p = { path = "../p2p", version = "1.1.1-beta.1" }
grin_pool = { path = "../pool", version = "1.1.1-beta.1" }
grin_store = { path = "../store", version = "1.1.1-beta.1" }
grin_util = { path = "../util", version = "1.1.1-beta.1" }
grin_api = { path = "../api", version = "2.0.0-beta.2" }
grin_chain = { path = "../chain", version = "2.0.0-beta.2" }
grin_core = { path = "../core", version = "2.0.0-beta.2" }
grin_keychain = { path = "../keychain", version = "2.0.0-beta.2" }
grin_p2p = { path = "../p2p", version = "2.0.0-beta.2" }
grin_pool = { path = "../pool", version = "2.0.0-beta.2" }
grin_store = { path = "../store", version = "2.0.0-beta.2" }
grin_util = { path = "../util", version = "2.0.0-beta.2" }

View file

@ -73,7 +73,6 @@ pub struct Server {
connect_thread: Option<JoinHandle<()>>,
sync_thread: JoinHandle<()>,
dandelion_thread: JoinHandle<()>,
p2p_thread: JoinHandle<()>,
}
impl Server {
@ -256,7 +255,7 @@ impl Server {
)?;
let p2p_inner = p2p_server.clone();
let p2p_thread = thread::Builder::new()
let _ = thread::Builder::new()
.name("p2p-server".to_string())
.spawn(move || {
if let Err(e) = p2p_inner.listen() {
@ -315,7 +314,6 @@ impl Server {
lock_file,
connect_thread,
sync_thread,
p2p_thread,
dandelion_thread,
})
}
@ -530,11 +528,9 @@ impl Server {
Ok(_) => info!("dandelion_monitor thread stopped"),
}
}
// this call is blocking and makes sure all peers stop, however
// we can't be sure that we stoped a listener blocked on accept, so we don't join the p2p thread
self.p2p.stop();
match self.p2p_thread.join() {
Err(e) => error!("failed to join to p2p thread: {:?}", e),
Ok(_) => info!("p2p thread stopped"),
}
let _ = self.lock_file.unlock();
}

View file

@ -18,6 +18,7 @@
use crate::util::RwLock;
use chrono::prelude::{DateTime, NaiveDateTime, Utc};
use rand::{thread_rng, Rng};
use serde_json::{json, Value};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
@ -28,6 +29,7 @@ use crate::common::types::Error;
use crate::core::core::verifier_cache::VerifierCache;
use crate::core::core::{Output, TxKernel};
use crate::core::libtx::secp_ser;
use crate::core::libtx::ProofBuilder;
use crate::core::{consensus, core, global};
use crate::keychain::{ExtKeychain, Identifier, Keychain};
use crate::pool;
@ -223,8 +225,14 @@ fn burn_reward(block_fees: BlockFees) -> Result<(core::Output, core::TxKernel, B
warn!("Burning block fees: {:?}", block_fees);
let keychain = ExtKeychain::from_random_seed(global::is_floonet())?;
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let (out, kernel) =
crate::core::libtx::reward::output(&keychain, &key_id, block_fees.fees, false).unwrap();
let (out, kernel) = crate::core::libtx::reward::output(
&keychain,
&ProofBuilder::new(&keychain),
&key_id,
block_fees.fees,
false,
)
.unwrap();
Ok((out, kernel, block_fees))
}
@ -258,15 +266,48 @@ fn get_coinbase(
/// Call the wallet API to create a coinbase output for the given block_fees.
/// Will retry based on default "retry forever with backoff" behavior.
fn create_coinbase(dest: &str, block_fees: &BlockFees) -> Result<CbData, Error> {
let url = format!("{}/v1/wallet/foreign/build_coinbase", dest);
match api::client::post(&url, None, &block_fees) {
Err(e) => {
error!(
"Failed to get coinbase from {}. Is the wallet listening?",
url
);
Err(Error::WalletComm(format!("{}", e)))
let url = format!("{}/v2/foreign", dest);
let req_body = json!({
"jsonrpc": "2.0",
"method": "build_coinbase",
"id": 1,
"params": {
"block_fees": block_fees
}
Ok(res) => Ok(res),
});
trace!("Sending build_coinbase request: {}", req_body);
let req = api::client::create_post_request(url.as_str(), None, &req_body)?;
let res: String = api::client::send_request(req).map_err(|e| {
let report = format!(
"Failed to get coinbase from {}. Is the wallet listening? {}",
dest, e
);
error!("{}", report);
Error::WalletComm(report)
})?;
let res: Value = serde_json::from_str(&res).unwrap();
trace!("Response: {}", res);
if res["error"] != json!(null) {
let report = format!(
"Failed to get coinbase from {}: Error: {}, Message: {}",
dest, res["error"]["code"], res["error"]["message"]
);
error!("{}", report);
return Err(Error::WalletComm(report));
}
let cb_data = res["result"]["Ok"].clone();
trace!("cb_data: {}", cb_data);
let ret_val = match serde_json::from_value::<CbData>(cb_data) {
Ok(r) => r,
Err(e) => {
let report = format!("Couldn't deserialize CbData: {}", e);
error!("{}", report);
return Err(Error::WalletComm(report));
}
};
Ok(ret_val)
}

View file

@ -1,5 +1,5 @@
name: grin
version: "1.1.1-beta.1"
version: "2.0.0-beta.2"
about: Lightweight implementation of the MimbleWimble protocol.
author: The Grin Team

View file

@ -1,6 +1,6 @@
[package]
name = "grin_store"
version = "1.1.1-beta.1"
version = "2.0.0-beta.2"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format."
license = "Apache-2.0"
@ -23,8 +23,8 @@ serde = "1"
serde_derive = "1"
log = "0.4"
grin_core = { path = "../core", version = "1.1.1-beta.1" }
grin_util = { path = "../util", version = "1.1.1-beta.1" }
grin_core = { path = "../core", version = "2.0.0-beta.2" }
grin_util = { path = "../util", version = "2.0.0-beta.2" }
[dev-dependencies]
chrono = "0.4.4"

View file

@ -1,6 +1,6 @@
[package]
name = "grin_util"
version = "1.1.1-beta.1"
version = "2.0.0-beta.2"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format."
license = "Apache-2.0"
@ -22,11 +22,11 @@ log = "0.4"
walkdir = "2"
zip = { version = "0.4", default-features = false }
parking_lot = {version = "0.6"}
zeroize = "0.5.2"
zeroize = "0.9"
[dependencies.grin_secp256k1zkp]
#git = "https://github.com/mimblewimble/rust-secp256k1-zkp"
#tag = "grin_integration_29"
#path = "../../rust-secp256k1-zkp"
version = "0.7.5"
version = "0.7.7"
features = ["bullet-proof-sizing"]