mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-12-25 07:29:32 -05:00
Save and recover protocol state from disk
NOTE: This implementation saves secrets to disk! It is not secure. The storage API allows the caller to atomically record the state of the protocol. The user can retrieve this recorded state and re-commence the protocol from that point. The state is recorded using a hard coded key, causing it to overwrite the previously recorded state. This limitation means that this recovery mechanism should not be used in a program that simultaneously manages the execution of multiple swaps. An e2e test was added to show how to save, recover and resume protocol execution. This logic could also be integrated into the run_until functions to automate saving but was not included at this stage as protocol execution is currently under development. Serialisation and deserialisation was implemented on the states to allow the to be stored using the database. Currently the secret's are also being stored to disk but should be recovered from a seed or wallets.
This commit is contained in:
parent
ea064c95b4
commit
39afb4196b
@ -7,14 +7,15 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
bitcoin = { version = "0.23", features = ["rand"] }
|
bitcoin = { version = "0.23", features = ["rand", "serde"] }
|
||||||
cross-curve-dleq = { git = "https://github.com/comit-network/cross-curve-dleq", rev = "49171f5e08473d46f951fb1fc338fe437d974d3c" }
|
cross-curve-dleq = { git = "https://github.com/comit-network/cross-curve-dleq", rev = "49171f5e08473d46f951fb1fc338fe437d974d3c" }
|
||||||
curve25519-dalek = "2"
|
curve25519-dalek = "2"
|
||||||
ecdsa_fun = { git = "https://github.com/LLFourn/secp256kfun", rev = "510d48ef6a2b19805f7f5c70c598e5b03f668e7a", features = ["libsecp_compat"] }
|
ecdsa_fun = { git = "https://github.com/LLFourn/secp256kfun", rev = "510d48ef6a2b19805f7f5c70c598e5b03f668e7a", features = ["libsecp_compat", "serde", "serialization"] }
|
||||||
ed25519-dalek = "1.0.0-pre.4" # Cannot be 1 because they depend on curve25519-dalek version 3
|
ed25519-dalek = { version = "1.0.0-pre.4", features = ["serde"] }# Cannot be 1 because they depend on curve25519-dalek version 3
|
||||||
miniscript = "1"
|
miniscript = { version = "1", features = ["serde"] }
|
||||||
monero = "0.9"
|
monero = { version = "0.9", features = ["serde_support"] }
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
sha2 = "0.9"
|
sha2 = "0.9"
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
@ -25,6 +26,8 @@ bitcoin-harness = { git = "https://github.com/coblox/bitcoin-harness-rs", rev =
|
|||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
monero-harness = { path = "../monero-harness" }
|
monero-harness = { path = "../monero-harness" }
|
||||||
reqwest = { version = "0.10", default-features = false }
|
reqwest = { version = "0.10", default-features = false }
|
||||||
|
serde_cbor = "0.11"
|
||||||
|
sled = "0.34"
|
||||||
testcontainers = "0.10"
|
testcontainers = "0.10"
|
||||||
tokio = { version = "0.2", default-features = false, features = ["blocking", "macros", "rt-core", "time", "rt-threaded"] }
|
tokio = { version = "0.2", default-features = false, features = ["blocking", "macros", "rt-core", "time", "rt-threaded"] }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
@ -3,6 +3,7 @@ use crate::{
|
|||||||
bitcoin::{BroadcastSignedTransaction, WatchForRawTransaction},
|
bitcoin::{BroadcastSignedTransaction, WatchForRawTransaction},
|
||||||
bob, monero,
|
bob, monero,
|
||||||
monero::{CreateWalletForOutput, Transfer},
|
monero::{CreateWalletForOutput, Transfer},
|
||||||
|
serde::{bitcoin_amount, cross_curve_dleq_scalar, ecdsa_fun_signature},
|
||||||
transport::{ReceiveMessage, SendMessage},
|
transport::{ReceiveMessage, SendMessage},
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
@ -11,6 +12,7 @@ use ecdsa_fun::{
|
|||||||
nonce::Deterministic,
|
nonce::Deterministic,
|
||||||
};
|
};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
|
||||||
@ -129,11 +131,13 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct State0 {
|
pub struct State0 {
|
||||||
a: bitcoin::SecretKey,
|
a: bitcoin::SecretKey,
|
||||||
|
#[serde(with = "cross_curve_dleq_scalar")]
|
||||||
s_a: cross_curve_dleq::Scalar,
|
s_a: cross_curve_dleq::Scalar,
|
||||||
v_a: monero::PrivateViewKey,
|
v_a: monero::PrivateViewKey,
|
||||||
|
#[serde(with = "bitcoin_amount")]
|
||||||
btc: bitcoin::Amount,
|
btc: bitcoin::Amount,
|
||||||
xmr: monero::Amount,
|
xmr: monero::Amount,
|
||||||
refund_timelock: u32,
|
refund_timelock: u32,
|
||||||
@ -215,14 +219,16 @@ impl State0 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct State1 {
|
pub struct State1 {
|
||||||
a: bitcoin::SecretKey,
|
a: bitcoin::SecretKey,
|
||||||
B: bitcoin::PublicKey,
|
B: bitcoin::PublicKey,
|
||||||
|
#[serde(with = "cross_curve_dleq_scalar")]
|
||||||
s_a: cross_curve_dleq::Scalar,
|
s_a: cross_curve_dleq::Scalar,
|
||||||
S_b_monero: monero::PublicKey,
|
S_b_monero: monero::PublicKey,
|
||||||
S_b_bitcoin: bitcoin::PublicKey,
|
S_b_bitcoin: bitcoin::PublicKey,
|
||||||
v: monero::PrivateViewKey,
|
v: monero::PrivateViewKey,
|
||||||
|
#[serde(with = "bitcoin_amount")]
|
||||||
btc: bitcoin::Amount,
|
btc: bitcoin::Amount,
|
||||||
xmr: monero::Amount,
|
xmr: monero::Amount,
|
||||||
refund_timelock: u32,
|
refund_timelock: u32,
|
||||||
@ -253,14 +259,16 @@ impl State1 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct State2 {
|
pub struct State2 {
|
||||||
a: bitcoin::SecretKey,
|
a: bitcoin::SecretKey,
|
||||||
B: bitcoin::PublicKey,
|
B: bitcoin::PublicKey,
|
||||||
|
#[serde(with = "cross_curve_dleq_scalar")]
|
||||||
s_a: cross_curve_dleq::Scalar,
|
s_a: cross_curve_dleq::Scalar,
|
||||||
S_b_monero: monero::PublicKey,
|
S_b_monero: monero::PublicKey,
|
||||||
S_b_bitcoin: bitcoin::PublicKey,
|
S_b_bitcoin: bitcoin::PublicKey,
|
||||||
v: monero::PrivateViewKey,
|
v: monero::PrivateViewKey,
|
||||||
|
#[serde(with = "bitcoin_amount")]
|
||||||
btc: bitcoin::Amount,
|
btc: bitcoin::Amount,
|
||||||
xmr: monero::Amount,
|
xmr: monero::Amount,
|
||||||
refund_timelock: u32,
|
refund_timelock: u32,
|
||||||
@ -328,14 +336,16 @@ impl State2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct State3 {
|
pub struct State3 {
|
||||||
a: bitcoin::SecretKey,
|
a: bitcoin::SecretKey,
|
||||||
B: bitcoin::PublicKey,
|
B: bitcoin::PublicKey,
|
||||||
|
#[serde(with = "cross_curve_dleq_scalar")]
|
||||||
s_a: cross_curve_dleq::Scalar,
|
s_a: cross_curve_dleq::Scalar,
|
||||||
S_b_monero: monero::PublicKey,
|
S_b_monero: monero::PublicKey,
|
||||||
S_b_bitcoin: bitcoin::PublicKey,
|
S_b_bitcoin: bitcoin::PublicKey,
|
||||||
v: monero::PrivateViewKey,
|
v: monero::PrivateViewKey,
|
||||||
|
#[serde(with = "bitcoin_amount")]
|
||||||
btc: bitcoin::Amount,
|
btc: bitcoin::Amount,
|
||||||
xmr: monero::Amount,
|
xmr: monero::Amount,
|
||||||
refund_timelock: u32,
|
refund_timelock: u32,
|
||||||
@ -344,7 +354,9 @@ pub struct State3 {
|
|||||||
redeem_address: bitcoin::Address,
|
redeem_address: bitcoin::Address,
|
||||||
punish_address: bitcoin::Address,
|
punish_address: bitcoin::Address,
|
||||||
tx_lock: bitcoin::TxLock,
|
tx_lock: bitcoin::TxLock,
|
||||||
|
#[serde(with = "ecdsa_fun_signature")]
|
||||||
tx_punish_sig_bob: bitcoin::Signature,
|
tx_punish_sig_bob: bitcoin::Signature,
|
||||||
|
#[serde(with = "ecdsa_fun_signature")]
|
||||||
tx_cancel_sig_bob: bitcoin::Signature,
|
tx_cancel_sig_bob: bitcoin::Signature,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,14 +393,16 @@ impl State3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct State4 {
|
pub struct State4 {
|
||||||
a: bitcoin::SecretKey,
|
a: bitcoin::SecretKey,
|
||||||
B: bitcoin::PublicKey,
|
B: bitcoin::PublicKey,
|
||||||
|
#[serde(with = "cross_curve_dleq_scalar")]
|
||||||
s_a: cross_curve_dleq::Scalar,
|
s_a: cross_curve_dleq::Scalar,
|
||||||
S_b_monero: monero::PublicKey,
|
S_b_monero: monero::PublicKey,
|
||||||
S_b_bitcoin: bitcoin::PublicKey,
|
S_b_bitcoin: bitcoin::PublicKey,
|
||||||
v: monero::PrivateViewKey,
|
v: monero::PrivateViewKey,
|
||||||
|
#[serde(with = "bitcoin_amount")]
|
||||||
btc: bitcoin::Amount,
|
btc: bitcoin::Amount,
|
||||||
xmr: monero::Amount,
|
xmr: monero::Amount,
|
||||||
refund_timelock: u32,
|
refund_timelock: u32,
|
||||||
@ -397,7 +411,9 @@ pub struct State4 {
|
|||||||
redeem_address: bitcoin::Address,
|
redeem_address: bitcoin::Address,
|
||||||
punish_address: bitcoin::Address,
|
punish_address: bitcoin::Address,
|
||||||
tx_lock: bitcoin::TxLock,
|
tx_lock: bitcoin::TxLock,
|
||||||
|
#[serde(with = "ecdsa_fun_signature")]
|
||||||
tx_punish_sig_bob: bitcoin::Signature,
|
tx_punish_sig_bob: bitcoin::Signature,
|
||||||
|
#[serde(with = "ecdsa_fun_signature")]
|
||||||
tx_cancel_sig_bob: bitcoin::Signature,
|
tx_cancel_sig_bob: bitcoin::Signature,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,14 +500,16 @@ impl State4 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct State5 {
|
pub struct State5 {
|
||||||
a: bitcoin::SecretKey,
|
a: bitcoin::SecretKey,
|
||||||
B: bitcoin::PublicKey,
|
B: bitcoin::PublicKey,
|
||||||
|
#[serde(with = "cross_curve_dleq_scalar")]
|
||||||
s_a: cross_curve_dleq::Scalar,
|
s_a: cross_curve_dleq::Scalar,
|
||||||
S_b_monero: monero::PublicKey,
|
S_b_monero: monero::PublicKey,
|
||||||
S_b_bitcoin: bitcoin::PublicKey,
|
S_b_bitcoin: bitcoin::PublicKey,
|
||||||
v: monero::PrivateViewKey,
|
v: monero::PrivateViewKey,
|
||||||
|
#[serde(with = "bitcoin_amount")]
|
||||||
btc: bitcoin::Amount,
|
btc: bitcoin::Amount,
|
||||||
xmr: monero::Amount,
|
xmr: monero::Amount,
|
||||||
refund_timelock: u32,
|
refund_timelock: u32,
|
||||||
@ -501,7 +519,9 @@ pub struct State5 {
|
|||||||
punish_address: bitcoin::Address,
|
punish_address: bitcoin::Address,
|
||||||
tx_lock: bitcoin::TxLock,
|
tx_lock: bitcoin::TxLock,
|
||||||
tx_lock_proof: monero::TransferProof,
|
tx_lock_proof: monero::TransferProof,
|
||||||
|
#[serde(with = "ecdsa_fun_signature")]
|
||||||
tx_punish_sig_bob: bitcoin::Signature,
|
tx_punish_sig_bob: bitcoin::Signature,
|
||||||
|
#[serde(with = "ecdsa_fun_signature")]
|
||||||
tx_cancel_sig_bob: bitcoin::Signature,
|
tx_cancel_sig_bob: bitcoin::Signature,
|
||||||
lock_xmr_fee: monero::Amount,
|
lock_xmr_fee: monero::Amount,
|
||||||
}
|
}
|
||||||
@ -575,14 +595,16 @@ impl State5 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct State6 {
|
pub struct State6 {
|
||||||
a: bitcoin::SecretKey,
|
a: bitcoin::SecretKey,
|
||||||
B: bitcoin::PublicKey,
|
B: bitcoin::PublicKey,
|
||||||
|
#[serde(with = "cross_curve_dleq_scalar")]
|
||||||
s_a: cross_curve_dleq::Scalar,
|
s_a: cross_curve_dleq::Scalar,
|
||||||
S_b_monero: monero::PublicKey,
|
S_b_monero: monero::PublicKey,
|
||||||
S_b_bitcoin: bitcoin::PublicKey,
|
S_b_bitcoin: bitcoin::PublicKey,
|
||||||
v: monero::PrivateViewKey,
|
v: monero::PrivateViewKey,
|
||||||
|
#[serde(with = "bitcoin_amount")]
|
||||||
btc: bitcoin::Amount,
|
btc: bitcoin::Amount,
|
||||||
xmr: monero::Amount,
|
xmr: monero::Amount,
|
||||||
refund_timelock: u32,
|
refund_timelock: u32,
|
||||||
@ -591,6 +613,7 @@ pub struct State6 {
|
|||||||
redeem_address: bitcoin::Address,
|
redeem_address: bitcoin::Address,
|
||||||
punish_address: bitcoin::Address,
|
punish_address: bitcoin::Address,
|
||||||
tx_lock: bitcoin::TxLock,
|
tx_lock: bitcoin::TxLock,
|
||||||
|
#[serde(with = "ecdsa_fun_signature")]
|
||||||
tx_punish_sig_bob: bitcoin::Signature,
|
tx_punish_sig_bob: bitcoin::Signature,
|
||||||
tx_redeem_encsig: EncryptedSignature,
|
tx_redeem_encsig: EncryptedSignature,
|
||||||
lock_xmr_fee: monero::Amount,
|
lock_xmr_fee: monero::Amount,
|
||||||
|
@ -21,6 +21,7 @@ use ecdsa_fun::{
|
|||||||
pub use ecdsa_fun::{adaptor::EncryptedSignature, Signature};
|
pub use ecdsa_fun::{adaptor::EncryptedSignature, Signature};
|
||||||
use miniscript::{Descriptor, Segwitv0};
|
use miniscript::{Descriptor, Segwitv0};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ pub use crate::bitcoin::transactions::{TxCancel, TxLock, TxPunish, TxRedeem, TxR
|
|||||||
|
|
||||||
pub const TX_FEE: u64 = 10_000;
|
pub const TX_FEE: u64 = 10_000;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
||||||
pub struct SecretKey {
|
pub struct SecretKey {
|
||||||
inner: Scalar,
|
inner: Scalar,
|
||||||
public: Point,
|
public: Point,
|
||||||
@ -83,7 +84,7 @@ impl SecretKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct PublicKey(Point);
|
pub struct PublicKey(Point);
|
||||||
|
|
||||||
impl From<PublicKey> for Point<Jacobian> {
|
impl From<PublicKey> for Point<Jacobian> {
|
||||||
|
@ -8,9 +8,10 @@ use bitcoin::{
|
|||||||
};
|
};
|
||||||
use ecdsa_fun::Signature;
|
use ecdsa_fun::Signature;
|
||||||
use miniscript::Descriptor;
|
use miniscript::Descriptor;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct TxLock {
|
pub struct TxLock {
|
||||||
inner: Transaction,
|
inner: Transaction,
|
||||||
output_descriptor: Descriptor<::bitcoin::PublicKey>,
|
output_descriptor: Descriptor<::bitcoin::PublicKey>,
|
||||||
|
@ -6,6 +6,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
monero,
|
monero,
|
||||||
monero::{CheckTransfer, CreateWalletForOutput},
|
monero::{CheckTransfer, CreateWalletForOutput},
|
||||||
|
serde::{bitcoin_amount, cross_curve_dleq_scalar, monero_private_key},
|
||||||
transport::{ReceiveMessage, SendMessage},
|
transport::{ReceiveMessage, SendMessage},
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
@ -15,6 +16,7 @@ use ecdsa_fun::{
|
|||||||
Signature,
|
Signature,
|
||||||
};
|
};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
|
||||||
@ -102,11 +104,13 @@ impl_from_child_enum!(State3, State);
|
|||||||
impl_from_child_enum!(State4, State);
|
impl_from_child_enum!(State4, State);
|
||||||
impl_from_child_enum!(State5, State);
|
impl_from_child_enum!(State5, State);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct State0 {
|
pub struct State0 {
|
||||||
b: bitcoin::SecretKey,
|
b: bitcoin::SecretKey,
|
||||||
|
#[serde(with = "cross_curve_dleq_scalar")]
|
||||||
s_b: cross_curve_dleq::Scalar,
|
s_b: cross_curve_dleq::Scalar,
|
||||||
v_b: monero::PrivateViewKey,
|
v_b: monero::PrivateViewKey,
|
||||||
|
#[serde(with = "bitcoin_amount")]
|
||||||
btc: bitcoin::Amount,
|
btc: bitcoin::Amount,
|
||||||
xmr: monero::Amount,
|
xmr: monero::Amount,
|
||||||
refund_timelock: u32,
|
refund_timelock: u32,
|
||||||
@ -190,14 +194,16 @@ impl State0 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct State1 {
|
pub struct State1 {
|
||||||
A: bitcoin::PublicKey,
|
A: bitcoin::PublicKey,
|
||||||
b: bitcoin::SecretKey,
|
b: bitcoin::SecretKey,
|
||||||
|
#[serde(with = "cross_curve_dleq_scalar")]
|
||||||
s_b: cross_curve_dleq::Scalar,
|
s_b: cross_curve_dleq::Scalar,
|
||||||
S_a_monero: monero::PublicKey,
|
S_a_monero: monero::PublicKey,
|
||||||
S_a_bitcoin: bitcoin::PublicKey,
|
S_a_bitcoin: bitcoin::PublicKey,
|
||||||
v: monero::PrivateViewKey,
|
v: monero::PrivateViewKey,
|
||||||
|
#[serde(with = "bitcoin_amount")]
|
||||||
btc: bitcoin::Amount,
|
btc: bitcoin::Amount,
|
||||||
xmr: monero::Amount,
|
xmr: monero::Amount,
|
||||||
refund_timelock: u32,
|
refund_timelock: u32,
|
||||||
@ -253,14 +259,16 @@ impl State1 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct State2 {
|
pub struct State2 {
|
||||||
A: bitcoin::PublicKey,
|
A: bitcoin::PublicKey,
|
||||||
b: bitcoin::SecretKey,
|
b: bitcoin::SecretKey,
|
||||||
|
#[serde(with = "cross_curve_dleq_scalar")]
|
||||||
s_b: cross_curve_dleq::Scalar,
|
s_b: cross_curve_dleq::Scalar,
|
||||||
S_a_monero: monero::PublicKey,
|
S_a_monero: monero::PublicKey,
|
||||||
S_a_bitcoin: bitcoin::PublicKey,
|
S_a_bitcoin: bitcoin::PublicKey,
|
||||||
v: monero::PrivateViewKey,
|
v: monero::PrivateViewKey,
|
||||||
|
#[serde(with = "bitcoin_amount")]
|
||||||
btc: bitcoin::Amount,
|
btc: bitcoin::Amount,
|
||||||
xmr: monero::Amount,
|
xmr: monero::Amount,
|
||||||
refund_timelock: u32,
|
refund_timelock: u32,
|
||||||
@ -324,14 +332,16 @@ impl State2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct State3 {
|
pub struct State3 {
|
||||||
A: bitcoin::PublicKey,
|
A: bitcoin::PublicKey,
|
||||||
b: bitcoin::SecretKey,
|
b: bitcoin::SecretKey,
|
||||||
|
#[serde(with = "cross_curve_dleq_scalar")]
|
||||||
s_b: cross_curve_dleq::Scalar,
|
s_b: cross_curve_dleq::Scalar,
|
||||||
S_a_monero: monero::PublicKey,
|
S_a_monero: monero::PublicKey,
|
||||||
S_a_bitcoin: bitcoin::PublicKey,
|
S_a_bitcoin: bitcoin::PublicKey,
|
||||||
v: monero::PrivateViewKey,
|
v: monero::PrivateViewKey,
|
||||||
|
#[serde(with = "bitcoin_amount")]
|
||||||
btc: bitcoin::Amount,
|
btc: bitcoin::Amount,
|
||||||
xmr: monero::Amount,
|
xmr: monero::Amount,
|
||||||
refund_timelock: u32,
|
refund_timelock: u32,
|
||||||
@ -429,14 +439,16 @@ impl State3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct State4 {
|
pub struct State4 {
|
||||||
A: bitcoin::PublicKey,
|
A: bitcoin::PublicKey,
|
||||||
b: bitcoin::SecretKey,
|
b: bitcoin::SecretKey,
|
||||||
|
#[serde(with = "cross_curve_dleq_scalar")]
|
||||||
s_b: cross_curve_dleq::Scalar,
|
s_b: cross_curve_dleq::Scalar,
|
||||||
S_a_monero: monero::PublicKey,
|
S_a_monero: monero::PublicKey,
|
||||||
S_a_bitcoin: bitcoin::PublicKey,
|
S_a_bitcoin: bitcoin::PublicKey,
|
||||||
v: monero::PrivateViewKey,
|
v: monero::PrivateViewKey,
|
||||||
|
#[serde(with = "bitcoin_amount")]
|
||||||
btc: bitcoin::Amount,
|
btc: bitcoin::Amount,
|
||||||
xmr: monero::Amount,
|
xmr: monero::Amount,
|
||||||
refund_timelock: u32,
|
refund_timelock: u32,
|
||||||
@ -496,15 +508,18 @@ impl State4 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct State5 {
|
pub struct State5 {
|
||||||
A: bitcoin::PublicKey,
|
A: bitcoin::PublicKey,
|
||||||
b: bitcoin::SecretKey,
|
b: bitcoin::SecretKey,
|
||||||
|
#[serde(with = "monero_private_key")]
|
||||||
s_a: monero::PrivateKey,
|
s_a: monero::PrivateKey,
|
||||||
|
#[serde(with = "cross_curve_dleq_scalar")]
|
||||||
s_b: cross_curve_dleq::Scalar,
|
s_b: cross_curve_dleq::Scalar,
|
||||||
S_a_monero: monero::PublicKey,
|
S_a_monero: monero::PublicKey,
|
||||||
S_a_bitcoin: bitcoin::PublicKey,
|
S_a_bitcoin: bitcoin::PublicKey,
|
||||||
v: monero::PrivateViewKey,
|
v: monero::PrivateViewKey,
|
||||||
|
#[serde(with = "bitcoin_amount")]
|
||||||
btc: bitcoin::Amount,
|
btc: bitcoin::Amount,
|
||||||
xmr: monero::Amount,
|
xmr: monero::Amount,
|
||||||
refund_timelock: u32,
|
refund_timelock: u32,
|
||||||
|
@ -49,4 +49,5 @@ pub mod alice;
|
|||||||
pub mod bitcoin;
|
pub mod bitcoin;
|
||||||
pub mod bob;
|
pub mod bob;
|
||||||
pub mod monero;
|
pub mod monero;
|
||||||
|
pub mod serde;
|
||||||
pub mod transport;
|
pub mod transport;
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
use crate::serde::monero_private_key;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
pub use curve25519_dalek::scalar::Scalar;
|
pub use curve25519_dalek::scalar::Scalar;
|
||||||
pub use monero::{Address, PrivateKey, PublicKey};
|
pub use monero::{Address, PrivateKey, PublicKey};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
|
|
||||||
pub fn random_private_key<R: RngCore + CryptoRng>(rng: &mut R) -> PrivateKey {
|
pub fn random_private_key<R: RngCore + CryptoRng>(rng: &mut R) -> PrivateKey {
|
||||||
@ -11,8 +13,8 @@ pub fn random_private_key<R: RngCore + CryptoRng>(rng: &mut R) -> PrivateKey {
|
|||||||
PrivateKey::from_scalar(scalar)
|
PrivateKey::from_scalar(scalar)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct PrivateViewKey(PrivateKey);
|
pub struct PrivateViewKey(#[serde(with = "monero_private_key")] PrivateKey);
|
||||||
|
|
||||||
impl PrivateViewKey {
|
impl PrivateViewKey {
|
||||||
pub fn new_random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
pub fn new_random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
||||||
@ -50,7 +52,7 @@ impl From<PublicViewKey> for PublicKey {
|
|||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct PublicViewKey(PublicKey);
|
pub struct PublicViewKey(PublicKey);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone, Deserialize, Serialize, PartialEq)]
|
||||||
pub struct Amount(u64);
|
pub struct Amount(u64);
|
||||||
|
|
||||||
impl Amount {
|
impl Amount {
|
||||||
@ -72,9 +74,10 @@ impl From<Amount> for u64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct TransferProof {
|
pub struct TransferProof {
|
||||||
tx_hash: TxHash,
|
tx_hash: TxHash,
|
||||||
|
#[serde(with = "monero_private_key")]
|
||||||
tx_key: PrivateKey,
|
tx_key: PrivateKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +94,7 @@ impl TransferProof {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add constructor/ change String to fixed length byte array
|
// TODO: add constructor/ change String to fixed length byte array
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct TxHash(pub String);
|
pub struct TxHash(pub String);
|
||||||
|
|
||||||
impl From<TxHash> for String {
|
impl From<TxHash> for String {
|
||||||
|
210
xmr-btc/src/serde.rs
Normal file
210
xmr-btc/src/serde.rs
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
pub mod ecdsa_fun_signature {
|
||||||
|
use serde::{de, de::Visitor, Deserializer, Serializer};
|
||||||
|
use std::{convert::TryFrom, fmt};
|
||||||
|
|
||||||
|
struct Bytes64Visitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for Bytes64Visitor {
|
||||||
|
type Value = ecdsa_fun::Signature;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(formatter, "a string containing 64 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bytes<E>(self, s: &[u8]) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
if let Ok(value) = <[u8; 64]>::try_from(s) {
|
||||||
|
let sig = ecdsa_fun::Signature::from_bytes(value)
|
||||||
|
.expect("bytes represent an integer greater than or equal to the curve order");
|
||||||
|
Ok(sig)
|
||||||
|
} else {
|
||||||
|
Err(de::Error::invalid_length(s.len(), &self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize<S>(x: &ecdsa_fun::Signature, s: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
s.serialize_bytes(&x.to_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D>(
|
||||||
|
deserializer: D,
|
||||||
|
) -> Result<ecdsa_fun::Signature, <D as Deserializer<'de>>::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let sig = deserializer.deserialize_bytes(Bytes64Visitor)?;
|
||||||
|
Ok(sig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod cross_curve_dleq_scalar {
|
||||||
|
use serde::{de, de::Visitor, Deserializer, Serializer};
|
||||||
|
use std::{convert::TryFrom, fmt};
|
||||||
|
|
||||||
|
struct Bytes32Visitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for Bytes32Visitor {
|
||||||
|
type Value = cross_curve_dleq::Scalar;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(formatter, "a string containing 32 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bytes<E>(self, s: &[u8]) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
if let Ok(value) = <[u8; 32]>::try_from(s) {
|
||||||
|
Ok(cross_curve_dleq::Scalar::from(value))
|
||||||
|
} else {
|
||||||
|
Err(de::Error::invalid_length(s.len(), &self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize<S>(x: &cross_curve_dleq::Scalar, s: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
// Serialise as ed25519 because the inner bytes are private
|
||||||
|
// TODO: Open PR in cross_curve_dleq to allow accessing the inner bytes
|
||||||
|
s.serialize_bytes(&x.into_ed25519().to_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D>(
|
||||||
|
deserializer: D,
|
||||||
|
) -> Result<cross_curve_dleq::Scalar, <D as Deserializer<'de>>::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let dleq = deserializer.deserialize_bytes(Bytes32Visitor)?;
|
||||||
|
Ok(dleq)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod monero_private_key {
|
||||||
|
use serde::{de, de::Visitor, Deserializer, Serializer};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
struct BytesVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for BytesVisitor {
|
||||||
|
type Value = monero::PrivateKey;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(formatter, "a string containing 32 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bytes<E>(self, s: &[u8]) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
if let Ok(key) = monero::PrivateKey::from_slice(s) {
|
||||||
|
Ok(key)
|
||||||
|
} else {
|
||||||
|
Err(de::Error::invalid_length(s.len(), &self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize<S>(x: &monero::PrivateKey, s: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
s.serialize_bytes(x.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D>(
|
||||||
|
deserializer: D,
|
||||||
|
) -> Result<monero::PrivateKey, <D as Deserializer<'de>>::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let key = deserializer.deserialize_bytes(BytesVisitor)?;
|
||||||
|
Ok(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod bitcoin_amount {
|
||||||
|
use serde::{Deserialize, Deserializer, Serializer};
|
||||||
|
|
||||||
|
pub fn serialize<S>(value: &bitcoin::Amount, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_u64(value.as_sat())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D>(
|
||||||
|
deserializer: D,
|
||||||
|
) -> Result<bitcoin::Amount, <D as Deserializer<'de>>::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let value = u64::deserialize(deserializer)?;
|
||||||
|
let amount = bitcoin::Amount::from_sat(value);
|
||||||
|
|
||||||
|
Ok(amount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use ::bitcoin::SigHash;
|
||||||
|
use curve25519_dalek::scalar::Scalar;
|
||||||
|
use rand::rngs::OsRng;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct CrossCurveDleqScalar(
|
||||||
|
#[serde(with = "cross_curve_dleq_scalar")] cross_curve_dleq::Scalar,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct ECDSAFunSignature(#[serde(with = "ecdsa_fun_signature")] ecdsa_fun::Signature);
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct MoneroPrivateKey(#[serde(with = "monero_private_key")] crate::monero::PrivateKey);
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct BitcoinAmount(#[serde(with = "bitcoin_amount")] ::bitcoin::Amount);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serde_cross_curv_dleq_scalar() {
|
||||||
|
let scalar = CrossCurveDleqScalar(cross_curve_dleq::Scalar::random(&mut OsRng));
|
||||||
|
let encoded = serde_cbor::to_vec(&scalar).unwrap();
|
||||||
|
let decoded: CrossCurveDleqScalar = serde_cbor::from_slice(&encoded).unwrap();
|
||||||
|
assert_eq!(scalar, decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serde_ecdsa_fun_sig() {
|
||||||
|
let secret_key = crate::bitcoin::SecretKey::new_random(&mut OsRng);
|
||||||
|
let sig = ECDSAFunSignature(secret_key.sign(SigHash::default()));
|
||||||
|
let encoded = serde_cbor::to_vec(&sig).unwrap();
|
||||||
|
let decoded: ECDSAFunSignature = serde_cbor::from_slice(&encoded).unwrap();
|
||||||
|
assert_eq!(sig, decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serde_monero_private_key() {
|
||||||
|
let key = MoneroPrivateKey(monero::PrivateKey::from_scalar(Scalar::random(&mut OsRng)));
|
||||||
|
let encoded = serde_cbor::to_vec(&key).unwrap();
|
||||||
|
let decoded: MoneroPrivateKey = serde_cbor::from_slice(&encoded).unwrap();
|
||||||
|
assert_eq!(key, decoded);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn serde_bitcoin_amount() {
|
||||||
|
let amount = BitcoinAmount(::bitcoin::Amount::from_sat(100));
|
||||||
|
let encoded = serde_cbor::to_vec(&amount).unwrap();
|
||||||
|
let decoded: BitcoinAmount = serde_cbor::from_slice(&encoded).unwrap();
|
||||||
|
assert_eq!(amount, decoded);
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,8 @@ mod harness;
|
|||||||
const TEN_XMR: u64 = 10_000_000_000_000;
|
const TEN_XMR: u64 = 10_000_000_000_000;
|
||||||
const RELATIVE_REFUND_TIMELOCK: u32 = 1;
|
const RELATIVE_REFUND_TIMELOCK: u32 = 1;
|
||||||
const RELATIVE_PUNISH_TIMELOCK: u32 = 1;
|
const RELATIVE_PUNISH_TIMELOCK: u32 = 1;
|
||||||
|
const ALICE_TEST_DB_FOLDER: &str = "../target/e2e-test-alice-recover";
|
||||||
|
const BOB_TEST_DB_FOLDER: &str = "../target/e2e-test-bob-recover";
|
||||||
|
|
||||||
pub async fn init_bitcoind(tc_client: &Cli) -> Bitcoind<'_> {
|
pub async fn init_bitcoind(tc_client: &Cli) -> Bitcoind<'_> {
|
||||||
let bitcoind = Bitcoind::new(tc_client, "0.19.1").expect("failed to create bitcoind");
|
let bitcoind = Bitcoind::new(tc_client, "0.19.1").expect("failed to create bitcoind");
|
||||||
@ -61,7 +63,7 @@ pub fn init_alice_and_bob_transports() -> (
|
|||||||
|
|
||||||
pub async fn init_test<'a>(
|
pub async fn init_test<'a>(
|
||||||
monero: &'a Monero<'a>,
|
monero: &'a Monero<'a>,
|
||||||
bitcoind: &Bitcoind<'_>,
|
bitcoind: &Bitcoind<'a>,
|
||||||
) -> (
|
) -> (
|
||||||
alice::State0,
|
alice::State0,
|
||||||
bob::State0,
|
bob::State0,
|
||||||
@ -150,12 +152,13 @@ mod tests {
|
|||||||
use crate::{
|
use crate::{
|
||||||
harness,
|
harness,
|
||||||
harness::node::{run_alice_until, run_bob_until},
|
harness::node::{run_alice_until, run_bob_until},
|
||||||
init_bitcoind, init_test,
|
init_bitcoind, init_test, ALICE_TEST_DB_FOLDER, BOB_TEST_DB_FOLDER,
|
||||||
};
|
};
|
||||||
use futures::future;
|
use futures::future;
|
||||||
use monero_harness::Monero;
|
use monero_harness::Monero;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use std::convert::TryInto;
|
|
||||||
|
use std::{convert::TryInto, path::Path};
|
||||||
use testcontainers::clients::Cli;
|
use testcontainers::clients::Cli;
|
||||||
use tracing_subscriber::util::SubscriberInitExt;
|
use tracing_subscriber::util::SubscriberInitExt;
|
||||||
use xmr_btc::{
|
use xmr_btc::{
|
||||||
@ -400,4 +403,126 @@ mod tests {
|
|||||||
initial_balances.bob_btc - swap_amounts.btc - lock_tx_bitcoin_fee
|
initial_balances.bob_btc - swap_amounts.btc - lock_tx_bitcoin_fee
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn recover_protocol_state_from_db() {
|
||||||
|
let _guard = tracing_subscriber::fmt()
|
||||||
|
.with_env_filter("info")
|
||||||
|
.set_default();
|
||||||
|
|
||||||
|
let cli = Cli::default();
|
||||||
|
let monero = Monero::new(&cli);
|
||||||
|
let bitcoind = init_bitcoind(&cli).await;
|
||||||
|
let alice_db = harness::storage::Database::open(Path::new(ALICE_TEST_DB_FOLDER)).unwrap();
|
||||||
|
let bob_db = harness::storage::Database::open(Path::new(BOB_TEST_DB_FOLDER)).unwrap();
|
||||||
|
|
||||||
|
let (
|
||||||
|
alice_state0,
|
||||||
|
bob_state0,
|
||||||
|
mut alice_node,
|
||||||
|
mut bob_node,
|
||||||
|
initial_balances,
|
||||||
|
swap_amounts,
|
||||||
|
) = init_test(&monero, &bitcoind).await;
|
||||||
|
|
||||||
|
{
|
||||||
|
let (alice_state, bob_state) = future::try_join(
|
||||||
|
run_alice_until(
|
||||||
|
&mut alice_node,
|
||||||
|
alice_state0.into(),
|
||||||
|
harness::alice::is_state5,
|
||||||
|
&mut OsRng,
|
||||||
|
),
|
||||||
|
run_bob_until(
|
||||||
|
&mut bob_node,
|
||||||
|
bob_state0.into(),
|
||||||
|
harness::bob::is_state3,
|
||||||
|
&mut OsRng,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let alice_state5: alice::State5 = alice_state.try_into().unwrap();
|
||||||
|
let bob_state3: bob::State3 = bob_state.try_into().unwrap();
|
||||||
|
|
||||||
|
// save state to db
|
||||||
|
alice_db.insert_latest_state(&alice_state5).await.unwrap();
|
||||||
|
bob_db.insert_latest_state(&bob_state3).await.unwrap();
|
||||||
|
};
|
||||||
|
|
||||||
|
let (alice_state6, bob_state5) = {
|
||||||
|
// recover state from db
|
||||||
|
let alice_state5: alice::State5 = alice_db.get_latest_state().unwrap();
|
||||||
|
let bob_state3: bob::State3 = bob_db.get_latest_state().unwrap();
|
||||||
|
|
||||||
|
let (alice_state, bob_state) = future::try_join(
|
||||||
|
run_alice_until(
|
||||||
|
&mut alice_node,
|
||||||
|
alice_state5.into(),
|
||||||
|
harness::alice::is_state6,
|
||||||
|
&mut OsRng,
|
||||||
|
),
|
||||||
|
run_bob_until(
|
||||||
|
&mut bob_node,
|
||||||
|
bob_state3.into(),
|
||||||
|
harness::bob::is_state5,
|
||||||
|
&mut OsRng,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let alice_state: alice::State6 = alice_state.try_into().unwrap();
|
||||||
|
let bob_state: bob::State5 = bob_state.try_into().unwrap();
|
||||||
|
|
||||||
|
(alice_state, bob_state)
|
||||||
|
};
|
||||||
|
|
||||||
|
let alice_final_btc_balance = alice_node.bitcoin_wallet.balance().await.unwrap();
|
||||||
|
let bob_final_btc_balance = bob_node.bitcoin_wallet.balance().await.unwrap();
|
||||||
|
|
||||||
|
let lock_tx_bitcoin_fee = bob_node
|
||||||
|
.bitcoin_wallet
|
||||||
|
.transaction_fee(bob_state5.tx_lock_id())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let alice_final_xmr_balance = alice_node
|
||||||
|
.monero_wallet
|
||||||
|
.0
|
||||||
|
.get_balance_alice()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
bob_node
|
||||||
|
.monero_wallet
|
||||||
|
.0
|
||||||
|
.wait_for_bob_wallet_block_height()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let bob_final_xmr_balance = bob_node.monero_wallet.0.get_balance_bob().await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
alice_final_btc_balance,
|
||||||
|
initial_balances.alice_btc + swap_amounts.btc
|
||||||
|
- bitcoin::Amount::from_sat(bitcoin::TX_FEE)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
bob_final_btc_balance,
|
||||||
|
initial_balances.bob_btc - swap_amounts.btc - lock_tx_bitcoin_fee
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
alice_final_xmr_balance,
|
||||||
|
initial_balances.alice_xmr
|
||||||
|
- u64::from(swap_amounts.xmr)
|
||||||
|
- u64::from(alice_state6.lock_xmr_fee())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
bob_final_xmr_balance,
|
||||||
|
initial_balances.bob_xmr + u64::from(swap_amounts.xmr)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
pub mod node;
|
pub mod node;
|
||||||
|
pub mod storage;
|
||||||
pub mod transport;
|
pub mod transport;
|
||||||
pub mod wallet;
|
pub mod wallet;
|
||||||
|
|
||||||
|
159
xmr-btc/tests/harness/storage.rs
Normal file
159
xmr-btc/tests/harness/storage.rs
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub struct Database {
|
||||||
|
db: sled::Db,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Database {
|
||||||
|
const LAST_STATE_KEY: &'static str = "latest_state";
|
||||||
|
|
||||||
|
pub fn open(path: &Path) -> Result<Self> {
|
||||||
|
let path = path
|
||||||
|
.to_str()
|
||||||
|
.ok_or_else(|| anyhow!("The path is not utf-8 valid: {:?}", path))?;
|
||||||
|
let db = sled::open(path).with_context(|| format!("Could not open the DB at {}", path))?;
|
||||||
|
|
||||||
|
Ok(Database { db })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn insert_latest_state<T>(&self, state: &T) -> Result<()>
|
||||||
|
where
|
||||||
|
T: Serialize + DeserializeOwned,
|
||||||
|
{
|
||||||
|
let key = serialize(&Self::LAST_STATE_KEY)?;
|
||||||
|
let new_value = serialize(&state).context("Could not serialize new state value")?;
|
||||||
|
|
||||||
|
let old_value = self.db.get(&key)?;
|
||||||
|
|
||||||
|
self.db
|
||||||
|
.compare_and_swap(key, old_value, Some(new_value))
|
||||||
|
.context("Could not write in the DB")?
|
||||||
|
.context("Stored swap somehow changed, aborting saving")?; // let _ =
|
||||||
|
|
||||||
|
self.db
|
||||||
|
.flush_async()
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
.context("Could not flush db")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_latest_state<T>(&self) -> anyhow::Result<T>
|
||||||
|
where
|
||||||
|
T: DeserializeOwned,
|
||||||
|
{
|
||||||
|
let key = serialize(&Self::LAST_STATE_KEY)?;
|
||||||
|
|
||||||
|
let encoded = self
|
||||||
|
.db
|
||||||
|
.get(&key)?
|
||||||
|
.ok_or_else(|| anyhow!("State does not exist {:?}", key))?;
|
||||||
|
|
||||||
|
let state = deserialize(&encoded).context("Could not deserialize state")?;
|
||||||
|
Ok(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize<T>(t: &T) -> anyhow::Result<Vec<u8>>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
Ok(serde_cbor::to_vec(t)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<T>(v: &[u8]) -> anyhow::Result<T>
|
||||||
|
where
|
||||||
|
T: DeserializeOwned,
|
||||||
|
{
|
||||||
|
Ok(serde_cbor::from_slice(&v)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
use super::*;
|
||||||
|
use bitcoin::SigHash;
|
||||||
|
use curve25519_dalek::scalar::Scalar;
|
||||||
|
use ecdsa_fun::fun::rand_core::OsRng;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use xmr_btc::serde::{
|
||||||
|
bitcoin_amount, cross_curve_dleq_scalar, ecdsa_fun_signature, monero_private_key,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct TestState {
|
||||||
|
A: xmr_btc::bitcoin::PublicKey,
|
||||||
|
a: xmr_btc::bitcoin::SecretKey,
|
||||||
|
#[serde(with = "cross_curve_dleq_scalar")]
|
||||||
|
s_a: ::cross_curve_dleq::Scalar,
|
||||||
|
#[serde(with = "monero_private_key")]
|
||||||
|
s_b: monero::PrivateKey,
|
||||||
|
S_a_monero: ::monero::PublicKey,
|
||||||
|
S_a_bitcoin: xmr_btc::bitcoin::PublicKey,
|
||||||
|
v: xmr_btc::monero::PrivateViewKey,
|
||||||
|
#[serde(with = "bitcoin_amount")]
|
||||||
|
btc: ::bitcoin::Amount,
|
||||||
|
xmr: xmr_btc::monero::Amount,
|
||||||
|
refund_timelock: u32,
|
||||||
|
refund_address: ::bitcoin::Address,
|
||||||
|
transaction: ::bitcoin::Transaction,
|
||||||
|
#[serde(with = "ecdsa_fun_signature")]
|
||||||
|
tx_punish_sig: xmr_btc::bitcoin::Signature,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn recover_state_from_db() {
|
||||||
|
let db = Database::open(Path::new("../target/test_recover.db")).unwrap();
|
||||||
|
|
||||||
|
let a = crate::bitcoin::SecretKey::new_random(&mut OsRng);
|
||||||
|
let s_a = cross_curve_dleq::Scalar::random(&mut OsRng);
|
||||||
|
let s_b = monero::PrivateKey::from_scalar(Scalar::random(&mut OsRng));
|
||||||
|
let v_a = xmr_btc::monero::PrivateViewKey::new_random(&mut OsRng);
|
||||||
|
let S_a_monero = monero::PublicKey::from_private_key(&monero::PrivateKey {
|
||||||
|
scalar: s_a.into_ed25519(),
|
||||||
|
});
|
||||||
|
let S_a_bitcoin = s_a.into_secp256k1().into();
|
||||||
|
let tx_punish_sig = a.sign(SigHash::default());
|
||||||
|
|
||||||
|
let state = TestState {
|
||||||
|
A: a.public(),
|
||||||
|
a,
|
||||||
|
s_b,
|
||||||
|
s_a,
|
||||||
|
S_a_monero,
|
||||||
|
S_a_bitcoin,
|
||||||
|
v: v_a,
|
||||||
|
btc: ::bitcoin::Amount::from_sat(100),
|
||||||
|
xmr: crate::monero::Amount::from_piconero(1000),
|
||||||
|
refund_timelock: 0,
|
||||||
|
refund_address: ::bitcoin::Address::from_str("1L5wSMgerhHg8GZGcsNmAx5EXMRXSKR3He")
|
||||||
|
.unwrap(),
|
||||||
|
transaction: ::bitcoin::Transaction {
|
||||||
|
version: 0,
|
||||||
|
lock_time: 0,
|
||||||
|
input: vec![::bitcoin::TxIn::default()],
|
||||||
|
output: vec![::bitcoin::TxOut::default()],
|
||||||
|
},
|
||||||
|
tx_punish_sig,
|
||||||
|
};
|
||||||
|
|
||||||
|
db.insert_latest_state(&state)
|
||||||
|
.await
|
||||||
|
.expect("Failed to save state the first time");
|
||||||
|
let recovered: TestState = db
|
||||||
|
.get_latest_state()
|
||||||
|
.expect("Failed to recover state the first time");
|
||||||
|
|
||||||
|
// We insert and recover twice to ensure database implementation allows the
|
||||||
|
// caller to write to an existing key
|
||||||
|
db.insert_latest_state(&recovered)
|
||||||
|
.await
|
||||||
|
.expect("Failed to save state the second time");
|
||||||
|
let recovered: TestState = db
|
||||||
|
.get_latest_state()
|
||||||
|
.expect("Failed to recover state the second time");
|
||||||
|
|
||||||
|
assert_eq!(state, recovered);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user