Skip to content

Commit 4cc3a8c

Browse files
committed
feat(node): process stake transactions when consolidating blocks
1 parent c131f74 commit 4cc3a8c

File tree

12 files changed

+230
-41
lines changed

12 files changed

+230
-41
lines changed

data_structures/build.rs

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ fn create_path_to_protobuf_schema_env() {
1414
}
1515

1616
fn main() {
17+
println!("cargo:rerun-if-changed=../schemas/witnet/witnet.proto");
18+
1719
create_path_to_protobuf_schema_env();
1820

1921
exonum_build::protobuf_generate(

data_structures/src/chain/mod.rs

+23-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use witnet_crypto::{
2525
secp256k1::{
2626
self,
2727
ecdsa::{RecoverableSignature, RecoveryId, Signature as Secp256k1_Signature},
28-
PublicKey as Secp256k1_PublicKey, SecretKey as Secp256k1_SecretKey,
28+
Message, PublicKey as Secp256k1_PublicKey, SecretKey as Secp256k1_SecretKey,
2929
},
3030
};
3131
use witnet_protected::Protected;
@@ -1012,6 +1012,25 @@ impl Signature {
10121012
}
10131013
}
10141014
}
1015+
1016+
pub fn verify(
1017+
&self,
1018+
msg: &Message,
1019+
public_key: &Secp256k1_PublicKey,
1020+
) -> Result<(), failure::Error> {
1021+
match self {
1022+
Secp256k1(x) => {
1023+
let signature = Secp256k1_Signature::from_der(x.der.as_slice())
1024+
.map_err(|_| Secp256k1ConversionError::FailSignatureConversion)?;
1025+
1026+
signature
1027+
.verify(msg, public_key)
1028+
.map_err(|inner| Secp256k1ConversionError::Secp256k1 { inner })?;
1029+
1030+
Ok(())
1031+
}
1032+
}
1033+
}
10151034
}
10161035

10171036
/// ECDSA (over secp256k1) signature
@@ -1498,11 +1517,12 @@ impl DataRequestOutput {
14981517
}
14991518
}
15001519

1501-
#[derive(Debug, Default, Eq, PartialEq, Clone, Serialize, Deserialize, ProtobufConvert, Hash)]
1520+
#[derive(Debug, Default, Eq, PartialEq, Clone, Hash, Serialize, Deserialize, ProtobufConvert)]
15021521
#[protobuf_convert(pb = "crate::proto::schema::witnet::StakeOutput")]
15031522
pub struct StakeOutput {
1504-
pub value: u64,
15051523
pub authorization: KeyedSignature,
1524+
pub key: StakeKey<PublicKeyHash>,
1525+
pub value: u64,
15061526
}
15071527

15081528
impl StakeOutput {

data_structures/src/staking/aux.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
use std::{rc::Rc, str::FromStr, sync::RwLock};
22

3+
use failure::Error;
34
use serde::{Deserialize, Serialize};
45

6+
use crate::{chain::PublicKeyHash, proto::ProtobufConvert};
7+
58
use super::prelude::*;
69

10+
/// Just a type alias for consistency of using the same data type to represent power.
11+
pub type Power = u64;
12+
713
/// The resulting type for all the fallible functions in this module.
814
pub type StakingResult<T, Address, Coins, Epoch> = Result<T, StakesError<Address, Coins, Epoch>>;
915

@@ -37,14 +43,27 @@ where
3743

3844
/// Couples a validator address with a withdrawer address together. This is meant to be used in `Stakes` as the index
3945
/// for the `by_key` index.
40-
#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
46+
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
4147
pub struct StakeKey<Address> {
4248
/// A validator address.
4349
pub validator: Address,
4450
/// A withdrawer address.
4551
pub withdrawer: Address,
4652
}
4753

54+
impl ProtobufConvert for StakeKey<PublicKeyHash> {
55+
type ProtoStruct = crate::proto::schema::witnet::StakeKey;
56+
57+
fn to_pb(&self) -> Self::ProtoStruct {
58+
let _proto = Self::ProtoStruct::new();
59+
todo!()
60+
}
61+
62+
fn from_pb(_pb: Self::ProtoStruct) -> Result<Self, Error> {
63+
todo!()
64+
}
65+
}
66+
4867
impl<Address, T> From<(T, T)> for StakeKey<Address>
4968
where
5069
T: Into<Address>,

data_structures/src/staking/errors.rs

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ pub enum StakesError<Address, Coins, Epoch> {
3434
},
3535
/// Tried to obtain a lock on a write-locked piece of data that is already locked.
3636
PoisonedLock,
37+
/// The authentication signature contained within a stake transaction is not valid for the given validator and
38+
/// withdrawer addresses.
39+
InvalidAuthentication,
3740
}
3841

3942
impl<T, Address, Coins, Epoch> From<PoisonError<T>> for StakesError<Address, Coins, Epoch> {

data_structures/src/staking/stake.rs

+11-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::marker::PhantomData;
1+
use std::{marker::PhantomData, ops::*};
22

33
use serde::{Deserialize, Serialize};
44

@@ -28,14 +28,13 @@ where
2828
+ From<u64>
2929
+ PartialOrd
3030
+ num_traits::Zero
31-
+ std::ops::Add<Output = Coins>
32-
+ std::ops::Sub<Output = Coins>
33-
+ std::ops::Mul
34-
+ std::ops::Mul<Epoch, Output = Power>,
35-
Epoch: Copy + Default + num_traits::Saturating + std::ops::Sub<Output = Epoch>,
36-
Power: std::ops::Add<Output = Power>
37-
+ std::ops::Div<Output = Power>
38-
+ std::ops::Div<Coins, Output = Epoch>,
31+
+ Add<Output = Coins>
32+
+ Sub<Output = Coins>
33+
+ Mul
34+
+ Mul<Epoch, Output = Power>,
35+
Epoch: Copy + Default + num_traits::Saturating + Sub<Output = Epoch> + From<u32>,
36+
Power: Add<Output = Power> + Div<Output = Power>,
37+
u64: From<Coins> + From<Power>,
3938
{
4039
/// Increase the amount of coins staked by a certain staker.
4140
///
@@ -67,7 +66,9 @@ where
6766
let product_added = coins * epoch;
6867

6968
let coins_after = coins_before + coins;
70-
let epoch_after = (product_before + product_added) / coins_after;
69+
let epoch_after = Epoch::from(
70+
(u64::from(product_before + product_added) / u64::from(coins_after)) as u32,
71+
);
7172

7273
self.coins = coins_after;
7374
self.epochs.update_all(epoch_after);

data_structures/src/staking/stakes.rs

+67-13
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
use std::collections::{btree_map::Entry, BTreeMap};
1+
use std::{
2+
collections::{btree_map::Entry, BTreeMap},
3+
ops::{Add, Div, Mul, Sub},
4+
};
25

36
use itertools::Itertools;
47
use serde::{Deserialize, Serialize};
58

9+
use crate::{chain::PublicKeyHash, transaction::StakeTransaction, wit::Wit};
10+
611
use super::prelude::*;
712

813
/// The main data structure that provides the "stakes tracker" functionality.
@@ -34,20 +39,16 @@ where
3439
+ Default
3540
+ Ord
3641
+ From<u64>
42+
+ Into<u64>
3743
+ num_traits::Zero
38-
+ std::ops::Add<Output = Coins>
39-
+ std::ops::Sub<Output = Coins>
40-
+ std::ops::Mul
41-
+ std::ops::Mul<Epoch, Output = Power>,
44+
+ Add<Output = Coins>
45+
+ Sub<Output = Coins>
46+
+ Mul
47+
+ Mul<Epoch, Output = Power>,
4248
Address: Clone + Ord + 'static,
43-
Epoch: Copy + Default + num_traits::Saturating + std::ops::Sub<Output = Epoch>,
44-
Power: Copy
45-
+ Default
46-
+ Ord
47-
+ std::ops::Add<Output = Power>
48-
+ std::ops::Div<Output = Power>
49-
+ std::ops::Div<Coins, Output = Epoch>
50-
+ 'static,
49+
Epoch: Copy + Default + num_traits::Saturating + Sub<Output = Epoch> + From<u32>,
50+
Power: Copy + Default + Ord + Add<Output = Power> + Div<Output = Power>,
51+
u64: From<Coins> + From<Power>,
5152
{
5253
/// Register a certain amount of additional stake for a certain address and epoch.
5354
pub fn add_stake<ISK>(
@@ -224,6 +225,59 @@ where
224225
}
225226
}
226227

228+
/// Adds stake, based on the data from a stake transaction.
229+
///
230+
/// This function was made static instead of adding it to `impl Stakes` because it is not generic over `Address` and
231+
/// `Coins`.
232+
pub fn process_stake_transaction<Epoch, Power>(
233+
stakes: &mut Stakes<PublicKeyHash, Wit, Epoch, Power>,
234+
transaction: &StakeTransaction,
235+
epoch: Epoch,
236+
) -> StakingResult<(), PublicKeyHash, Wit, Epoch>
237+
where
238+
Epoch: Copy + Default + Sub<Output = Epoch> + num_traits::Saturating + From<u32>,
239+
Power:
240+
Add<Output = Power> + Copy + Default + Div<Output = Power> + Ord,
241+
Wit: Mul<Epoch, Output = Power>,
242+
u64: From<Wit> + From<Power>,
243+
{
244+
// This line would check that the authorization message is valid for the provided validator and withdrawer
245+
// address. But it is commented out here because stake transactions should be validated upfront (when
246+
// considering block candidates). The line is reproduced here for later reference when implementing those
247+
// validations. Once those are in place, we're ok to remove this comment.
248+
//transaction.body.authorization_is_valid().map_err(|_| StakesError::InvalidAuthentication)?;
249+
250+
let key = transaction.body.output.key.clone();
251+
let coins = Wit::from_nanowits(transaction.body.output.value);
252+
253+
stakes.add_stake(key, coins, epoch)?;
254+
255+
Ok(())
256+
}
257+
258+
/// Adds stakes, based on the data from multiple stake transactions.
259+
///
260+
/// This function was made static instead of adding it to `impl Stakes` because it is not generic over `Address` and
261+
/// `Coins`.
262+
pub fn process_stake_transactions<'a, Epoch, Power>(
263+
stakes: &mut Stakes<PublicKeyHash, Wit, Epoch, Power>,
264+
transactions: impl Iterator<Item = &'a StakeTransaction>,
265+
epoch: Epoch,
266+
) -> Result<(), StakesError<PublicKeyHash, Wit, Epoch>>
267+
where
268+
Epoch: Copy + Default + Sub<Output = Epoch> + num_traits::Saturating + From<u32>,
269+
Power:
270+
Add<Output = Power> + Copy + Default + Div<Output = Power> + Ord,
271+
Wit: Mul<Epoch, Output = Power>,
272+
u64: From<Wit> + From<Power>,
273+
{
274+
for transaction in transactions {
275+
process_stake_transaction(stakes, transaction, epoch)?;
276+
}
277+
278+
Ok(())
279+
}
280+
227281
#[cfg(test)]
228282
mod tests {
229283
use super::*;

data_structures/src/transaction.rs

+18-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ use std::sync::{Arc, RwLock};
33

44
use protobuf::Message;
55
use serde::{Deserialize, Serialize};
6-
use witnet_crypto::{hash::calculate_sha256, merkle::FullMerkleTree};
6+
7+
use witnet_crypto::{
8+
hash::calculate_sha256, merkle::FullMerkleTree, secp256k1::Message as Secp256k1Message,
9+
signature::PublicKey,
10+
};
711

812
use crate::{
913
chain::{
@@ -754,6 +758,16 @@ pub struct StakeTransactionBody {
754758
}
755759

756760
impl StakeTransactionBody {
761+
pub fn authorization_is_valid(&self) -> Result<(), failure::Error> {
762+
let msg = Secp256k1Message::from_digest(self.output.key.withdrawer.as_secp256k1_msg());
763+
let public_key = PublicKey::from_slice(&self.output.authorization.public_key.bytes)?;
764+
765+
self.output
766+
.authorization
767+
.signature
768+
.verify(&msg, &public_key)
769+
}
770+
757771
/// Construct a `StakeTransactionBody` from a list of inputs and one `StakeOutput`.
758772
pub fn new(
759773
inputs: Vec<Input>,
@@ -823,7 +837,7 @@ impl UnstakeTransaction {
823837
#[derive(Debug, Default, Eq, PartialEq, Clone, Serialize, Deserialize, ProtobufConvert, Hash)]
824838
#[protobuf_convert(pb = "witnet::UnstakeTransactionBody")]
825839
pub struct UnstakeTransactionBody {
826-
pub operator: PublicKeyHash,
840+
pub validator: PublicKeyHash,
827841
pub withdrawal: ValueTransferOutput,
828842

829843
#[protobuf_convert(skip)]
@@ -833,9 +847,9 @@ pub struct UnstakeTransactionBody {
833847

834848
impl UnstakeTransactionBody {
835849
/// Creates a new stake transaction body.
836-
pub fn new(operator: PublicKeyHash, withdrawal: ValueTransferOutput) -> Self {
850+
pub fn new(validator: PublicKeyHash, withdrawal: ValueTransferOutput) -> Self {
837851
UnstakeTransactionBody {
838-
operator,
852+
validator,
839853
withdrawal,
840854
..Default::default()
841855
}

0 commit comments

Comments
 (0)