Skip to content

Commit

Permalink
Merge pull request #23 from consenlabs/feature/bls-to-execution-change
Browse files Browse the repository at this point in the history
feat: add BLSToExecutionEhange signature feature
  • Loading branch information
XuNeal committed Mar 30, 2023
2 parents 31f083f + 9d2ed31 commit d61e0e4
Show file tree
Hide file tree
Showing 12 changed files with 407 additions and 10 deletions.
31 changes: 31 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.5.0
2.6.0
9 changes: 8 additions & 1 deletion token-core/tcx-eth2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,11 @@ tcx-constants = { path = "../tcx-constants" }
tcx-primitive = { path = "../tcx-primitive" }
tcx-crypto = { path = "../tcx-crypto" }

hex = "0.3.1"
hex = "0.3.1"
prost = "0.11.2"
bytes = "1.3.0"
lazy_static = "1.4.0"
prost-types = "0.11.2"
failure = "0.1.8"
ssz-rs = { git = "https://github.com/ChorusOne/ssz-rs.git", rev = "cb08f18ca919cc1b685b861d0fa9e2daabe89737" }
ssz-rs-derive = { git = "https://github.com/ChorusOne/ssz-rs.git", rev = "cb08f18ca919cc1b685b861d0fa9e2daabe89737" }
144 changes: 144 additions & 0 deletions token-core/tcx-eth2/src/bls_to_execution_change.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
use crate::hex_to_bytes;
use lazy_static::lazy_static;
use ssz_rs::prelude::*;
use ssz_rs::{Node, Vector};
use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};
use std::str::FromStr;

const DOMAIN_LEN: usize = 32;
const DOMAIN_TYPE_LEN: usize = 4;
const BLS_PUBKEY_LEN: usize = 48;
const EXECUTION_ADDR_LEN: usize = 20;
type DomainType = Vector<u8, DOMAIN_TYPE_LEN>;
type Domain = Vector<u8, DOMAIN_LEN>;
type Version = Vector<u8, 4>;
type BLSPubkey = Vector<u8, BLS_PUBKEY_LEN>;
type ExecutionAddress = Vector<u8, EXECUTION_ADDR_LEN>;

lazy_static! {
static ref DOMAIN_BLS_TO_EXECUTION_CHANGE: DomainType =
Vector::<u8, DOMAIN_TYPE_LEN>::deserialize(&[0x0A, 0, 0, 0])
.expect("failed to deserialize");
}

impl BLSToExecutionRequest {
pub fn generate_bls_to_execution_change_hash(&self) -> tcx_constants::Result<String> {
let to_execution_address_bytes = hex_to_bytes(&self.to_execution_address)?;
let to_execution_address =
Vector::<u8, EXECUTION_ADDR_LEN>::deserialize(to_execution_address_bytes.as_ref())?;
let from_bls_pubkey_bytes = hex_to_bytes(&self.from_bls_pubkey)?;
let from_bls_pubkey =
Vector::<u8, BLS_PUBKEY_LEN>::deserialize(from_bls_pubkey_bytes.as_ref())?;
let validator_index = self.validator_index;
let message = BLSToExecutionChange {
validator_index,
from_bls_pubkey,
to_execution_address,
};

let fork_version = Vector::<u8, DOMAIN_TYPE_LEN>::deserialize(
hex_to_bytes(&self.genesis_fork_version)?.as_slice(),
)?;
let validator_root =
Node::try_from(hex_to_bytes(&self.genesis_validators_root)?.as_slice())?;
let domain = compute_domain(
&DOMAIN_BLS_TO_EXECUTION_CHANGE,
fork_version,
&validator_root,
)?;
let signing_root = compute_signing_root(message.clone(), domain)?;
let message = signing_root.as_bytes();
Ok(hex::encode(message))
}
}

pub fn compute_domain(
domain_type: &DomainType,
fork_version: Version,
genesis_validators_root: &Node,
) -> Result<Domain, MerkleizationError> {
if domain_type.len() != 4 {
//todo
}
let fork_data_root = compute_fork_data_root(fork_version, genesis_validators_root)?;
let mut bytes = Vec::new();
domain_type.serialize(&mut bytes)?;
fork_data_root.serialize(&mut bytes)?;
Ok(Vector::deserialize(&bytes[0..DOMAIN_LEN]).expect("invalid domain data"))
}

pub fn compute_fork_data_root(
current_version: Version,
genesis_validators_root: &Node,
) -> Result<Node, MerkleizationError> {
ForkData {
current_version,
genesis_validators_root: genesis_validators_root.to_owned(),
}
.hash_tree_root()
}

pub fn compute_signing_root<T: SimpleSerialize>(
mut ssz_object: T,
domain: Domain,
) -> Result<Node, MerkleizationError> {
SigningData {
object_root: ssz_object.hash_tree_root()?,
domain,
}
.hash_tree_root()
}

#[derive(SimpleSerialize, Default)]
pub struct ForkData {
current_version: Version,
genesis_validators_root: Node,
}

#[derive(SimpleSerialize, Default)]
pub struct SigningData {
object_root: Node,
domain: Domain,
}

#[derive(SimpleSerialize, Default, Clone, Debug)]
pub struct BLSToExecutionChange {
validator_index: u32,
from_bls_pubkey: BLSPubkey,
to_execution_address: ExecutionAddress,
}

#[derive(Clone)]
pub struct BLSToExecutionRequest {
pub genesis_fork_version: String,
pub genesis_validators_root: String,
pub validator_index: u32,
pub from_bls_pubkey: String,
pub to_execution_address: String,
}

#[cfg(test)]
mod test {
use crate::bls_to_execution_change::BLSToExecutionRequest;
use crate::transaction::SignBlsToExecutionChangeParam;
use tcx_chain::Keystore;

#[test]
fn test_generate_bls_to_execution_change_hash() {
let mut blsToExecutionRequest = BLSToExecutionRequest {
genesis_fork_version: "0x03000000".to_string(),
genesis_validators_root: "0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95".to_string(),
validator_index: 0,
from_bls_pubkey: "0x99b1f1d84d76185466d86c34bde1101316afddae76217aa86cd066979b19858c2c9d9e56eebc1e067ac54277a61790db".to_string(),
to_execution_address: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15".to_string(),
};
let ret_data = blsToExecutionRequest
.generate_bls_to_execution_change_hash()
.expect("generate_bls_to_execution_change_hash_error");
assert_eq!(
ret_data,
"23ba0fe9dc5d2fae789f31fdccb4e28e74b89aec26bafdd6c96ced598542f53e"
);
}
}
26 changes: 20 additions & 6 deletions token-core/tcx-eth2/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
extern crate core;

pub mod address;
mod bls_to_execution_change;
pub mod signer;
pub mod transaction;
use failure::Fail;
use tcx_chain::Result;

#[derive(Fail, Debug, PartialEq)]
pub enum Error {
#[fail(display = "invalid_hex_value")]
InvalidHexValue,
}

#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
pub fn hex_to_bytes(value: &str) -> Result<Vec<u8>> {
let result = if value.starts_with("0x") || value.starts_with("0X") {
hex::decode(&value[2..])
} else {
hex::decode(&value[..])
};
result.map_err(|_| Error::InvalidHexValue.into())
}
50 changes: 50 additions & 0 deletions token-core/tcx-eth2/src/signer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use crate::bls_to_execution_change::{compute_domain, BLSToExecutionRequest};
use crate::transaction::{
BlsToExecutionChangeMessage, SignBlsToExecutionChangeParam, SignBlsToExecutionChangeResult,
SignedBlsToExecutionChange,
};
use crate::{hex_to_bytes, Error, Result};
use ssz_rs::{Deserialize, Vector};
use tcx_chain::{
ChainSigner, Keystore, KeystoreGuard, TransactionSigner as TraitTransactionSigner,
};

impl SignBlsToExecutionChangeParam {
pub fn sign_bls_to_execution_change(
&self,
keystore: &mut Keystore,
) -> Result<SignBlsToExecutionChangeResult> {
let mut blsToExecutionRequest = BLSToExecutionRequest {
genesis_fork_version: self.genesis_fork_version.to_string(),
genesis_validators_root: self.genesis_validators_root.to_string(),
validator_index: 0,
from_bls_pubkey: self.from_bls_pub_key.to_string(),
to_execution_address: self.eth1_withdrawal_address.to_string(),
};
let mut signeds = vec![];
for validator_index in &self.validator_index {
blsToExecutionRequest.validator_index = *validator_index;
let message = blsToExecutionRequest.generate_bls_to_execution_change_hash()?;

let signature = keystore.sign_hash(
hex::decode(message)?.as_slice(),
"ETHEREUM2",
self.from_bls_pub_key.as_str(),
None,
)?;
let blsToExecutionChangeMessage = BlsToExecutionChangeMessage {
validator_index: *validator_index,
from_bls_pubkey: self.from_bls_pub_key.to_string(),
to_execution_address: self.eth1_withdrawal_address.to_string(),
};
let signedBlsToExecutionChange = SignedBlsToExecutionChange {
message: Some(blsToExecutionChangeMessage),
signature: hex::encode(signature),
};
signeds.push(signedBlsToExecutionChange);
}

let signBlsToExecutionChangeResult = SignBlsToExecutionChangeResult { signeds };
Ok(signBlsToExecutionChangeResult)
}
}
42 changes: 42 additions & 0 deletions token-core/tcx-eth2/src/transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SignBlsToExecutionChangeParam {
#[prost(string, tag = "1")]
pub id: ::prost::alloc::string::String,
#[prost(string, tag = "2")]
pub password: ::prost::alloc::string::String,
#[prost(string, tag = "3")]
pub genesis_fork_version: ::prost::alloc::string::String,
#[prost(string, tag = "4")]
pub genesis_validators_root: ::prost::alloc::string::String,
#[prost(uint32, repeated, tag = "5")]
pub validator_index: ::prost::alloc::vec::Vec<u32>,
#[prost(string, tag = "6")]
pub from_bls_pub_key: ::prost::alloc::string::String,
#[prost(string, tag = "7")]
pub eth1_withdrawal_address: ::prost::alloc::string::String,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SignBlsToExecutionChangeResult {
#[prost(message, repeated, tag = "1")]
pub signeds: ::prost::alloc::vec::Vec<SignedBlsToExecutionChange>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SignedBlsToExecutionChange {
#[prost(message, optional, tag = "1")]
pub message: ::core::option::Option<BlsToExecutionChangeMessage>,
#[prost(string, tag = "2")]
pub signature: ::prost::alloc::string::String,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BlsToExecutionChangeMessage {
#[prost(uint32, tag = "1")]
pub validator_index: u32,
#[prost(string, tag = "2")]
pub from_bls_pubkey: ::prost::alloc::string::String,
#[prost(string, tag = "3")]
pub to_execution_address: ::prost::alloc::string::String,
}
2 changes: 1 addition & 1 deletion token-core/tcx-primitive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ tiny-bip39 = "0.7.3"
ed25519-bip32 = "0.3.2"
blake2b_simd = "1.0.0"
iop-keyvault = "0.0.5"
bls-signatures = "0.13.0"
bls-signatures = { git = "https://github.com/XuNeal/bls-signatures.git", branch = "feat/pop_dst" }
#rust-crypto = "0.2"
num-bigint = "0.4"
num-traits = "0.2"
Expand Down
4 changes: 4 additions & 0 deletions token-core/tcx-proto/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,8 @@ fn main() {
//tcx-tezos
env::set_var("OUT_DIR", "../tcx-tezos/src");
prost_build::compile_protos(&["src/tezos.proto"], &["src/"]).unwrap();

//tcx-eth2
env::set_var("OUT_DIR", "../tcx-eth2/src");
prost_build::compile_protos(&["src/eth2.proto"], &["src/"]).unwrap();
}
26 changes: 26 additions & 0 deletions token-core/tcx-proto/src/eth2.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
syntax = "proto3";
package transaction;
message SignBLSToExecutionChangeParam {
string id = 1;
string password = 2;
string genesisForkVersion = 3;
string genesisValidatorsRoot = 4;
repeated uint32 validatorIndex = 5;
string fromBlsPubKey = 6;
string eth1WithdrawalAddress = 7;
}

message SignBLSToExecutionChangeResult {
repeated SignedBLSToExecutionChange signeds = 1;
}

message SignedBLSToExecutionChange {
BLSToExecutionChangeMessage message = 1;
string signature = 2;
}

message BLSToExecutionChangeMessage{
uint32 validator_index = 1;
string from_bls_pubkey = 2;
string to_execution_address = 3;
}

0 comments on commit d61e0e4

Please sign in to comment.