Slate version conversions (#13)

* add slate upgrading/downgrading

* add slate versions

* slate versioning compilation and tests

* transaction slate responses downgrade themselves to the version of the caller

* add command line arg for target slate version output

* v2 versioning comment info

* doctest fixes
This commit is contained in:
Yeastplume 2019-03-12 16:48:14 +00:00 committed by GitHub
parent be99ef8335
commit 02655afca8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 3234 additions and 353 deletions

28
Cargo.lock generated
View file

@ -418,7 +418,7 @@ dependencies = [
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -462,7 +462,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -553,7 +553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "grin_api"
version = "1.1.0"
source = "git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0#1b3eaba3025f92770ba468de9e77ebd1e4a660cd"
source = "git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0#c3496b45dd2704b6d542dad3447e8649d25399e4"
dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -607,7 +607,7 @@ dependencies = [
[[package]]
name = "grin_chain"
version = "1.1.0"
source = "git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0#1b3eaba3025f92770ba468de9e77ebd1e4a660cd"
source = "git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0#c3496b45dd2704b6d542dad3447e8649d25399e4"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -630,7 +630,7 @@ dependencies = [
[[package]]
name = "grin_core"
version = "1.1.0"
source = "git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0#1b3eaba3025f92770ba468de9e77ebd1e4a660cd"
source = "git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0#c3496b45dd2704b6d542dad3447e8649d25399e4"
dependencies = [
"blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -656,7 +656,7 @@ dependencies = [
[[package]]
name = "grin_keychain"
version = "1.1.0"
source = "git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0#1b3eaba3025f92770ba468de9e77ebd1e4a660cd"
source = "git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0#c3496b45dd2704b6d542dad3447e8649d25399e4"
dependencies = [
"blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -699,7 +699,7 @@ dependencies = [
[[package]]
name = "grin_p2p"
version = "1.1.0"
source = "git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0#1b3eaba3025f92770ba468de9e77ebd1e4a660cd"
source = "git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0#c3496b45dd2704b6d542dad3447e8649d25399e4"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
@ -719,7 +719,7 @@ dependencies = [
[[package]]
name = "grin_pool"
version = "1.1.0"
source = "git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0#1b3eaba3025f92770ba468de9e77ebd1e4a660cd"
source = "git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0#c3496b45dd2704b6d542dad3447e8649d25399e4"
dependencies = [
"blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -786,7 +786,7 @@ dependencies = [
[[package]]
name = "grin_store"
version = "1.1.0"
source = "git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0#1b3eaba3025f92770ba468de9e77ebd1e4a660cd"
source = "git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0#c3496b45dd2704b6d542dad3447e8649d25399e4"
dependencies = [
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"croaring 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -806,7 +806,7 @@ dependencies = [
[[package]]
name = "grin_util"
version = "1.1.0"
source = "git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0#1b3eaba3025f92770ba468de9e77ebd1e4a660cd"
source = "git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0#c3496b45dd2704b6d542dad3447e8649d25399e4"
dependencies = [
"backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1991,7 +1991,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -2071,7 +2071,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.15.27"
version = "0.15.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2086,7 +2086,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2860,7 +2860,7 @@ dependencies = [
"checksum string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b639411d0b9c738748b5397d5ceba08e648f4f1992231aa859af1a017f31f60b"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
"checksum supercow 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "171758edb47aa306a78dfa4ab9aeb5167405bd4e3dc2b64e88f6a84bbe98bd63"
"checksum syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)" = "525bd55255f03c816e5d7f615587bd13030c7103354fadb104993dcee6a788ec"
"checksum syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1825685f977249735d510a242a6727b46efe914bb67e38d30c071b1b72b1d5c2"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
"checksum tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b86c784c88d98c801132806dadd3819ed29d8600836c4088e855cdf3e178ed8a"
"checksum term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6b677dd1e8214ea1ef4297f85dbcbed8e8cdddb561040cc998ca2551c37561"

View file

@ -270,6 +270,11 @@ pub trait OwnerApi {
"jsonrpc": "2.0",
"method": "finalize_tx",
"params": [{
"version_info": {
"version": 2,
"orig_version": 2,
"min_compat_version": 0
},
"amount": 0,
"fee": 0,
"height": 0,
@ -284,8 +289,7 @@ pub trait OwnerApi {
"outputs": []
},
"offset": "0000000000000000000000000000000000000000000000000000000000000000"
},
"version": 1
}
}],
"id": 1
},
@ -416,6 +420,11 @@ pub trait OwnerApi {
"jsonrpc": "2.0",
"method": "verify_slate_messages",
"params": [{
"version_info": {
"version": 2,
"orig_version": 2,
"min_compat_version": 0
},
"amount": 0,
"fee": 0,
"height": 0,
@ -430,8 +439,7 @@ pub trait OwnerApi {
"outputs": []
},
"offset": "0000000000000000000000000000000000000000000000000000000000000000"
},
"version": 1
}
}],
"id": 1
},
@ -1103,6 +1111,7 @@ where
num_change_outputs: usize,
selection_strategy_is_use_all: bool,
message: Option<String>,
target_slate_version: Option<u16>,
) -> Result<(Slate, OutputLockFn<W, C, K>), Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
@ -1148,6 +1157,10 @@ where
}
w.close()?;
// set target slate version
if let Some(v) = target_slate_version {
slate.version_info.orig_version = v;
}
Ok((slate, lock_fn))
}
@ -1502,6 +1515,11 @@ pub trait ForeignApi {
"method": "verify_slate_messages",
"params": [
{
"version_info": {
"version": 2,
"orig_version": 2,
"min_compat_version": 0
},
"amount": 0,
"fee": 0,
"height": 0,
@ -1516,8 +1534,7 @@ pub trait ForeignApi {
"outputs": []
},
"offset": "0000000000000000000000000000000000000000000000000000000000000000"
},
"version": 1
}
}
],
"id": 1
@ -1539,13 +1556,18 @@ pub trait ForeignApi {
# Json rpc example
```
```ignore //TODO: No idea why this isn't expanding properly, check as we adjust the API
# grin_apiwallet::doctest_helper_json_rpc_foreign_assert_response!(
{
"jsonrpc": "2.0",
"method": "receive_tx",
"params": [
{
"version_info": {
"version": 2,
"orig_version": 2,
"min_compat_version": 0
},
"amount": 0,
"fee": 0,
"height": 0,
@ -1560,8 +1582,7 @@ pub trait ForeignApi {
"outputs": []
},
"offset": "0000000000000000000000000000000000000000000000000000000000000000"
},
"version": 1
}
},
null,
null

View file

@ -176,6 +176,18 @@ pub enum ErrorKind {
#[fail(display = "Committed Error")]
Committed(committed::Error),
/// Can't parse slate version
#[fail(display = "Can't parse slate version")]
SlateVersionParse,
/// Can't deserialize slate
#[fail(display = "Can't Deserialize slate")]
SlateDeser,
/// Unknown slate version
#[fail(display = "Unknown Slate Version: {}", _0)]
SlateVersion(u16),
/// Other
#[fail(display = "Generic error: {}", _0)]
GenericError(String),

View file

@ -18,7 +18,6 @@
use crate::blake2::blake2b::blake2b;
use crate::error::{Error, ErrorKind};
use crate::keychain::{BlindSum, BlindingFactor, Keychain};
use crate::slate_versions::v0::SlateV0;
use crate::util::secp;
use crate::util::secp::key::{PublicKey, SecretKey};
use crate::util::secp::Signature;
@ -31,27 +30,14 @@ use grin_core::libtx::{aggsig, build, secp_ser, tx_fee};
use rand::thread_rng;
use std::sync::Arc;
use uuid::Uuid;
use failure::ResultExt;
use serde_json;
const CURRENT_SLATE_VERSION: u64 = 1;
use crate::slate_versions::v0::SlateV0;
use crate::slate_versions::v1::SlateV1;
use crate::slate_versions::v2::SlateV2;
/// A wrapper around slates the enables support for versioning
#[derive(Serialize, Deserialize, Clone)]
#[serde(untagged)]
pub enum VersionedSlate {
/// Pre versioning version
V0(SlateV0),
/// Version 1 with versioning and hex serialization - current
V1(Slate),
}
impl From<VersionedSlate> for Slate {
fn from(ver: VersionedSlate) -> Self {
match ver {
VersionedSlate::V0(slate_v0) => Slate::from(slate_v0),
VersionedSlate::V1(slate) => slate,
}
}
}
const CURRENT_SLATE_VERSION: u16 = 2;
/// Public data for each participant in the slate
@ -126,6 +112,8 @@ impl ParticipantMessageData {
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Slate {
/// Versioning info
pub version_info: VersionCompatInfo,
/// The number of participants intended to take part in this transaction
pub num_participants: usize,
/// Unique transaction ID, selected by sender
@ -145,13 +133,17 @@ pub struct Slate {
/// insert their public data here. For now, 0 is sender and 1
/// is receiver, though this will change for multi-party
pub participant_data: Vec<ParticipantData>,
/// Slate format version
#[serde(default = "no_version")]
pub version: u64,
}
fn no_version() -> u64 {
0
/// Versioning and compatibility info about this slate
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct VersionCompatInfo {
/// The current version of the slate format
pub version: u16,
/// Original version this slate was converted from
pub orig_version: u16,
/// Minimum version this slate is compatible with
pub min_compat_version: u16,
}
/// Helper just to facilitate serialization
@ -162,6 +154,71 @@ pub struct ParticipantMessages {
}
impl Slate {
// TODO: Reduce the number of changes that need to occur below for each new
// slate version
fn parse_slate_version(slate_json: &str) -> Result<u16, Error> {
// keep attempting to deser, working through known versions until we have
// enough to get the version out
let res : Result<SlateV2, serde_json::Error> = serde_json::from_str(slate_json);
if let Ok(s) = res {
return Ok(s.version_info.version);
}
let res : Result<SlateV1, serde_json::Error> = serde_json::from_str(slate_json);
if let Ok(s) = res {
return Ok(s.version as u16);
}
let res : Result<SlateV0, serde_json::Error> = serde_json::from_str(slate_json);
if let Ok(_) = res {
return Ok(0);
}
Err(ErrorKind::SlateVersionParse)?
}
/// Recieve a slate, upgrade it to the latest version internally
pub fn deserialize_upgrade(slate_json: &str) -> Result<Slate, Error> {
let version = Slate::parse_slate_version(slate_json)?;
let v2 = match version {
2 => serde_json::from_str(slate_json).context(ErrorKind::SlateDeser)?,
1 => {
let mut v1 : SlateV1 = serde_json::from_str(slate_json).context(ErrorKind::SlateDeser)?;
v1.orig_version = 1;
SlateV2::from(v1)
}
0 => {
let v0 : SlateV0 = serde_json::from_str(slate_json).context(ErrorKind::SlateDeser)?;
let v1 = SlateV1::from(v0);
SlateV2::from(v1)
}
_ => return Err(ErrorKind::SlateVersion(version))?,
};
let f = serde_json::to_string(&v2).context(ErrorKind::SlateDeser)?;
Ok(serde_json::from_str(&f).context(ErrorKind::SlateDeser)?)
}
/// Downgrate slate to desired version
pub fn serialize_to_version(&self, version: Option<u16>) -> Result<String, Error> {
let version = match version {
Some(v) => v,
None => CURRENT_SLATE_VERSION,
};
let ser_self = serde_json::to_string(&self).context(ErrorKind::SlateDeser)?;
match version {
2 => Ok(ser_self.clone()),
1 => {
let v2 : SlateV2 = serde_json::from_str(&ser_self).context(ErrorKind::SlateDeser)?;
let v1 = SlateV1::from(v2);
Ok(serde_json::to_string(&v1).context(ErrorKind::SlateDeser)?)
}
0 => {
let v2 : SlateV2 = serde_json::from_str(&ser_self).context(ErrorKind::SlateDeser)?;
let v1 = SlateV1::from(v2);
let v0 = SlateV0::from(v1);
Ok(serde_json::to_string(&v0).context(ErrorKind::SlateDeser)?)
}
_ => Err(ErrorKind::SlateVersion(version))?,
}
}
/// Create a new slate
pub fn blank(num_participants: usize) -> Slate {
Slate {
@ -173,7 +230,11 @@ impl Slate {
height: 0,
lock_height: 0,
participant_data: vec![],
version: CURRENT_SLATE_VERSION,
version_info: VersionCompatInfo {
version: CURRENT_SLATE_VERSION,
orig_version: CURRENT_SLATE_VERSION,
min_compat_version: 0,
},
}
}

View file

@ -16,3 +16,7 @@
//! Used for serialization and deserialization of slates in a backwards compatible way.
#[allow(missing_docs)]
pub mod v0;
#[allow(missing_docs)]
pub mod v1;
#[allow(missing_docs)]
pub mod v2;

View file

@ -12,18 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Contains V0 of the slate
//! Contains V0 of the slate (grin 1.0.0)
//! And methods to downgrade v1 to v0
use crate::core::core::transaction::{
Input, KernelFeatures, Output, OutputFeatures, Transaction, TransactionBody, TxKernel,
KernelFeatures, OutputFeatures,
};
use crate::keychain::BlindingFactor;
use crate::slate::{ParticipantData, Slate};
use crate::util::secp;
use crate::util::secp::key::PublicKey;
use crate::util::secp::pedersen::{Commitment, RangeProof};
use crate::util::secp::Signature;
use uuid::Uuid;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SlateV0 {
/// The number of participants intended to take part in this transaction
@ -119,252 +120,3 @@ pub struct TxKernelV0 {
/// the transaction fee.
pub excess_sig: secp::Signature,
}
impl From<SlateV0> for Slate {
fn from(slate: SlateV0) -> Slate {
let SlateV0 {
num_participants,
id,
tx,
amount,
fee,
height,
lock_height,
participant_data,
} = slate;
let tx = Transaction::from(tx);
let participant_data = map_vec!(participant_data, |data| ParticipantData::from(data));
let version = 0;
Slate {
num_participants,
id,
tx,
amount,
fee,
height,
lock_height,
participant_data,
version,
}
}
}
impl From<&ParticipantDataV0> for ParticipantData {
fn from(data: &ParticipantDataV0) -> ParticipantData {
let ParticipantDataV0 {
id,
public_blind_excess,
public_nonce,
part_sig,
message,
message_sig,
} = data;
let id = *id;
let public_blind_excess = *public_blind_excess;
let public_nonce = *public_nonce;
let part_sig = *part_sig;
let message: Option<String> = message.as_ref().map(|t| String::from(&**t));
let message_sig = *message_sig;
ParticipantData {
id,
public_blind_excess,
public_nonce,
part_sig,
message,
message_sig,
}
}
}
impl From<TransactionV0> for Transaction {
fn from(tx: TransactionV0) -> Transaction {
let TransactionV0 { offset, body } = tx;
let body = TransactionBody::from(&body);
let transaction = Transaction::new(body.inputs, body.outputs, body.kernels);
transaction.with_offset(offset)
}
}
impl From<&TransactionBodyV0> for TransactionBody {
fn from(body: &TransactionBodyV0) -> Self {
let TransactionBodyV0 {
inputs,
outputs,
kernels,
} = body;
let inputs = map_vec!(inputs, |inp| Input::from(inp));
let outputs = map_vec!(outputs, |out| Output::from(out));
let kernels = map_vec!(kernels, |kern| TxKernel::from(kern));
TransactionBody {
inputs,
outputs,
kernels,
}
}
}
impl From<&InputV0> for Input {
fn from(input: &InputV0) -> Input {
let InputV0 { features, commit } = *input;
Input { features, commit }
}
}
impl From<&OutputV0> for Output {
fn from(output: &OutputV0) -> Output {
let OutputV0 {
features,
commit,
proof,
} = *output;
Output {
features,
commit,
proof,
}
}
}
impl From<&TxKernelV0> for TxKernel {
fn from(kernel: &TxKernelV0) -> TxKernel {
let TxKernelV0 {
features,
fee,
lock_height,
excess,
excess_sig,
} = *kernel;
TxKernel {
features,
fee,
lock_height,
excess,
excess_sig,
}
}
}
impl From<Slate> for SlateV0 {
fn from(slate: Slate) -> SlateV0 {
let Slate {
num_participants,
id,
tx,
amount,
fee,
height,
lock_height,
participant_data,
version: _,
} = slate;
let tx = TransactionV0::from(tx);
let participant_data = map_vec!(participant_data, |data| ParticipantDataV0::from(data));
SlateV0 {
num_participants,
id,
tx,
amount,
fee,
height,
lock_height,
participant_data,
}
}
}
impl From<&ParticipantData> for ParticipantDataV0 {
fn from(data: &ParticipantData) -> ParticipantDataV0 {
let ParticipantData {
id,
public_blind_excess,
public_nonce,
part_sig,
message,
message_sig,
} = data;
let id = *id;
let public_blind_excess = *public_blind_excess;
let public_nonce = *public_nonce;
let part_sig = *part_sig;
let message: Option<String> = message.as_ref().map(|t| String::from(&**t));
let message_sig = *message_sig;
ParticipantDataV0 {
id,
public_blind_excess,
public_nonce,
part_sig,
message,
message_sig,
}
}
}
impl From<Transaction> for TransactionV0 {
fn from(tx: Transaction) -> TransactionV0 {
let offset = tx.offset;
let body: TransactionBody = tx.into();
let body = TransactionBodyV0::from(&body);
TransactionV0 { offset, body }
}
}
impl From<&TransactionBody> for TransactionBodyV0 {
fn from(body: &TransactionBody) -> Self {
let TransactionBody {
inputs,
outputs,
kernels,
} = body;
let inputs = map_vec!(inputs, |inp| InputV0::from(inp));
let outputs = map_vec!(outputs, |out| OutputV0::from(out));
let kernels = map_vec!(kernels, |kern| TxKernelV0::from(kern));
TransactionBodyV0 {
inputs,
outputs,
kernels,
}
}
}
impl From<&Input> for InputV0 {
fn from(input: &Input) -> Self {
let Input { features, commit } = *input;
InputV0 { features, commit }
}
}
impl From<&Output> for OutputV0 {
fn from(output: &Output) -> Self {
let Output {
features,
commit,
proof,
} = *output;
OutputV0 {
features,
commit,
proof,
}
}
}
impl From<&TxKernel> for TxKernelV0 {
fn from(kernel: &TxKernel) -> Self {
let TxKernel {
features,
fee,
lock_height,
excess,
excess_sig,
} = *kernel;
TxKernelV0 {
features,
fee,
lock_height,
excess,
excess_sig,
}
}
}

View file

@ -0,0 +1,391 @@
// 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.
//! Contains V1 of the slate (grin 1.0.1, 1.0.2)
//! Changes from V0:
//! * Addition of a version field to Slate struct
use crate::core::core::transaction::{
KernelFeatures, OutputFeatures,
};
use crate::keychain::BlindingFactor;
use crate::util::secp;
use crate::util::secp::key::PublicKey;
use crate::util::secp::pedersen::{Commitment, RangeProof};
use crate::util::secp::Signature;
use uuid::Uuid;
use crate::slate_versions::v0::{SlateV0, TransactionV0, TransactionBodyV0, InputV0, OutputV0,TxKernelV0, ParticipantDataV0};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SlateV1 {
/// The number of participants intended to take part in this transaction
pub num_participants: usize,
/// Unique transaction ID, selected by sender
pub id: Uuid,
/// The core transaction data:
/// inputs, outputs, kernels, kernel offset
pub tx: TransactionV1,
/// base amount (excluding fee)
pub amount: u64,
/// fee amount
pub fee: u64,
/// Block height for the transaction
pub height: u64,
/// Lock height
pub lock_height: u64,
/// Participant data, each participant in the transaction will
/// insert their public data here. For now, 0 is sender and 1
/// is receiver, though this will change for multi-party
pub participant_data: Vec<ParticipantDataV1>,
/// Version
pub version: u64,
#[serde(skip)]
pub orig_version: u64,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ParticipantDataV1 {
/// Id of participant in the transaction. (For now, 0=sender, 1=rec)
pub id: u64,
/// Public key corresponding to private blinding factor
pub public_blind_excess: PublicKey,
/// Public key corresponding to private nonce
pub public_nonce: PublicKey,
/// Public partial signature
pub part_sig: Option<Signature>,
/// A message for other participants
pub message: Option<String>,
/// Signature, created with private key corresponding to 'public_blind_excess'
pub message_sig: Option<Signature>,
}
/// A transaction
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TransactionV1 {
/// The kernel "offset" k2
/// excess is k1G after splitting the key k = k1 + k2
pub offset: BlindingFactor,
/// The transaction body - inputs/outputs/kernels
pub body: TransactionBodyV1,
}
/// TransactionBody is a common abstraction for transaction and block
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TransactionBodyV1 {
/// List of inputs spent by the transaction.
pub inputs: Vec<InputV1>,
/// List of outputs the transaction produces.
pub outputs: Vec<OutputV1>,
/// List of kernels that make up this transaction (usually a single kernel).
pub kernels: Vec<TxKernelV1>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct InputV1 {
/// The features of the output being spent.
/// We will check maturity for coinbase output.
pub features: OutputFeatures,
/// The commit referencing the output being spent.
pub commit: Commitment,
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub struct OutputV1 {
/// Options for an output's structure or use
pub features: OutputFeatures,
/// The homomorphic commitment representing the output amount
pub commit: Commitment,
/// A proof that the commitment is in the right range
pub proof: RangeProof,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TxKernelV1 {
/// Options for a kernel's structure or use
pub features: KernelFeatures,
/// Fee originally included in the transaction this proof is for.
pub fee: u64,
/// This kernel is not valid earlier than lock_height blocks
/// The max lock_height of all *inputs* to this transaction
pub lock_height: u64,
/// Remainder of the sum of all transaction commitments. If the transaction
/// is well formed, amounts components should sum to zero and the excess
/// is hence a valid public key.
pub excess: Commitment,
/// The signature proving the excess is a valid public key, which signs
/// the transaction fee.
pub excess_sig: secp::Signature,
}
// V1 to V0 Downgrade Conversion ////////////////////////////////////
impl From<SlateV1> for SlateV0 {
fn from(slate: SlateV1) -> SlateV0 {
let SlateV1 {
num_participants,
id,
tx,
amount,
fee,
height,
lock_height,
participant_data,
version: _,
orig_version: _,
} = slate;
let tx = TransactionV0::from(tx);
let participant_data = map_vec!(participant_data, |data| ParticipantDataV0::from(data));
SlateV0 {
num_participants,
id,
tx,
amount,
fee,
height,
lock_height,
participant_data,
}
}
}
impl From<&ParticipantDataV1> for ParticipantDataV0 {
fn from(data: &ParticipantDataV1) -> ParticipantDataV0 {
let ParticipantDataV1 {
id,
public_blind_excess,
public_nonce,
part_sig,
message,
message_sig,
} = data;
let id = *id;
let public_blind_excess = *public_blind_excess;
let public_nonce = *public_nonce;
let part_sig = *part_sig;
let message: Option<String> = message.as_ref().map(|t| String::from(&**t));
let message_sig = *message_sig;
ParticipantDataV0 {
id,
public_blind_excess,
public_nonce,
part_sig,
message,
message_sig,
}
}
}
impl From<TransactionV1> for TransactionV0 {
fn from(tx: TransactionV1) -> TransactionV0 {
let TransactionV1 { offset, body } = tx;
let body = TransactionBodyV0::from(&body);
TransactionV0 {
offset,
body,
}
}
}
impl From<&TransactionBodyV1> for TransactionBodyV0 {
fn from(body: &TransactionBodyV1) -> Self {
let TransactionBodyV1 {
inputs,
outputs,
kernels,
} = body;
let inputs = map_vec!(inputs, |inp| InputV0::from(inp));
let outputs = map_vec!(outputs, |out| OutputV0::from(out));
let kernels = map_vec!(kernels, |kern| TxKernelV0::from(kern));
TransactionBodyV0 {
inputs,
outputs,
kernels,
}
}
}
impl From<&InputV1> for InputV0 {
fn from(input: &InputV1) -> InputV0 {
let InputV1 { features, commit } = *input;
InputV0 { features, commit }
}
}
impl From<&OutputV1> for OutputV0 {
fn from(output: &OutputV1) -> OutputV0 {
let OutputV1 {
features,
commit,
proof,
} = *output;
OutputV0 {
features,
commit,
proof,
}
}
}
impl From<&TxKernelV1> for TxKernelV0 {
fn from(kernel: &TxKernelV1) -> TxKernelV0 {
let TxKernelV1 {
features,
fee,
lock_height,
excess,
excess_sig,
} = *kernel;
TxKernelV0 {
features,
fee,
lock_height,
excess,
excess_sig,
}
}
}
// V0 to V1 Upgrade Conversion ////////////////////////////////////
impl From<SlateV0> for SlateV1 {
fn from(slate: SlateV0) -> SlateV1 {
let SlateV0 {
num_participants,
id,
tx,
amount,
fee,
height,
lock_height,
participant_data,
} = slate;
let tx = TransactionV1::from(tx);
let participant_data = map_vec!(participant_data, |data| ParticipantDataV1::from(data));
SlateV1 {
num_participants,
id,
tx,
amount,
fee,
height,
lock_height,
participant_data,
version: 1,
orig_version: 0,
}
}
}
impl From<&ParticipantDataV0> for ParticipantDataV1 {
fn from(data: &ParticipantDataV0) -> ParticipantDataV1 {
let ParticipantDataV0 {
id,
public_blind_excess,
public_nonce,
part_sig,
message,
message_sig,
} = data;
let id = *id;
let public_blind_excess = *public_blind_excess;
let public_nonce = *public_nonce;
let part_sig = *part_sig;
let message: Option<String> = message.as_ref().map(|t| String::from(&**t));
let message_sig = *message_sig;
ParticipantDataV1 {
id,
public_blind_excess,
public_nonce,
part_sig,
message,
message_sig,
}
}
}
impl From<TransactionV0> for TransactionV1 {
fn from(tx: TransactionV0) -> TransactionV1 {
let TransactionV0 { offset, body } = tx;
let body = TransactionBodyV1::from(&body);
TransactionV1 {
offset,
body,
}
}
}
impl From<&TransactionBodyV0> for TransactionBodyV1 {
fn from(body: &TransactionBodyV0) -> Self {
let TransactionBodyV0 {
inputs,
outputs,
kernels,
} = body;
let inputs = map_vec!(inputs, |inp| InputV1::from(inp));
let outputs = map_vec!(outputs, |out| OutputV1::from(out));
let kernels = map_vec!(kernels, |kern| TxKernelV1::from(kern));
TransactionBodyV1 {
inputs,
outputs,
kernels,
}
}
}
impl From<&InputV0> for InputV1 {
fn from(input: &InputV0) -> InputV1 {
let InputV0 { features, commit } = *input;
InputV1 { features, commit }
}
}
impl From<&OutputV0> for OutputV1 {
fn from(output: &OutputV0) -> OutputV1 {
let OutputV0 {
features,
commit,
proof,
} = *output;
OutputV1 {
features,
commit,
proof,
}
}
}
impl From<&TxKernelV0> for TxKernelV1 {
fn from(kernel: &TxKernelV0) -> TxKernelV1 {
let TxKernelV0 {
features,
fee,
lock_height,
excess,
excess_sig,
} = *kernel;
TxKernelV1 {
features,
fee,
lock_height,
excess,
excess_sig,
}
}
}

View file

@ -0,0 +1,460 @@
// 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.
//! Contains V2 of the slate (grin-wallet 1.1.0)
//! Changes from V1:
//! * ParticipantData struct fields serialized as hex strings instead of arrays:
//! * public_blind_excess
//! * public_nonce
//! * part_sig
//! * message_sig
//! * Transaction fields serialized as hex strings instead of arrays:
//! * offset
//! * Input field serialized as hex strings instead of arrays:
//! commit
//! * Output fields serialized as hex strings instead of arrays:
//! commit
//! proof
//! * TxKernel fields serialized as hex strings instead of arrays:
//! commit
//! signature
//! * version_info field removed
//! * VersionCompatInfo struct created with fields and added to beginning of struct
//! version: u16
//! orig_verion: u16,
//! min_compat_version: u16
use crate::core::core::transaction::{
KernelFeatures, OutputFeatures,
};
use crate::keychain::BlindingFactor;
use crate::util::secp;
use crate::util::secp::key::PublicKey;
use crate::util::secp::pedersen::{Commitment, RangeProof};
use crate::util::secp::Signature;
use crate::core::libtx::secp_ser;
use uuid::Uuid;
use crate::slate_versions::v1::{SlateV1, TransactionV1, TransactionBodyV1, InputV1, OutputV1,TxKernelV1, ParticipantDataV1};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SlateV2 {
/// Versioning info
pub version_info: VersionCompatInfoV2,
/// The number of participants intended to take part in this transaction
pub num_participants: usize,
/// Unique transaction ID, selected by sender
pub id: Uuid,
/// The core transaction data:
/// inputs, outputs, kernels, kernel offset
pub tx: TransactionV2,
/// base amount (excluding fee)
pub amount: u64,
/// fee amount
pub fee: u64,
/// Block height for the transaction
pub height: u64,
/// Lock height
pub lock_height: u64,
/// Participant data, each participant in the transaction will
/// insert their public data here. For now, 0 is sender and 1
/// is receiver, though this will change for multi-party
pub participant_data: Vec<ParticipantDataV2>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct VersionCompatInfoV2 {
/// The current version of the slate format
pub version: u16,
/// Original version this slate was converted from
pub orig_version: u16,
/// Minimum version this slate is compatible with
pub min_compat_version: u16,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ParticipantDataV2 {
/// Id of participant in the transaction. (For now, 0=sender, 1=rec)
pub id: u64,
/// Public key corresponding to private blinding factor
#[serde(with = "secp_ser::pubkey_serde")]
pub public_blind_excess: PublicKey,
/// Public key corresponding to private nonce
#[serde(with = "secp_ser::pubkey_serde")]
pub public_nonce: PublicKey,
/// Public partial signature
#[serde(with = "secp_ser::option_sig_serde")]
pub part_sig: Option<Signature>,
/// A message for other participants
pub message: Option<String>,
/// Signature, created with private key corresponding to 'public_blind_excess'
#[serde(with = "secp_ser::option_sig_serde")]
pub message_sig: Option<Signature>,
}
/// A transaction
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TransactionV2 {
/// The kernel "offset" k2
/// excess is k1G after splitting the key k = k1 + k2
#[serde(
serialize_with = "secp_ser::as_hex",
deserialize_with = "secp_ser::blind_from_hex"
)]
pub offset: BlindingFactor,
/// The transaction body - inputs/outputs/kernels
pub body: TransactionBodyV2,
}
/// TransactionBody is a common abstraction for transaction and block
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TransactionBodyV2 {
/// List of inputs spent by the transaction.
pub inputs: Vec<InputV2>,
/// List of outputs the transaction produces.
pub outputs: Vec<OutputV2>,
/// List of kernels that make up this transaction (usually a single kernel).
pub kernels: Vec<TxKernelV2>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct InputV2 {
/// The features of the output being spent.
/// We will check maturity for coinbase output.
pub features: OutputFeatures,
/// The commit referencing the output being spent.
#[serde(
serialize_with = "secp_ser::as_hex",
deserialize_with = "secp_ser::commitment_from_hex"
)]
pub commit: Commitment,
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub struct OutputV2 {
/// Options for an output's structure or use
pub features: OutputFeatures,
/// The homomorphic commitment representing the output amount
#[serde(
serialize_with = "secp_ser::as_hex",
deserialize_with = "secp_ser::commitment_from_hex"
)]
pub commit: Commitment,
/// A proof that the commitment is in the right range
#[serde(
serialize_with = "secp_ser::as_hex",
deserialize_with = "secp_ser::rangeproof_from_hex"
)]
pub proof: RangeProof,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TxKernelV2 {
/// Options for a kernel's structure or use
pub features: KernelFeatures,
/// Fee originally included in the transaction this proof is for.
pub fee: u64,
/// This kernel is not valid earlier than lock_height blocks
/// The max lock_height of all *inputs* to this transaction
pub lock_height: u64,
/// Remainder of the sum of all transaction commitments. If the transaction
/// is well formed, amounts components should sum to zero and the excess
/// is hence a valid public key.
#[serde(
serialize_with = "secp_ser::as_hex",
deserialize_with = "secp_ser::commitment_from_hex"
)]
pub excess: Commitment,
/// The signature proving the excess is a valid public key, which signs
/// the transaction fee.
#[serde(with = "secp_ser::sig_serde")]
pub excess_sig: secp::Signature,
}
// V2 to V1 Downgrade Conversion ////////////////////////////////////
impl From<SlateV2> for SlateV1 {
fn from(slate: SlateV2) -> SlateV1 {
let SlateV2 {
num_participants,
id,
tx,
amount,
fee,
height,
lock_height,
participant_data,
version_info,
} = slate;
let tx = TransactionV1::from(tx);
let version = 1;
let orig_version = version_info.orig_version as u64;
let participant_data = map_vec!(participant_data, |data| ParticipantDataV1::from(data));
SlateV1 {
num_participants,
id,
tx,
amount,
fee,
height,
lock_height,
participant_data,
version,
orig_version,
}
}
}
impl From<&ParticipantDataV2> for ParticipantDataV1 {
fn from(data: &ParticipantDataV2) -> ParticipantDataV1 {
let ParticipantDataV2 {
id,
public_blind_excess,
public_nonce,
part_sig,
message,
message_sig,
} = data;
let id = *id;
let public_blind_excess = *public_blind_excess;
let public_nonce = *public_nonce;
let part_sig = *part_sig;
let message: Option<String> = message.as_ref().map(|t| String::from(&**t));
let message_sig = *message_sig;
ParticipantDataV1 {
id,
public_blind_excess,
public_nonce,
part_sig,
message,
message_sig,
}
}
}
impl From<TransactionV2> for TransactionV1 {
fn from(tx: TransactionV2) -> TransactionV1 {
let TransactionV2 { offset, body } = tx;
let body = TransactionBodyV1::from(&body);
/*let transaction = TransactionV2::new(body.inputs, body.outputs, body.kernels);
transaction.with_offset(offset)*/
TransactionV1 {
offset,
body,
}
}
}
impl From<&TransactionBodyV2> for TransactionBodyV1 {
fn from(body: &TransactionBodyV2) -> Self {
let TransactionBodyV2 {
inputs,
outputs,
kernels,
} = body;
let inputs = map_vec!(inputs, |inp| InputV1::from(inp));
let outputs = map_vec!(outputs, |out| OutputV1::from(out));
let kernels = map_vec!(kernels, |kern| TxKernelV1::from(kern));
TransactionBodyV1 {
inputs,
outputs,
kernels,
}
}
}
impl From<&InputV2> for InputV1 {
fn from(input: &InputV2) -> InputV1 {
let InputV2 { features, commit } = *input;
InputV1 { features, commit }
}
}
impl From<&OutputV2> for OutputV1 {
fn from(output: &OutputV2) -> OutputV1 {
let OutputV2 {
features,
commit,
proof,
} = *output;
OutputV1 {
features,
commit,
proof,
}
}
}
impl From<&TxKernelV2> for TxKernelV1 {
fn from(kernel: &TxKernelV2) -> TxKernelV1 {
let TxKernelV2 {
features,
fee,
lock_height,
excess,
excess_sig,
} = *kernel;
TxKernelV1 {
features,
fee,
lock_height,
excess,
excess_sig,
}
}
}
// V1 to V2 Upgrade Conversion ////////////////////////////////////
impl From<SlateV1> for SlateV2 {
fn from(slate: SlateV1) -> SlateV2 {
let SlateV1 {
num_participants,
id,
tx,
amount,
fee,
height,
lock_height,
participant_data,
version: _,
orig_version,
} = slate;
let tx = TransactionV2::from(tx);
let version = 2;
let min_compat_version = 0;
let orig_version = orig_version as u16;
let participant_data = map_vec!(participant_data, |data| ParticipantDataV2::from(data));
let version_info = VersionCompatInfoV2 {
version,
orig_version,
min_compat_version,
};
SlateV2 {
num_participants,
id,
tx,
amount,
fee,
height,
lock_height,
participant_data,
version_info,
}
}
}
impl From<&ParticipantDataV1> for ParticipantDataV2 {
fn from(data: &ParticipantDataV1) -> ParticipantDataV2 {
let ParticipantDataV1 {
id,
public_blind_excess,
public_nonce,
part_sig,
message,
message_sig,
} = data;
let id = *id;
let public_blind_excess = *public_blind_excess;
let public_nonce = *public_nonce;
let part_sig = *part_sig;
let message: Option<String> = message.as_ref().map(|t| String::from(&**t));
let message_sig = *message_sig;
ParticipantDataV2 {
id,
public_blind_excess,
public_nonce,
part_sig,
message,
message_sig,
}
}
}
impl From<TransactionV1> for TransactionV2 {
fn from(tx: TransactionV1) -> TransactionV2 {
let TransactionV1 { offset, body } = tx;
let body = TransactionBodyV2::from(&body);
/*let transaction = TransactionV2::new(body.inputs, body.outputs, body.kernels);
transaction.with_offset(offset)*/
TransactionV2 {
offset,
body,
}
}
}
impl From<&TransactionBodyV1> for TransactionBodyV2 {
fn from(body: &TransactionBodyV1) -> Self {
let TransactionBodyV1 {
inputs,
outputs,
kernels,
} = body;
let inputs = map_vec!(inputs, |inp| InputV2::from(inp));
let outputs = map_vec!(outputs, |out| OutputV2::from(out));
let kernels = map_vec!(kernels, |kern| TxKernelV2::from(kern));
TransactionBodyV2 {
inputs,
outputs,
kernels,
}
}
}
impl From<&InputV1> for InputV2 {
fn from(input: &InputV1) -> InputV2 {
let InputV1 { features, commit } = *input;
InputV2 { features, commit }
}
}
impl From<&OutputV1> for OutputV2 {
fn from(output: &OutputV1) -> OutputV2 {
let OutputV1 {
features,
commit,
proof,
} = *output;
OutputV2 {
features,
commit,
proof,
}
}
}
impl From<&TxKernelV1> for TxKernelV2 {
fn from(kernel: &TxKernelV1) -> TxKernelV2 {
let TxKernelV1 {
features,
fee,
lock_height,
excess,
excess_sig,
} = *kernel;
TxKernelV2 {
features,
fee,
lock_height,
excess,
excess_sig,
}
}
}

View file

@ -720,4 +720,6 @@ pub struct SendTXArgs {
pub selection_strategy_is_use_all: bool,
/// Optional message, that will be signed
pub message: Option<String>,
/// Optional slate version to target when sending
pub target_slate_version: Option<u16>,
}

View file

@ -0,0 +1,69 @@
// Copyright 2019 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.
//! core::libtx specific tests
use grin_libwallet::slate::Slate;
// test all slate conversions
#[test]
fn slate_conversions() {
// Test V0 to V2
let v0 = include_str!("slates/v0.slate");
let res = Slate::deserialize_upgrade(&v0);
assert!(res.is_ok());
// should serialize as latest
let res = res.unwrap();
assert_eq!(res.version_info.orig_version, 0);
let s = res.serialize_to_version(Some(2));
assert!(s.is_ok());
println!("v0 -> v2: {}", s.unwrap());
// Test V1 to V2
let v1 = include_str!("slates/v1.slate");
let res = Slate::deserialize_upgrade(&v1);
assert!(res.is_ok());
// should serialize as latest
let res = res.unwrap();
assert_eq!(res.version_info.orig_version, 1);
let s = res.serialize_to_version(Some(2));
assert!(s.is_ok());
println!("v1 -> v2: {}", s.unwrap());
// V2 -> V2, check version
let v2 = include_str!("slates/v2.slate");
let res = Slate::deserialize_upgrade(&v2);
assert!(res.is_ok());
let res = res.unwrap().serialize_to_version(Some(2));
let s = res.unwrap();
let res = Slate::deserialize_upgrade(&s).unwrap();
assert_eq!(res.version_info.orig_version, 2);
// Downgrade to V1
let v2 = include_str!("slates/v2.slate");
let res = Slate::deserialize_upgrade(&v2);
assert!(res.is_ok());
// downgrade
let s = res.unwrap().serialize_to_version(Some(1));
assert!(s.is_ok());
println!("v2 -> v1: {}", s.unwrap());
// Downgrade to V0
let v2 = include_str!("slates/v2.slate");
let res = Slate::deserialize_upgrade(&v2);
assert!(res.is_ok());
// downgrade
let s = res.unwrap().serialize_to_version(Some(0));
assert!(s.is_ok());
println!("v2 -> v0: {}", s.unwrap());
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,54 @@
{
"version_info": {
"version": 2,
"orig_version": 2,
"min_compat_version": 0
},
"num_participants": 2,
"id": "e0c69803-db50-40d9-a968-496e86660cd4",
"tx": {
"offset": "a853afebf15d8c111f654059940945b4782c38660397257707b53ebfdb403a52",
"body": {
"inputs": [
{
"features": "Plain",
"commit": "09d304aed6300f8124eb8b2d46cc1e0a7b7a9b9042b9cb35e020dd9552df9c697c"
},
{
"features": "Plain",
"commit": "09d3cc915dc317485dc8bbf5ec4669a40bb9d3300c96df3384d116ddad498d0db1"
}
],
"outputs": [
{
"features": "Plain",
"commit": "08d3453eb5ce35a1b6bbc2a7a9afe32483774c011f9975f42393468fa5cd4349a7",
"proof": "db206834c022eec1f346a67b571941f1b6867ae4bd8189ca064b690b32367e454a4a5add51761c472b0e0994ce7f00578bc06ae7b9afdf8ce2118546771976d900464214d3b831fe74a94876980a928315afb5c2af018f5d595e56fd740658b0c4f2d4f463e401cbec2704b31005cd8d7d87458290a3668cc2e82c2b0867d991072544f9e8c805056c97ff66cc052cf2a9666768d0d68acdc6ea1fc80fb9b5e6e19366c7b49ada38b368c0c3e3f73977df003f0c6744737b31b058c7d4e2766e97ee04147ef04be22906f087842205813c7d817598c689c840087d35cc9ce9a98f52e68c66bdde0521acf814737efd072654728f418e6494a7eb7fa6305ec7d572abb91d3bfabf7215e77e0c9cf33769572ff9a8671a24e0a04302e6ac5cee9928ec11d7c9861ed18718142a1563967955e428e4134c6dde88bdbea11248ae99d784a56592a065122948b2c2fb8be25c119345b9fa7db2efbdfcf846e9ba47efff3d0024bdb998e93bcabe1a00222ba36b88ec4f7c2a2151bf00b225f6a14b4de66658daecaa219813f51a9239eec961c6713106b64c4f1ff851e54795220ee3cdc59531f0acc050e17c848b21b916b571b2f6b093fccec046587d0a1718c82bd7a78e22223fe1484dec841820139950dce84c97659b0eac1bfa5fce85d5602f480d714dcab1459c4f29e2746bccb4494d800935ddc630f53257649f1544702003a583d55422e957192faebffcb8d883ec6bb2132c86249d6b50edae84f3c06842b2714267249c8df58e2edc3aca69dff66ee32fb5d93db9156df373ab51df2c094742517b46ff95298caec3464151ea91c8a8fe74bb60ffb94c7c974aa6cb2e47dd1ee05f471e2d2f0b555efe17302769139760bc110c979453f7bfab43b3f3cba4d94c8a5eeb58264bb5c16de6acbbc9c56cb069e7e1ac1f7838d0a6424017b8d563"
}
],
"kernels": [
{
"features": "HeightLocked",
"fee": 7000000,
"lock_height": 70194,
"excess": "000000000000000000000000000000000000000000000000000000000000000000",
"excess_sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
}
]
}
},
"amount": 84825921007,
"fee": 7000000,
"height": 70194,
"lock_height": 70194,
"participant_data": [
{
"id": 0,
"public_blind_excess": "0391f8fc74bb5ff4de373352e7dee00860d4fb78ed7a99765585af980d8a31c615",
"public_nonce": "0206562c21a7f3a003622722ee93c4ecbbecead4a6ad8ee5d930b51ca4a6ca6d01",
"part_sig": null,
"message": null,
"message_sig": null
}
]
}

View file

@ -16,7 +16,6 @@
use std::fs::File;
use std::io::{Read, Write};
use crate::adapters::util::{deserialize_slate, serialize_slate};
use crate::libwallet::slate::Slate;
use crate::libwallet::Error;
use crate::{WalletCommAdapter, WalletConfig};
@ -43,7 +42,7 @@ impl WalletCommAdapter for FileWalletCommAdapter {
fn send_tx_async(&self, dest: &str, slate: &Slate) -> Result<(), Error> {
let mut pub_tx = File::create(dest)?;
let slate_string = serialize_slate(slate);
let slate_string = slate.serialize_to_version(Some(slate.version_info.orig_version))?;
pub_tx.write_all(slate_string.as_bytes())?;
pub_tx.sync_all()?;
Ok(())
@ -53,7 +52,7 @@ impl WalletCommAdapter for FileWalletCommAdapter {
let mut pub_tx_f = File::open(params)?;
let mut content = String::new();
pub_tx_f.read_to_string(&mut content)?;
Ok(deserialize_slate(&content))
Ok(Slate::deserialize_upgrade(&content)?)
}
fn listen(

View file

@ -12,10 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::adapters::util::get_versioned_slate;
use crate::api;
use crate::controller;
use crate::libwallet::slate::{Slate, VersionedSlate};
use crate::libwallet::slate::Slate;
use crate::libwallet::{Error, ErrorKind};
use crate::{instantiate_wallet, HTTPNodeClient, WalletCommAdapter, WalletConfig};
/// HTTP Wallet 'plugin' implementation
@ -48,15 +47,15 @@ impl WalletCommAdapter for HTTPWalletCommAdapter {
}
let url = format!("{}/v1/wallet/foreign/receive_tx", dest);
debug!("Posting transaction slate to {}", url);
let slate = get_versioned_slate(slate);
let res: Result<VersionedSlate, _> = api::client::post(url.as_str(), None, &slate);
let slate = slate.serialize_to_version(Some(slate.version_info.orig_version))?;
let res: Result<String, _> = api::client::post(url.as_str(), None, &slate);
match res {
Err(e) => {
let report = format!("Posting transaction slate (is recipient listening?): {}", e);
error!("{}", report);
Err(ErrorKind::ClientCallback(report).into())
}
Ok(r) => Ok(r.into()),
Ok(r) => Ok(Slate::deserialize_upgrade(&r)?),
}
}

View file

@ -15,8 +15,7 @@
// Keybase Wallet Plugin
use crate::controller;
use crate::libwallet::slate::{Slate, VersionedSlate};
use crate::libwallet::slate_versions::v0::SlateV0;
use crate::libwallet::slate::Slate;
use crate::libwallet::{Error, ErrorKind};
use crate::{instantiate_wallet, HTTPNodeClient, WalletCommAdapter, WalletConfig};
use failure::ResultExt;
@ -259,7 +258,7 @@ fn poll(nseconds: u64, channel: &str) -> Option<Slate> {
while start.elapsed().as_secs() < nseconds {
let unread = read_from_channel(channel, SLATE_SIGNED);
for msg in unread.unwrap().iter() {
let blob = from_str::<VersionedSlate>(msg);
let blob = Slate::deserialize_upgrade(&msg);
match blob {
Ok(slate) => {
let slate: Slate = slate.into();
@ -268,7 +267,7 @@ fn poll(nseconds: u64, channel: &str) -> Option<Slate> {
channel, slate.id,
);
return Some(slate);
}
},
Err(_) => (),
}
}
@ -350,7 +349,7 @@ impl WalletCommAdapter for KeybaseWalletCommAdapter {
break;
}
for (msg, channel) in &unread.unwrap() {
let blob = from_str::<VersionedSlate>(msg);
let blob = Slate::deserialize_upgrade(&msg);
match blob {
Ok(message) => {
let mut slate: Slate = message.clone().into();
@ -385,15 +384,9 @@ impl WalletCommAdapter for KeybaseWalletCommAdapter {
}) {
// Reply to the same channel with topic SLATE_SIGNED
Ok(_) => {
let success = match message {
// Send the same version of slate that was sent to us
VersionedSlate::V0(_) => {
send(SlateV0::from(slate), channel, SLATE_SIGNED, TTL)
}
VersionedSlate::V1(_) => {
send(slate, channel, SLATE_SIGNED, TTL)
}
};
let slate = slate.serialize_to_version(Some(slate.version_info.orig_version))?;
// TODO: Send the same version of slate that was sent to us
let success = send(slate, channel, SLATE_SIGNED, TTL);
if success {
notify_on_receive(

View file

@ -16,7 +16,6 @@ mod file;
mod http;
mod keybase;
mod null;
pub mod util;
pub use self::file::FileWalletCommAdapter;
pub use self::http::HTTPWalletCommAdapter;

View file

@ -1,23 +0,0 @@
use crate::libwallet::slate::{Slate, VersionedSlate};
use crate::libwallet::slate_versions::v0::SlateV0;
use crate::libwallet::ErrorKind;
use serde_json as json;
pub fn get_versioned_slate(slate: &Slate) -> VersionedSlate {
let slate = slate.clone();
match slate.version {
0 => VersionedSlate::V0(SlateV0::from(slate)),
_ => VersionedSlate::V1(slate),
}
}
pub fn serialize_slate(slate: &Slate) -> String {
json::to_string(&get_versioned_slate(slate)).unwrap()
}
pub fn deserialize_slate(raw_slate: &str) -> Slate {
let versioned_slate: VersionedSlate = json::from_str(&raw_slate)
.map_err(|err| ErrorKind::Format(err.to_string()))
.unwrap();
versioned_slate.into()
}

View file

@ -206,6 +206,7 @@ pub struct SendArgs {
pub change_outputs: usize,
pub fluff: bool,
pub max_outputs: usize,
pub target_slate_version: Option<u16>,
}
pub fn send(
@ -241,6 +242,7 @@ pub fn send(
args.change_outputs,
args.selection_strategy == "all",
args.message.clone(),
args.target_slate_version,
);
let (mut slate, lock_fn) = match result {
Ok(s) => {

View file

@ -15,14 +15,13 @@
//! Controller for wallet.. instantiates and handles listeners (or single-run
//! invocations) as needed.
//! Still experimental
use crate::adapters::util::get_versioned_slate;
use crate::adapters::{FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCommAdapter};
use crate::api::{ApiServer, BasicAuthMiddleware, Handler, ResponseFuture, Router, TLSConfig};
use crate::apiwallet::api::{APIForeign, APIOwner};
use crate::core::core;
use crate::core::core::Transaction;
use crate::keychain::Keychain;
use crate::libwallet::slate::{Slate, VersionedSlate};
use crate::libwallet::slate::Slate;
use crate::libwallet::types::{
CbData, NodeClient, OutputData, SendTXArgs, TxLogEntry, WalletBackend, WalletInfo,
};
@ -330,6 +329,7 @@ where
args.num_change_outputs,
args.selection_strategy_is_use_all,
args.message,
args.target_slate_version,
);
let (mut slate, lock_fn) = match result {
Ok(s) => {
@ -661,17 +661,17 @@ where
&self,
req: Request<Body>,
api: APIForeign<T, C, K>,
) -> Box<dyn Future<Item = VersionedSlate, Error = Error> + Send> {
) -> Box<dyn Future<Item = String, Error = Error> + Send> {
Box::new(parse_body(req).and_then(
//TODO: No way to insert a message from the params
move |slate: VersionedSlate| {
let mut slate: Slate = slate.into();
move |slate_str: String| {
let mut slate: Slate = Slate::deserialize_upgrade(&slate_str).unwrap();
if let Err(e) = api.verify_slate_messages(&slate) {
error!("Error validating participant messages: {}", e);
err(e)
} else {
match api.receive_tx(&mut slate, None, None) {
Ok(_) => ok(get_versioned_slate(&slate.clone())),
Ok(_) => ok(slate.serialize_to_version(Some(slate.version_info.orig_version)).unwrap()),
Err(e) => {
error!("receive_tx: failed with error: {}", e);
err(e)

View file

@ -202,6 +202,7 @@ where
1, // num change outputs
true, // select all outputs
None,
None,
)?;
let mut slate = client.send_tx_slate_direct(dest, &slate_i)?;
api.tx_lock_outputs(&slate, lock_fn)?;

View file

@ -185,6 +185,7 @@ fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
1, // num change outputs
true, // select all outputs
None,
None,
)?;
slate = client1.send_tx_slate_direct("wallet2", &slate)?;
api.tx_lock_outputs(&slate, lock_fn)?;

View file

@ -163,6 +163,7 @@ fn check_repair_impl(test_dir: &str) -> Result<(), libwallet::Error> {
1, // num change outputs
true, // select all outputs
None, // optional message
None,
)?;
// output tx file
let file_adapter = FileWalletCommAdapter::new();

View file

@ -111,6 +111,7 @@ fn file_exchange_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
1, // num change outputs
true, // select all outputs
Some(message.to_owned()), // optional message
None,
)?;
// output tx file
let file_adapter = FileWalletCommAdapter::new();

View file

@ -109,6 +109,7 @@ fn file_repost_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
1, // num change outputs
true, // select all outputs
None,
None,
)?;
// output tx file
let file_adapter = FileWalletCommAdapter::new();
@ -205,6 +206,7 @@ fn file_repost_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
1, // num change outputs
true, // select all outputs
None,
None,
)?;
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, lock_fn)?;

View file

@ -242,6 +242,7 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
1, // num change outputs
true, // select all outputs
None,
None,
)?;
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, lock_fn)?;
@ -264,6 +265,7 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
1, // num change outputs
true, // select all outputs
None,
None,
)?;
slate = client1.send_tx_slate_direct("wallet3", &slate_i)?;
sender_api.tx_lock_outputs(&slate, lock_fn)?;
@ -286,6 +288,7 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
1, // num change outputs
true, // select all outputs
None,
None,
)?;
slate = client3.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, lock_fn)?;
@ -314,6 +317,7 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
1, // num change outputs
true, // select all outputs
None,
None,
)?;
slate = client3.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, lock_fn)?;

View file

@ -92,6 +92,7 @@ fn self_send_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
1, // num change outputs
true, // select all outputs
None,
None,
)?;
api.tx_lock_outputs(&slate, lock_fn)?;
// Send directly to self

View file

@ -104,6 +104,7 @@ fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> {
1, // num change outputs
true, // select all outputs
None,
None,
)?;
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, lock_fn)?;
@ -266,6 +267,7 @@ fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> {
1, // num change outputs
true, // select all outputs
None,
None,
)?;
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, lock_fn)?;
@ -363,6 +365,7 @@ fn tx_rollback(test_dir: &str) -> Result<(), libwallet::Error> {
1, // num change outputs
true, // select all outputs
None,
None,
)?;
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, lock_fn)?;

View file

@ -393,6 +393,17 @@ pub fn parse_send_args(args: &ArgMatches) -> Result<command::SendArgs, ParseErro
// max_outputs
let max_outputs = 500;
// target slate version to create/send
let target_slate_version = {
match args.is_present("target_slate_version") {
true => {
let v = parse_required(args, "target_slate_version")?;
Some(parse_u64(v, "target_slate_version")? as u16)
},
false => None,
}
};
Ok(command::SendArgs {
amount: amount,
message: message,
@ -404,6 +415,7 @@ pub fn parse_send_args(args: &ArgMatches) -> Result<command::SendArgs, ParseErro
change_outputs: change_outputs,
fluff: fluff,
max_outputs: max_outputs,
target_slate_version: target_slate_version,
})
}

View file

@ -132,6 +132,11 @@ subcommands:
short: t
long: stored_tx
takes_value: true
- slate_version:
help: Target slate version to output/send to receiver
short: v
long: slate_version
takes_value: true
- receive:
about: Processes a transaction file to accept a transfer from a sender
args: