Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support posv #1815

Merged
merged 8 commits into from
May 15, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions mm2src/coins/utxo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,9 @@ pub struct UtxoCoinConf {
/// https://en.bitcoin.it/wiki/Proof_of_work
/// The actual meaning of this is nTime field is used in transaction
pub is_pos: bool,
/// Defines if coin uses PoSV transaction format (Reddcoin, Potcoin, et al).
/// n_time field is appended to end of transaction
pub is_posv: bool,
/// Special field for Zcash and it's forks
/// Defines if Overwinter network upgrade was activated
/// https://z.cash/upgrade/overwinter/
Expand Down Expand Up @@ -795,6 +798,7 @@ impl UtxoCoinFields {
shielded_spends: vec![],
shielded_outputs: vec![],
zcash: self.conf.zcash,
posv: self.conf.is_posv,
str_d_zeel,
hash_algo: self.tx_hash_algo.into(),
}
Expand Down
1 change: 1 addition & 0 deletions mm2src/coins/utxo/slp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,7 @@ impl SlpToken {
join_split_sig: Default::default(),
binding_sig: Default::default(),
zcash: unsigned.zcash,
posv: unsigned.posv,
str_d_zeel: unsigned.str_d_zeel,
tx_hash_algo: self.platform_coin.as_ref().tx_hash_algo,
};
Expand Down
4 changes: 4 additions & 0 deletions mm2src/coins/utxo/utxo_builder/utxo_conf_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ impl<'a> UtxoConfBuilder<'a> {
let mature_confirmations = self.mature_confirmations();

let is_pos = self.is_pos();
let is_posv = self.is_posv();
let segwit = self.segwit();
let force_min_relay_fee = self.conf["force_min_relay_fee"].as_bool().unwrap_or(false);
let mtp_block_count = self.mtp_block_count();
Expand All @@ -95,6 +96,7 @@ impl<'a> UtxoConfBuilder<'a> {
Ok(UtxoCoinConf {
ticker: self.ticker.to_owned(),
is_pos,
is_posv,
requires_notarization,
overwintered,
pub_addr_prefix,
Expand Down Expand Up @@ -266,6 +268,8 @@ impl<'a> UtxoConfBuilder<'a> {

fn is_pos(&self) -> bool { self.conf["isPoS"].as_u64() == Some(1) }

fn is_posv(&self) -> bool { self.conf["isPoSV"].as_u64() == Some(1) }

fn segwit(&self) -> bool { self.conf["segwit"].as_bool().unwrap_or(false) }

fn mtp_block_count(&self) -> NonZeroU64 {
Expand Down
2 changes: 2 additions & 0 deletions mm2src/coins/utxo/utxo_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,7 @@ pub async fn p2sh_spending_tx<T: UtxoCommonOps>(coin: &T, input: P2SHSpendingTxI
version_group_id: coin.as_ref().conf.version_group_id,
consensus_branch_id: coin.as_ref().conf.consensus_branch_id,
zcash: coin.as_ref().conf.zcash,
posv: coin.as_ref().conf.is_posv,
str_d_zeel,
hash_algo,
};
Expand Down Expand Up @@ -1165,6 +1166,7 @@ pub async fn p2sh_spending_tx<T: UtxoCommonOps>(coin: &T, input: P2SHSpendingTxI
join_split_sig: H512::default(),
join_split_pubkey: H256::default(),
zcash: coin.as_ref().conf.zcash,
posv: coin.as_ref().conf.is_posv,
str_d_zeel: unsigned.str_d_zeel,
tx_hash_algo: unsigned.hash_algo.into(),
})
Expand Down
1 change: 1 addition & 0 deletions mm2src/coins/utxo/utxo_common_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ pub(super) fn utxo_coin_fields_for_test(
UtxoCoinFields {
conf: UtxoCoinConf {
is_pos: false,
is_posv: false,
requires_notarization: false.into(),
overwintered: true,
segwit: true,
Expand Down
1 change: 1 addition & 0 deletions mm2src/coins/utxo_signer/src/sign_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub(crate) fn complete_tx(unsigned: TransactionInputSigner, signed_inputs: Vec<T
join_split_sig: H512::default(),
join_split_pubkey: H256::default(),
zcash: unsigned.zcash,
posv: unsigned.posv,
str_d_zeel: unsigned.str_d_zeel,
tx_hash_algo: unsigned.hash_algo.into(),
}
Expand Down
135 changes: 133 additions & 2 deletions mm2src/mm2_bitcoin/chain/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ pub struct Transaction {
pub join_split_sig: H512,
pub binding_sig: H512,
pub zcash: bool,
pub posv: bool,
/// https://github.com/navcoin/navcoin-core/blob/556250920fef9dc3eddd28996329ba316de5f909/src/primitives/transaction.h#L497
pub str_d_zeel: Option<String>,
pub tx_hash_algo: TxHashAlgo,
Expand Down Expand Up @@ -358,15 +359,23 @@ impl Serializable for Transaction {
stream.append(&self.version_group_id);
}

if let Some(n_time) = self.n_time {
stream.append(&n_time);
if !self.posv {
if let Some(n_time) = self.n_time {
stream.append(&n_time);
}
}

stream
.append_list(&self.inputs)
.append_list(&self.outputs)
.append(&self.lock_time);

if self.posv {
if let Some(n_time) = self.n_time {
stream.append(&n_time);
}
}

if self.overwintered && self.version >= 3 {
stream.append(&self.expiry_height);
if self.version >= 4 {
Expand Down Expand Up @@ -418,6 +427,7 @@ pub enum TxType {
StandardWithWitness,
Zcash,
PosWithNTime,
PosvWithNTime,
}

pub fn deserialize_tx<T>(reader: &mut Reader<T>, tx_type: TxType) -> Result<Transaction, Error>
Expand Down Expand Up @@ -511,6 +521,8 @@ where
None
};

let posv = false;

Ok(Transaction {
version,
n_time,
Expand All @@ -528,6 +540,88 @@ where
shielded_spends,
shielded_outputs,
zcash,
posv,
str_d_zeel,
tx_hash_algo: TxHashAlgo::DSHA256,
})
}

pub fn deserialize_tx_posv<T>(reader: &mut Reader<T>, tx_type: TxType) -> Result<Transaction, Error>
shamardy marked this conversation as resolved.
Show resolved Hide resolved
where
T: io::Read,
{
let version = reader.read()?;

let mut inputs: Vec<TransactionInput> = reader.read_list_max(MAX_LIST_SIZE)?;
let read_witness = if inputs.is_empty() && tx_type == TxType::PosvWithNTime {
let witness_flag: u8 = reader.read()?;
if witness_flag != WITNESS_FLAG {
return Err(Error::MalformedData);
}

inputs = reader.read_list_max(MAX_LIST_SIZE)?;
true
} else {
false
};
let outputs = reader.read_list_max(MAX_LIST_SIZE)?;
if outputs.is_empty() && tx_type == TxType::PosvWithNTime {
return Err(Error::Custom("Transaction has no output".into()));
}
if read_witness {
for input in inputs.iter_mut() {
input.script_witness = reader.read_list_max(MAX_LIST_SIZE)?;
}
}

let lock_time = reader.read()?;

let mut posv = false;
if tx_type == TxType::PosvWithNTime && reader.is_finished() {
return Err(Error::MalformedData);
}

let n_time = if tx_type == TxType::PosvWithNTime && !reader.is_finished() {
posv = true;
Some(reader.read()?)
} else {
None
};

let overwintered: bool = false;
let version_group_id = 0;

let expiry_height = 0;
let value_balance = 0;
let shielded_spends = vec![];
let shielded_outputs = vec![];

let join_splits = vec![];
let join_split_pubkey = H256::default();
let join_split_sig = H512::default();
let binding_sig = H512::default();

let zcash = false;
let str_d_zeel = None;

Ok(Transaction {
version,
n_time,
overwintered,
version_group_id,
expiry_height,
value_balance,
inputs,
outputs,
lock_time,
binding_sig,
join_split_pubkey,
join_split_sig,
join_splits,
shielded_spends,
shielded_outputs,
zcash,
posv,
str_d_zeel,
tx_hash_algo: TxHashAlgo::DSHA256,
})
Expand All @@ -545,6 +639,9 @@ impl Deserializable for Transaction {
// specific use case
let mut buffer = vec![];
reader.read_to_end(&mut buffer)?;
if let Ok(t) = deserialize_tx_posv(&mut Reader::from_read(buffer.as_slice()), TxType::PosvWithNTime) {
return Ok(t);
}
if let Ok(t) = deserialize_tx(&mut Reader::from_read(buffer.as_slice()), TxType::StandardWithWitness) {
return Ok(t);
}
Expand Down Expand Up @@ -836,6 +933,7 @@ mod tests {
}],
lock_time: 0x00000011,
zcash: false,
posv: false,
str_d_zeel: None,
tx_hash_algo: TxHashAlgo::DSHA256,
};
Expand Down Expand Up @@ -1021,6 +1119,7 @@ mod tests {
}],
lock_time: 1632875267,
zcash: false,
posv: false,
str_d_zeel: None,
tx_hash_algo: TxHashAlgo::DSHA256,
};
Expand All @@ -1034,4 +1133,36 @@ mod tests {
let ext_tx = ExtTransaction::from(tx.clone());
assert_eq!(tx.hash().reversed().to_string(), ext_tx.txid().to_string());
}

#[test]
fn n_time_posv_transaction() {
let raw = "0200000001fa402b05b9108ec4762247d74c48a2ff303dd832d24c341c486e32cef0434177010000004847304402207a5283cc0fe6fc384744545cb600206ec730d0cdfa6a5e1479cb509fda536ee402202bec1e79b90638f1c608d805b2877fefc8fa6d0df279f58f0a70883e0e0609ce01ffffffff030000000000000000006a734110a10a0000232102fa0ecb032c7cb7be378efd03a84532b5cf1795996bfad854f042dc521616bfdcacd57f643201000000232103c8fc5c87f00bcc32b5ce5c036957f8befeff05bf4d88d2dcde720249f78d9313ac00000000dfcb3c64";
let t: Transaction = raw.into();

assert_eq!(t.version, 2);
assert_eq!(t.lock_time, 0);
assert_eq!(t.inputs.len(), 1);
assert_eq!(t.outputs.len(), 3);
assert_eq!(t.n_time, Some(1681705951));
assert_eq!(t.posv, true);
shamardy marked this conversation as resolved.
Show resolved Hide resolved

let serialized = serialize(&t);
assert_eq!(Bytes::from(raw), serialized);
}

#[test]
fn n_time_posv_transaction_locktime() {
let raw = "0200000001a471828d6290f5ca7935bc26a9d07cda37227ca3fb0e8d1282296ea839c134810100000048473044022067bbc1176fe4fa8681db854d9fce8d47d5613d01820ef78a6ab76c4f067500990220560d00098fcb69eb8587873f8072c11bcf832308fdcf5712d0e4aea4b761060e01fdffffff02940237c31d0600001976a9147fb8384a3f328148137447cdf6b1c3c1f0a8559588ac00e40b54020000001976a91485ee21a7f8cdd9034fb55004e0d8ed27db1c03c288acbd8d05002b584e63";
let t: Transaction = raw.into();

assert_eq!(t.version, 2);
assert_eq!(t.lock_time, 363965);
assert_eq!(t.inputs.len(), 1);
assert_eq!(t.outputs.len(), 2);
assert_eq!(t.n_time, Some(1666078763));
assert_eq!(t.posv, true);
shamardy marked this conversation as resolved.
Show resolved Hide resolved

let serialized = serialize(&t);
assert_eq!(Bytes::from(raw), serialized);
}
}
Loading