Skip to content

Commit

Permalink
feat(node): perform further integration of staking methods
Browse files Browse the repository at this point in the history
  • Loading branch information
aesedepece committed Jan 4, 2024
1 parent 7b152b3 commit 1f4bc00
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 191 deletions.
2 changes: 1 addition & 1 deletion crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ hmac = "0.7.1"
memzero = "0.1.0"
rand = "0.7.3"
ring = "0.16.11"
secp256k1 = { version = "0.22.2", features = ["global-context"] }
secp256k1 = { version = "0.22.2", features = ["global-context", "recovery"] }
serde = { version = "1.0.104", optional = true }
sha2 = "0.8.1"
tiny-bip39 = "0.7.0"
Expand Down
68 changes: 54 additions & 14 deletions data_structures/src/chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,20 @@ use witnet_crypto::{
key::ExtendedSK,
merkle::merkle_tree_root as crypto_merkle_tree_root,
secp256k1::{
ecdsa::Signature as Secp256k1_Signature, PublicKey as Secp256k1_PublicKey,
self,
PublicKey as Secp256k1_PublicKey,
ecdsa::{
RecoverableSignature, RecoveryId,
Signature as Secp256k1_Signature,
},
SecretKey as Secp256k1_SecretKey,
},
};
use witnet_protected::Protected;
use witnet_reputation::{ActiveReputationSet, TotalReputationSet};

use crate::{
chain::{tapi::TapiEngine, Signature::Secp256k1},
chain::{Signature::Secp256k1, tapi::TapiEngine},
data_request::{calculate_reward_collateral_ratio, DataRequestPool},
error::{
DataRequestError, EpochCalculationError, OutputPointerParseError, Secp256k1ConversionError,
Expand All @@ -45,7 +50,7 @@ use crate::{
UnstakeTransaction, VTTransaction,
},
transaction::{
MemoHash, MemoizedHashable, BETA, COMMIT_WEIGHT, OUTPUT_SIZE, REVEAL_WEIGHT, TALLY_WEIGHT,
BETA, COMMIT_WEIGHT, MemoHash, MemoizedHashable, OUTPUT_SIZE, REVEAL_WEIGHT, TALLY_WEIGHT,
},
utxo_pool::{OldUnspentOutputsPool, OwnUnspentOutputsPool, UnspentOutputsPool},
vrf::{BlockEligibilityClaim, DataRequestEligibilityClaim},
Expand Down Expand Up @@ -1236,6 +1241,15 @@ pub struct PublicKeyHash {
pub(crate) hash: [u8; 20],
}

impl PublicKeyHash {
pub fn as_secp256k1_msg(&self) -> [u8; secp256k1::constants::MESSAGE_SIZE] {
let mut msg = [0u8; secp256k1::constants::MESSAGE_SIZE];
msg[0..20].clone_from_slice(self.as_ref());

msg
}
}

impl AsRef<[u8]> for PublicKeyHash {
fn as_ref(&self) -> &[u8] {
self.hash.as_ref()
Expand Down Expand Up @@ -1556,6 +1570,33 @@ pub struct KeyedSignature {
pub public_key: PublicKey,
}

impl KeyedSignature {
pub fn from_recoverable_hex(string: &str, msg: &[u8]) -> Self {
let bytes = hex::decode(string).unwrap();

Self::from_recoverable_slice(&bytes, msg)
}
pub fn from_recoverable(recoverable: &RecoverableSignature, message: &[u8]) -> Self {
let msg = secp256k1::Message::from_slice(message).unwrap();
let signature = recoverable.to_standard();
let public_key = recoverable.recover(&msg).unwrap();

KeyedSignature {
signature: signature.into(),
public_key: public_key.into(),
}
}

// Recovers a keyed signature from its serialized form and a known message.
pub fn from_recoverable_slice(compact: &[u8], message: &[u8]) -> Self {
let recid = RecoveryId::from_i32(0).unwrap();
let recoverable =
secp256k1::ecdsa::RecoverableSignature::from_compact(compact, recid).unwrap();

Self::from_recoverable(&recoverable, message)
}
}

/// Public Key data structure
#[derive(Debug, Default, Eq, PartialEq, Clone, Hash, Serialize, Deserialize)]
pub struct PublicKey {
Expand Down Expand Up @@ -1597,21 +1638,20 @@ impl PublicKey {
}
}

pub fn from_str(serialized: &str) -> Self {
let mut pk = hex::decode(serialized).unwrap();
pk.resize(33, 0);
let mut array_bytes = [0u8; 33];
array_bytes.copy_from_slice(&pk[..33]);

Self::from_bytes(array_bytes)
}

/// Returns the PublicKeyHash related to the PublicKey
pub fn pkh(&self) -> PublicKeyHash {
PublicKeyHash::from_public_key(self)
}
}

impl std::str::FromStr for PublicKey {
type Err = Secp256k1ConversionError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from_slice(s.as_bytes())
}
}

/// Secret Key data structure
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub struct SecretKey {
Expand Down Expand Up @@ -4465,14 +4505,14 @@ pub fn block_example() -> Block {
#[cfg(test)]
mod tests {
use witnet_crypto::{
merkle::{merkle_tree_root, InclusionProof},
merkle::{InclusionProof, merkle_tree_root},
secp256k1::{PublicKey as Secp256k1_PublicKey, SecretKey as Secp256k1_SecretKey},
signature::sign,
};

use crate::{
proto::versioning::{ProtocolVersion, VersionedHashable},
superblock::{mining_build_superblock, ARSIdentities},
superblock::{ARSIdentities, mining_build_superblock},
transaction::{CommitTransactionBody, RevealTransactionBody, VTTransactionBody},
};

Expand Down
213 changes: 88 additions & 125 deletions node/src/actors/json_rpc/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ use itertools::Itertools;
use jsonrpc_core::{BoxFuture, Error, Params, Value};
use jsonrpc_pubsub::{Subscriber, SubscriptionId};
use serde::{Deserialize, Serialize};
use witnet_crypto::{key::KeyPath, secp256k1::ecdsa::Signature};
use witnet_crypto::key::KeyPath;
use witnet_data_structures::{
chain::{
tapi::ActiveWips, Block, DataRequestOutput, Environment, Epoch, Hash, Hashable,
KeyedSignature, PublicKey, PublicKeyHash, RADType, StakeOutput, StateMachine, SyncStatus,
tapi::ActiveWips, Block, DataRequestOutput, Epoch, Hash, Hashable, KeyedSignature,
PublicKeyHash, RADType, StakeOutput, StateMachine, SyncStatus,
},
get_environment,
transaction::Transaction,
vrf::VrfMessage,
};
Expand All @@ -36,14 +37,13 @@ use crate::{
inventory_manager::{InventoryManager, InventoryManagerError},
json_rpc::Subscriptions,
messages::{
AddCandidates, AddPeers, AddTransaction, AuthorizationParams, AuthorizeStake, BuildDrt,
BuildStake, BuildStakeParams, BuildVtt, ClearPeers, DropAllPeers, EstimatePriority,
GetBalance, GetBalanceTarget, GetBlocksEpochRange, GetConsolidatedPeers,
GetDataRequestInfo, GetEpoch, GetHighestCheckpointBeacon, GetItemBlock,
GetItemSuperblock, GetItemTransaction, GetKnownPeers, GetMemoryTransaction, GetMempool,
GetNodeStats, GetReputation, GetSignalingInfo, GetState, GetSupplyInfo, GetUtxoInfo,
InitializePeers, IsConfirmedBlock, Rewind, SnapshotExport, SnapshotImport,
StakeAuthorization,
AddCandidates, AddPeers, AddTransaction, AuthorizeStake, BuildDrt, BuildStake,
BuildStakeParams, BuildVtt, ClearPeers, DropAllPeers, EstimatePriority, GetBalance,
GetBalanceTarget, GetBlocksEpochRange, GetConsolidatedPeers, GetDataRequestInfo,
GetEpoch, GetHighestCheckpointBeacon, GetItemBlock, GetItemSuperblock,
GetItemTransaction, GetKnownPeers, GetMemoryTransaction, GetMempool, GetNodeStats,
GetReputation, GetSignalingInfo, GetState, GetSupplyInfo, GetUtxoInfo, InitializePeers,
IsConfirmedBlock, Rewind, SnapshotExport, SnapshotImport, StakeAuthorization,
},
peers_manager::PeersManager,
sessions_manager::SessionsManager,
Expand Down Expand Up @@ -1940,83 +1940,62 @@ pub async fn snapshot_import(params: Result<SnapshotImportParams, Error>) -> Jso
}
/// Build a stake transaction
pub async fn stake(params: Result<BuildStakeParams, Error>) -> JsonRpcResult {
log::debug!("Creating stake transaction from JSON-RPC.");
// Short-circuit if parameters are wrong
let params = params?;

match params {
Ok(msg) => {
let withdrawer = match msg.withdrawer {
Some(withdrawer) => withdrawer,
None => {
let pk = signature_mngr::public_key()
.map(|res| {
res.map_err(internal_error)
.map(|pk| pk.pkh().bech32(Environment::Mainnet))
})
.await;

pk.unwrap()
}
};

let authorization: AuthorizationParams = match msg.authorization {
Some(authorization) => authorization,
None => {
let mut data = [0u8; witnet_crypto::secp256k1::constants::MESSAGE_SIZE];
data[0..20].clone_from_slice(withdrawer.as_ref());
// If a withdrawer address is not specified, default to local node address
let withdrawer = if let Some(address) = params.withdrawer {
address.try_do_magic(|hex_str| PublicKeyHash::from_bech32(get_environment(), &hex_str)).unwrap()
} else {
let pk = signature_mngr::public_key().await.unwrap();

let keyed_signature = signature_mngr::sign_data(data)
.map(|res| res.map_err(internal_error))
.await
.unwrap();
PublicKeyHash::from_public_key(&pk)
};

AuthorizationParams {
authorization: hex::encode(keyed_signature.signature.to_bytes().unwrap()),
public_key: hex::encode(keyed_signature.public_key.to_bytes()),
}
}
};
// This is the actual message that gets signed as part of the authorization
let msg = withdrawer.as_secp256k1_msg();

let signature = Signature::from_str(&authorization.authorization).unwrap();
let authorization = KeyedSignature {
signature: signature.into(),
// TODO: https://docs.rs/secp256k1/0.22.2/secp256k1/struct.Secp256k1.html#method.recover_ecdsa
// public_key: signature.recover_ecdsa(withdrawer, signature)
public_key: PublicKey::from_str(&authorization.public_key),
};
// If no authorization message is provided, generate a new one using the withdrawer address
let authorization = if let Some(signature) = params.authorization {
signature.do_magic(|hex_str| KeyedSignature::from_recoverable_hex(&hex_str, &msg))
} else {
signature_mngr::sign_data(msg)
.map(|res| res.map_err(internal_error))
.await
.unwrap()
};

let build_stake = BuildStake {
dry_run: msg.dry_run,
fee: msg.fee,
utxo_strategy: msg.utxo_strategy,
stake_output: StakeOutput {
authorization,
value: msg.value,
},
};
// Construct a BuildStake message that we can relay to the ChainManager for creation of the Stake transaction
let build_stake = BuildStake {
dry_run: params.dry_run,
fee: params.fee,
utxo_strategy: params.utxo_strategy,
stake_output: StakeOutput {
authorization,
value: params.value,
},
};

ChainManager::from_registry()
.send(build_stake)
.map(|res| match res {
Ok(Ok(hash)) => match serde_json::to_value(hash) {
Ok(x) => Ok(x),
Err(e) => {
let err = internal_error_s(e);
Err(err)
}
},
Ok(Err(e)) => {
let err = internal_error_s(e);
Err(err)
}
Err(e) => {
let err = internal_error_s(e);
Err(err)
}
})
.await
}
Err(err) => Err(err),
}
ChainManager::from_registry()
.send(build_stake)
.map(|res| match res {
Ok(Ok(hash)) => match serde_json::to_value(hash) {
Ok(x) => Ok(x),
Err(e) => {
let err = internal_error_s(e);
Err(err)
}
},
Ok(Err(e)) => {
let err = internal_error_s(e);
Err(err)
}
Err(e) => {
let err = internal_error_s(e);
Err(err)
}
})
.await
}

/// Create a stake authorization for the given address.
Expand All @@ -2026,49 +2005,33 @@ pub async fn stake(params: Result<BuildStakeParams, Error>) -> JsonRpcResult {
{"jsonrpc": "2.0","method": "authorizeStake", "params": {"withdrawer":"wit1lkzl4a365fvrr604pwqzykxugpglkrp5ekj0k0"}, "id": "1"}
*/
pub async fn authorize_stake(params: Result<AuthorizeStake, Error>) -> JsonRpcResult {
print!("Inside authorize_stake");
log::debug!("Creating an authorization stake from JSON-RPC.");
match params {
Ok(msg) => {
let mut withdrawer = msg.withdrawer;
if withdrawer.is_none() {
let pk = signature_mngr::public_key()
.map(|res| {
res.map_err(internal_error)
.map(|pk| pk.pkh().bech32(Environment::Mainnet))
})
.await;
withdrawer = Some(pk.unwrap());
}
// FIXME: we could use directly the pk calculated above in one case
let pkh =
PublicKeyHash::from_bech32(Environment::Mainnet, &withdrawer.clone().unwrap())
.unwrap();
let mut data = [0u8; witnet_crypto::secp256k1::constants::MESSAGE_SIZE];
data[0..20].clone_from_slice(pkh.as_ref());

signature_mngr::sign_data(data)
.map(|res| {
res.map_err(internal_error).and_then(|ks| {
let a = StakeAuthorization {
withdrawer: withdrawer.unwrap(),
signature: hex::encode(ks.signature.to_bytes().unwrap()),
public_key: hex::encode(ks.public_key.to_bytes()),
};

match serde_json::to_value(a) {
Ok(value) => Ok(value),
Err(e) => {
let err = internal_error_s(e);
Err(err)
}
}
})
})
.await
}
Err(err) => Err(err),
}
// Short-circuit if parameters are wrong
let params = params?;

// If a withdrawer address is not specified, default to local node address
let withdrawer = if let Some(address) = params.withdrawer {
PublicKeyHash::from_bech32(get_environment(), &address).map_err(internal_error)?
} else {
let pk = signature_mngr::public_key().await.unwrap();

PublicKeyHash::from_public_key(&pk)
};

// This is the actual message that gets signed as part of the authorization
let msg = withdrawer.as_secp256k1_msg();

signature_mngr::sign_data(msg)
.map(|res| {
res.map_err(internal_error).and_then(|signature| {
let authorization = StakeAuthorization {
withdrawer,
signature,
};

serde_json::to_value(authorization).map_err(internal_error)
})
})
.await
}

#[cfg(test)]
Expand Down
Loading

0 comments on commit 1f4bc00

Please sign in to comment.