Update to bitcoin 0.30

Also performs a general update with a variety of upgraded Substrate depends.
This commit is contained in:
Luke Parker
2023-04-09 02:31:10 -04:00
parent 96525330c2
commit f6206b60ec
17 changed files with 387 additions and 284 deletions

429
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -16,8 +16,8 @@ rand_core = "0.6"
sha2 = "0.10" sha2 = "0.10"
secp256k1 = { version = "0.24", features = ["global-context"] } secp256k1 = { version = "0.27", features = ["global-context"] }
bitcoin = { version = "0.29", features = ["serde"] } bitcoin = { version = "0.30", features = ["serde"] }
k256 = { version = "0.13", features = ["arithmetic"] } k256 = { version = "0.13", features = ["arithmetic"] }
transcript = { package = "flexible-transcript", path = "../../crypto/transcript", version = "0.3", features = ["recommended"] } transcript = { package = "flexible-transcript", path = "../../crypto/transcript", version = "0.3", features = ["recommended"] }

View File

@@ -23,7 +23,7 @@ use frost::{
algorithm::{Hram as HramTrait, Algorithm, Schnorr as FrostSchnorr}, algorithm::{Hram as HramTrait, Algorithm, Schnorr as FrostSchnorr},
}; };
use bitcoin::XOnlyPublicKey; use bitcoin::key::XOnlyPublicKey;
/// Get the x coordinate of a non-infinity, even point. Panics on invalid input. /// Get the x coordinate of a non-infinity, even point. Panics on invalid input.
pub fn x(key: &ProjectivePoint) -> [u8; 32] { pub fn x(key: &ProjectivePoint) -> [u8; 32] {

View File

@@ -6,10 +6,7 @@ use serde::{Deserialize, de::DeserializeOwned};
use serde_json::json; use serde_json::json;
use bitcoin::{ use bitcoin::{
hashes::{ hashes::{Hash, hex::FromHex},
Hash,
hex::{FromHex, ToHex},
},
consensus::encode, consensus::encode,
Txid, Transaction, BlockHash, Block, Txid, Transaction, BlockHash, Block,
}; };
@@ -88,8 +85,11 @@ impl Rpc {
/// Get the hash of a block by the block's number. /// Get the hash of a block by the block's number.
pub async fn get_block_hash(&self, number: usize) -> Result<[u8; 32], RpcError> { pub async fn get_block_hash(&self, number: usize) -> Result<[u8; 32], RpcError> {
let mut hash = let mut hash = *self
self.rpc_call::<BlockHash>("getblockhash", json!([number])).await?.as_hash().into_inner(); .rpc_call::<BlockHash>("getblockhash", json!([number]))
.await?
.as_raw_hash()
.as_byte_array();
// bitcoin stores the inner bytes in reverse order. // bitcoin stores the inner bytes in reverse order.
hash.reverse(); hash.reverse();
Ok(hash) Ok(hash)
@@ -101,16 +101,16 @@ impl Rpc {
struct Number { struct Number {
height: usize, height: usize,
} }
Ok(self.rpc_call::<Number>("getblockheader", json!([hash.to_hex()])).await?.height) Ok(self.rpc_call::<Number>("getblockheader", json!([hex::encode(hash)])).await?.height)
} }
/// Get a block by its hash. /// Get a block by its hash.
pub async fn get_block(&self, hash: &[u8; 32]) -> Result<Block, RpcError> { pub async fn get_block(&self, hash: &[u8; 32]) -> Result<Block, RpcError> {
let hex = self.rpc_call::<String>("getblock", json!([hash.to_hex(), 0])).await?; let hex = self.rpc_call::<String>("getblock", json!([hex::encode(hash), 0])).await?;
let bytes: Vec<u8> = FromHex::from_hex(&hex).map_err(|_| RpcError::InvalidResponse)?; let bytes: Vec<u8> = FromHex::from_hex(&hex).map_err(|_| RpcError::InvalidResponse)?;
let block: Block = encode::deserialize(&bytes).map_err(|_| RpcError::InvalidResponse)?; let block: Block = encode::deserialize(&bytes).map_err(|_| RpcError::InvalidResponse)?;
let mut block_hash = block.block_hash().as_hash().into_inner(); let mut block_hash = *block.block_hash().as_raw_hash().as_byte_array();
block_hash.reverse(); block_hash.reverse();
if hash != &block_hash { if hash != &block_hash {
Err(RpcError::InvalidResponse)?; Err(RpcError::InvalidResponse)?;
@@ -130,11 +130,11 @@ impl Rpc {
/// Get a transaction by its hash. /// Get a transaction by its hash.
pub async fn get_transaction(&self, hash: &[u8; 32]) -> Result<Transaction, RpcError> { pub async fn get_transaction(&self, hash: &[u8; 32]) -> Result<Transaction, RpcError> {
let hex = self.rpc_call::<String>("getrawtransaction", json!([hash.to_hex()])).await?; let hex = self.rpc_call::<String>("getrawtransaction", json!([hex::encode(hash)])).await?;
let bytes: Vec<u8> = FromHex::from_hex(&hex).map_err(|_| RpcError::InvalidResponse)?; let bytes: Vec<u8> = FromHex::from_hex(&hex).map_err(|_| RpcError::InvalidResponse)?;
let tx: Transaction = encode::deserialize(&bytes).map_err(|_| RpcError::InvalidResponse)?; let tx: Transaction = encode::deserialize(&bytes).map_err(|_| RpcError::InvalidResponse)?;
let mut tx_hash = tx.txid().as_hash().into_inner(); let mut tx_hash = *tx.txid().as_raw_hash().as_byte_array();
tx_hash.reverse(); tx_hash.reverse();
if hash != &tx_hash { if hash != &tx_hash {
Err(RpcError::InvalidResponse)?; Err(RpcError::InvalidResponse)?;

View File

@@ -14,8 +14,8 @@ use frost::{
use bitcoin::{ use bitcoin::{
consensus::encode::{Decodable, serialize}, consensus::encode::{Decodable, serialize},
schnorr::TweakedPublicKey, key::TweakedPublicKey,
OutPoint, Script, TxOut, Transaction, Block, Network, Address, OutPoint, ScriptBuf, TxOut, Transaction, Block, Network, Address,
}; };
use crate::crypto::{x_only, make_even}; use crate::crypto::{x_only, make_even};
@@ -95,7 +95,7 @@ impl ReceivedOutput {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Scanner { pub struct Scanner {
key: ProjectivePoint, key: ProjectivePoint,
scripts: HashMap<Script, Scalar>, scripts: HashMap<ScriptBuf, Scalar>,
} }
impl Scanner { impl Scanner {

View File

@@ -13,9 +13,10 @@ use k256::{elliptic_curve::sec1::ToEncodedPoint, Scalar};
use frost::{curve::Secp256k1, Participant, ThresholdKeys, FrostError, sign::*}; use frost::{curve::Secp256k1, Participant, ThresholdKeys, FrostError, sign::*};
use bitcoin::{ use bitcoin::{
hashes::Hash, sighash::{TapSighashType, SighashCache, Prevouts},
util::sighash::{SchnorrSighashType, SighashCache, Prevouts}, absolute::LockTime,
OutPoint, Script, Sequence, Witness, TxIn, TxOut, PackedLockTime, Transaction, Network, Address, script::{PushBytesBuf, ScriptBuf},
OutPoint, Sequence, Witness, TxIn, TxOut, Transaction, Network, Address,
}; };
use crate::{ use crate::{
@@ -61,18 +62,18 @@ impl SignableTransaction {
// 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: 2,
lock_time: PackedLockTime::ZERO, lock_time: LockTime::ZERO,
input: vec![ input: vec![
TxIn { TxIn {
// This is a fixed size // This is a fixed size
// See https://developer.bitcoin.org/reference/transactions.html#raw-transaction-format // See https://developer.bitcoin.org/reference/transactions.html#raw-transaction-format
previous_output: OutPoint::default(), previous_output: OutPoint::default(),
// This is empty for a Taproot spend // This is empty for a Taproot spend
script_sig: Script::new(), script_sig: ScriptBuf::new(),
// This is fixed size, yet we do use Sequence::MAX // This is fixed size, yet we do use Sequence::MAX
sequence: Sequence::MAX, sequence: Sequence::MAX,
// Our witnesses contains a single 64-byte signature // Our witnesses contains a single 64-byte signature
witness: Witness::from_vec(vec![vec![0; 64]]) witness: Witness::from_slice(&[vec![0; 64]])
}; };
inputs inputs
], ],
@@ -137,7 +138,7 @@ impl SignableTransaction {
.iter() .iter()
.map(|input| TxIn { .map(|input| TxIn {
previous_output: input.outpoint, previous_output: input.outpoint,
script_sig: Script::new(), script_sig: ScriptBuf::new(),
sequence: Sequence::MAX, sequence: Sequence::MAX,
witness: Witness::new(), witness: Witness::new(),
}) })
@@ -151,7 +152,13 @@ impl SignableTransaction {
// Add the OP_RETURN output // Add the OP_RETURN output
if let Some(data) = data { if let Some(data) = data {
tx_outs.push(TxOut { value: 0, script_pubkey: Script::new_op_return(&data) }) tx_outs.push(TxOut {
value: 0,
script_pubkey: ScriptBuf::new_op_return(
&PushBytesBuf::try_from(data)
.expect("data didn't fit into PushBytes depsite being checked"),
),
})
} }
let mut weight = Self::calculate_weight(tx_ins.len(), payments, None); let mut weight = Self::calculate_weight(tx_ins.len(), payments, None);
@@ -182,12 +189,7 @@ impl SignableTransaction {
} }
Ok(SignableTransaction { Ok(SignableTransaction {
tx: Transaction { tx: Transaction { version: 2, lock_time: LockTime::ZERO, input: tx_ins, output: tx_outs },
version: 2,
lock_time: PackedLockTime::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,
@@ -208,7 +210,7 @@ impl SignableTransaction {
// Transcript the inputs and outputs // Transcript the inputs and outputs
let tx = &self.tx; let tx = &self.tx;
for input in &tx.input { for input in &tx.input {
transcript.append_message(b"input_hash", input.previous_output.txid.as_hash().into_inner()); transcript.append_message(b"input_hash", input.previous_output.txid);
transcript.append_message(b"input_output_index", input.previous_output.vout.to_le_bytes()); transcript.append_message(b"input_output_index", input.previous_output.vout.to_le_bytes());
} }
for payment in &tx.output { for payment in &tx.output {
@@ -335,9 +337,10 @@ impl SignMachine<Transaction> for TransactionSignMachine {
.map(|(i, sig)| { .map(|(i, sig)| {
let (sig, share) = sig.sign( let (sig, share) = sig.sign(
commitments[i].clone(), commitments[i].clone(),
&cache cache
.taproot_key_spend_signature_hash(i, &prevouts, SchnorrSighashType::Default) .taproot_key_spend_signature_hash(i, &prevouts, TapSighashType::Default)
.unwrap(), .unwrap()
.as_ref(),
)?; )?;
shares.push(share); shares.push(share);
Ok(sig) Ok(sig)
@@ -369,7 +372,7 @@ impl SignatureMachine<Transaction> for TransactionSignatureMachine {
shares.iter_mut().map(|(l, shares)| (*l, shares.remove(0))).collect::<HashMap<_, _>>(), shares.iter_mut().map(|(l, shares)| (*l, shares.remove(0))).collect::<HashMap<_, _>>(),
)?; )?;
let mut witness: Witness = Witness::new(); let mut witness = Witness::new();
witness.push(sig.as_ref()); witness.push(sig.as_ref());
input.witness = witness; input.witness = witness;
} }

View File

@@ -18,7 +18,7 @@ async_sequential! {
// Test get_block by checking the received block's hash matches the request // Test get_block by checking the received block's hash matches the request
let block = rpc.get_block(&hash).await.unwrap(); let block = rpc.get_block(&hash).await.unwrap();
// Hashes are stored in reverse. It's bs from Satoshi // Hashes are stored in reverse. It's bs from Satoshi
let mut block_hash = block.block_hash().as_hash().into_inner(); let mut block_hash = *block.block_hash().as_raw_hash().as_byte_array();
block_hash.reverse(); block_hash.reverse();
assert_eq!(hash, block_hash); assert_eq!(hash, block_hash);
} }

View File

@@ -20,11 +20,9 @@ use frost::{
use bitcoin_serai::{ use bitcoin_serai::{
bitcoin::{ bitcoin::{
hashes::Hash as HashTrait, hashes::Hash as HashTrait,
blockdata::{ blockdata::opcodes::all::OP_RETURN,
opcodes::all::OP_RETURN, script::{PushBytesBuf, Instruction, Instructions, Script},
script::{Instruction, Instructions}, OutPoint, TxOut, Transaction, Network, Address,
},
OutPoint, Script, TxOut, Transaction, Network, Address,
}, },
wallet::{tweak_keys, address, ReceivedOutput, Scanner, TransactionError, SignableTransaction}, wallet::{tweak_keys, address, ReceivedOutput, Scanner, TransactionError, SignableTransaction},
rpc::Rpc, rpc::Rpc,
@@ -54,7 +52,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::new(), Network::Regtest).unwrap()]), serde_json::json!([100, Address::p2sh(Script::empty(), Network::Regtest).unwrap()]),
) )
.await .await
.unwrap(); .unwrap();
@@ -306,7 +304,7 @@ async_sequential! {
// 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
// effectively test // effectively test
rpc.send_raw_transaction(&tx).await.unwrap(); rpc.send_raw_transaction(&tx).await.unwrap();
let mut hash = tx.txid().as_hash().into_inner(); let mut hash = *tx.txid().as_raw_hash().as_byte_array();
hash.reverse(); hash.reverse();
assert_eq!(tx, rpc.get_transaction(&hash).await.unwrap()); assert_eq!(tx, rpc.get_transaction(&hash).await.unwrap());
} }
@@ -338,7 +336,10 @@ async_sequential! {
assert!(tx.output[0].script_pubkey.is_op_return()); assert!(tx.output[0].script_pubkey.is_op_return());
let check = |mut instructions: Instructions| { let check = |mut instructions: Instructions| {
assert_eq!(instructions.next().unwrap().unwrap(), Instruction::Op(OP_RETURN)); assert_eq!(instructions.next().unwrap().unwrap(), Instruction::Op(OP_RETURN));
assert_eq!(instructions.next().unwrap().unwrap(), Instruction::PushBytes(&data)); assert_eq!(
instructions.next().unwrap().unwrap(),
Instruction::PushBytes(&PushBytesBuf::try_from(data.clone()).unwrap()),
);
assert!(instructions.next().is_none()); assert!(instructions.next().is_none());
}; };
check(tx.output[0].script_pubkey.instructions()); check(tx.output[0].script_pubkey.instructions());

View File

@@ -55,7 +55,7 @@ dalek-ff-group = { path = "../../crypto/dalek-ff-group", version = "0.3" }
monero-generators = { path = "generators", version = "0.3" } monero-generators = { path = "generators", version = "0.3" }
[dev-dependencies] [dev-dependencies]
hex-literal = "0.3" hex-literal = "0.4"
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
monero-rpc = "0.3" monero-rpc = "0.3"

View File

@@ -29,7 +29,7 @@ group = "0.13"
multiexp = { path = "../multiexp", version = "0.3", features = ["batch"], optional = true } multiexp = { path = "../multiexp", version = "0.3", features = ["batch"], optional = true }
[dev-dependencies] [dev-dependencies]
hex-literal = "0.3" hex-literal = "0.4"
blake2 = "0.10" blake2 = "0.10"

View File

@@ -38,7 +38,7 @@ transcript = { package = "flexible-transcript", path = "../crypto/transcript" }
frost = { package = "modular-frost", path = "../crypto/frost", features = ["ristretto"] } frost = { package = "modular-frost", path = "../crypto/frost", features = ["ristretto"] }
# Bitcoin # Bitcoin
secp256k1 = { version = "0.24", features = ["global-context", "rand-std"], optional = true } secp256k1 = { version = "0.27", features = ["global-context", "rand-std"], optional = true }
k256 = { version = "0.13", features = ["arithmetic"], optional = true } k256 = { version = "0.13", features = ["arithmetic"], optional = true }
bitcoin-serai = { path = "../coins/bitcoin", optional = true } bitcoin-serai = { path = "../coins/bitcoin", optional = true }

View File

@@ -16,10 +16,8 @@ use bitcoin_serai::{
bitcoin::{ bitcoin::{
hashes::Hash as HashTrait, hashes::Hash as HashTrait,
consensus::{Encodable, Decodable}, consensus::{Encodable, Decodable},
psbt::serialize::Serialize, script::Instruction,
OutPoint, OutPoint, Transaction, Block, Network,
blockdata::script::Instruction,
Transaction, Block, Network,
}, },
wallet::{ wallet::{
tweak_keys, address, ReceivedOutput, Scanner, TransactionError, tweak_keys, address, ReceivedOutput, Scanner, TransactionError,
@@ -31,9 +29,11 @@ use bitcoin_serai::{
#[cfg(test)] #[cfg(test)]
use bitcoin_serai::bitcoin::{ use bitcoin_serai::bitcoin::{
secp256k1::{SECP256K1, SecretKey, Message}, secp256k1::{SECP256K1, SecretKey, Message},
PrivateKey, PublicKey, EcdsaSighashType, PrivateKey, PublicKey,
blockdata::script::Builder, sighash::{EcdsaSighashType, SighashCache},
PackedLockTime, Sequence, Script, Witness, TxIn, TxOut, Address as BAddress, script::{PushBytesBuf, Builder},
absolute::LockTime,
Sequence, Script, Witness, TxIn, TxOut, Address as BAddress,
}; };
use serai_client::{ use serai_client::{
@@ -134,19 +134,21 @@ pub struct Fee(u64);
impl TransactionTrait<Bitcoin> for Transaction { impl TransactionTrait<Bitcoin> for Transaction {
type Id = [u8; 32]; type Id = [u8; 32];
fn id(&self) -> Self::Id { fn id(&self) -> Self::Id {
let mut hash = self.txid().as_hash().into_inner(); let mut hash = *self.txid().as_raw_hash().as_byte_array();
hash.reverse(); hash.reverse();
hash hash
} }
fn serialize(&self) -> Vec<u8> { fn serialize(&self) -> Vec<u8> {
Serialize::serialize(self) let mut buf = vec![];
self.consensus_encode(&mut buf).unwrap();
buf
} }
#[cfg(test)] #[cfg(test)]
async fn fee(&self, coin: &Bitcoin) -> u64 { async fn fee(&self, coin: &Bitcoin) -> u64 {
let mut value = 0; let mut value = 0;
for input in &self.input { for input in &self.input {
let output = input.previous_output; let output = input.previous_output;
let mut hash = output.txid.as_hash().into_inner(); let mut hash = *output.txid.as_raw_hash().as_byte_array();
hash.reverse(); hash.reverse();
value += coin.rpc.get_transaction(&hash).await.unwrap().output value += coin.rpc.get_transaction(&hash).await.unwrap().output
[usize::try_from(output.vout).unwrap()] [usize::try_from(output.vout).unwrap()]
@@ -191,7 +193,7 @@ impl Eq for SignableTransaction {}
impl BlockTrait<Bitcoin> for Block { impl BlockTrait<Bitcoin> for Block {
type Id = [u8; 32]; type Id = [u8; 32];
fn id(&self) -> Self::Id { fn id(&self) -> Self::Id {
let mut hash = self.block_hash().as_hash().into_inner(); let mut hash = *self.block_hash().as_raw_hash().as_byte_array();
hash.reverse(); hash.reverse();
hash hash
} }
@@ -350,7 +352,7 @@ impl Coin for Bitcoin {
for output in &tx.output { for output in &tx.output {
if output.script_pubkey.is_op_return() { if output.script_pubkey.is_op_return() {
match output.script_pubkey.instructions_minimal().last() { match output.script_pubkey.instructions_minimal().last() {
Some(Ok(Instruction::PushBytes(data))) => return data.to_vec(), Some(Ok(Instruction::PushBytes(data))) => return data.as_bytes().to_vec(),
_ => continue, _ => continue,
} }
} }
@@ -552,7 +554,7 @@ impl Coin for Bitcoin {
.rpc .rpc
.rpc_call::<Vec<String>>( .rpc_call::<Vec<String>>(
"generatetoaddress", "generatetoaddress",
serde_json::json!([1, BAddress::p2sh(&Script::new(), Network::Regtest).unwrap()]), serde_json::json!([1, BAddress::p2sh(Script::empty(), Network::Regtest).unwrap()]),
) )
.await .await
.unwrap(); .unwrap();
@@ -579,10 +581,10 @@ impl Coin 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: 2,
lock_time: PackedLockTime::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::default(), script_sig: Script::empty().into(),
sequence: Sequence(u32::MAX), sequence: Sequence(u32::MAX),
witness: Witness::default(), witness: Witness::default(),
}], }],
@@ -595,15 +597,20 @@ impl Coin for Bitcoin {
let mut der = SECP256K1 let mut der = SECP256K1
.sign_ecdsa_low_r( .sign_ecdsa_low_r(
&Message::from( &Message::from(
tx.signature_hash(0, &main_addr.script_pubkey(), EcdsaSighashType::All.to_u32()) SighashCache::new(&tx)
.as_hash(), .legacy_signature_hash(0, &main_addr.script_pubkey(), EcdsaSighashType::All.to_u32())
.unwrap()
.to_raw_hash(),
), ),
&private_key.inner, &private_key.inner,
) )
.serialize_der() .serialize_der()
.to_vec(); .to_vec();
der.push(1); der.push(1);
tx.input[0].script_sig = Builder::new().push_slice(&der).push_key(&public_key).into_script(); tx.input[0].script_sig = Builder::new()
.push_slice(PushBytesBuf::try_from(der).unwrap())
.push_key(&public_key)
.into_script();
let block = self.get_latest_block_number().await.unwrap() + 1; let block = self.get_latest_block_number().await.unwrap() + 1;
self.rpc.send_raw_transaction(&tx).await.unwrap(); self.rpc.send_raw_transaction(&tx).await.unwrap();

View File

@@ -164,7 +164,7 @@ impl<C: Coin, D: Db> KeyGen<C, D> {
let rng = |label, id: KeyGenId| { let rng = |label, id: KeyGenId| {
let mut transcript = RecommendedTranscript::new(label); let mut transcript = RecommendedTranscript::new(label);
transcript.append_message(b"entropy", self.entropy.as_ref()); transcript.append_message(b"entropy", &self.entropy);
transcript.append_message(b"context", context(&id)); transcript.append_message(b"context", context(&id));
ChaCha20Rng::from_seed(transcript.rng_seed(b"rng")) ChaCha20Rng::from_seed(transcript.rng_seed(b"rng"))
}; };

View File

@@ -178,17 +178,19 @@ async fn run<C: Coin, D: Db, Co: Coordinator>(raw_db: D, coin: C, mut coordinato
} }
let bytes = Zeroizing::new(hex::decode(entropy).expect("entropy wasn't hex-formatted")); let bytes = Zeroizing::new(hex::decode(entropy).expect("entropy wasn't hex-formatted"));
let mut entropy = Zeroizing::new([0; 32]); let mut entropy = Zeroizing::new([0; 32]);
entropy.as_mut().copy_from_slice(bytes.as_ref()); let entropy_mut: &mut [u8] = entropy.as_mut();
entropy_mut.copy_from_slice(bytes.as_ref());
let mut transcript = RecommendedTranscript::new(b"Serai Processor Entropy"); let mut transcript = RecommendedTranscript::new(b"Serai Processor Entropy");
transcript.append_message(b"entropy", entropy.as_ref()); transcript.append_message(b"entropy", entropy);
transcript transcript
}; };
let mut entropy = |label| { let mut entropy = |label| {
let mut challenge = entropy_transcript.challenge(label); let mut challenge = entropy_transcript.challenge(label);
let mut res = Zeroizing::new([0; 32]); let mut res = Zeroizing::new([0; 32]);
res.as_mut().copy_from_slice(&challenge[.. 32]); let res_mut: &mut [u8] = res.as_mut();
res_mut.copy_from_slice(&challenge[.. 32]);
challenge.zeroize(); challenge.zeroize();
res res
}; };

View File

@@ -23,7 +23,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.27", default-features = false, features = ["jsonrpsee-ws"], optional = true } subxt = { version = "0.27", default-features = false, features = ["jsonrpsee-ws"], optional = true }
bitcoin = { version = "0.29", optional = true } bitcoin = { version = "0.30", optional = true }
ciphersuite = { path = "../../crypto/ciphersuite", version = "0.3", optional = true } ciphersuite = { path = "../../crypto/ciphersuite", version = "0.3", 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 }

View File

@@ -6,16 +6,40 @@ use bitcoin::{
hashes::{Hash as HashTrait, hash160::Hash}, hashes::{Hash as HashTrait, hash160::Hash},
PubkeyHash, ScriptHash, PubkeyHash, ScriptHash,
network::constants::Network, network::constants::Network,
util::address::{Error, WitnessVersion, Payload, Address as BAddress}, address::{
Error, WitnessVersion, Payload, WitnessProgram, NetworkChecked, Address as BAddressGeneric,
},
}; };
#[derive(Clone, PartialEq, Eq, Debug)] type BAddress = BAddressGeneric<NetworkChecked>;
#[derive(Clone, Eq, Debug)]
pub struct Address(pub BAddress); pub struct Address(pub BAddress);
impl PartialEq for Address {
fn eq(&self, other: &Self) -> bool {
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> {
BAddress::from_str(str).map(Address) let mut original_address = BAddressGeneric::from_str(str)?;
// Standardize the network
original_address.network = Network::Bitcoin;
let address = original_address
.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))
} }
} }
@@ -38,26 +62,26 @@ enum EncodedAddress {
impl TryFrom<Vec<u8>> for Address { impl TryFrom<Vec<u8>> for Address {
type Error = (); type Error = ();
fn try_from(data: Vec<u8>) -> Result<Address, ()> { fn try_from(data: Vec<u8>) -> Result<Address, ()> {
Ok(Address(BAddress { Ok(Address(BAddress::new(
network: Network::Bitcoin, Network::Bitcoin,
payload: match EncodedAddress::decode(&mut data.as_ref()).map_err(|_| ())? { match EncodedAddress::decode(&mut data.as_ref()).map_err(|_| ())? {
EncodedAddress::P2PKH(hash) => { EncodedAddress::P2PKH(hash) => {
Payload::PubkeyHash(PubkeyHash::from_hash(Hash::from_inner(hash))) Payload::PubkeyHash(PubkeyHash::from_raw_hash(Hash::from_byte_array(hash)))
} }
EncodedAddress::P2SH(hash) => { EncodedAddress::P2SH(hash) => {
Payload::ScriptHash(ScriptHash::from_hash(Hash::from_inner(hash))) Payload::ScriptHash(ScriptHash::from_raw_hash(Hash::from_byte_array(hash)))
} }
EncodedAddress::P2WPKH(hash) => { EncodedAddress::P2WPKH(hash) => {
Payload::WitnessProgram { version: WitnessVersion::V0, program: hash.to_vec() } Payload::WitnessProgram(WitnessProgram::new(WitnessVersion::V0, hash).unwrap())
} }
EncodedAddress::P2WSH(hash) => { EncodedAddress::P2WSH(hash) => {
Payload::WitnessProgram { version: WitnessVersion::V0, program: hash.to_vec() } Payload::WitnessProgram(WitnessProgram::new(WitnessVersion::V0, hash).unwrap())
} }
EncodedAddress::P2TR(key) => { EncodedAddress::P2TR(key) => {
Payload::WitnessProgram { version: WitnessVersion::V1, program: key.to_vec() } Payload::WitnessProgram(WitnessProgram::new(WitnessVersion::V1, key).unwrap())
} }
}, },
})) )))
} }
} }
@@ -67,20 +91,29 @@ impl TryInto<Vec<u8>> for Address {
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_hash().into_inner()), Payload::PubkeyHash(hash) => EncodedAddress::P2PKH(*hash.as_raw_hash().as_byte_array()),
Payload::ScriptHash(hash) => EncodedAddress::P2SH(hash.as_hash().into_inner()), Payload::ScriptHash(hash) => EncodedAddress::P2SH(*hash.as_raw_hash().as_byte_array()),
Payload::WitnessProgram { version: WitnessVersion::V0, program } => { Payload::WitnessProgram(program) => match program.version() {
if program.len() == 20 { WitnessVersion::V0 => {
EncodedAddress::P2WPKH(program.try_into().map_err(|_| ())?) let program = program.program();
} else if program.len() == 32 { if program.len() == 20 {
EncodedAddress::P2WSH(program.try_into().map_err(|_| ())?) let mut buf = [0; 20];
} else { buf.copy_from_slice(program.as_ref());
Err(())? EncodedAddress::P2WPKH(buf)
} else if program.len() == 32 {
let mut buf = [0; 32];
buf.copy_from_slice(program.as_ref());
EncodedAddress::P2WSH(buf)
} else {
Err(())?
}
} }
} WitnessVersion::V1 => {
Payload::WitnessProgram { version: WitnessVersion::V1, program } => { let program_ref: &[u8] = program.program().as_ref();
EncodedAddress::P2TR(program.try_into().map_err(|_| ())?) EncodedAddress::P2TR(program_ref.try_into().map_err(|_| ())?)
} }
_ => Err(())?,
},
_ => Err(())?, _ => Err(())?,
}) })
.encode(), .encode(),

View File

@@ -68,8 +68,8 @@ impl TryFrom<Vec<u8>> for Address {
} }
}, },
), ),
Ed25519::read_G(&mut addr.spend.as_ref()).map_err(|_| ())?.0, Ed25519::read_G::<&[u8]>(&mut addr.spend.as_ref()).map_err(|_| ())?.0,
Ed25519::read_G(&mut addr.view.as_ref()).map_err(|_| ())?.0, Ed25519::read_G::<&[u8]>(&mut addr.view.as_ref()).map_err(|_| ())?.0,
))) )))
} }
} }