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::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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
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 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::*;
|
||||||
|
|
Loading…
Reference in a new issue