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

View file

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

View file

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

View file

@ -6,9 +6,9 @@ use uuid::Uuid;
use web3::{ use web3::{
api::Web3, api::Web3,
contract::{Contract, Options}, contract::{Contract, Options},
ethabi::{Event, EventParam, ParamType, RawLog, token::Token, encode}, ethabi::{Event, EventParam, ParamType, RawLog, token::Token},
transports::Http, transports::Http,
types::{BlockNumber, FilterBuilder, H256, U256}, types::{BlockNumber, FilterBuilder, H256},
}; };
use crate::config::BlockchainConfig; use crate::config::BlockchainConfig;
@ -23,7 +23,8 @@ use crate::models::posts::queries::{
use super::api::connect; use super::api::connect;
use super::contracts::{MANAGER, COLLECTIBLE, load_abi}; use super::contracts::{MANAGER, COLLECTIBLE, load_abi};
use super::errors::EthereumError; 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 const TOKEN_WAIT_TIME: i64 = 10; // in minutes
@ -189,18 +190,17 @@ pub fn create_mint_signature(
user_address: &str, user_address: &str,
token_uri: &str, token_uri: &str,
) -> Result<SignatureData, EthereumError> { ) -> Result<SignatureData, EthereumError> {
let contract_address = parse_address(&blockchain_config.contract_address)?;
let user_address = parse_address(user_address)?; let user_address = parse_address(user_address)?;
let chain_id: U256 = blockchain_config.ethereum_chain_id().into(); let call_args: CallArgs = vec![
let chain_id_token = Token::Uint(chain_id); Box::new(user_address),
let chain_id_bin = encode(&[chain_id_token]); Box::new(token_uri.to_string()),
let message = [ ];
&chain_id_bin, let signature = sign_contract_call(
contract_address.as_bytes(), &blockchain_config.signing_key,
"mint".as_bytes(), blockchain_config.ethereum_chain_id(),
user_address.as_bytes(), &blockchain_config.contract_address,
token_uri.as_bytes(), "mint",
].concat(); call_args,
let signature = sign_message(&blockchain_config.signing_key, &message)?; )?;
Ok(signature) 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 std::str::FromStr;
use regex::Regex; use regex::Regex;
use secp256k1::{Error as KeyError, SecretKey, rand::rngs::OsRng}; use secp256k1::SecretKey;
use serde::Serialize;
use web3::{ use web3::{
signing::{keccak256, Key, SigningError}, signing::Key,
types::Address, types::Address,
}; };
@ -34,11 +33,8 @@ pub fn parse_caip2_chain_id(chain_id: &str) -> Result<u32, ChainIdError> {
Ok(eth_chain_id) Ok(eth_chain_id)
} }
pub fn generate_ethereum_address() -> (SecretKey, Address) { pub fn key_to_ethereum_address(private_key: &SecretKey) -> Address {
let mut rng = OsRng::new().expect("failed to initialize RNG"); private_key.address()
let secret_key = SecretKey::new(&mut rng);
let address = Box::new(secret_key).address();
(secret_key, address)
} }
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
@ -49,43 +45,6 @@ pub fn parse_address(address: &str) -> Result<Address, AddressError> {
Address::from_str(address).map_err(|_| 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;