Move functions for creating ethereum signatures to ethereum::signatures module

This commit is contained in:
silverpill 2022-01-26 13:01:05 +00:00
parent 8b2474c448
commit 237185bb14
6 changed files with 134 additions and 63 deletions

View file

@ -4,7 +4,8 @@ use uuid::Uuid;
use mitra::config;
use mitra::database::create_database_client;
use mitra::database::migrate::apply_migrations;
use mitra::ethereum::utils::generate_ethereum_address;
use mitra::ethereum::signatures::generate_ecdsa_key;
use mitra::ethereum::utils::key_to_ethereum_address;
use mitra::logger::configure_logger;
use mitra::models::posts::queries::delete_post;
use mitra::models::profiles::queries::delete_profile;
@ -77,7 +78,8 @@ async fn main() {
match opts.subcmd {
SubCommand::GenerateRsaKey(cmd) => cmd.execute(),
SubCommand::GenerateEthereumAddress(_) => {
let (private_key, address) = generate_ethereum_address();
let private_key = generate_ecdsa_key();
let address = key_to_ethereum_address(&private_key);
println!(
"address {:?}; private key {}",
address, private_key,

View file

@ -1,6 +1,7 @@
use crate::errors::DatabaseError;
use super::contracts::ArtifactError;
use super::utils::{AddressError, SignatureError};
use super::signatures::SignatureError;
use super::utils::AddressError;
#[derive(thiserror::Error, Debug)]
pub enum EthereumError {

View file

@ -3,4 +3,5 @@ pub mod contracts;
mod errors;
pub mod gate;
pub mod nft;
pub mod signatures;
pub mod utils;

View file

@ -6,9 +6,9 @@ use uuid::Uuid;
use web3::{
api::Web3,
contract::{Contract, Options},
ethabi::{Event, EventParam, ParamType, RawLog, token::Token, encode},
ethabi::{Event, EventParam, ParamType, RawLog, token::Token},
transports::Http,
types::{BlockNumber, FilterBuilder, H256, U256},
types::{BlockNumber, FilterBuilder, H256},
};
use crate::config::BlockchainConfig;
@ -23,7 +23,8 @@ use crate::models::posts::queries::{
use super::api::connect;
use super::contracts::{MANAGER, COLLECTIBLE, load_abi};
use super::errors::EthereumError;
use super::utils::{parse_address, sign_message, SignatureData};
use super::signatures::{sign_contract_call, CallArgs, SignatureData};
use super::utils::parse_address;
const TOKEN_WAIT_TIME: i64 = 10; // in minutes
@ -189,18 +190,17 @@ pub fn create_mint_signature(
user_address: &str,
token_uri: &str,
) -> Result<SignatureData, EthereumError> {
let contract_address = parse_address(&blockchain_config.contract_address)?;
let user_address = parse_address(user_address)?;
let chain_id: U256 = blockchain_config.ethereum_chain_id().into();
let chain_id_token = Token::Uint(chain_id);
let chain_id_bin = encode(&[chain_id_token]);
let message = [
&chain_id_bin,
contract_address.as_bytes(),
"mint".as_bytes(),
user_address.as_bytes(),
token_uri.as_bytes(),
].concat();
let signature = sign_message(&blockchain_config.signing_key, &message)?;
let call_args: CallArgs = vec![
Box::new(user_address),
Box::new(token_uri.to_string()),
];
let signature = sign_contract_call(
&blockchain_config.signing_key,
blockchain_config.ethereum_chain_id(),
&blockchain_config.contract_address,
"mint",
call_args,
)?;
Ok(signature)
}

108
src/ethereum/signatures.rs Normal file
View file

@ -0,0 +1,108 @@
use std::str::FromStr;
use secp256k1::{Error as KeyError, SecretKey, rand::rngs::OsRng};
use serde::Serialize;
use web3::ethabi::{token::Token, encode};
use web3::signing::{keccak256, Key, SigningError};
use web3::types::{Address, U256};
/// Generates signing key
pub fn generate_ecdsa_key() -> SecretKey {
let mut rng = OsRng::new().expect("failed to initialize RNG");
SecretKey::new(&mut rng)
}
#[derive(Serialize)]
pub struct SignatureData {
pub v: u64,
pub r: String,
pub s: String,
}
#[derive(thiserror::Error, Debug)]
pub enum SignatureError {
#[error("invalid key")]
InvalidKey(#[from] KeyError),
#[error("invalid data")]
InvalidData,
#[error("signing error")]
SigningError(#[from] SigningError),
}
pub fn sign_message(
signing_key: &str,
message: &[u8],
) -> Result<SignatureData, SignatureError> {
let key = SecretKey::from_str(signing_key)?;
let message_hash = keccak256(message);
let eip_191_message = [
"\x19Ethereum Signed Message:\n32".as_bytes(),
&message_hash,
].concat();
let eip_191_message_hash = keccak256(&eip_191_message);
let signature = Box::new(key).sign(&eip_191_message_hash, None)?;
let signature_data = SignatureData {
v: signature.v,
r: hex::encode(signature.r.as_bytes()),
s: hex::encode(signature.s.as_bytes()),
};
Ok(signature_data)
}
pub type CallArgs = Vec<Box<dyn AsRef<[u8]>>>;
pub fn sign_contract_call(
signing_key: &str,
chain_id: u32,
contract_address: &str,
method_name: &str,
method_args: CallArgs,
) -> Result<SignatureData, SignatureError> {
let chain_id: U256 = chain_id.into();
let chain_id_token = Token::Uint(chain_id);
let chain_id_bin = encode(&[chain_id_token]);
let contract_address = Address::from_str(contract_address)
.map_err(|_| SignatureError::InvalidData)?;
let mut message = [
&chain_id_bin,
contract_address.as_bytes(),
method_name.as_bytes(),
].concat();
for arg in method_args {
message.extend(arg.as_ref().as_ref());
};
let signature = sign_message(signing_key, &message)?;
Ok(signature)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sign_message() {
let signing_key = generate_ecdsa_key().to_string();
let message = "test_message";
let result = sign_message(&signing_key, message.as_bytes()).unwrap();
assert!(result.v == 27 || result.v == 28);
}
#[test]
fn test_sign_contract_call() {
let signing_key = generate_ecdsa_key().to_string();
let chain_id = 1;
let contract_address = "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0";
let method_name = "test";
let method_args: CallArgs = vec![Box::new("arg1"), Box::new("arg2")];
let result = sign_contract_call(
&signing_key,
chain_id,
contract_address,
method_name,
method_args,
).unwrap();
assert!(result.v == 27 || result.v == 28);
}
}

View file

@ -1,10 +1,9 @@
use std::str::FromStr;
use regex::Regex;
use secp256k1::{Error as KeyError, SecretKey, rand::rngs::OsRng};
use serde::Serialize;
use secp256k1::SecretKey;
use web3::{
signing::{keccak256, Key, SigningError},
signing::Key,
types::Address,
};
@ -34,11 +33,8 @@ pub fn parse_caip2_chain_id(chain_id: &str) -> Result<u32, ChainIdError> {
Ok(eth_chain_id)
}
pub fn generate_ethereum_address() -> (SecretKey, Address) {
let mut rng = OsRng::new().expect("failed to initialize RNG");
let secret_key = SecretKey::new(&mut rng);
let address = Box::new(secret_key).address();
(secret_key, address)
pub fn key_to_ethereum_address(private_key: &SecretKey) -> Address {
private_key.address()
}
#[derive(thiserror::Error, Debug)]
@ -49,43 +45,6 @@ pub fn parse_address(address: &str) -> Result<Address, AddressError> {
Address::from_str(address).map_err(|_| AddressError)
}
#[derive(Serialize)]
pub struct SignatureData {
pub v: u64,
pub r: String,
pub s: String,
}
#[derive(thiserror::Error, Debug)]
pub enum SignatureError {
#[error("invalid key")]
InvalidKey(#[from] KeyError),
#[error("signing error")]
SigningError(#[from] SigningError),
}
pub fn sign_message(
signing_key: &str,
message: &[u8],
) -> Result<SignatureData, SignatureError> {
let key = SecretKey::from_str(signing_key)?;
let message_hash = keccak256(message);
let eip_191_message = [
"\x19Ethereum Signed Message:\n32".as_bytes(),
&message_hash,
].concat();
let eip_191_message_hash = keccak256(&eip_191_message);
let signature = Box::new(key).sign(&eip_191_message_hash, None)?;
let signature_data = SignatureData {
v: signature.v,
r: hex::encode(signature.r.as_bytes()),
s: hex::encode(signature.s.as_bytes()),
};
Ok(signature_data)
}
#[cfg(test)]
mod tests {
use super::*;