mirror of
https://github.com/serai-dex/serai.git
synced 2025-01-05 10:29:40 +00:00
bitcoin 0.31
This commit is contained in:
parent
2958f196fc
commit
34bcb9eb01
12 changed files with 122 additions and 91 deletions
50
Cargo.lock
generated
50
Cargo.lock
generated
|
@ -440,6 +440,12 @@ version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
|
checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bech32"
|
||||||
|
version = "0.10.0-beta"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "beef"
|
name = "beef"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
@ -496,24 +502,28 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitcoin"
|
name = "bitcoin"
|
||||||
version = "0.30.1"
|
version = "0.31.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4e99ff7289b20a7385f66a0feda78af2fc119d28fb56aea8886a9cd0a4abdd75"
|
checksum = "5973a027b341b462105675962214dfe3c938ad9afd395d84b28602608bdcec7b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bech32",
|
"bech32 0.10.0-beta",
|
||||||
"bitcoin-private",
|
"bitcoin-internals",
|
||||||
"bitcoin_hashes",
|
"bitcoin_hashes",
|
||||||
"core2 0.3.3",
|
"core2 0.3.3",
|
||||||
|
"hex-conservative",
|
||||||
"hex_lit",
|
"hex_lit",
|
||||||
"secp256k1",
|
"secp256k1",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitcoin-private"
|
name = "bitcoin-internals"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57"
|
checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitcoin-serai"
|
name = "bitcoin-serai"
|
||||||
|
@ -538,12 +548,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitcoin_hashes"
|
name = "bitcoin_hashes"
|
||||||
version = "0.12.0"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501"
|
checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitcoin-private",
|
"bitcoin-internals",
|
||||||
"core2 0.3.3",
|
"core2 0.3.3",
|
||||||
|
"hex-conservative",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1050,7 +1061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979"
|
checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.5",
|
"base64 0.21.5",
|
||||||
"bech32",
|
"bech32 0.9.1",
|
||||||
"bs58",
|
"bs58",
|
||||||
"digest 0.10.7",
|
"digest 0.10.7",
|
||||||
"generic-array 0.14.7",
|
"generic-array 0.14.7",
|
||||||
|
@ -3274,6 +3285,15 @@ version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex-conservative"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2"
|
||||||
|
dependencies = [
|
||||||
|
"core2 0.3.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hex-literal"
|
name = "hex-literal"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
@ -8044,9 +8064,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "secp256k1"
|
name = "secp256k1"
|
||||||
version = "0.27.0"
|
version = "0.28.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f"
|
checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitcoin_hashes",
|
"bitcoin_hashes",
|
||||||
"rand",
|
"rand",
|
||||||
|
@ -8056,9 +8076,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "secp256k1-sys"
|
name = "secp256k1-sys"
|
||||||
version = "0.8.1"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e"
|
checksum = "09e67c467c38fd24bd5499dc9a18183b31575c12ee549197e3e20d57aa4fe3b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
|
@ -17,8 +17,8 @@ rand_core = { version = "0.6", default-features = false }
|
||||||
|
|
||||||
sha2 = { version = "0.10", default-features = false }
|
sha2 = { version = "0.10", default-features = false }
|
||||||
|
|
||||||
secp256k1 = { version = "0.27", default-features = false }
|
secp256k1 = { version = "0.28", default-features = false }
|
||||||
bitcoin = { version = "0.30", default-features = false, features = ["no-std"] }
|
bitcoin = { version = "0.31", default-features = false, features = ["no-std"] }
|
||||||
|
|
||||||
k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits"] }
|
k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits"] }
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ impl ReceivedOutput {
|
||||||
|
|
||||||
/// The value of this output.
|
/// The value of this output.
|
||||||
pub fn value(&self) -> u64 {
|
pub fn value(&self) -> u64 {
|
||||||
self.output.value
|
self.output.value.to_sat()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a ReceivedOutput from a generic satisfying Read.
|
/// Read a ReceivedOutput from a generic satisfying Read.
|
||||||
|
|
|
@ -16,7 +16,8 @@ use bitcoin::{
|
||||||
sighash::{TapSighashType, SighashCache, Prevouts},
|
sighash::{TapSighashType, SighashCache, Prevouts},
|
||||||
absolute::LockTime,
|
absolute::LockTime,
|
||||||
script::{PushBytesBuf, ScriptBuf},
|
script::{PushBytesBuf, ScriptBuf},
|
||||||
OutPoint, Sequence, Witness, TxIn, TxOut, Transaction, Address,
|
transaction::{Version, Transaction},
|
||||||
|
OutPoint, Sequence, Witness, TxIn, Amount, TxOut, Address,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -62,7 +63,7 @@ impl SignableTransaction {
|
||||||
fn calculate_weight(inputs: usize, payments: &[(Address, u64)], change: Option<&Address>) -> u64 {
|
fn calculate_weight(inputs: usize, payments: &[(Address, u64)], change: Option<&Address>) -> u64 {
|
||||||
// Expand this a full transaction in order to use the bitcoin library's weight function
|
// Expand this a full transaction in order to use the bitcoin library's weight function
|
||||||
let mut tx = Transaction {
|
let mut tx = Transaction {
|
||||||
version: 2,
|
version: Version(2),
|
||||||
lock_time: LockTime::ZERO,
|
lock_time: LockTime::ZERO,
|
||||||
input: vec![
|
input: vec![
|
||||||
TxIn {
|
TxIn {
|
||||||
|
@ -82,13 +83,16 @@ impl SignableTransaction {
|
||||||
.iter()
|
.iter()
|
||||||
// The payment is a fixed size so we don't have to use it here
|
// The payment is a fixed size so we don't have to use it here
|
||||||
// The script pub key is not of a fixed size and does have to be used here
|
// The script pub key is not of a fixed size and does have to be used here
|
||||||
.map(|payment| TxOut { value: payment.1, script_pubkey: payment.0.script_pubkey() })
|
.map(|payment| TxOut {
|
||||||
|
value: Amount::from_sat(payment.1),
|
||||||
|
script_pubkey: payment.0.script_pubkey(),
|
||||||
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
};
|
};
|
||||||
if let Some(change) = change {
|
if let Some(change) = change {
|
||||||
// Use a 0 value since we're currently unsure what the change amount will be, and since
|
// Use a 0 value since we're currently unsure what the change amount will be, and since
|
||||||
// the value is fixed size (so any value could be used here)
|
// the value is fixed size (so any value could be used here)
|
||||||
tx.output.push(TxOut { value: 0, script_pubkey: change.script_pubkey() });
|
tx.output.push(TxOut { value: Amount::ZERO, script_pubkey: change.script_pubkey() });
|
||||||
}
|
}
|
||||||
u64::try_from(tx.weight()).unwrap()
|
u64::try_from(tx.weight()).unwrap()
|
||||||
}
|
}
|
||||||
|
@ -103,8 +107,8 @@ impl SignableTransaction {
|
||||||
|
|
||||||
/// Returns the fee this transaction will use.
|
/// Returns the fee this transaction will use.
|
||||||
pub fn fee(&self) -> u64 {
|
pub fn fee(&self) -> u64 {
|
||||||
self.prevouts.iter().map(|prevout| prevout.value).sum::<u64>() -
|
self.prevouts.iter().map(|prevout| prevout.value.to_sat()).sum::<u64>() -
|
||||||
self.tx.output.iter().map(|prevout| prevout.value).sum::<u64>()
|
self.tx.output.iter().map(|prevout| prevout.value.to_sat()).sum::<u64>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new SignableTransaction.
|
/// Create a new SignableTransaction.
|
||||||
|
@ -139,7 +143,7 @@ impl SignableTransaction {
|
||||||
Err(TransactionError::TooMuchData)?;
|
Err(TransactionError::TooMuchData)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let input_sat = inputs.iter().map(|input| input.output.value).sum::<u64>();
|
let input_sat = inputs.iter().map(|input| input.output.value.to_sat()).sum::<u64>();
|
||||||
let offsets = inputs.iter().map(|input| input.offset).collect();
|
let offsets = inputs.iter().map(|input| input.offset).collect();
|
||||||
let tx_ins = inputs
|
let tx_ins = inputs
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -154,15 +158,18 @@ impl SignableTransaction {
|
||||||
let payment_sat = payments.iter().map(|payment| payment.1).sum::<u64>();
|
let payment_sat = payments.iter().map(|payment| payment.1).sum::<u64>();
|
||||||
let mut tx_outs = payments
|
let mut tx_outs = payments
|
||||||
.iter()
|
.iter()
|
||||||
.map(|payment| TxOut { value: payment.1, script_pubkey: payment.0.script_pubkey() })
|
.map(|payment| TxOut {
|
||||||
|
value: Amount::from_sat(payment.1),
|
||||||
|
script_pubkey: payment.0.script_pubkey(),
|
||||||
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// Add the OP_RETURN output
|
// Add the OP_RETURN output
|
||||||
if let Some(data) = data {
|
if let Some(data) = data {
|
||||||
tx_outs.push(TxOut {
|
tx_outs.push(TxOut {
|
||||||
value: 0,
|
value: Amount::ZERO,
|
||||||
script_pubkey: ScriptBuf::new_op_return(
|
script_pubkey: ScriptBuf::new_op_return(
|
||||||
&PushBytesBuf::try_from(data)
|
PushBytesBuf::try_from(data)
|
||||||
.expect("data didn't fit into PushBytes depsite being checked"),
|
.expect("data didn't fit into PushBytes depsite being checked"),
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
@ -209,7 +216,8 @@ impl SignableTransaction {
|
||||||
let fee_with_change = fee_per_weight * weight_with_change;
|
let fee_with_change = fee_per_weight * weight_with_change;
|
||||||
if let Some(value) = input_sat.checked_sub(payment_sat + fee_with_change) {
|
if let Some(value) = input_sat.checked_sub(payment_sat + fee_with_change) {
|
||||||
if value >= DUST {
|
if value >= DUST {
|
||||||
tx_outs.push(TxOut { value, script_pubkey: change.script_pubkey() });
|
tx_outs
|
||||||
|
.push(TxOut { value: Amount::from_sat(value), script_pubkey: change.script_pubkey() });
|
||||||
weight = weight_with_change;
|
weight = weight_with_change;
|
||||||
needed_fee = fee_with_change;
|
needed_fee = fee_with_change;
|
||||||
}
|
}
|
||||||
|
@ -225,7 +233,12 @@ impl SignableTransaction {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(SignableTransaction {
|
Ok(SignableTransaction {
|
||||||
tx: Transaction { version: 2, lock_time: LockTime::ZERO, input: tx_ins, output: tx_outs },
|
tx: Transaction {
|
||||||
|
version: Version(2),
|
||||||
|
lock_time: LockTime::ZERO,
|
||||||
|
input: tx_ins,
|
||||||
|
output: tx_outs,
|
||||||
|
},
|
||||||
offsets,
|
offsets,
|
||||||
prevouts: inputs.drain(..).map(|input| input.output).collect(),
|
prevouts: inputs.drain(..).map(|input| input.output).collect(),
|
||||||
needed_fee,
|
needed_fee,
|
||||||
|
@ -256,7 +269,7 @@ impl SignableTransaction {
|
||||||
}
|
}
|
||||||
for payment in &tx.output {
|
for payment in &tx.output {
|
||||||
transcript.append_message(b"output_script", payment.script_pubkey.as_bytes());
|
transcript.append_message(b"output_script", payment.script_pubkey.as_bytes());
|
||||||
transcript.append_message(b"output_amount", payment.value.to_le_bytes());
|
transcript.append_message(b"output_amount", payment.value.to_sat().to_le_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut sigs = vec![];
|
let mut sigs = vec![];
|
||||||
|
|
|
@ -23,7 +23,7 @@ use bitcoin_serai::{
|
||||||
blockdata::opcodes::all::OP_RETURN,
|
blockdata::opcodes::all::OP_RETURN,
|
||||||
script::{PushBytesBuf, Instruction, Instructions, Script},
|
script::{PushBytesBuf, Instruction, Instructions, Script},
|
||||||
address::NetworkChecked,
|
address::NetworkChecked,
|
||||||
OutPoint, TxOut, Transaction, Network, Address,
|
OutPoint, Amount, TxOut, Transaction, Network, Address,
|
||||||
},
|
},
|
||||||
wallet::{
|
wallet::{
|
||||||
tweak_keys, address_payload, ReceivedOutput, Scanner, TransactionError, SignableTransaction,
|
tweak_keys, address_payload, ReceivedOutput, Scanner, TransactionError, SignableTransaction,
|
||||||
|
@ -58,7 +58,7 @@ async fn send_and_get_output(rpc: &Rpc, scanner: &Scanner, key: ProjectivePoint)
|
||||||
rpc
|
rpc
|
||||||
.rpc_call::<Vec<String>>(
|
.rpc_call::<Vec<String>>(
|
||||||
"generatetoaddress",
|
"generatetoaddress",
|
||||||
serde_json::json!([100, Address::p2sh(Script::empty(), Network::Regtest).unwrap()]),
|
serde_json::json!([100, Address::p2sh(Script::new(), Network::Regtest).unwrap()]),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -70,7 +70,7 @@ async fn send_and_get_output(rpc: &Rpc, scanner: &Scanner, key: ProjectivePoint)
|
||||||
|
|
||||||
assert_eq!(outputs.len(), 1);
|
assert_eq!(outputs.len(), 1);
|
||||||
assert_eq!(outputs[0].outpoint(), &OutPoint::new(block.txdata[0].txid(), 0));
|
assert_eq!(outputs[0].outpoint(), &OutPoint::new(block.txdata[0].txid(), 0));
|
||||||
assert_eq!(outputs[0].value(), block.txdata[0].output[0].value);
|
assert_eq!(outputs[0].value(), block.txdata[0].output[0].value.to_sat());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ReceivedOutput::read::<&[u8]>(&mut outputs[0].serialize().as_ref()).unwrap(),
|
ReceivedOutput::read::<&[u8]>(&mut outputs[0].serialize().as_ref()).unwrap(),
|
||||||
|
@ -296,21 +296,24 @@ async_sequential! {
|
||||||
|
|
||||||
// Make sure the payments were properly created
|
// Make sure the payments were properly created
|
||||||
for ((output, scanned), payment) in tx.output.iter().zip(outputs.iter()).zip(payments.iter()) {
|
for ((output, scanned), payment) in tx.output.iter().zip(outputs.iter()).zip(payments.iter()) {
|
||||||
assert_eq!(output, &TxOut { script_pubkey: payment.0.script_pubkey(), value: payment.1 });
|
assert_eq!(
|
||||||
|
output,
|
||||||
|
&TxOut { script_pubkey: payment.0.script_pubkey(), value: Amount::from_sat(payment.1) },
|
||||||
|
);
|
||||||
assert_eq!(scanned.value(), payment.1 );
|
assert_eq!(scanned.value(), payment.1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the change is correct
|
// Make sure the change is correct
|
||||||
assert_eq!(needed_fee, u64::try_from(tx.weight()).unwrap() * FEE);
|
assert_eq!(needed_fee, u64::try_from(tx.weight()).unwrap() * FEE);
|
||||||
let input_value = output.value() + offset_output.value();
|
let input_value = output.value() + offset_output.value();
|
||||||
let output_value = tx.output.iter().map(|output| output.value).sum::<u64>();
|
let output_value = tx.output.iter().map(|output| output.value.to_sat()).sum::<u64>();
|
||||||
assert_eq!(input_value - output_value, needed_fee);
|
assert_eq!(input_value - output_value, needed_fee);
|
||||||
|
|
||||||
let change_amount =
|
let change_amount =
|
||||||
input_value - payments.iter().map(|payment| payment.1).sum::<u64>() - needed_fee;
|
input_value - payments.iter().map(|payment| payment.1).sum::<u64>() - needed_fee;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tx.output[2],
|
tx.output[2],
|
||||||
TxOut { script_pubkey: change_addr.script_pubkey(), value: change_amount },
|
TxOut { script_pubkey: change_addr.script_pubkey(), value: Amount::from_sat(change_amount) },
|
||||||
);
|
);
|
||||||
|
|
||||||
// This also tests send_raw_transaction and get_transaction, which the RPC test can't
|
// This also tests send_raw_transaction and get_transaction, which the RPC test can't
|
||||||
|
|
|
@ -42,7 +42,7 @@ frost-schnorrkel = { path = "../crypto/schnorrkel" }
|
||||||
sp-application-crypto = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-application-crypto = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||||
|
|
||||||
# Bitcoin
|
# Bitcoin
|
||||||
secp256k1 = { version = "0.27", features = ["global-context", "rand-std"], optional = true }
|
secp256k1 = { version = "0.28", features = ["global-context", "rand-std"], optional = true }
|
||||||
k256 = { version = "^0.13.1", optional = true }
|
k256 = { version = "^0.13.1", optional = true }
|
||||||
bitcoin-serai = { path = "../coins/bitcoin", optional = true }
|
bitcoin-serai = { path = "../coins/bitcoin", optional = true }
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,8 @@ use bitcoin_serai::bitcoin::{
|
||||||
sighash::{EcdsaSighashType, SighashCache},
|
sighash::{EcdsaSighashType, SighashCache},
|
||||||
script::{PushBytesBuf, Builder},
|
script::{PushBytesBuf, Builder},
|
||||||
absolute::LockTime,
|
absolute::LockTime,
|
||||||
Sequence, Script, Witness, TxIn,
|
Sequence, Script, Witness, TxIn, Amount as BAmount,
|
||||||
|
transaction::Version,
|
||||||
};
|
};
|
||||||
|
|
||||||
use serai_client::{
|
use serai_client::{
|
||||||
|
@ -106,7 +107,7 @@ impl OutputTrait<Bitcoin> for Output {
|
||||||
|
|
||||||
fn key(&self) -> ProjectivePoint {
|
fn key(&self) -> ProjectivePoint {
|
||||||
let script = &self.output.output().script_pubkey;
|
let script = &self.output.output().script_pubkey;
|
||||||
assert!(script.is_v1_p2tr());
|
assert!(script.is_p2tr());
|
||||||
let Instruction::PushBytes(key) = script.instructions_minimal().last().unwrap().unwrap() else {
|
let Instruction::PushBytes(key) = script.instructions_minimal().last().unwrap().unwrap() else {
|
||||||
panic!("last item in v1 Taproot script wasn't bytes")
|
panic!("last item in v1 Taproot script wasn't bytes")
|
||||||
};
|
};
|
||||||
|
@ -177,10 +178,11 @@ impl TransactionTrait<Bitcoin> for Transaction {
|
||||||
hash.reverse();
|
hash.reverse();
|
||||||
value += network.rpc.get_transaction(&hash).await.unwrap().output
|
value += network.rpc.get_transaction(&hash).await.unwrap().output
|
||||||
[usize::try_from(output.vout).unwrap()]
|
[usize::try_from(output.vout).unwrap()]
|
||||||
.value;
|
.value
|
||||||
|
.to_sat();
|
||||||
}
|
}
|
||||||
for output in &self.output {
|
for output in &self.output {
|
||||||
value -= output.value;
|
value -= output.value.to_sat();
|
||||||
}
|
}
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
@ -331,9 +333,10 @@ impl Bitcoin {
|
||||||
input_tx.reverse();
|
input_tx.reverse();
|
||||||
in_value += self.get_transaction(&input_tx).await?.output
|
in_value += self.get_transaction(&input_tx).await?.output
|
||||||
[usize::try_from(input.previous_output.vout).unwrap()]
|
[usize::try_from(input.previous_output.vout).unwrap()]
|
||||||
.value;
|
.value
|
||||||
|
.to_sat();
|
||||||
}
|
}
|
||||||
let out = tx.output.iter().map(|output| output.value).sum::<u64>();
|
let out = tx.output.iter().map(|output| output.value.to_sat()).sum::<u64>();
|
||||||
fees.push((in_value - out) / tx.weight().to_wu());
|
fees.push((in_value - out) / tx.weight().to_wu());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -708,7 +711,7 @@ impl Network for Bitcoin {
|
||||||
.rpc
|
.rpc
|
||||||
.rpc_call::<Vec<String>>(
|
.rpc_call::<Vec<String>>(
|
||||||
"generatetoaddress",
|
"generatetoaddress",
|
||||||
serde_json::json!([1, BAddress::p2sh(Script::empty(), BitcoinNetwork::Regtest).unwrap()]),
|
serde_json::json!([1, BAddress::p2sh(Script::new(), BitcoinNetwork::Regtest).unwrap()]),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -734,16 +737,16 @@ impl Network for Bitcoin {
|
||||||
|
|
||||||
let tx = self.get_block(new_block).await.unwrap().txdata.swap_remove(0);
|
let tx = self.get_block(new_block).await.unwrap().txdata.swap_remove(0);
|
||||||
let mut tx = Transaction {
|
let mut tx = Transaction {
|
||||||
version: 2,
|
version: Version(2),
|
||||||
lock_time: LockTime::ZERO,
|
lock_time: LockTime::ZERO,
|
||||||
input: vec![TxIn {
|
input: vec![TxIn {
|
||||||
previous_output: OutPoint { txid: tx.txid(), vout: 0 },
|
previous_output: OutPoint { txid: tx.txid(), vout: 0 },
|
||||||
script_sig: Script::empty().into(),
|
script_sig: Script::new().into(),
|
||||||
sequence: Sequence(u32::MAX),
|
sequence: Sequence(u32::MAX),
|
||||||
witness: Witness::default(),
|
witness: Witness::default(),
|
||||||
}],
|
}],
|
||||||
output: vec![TxOut {
|
output: vec![TxOut {
|
||||||
value: tx.output[0].value - 10000,
|
value: tx.output[0].value - BAmount::from_sat(10000),
|
||||||
script_pubkey: address.0.script_pubkey(),
|
script_pubkey: address.0.script_pubkey(),
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,7 +26,7 @@ serai-runtime = { path = "../runtime", version = "0.1" }
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate" }
|
sp-core = { git = "https://github.com/serai-dex/substrate" }
|
||||||
subxt = { version = "0.29", default-features = false, features = ["jsonrpsee-ws"], optional = true }
|
subxt = { version = "0.29", default-features = false, features = ["jsonrpsee-ws"], optional = true }
|
||||||
|
|
||||||
bitcoin = { version = "0.30", optional = true }
|
bitcoin = { version = "0.31", optional = true }
|
||||||
|
|
||||||
ciphersuite = { path = "../../crypto/ciphersuite", version = "0.4", optional = true }
|
ciphersuite = { path = "../../crypto/ciphersuite", version = "0.4", optional = true }
|
||||||
monero-serai = { path = "../../coins/monero", version = "0.1.4-alpha", optional = true }
|
monero-serai = { path = "../../coins/monero", version = "0.1.4-alpha", optional = true }
|
||||||
|
|
|
@ -5,10 +5,9 @@ use scale::{Encode, Decode};
|
||||||
use bitcoin::{
|
use bitcoin::{
|
||||||
hashes::{Hash as HashTrait, hash160::Hash},
|
hashes::{Hash as HashTrait, hash160::Hash},
|
||||||
PubkeyHash, ScriptHash,
|
PubkeyHash, ScriptHash,
|
||||||
network::constants::Network,
|
network::Network,
|
||||||
address::{
|
WitnessVersion, WitnessProgram,
|
||||||
Error, WitnessVersion, Payload, WitnessProgram, NetworkChecked, Address as BAddressGeneric,
|
address::{Error, Payload, NetworkChecked, Address as BAddressGeneric},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type BAddress = BAddressGeneric<NetworkChecked>;
|
type BAddress = BAddressGeneric<NetworkChecked>;
|
||||||
|
@ -18,28 +17,20 @@ pub struct Address(pub BAddress);
|
||||||
|
|
||||||
impl PartialEq for Address {
|
impl PartialEq for Address {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.0.payload == other.0.payload
|
// Since Serai defines the Bitcoin-address specification as a variant of the payload alone,
|
||||||
|
// define equivalency as the payload alone
|
||||||
|
self.0.payload() == other.0.payload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Address {
|
impl FromStr for Address {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
fn from_str(str: &str) -> Result<Address, Error> {
|
fn from_str(str: &str) -> Result<Address, Error> {
|
||||||
let mut original_address = BAddressGeneric::from_str(str)?;
|
Ok(Address(
|
||||||
// Standardize the network
|
BAddressGeneric::from_str(str)
|
||||||
original_address.network = Network::Bitcoin;
|
.map_err(|_| Error::UnrecognizedScript)?
|
||||||
let address = original_address
|
.require_network(Network::Bitcoin)?,
|
||||||
.clone()
|
))
|
||||||
.require_network(Network::Bitcoin)
|
|
||||||
.expect("network wasn't mainnet despite overriding network");
|
|
||||||
|
|
||||||
// Also check this isn't caching the string internally
|
|
||||||
if BAddressGeneric::from_str(&address.to_string())? != original_address {
|
|
||||||
// TODO: Make this an error?
|
|
||||||
panic!("Address::from_str(address.to_string()) != address for Bitcoin");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Address(address))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +81,7 @@ impl TryInto<Vec<u8>> for Address {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
fn try_into(self) -> Result<Vec<u8>, ()> {
|
fn try_into(self) -> Result<Vec<u8>, ()> {
|
||||||
Ok(
|
Ok(
|
||||||
(match self.0.payload {
|
(match self.0.payload() {
|
||||||
Payload::PubkeyHash(hash) => EncodedAddress::P2PKH(*hash.as_raw_hash().as_byte_array()),
|
Payload::PubkeyHash(hash) => EncodedAddress::P2PKH(*hash.as_raw_hash().as_byte_array()),
|
||||||
Payload::ScriptHash(hash) => EncodedAddress::P2SH(*hash.as_raw_hash().as_byte_array()),
|
Payload::ScriptHash(hash) => EncodedAddress::P2SH(*hash.as_raw_hash().as_byte_array()),
|
||||||
Payload::WitnessProgram(program) => match program.version() {
|
Payload::WitnessProgram(program) => match program.version() {
|
||||||
|
|
|
@ -51,7 +51,7 @@ async fn mint_and_burn_test() {
|
||||||
secp256k1::{SECP256K1, SecretKey},
|
secp256k1::{SECP256K1, SecretKey},
|
||||||
PrivateKey, PublicKey,
|
PrivateKey, PublicKey,
|
||||||
consensus::Encodable,
|
consensus::Encodable,
|
||||||
network::constants::Network,
|
network::Network,
|
||||||
address::Address,
|
address::Address,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -263,8 +263,9 @@ async fn mint_and_burn_test() {
|
||||||
sighash::{EcdsaSighashType, SighashCache},
|
sighash::{EcdsaSighashType, SighashCache},
|
||||||
script::{PushBytesBuf, Script, ScriptBuf, Builder},
|
script::{PushBytesBuf, Script, ScriptBuf, Builder},
|
||||||
absolute::LockTime,
|
absolute::LockTime,
|
||||||
|
transaction::{Version, Transaction},
|
||||||
address::Payload,
|
address::Payload,
|
||||||
Sequence, Witness, OutPoint, TxIn, TxOut, Transaction, Network,
|
Sequence, Witness, OutPoint, TxIn, Amount, TxOut, Network,
|
||||||
};
|
};
|
||||||
|
|
||||||
let private_key =
|
let private_key =
|
||||||
|
@ -278,17 +279,17 @@ async fn mint_and_burn_test() {
|
||||||
rpc.get_block(&rpc.get_block_hash(1).await.unwrap()).await.unwrap().txdata.swap_remove(0);
|
rpc.get_block(&rpc.get_block_hash(1).await.unwrap()).await.unwrap().txdata.swap_remove(0);
|
||||||
#[allow(clippy::inconsistent_digit_grouping)]
|
#[allow(clippy::inconsistent_digit_grouping)]
|
||||||
let mut tx = Transaction {
|
let mut tx = Transaction {
|
||||||
version: 2,
|
version: Version(2),
|
||||||
lock_time: LockTime::ZERO,
|
lock_time: LockTime::ZERO,
|
||||||
input: vec![TxIn {
|
input: vec![TxIn {
|
||||||
previous_output: OutPoint { txid: tx.txid(), vout: 0 },
|
previous_output: OutPoint { txid: tx.txid(), vout: 0 },
|
||||||
script_sig: Script::empty().into(),
|
script_sig: Script::new().into(),
|
||||||
sequence: Sequence(u32::MAX),
|
sequence: Sequence(u32::MAX),
|
||||||
witness: Witness::default(),
|
witness: Witness::default(),
|
||||||
}],
|
}],
|
||||||
output: vec![
|
output: vec![
|
||||||
TxOut {
|
TxOut {
|
||||||
value: 1_100_000_00,
|
value: Amount::from_sat(1_100_000_00),
|
||||||
script_pubkey: Payload::p2tr_tweaked(TweakedPublicKey::dangerous_assume_tweaked(
|
script_pubkey: Payload::p2tr_tweaked(TweakedPublicKey::dangerous_assume_tweaked(
|
||||||
XOnlyPublicKey::from_slice(&bitcoin_key_pair.1[1 ..]).unwrap(),
|
XOnlyPublicKey::from_slice(&bitcoin_key_pair.1[1 ..]).unwrap(),
|
||||||
))
|
))
|
||||||
|
@ -296,16 +297,16 @@ async fn mint_and_burn_test() {
|
||||||
},
|
},
|
||||||
TxOut {
|
TxOut {
|
||||||
// change = amount spent - fee
|
// change = amount spent - fee
|
||||||
value: tx.output[0].value - 1_100_000_00 - 1_000_00,
|
value: Amount::from_sat(tx.output[0].value.to_sat() - 1_100_000_00 - 1_000_00),
|
||||||
script_pubkey: Payload::p2tr_tweaked(TweakedPublicKey::dangerous_assume_tweaked(
|
script_pubkey: Payload::p2tr_tweaked(TweakedPublicKey::dangerous_assume_tweaked(
|
||||||
XOnlyPublicKey::from_slice(&public_key.inner.serialize()[1 ..]).unwrap(),
|
XOnlyPublicKey::from_slice(&public_key.inner.serialize()[1 ..]).unwrap(),
|
||||||
))
|
))
|
||||||
.script_pubkey(),
|
.script_pubkey(),
|
||||||
},
|
},
|
||||||
TxOut {
|
TxOut {
|
||||||
value: 0,
|
value: Amount::ZERO,
|
||||||
script_pubkey: ScriptBuf::new_op_return(
|
script_pubkey: ScriptBuf::new_op_return(
|
||||||
&PushBytesBuf::try_from(Shorthand::transfer(None, serai_addr).encode()).unwrap(),
|
PushBytesBuf::try_from(Shorthand::transfer(None, serai_addr).encode()).unwrap(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -447,7 +448,7 @@ async fn mint_and_burn_test() {
|
||||||
|
|
||||||
// Create a random Bitcoin/Monero address
|
// Create a random Bitcoin/Monero address
|
||||||
let bitcoin_addr = {
|
let bitcoin_addr = {
|
||||||
use bitcoin_serai::bitcoin::{network::constants::Network, key::PublicKey, address::Address};
|
use bitcoin_serai::bitcoin::{network::Network, key::PublicKey, address::Address};
|
||||||
// Uses Network::Bitcoin since it doesn't actually matter, Serai strips it out
|
// Uses Network::Bitcoin since it doesn't actually matter, Serai strips it out
|
||||||
// TODO: Move Serai to Payload from Address
|
// TODO: Move Serai to Payload from Address
|
||||||
Address::p2pkh(
|
Address::p2pkh(
|
||||||
|
@ -555,9 +556,9 @@ async fn mint_and_burn_test() {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let tx_fee = 1_100_000_00 -
|
let tx_fee = 1_100_000_00 -
|
||||||
block.txdata[1].output.iter().map(|output| output.value).sum::<u64>();
|
block.txdata[1].output.iter().map(|output| output.value.to_sat()).sum::<u64>();
|
||||||
|
|
||||||
assert_eq!(received_output.value, 1_000_000_00 - tx_fee);
|
assert_eq!(received_output.value.to_sat(), 1_000_000_00 - tx_fee);
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -251,7 +251,7 @@ impl Coordinator {
|
||||||
match self.network {
|
match self.network {
|
||||||
NetworkId::Bitcoin => {
|
NetworkId::Bitcoin => {
|
||||||
use bitcoin_serai::{
|
use bitcoin_serai::{
|
||||||
bitcoin::{consensus::Encodable, network::constants::Network, Script, Address},
|
bitcoin::{consensus::Encodable, network::Network, Script, Address},
|
||||||
rpc::Rpc,
|
rpc::Rpc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -260,7 +260,7 @@ impl Coordinator {
|
||||||
rpc
|
rpc
|
||||||
.rpc_call::<Vec<String>>(
|
.rpc_call::<Vec<String>>(
|
||||||
"generatetoaddress",
|
"generatetoaddress",
|
||||||
serde_json::json!([1, Address::p2sh(Script::empty(), Network::Regtest).unwrap()]),
|
serde_json::json!([1, Address::p2sh(Script::new(), Network::Regtest).unwrap()]),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -233,28 +233,28 @@ impl Wallet {
|
||||||
sighash::{EcdsaSighashType, SighashCache},
|
sighash::{EcdsaSighashType, SighashCache},
|
||||||
script::{PushBytesBuf, Script, ScriptBuf, Builder},
|
script::{PushBytesBuf, Script, ScriptBuf, Builder},
|
||||||
address::Payload,
|
address::Payload,
|
||||||
OutPoint, Sequence, Witness, TxIn, TxOut,
|
OutPoint, Sequence, Witness, TxIn, Amount, TxOut,
|
||||||
absolute::LockTime,
|
absolute::LockTime,
|
||||||
Transaction,
|
transaction::{Version, Transaction},
|
||||||
};
|
};
|
||||||
|
|
||||||
const AMOUNT: u64 = 100000000;
|
const AMOUNT: u64 = 100000000;
|
||||||
let mut tx = Transaction {
|
let mut tx = Transaction {
|
||||||
version: 2,
|
version: Version(2),
|
||||||
lock_time: LockTime::ZERO,
|
lock_time: LockTime::ZERO,
|
||||||
input: vec![TxIn {
|
input: vec![TxIn {
|
||||||
previous_output: OutPoint { txid: input_tx.txid(), vout: 0 },
|
previous_output: OutPoint { txid: input_tx.txid(), vout: 0 },
|
||||||
script_sig: Script::empty().into(),
|
script_sig: Script::new().into(),
|
||||||
sequence: Sequence(u32::MAX),
|
sequence: Sequence(u32::MAX),
|
||||||
witness: Witness::default(),
|
witness: Witness::default(),
|
||||||
}],
|
}],
|
||||||
output: vec![
|
output: vec![
|
||||||
TxOut {
|
TxOut {
|
||||||
value: input_tx.output[0].value - AMOUNT - 10000,
|
value: Amount::from_sat(input_tx.output[0].value.to_sat() - AMOUNT - 10000),
|
||||||
script_pubkey: input_tx.output[0].script_pubkey.clone(),
|
script_pubkey: input_tx.output[0].script_pubkey.clone(),
|
||||||
},
|
},
|
||||||
TxOut {
|
TxOut {
|
||||||
value: AMOUNT,
|
value: Amount::from_sat(AMOUNT),
|
||||||
script_pubkey: Payload::p2tr_tweaked(TweakedPublicKey::dangerous_assume_tweaked(
|
script_pubkey: Payload::p2tr_tweaked(TweakedPublicKey::dangerous_assume_tweaked(
|
||||||
XOnlyPublicKey::from_slice(&to[1 ..]).unwrap(),
|
XOnlyPublicKey::from_slice(&to[1 ..]).unwrap(),
|
||||||
))
|
))
|
||||||
|
@ -265,9 +265,9 @@ impl Wallet {
|
||||||
|
|
||||||
if let Some(instruction) = instruction {
|
if let Some(instruction) = instruction {
|
||||||
tx.output.push(TxOut {
|
tx.output.push(TxOut {
|
||||||
value: 0,
|
value: Amount::ZERO,
|
||||||
script_pubkey: ScriptBuf::new_op_return(
|
script_pubkey: ScriptBuf::new_op_return(
|
||||||
&PushBytesBuf::try_from(
|
PushBytesBuf::try_from(
|
||||||
Shorthand::Raw(RefundableInInstruction { origin: None, instruction }).encode(),
|
Shorthand::Raw(RefundableInInstruction { origin: None, instruction }).encode(),
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
|
Loading…
Reference in a new issue