mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Implement Featured Addresses
Closes https://github.com/serai-dex/serai/issues/37.
This commit is contained in:
@@ -1,6 +1,13 @@
|
|||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
|
|
||||||
use crate::wallet::address::{Network, AddressType, Address};
|
use rand_core::{RngCore, OsRng};
|
||||||
|
|
||||||
|
use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, edwards::CompressedEdwardsY};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
random_scalar,
|
||||||
|
wallet::address::{Network, AddressType, AddressMeta, Address},
|
||||||
|
};
|
||||||
|
|
||||||
const SPEND: [u8; 32] = hex!("f8631661f6ab4e6fda310c797330d86e23a682f20d5bc8cc27b18051191f16d7");
|
const SPEND: [u8; 32] = hex!("f8631661f6ab4e6fda310c797330d86e23a682f20d5bc8cc27b18051191f16d7");
|
||||||
const VIEW: [u8; 32] = hex!("4a1535063ad1fee2dabbf909d4fd9a873e29541b401f0944754e17c9a41820ce");
|
const VIEW: [u8; 32] = hex!("4a1535063ad1fee2dabbf909d4fd9a873e29541b401f0944754e17c9a41820ce");
|
||||||
@@ -19,14 +26,19 @@ const SUB_VIEW: [u8; 32] = hex!("9bc2b464de90d058468522098d5610c5019c45fd1711a95
|
|||||||
const SUBADDRESS: &'static str =
|
const SUBADDRESS: &'static str =
|
||||||
"8C5zHM5ud8nGC4hC2ULiBLSWx9infi8JUUmWEat4fcTf8J4H38iWYVdFmPCA9UmfLTZxD43RsyKnGEdZkoGij6csDeUnbEB";
|
"8C5zHM5ud8nGC4hC2ULiBLSWx9infi8JUUmWEat4fcTf8J4H38iWYVdFmPCA9UmfLTZxD43RsyKnGEdZkoGij6csDeUnbEB";
|
||||||
|
|
||||||
|
const FEATURED_JSON: &'static str = include_str!("featured_addresses.json");
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn standard_address() {
|
fn standard_address() {
|
||||||
let addr = Address::from_str(STANDARD, Network::Mainnet).unwrap();
|
let addr = Address::from_str(STANDARD, Network::Mainnet).unwrap();
|
||||||
assert_eq!(addr.meta.network, Network::Mainnet);
|
assert_eq!(addr.meta.network, Network::Mainnet);
|
||||||
assert_eq!(addr.meta.kind, AddressType::Standard);
|
assert_eq!(addr.meta.kind, AddressType::Standard);
|
||||||
assert_eq!(addr.meta.guaranteed, false);
|
assert_eq!(addr.meta.kind.subaddress(), false);
|
||||||
|
assert_eq!(addr.meta.kind.payment_id(), None);
|
||||||
|
assert_eq!(addr.meta.kind.guaranteed(), false);
|
||||||
assert_eq!(addr.spend.compress().to_bytes(), SPEND);
|
assert_eq!(addr.spend.compress().to_bytes(), SPEND);
|
||||||
assert_eq!(addr.view.compress().to_bytes(), VIEW);
|
assert_eq!(addr.view.compress().to_bytes(), VIEW);
|
||||||
|
assert_eq!(addr.to_string(), STANDARD);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -34,9 +46,12 @@ fn integrated_address() {
|
|||||||
let addr = Address::from_str(INTEGRATED, Network::Mainnet).unwrap();
|
let addr = Address::from_str(INTEGRATED, Network::Mainnet).unwrap();
|
||||||
assert_eq!(addr.meta.network, Network::Mainnet);
|
assert_eq!(addr.meta.network, Network::Mainnet);
|
||||||
assert_eq!(addr.meta.kind, AddressType::Integrated(PAYMENT_ID));
|
assert_eq!(addr.meta.kind, AddressType::Integrated(PAYMENT_ID));
|
||||||
assert_eq!(addr.meta.guaranteed, false);
|
assert_eq!(addr.meta.kind.subaddress(), false);
|
||||||
|
assert_eq!(addr.meta.kind.payment_id(), Some(PAYMENT_ID));
|
||||||
|
assert_eq!(addr.meta.kind.guaranteed(), false);
|
||||||
assert_eq!(addr.spend.compress().to_bytes(), SPEND);
|
assert_eq!(addr.spend.compress().to_bytes(), SPEND);
|
||||||
assert_eq!(addr.view.compress().to_bytes(), VIEW);
|
assert_eq!(addr.view.compress().to_bytes(), VIEW);
|
||||||
|
assert_eq!(addr.to_string(), INTEGRATED);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -44,7 +59,113 @@ fn subaddress() {
|
|||||||
let addr = Address::from_str(SUBADDRESS, Network::Mainnet).unwrap();
|
let addr = Address::from_str(SUBADDRESS, Network::Mainnet).unwrap();
|
||||||
assert_eq!(addr.meta.network, Network::Mainnet);
|
assert_eq!(addr.meta.network, Network::Mainnet);
|
||||||
assert_eq!(addr.meta.kind, AddressType::Subaddress);
|
assert_eq!(addr.meta.kind, AddressType::Subaddress);
|
||||||
assert_eq!(addr.meta.guaranteed, false);
|
assert_eq!(addr.meta.kind.subaddress(), true);
|
||||||
|
assert_eq!(addr.meta.kind.payment_id(), None);
|
||||||
|
assert_eq!(addr.meta.kind.guaranteed(), false);
|
||||||
assert_eq!(addr.spend.compress().to_bytes(), SUB_SPEND);
|
assert_eq!(addr.spend.compress().to_bytes(), SUB_SPEND);
|
||||||
assert_eq!(addr.view.compress().to_bytes(), SUB_VIEW);
|
assert_eq!(addr.view.compress().to_bytes(), SUB_VIEW);
|
||||||
|
assert_eq!(addr.to_string(), SUBADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn featured() {
|
||||||
|
for (network, first) in
|
||||||
|
[(Network::Mainnet, 'C'), (Network::Testnet, 'K'), (Network::Stagenet, 'F')]
|
||||||
|
{
|
||||||
|
for _ in 0 .. 100 {
|
||||||
|
let spend = &random_scalar(&mut OsRng) * &ED25519_BASEPOINT_TABLE;
|
||||||
|
let view = &random_scalar(&mut OsRng) * &ED25519_BASEPOINT_TABLE;
|
||||||
|
|
||||||
|
for features in 0 .. (1 << 3) {
|
||||||
|
const SUBADDRESS_FEATURE_BIT: u8 = 1;
|
||||||
|
const INTEGRATED_FEATURE_BIT: u8 = 1 << 1;
|
||||||
|
const GUARANTEED_FEATURE_BIT: u8 = 1 << 2;
|
||||||
|
|
||||||
|
let subaddress = (features & SUBADDRESS_FEATURE_BIT) == SUBADDRESS_FEATURE_BIT;
|
||||||
|
|
||||||
|
let mut id = [0; 8];
|
||||||
|
OsRng.fill_bytes(&mut id);
|
||||||
|
let id = Some(id).filter(|_| (features & INTEGRATED_FEATURE_BIT) == INTEGRATED_FEATURE_BIT);
|
||||||
|
|
||||||
|
let guaranteed = (features & GUARANTEED_FEATURE_BIT) == GUARANTEED_FEATURE_BIT;
|
||||||
|
|
||||||
|
let kind = AddressType::Featured(subaddress, id, guaranteed);
|
||||||
|
let meta = AddressMeta { network, kind };
|
||||||
|
let addr = Address::new(meta, spend, view);
|
||||||
|
|
||||||
|
assert_eq!(addr.to_string().chars().next().unwrap(), first);
|
||||||
|
assert_eq!(Address::from_str(&addr.to_string(), network).unwrap(), addr);
|
||||||
|
|
||||||
|
assert_eq!(addr.spend, spend);
|
||||||
|
assert_eq!(addr.view, view);
|
||||||
|
|
||||||
|
assert_eq!(addr.subaddress(), subaddress);
|
||||||
|
assert_eq!(addr.payment_id(), id);
|
||||||
|
assert_eq!(addr.guaranteed(), guaranteed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn featured_vectors() {
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct Vector {
|
||||||
|
address: String,
|
||||||
|
|
||||||
|
network: String,
|
||||||
|
spend: String,
|
||||||
|
view: String,
|
||||||
|
|
||||||
|
subaddress: bool,
|
||||||
|
integrated: bool,
|
||||||
|
payment_id: Option<[u8; 8]>,
|
||||||
|
guaranteed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
let vectors = serde_json::from_str::<Vec<Vector>>(FEATURED_JSON).unwrap();
|
||||||
|
for vector in vectors {
|
||||||
|
let first = vector.address.chars().next().unwrap();
|
||||||
|
let network = match vector.network.as_str() {
|
||||||
|
"Mainnet" => {
|
||||||
|
assert_eq!(first, 'C');
|
||||||
|
Network::Mainnet
|
||||||
|
}
|
||||||
|
"Testnet" => {
|
||||||
|
assert_eq!(first, 'K');
|
||||||
|
Network::Testnet
|
||||||
|
}
|
||||||
|
"Stagenet" => {
|
||||||
|
assert_eq!(first, 'F');
|
||||||
|
Network::Stagenet
|
||||||
|
}
|
||||||
|
_ => panic!("Unknown network"),
|
||||||
|
};
|
||||||
|
let spend =
|
||||||
|
CompressedEdwardsY::from_slice(&hex::decode(vector.spend).unwrap()).decompress().unwrap();
|
||||||
|
let view =
|
||||||
|
CompressedEdwardsY::from_slice(&hex::decode(vector.view).unwrap()).decompress().unwrap();
|
||||||
|
|
||||||
|
let addr = Address::from_str(&vector.address, network).unwrap();
|
||||||
|
assert_eq!(addr.spend, spend);
|
||||||
|
assert_eq!(addr.view, view);
|
||||||
|
|
||||||
|
assert_eq!(addr.subaddress(), vector.subaddress);
|
||||||
|
assert_eq!(vector.integrated, vector.payment_id.is_some());
|
||||||
|
assert_eq!(addr.payment_id(), vector.payment_id);
|
||||||
|
assert_eq!(addr.guaranteed(), vector.guaranteed);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Address::new(
|
||||||
|
AddressMeta {
|
||||||
|
network,
|
||||||
|
kind: AddressType::Featured(vector.subaddress, vector.payment_id, vector.guaranteed)
|
||||||
|
},
|
||||||
|
spend,
|
||||||
|
view
|
||||||
|
)
|
||||||
|
.to_string(),
|
||||||
|
vector.address
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
230
coins/monero/src/tests/featured_addresses.json
Normal file
230
coins/monero/src/tests/featured_addresses.json
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"address": "CjWdTpuDaZ69nTGxzm9YarR82YDYFECi1WaaREZTMy5yDsjaRX5bC3cbC3JpcrBPd7YYpjoWKuBMidgGaKBK5Jye2v3pYyUDn",
|
||||||
|
"network": "Mainnet",
|
||||||
|
"spend": "258dfe7eef9be934839f3b8e0d40e79035fe85879c0a9eb0d7372ae2deb0004c",
|
||||||
|
"view": "f91382373045f3cc69233254ab0406bc9e008707569ff9db4718654812d839df",
|
||||||
|
"subaddress": false,
|
||||||
|
"integrated": false,
|
||||||
|
"guaranteed": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "CjWdTpuDaZ69nTGxzm9YarR82YDYFECi1WaaREZTMy5yDsjaRX5bC3cbC3JpcrBPd7YYpjoWKuBMidgGaKBK5Jye2v3wfMHCy",
|
||||||
|
"network": "Mainnet",
|
||||||
|
"spend": "258dfe7eef9be934839f3b8e0d40e79035fe85879c0a9eb0d7372ae2deb0004c",
|
||||||
|
"view": "f91382373045f3cc69233254ab0406bc9e008707569ff9db4718654812d839df",
|
||||||
|
"subaddress": true,
|
||||||
|
"integrated": false,
|
||||||
|
"guaranteed": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "CjWdTpuDaZ69nTGxzm9YarR82YDYFECi1WaaREZTMy5yDsjaRX5bC3cbC3JpcrBPd7YYpjoWKuBMidgGaKBK5JyeeJTo4p5ayvj36PStM5AX",
|
||||||
|
"network": "Mainnet",
|
||||||
|
"spend": "258dfe7eef9be934839f3b8e0d40e79035fe85879c0a9eb0d7372ae2deb0004c",
|
||||||
|
"view": "f91382373045f3cc69233254ab0406bc9e008707569ff9db4718654812d839df",
|
||||||
|
"subaddress": false,
|
||||||
|
"integrated": true,
|
||||||
|
"payment_id": [46, 48, 134, 34, 245, 148, 243, 195],
|
||||||
|
"guaranteed": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "CjWdTpuDaZ69nTGxzm9YarR82YDYFECi1WaaREZTMy5yDsjaRX5bC3cbC3JpcrBPd7YYpjoWKuBMidgGaKBK5JyeeJWv5WqMCNE2hRs9rJfy",
|
||||||
|
"network": "Mainnet",
|
||||||
|
"spend": "258dfe7eef9be934839f3b8e0d40e79035fe85879c0a9eb0d7372ae2deb0004c",
|
||||||
|
"view": "f91382373045f3cc69233254ab0406bc9e008707569ff9db4718654812d839df",
|
||||||
|
"subaddress": true,
|
||||||
|
"integrated": true,
|
||||||
|
"payment_id": [153, 176, 98, 204, 151, 27, 197, 168],
|
||||||
|
"guaranteed": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "CjWdTpuDaZ69nTGxzm9YarR82YDYFECi1WaaREZTMy5yDsjaRX5bC3cbC3JpcrBPd7YYpjoWKuBMidgGaKBK5Jye2v4DwqwH1",
|
||||||
|
"network": "Mainnet",
|
||||||
|
"spend": "258dfe7eef9be934839f3b8e0d40e79035fe85879c0a9eb0d7372ae2deb0004c",
|
||||||
|
"view": "f91382373045f3cc69233254ab0406bc9e008707569ff9db4718654812d839df",
|
||||||
|
"subaddress": false,
|
||||||
|
"integrated": false,
|
||||||
|
"guaranteed": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "CjWdTpuDaZ69nTGxzm9YarR82YDYFECi1WaaREZTMy5yDsjaRX5bC3cbC3JpcrBPd7YYpjoWKuBMidgGaKBK5Jye2v4Pyz8bD",
|
||||||
|
"network": "Mainnet",
|
||||||
|
"spend": "258dfe7eef9be934839f3b8e0d40e79035fe85879c0a9eb0d7372ae2deb0004c",
|
||||||
|
"view": "f91382373045f3cc69233254ab0406bc9e008707569ff9db4718654812d839df",
|
||||||
|
"subaddress": true,
|
||||||
|
"integrated": false,
|
||||||
|
"guaranteed": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "CjWdTpuDaZ69nTGxzm9YarR82YDYFECi1WaaREZTMy5yDsjaRX5bC3cbC3JpcrBPd7YYpjoWKuBMidgGaKBK5JyeeJcwt7hykou237MqZZDA",
|
||||||
|
"network": "Mainnet",
|
||||||
|
"spend": "258dfe7eef9be934839f3b8e0d40e79035fe85879c0a9eb0d7372ae2deb0004c",
|
||||||
|
"view": "f91382373045f3cc69233254ab0406bc9e008707569ff9db4718654812d839df",
|
||||||
|
"subaddress": false,
|
||||||
|
"integrated": true,
|
||||||
|
"payment_id": [88, 37, 149, 111, 171, 108, 120, 181],
|
||||||
|
"guaranteed": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "CjWdTpuDaZ69nTGxzm9YarR82YDYFECi1WaaREZTMy5yDsjaRX5bC3cbC3JpcrBPd7YYpjoWKuBMidgGaKBK5JyeeJfTrFAp69u2MYbf5YeN",
|
||||||
|
"network": "Mainnet",
|
||||||
|
"spend": "258dfe7eef9be934839f3b8e0d40e79035fe85879c0a9eb0d7372ae2deb0004c",
|
||||||
|
"view": "f91382373045f3cc69233254ab0406bc9e008707569ff9db4718654812d839df",
|
||||||
|
"subaddress": true,
|
||||||
|
"integrated": true,
|
||||||
|
"payment_id": [125, 69, 155, 152, 140, 160, 157, 186],
|
||||||
|
"guaranteed": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "Kgx5uCVsMSEVm7seL8tjyRGmmVXjWfEowKpKjgaXUGVyMViBYMh13VQ4mfqpB7zEVVcJx3E8FFgAuQ8cq6mg5x712U9w7ScYA",
|
||||||
|
"network": "Testnet",
|
||||||
|
"spend": "bba3a8a5bb47f7abf2e2dffeaf43385e4b308fd63a9ff6707e355f3b0a6c247a",
|
||||||
|
"view": "881713a4fa9777168a54bbdcb75290d319fb92fdf1026a8a4b125a8e341de8ab",
|
||||||
|
"subaddress": false,
|
||||||
|
"integrated": false,
|
||||||
|
"guaranteed": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "Kgx5uCVsMSEVm7seL8tjyRGmmVXjWfEowKpKjgaXUGVyMViBYMh13VQ4mfqpB7zEVVcJx3E8FFgAuQ8cq6mg5x712UA2gCrT1",
|
||||||
|
"network": "Testnet",
|
||||||
|
"spend": "bba3a8a5bb47f7abf2e2dffeaf43385e4b308fd63a9ff6707e355f3b0a6c247a",
|
||||||
|
"view": "881713a4fa9777168a54bbdcb75290d319fb92fdf1026a8a4b125a8e341de8ab",
|
||||||
|
"subaddress": true,
|
||||||
|
"integrated": false,
|
||||||
|
"guaranteed": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "Kgx5uCVsMSEVm7seL8tjyRGmmVXjWfEowKpKjgaXUGVyMViBYMh13VQ4mfqpB7zEVVcJx3E8FFgAuQ8cq6mg5x71Vc1DbPKwJu81cxJjqBkS",
|
||||||
|
"network": "Testnet",
|
||||||
|
"spend": "bba3a8a5bb47f7abf2e2dffeaf43385e4b308fd63a9ff6707e355f3b0a6c247a",
|
||||||
|
"view": "881713a4fa9777168a54bbdcb75290d319fb92fdf1026a8a4b125a8e341de8ab",
|
||||||
|
"subaddress": false,
|
||||||
|
"integrated": true,
|
||||||
|
"payment_id": [92, 225, 118, 220, 39, 3, 72, 51],
|
||||||
|
"guaranteed": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "Kgx5uCVsMSEVm7seL8tjyRGmmVXjWfEowKpKjgaXUGVyMViBYMh13VQ4mfqpB7zEVVcJx3E8FFgAuQ8cq6mg5x71Vc2o1rPMaXN31Fe5J6dn",
|
||||||
|
"network": "Testnet",
|
||||||
|
"spend": "bba3a8a5bb47f7abf2e2dffeaf43385e4b308fd63a9ff6707e355f3b0a6c247a",
|
||||||
|
"view": "881713a4fa9777168a54bbdcb75290d319fb92fdf1026a8a4b125a8e341de8ab",
|
||||||
|
"subaddress": true,
|
||||||
|
"integrated": true,
|
||||||
|
"payment_id": [20, 120, 47, 89, 72, 165, 233, 115],
|
||||||
|
"guaranteed": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "Kgx5uCVsMSEVm7seL8tjyRGmmVXjWfEowKpKjgaXUGVyMViBYMh13VQ4mfqpB7zEVVcJx3E8FFgAuQ8cq6mg5x712UAQHCRZ4",
|
||||||
|
"network": "Testnet",
|
||||||
|
"spend": "bba3a8a5bb47f7abf2e2dffeaf43385e4b308fd63a9ff6707e355f3b0a6c247a",
|
||||||
|
"view": "881713a4fa9777168a54bbdcb75290d319fb92fdf1026a8a4b125a8e341de8ab",
|
||||||
|
"subaddress": false,
|
||||||
|
"integrated": false,
|
||||||
|
"guaranteed": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "Kgx5uCVsMSEVm7seL8tjyRGmmVXjWfEowKpKjgaXUGVyMViBYMh13VQ4mfqpB7zEVVcJx3E8FFgAuQ8cq6mg5x712UAUzqaii",
|
||||||
|
"network": "Testnet",
|
||||||
|
"spend": "bba3a8a5bb47f7abf2e2dffeaf43385e4b308fd63a9ff6707e355f3b0a6c247a",
|
||||||
|
"view": "881713a4fa9777168a54bbdcb75290d319fb92fdf1026a8a4b125a8e341de8ab",
|
||||||
|
"subaddress": true,
|
||||||
|
"integrated": false,
|
||||||
|
"guaranteed": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "Kgx5uCVsMSEVm7seL8tjyRGmmVXjWfEowKpKjgaXUGVyMViBYMh13VQ4mfqpB7zEVVcJx3E8FFgAuQ8cq6mg5x71VcAsfQc3gJQ2gHLd5DiQ",
|
||||||
|
"network": "Testnet",
|
||||||
|
"spend": "bba3a8a5bb47f7abf2e2dffeaf43385e4b308fd63a9ff6707e355f3b0a6c247a",
|
||||||
|
"view": "881713a4fa9777168a54bbdcb75290d319fb92fdf1026a8a4b125a8e341de8ab",
|
||||||
|
"subaddress": false,
|
||||||
|
"integrated": true,
|
||||||
|
"payment_id": [193, 149, 123, 214, 180, 205, 195, 91],
|
||||||
|
"guaranteed": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "Kgx5uCVsMSEVm7seL8tjyRGmmVXjWfEowKpKjgaXUGVyMViBYMh13VQ4mfqpB7zEVVcJx3E8FFgAuQ8cq6mg5x71VcDBAD5jbZQ3AMHFyvQB",
|
||||||
|
"network": "Testnet",
|
||||||
|
"spend": "bba3a8a5bb47f7abf2e2dffeaf43385e4b308fd63a9ff6707e355f3b0a6c247a",
|
||||||
|
"view": "881713a4fa9777168a54bbdcb75290d319fb92fdf1026a8a4b125a8e341de8ab",
|
||||||
|
"subaddress": true,
|
||||||
|
"integrated": true,
|
||||||
|
"payment_id": [205, 170, 65, 0, 51, 175, 251, 184],
|
||||||
|
"guaranteed": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "FSDinqdKK54PbjF73GgW3nUpf7bF8QbyxFCUurENmUyeEfSxSLL2hxwANBLzq1A8gTSAzzEn65hKjetA8o5BvjV61VPJnBtTP",
|
||||||
|
"network": "Stagenet",
|
||||||
|
"spend": "4cd503040f5e43871bf37d8ca7177da655bda410859af754e24e7b44437f3151",
|
||||||
|
"view": "af60d42b6c6e4437fd93eb32657a14967efa393630d7aee27b5973c8e1c5ad39",
|
||||||
|
"subaddress": false,
|
||||||
|
"integrated": false,
|
||||||
|
"guaranteed": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "FSDinqdKK54PbjF73GgW3nUpf7bF8QbyxFCUurENmUyeEfSxSLL2hxwANBLzq1A8gTSAzzEn65hKjetA8o5BvjV61VPUrwMvP",
|
||||||
|
"network": "Stagenet",
|
||||||
|
"spend": "4cd503040f5e43871bf37d8ca7177da655bda410859af754e24e7b44437f3151",
|
||||||
|
"view": "af60d42b6c6e4437fd93eb32657a14967efa393630d7aee27b5973c8e1c5ad39",
|
||||||
|
"subaddress": true,
|
||||||
|
"integrated": false,
|
||||||
|
"guaranteed": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "FSDinqdKK54PbjF73GgW3nUpf7bF8QbyxFCUurENmUyeEfSxSLL2hxwANBLzq1A8gTSAzzEn65hKjetA8o5BvjV6AY5ECEhP5Nr1aCRPXdxk",
|
||||||
|
"network": "Stagenet",
|
||||||
|
"spend": "4cd503040f5e43871bf37d8ca7177da655bda410859af754e24e7b44437f3151",
|
||||||
|
"view": "af60d42b6c6e4437fd93eb32657a14967efa393630d7aee27b5973c8e1c5ad39",
|
||||||
|
"subaddress": false,
|
||||||
|
"integrated": true,
|
||||||
|
"payment_id": [173, 149, 78, 64, 215, 211, 66, 170],
|
||||||
|
"guaranteed": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "FSDinqdKK54PbjF73GgW3nUpf7bF8QbyxFCUurENmUyeEfSxSLL2hxwANBLzq1A8gTSAzzEn65hKjetA8o5BvjV6AY882kTUS1D2LttnPvTR",
|
||||||
|
"network": "Stagenet",
|
||||||
|
"spend": "4cd503040f5e43871bf37d8ca7177da655bda410859af754e24e7b44437f3151",
|
||||||
|
"view": "af60d42b6c6e4437fd93eb32657a14967efa393630d7aee27b5973c8e1c5ad39",
|
||||||
|
"subaddress": true,
|
||||||
|
"integrated": true,
|
||||||
|
"payment_id": [254, 159, 186, 162, 1, 8, 156, 108],
|
||||||
|
"guaranteed": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "FSDinqdKK54PbjF73GgW3nUpf7bF8QbyxFCUurENmUyeEfSxSLL2hxwANBLzq1A8gTSAzzEn65hKjetA8o5BvjV61VPpBBo8F",
|
||||||
|
"network": "Stagenet",
|
||||||
|
"spend": "4cd503040f5e43871bf37d8ca7177da655bda410859af754e24e7b44437f3151",
|
||||||
|
"view": "af60d42b6c6e4437fd93eb32657a14967efa393630d7aee27b5973c8e1c5ad39",
|
||||||
|
"subaddress": false,
|
||||||
|
"integrated": false,
|
||||||
|
"guaranteed": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "FSDinqdKK54PbjF73GgW3nUpf7bF8QbyxFCUurENmUyeEfSxSLL2hxwANBLzq1A8gTSAzzEn65hKjetA8o5BvjV61VPuUJX3b",
|
||||||
|
"network": "Stagenet",
|
||||||
|
"spend": "4cd503040f5e43871bf37d8ca7177da655bda410859af754e24e7b44437f3151",
|
||||||
|
"view": "af60d42b6c6e4437fd93eb32657a14967efa393630d7aee27b5973c8e1c5ad39",
|
||||||
|
"subaddress": true,
|
||||||
|
"integrated": false,
|
||||||
|
"guaranteed": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "FSDinqdKK54PbjF73GgW3nUpf7bF8QbyxFCUurENmUyeEfSxSLL2hxwANBLzq1A8gTSAzzEn65hKjetA8o5BvjV6AYCZPxVAoDu21DryMoto",
|
||||||
|
"network": "Stagenet",
|
||||||
|
"spend": "4cd503040f5e43871bf37d8ca7177da655bda410859af754e24e7b44437f3151",
|
||||||
|
"view": "af60d42b6c6e4437fd93eb32657a14967efa393630d7aee27b5973c8e1c5ad39",
|
||||||
|
"subaddress": false,
|
||||||
|
"integrated": true,
|
||||||
|
"payment_id": [3, 115, 230, 129, 172, 108, 116, 235],
|
||||||
|
"guaranteed": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "FSDinqdKK54PbjF73GgW3nUpf7bF8QbyxFCUurENmUyeEfSxSLL2hxwANBLzq1A8gTSAzzEn65hKjetA8o5BvjV6AYFYCqKQAWL18KkpBQ8R",
|
||||||
|
"network": "Stagenet",
|
||||||
|
"spend": "4cd503040f5e43871bf37d8ca7177da655bda410859af754e24e7b44437f3151",
|
||||||
|
"view": "af60d42b6c6e4437fd93eb32657a14967efa393630d7aee27b5973c8e1c5ad39",
|
||||||
|
"subaddress": true,
|
||||||
|
"integrated": true,
|
||||||
|
"payment_id": [94, 122, 63, 167, 209, 225, 14, 180],
|
||||||
|
"guaranteed": true
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -25,23 +25,41 @@ pub enum AddressType {
|
|||||||
Standard,
|
Standard,
|
||||||
Integrated([u8; 8]),
|
Integrated([u8; 8]),
|
||||||
Subaddress,
|
Subaddress,
|
||||||
|
Featured(bool, Option<[u8; 8]>, bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddressType {
|
impl AddressType {
|
||||||
fn network_bytes(network: Network) -> (u8, u8, u8) {
|
fn network_bytes(network: Network) -> (u8, u8, u8, u8) {
|
||||||
match network {
|
match network {
|
||||||
Network::Mainnet => (18, 19, 42),
|
Network::Mainnet => (18, 19, 42, 70),
|
||||||
Network::Testnet => (53, 54, 63),
|
Network::Testnet => (53, 54, 63, 111),
|
||||||
Network::Stagenet => (24, 25, 36),
|
Network::Stagenet => (24, 25, 36, 86),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn subaddress(&self) -> bool {
|
||||||
|
matches!(self, AddressType::Subaddress) || matches!(self, AddressType::Featured(true, ..))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn payment_id(&self) -> Option<[u8; 8]> {
|
||||||
|
if let AddressType::Integrated(id) = self {
|
||||||
|
Some(*id)
|
||||||
|
} else if let AddressType::Featured(_, id, _) = self {
|
||||||
|
*id
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn guaranteed(&self) -> bool {
|
||||||
|
matches!(self, AddressType::Featured(_, _, true))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||||
pub struct AddressMeta {
|
pub struct AddressMeta {
|
||||||
pub network: Network,
|
pub network: Network,
|
||||||
pub kind: AddressType,
|
pub kind: AddressType,
|
||||||
pub guaranteed: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Error, Debug)]
|
#[derive(Clone, Error, Debug)]
|
||||||
@@ -52,45 +70,57 @@ pub enum AddressError {
|
|||||||
InvalidEncoding,
|
InvalidEncoding,
|
||||||
#[error("invalid length")]
|
#[error("invalid length")]
|
||||||
InvalidLength,
|
InvalidLength,
|
||||||
#[error("different network than expected")]
|
|
||||||
DifferentNetwork,
|
|
||||||
#[error("invalid key")]
|
#[error("invalid key")]
|
||||||
InvalidKey,
|
InvalidKey,
|
||||||
|
#[error("unknown features")]
|
||||||
|
UnknownFeatures,
|
||||||
|
#[error("different network than expected")]
|
||||||
|
DifferentNetwork,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddressMeta {
|
impl AddressMeta {
|
||||||
#[allow(clippy::wrong_self_convention)]
|
#[allow(clippy::wrong_self_convention)]
|
||||||
fn to_byte(&self) -> u8 {
|
fn to_byte(&self) -> u8 {
|
||||||
let bytes = AddressType::network_bytes(self.network);
|
let bytes = AddressType::network_bytes(self.network);
|
||||||
let byte = match self.kind {
|
match self.kind {
|
||||||
AddressType::Standard => bytes.0,
|
AddressType::Standard => bytes.0,
|
||||||
AddressType::Integrated(_) => bytes.1,
|
AddressType::Integrated(_) => bytes.1,
|
||||||
AddressType::Subaddress => bytes.2,
|
AddressType::Subaddress => bytes.2,
|
||||||
};
|
AddressType::Featured(..) => bytes.3,
|
||||||
byte | (if self.guaranteed { 1 << 7 } else { 0 })
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns an incomplete type in the case of Integrated addresses
|
// Returns an incomplete type in the case of Integrated/Featured addresses
|
||||||
fn from_byte(byte: u8) -> Result<AddressMeta, AddressError> {
|
fn from_byte(byte: u8) -> Result<AddressMeta, AddressError> {
|
||||||
let actual = byte & 0b01111111;
|
|
||||||
let guaranteed = (byte >> 7) == 1;
|
|
||||||
|
|
||||||
let mut meta = None;
|
let mut meta = None;
|
||||||
for network in [Network::Mainnet, Network::Testnet, Network::Stagenet] {
|
for network in [Network::Mainnet, Network::Testnet, Network::Stagenet] {
|
||||||
let (standard, integrated, subaddress) = AddressType::network_bytes(network);
|
let (standard, integrated, subaddress, featured) = AddressType::network_bytes(network);
|
||||||
if let Some(kind) = match actual {
|
if let Some(kind) = match byte {
|
||||||
_ if actual == standard => Some(AddressType::Standard),
|
_ if byte == standard => Some(AddressType::Standard),
|
||||||
_ if actual == integrated => Some(AddressType::Integrated([0; 8])),
|
_ if byte == integrated => Some(AddressType::Integrated([0; 8])),
|
||||||
_ if actual == subaddress => Some(AddressType::Subaddress),
|
_ if byte == subaddress => Some(AddressType::Subaddress),
|
||||||
|
_ if byte == featured => Some(AddressType::Featured(false, None, false)),
|
||||||
_ => None,
|
_ => None,
|
||||||
} {
|
} {
|
||||||
meta = Some(AddressMeta { network, kind, guaranteed });
|
meta = Some(AddressMeta { network, kind });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
meta.ok_or(AddressError::InvalidByte)
|
meta.ok_or(AddressError::InvalidByte)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn subaddress(&self) -> bool {
|
||||||
|
self.kind.subaddress()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn payment_id(&self) -> Option<[u8; 8]> {
|
||||||
|
self.kind.payment_id()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn guaranteed(&self) -> bool {
|
||||||
|
self.kind.guaranteed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||||
@@ -101,9 +131,9 @@ pub struct Address {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ViewPair {
|
impl ViewPair {
|
||||||
pub fn address(&self, network: Network, kind: AddressType, guaranteed: bool) -> Address {
|
pub fn address(&self, network: Network, kind: AddressType) -> Address {
|
||||||
Address {
|
Address {
|
||||||
meta: AddressMeta { network, kind, guaranteed },
|
meta: AddressMeta { network, kind },
|
||||||
spend: self.spend,
|
spend: self.spend,
|
||||||
view: &self.view * &ED25519_BASEPOINT_TABLE,
|
view: &self.view * &ED25519_BASEPOINT_TABLE,
|
||||||
}
|
}
|
||||||
@@ -115,7 +145,15 @@ impl ToString for Address {
|
|||||||
let mut data = vec![self.meta.to_byte()];
|
let mut data = vec![self.meta.to_byte()];
|
||||||
data.extend(self.spend.compress().to_bytes());
|
data.extend(self.spend.compress().to_bytes());
|
||||||
data.extend(self.view.compress().to_bytes());
|
data.extend(self.view.compress().to_bytes());
|
||||||
if let AddressType::Integrated(id) = self.meta.kind {
|
if let AddressType::Featured(subaddress, payment_id, guaranteed) = self.meta.kind {
|
||||||
|
// Technically should be a VarInt, yet we don't have enough features it's needed
|
||||||
|
data.push(
|
||||||
|
(if subaddress { 1 } else { 0 }) +
|
||||||
|
((if payment_id.is_some() { 1 } else { 0 }) << 1) +
|
||||||
|
((if guaranteed { 1 } else { 0 }) << 2),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(id) = self.meta.kind.payment_id() {
|
||||||
data.extend(id);
|
data.extend(id);
|
||||||
}
|
}
|
||||||
encode_check(&data).unwrap()
|
encode_check(&data).unwrap()
|
||||||
@@ -123,36 +161,80 @@ impl ToString for Address {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Address {
|
impl Address {
|
||||||
pub fn from_str(s: &str, network: Network) -> Result<Self, AddressError> {
|
pub fn new(meta: AddressMeta, spend: EdwardsPoint, view: EdwardsPoint) -> Address {
|
||||||
|
Address { meta, spend, view }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_str_raw(s: &str) -> Result<Address, AddressError> {
|
||||||
let raw = decode_check(s).map_err(|_| AddressError::InvalidEncoding)?;
|
let raw = decode_check(s).map_err(|_| AddressError::InvalidEncoding)?;
|
||||||
if raw.len() == 1 {
|
if raw.len() < (1 + 32 + 32) {
|
||||||
Err(AddressError::InvalidLength)?;
|
Err(AddressError::InvalidLength)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut meta = AddressMeta::from_byte(raw[0])?;
|
let mut meta = AddressMeta::from_byte(raw[0])?;
|
||||||
if meta.network != network {
|
|
||||||
Err(AddressError::DifferentNetwork)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let len = match meta.kind {
|
|
||||||
AddressType::Standard | AddressType::Subaddress => 65,
|
|
||||||
AddressType::Integrated(_) => 73,
|
|
||||||
};
|
|
||||||
if raw.len() != len {
|
|
||||||
Err(AddressError::InvalidLength)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let spend = CompressedEdwardsY(raw[1 .. 33].try_into().unwrap())
|
let spend = CompressedEdwardsY(raw[1 .. 33].try_into().unwrap())
|
||||||
.decompress()
|
.decompress()
|
||||||
.ok_or(AddressError::InvalidKey)?;
|
.ok_or(AddressError::InvalidKey)?;
|
||||||
let view = CompressedEdwardsY(raw[33 .. 65].try_into().unwrap())
|
let view = CompressedEdwardsY(raw[33 .. 65].try_into().unwrap())
|
||||||
.decompress()
|
.decompress()
|
||||||
.ok_or(AddressError::InvalidKey)?;
|
.ok_or(AddressError::InvalidKey)?;
|
||||||
|
let mut read = 65;
|
||||||
|
|
||||||
if let AddressType::Integrated(ref mut payment_id) = meta.kind {
|
if matches!(meta.kind, AddressType::Featured(..)) {
|
||||||
payment_id.copy_from_slice(&raw[65 .. 73]);
|
if raw[read] >= (2 << 3) {
|
||||||
|
Err(AddressError::UnknownFeatures)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let subaddress = (raw[read] & 1) == 1;
|
||||||
|
let integrated = ((raw[read] >> 1) & 1) == 1;
|
||||||
|
let guaranteed = ((raw[read] >> 2) & 1) == 1;
|
||||||
|
|
||||||
|
meta.kind =
|
||||||
|
AddressType::Featured(subaddress, Some([0; 8]).filter(|_| integrated), guaranteed);
|
||||||
|
read += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update read early so we can verify the length
|
||||||
|
if meta.kind.payment_id().is_some() {
|
||||||
|
read += 8;
|
||||||
|
}
|
||||||
|
if raw.len() != read {
|
||||||
|
Err(AddressError::InvalidLength)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let AddressType::Integrated(ref mut id) = meta.kind {
|
||||||
|
id.copy_from_slice(&raw[(read - 8) .. read]);
|
||||||
|
}
|
||||||
|
if let AddressType::Featured(_, Some(ref mut id), _) = meta.kind {
|
||||||
|
id.copy_from_slice(&raw[(read - 8) .. read]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Address { meta, spend, view })
|
Ok(Address { meta, spend, view })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_str(s: &str, network: Network) -> Result<Address, AddressError> {
|
||||||
|
Self::from_str_raw(s).and_then(|addr| {
|
||||||
|
if addr.meta.network == network {
|
||||||
|
Ok(addr)
|
||||||
|
} else {
|
||||||
|
Err(AddressError::DifferentNetwork)?
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn network(&self) -> Network {
|
||||||
|
self.meta.network
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subaddress(&self) -> bool {
|
||||||
|
self.meta.subaddress()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn payment_id(&self) -> Option<[u8; 8]> {
|
||||||
|
self.meta.payment_id()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn guaranteed(&self) -> bool {
|
||||||
|
self.meta.guaranteed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,8 @@ use crate::{
|
|||||||
transaction::{Input, Output, Timelock, TransactionPrefix, Transaction},
|
transaction::{Input, Output, Timelock, TransactionPrefix, Transaction},
|
||||||
rpc::{Rpc, RpcError},
|
rpc::{Rpc, RpcError},
|
||||||
wallet::{
|
wallet::{
|
||||||
address::{AddressType, Address},
|
address::Address, SpendableOutput, Decoys, PaymentId, ExtraField, Extra, key_image_sort,
|
||||||
SpendableOutput, Decoys, PaymentId, ExtraField, Extra, key_image_sort, uniqueness, shared_key,
|
uniqueness, shared_key, commitment_mask, amount_encryption,
|
||||||
commitment_mask, amount_encryption,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
#[cfg(feature = "multisig")]
|
#[cfg(feature = "multisig")]
|
||||||
@@ -53,19 +52,20 @@ impl SendOutput {
|
|||||||
) -> SendOutput {
|
) -> SendOutput {
|
||||||
let r = random_scalar(rng);
|
let r = random_scalar(rng);
|
||||||
let (view_tag, shared_key) =
|
let (view_tag, shared_key) =
|
||||||
shared_key(Some(unique).filter(|_| output.0.meta.guaranteed), &r, &output.0.view, o);
|
shared_key(Some(unique).filter(|_| output.0.meta.kind.guaranteed()), &r, &output.0.view, o);
|
||||||
|
|
||||||
let spend = output.0.spend;
|
if output.0.meta.kind.payment_id().is_some() {
|
||||||
SendOutput {
|
unimplemented!("integrated addresses aren't currently supported");
|
||||||
R: match output.0.meta.kind {
|
|
||||||
AddressType::Standard => &r * &ED25519_BASEPOINT_TABLE,
|
|
||||||
AddressType::Integrated(_) => {
|
|
||||||
unimplemented!("SendOutput::new doesn't support Integrated addresses")
|
|
||||||
}
|
}
|
||||||
AddressType::Subaddress => r * spend,
|
|
||||||
|
SendOutput {
|
||||||
|
R: if !output.0.meta.kind.subaddress() {
|
||||||
|
&r * &ED25519_BASEPOINT_TABLE
|
||||||
|
} else {
|
||||||
|
r * output.0.spend
|
||||||
},
|
},
|
||||||
view_tag,
|
view_tag,
|
||||||
dest: ((&shared_key * &ED25519_BASEPOINT_TABLE) + spend),
|
dest: ((&shared_key * &ED25519_BASEPOINT_TABLE) + output.0.spend),
|
||||||
commitment: Commitment::new(commitment_mask(shared_key), output.1),
|
commitment: Commitment::new(commitment_mask(shared_key), output.1),
|
||||||
amount: amount_encryption(output.1, shared_key),
|
amount: amount_encryption(output.1, shared_key),
|
||||||
}
|
}
|
||||||
@@ -179,10 +179,13 @@ impl SignableTransaction {
|
|||||||
fee_rate: Fee,
|
fee_rate: Fee,
|
||||||
) -> Result<SignableTransaction, TransactionError> {
|
) -> Result<SignableTransaction, TransactionError> {
|
||||||
// Make sure all addresses are valid
|
// Make sure all addresses are valid
|
||||||
let test = |addr: Address| match addr.meta.kind {
|
let test = |addr: Address| {
|
||||||
AddressType::Standard => Ok(()),
|
if addr.meta.kind.payment_id().is_some() {
|
||||||
AddressType::Integrated(..) => Err(TransactionError::InvalidAddress),
|
// TODO
|
||||||
AddressType::Subaddress => Ok(()),
|
Err(TransactionError::InvalidAddress)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for payment in &payments {
|
for payment in &payments {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ pub async fn rpc() -> Rpc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let addr = Address {
|
let addr = Address {
|
||||||
meta: AddressMeta { network: Network::Mainnet, kind: AddressType::Standard, guaranteed: false },
|
meta: AddressMeta { network: Network::Mainnet, kind: AddressType::Standard },
|
||||||
spend: &random_scalar(&mut OsRng) * &ED25519_BASEPOINT_TABLE,
|
spend: &random_scalar(&mut OsRng) * &ED25519_BASEPOINT_TABLE,
|
||||||
view: &random_scalar(&mut OsRng) * &ED25519_BASEPOINT_TABLE,
|
view: &random_scalar(&mut OsRng) * &ED25519_BASEPOINT_TABLE,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ async fn send_core(test: usize, multisig: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let view_pair = ViewPair { view, spend: spend_pub };
|
let view_pair = ViewPair { view, spend: spend_pub };
|
||||||
let addr = view_pair.address(Network::Mainnet, AddressType::Standard, false);
|
let addr = view_pair.address(Network::Mainnet, AddressType::Standard);
|
||||||
|
|
||||||
let fee = rpc.get_fee().await.unwrap();
|
let fee = rpc.get_fee().await.unwrap();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user