mirror of
https://github.com/serai-dex/serai.git
synced 2024-11-16 17:07:35 +00:00
Update to bitcoin 0.30
Also performs a general update with a variety of upgraded Substrate depends.
This commit is contained in:
parent
96525330c2
commit
f6206b60ec
17 changed files with 387 additions and 284 deletions
429
Cargo.lock
generated
429
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -16,8 +16,8 @@ rand_core = "0.6"
|
|||
|
||||
sha2 = "0.10"
|
||||
|
||||
secp256k1 = { version = "0.24", features = ["global-context"] }
|
||||
bitcoin = { version = "0.29", features = ["serde"] }
|
||||
secp256k1 = { version = "0.27", features = ["global-context"] }
|
||||
bitcoin = { version = "0.30", features = ["serde"] }
|
||||
|
||||
k256 = { version = "0.13", features = ["arithmetic"] }
|
||||
transcript = { package = "flexible-transcript", path = "../../crypto/transcript", version = "0.3", features = ["recommended"] }
|
||||
|
|
|
@ -23,7 +23,7 @@ use frost::{
|
|||
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.
|
||||
pub fn x(key: &ProjectivePoint) -> [u8; 32] {
|
||||
|
|
|
@ -6,10 +6,7 @@ use serde::{Deserialize, de::DeserializeOwned};
|
|||
use serde_json::json;
|
||||
|
||||
use bitcoin::{
|
||||
hashes::{
|
||||
Hash,
|
||||
hex::{FromHex, ToHex},
|
||||
},
|
||||
hashes::{Hash, hex::FromHex},
|
||||
consensus::encode,
|
||||
Txid, Transaction, BlockHash, Block,
|
||||
};
|
||||
|
@ -88,8 +85,11 @@ impl Rpc {
|
|||
|
||||
/// Get the hash of a block by the block's number.
|
||||
pub async fn get_block_hash(&self, number: usize) -> Result<[u8; 32], RpcError> {
|
||||
let mut hash =
|
||||
self.rpc_call::<BlockHash>("getblockhash", json!([number])).await?.as_hash().into_inner();
|
||||
let mut hash = *self
|
||||
.rpc_call::<BlockHash>("getblockhash", json!([number]))
|
||||
.await?
|
||||
.as_raw_hash()
|
||||
.as_byte_array();
|
||||
// bitcoin stores the inner bytes in reverse order.
|
||||
hash.reverse();
|
||||
Ok(hash)
|
||||
|
@ -101,16 +101,16 @@ impl Rpc {
|
|||
struct Number {
|
||||
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.
|
||||
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 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();
|
||||
if hash != &block_hash {
|
||||
Err(RpcError::InvalidResponse)?;
|
||||
|
@ -130,11 +130,11 @@ impl Rpc {
|
|||
|
||||
/// Get a transaction by its hash.
|
||||
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 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();
|
||||
if hash != &tx_hash {
|
||||
Err(RpcError::InvalidResponse)?;
|
||||
|
|
|
@ -14,8 +14,8 @@ use frost::{
|
|||
|
||||
use bitcoin::{
|
||||
consensus::encode::{Decodable, serialize},
|
||||
schnorr::TweakedPublicKey,
|
||||
OutPoint, Script, TxOut, Transaction, Block, Network, Address,
|
||||
key::TweakedPublicKey,
|
||||
OutPoint, ScriptBuf, TxOut, Transaction, Block, Network, Address,
|
||||
};
|
||||
|
||||
use crate::crypto::{x_only, make_even};
|
||||
|
@ -95,7 +95,7 @@ impl ReceivedOutput {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct Scanner {
|
||||
key: ProjectivePoint,
|
||||
scripts: HashMap<Script, Scalar>,
|
||||
scripts: HashMap<ScriptBuf, Scalar>,
|
||||
}
|
||||
|
||||
impl Scanner {
|
||||
|
|
|
@ -13,9 +13,10 @@ use k256::{elliptic_curve::sec1::ToEncodedPoint, Scalar};
|
|||
use frost::{curve::Secp256k1, Participant, ThresholdKeys, FrostError, sign::*};
|
||||
|
||||
use bitcoin::{
|
||||
hashes::Hash,
|
||||
util::sighash::{SchnorrSighashType, SighashCache, Prevouts},
|
||||
OutPoint, Script, Sequence, Witness, TxIn, TxOut, PackedLockTime, Transaction, Network, Address,
|
||||
sighash::{TapSighashType, SighashCache, Prevouts},
|
||||
absolute::LockTime,
|
||||
script::{PushBytesBuf, ScriptBuf},
|
||||
OutPoint, Sequence, Witness, TxIn, TxOut, Transaction, Network, Address,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -61,18 +62,18 @@ impl SignableTransaction {
|
|||
// Expand this a full transaction in order to use the bitcoin library's weight function
|
||||
let mut tx = Transaction {
|
||||
version: 2,
|
||||
lock_time: PackedLockTime::ZERO,
|
||||
lock_time: LockTime::ZERO,
|
||||
input: vec![
|
||||
TxIn {
|
||||
// This is a fixed size
|
||||
// See https://developer.bitcoin.org/reference/transactions.html#raw-transaction-format
|
||||
previous_output: OutPoint::default(),
|
||||
// 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
|
||||
sequence: Sequence::MAX,
|
||||
// Our witnesses contains a single 64-byte signature
|
||||
witness: Witness::from_vec(vec![vec![0; 64]])
|
||||
witness: Witness::from_slice(&[vec![0; 64]])
|
||||
};
|
||||
inputs
|
||||
],
|
||||
|
@ -137,7 +138,7 @@ impl SignableTransaction {
|
|||
.iter()
|
||||
.map(|input| TxIn {
|
||||
previous_output: input.outpoint,
|
||||
script_sig: Script::new(),
|
||||
script_sig: ScriptBuf::new(),
|
||||
sequence: Sequence::MAX,
|
||||
witness: Witness::new(),
|
||||
})
|
||||
|
@ -151,7 +152,13 @@ impl SignableTransaction {
|
|||
|
||||
// Add the OP_RETURN output
|
||||
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);
|
||||
|
@ -182,12 +189,7 @@ impl SignableTransaction {
|
|||
}
|
||||
|
||||
Ok(SignableTransaction {
|
||||
tx: Transaction {
|
||||
version: 2,
|
||||
lock_time: PackedLockTime::ZERO,
|
||||
input: tx_ins,
|
||||
output: tx_outs,
|
||||
},
|
||||
tx: Transaction { version: 2, lock_time: LockTime::ZERO, input: tx_ins, output: tx_outs },
|
||||
offsets,
|
||||
prevouts: inputs.drain(..).map(|input| input.output).collect(),
|
||||
needed_fee,
|
||||
|
@ -208,7 +210,7 @@ impl SignableTransaction {
|
|||
// Transcript the inputs and outputs
|
||||
let tx = &self.tx;
|
||||
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());
|
||||
}
|
||||
for payment in &tx.output {
|
||||
|
@ -335,9 +337,10 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
|||
.map(|(i, sig)| {
|
||||
let (sig, share) = sig.sign(
|
||||
commitments[i].clone(),
|
||||
&cache
|
||||
.taproot_key_spend_signature_hash(i, &prevouts, SchnorrSighashType::Default)
|
||||
.unwrap(),
|
||||
cache
|
||||
.taproot_key_spend_signature_hash(i, &prevouts, TapSighashType::Default)
|
||||
.unwrap()
|
||||
.as_ref(),
|
||||
)?;
|
||||
shares.push(share);
|
||||
Ok(sig)
|
||||
|
@ -369,7 +372,7 @@ impl SignatureMachine<Transaction> for TransactionSignatureMachine {
|
|||
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());
|
||||
input.witness = witness;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ async_sequential! {
|
|||
// Test get_block by checking the received block's hash matches the request
|
||||
let block = rpc.get_block(&hash).await.unwrap();
|
||||
// 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();
|
||||
assert_eq!(hash, block_hash);
|
||||
}
|
||||
|
|
|
@ -20,11 +20,9 @@ use frost::{
|
|||
use bitcoin_serai::{
|
||||
bitcoin::{
|
||||
hashes::Hash as HashTrait,
|
||||
blockdata::{
|
||||
opcodes::all::OP_RETURN,
|
||||
script::{Instruction, Instructions},
|
||||
},
|
||||
OutPoint, Script, TxOut, Transaction, Network, Address,
|
||||
blockdata::opcodes::all::OP_RETURN,
|
||||
script::{PushBytesBuf, Instruction, Instructions, Script},
|
||||
OutPoint, TxOut, Transaction, Network, Address,
|
||||
},
|
||||
wallet::{tweak_keys, address, ReceivedOutput, Scanner, TransactionError, SignableTransaction},
|
||||
rpc::Rpc,
|
||||
|
@ -54,7 +52,7 @@ async fn send_and_get_output(rpc: &Rpc, scanner: &Scanner, key: ProjectivePoint)
|
|||
rpc
|
||||
.rpc_call::<Vec<String>>(
|
||||
"generatetoaddress",
|
||||
serde_json::json!([100, Address::p2sh(&Script::new(), Network::Regtest).unwrap()]),
|
||||
serde_json::json!([100, Address::p2sh(Script::empty(), Network::Regtest).unwrap()]),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -306,7 +304,7 @@ async_sequential! {
|
|||
// This also tests send_raw_transaction and get_transaction, which the RPC test can't
|
||||
// effectively test
|
||||
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();
|
||||
assert_eq!(tx, rpc.get_transaction(&hash).await.unwrap());
|
||||
}
|
||||
|
@ -338,7 +336,10 @@ async_sequential! {
|
|||
assert!(tx.output[0].script_pubkey.is_op_return());
|
||||
let check = |mut instructions: Instructions| {
|
||||
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());
|
||||
};
|
||||
check(tx.output[0].script_pubkey.instructions());
|
||||
|
|
|
@ -55,7 +55,7 @@ dalek-ff-group = { path = "../../crypto/dalek-ff-group", version = "0.3" }
|
|||
monero-generators = { path = "generators", version = "0.3" }
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.3"
|
||||
hex-literal = "0.4"
|
||||
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
monero-rpc = "0.3"
|
||||
|
|
|
@ -29,7 +29,7 @@ group = "0.13"
|
|||
multiexp = { path = "../multiexp", version = "0.3", features = ["batch"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.3"
|
||||
hex-literal = "0.4"
|
||||
|
||||
blake2 = "0.10"
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ transcript = { package = "flexible-transcript", path = "../crypto/transcript" }
|
|||
frost = { package = "modular-frost", path = "../crypto/frost", features = ["ristretto"] }
|
||||
|
||||
# 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 }
|
||||
bitcoin-serai = { path = "../coins/bitcoin", optional = true }
|
||||
|
||||
|
|
|
@ -16,10 +16,8 @@ use bitcoin_serai::{
|
|||
bitcoin::{
|
||||
hashes::Hash as HashTrait,
|
||||
consensus::{Encodable, Decodable},
|
||||
psbt::serialize::Serialize,
|
||||
OutPoint,
|
||||
blockdata::script::Instruction,
|
||||
Transaction, Block, Network,
|
||||
script::Instruction,
|
||||
OutPoint, Transaction, Block, Network,
|
||||
},
|
||||
wallet::{
|
||||
tweak_keys, address, ReceivedOutput, Scanner, TransactionError,
|
||||
|
@ -31,9 +29,11 @@ use bitcoin_serai::{
|
|||
#[cfg(test)]
|
||||
use bitcoin_serai::bitcoin::{
|
||||
secp256k1::{SECP256K1, SecretKey, Message},
|
||||
PrivateKey, PublicKey, EcdsaSighashType,
|
||||
blockdata::script::Builder,
|
||||
PackedLockTime, Sequence, Script, Witness, TxIn, TxOut, Address as BAddress,
|
||||
PrivateKey, PublicKey,
|
||||
sighash::{EcdsaSighashType, SighashCache},
|
||||
script::{PushBytesBuf, Builder},
|
||||
absolute::LockTime,
|
||||
Sequence, Script, Witness, TxIn, TxOut, Address as BAddress,
|
||||
};
|
||||
|
||||
use serai_client::{
|
||||
|
@ -134,19 +134,21 @@ pub struct Fee(u64);
|
|||
impl TransactionTrait<Bitcoin> for Transaction {
|
||||
type Id = [u8; 32];
|
||||
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
|
||||
}
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
Serialize::serialize(self)
|
||||
let mut buf = vec![];
|
||||
self.consensus_encode(&mut buf).unwrap();
|
||||
buf
|
||||
}
|
||||
#[cfg(test)]
|
||||
async fn fee(&self, coin: &Bitcoin) -> u64 {
|
||||
let mut value = 0;
|
||||
for input in &self.input {
|
||||
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();
|
||||
value += coin.rpc.get_transaction(&hash).await.unwrap().output
|
||||
[usize::try_from(output.vout).unwrap()]
|
||||
|
@ -191,7 +193,7 @@ impl Eq for SignableTransaction {}
|
|||
impl BlockTrait<Bitcoin> for Block {
|
||||
type Id = [u8; 32];
|
||||
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
|
||||
}
|
||||
|
@ -350,7 +352,7 @@ impl Coin for Bitcoin {
|
|||
for output in &tx.output {
|
||||
if output.script_pubkey.is_op_return() {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
@ -552,7 +554,7 @@ impl Coin for Bitcoin {
|
|||
.rpc
|
||||
.rpc_call::<Vec<String>>(
|
||||
"generatetoaddress",
|
||||
serde_json::json!([1, BAddress::p2sh(&Script::new(), Network::Regtest).unwrap()]),
|
||||
serde_json::json!([1, BAddress::p2sh(Script::empty(), Network::Regtest).unwrap()]),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -579,10 +581,10 @@ impl Coin for Bitcoin {
|
|||
let tx = self.get_block(new_block).await.unwrap().txdata.swap_remove(0);
|
||||
let mut tx = Transaction {
|
||||
version: 2,
|
||||
lock_time: PackedLockTime::ZERO,
|
||||
lock_time: LockTime::ZERO,
|
||||
input: vec![TxIn {
|
||||
previous_output: OutPoint { txid: tx.txid(), vout: 0 },
|
||||
script_sig: Script::default(),
|
||||
script_sig: Script::empty().into(),
|
||||
sequence: Sequence(u32::MAX),
|
||||
witness: Witness::default(),
|
||||
}],
|
||||
|
@ -595,15 +597,20 @@ impl Coin for Bitcoin {
|
|||
let mut der = SECP256K1
|
||||
.sign_ecdsa_low_r(
|
||||
&Message::from(
|
||||
tx.signature_hash(0, &main_addr.script_pubkey(), EcdsaSighashType::All.to_u32())
|
||||
.as_hash(),
|
||||
SighashCache::new(&tx)
|
||||
.legacy_signature_hash(0, &main_addr.script_pubkey(), EcdsaSighashType::All.to_u32())
|
||||
.unwrap()
|
||||
.to_raw_hash(),
|
||||
),
|
||||
&private_key.inner,
|
||||
)
|
||||
.serialize_der()
|
||||
.to_vec();
|
||||
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;
|
||||
self.rpc.send_raw_transaction(&tx).await.unwrap();
|
||||
|
|
|
@ -164,7 +164,7 @@ impl<C: Coin, D: Db> KeyGen<C, D> {
|
|||
|
||||
let rng = |label, id: KeyGenId| {
|
||||
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));
|
||||
ChaCha20Rng::from_seed(transcript.rng_seed(b"rng"))
|
||||
};
|
||||
|
|
|
@ -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 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");
|
||||
transcript.append_message(b"entropy", entropy.as_ref());
|
||||
transcript.append_message(b"entropy", entropy);
|
||||
transcript
|
||||
};
|
||||
|
||||
let mut entropy = |label| {
|
||||
let mut challenge = entropy_transcript.challenge(label);
|
||||
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();
|
||||
res
|
||||
};
|
||||
|
|
|
@ -23,7 +23,7 @@ serai-runtime = { path = "../runtime", version = "0.1" }
|
|||
sp-core = { git = "https://github.com/serai-dex/substrate" }
|
||||
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 }
|
||||
monero-serai = { path = "../../coins/monero", version = "0.1.4-alpha", optional = true }
|
||||
|
|
|
@ -6,16 +6,40 @@ use bitcoin::{
|
|||
hashes::{Hash as HashTrait, hash160::Hash},
|
||||
PubkeyHash, ScriptHash,
|
||||
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);
|
||||
|
||||
impl PartialEq for Address {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.payload == other.0.payload
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Address {
|
||||
type Err = 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 {
|
||||
type Error = ();
|
||||
fn try_from(data: Vec<u8>) -> Result<Address, ()> {
|
||||
Ok(Address(BAddress {
|
||||
network: Network::Bitcoin,
|
||||
payload: match EncodedAddress::decode(&mut data.as_ref()).map_err(|_| ())? {
|
||||
Ok(Address(BAddress::new(
|
||||
Network::Bitcoin,
|
||||
match EncodedAddress::decode(&mut data.as_ref()).map_err(|_| ())? {
|
||||
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) => {
|
||||
Payload::ScriptHash(ScriptHash::from_hash(Hash::from_inner(hash)))
|
||||
Payload::ScriptHash(ScriptHash::from_raw_hash(Hash::from_byte_array(hash)))
|
||||
}
|
||||
EncodedAddress::P2WPKH(hash) => {
|
||||
Payload::WitnessProgram { version: WitnessVersion::V0, program: hash.to_vec() }
|
||||
Payload::WitnessProgram(WitnessProgram::new(WitnessVersion::V0, hash).unwrap())
|
||||
}
|
||||
EncodedAddress::P2WSH(hash) => {
|
||||
Payload::WitnessProgram { version: WitnessVersion::V0, program: hash.to_vec() }
|
||||
Payload::WitnessProgram(WitnessProgram::new(WitnessVersion::V0, hash).unwrap())
|
||||
}
|
||||
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>, ()> {
|
||||
Ok(
|
||||
(match self.0.payload {
|
||||
Payload::PubkeyHash(hash) => EncodedAddress::P2PKH(hash.as_hash().into_inner()),
|
||||
Payload::ScriptHash(hash) => EncodedAddress::P2SH(hash.as_hash().into_inner()),
|
||||
Payload::WitnessProgram { version: WitnessVersion::V0, program } => {
|
||||
if program.len() == 20 {
|
||||
EncodedAddress::P2WPKH(program.try_into().map_err(|_| ())?)
|
||||
} else if program.len() == 32 {
|
||||
EncodedAddress::P2WSH(program.try_into().map_err(|_| ())?)
|
||||
} else {
|
||||
Err(())?
|
||||
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::WitnessProgram(program) => match program.version() {
|
||||
WitnessVersion::V0 => {
|
||||
let program = program.program();
|
||||
if program.len() == 20 {
|
||||
let mut buf = [0; 20];
|
||||
buf.copy_from_slice(program.as_ref());
|
||||
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(())?
|
||||
}
|
||||
}
|
||||
}
|
||||
Payload::WitnessProgram { version: WitnessVersion::V1, program } => {
|
||||
EncodedAddress::P2TR(program.try_into().map_err(|_| ())?)
|
||||
}
|
||||
WitnessVersion::V1 => {
|
||||
let program_ref: &[u8] = program.program().as_ref();
|
||||
EncodedAddress::P2TR(program_ref.try_into().map_err(|_| ())?)
|
||||
}
|
||||
_ => Err(())?,
|
||||
},
|
||||
_ => Err(())?,
|
||||
})
|
||||
.encode(),
|
||||
|
|
|
@ -68,8 +68,8 @@ impl TryFrom<Vec<u8>> for Address {
|
|||
}
|
||||
},
|
||||
),
|
||||
Ed25519::read_G(&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.spend.as_ref()).map_err(|_| ())?.0,
|
||||
Ed25519::read_G::<&[u8]>(&mut addr.view.as_ref()).map_err(|_| ())?.0,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue