Reputation: 365
Why in the bottom section of this code, do I need to use the following pattern:
let a = urlpath.to_string();
let b = nonce.to_string();
let c = ordertype.to_string();
let d = pair.to_string();
let e = price.to_string();
let f = type_.to_string();
let g = volume.to_string();
let h = api_sec.to_string();
let kwargs = vec![("cmd", "account_balance"), ("urlpath", &a), ("nonce", &b), ("ordertype", &c), ("pair", &d), ("price", &e), ("type", &f), ("volume", &g), ("secret", &h)];
If I replace the variable &a in the vec! with &urlpath.to_string() then it fails saying a temporary value is being dropped and it's later used.
But doesn't that expression evaluate to the same thing, regardless if I add the additional let statements? How can I make this more Rust idiomatic?
use std::{time::{SystemTime, UNIX_EPOCH}};
use pyo3::types::IntoPyDict;
fn main() -> PyResult<()> {
let urlpath = "/0/private/Balance";
println!("{}", urlpath);
let api_sec = "<REPLACE>";
println!("{}", api_sec);
let nonce = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_millis();
println!("{}", nonce);
let ordertype = "limit";
println!("{}", ordertype);
let pair = "XBTUSD";
println!("{}", pair);
let price: i32 = 37500;
println!("{}", price);
let type_ = "buy";
println!("{}", type_);
let volume = 1.25;
println!("{}", volume);
Python::with_gil(|py| {
let fun: Py<PyAny> = PyModule::from_code(
py,
"
import urllib.parse
import hashlib
import hmac
import base64
def get_kraken_signature(*args, **kwargs):
if args != ():
print('called with args', args)
if kwargs != {}:
print('called with kwargs', kwargs)
if args == () and kwargs == {}:
print('called with no arguments')
if kwargs[\"cmd\"] == \"account_balance\":
urlpath = kwargs[\"urlpath\"]
data = {
\"nonce\": kwargs[\"nonce\"],
}
secret = kwargs[\"secret\"]
elif kwargs[\"cmd\"] == \"send_order\":
urlpath = kwargs[\"urlpath\"]
data = {
\"nonce\": kwargs[\"nonce\"],
\"ordertype\": kwargs[\"ordertype\"],
\"pair\": kwargs[\"pair\"],
\"price\": kwargs[\"price\"],
\"type\": kwargs[\"type\"],
\"volume\": kwargs[\"volume\"],
}
secret = kwargs[\"secret\"]
else:
exit(0)
postdata = urllib.parse.urlencode(data)
encoded = (str(data['nonce']) + postdata).encode()
message = urlpath.encode() + hashlib.sha256(encoded).digest()
mac = hmac.new(base64.b64decode(secret), message, hashlib.sha512)
sigdigest = base64.b64encode(mac.digest())
print(\"API-Sign: {}\".format(sigdigest.decode()))
return sigdigest.decode()
",
"",
"",
)?.getattr("get_kraken_signature")?.into();
let a = urlpath.to_string();
let b = nonce.to_string();
let c = ordertype.to_string();
let d = pair.to_string();
let e = price.to_string();
let f = type_.to_string();
let g = volume.to_string();
let h = api_sec.to_string();
let kwargs = vec![("cmd", "account_balance"), ("urlpath", &a), ("nonce", &b), ("ordertype", &c), ("pair", &d), ("price", &e), ("type", &f), ("volume", &g), ("secret", &h)];
let result = fun.call(py, (), Some(kwargs.into_py_dict(py)))?;
println!("{}", result);
Ok(())
})
}
BONUS: Second part of the question, is how can I rewrite the Python portion in idiomatic Rust? I have tried and failed, so would be helpful if any crypto experts can assist.
Upvotes: 1
Views: 160
Reputation: 11807
First Part: (Explanation)
Since a
is the owner of the value and you are passing reference using the owner which will remain in scope even after execution but in the case when you directly pass &urlpath.to_string()
there isn't any owner and as soon the execution ends the value would be dropped and there will be a dangling reference which is the cause for the message.
Second Part: (Python to rust conversion)
I am not a crypto expert but I tried to convert the same script you provided without the condition part and matched the output in python and rust.
extern crate url;
extern crate base64;
// use std::time::{SystemTime, UNIX_EPOCH};
use url::form_urlencoded;
use sha2::{Sha256, Digest};
extern crate ring;
extern crate data_encoding;
use ring::hmac;
use data_encoding::BASE64;
use std::collections::HashMap;
fn main() {
let urlpath = String::from("/0/private/Balance");
// let nonce = SystemTime::now()
// .duration_since(UNIX_EPOCH)
// .expect("Time went backwards")
// .as_millis();
let nonce: &str = &(1645371362680 as i64).to_string();
let mut data = HashMap::new();
data.insert("nonce", nonce);
let postdata: String = form_urlencoded::Serializer::new(String::new())
.extend_pairs(data.iter())
.finish();
let encoded = format!("{}{}", nonce, postdata);
let message: Vec<u8> = [urlpath.as_bytes(), Sha256::digest(encoded.as_bytes()).as_ref()].concat();
let secret_key = String::from("secret");
let signed_key = hmac::Key::new(hmac::HMAC_SHA512, secret_key.as_bytes());
let signature = hmac::sign(&signed_key, &message);
let b64_encoded_sig = BASE64.encode(signature.as_ref());
println!("Output: {}", b64_encoded_sig);
}
Upvotes: 1