Move functions for creating ethereum signatures to ethereum::signatures module
This commit is contained in:
parent
8b2474c448
commit
237185bb14
6 changed files with 134 additions and 63 deletions
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -3,4 +3,5 @@ pub mod contracts;
|
|||
mod errors;
|
||||
pub mod gate;
|
||||
pub mod nft;
|
||||
pub mod signatures;
|
||||
pub mod utils;
|
||||
|
|
|
@ -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
108
src/ethereum/signatures.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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::*;
|
||||
|
|
Loading…
Reference in a new issue