2021-05-04 02:59:44 -04:00
#![ allow(non_snake_case) ]
2021-05-05 23:39:05 -04:00
use monero ::blockdata ::transaction ::KeyImage ;
use monero ::util ::key ::H ;
use monero ::ViewPair ;
2021-05-04 02:59:44 -04:00
use curve25519_dalek ::constants ::ED25519_BASEPOINT_POINT ;
use curve25519_dalek ::edwards ::EdwardsPoint ;
use curve25519_dalek ::scalar ::Scalar ;
use hash_edwards_to_edwards ::hash_point_to_point ;
2021-05-04 03:58:54 -04:00
use monero ::blockdata ::transaction ::{ ExtraField , SubField , TxOutTarget } ;
2021-05-04 02:59:44 -04:00
use monero ::cryptonote ::hash ::Hashable ;
2021-05-04 03:58:54 -04:00
use monero ::cryptonote ::onetime_key ::KeyGenerator ;
use monero ::util ::ringct ::{ EcdhInfo , RctSig , RctSigBase , RctSigPrunable , RctType } ;
use monero ::{ PrivateKey , PublicKey } ;
use monero ::{ Transaction , TransactionPrefix , TxIn , TxOut , VarInt } ;
2021-05-03 23:37:07 -04:00
use monero_rpc ::monerod ;
2021-05-04 02:59:44 -04:00
use monero_rpc ::monerod ::{ GetOutputsOut , MonerodRpc } ;
2021-05-06 03:54:50 -04:00
use monero_wallet ::{ MonerodClientExt } ;
2021-05-04 02:59:44 -04:00
use rand ::rngs ::OsRng ;
use rand ::{ Rng , SeedableRng } ;
use std ::convert ::TryInto ;
2021-05-03 23:37:07 -04:00
use std ::iter ;
// [0u8; 32] = 466iKkx7MqVGD46dje3kwvSQRMfhNCvGaXTRATbQgz7kS8XTMmRmoTw9oJRRj523kTdQj8gXnF2xU9fmEPy9WXTr6pwetQj
// [1u8; 32] = 47HCnKkBEeYfX5pScvBETAKdjBEPN7FcXEJPUqDPzWGCc6wC8VAdS8CjdtgKuSaY72K8fkoswjp176vbSPS8hzS17EZv8gj
2021-05-04 03:58:54 -04:00
#[ tokio::test ]
async fn make_blocks ( ) {
let client = monerod ::Client ::localhost ( 18081 ) . unwrap ( ) ;
2021-05-06 03:54:50 -04:00
client . generateblocks ( 10 , " 498AVruCDWgP9Az9LjMm89VWjrBrSZ2W2K3HFBiyzzrRjUJWUcCVxvY1iitfuKoek2FdX6MKGAD9Qb1G1P8QgR5jPmmt3Vj " . to_owned ( ) ) . await . unwrap ( ) ;
2021-05-04 03:58:54 -04:00
}
2021-05-03 21:46:58 -04:00
#[ tokio::test ]
async fn monerod_integration_test ( ) {
2021-05-03 23:37:07 -04:00
let client = monerod ::Client ::localhost ( 18081 ) . unwrap ( ) ;
let mut rng = rand ::rngs ::StdRng ::from_seed ( [ 0 u8 ; 32 ] ) ;
2021-05-04 02:59:44 -04:00
let s_prime_a = curve25519_dalek ::scalar ::Scalar ::random ( & mut rng ) ;
let s_b = curve25519_dalek ::scalar ::Scalar ::random ( & mut rng ) ;
let lock_kp = monero ::KeyPair {
view : monero ::PrivateKey ::from_scalar ( curve25519_dalek ::scalar ::Scalar ::random ( & mut rng ) ) ,
spend : monero ::PrivateKey ::from_scalar ( s_prime_a + s_b ) ,
} ;
2021-05-03 23:37:07 -04:00
2021-05-04 02:59:44 -04:00
let lock_address = monero ::Address ::from_keypair ( monero ::Network ::Mainnet , & lock_kp ) ;
2021-05-06 03:54:50 -04:00
dbg! ( lock_address . to_string ( ) ) ;
let lock_tx = " fccc6c5177f3af65c6c9b26199854da1efcd9b826502d55582ba4882c35410e0 "
2021-05-04 02:59:44 -04:00
. parse ( )
. unwrap ( ) ;
2021-05-03 23:37:07 -04:00
2021-05-06 03:54:50 -04:00
let o_indexes_response = client . get_o_indexes ( lock_tx ) . await . unwrap ( ) ;
let transaction = client . get_transactions ( & [ lock_tx ] ) . await . unwrap ( ) . pop ( ) . unwrap ( ) ;
2021-05-03 23:37:07 -04:00
2021-05-06 03:54:50 -04:00
let our_output = transaction . check_outputs ( & ViewPair ::from ( & lock_kp ) , 0 .. 1 , 0 .. 1 ) . expect ( " to have outputs in this transaction " ) . pop ( ) . expect ( " to own at least one output " ) ;
let real_key_offset = o_indexes_response . o_indexes [ our_output . index ] ;
2021-05-03 23:37:07 -04:00
2021-05-04 03:58:54 -04:00
let ( lower , upper ) = client . calculate_key_offset_boundaries ( ) . await . unwrap ( ) ;
2021-05-03 23:37:07 -04:00
let mut key_offsets = Vec ::with_capacity ( 11 ) ;
key_offsets . push ( VarInt ( real_key_offset ) ) ;
2021-05-05 23:39:05 -04:00
for _ in 0 .. 10 {
2021-05-03 23:37:07 -04:00
loop {
let decoy_offset = VarInt ( rng . gen_range ( lower . 0 , upper . 0 ) ) ;
if key_offsets . contains ( & decoy_offset ) {
continue ;
}
key_offsets . push ( decoy_offset ) ;
break ;
}
}
2021-05-04 02:59:44 -04:00
let response = client
. get_outs (
key_offsets
. iter ( )
. map ( | offset | GetOutputsOut {
amount : 0 ,
index : offset . 0 ,
} )
. collect ( ) ,
)
. await
. unwrap ( ) ;
let ring = response
. outs
. iter ( )
. map ( | out | out . key . point . decompress ( ) . unwrap ( ) )
. collect ::< Vec < _ > > ( )
. try_into ( )
. unwrap ( ) ;
2021-05-03 23:37:07 -04:00
2021-05-04 02:59:44 -04:00
key_offsets . sort ( ) ;
2021-05-03 23:37:07 -04:00
let relative_key_offsets = to_relative_offsets ( & key_offsets ) ;
2021-05-06 03:54:50 -04:00
let lock_amount = 1_000_000_000_000 ;
2021-05-04 02:59:44 -04:00
let fee = 10_000 ;
2021-05-05 23:39:05 -04:00
let spend_amount = lock_amount - fee ;
// TODO: Pay lock amount to shared address (s_prime_a + s_b)
2021-05-04 02:59:44 -04:00
2021-05-05 23:39:05 -04:00
let target_address = " 498AVruCDWgP9Az9LjMm89VWjrBrSZ2W2K3HFBiyzzrRjUJWUcCVxvY1iitfuKoek2FdX6MKGAD9Qb1G1P8QgR5jPmmt3Vj " . parse ::< monero ::Address > ( ) . unwrap ( ) ;
let ecdh_key = PrivateKey ::random ( & mut rng ) ;
let ( ecdh_info , out_blinding ) = EcdhInfo ::new_bulletproof ( spend_amount , ecdh_key . scalar ) ;
// TODO: Modify API to let us determine the blindings ahead of time
let ( bulletproof , out_pk ) =
monero ::make_bulletproof ( & mut rng , & [ spend_amount ] , & [ out_blinding ] ) . unwrap ( ) ;
2021-05-06 03:54:50 -04:00
2021-05-04 02:59:44 -04:00
let out_pk = out_pk
. iter ( )
. map ( | c | monero ::util ::ringct ::CtKey {
mask : monero ::util ::ringct ::Key { key : c . to_bytes ( ) } ,
} )
. collect ( ) ;
2021-05-05 23:39:05 -04:00
let k_image = {
let k = lock_kp . spend . scalar ;
let K = ViewPair ::from ( & lock_kp ) . spend . point ;
2021-05-04 03:58:54 -04:00
2021-05-05 23:39:05 -04:00
let k_image = k * hash_point_to_point ( K . decompress ( ) . unwrap ( ) ) ;
KeyImage { image : monero ::cryptonote ::hash ::Hash ( k_image . compress ( ) . to_bytes ( ) ) }
} ;
2021-05-04 03:58:54 -04:00
2021-05-04 02:59:44 -04:00
let prefix = TransactionPrefix {
version : VarInt ( 2 ) ,
unlock_time : Default ::default ( ) ,
inputs : vec ! [ TxIn ::ToKey {
amount : VarInt ( 0 ) ,
key_offsets : relative_key_offsets ,
2021-05-05 23:39:05 -04:00
k_image ,
2021-05-04 02:59:44 -04:00
} ] ,
2021-05-04 03:58:54 -04:00
outputs : vec ! [ TxOut {
amount : VarInt ( 0 ) ,
target : TxOutTarget ::ToKey {
key : KeyGenerator ::from_random (
target_address . public_view ,
target_address . public_spend ,
ecdh_key ,
)
2021-05-05 23:39:05 -04:00
. one_time_key ( 0 ) , // TODO: It works with 1 output, but we must choose it based on the output index
2021-05-04 03:58:54 -04:00
} ,
} ] ,
extra : ExtraField ( vec! [ SubField ::TxPublicKey ( PublicKey ::from_private_key (
& ecdh_key ,
) ) ] ) ,
2021-05-04 02:59:44 -04:00
} ;
let ( adaptor_sig , adaptor ) =
single_party_adaptor_sig ( s_prime_a , s_b , ring , & prefix . hash ( ) . to_bytes ( ) ) ;
let sig = adaptor_sig . adapt ( adaptor ) ;
2021-05-05 23:39:05 -04:00
let pseudo_out = {
let amount = Scalar ::from ( lock_amount ) ;
2021-05-06 03:54:50 -04:00
let blinding = out_blinding ;
2021-05-05 23:39:05 -04:00
let commitment = ( blinding * ED25519_BASEPOINT_POINT ) + ( amount * H . point . decompress ( ) . unwrap ( ) ) ;
monero ::util ::ringct ::Key { key : commitment . compress ( ) . to_bytes ( ) }
} ;
2021-05-03 23:37:07 -04:00
let transaction = Transaction {
2021-05-04 02:59:44 -04:00
prefix ,
signatures : Vec ::new ( ) ,
rct_signatures : RctSig {
sig : Some ( RctSigBase {
rct_type : RctType ::Clsag ,
txn_fee : VarInt ( fee ) ,
pseudo_outs : Vec ::new ( ) ,
2021-05-05 23:39:05 -04:00
ecdh_info : vec ! [ ecdh_info ] ,
2021-05-04 02:59:44 -04:00
out_pk ,
} ) ,
p : Some ( RctSigPrunable {
range_sigs : Vec ::new ( ) ,
bulletproofs : vec ! [ bulletproof ] ,
MGs : Vec ::new ( ) ,
Clsags : vec ! [ sig . into ( ) ] ,
2021-05-05 23:39:05 -04:00
pseudo_outs : vec ! [ pseudo_out ] ,
2021-05-04 02:59:44 -04:00
} ) ,
2021-05-03 23:37:07 -04:00
} ,
} ;
2021-05-04 03:58:54 -04:00
2021-05-06 03:25:53 -04:00
client . send_raw_transaction ( transaction ) . await . unwrap ( ) ;
2021-05-03 23:37:07 -04:00
}
fn to_relative_offsets ( offsets : & [ VarInt ] ) -> Vec < VarInt > {
let vals = offsets . iter ( ) ;
let next_vals = offsets . iter ( ) . skip ( 1 ) ;
2021-05-04 02:59:44 -04:00
let diffs = vals
. zip ( next_vals )
. map ( | ( cur , next ) | VarInt ( next . 0 - cur . 0 ) ) ;
2021-05-03 23:37:07 -04:00
iter ::once ( offsets [ 0 ] . clone ( ) ) . chain ( diffs ) . collect ( )
}
2021-05-04 02:59:44 -04:00
/// First element of ring is the real pk.
fn single_party_adaptor_sig (
s_prime_a : Scalar ,
s_b : Scalar ,
ring : [ EdwardsPoint ; monero_adaptor ::RING_SIZE ] ,
msg : & [ u8 ; 32 ] ,
) -> ( monero_adaptor ::AdaptorSignature , Scalar ) {
let ( r_a , R_a , R_prime_a ) = {
let r_a = Scalar ::random ( & mut OsRng ) ;
let R_a = r_a * ED25519_BASEPOINT_POINT ;
let pk_hashed_to_point = hash_point_to_point ( ring [ 0 ] ) ;
let R_prime_a = r_a * pk_hashed_to_point ;
( r_a , R_a , R_prime_a )
} ;
let alice = monero_adaptor ::Alice0 ::new ( ring , * msg , R_a , R_prime_a , s_prime_a ) . unwrap ( ) ;
let bob = monero_adaptor ::Bob0 ::new ( ring , * msg , R_a , R_prime_a , s_b ) . unwrap ( ) ;
let msg = alice . next_message ( ) ;
let bob = bob . receive ( msg ) ;
let msg = bob . next_message ( ) ;
let alice = alice . receive ( msg ) . unwrap ( ) ;
let msg = alice . next_message ( ) ;
let bob = bob . receive ( msg ) . unwrap ( ) ;
let msg = bob . next_message ( ) ;
let alice = alice . receive ( msg ) ;
( alice . adaptor_sig , r_a )
}
2021-05-03 23:37:07 -04:00
#[ cfg(test) ]
mod tests {
use super ::* ;
#[ test ]
fn calculate_relative_key_offsets ( ) {
let key_offsets = [
VarInt ( 78 ) ,
VarInt ( 81 ) ,
VarInt ( 91 ) ,
VarInt ( 91 ) ,
VarInt ( 96 ) ,
VarInt ( 98 ) ,
VarInt ( 101 ) ,
VarInt ( 112 ) ,
VarInt ( 113 ) ,
VarInt ( 114 ) ,
VarInt ( 117 ) ,
] ;
let relative_offsets = to_relative_offsets ( & key_offsets ) ;
2021-05-04 02:59:44 -04:00
assert_eq! (
& relative_offsets ,
& [
VarInt ( 78 ) ,
VarInt ( 3 ) ,
VarInt ( 10 ) ,
VarInt ( 0 ) ,
VarInt ( 5 ) ,
VarInt ( 2 ) ,
VarInt ( 3 ) ,
VarInt ( 11 ) ,
VarInt ( 1 ) ,
VarInt ( 1 ) ,
VarInt ( 3 ) ,
]
)
2021-05-03 23:37:07 -04:00
}
2021-05-03 21:46:58 -04:00
}