Skip to content

Commit cdbd32e

Browse files
committed
fix(validations): fix signing and validation of VTTs
1 parent 5719ce5 commit cdbd32e

File tree

7 files changed

+276
-202
lines changed

7 files changed

+276
-202
lines changed

data_structures/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,8 @@ mod tests {
200200
// If this default changes before the transition to V2 is complete, almost everything will
201201
// break because data structures change schema and, serialization changes and hash
202202
// derivation breaks too
203-
let version = get_protocol_version(None);
204-
assert_eq!(version, ProtocolVersion::V1_7);
203+
let protocol_version = get_protocol_version(None);
204+
assert_eq!(protocol_version, ProtocolVersion::V1_7);
205205

206206
// Register the different protocol versions
207207
register_protocol_version(ProtocolVersion::V1_7, 100, 45);

data_structures/src/proto/versioning.rs

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@ use protobuf::Message as _;
88
use serde::{Deserialize, Serialize};
99
use strum_macros::{Display, EnumString};
1010

11-
use crate::chain::Epoch;
12-
use crate::proto::schema::witnet::SuperBlock;
1311
use crate::{
14-
chain::Hash,
12+
chain::{Epoch, Hash},
1513
get_protocol_version,
1614
proto::{
1715
schema::witnet::{
@@ -23,7 +21,6 @@ use crate::{
2321
},
2422
ProtobufConvert,
2523
},
26-
transaction::MemoizedHashable,
2724
types::Message,
2825
};
2926

@@ -230,14 +227,7 @@ impl Versioned for crate::chain::BlockHeader {
230227
}
231228

232229
impl Versioned for crate::chain::SuperBlock {
233-
type LegacyType = SuperBlock;
234-
235-
fn to_versioned_pb_bytes(&self, _version: ProtocolVersion) -> Result<Vec<u8>, Error>
236-
where
237-
<Self as ProtobufConvert>::ProtoStruct: protobuf::Message,
238-
{
239-
Ok(self.hashable_bytes())
240-
}
230+
type LegacyType = <Self as ProtobufConvert>::ProtoStruct;
241231
}
242232

243233
impl Versioned for crate::chain::Block {
@@ -274,10 +264,38 @@ impl Versioned for Message {
274264
}
275265
}
276266

267+
impl Versioned for crate::transaction::Transaction {
268+
type LegacyType = <Self as ProtobufConvert>::ProtoStruct;
269+
}
270+
271+
impl Versioned for crate::transaction::VTTransaction {
272+
type LegacyType = <Self as ProtobufConvert>::ProtoStruct;
273+
}
274+
impl Versioned for crate::transaction::VTTransactionBody {
275+
type LegacyType = <Self as ProtobufConvert>::ProtoStruct;
276+
}
277+
impl Versioned for crate::transaction::DRTransactionBody {
278+
type LegacyType = <Self as ProtobufConvert>::ProtoStruct;
279+
}
280+
impl Versioned for crate::transaction::CommitTransactionBody {
281+
type LegacyType = <Self as ProtobufConvert>::ProtoStruct;
282+
}
283+
impl Versioned for crate::transaction::RevealTransaction {
284+
type LegacyType = <Self as ProtobufConvert>::ProtoStruct;
285+
}
286+
impl Versioned for crate::transaction::RevealTransactionBody {
287+
type LegacyType = <Self as ProtobufConvert>::ProtoStruct;
288+
}
289+
277290
pub trait AutoVersioned: ProtobufConvert {}
278291

279292
impl AutoVersioned for crate::chain::BlockHeader {}
280293
impl AutoVersioned for crate::chain::SuperBlock {}
294+
impl AutoVersioned for crate::transaction::VTTransactionBody {}
295+
impl AutoVersioned for crate::transaction::DRTransactionBody {}
296+
impl AutoVersioned for crate::transaction::CommitTransactionBody {}
297+
impl AutoVersioned for crate::transaction::RevealTransaction {}
298+
impl AutoVersioned for crate::transaction::RevealTransactionBody {}
281299

282300
pub trait VersionedHashable {
283301
fn versioned_hash(&self, version: ProtocolVersion) -> Hash;
@@ -288,14 +306,34 @@ where
288306
T: AutoVersioned + Versioned,
289307
<Self as ProtobufConvert>::ProtoStruct: protobuf::Message,
290308
{
309+
#[inline]
291310
fn versioned_hash(&self, version: ProtocolVersion) -> Hash {
292311
// This unwrap is kept in here just because we want `VersionedHashable` to have the same interface as
293312
// `Hashable`.
294313
witnet_crypto::hash::calculate_sha256(&self.to_versioned_pb_bytes(version).unwrap()).into()
295314
}
296315
}
297316

317+
impl VersionedHashable for crate::transaction::Transaction {
318+
fn versioned_hash(&self, version: ProtocolVersion) -> Hash {
319+
match self {
320+
crate::transaction::Transaction::ValueTransfer(tx) => tx.versioned_hash(version),
321+
_ => {
322+
todo!();
323+
}
324+
}
325+
}
326+
}
327+
328+
impl VersionedHashable for crate::transaction::VTTransaction {
329+
#[inline]
330+
fn versioned_hash(&self, version: ProtocolVersion) -> Hash {
331+
self.body.versioned_hash(version)
332+
}
333+
}
334+
298335
impl VersionedHashable for crate::chain::Block {
336+
#[inline]
299337
fn versioned_hash(&self, version: ProtocolVersion) -> Hash {
300338
self.block_header.versioned_hash(version)
301339
}

data_structures/src/staking/stakes.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,7 @@ mod tests {
900900
);
901901
assert_eq!(stakes.query_power(bob, Capability::Mining, 101), Ok(1_620));
902902
assert_eq!(stakes.query_power(charlie, Capability::Mining, 101), Ok(0));
903+
assert_eq!(stakes.query_power(charlie, Capability::Mining, 102), Ok(0));
903904
assert_eq!(
904905
stakes.query_power(alice, Capability::Witnessing, 101),
905906
Ok(1_010)

node/src/actors/chain_manager/mining.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,13 @@ impl ChainManager {
114114

115115
let mut beacon = chain_info.highest_block_checkpoint;
116116
let mut vrf_input = chain_info.highest_vrf_output;
117+
let protocol_version = get_protocol_version(self.current_epoch);
117118

118119
// The highest checkpoint beacon should contain the current epoch
119120
beacon.checkpoint = current_epoch;
120121
vrf_input.checkpoint = current_epoch;
121122

122-
let target_hash = if get_protocol_version(self.current_epoch) == V2_0 {
123+
let target_hash = if protocol_version == V2_0 {
123124
let eligibility = self
124125
.chain_state
125126
.stakes
@@ -235,8 +236,7 @@ impl ChainManager {
235236
);
236237

237238
// Sign the block hash
238-
let protocol = get_protocol_version(Some(block_header.beacon.checkpoint));
239-
let block_header_data = block_header.versioned_hash(protocol).data();
239+
let block_header_data = block_header.versioned_hash(protocol_version).data();
240240

241241
signature_mngr::sign_data(block_header_data)
242242
.map(|res| {
@@ -253,11 +253,11 @@ impl ChainManager {
253253
beacon,
254254
epoch_constants,
255255
)
256-
.map_ok(|_diff, act, _ctx| {
256+
.map_ok(move |_diff, act, _ctx| {
257257
// Send AddCandidates message to self
258258
// This will run all the validations again
259259

260-
let block_hash = block.hash();
260+
let block_hash = block.versioned_hash(protocol_version);
261261
// FIXME(#1773): Currently last_block_proposed is not used, but removing it is a breaking change
262262
act.chain_state.node_stats.last_block_proposed = block_hash;
263263
act.chain_state.node_stats.block_proposed_count += 1;

node/src/actors/chain_manager/mod.rs

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ use witnet_data_structures::{
8282
},
8383
utxo_pool::{Diff, OwnUnspentOutputsPool, UnspentOutputsPool, UtxoDiff, UtxoWriteBatch},
8484
vrf::VrfCtx,
85-
wit::{Wit, WIT_DECIMAL_PLACES},
85+
wit::Wit,
8686
};
8787
use witnet_rad::{error::RadError::TooManyWitnesses, types::RadonTypes};
8888
use witnet_util::timestamp::seconds_to_human_string;
@@ -717,8 +717,8 @@ impl ChainManager {
717717
resynchronizing,
718718
&active_wips,
719719
Some(&mut transaction_visitor),
720-
protocol_version,
721720
&self.chain_state.stakes,
721+
protocol_version,
722722
)?;
723723

724724
// Extract the collected priorities from the internal state of the visitor
@@ -884,8 +884,8 @@ impl ChainManager {
884884
false,
885885
&active_wips,
886886
Some(&mut transaction_visitor),
887-
protocol_version,
888887
&self.chain_state.stakes,
888+
protocol_version,
889889
) {
890890
Ok(utxo_diff) => {
891891
let priorities = transaction_visitor.take_state();
@@ -1552,6 +1552,7 @@ impl ChainManager {
15521552
// than or equal to the current epoch
15531553
block_epoch: current_epoch,
15541554
};
1555+
let protocol_version = get_protocol_version(Some(current_epoch));
15551556
let collateral_age = if active_wips.wip0027() {
15561557
PSEUDO_CONSENSUS_CONSTANTS_WIP0027_COLLATERAL_AGE
15571558
} else {
@@ -1580,6 +1581,7 @@ impl ChainManager {
15801581
&active_wips,
15811582
chain_info.consensus_constants.superblock_period,
15821583
&self.chain_state.stakes,
1584+
protocol_version,
15831585
))
15841586
.into_actor(self)
15851587
.and_then(|fee, act, _ctx| {
@@ -2105,8 +2107,8 @@ impl ChainManager {
21052107
self.chain_state.reputation_engine.as_ref().unwrap(),
21062108
&consensus_constants,
21072109
&active_wips,
2108-
protocol_version,
21092110
&self.chain_state.stakes,
2111+
protocol_version,
21102112
);
21112113

21122114
let fut = async {
@@ -2131,6 +2133,7 @@ impl ChainManager {
21312133
&active_wips,
21322134
None,
21332135
&act.chain_state.stakes,
2136+
protocol_version,
21342137
);
21352138
async {
21362139
// Short-circuit if validation failed
@@ -2906,8 +2909,8 @@ pub fn process_validations(
29062909
resynchronizing: bool,
29072910
active_wips: &ActiveWips,
29082911
transaction_visitor: Option<&mut dyn Visitor<Visitable = (Transaction, u64, u32)>>,
2909-
protocol_version: ProtocolVersion,
29102912
stakes: &StakesTracker,
2913+
protocol_version: ProtocolVersion,
29112914
) -> Result<Diff, failure::Error> {
29122915
if !resynchronizing {
29132916
let mut signatures_to_verify = vec![];
@@ -2920,8 +2923,8 @@ pub fn process_validations(
29202923
rep_eng,
29212924
consensus_constants,
29222925
active_wips,
2923-
protocol_version,
29242926
stakes,
2927+
protocol_version,
29252928
)?;
29262929
log::debug!("Verifying {} block signatures", signatures_to_verify.len());
29272930
verify_signatures(signatures_to_verify, vrf_ctx)?;
@@ -2941,6 +2944,7 @@ pub fn process_validations(
29412944
active_wips,
29422945
transaction_visitor,
29432946
stakes,
2947+
protocol_version,
29442948
)?;
29452949

29462950
if !resynchronizing {
@@ -3545,6 +3549,7 @@ mod tests {
35453549
OutputPointer, PartialConsensusConstants, PublicKey, SecretKey, Signature,
35463550
ValueTransferOutput,
35473551
},
3552+
proto::versioning::VersionedHashable,
35483553
transaction::{
35493554
CommitTransaction, DRTransaction, MintTransaction, RevealTransaction, VTTransaction,
35503555
VTTransactionBody,
@@ -3983,8 +3988,12 @@ mod tests {
39833988
static PRIV_KEY_1: [u8; 32] = [0xcd; 32];
39843989
static PRIV_KEY_2: [u8; 32] = [0x43; 32];
39853990

3986-
fn sign_tx<H: Hashable>(mk: [u8; 32], tx: &H) -> KeyedSignature {
3987-
let Hash::SHA256(data) = tx.hash();
3991+
fn sign_tx<H: VersionedHashable>(
3992+
mk: [u8; 32],
3993+
tx: &H,
3994+
protocol_version: ProtocolVersion,
3995+
) -> KeyedSignature {
3996+
let Hash::SHA256(data) = tx.versioned_hash(protocol_version);
39883997

39893998
let secret_key =
39903999
Secp256k1_SecretKey::from_slice(&mk).expect("32 bytes, within curve order");
@@ -4011,6 +4020,7 @@ mod tests {
40114020
fn create_valid_block(chain_manager: &mut ChainManager, priv_key: &[u8; 32]) -> Block {
40124021
let vrf = &mut VrfCtx::secp256k1().unwrap();
40134022
let current_epoch = chain_manager.current_epoch.unwrap();
4023+
let protocol_version = get_protocol_version(Some(current_epoch));
40144024

40154025
let consensus_constants = chain_manager.consensus_constants();
40164026
let secret_key = SecretKey {
@@ -4064,7 +4074,7 @@ mod tests {
40644074
proof: BlockEligibilityClaim::create(vrf, &secret_key, vrf_input).unwrap(),
40654075
..Default::default()
40664076
};
4067-
let block_sig = sign_tx(*priv_key, &block_header);
4077+
let block_sig = sign_tx(*priv_key, &block_header, protocol_version);
40684078

40694079
Block::new(block_header, block_sig, txns)
40704080
}
@@ -4178,6 +4188,7 @@ mod tests {
41784188
fn create_valid_transaction(
41794189
_chain_manager: &mut ChainManager,
41804190
priv_key: &[u8; 32],
4191+
protocol_version: ProtocolVersion,
41814192
) -> Transaction {
41824193
let my_pkh = pkh(priv_key);
41834194

@@ -4197,7 +4208,7 @@ mod tests {
41974208
let outputs = vec![vto];
41984209

41994210
let vt_body = VTTransactionBody::new(inputs, outputs);
4200-
let signatures = vec![sign_tx(*priv_key, &vt_body)];
4211+
let signatures = vec![sign_tx(*priv_key, &vt_body, protocol_version)];
42014212
let vtt = VTTransaction::new(vt_body, signatures);
42024213

42034214
Transaction::ValueTransfer(vtt)
@@ -4211,14 +4222,16 @@ mod tests {
42114222
let mut config = Config::default();
42124223
config.storage.backend = StorageBackend::HashMap;
42134224
let config = Arc::new(config);
4225+
let epoch = Some(2000000);
4226+
let protocol_version = get_protocol_version(epoch);
42144227
// Start relevant actors
42154228
config_mngr::start(config);
42164229
storage_mngr::start();
42174230

42184231
let mut ctx = Context::new();
42194232
let mut chain_manager = ChainManager::default();
42204233

4221-
chain_manager.current_epoch = Some(2000000);
4234+
chain_manager.current_epoch = epoch;
42224235
// 1 epoch = 1000 seconds, for easy testing
42234236
chain_manager.epoch_constants = Some(EpochConstants {
42244237
checkpoint_zero_timestamp: 0,
@@ -4259,7 +4272,7 @@ mod tests {
42594272
chain_manager.vrf_ctx = Some(VrfCtx::secp256k1().unwrap());
42604273
chain_manager.sm_state = StateMachine::Synced;
42614274

4262-
let t1 = create_valid_transaction(&mut chain_manager, &PRIV_KEY_1);
4275+
let t1 = create_valid_transaction(&mut chain_manager, &PRIV_KEY_1, protocol_version);
42634276
let mut t1_mal = t1.clone();
42644277
// Malleability!
42654278
match &mut t1_mal {
@@ -4280,7 +4293,10 @@ mod tests {
42804293
}
42814294

42824295
// Changing signatures field does not change transaction hash
4283-
assert_eq!(t1.hash(), t1_mal.hash());
4296+
assert_eq!(
4297+
t1.versioned_hash(protocol_version),
4298+
t1_mal.versioned_hash(protocol_version)
4299+
);
42844300
// But the transactions are different
42854301
assert_ne!(t1, t1_mal);
42864302

0 commit comments

Comments
 (0)