mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-23 07:35:03 +01:00
fix(NUT10): secret tag is optional
This commit is contained in:
@@ -39,10 +39,13 @@ impl Deref for JsP2PKSpendingConditions {
|
|||||||
#[wasm_bindgen(js_class = P2PKSpendingConditions)]
|
#[wasm_bindgen(js_class = P2PKSpendingConditions)]
|
||||||
impl JsP2PKSpendingConditions {
|
impl JsP2PKSpendingConditions {
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
pub fn new(pubkey: String, conditions: JsConditions) -> Result<JsP2PKSpendingConditions> {
|
pub fn new(
|
||||||
|
pubkey: String,
|
||||||
|
conditions: Option<JsConditions>,
|
||||||
|
) -> Result<JsP2PKSpendingConditions> {
|
||||||
let pubkey = PublicKey::from_str(&pubkey).map_err(into_err)?;
|
let pubkey = PublicKey::from_str(&pubkey).map_err(into_err)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
inner: SpendingConditions::new_p2pk(pubkey, conditions.deref().clone()),
|
inner: SpendingConditions::new_p2pk(pubkey, conditions.map(|c| c.deref().clone())),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,9 +39,12 @@ impl Deref for JsHTLCSpendingConditions {
|
|||||||
#[wasm_bindgen(js_class = HTLCSpendingConditions)]
|
#[wasm_bindgen(js_class = HTLCSpendingConditions)]
|
||||||
impl JsHTLCSpendingConditions {
|
impl JsHTLCSpendingConditions {
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
pub fn new(preimage: String, conditions: JsConditions) -> Result<JsHTLCSpendingConditions> {
|
pub fn new(
|
||||||
|
preimage: String,
|
||||||
|
conditions: Option<JsConditions>,
|
||||||
|
) -> Result<JsHTLCSpendingConditions> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
inner: SpendingConditions::new_htlc(preimage, conditions.deref().clone())
|
inner: SpendingConditions::new_htlc(preimage, conditions.map(|c| c.deref().clone()))
|
||||||
.map_err(into_err)?,
|
.map_err(into_err)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,10 @@ pub async fn send(wallet: Wallet, sub_command_args: &SendSubCommand) -> Result<(
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Some(SpendingConditions::new_htlc(preimage.clone(), conditions)?)
|
Some(SpendingConditions::new_htlc(
|
||||||
|
preimage.clone(),
|
||||||
|
Some(conditions),
|
||||||
|
)?)
|
||||||
}
|
}
|
||||||
None => match sub_command_args.pubkey.is_empty() {
|
None => match sub_command_args.pubkey.is_empty() {
|
||||||
true => None,
|
true => None,
|
||||||
@@ -136,7 +139,7 @@ pub async fn send(wallet: Wallet, sub_command_args: &SendSubCommand) -> Result<(
|
|||||||
|
|
||||||
Some(SpendingConditions::P2PKConditions {
|
Some(SpendingConditions::P2PKConditions {
|
||||||
data: data_pubkey,
|
data: data_pubkey,
|
||||||
conditions,
|
conditions: Some(conditions),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::time::Duration;
|
|||||||
use cdk::amount::SplitTarget;
|
use cdk::amount::SplitTarget;
|
||||||
use cdk::cdk_database::WalletMemoryDatabase;
|
use cdk::cdk_database::WalletMemoryDatabase;
|
||||||
use cdk::error::Error;
|
use cdk::error::Error;
|
||||||
use cdk::nuts::{Conditions, CurrencyUnit, SecretKey, SpendingConditions};
|
use cdk::nuts::{CurrencyUnit, SecretKey, SpendingConditions};
|
||||||
use cdk::wallet::Wallet;
|
use cdk::wallet::Wallet;
|
||||||
use cdk::{Amount, UncheckedUrl};
|
use cdk::{Amount, UncheckedUrl};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@@ -51,8 +51,7 @@ async fn main() -> Result<(), Error> {
|
|||||||
|
|
||||||
let secret = SecretKey::generate();
|
let secret = SecretKey::generate();
|
||||||
|
|
||||||
let spending_conditions =
|
let spending_conditions = SpendingConditions::new_p2pk(secret.public_key(), None);
|
||||||
SpendingConditions::new_p2pk(secret.public_key(), Conditions::default());
|
|
||||||
|
|
||||||
let token = wallet
|
let token = wallet
|
||||||
.send(
|
.send(
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ pub struct SecretData {
|
|||||||
/// Expresses the spending condition specific to each kind
|
/// Expresses the spending condition specific to each kind
|
||||||
pub data: String,
|
pub data: String,
|
||||||
/// Additional data committed to and can be used for feature extensions
|
/// Additional data committed to and can be used for feature extensions
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub tags: Vec<Vec<String>>,
|
pub tags: Option<Vec<Vec<String>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize)]
|
||||||
@@ -38,16 +38,17 @@ pub struct Secret {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Secret {
|
impl Secret {
|
||||||
pub fn new<S, V>(kind: Kind, data: S, tags: V) -> Self
|
pub fn new<S, V>(kind: Kind, data: S, tags: Option<V>) -> Self
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
V: Into<Vec<Vec<String>>>,
|
V: Into<Vec<Vec<String>>>,
|
||||||
{
|
{
|
||||||
let nonce = crate::secret::Secret::generate().to_string();
|
let nonce = crate::secret::Secret::generate().to_string();
|
||||||
|
|
||||||
let secret_data = SecretData {
|
let secret_data = SecretData {
|
||||||
nonce,
|
nonce,
|
||||||
data: data.into(),
|
data: data.into(),
|
||||||
tags: tags.into(),
|
tags: tags.map(|v| v.into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
Self { kind, secret_data }
|
Self { kind, secret_data }
|
||||||
@@ -94,11 +95,11 @@ mod tests {
|
|||||||
nonce: "5d11913ee0f92fefdc82a6764fd2457a".to_string(),
|
nonce: "5d11913ee0f92fefdc82a6764fd2457a".to_string(),
|
||||||
data: "026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198"
|
data: "026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
tags: vec![vec![
|
tags: Some(vec![vec![
|
||||||
"key".to_string(),
|
"key".to_string(),
|
||||||
"value1".to_string(),
|
"value1".to_string(),
|
||||||
"value2".to_string(),
|
"value2".to_string(),
|
||||||
]],
|
]]),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,8 @@ impl Proof {
|
|||||||
/// Verify P2PK signature on [Proof]
|
/// Verify P2PK signature on [Proof]
|
||||||
pub fn verify_p2pk(&self) -> Result<(), Error> {
|
pub fn verify_p2pk(&self) -> Result<(), Error> {
|
||||||
let secret: Nut10Secret = self.secret.clone().try_into()?;
|
let secret: Nut10Secret = self.secret.clone().try_into()?;
|
||||||
let spending_conditions: Conditions = secret.secret_data.tags.try_into()?;
|
let spending_conditions: Conditions =
|
||||||
|
secret.secret_data.tags.unwrap_or_default().try_into()?;
|
||||||
let msg: &[u8] = self.secret.as_bytes();
|
let msg: &[u8] = self.secret.as_bytes();
|
||||||
|
|
||||||
let mut valid_sigs = 0;
|
let mut valid_sigs = 0;
|
||||||
@@ -254,18 +255,18 @@ pub enum SpendingConditions {
|
|||||||
/// NUT11 Spending conditions
|
/// NUT11 Spending conditions
|
||||||
P2PKConditions {
|
P2PKConditions {
|
||||||
data: PublicKey,
|
data: PublicKey,
|
||||||
conditions: Conditions,
|
conditions: Option<Conditions>,
|
||||||
},
|
},
|
||||||
/// NUT14 Spending conditions
|
/// NUT14 Spending conditions
|
||||||
HTLCConditions {
|
HTLCConditions {
|
||||||
data: Sha256Hash,
|
data: Sha256Hash,
|
||||||
conditions: Conditions,
|
conditions: Option<Conditions>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpendingConditions {
|
impl SpendingConditions {
|
||||||
/// New HTLC [SpendingConditions]
|
/// New HTLC [SpendingConditions]
|
||||||
pub fn new_htlc(preimage: String, conditions: Conditions) -> Result<Self, Error> {
|
pub fn new_htlc(preimage: String, conditions: Option<Conditions>) -> Result<Self, Error> {
|
||||||
let htlc = Sha256Hash::hash(&hex::decode(preimage)?);
|
let htlc = Sha256Hash::hash(&hex::decode(preimage)?);
|
||||||
|
|
||||||
Ok(Self::HTLCConditions {
|
Ok(Self::HTLCConditions {
|
||||||
@@ -275,7 +276,7 @@ impl SpendingConditions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// New P2PK [SpendingConditions]
|
/// New P2PK [SpendingConditions]
|
||||||
pub fn new_p2pk(pubkey: PublicKey, conditions: Conditions) -> Self {
|
pub fn new_p2pk(pubkey: PublicKey, conditions: Option<Conditions>) -> Self {
|
||||||
Self::P2PKConditions {
|
Self::P2PKConditions {
|
||||||
data: pubkey,
|
data: pubkey,
|
||||||
conditions,
|
conditions,
|
||||||
@@ -292,8 +293,8 @@ impl SpendingConditions {
|
|||||||
|
|
||||||
pub fn num_sigs(&self) -> Option<u64> {
|
pub fn num_sigs(&self) -> Option<u64> {
|
||||||
match self {
|
match self {
|
||||||
Self::P2PKConditions { conditions, .. } => conditions.num_sigs,
|
Self::P2PKConditions { conditions, .. } => conditions.as_ref().and_then(|c| c.num_sigs),
|
||||||
Self::HTLCConditions { conditions, .. } => conditions.num_sigs,
|
Self::HTLCConditions { conditions, .. } => conditions.as_ref().and_then(|c| c.num_sigs),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,25 +302,31 @@ impl SpendingConditions {
|
|||||||
match self {
|
match self {
|
||||||
Self::P2PKConditions { data, conditions } => {
|
Self::P2PKConditions { data, conditions } => {
|
||||||
let mut pubkeys = vec![*data];
|
let mut pubkeys = vec![*data];
|
||||||
|
if let Some(conditions) = conditions {
|
||||||
pubkeys.extend(conditions.pubkeys.clone().unwrap_or_default());
|
pubkeys.extend(conditions.pubkeys.clone().unwrap_or_default());
|
||||||
|
}
|
||||||
|
|
||||||
Some(pubkeys)
|
Some(pubkeys)
|
||||||
}
|
}
|
||||||
Self::HTLCConditions { conditions, .. } => conditions.pubkeys.clone(),
|
Self::HTLCConditions { conditions, .. } => conditions.clone().and_then(|c| c.pubkeys),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn locktime(&self) -> Option<u64> {
|
pub fn locktime(&self) -> Option<u64> {
|
||||||
match self {
|
match self {
|
||||||
Self::P2PKConditions { conditions, .. } => conditions.locktime,
|
Self::P2PKConditions { conditions, .. } => conditions.as_ref().and_then(|c| c.locktime),
|
||||||
Self::HTLCConditions { conditions, .. } => conditions.locktime,
|
Self::HTLCConditions { conditions, .. } => conditions.as_ref().and_then(|c| c.locktime),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refund_keys(&self) -> &Option<Vec<PublicKey>> {
|
pub fn refund_keys(&self) -> Option<Vec<PublicKey>> {
|
||||||
match self {
|
match self {
|
||||||
Self::P2PKConditions { conditions, .. } => &conditions.refund_keys,
|
Self::P2PKConditions { conditions, .. } => {
|
||||||
Self::HTLCConditions { conditions, .. } => &conditions.refund_keys,
|
conditions.clone().and_then(|c| c.refund_keys)
|
||||||
|
}
|
||||||
|
Self::HTLCConditions { conditions, .. } => {
|
||||||
|
conditions.clone().and_then(|c| c.refund_keys)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -339,12 +346,12 @@ impl TryFrom<Nut10Secret> for SpendingConditions {
|
|||||||
match secret.kind {
|
match secret.kind {
|
||||||
Kind::P2PK => Ok(SpendingConditions::P2PKConditions {
|
Kind::P2PK => Ok(SpendingConditions::P2PKConditions {
|
||||||
data: PublicKey::from_str(&secret.secret_data.data)?,
|
data: PublicKey::from_str(&secret.secret_data.data)?,
|
||||||
conditions: secret.secret_data.tags.try_into()?,
|
conditions: secret.secret_data.tags.and_then(|t| t.try_into().ok()),
|
||||||
}),
|
}),
|
||||||
Kind::HTLC => Ok(Self::HTLCConditions {
|
Kind::HTLC => Ok(Self::HTLCConditions {
|
||||||
data: Sha256Hash::from_str(&secret.secret_data.data)
|
data: Sha256Hash::from_str(&secret.secret_data.data)
|
||||||
.map_err(|_| Error::InvalidHash)?,
|
.map_err(|_| Error::InvalidHash)?,
|
||||||
conditions: secret.secret_data.tags.try_into()?,
|
conditions: secret.secret_data.tags.and_then(|t| t.try_into().ok()),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -578,7 +585,8 @@ pub fn enforce_sig_flag(proofs: Proofs) -> (SigFlag, HashSet<PublicKey>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(conditions) = Conditions::try_from(secret.secret_data.tags) {
|
if let Some(tags) = secret.secret_data.tags {
|
||||||
|
if let Ok(conditions) = Conditions::try_from(tags) {
|
||||||
if conditions.sig_flag.eq(&SigFlag::SigAll) {
|
if conditions.sig_flag.eq(&SigFlag::SigAll) {
|
||||||
sig_flag = SigFlag::SigAll;
|
sig_flag = SigFlag::SigAll;
|
||||||
}
|
}
|
||||||
@@ -589,6 +597,7 @@ pub fn enforce_sig_flag(proofs: Proofs) -> (SigFlag, HashSet<PublicKey>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(sig_flag, pubkeys)
|
(sig_flag, pubkeys)
|
||||||
}
|
}
|
||||||
@@ -744,7 +753,7 @@ mod tests {
|
|||||||
sig_flag: SigFlag::SigAll,
|
sig_flag: SigFlag::SigAll,
|
||||||
};
|
};
|
||||||
|
|
||||||
let secret: Nut10Secret = Nut10Secret::new(Kind::P2PK, data.to_string(), conditions);
|
let secret: Nut10Secret = Nut10Secret::new(Kind::P2PK, data.to_string(), Some(conditions));
|
||||||
|
|
||||||
let secret_str = serde_json::to_string(&secret).unwrap();
|
let secret_str = serde_json::to_string(&secret).unwrap();
|
||||||
|
|
||||||
@@ -778,7 +787,7 @@ mod tests {
|
|||||||
sig_flag: SigFlag::SigInputs,
|
sig_flag: SigFlag::SigInputs,
|
||||||
};
|
};
|
||||||
|
|
||||||
let secret: Secret = Nut10Secret::new(Kind::P2PK, v_key.to_string(), conditions)
|
let secret: Secret = Nut10Secret::new(Kind::P2PK, v_key.to_string(), Some(conditions))
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -56,8 +56,15 @@ impl Proof {
|
|||||||
/// Verify HTLC
|
/// Verify HTLC
|
||||||
pub fn verify_htlc(&self) -> Result<(), Error> {
|
pub fn verify_htlc(&self) -> Result<(), Error> {
|
||||||
let secret: Secret = self.secret.clone().try_into()?;
|
let secret: Secret = self.secret.clone().try_into()?;
|
||||||
let conditions: Conditions = secret.secret_data.tags.try_into()?;
|
let conditions: Option<Conditions> =
|
||||||
|
secret.secret_data.tags.and_then(|c| c.try_into().ok());
|
||||||
|
|
||||||
|
let htlc_witness = match &self.witness {
|
||||||
|
Some(Witness::HTLCWitness(witness)) => witness,
|
||||||
|
_ => return Err(Error::IncorrectSecretKind),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(conditions) = conditions {
|
||||||
// Check locktime
|
// Check locktime
|
||||||
if let Some(locktime) = conditions.locktime {
|
if let Some(locktime) = conditions.locktime {
|
||||||
// If locktime is in passed and no refund keys provided anyone can spend
|
// If locktime is in passed and no refund keys provided anyone can spend
|
||||||
@@ -66,7 +73,9 @@ impl Proof {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If refund keys are provided verify p2pk signatures
|
// If refund keys are provided verify p2pk signatures
|
||||||
if let (Some(refund_key), Some(signatures)) = (conditions.refund_keys, &self.witness) {
|
if let (Some(refund_key), Some(signatures)) =
|
||||||
|
(conditions.refund_keys, &self.witness)
|
||||||
|
{
|
||||||
let signatures: Vec<Signature> = signatures
|
let signatures: Vec<Signature> = signatures
|
||||||
.signatures()
|
.signatures()
|
||||||
.ok_or(Error::SignaturesNotProvided)?
|
.ok_or(Error::SignaturesNotProvided)?
|
||||||
@@ -80,28 +89,10 @@ impl Proof {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if secret.kind.ne(&super::Kind::HTLC) {
|
|
||||||
return Err(Error::IncorrectSecretKind);
|
|
||||||
}
|
|
||||||
|
|
||||||
let htlc_witness = match &self.witness {
|
|
||||||
Some(Witness::HTLCWitness(witness)) => witness,
|
|
||||||
_ => return Err(Error::IncorrectSecretKind),
|
|
||||||
};
|
|
||||||
|
|
||||||
let hash_lock =
|
|
||||||
Sha256Hash::from_str(&secret.secret_data.data).map_err(|_| Error::InvalidHash)?;
|
|
||||||
|
|
||||||
let preimage_hash = Sha256Hash::hash(htlc_witness.preimage.as_bytes());
|
|
||||||
|
|
||||||
if hash_lock.ne(&preimage_hash) {
|
|
||||||
return Err(Error::Preimage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If pubkeys are present check there is a valid signature
|
// If pubkeys are present check there is a valid signature
|
||||||
if let Some(pubkey) = conditions.pubkeys {
|
if let Some(pubkey) = conditions.pubkeys {
|
||||||
let req_sigs = conditions.num_sigs.unwrap_or(1);
|
let req_sigs = conditions.num_sigs.unwrap_or(1);
|
||||||
|
|
||||||
let signatures = htlc_witness
|
let signatures = htlc_witness
|
||||||
.signatures
|
.signatures
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -116,6 +107,20 @@ impl Proof {
|
|||||||
return Err(Error::IncorrectSecretKind);
|
return Err(Error::IncorrectSecretKind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if secret.kind.ne(&super::Kind::HTLC) {
|
||||||
|
return Err(Error::IncorrectSecretKind);
|
||||||
|
}
|
||||||
|
|
||||||
|
let hash_lock =
|
||||||
|
Sha256Hash::from_str(&secret.secret_data.data).map_err(|_| Error::InvalidHash)?;
|
||||||
|
|
||||||
|
let preimage_hash = Sha256Hash::hash(htlc_witness.preimage.as_bytes());
|
||||||
|
|
||||||
|
if hash_lock.ne(&preimage_hash) {
|
||||||
|
return Err(Error::Preimage);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1344,7 +1344,8 @@ impl Wallet {
|
|||||||
proof.secret.clone(),
|
proof.secret.clone(),
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
let conditions: Result<Conditions, _> = secret.secret_data.tags.try_into();
|
let conditions: Result<Conditions, _> =
|
||||||
|
secret.secret_data.tags.unwrap_or_default().try_into();
|
||||||
if let Ok(conditions) = conditions {
|
if let Ok(conditions) = conditions {
|
||||||
let mut pubkeys = conditions.pubkeys.unwrap_or_default();
|
let mut pubkeys = conditions.pubkeys.unwrap_or_default();
|
||||||
|
|
||||||
@@ -1548,6 +1549,8 @@ impl Wallet {
|
|||||||
SpendingConditions::P2PKConditions { data, conditions } => {
|
SpendingConditions::P2PKConditions { data, conditions } => {
|
||||||
let mut pubkeys = vec![data];
|
let mut pubkeys = vec![data];
|
||||||
|
|
||||||
|
match conditions {
|
||||||
|
Some(conditions) => {
|
||||||
pubkeys.extend(conditions.pubkeys.unwrap_or_default());
|
pubkeys.extend(conditions.pubkeys.unwrap_or_default());
|
||||||
|
|
||||||
(
|
(
|
||||||
@@ -1557,12 +1560,21 @@ impl Wallet {
|
|||||||
conditions.num_sigs,
|
conditions.num_sigs,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SpendingConditions::HTLCConditions { conditions, .. } => (
|
None => (None, Some(pubkeys), None, None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SpendingConditions::HTLCConditions {
|
||||||
|
conditions,
|
||||||
|
data: _,
|
||||||
|
} => match conditions {
|
||||||
|
Some(conditions) => (
|
||||||
conditions.refund_keys,
|
conditions.refund_keys,
|
||||||
conditions.pubkeys,
|
conditions.pubkeys,
|
||||||
conditions.locktime,
|
conditions.locktime,
|
||||||
conditions.num_sigs,
|
conditions.num_sigs,
|
||||||
),
|
),
|
||||||
|
None => (None, None, None, None),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if refund_keys.is_some() && locktime.is_none() {
|
if refund_keys.is_some() && locktime.is_none() {
|
||||||
|
|||||||
Reference in New Issue
Block a user