Skip to content

Commit 8a3f46a

Browse files
authored
Fix equivocation verification in the runtime (#583)
* Fix equivocation verification in the runtime * Fix tests
1 parent 00670c9 commit 8a3f46a

File tree

4 files changed

+115
-8
lines changed

4 files changed

+115
-8
lines changed

crates/pallet-subspace/src/mock.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ use sp_consensus_slots::Slot;
2929
use sp_consensus_subspace::digests::{CompatibleDigestItem, PreDigest};
3030
use sp_consensus_subspace::{FarmerSignature, SignedVote, Vote};
3131
use sp_core::crypto::UncheckedFrom;
32-
use sp_core::sr25519::Pair;
33-
use sp_core::{Pair as PairTrait, H256};
32+
use sp_core::H256;
3433
use sp_runtime::testing::{Digest, DigestItem, Header, TestXt};
3534
use sp_runtime::traits::{Block as BlockT, Header as _, IdentityLookup};
3635
use sp_runtime::Perbill;
@@ -305,8 +304,15 @@ pub fn generate_equivocation_proof(
305304
// digest item
306305
let seal_header = |header: &mut Header| {
307306
let prehash = header.hash();
308-
let signature = Pair::from(keypair.secret.clone()).sign(prehash.as_ref());
309-
let seal = DigestItem::subspace_seal(signature.into());
307+
let signature = FarmerSignature::unchecked_from(
308+
keypair
309+
.sign(
310+
schnorrkel::context::signing_context(REWARD_SIGNING_CONTEXT)
311+
.bytes(prehash.as_bytes()),
312+
)
313+
.to_bytes(),
314+
);
315+
let seal = DigestItem::subspace_seal(signature);
310316
header.digest_mut().push(seal);
311317
};
312318

crates/sp-consensus-subspace/src/lib.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
pub mod digests;
2323
pub mod inherents;
2424
pub mod offence;
25+
#[cfg(test)]
26+
mod tests;
2527
pub mod verification;
2628

2729
use crate::digests::{
@@ -38,12 +40,12 @@ use sp_consensus_slots::Slot;
3840
use sp_core::crypto::KeyTypeId;
3941
use sp_core::H256;
4042
use sp_io::hashing;
41-
use sp_runtime::{ConsensusEngineId, RuntimeAppPublic};
43+
use sp_runtime::ConsensusEngineId;
4244
use sp_std::vec::Vec;
4345
use subspace_core_primitives::{
4446
Randomness, RootBlock, Salt, Sha256Hash, Solution, Tag, TagSignature,
4547
};
46-
use subspace_solving::create_tag_signature_transcript;
48+
use subspace_solving::{create_tag_signature_transcript, REWARD_SIGNING_CONTEXT};
4749

4850
/// Key type for Subspace pallet.
4951
const KEY_TYPE: KeyTypeId = KeyTypeId(*b"sub_");
@@ -162,7 +164,13 @@ where
162164
};
163165
let pre_hash = header.hash();
164166

165-
offender.verify(&pre_hash, &seal)
167+
verification::check_reward_signature(
168+
pre_hash.as_ref(),
169+
&seal,
170+
offender,
171+
&schnorrkel::signing_context(REWARD_SIGNING_CONTEXT),
172+
)
173+
.is_ok()
166174
}
167175

168176
/// Verifies the equivocation proof by making sure that: both headers have
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use crate::{
2+
is_equivocation_proof_valid, CompatibleDigestItem, EquivocationProof, FarmerPublicKey,
3+
FarmerSignature,
4+
};
5+
use schnorrkel::Keypair;
6+
use sp_consensus_slots::Slot;
7+
use sp_core::crypto::UncheckedFrom;
8+
use sp_runtime::traits::BlakeTwo256;
9+
use sp_runtime::{Digest, DigestItem};
10+
use subspace_core_primitives::{LocalChallenge, Solution, TagSignature};
11+
use subspace_solving::REWARD_SIGNING_CONTEXT;
12+
13+
type Header = sp_runtime::generic::Header<u32, BlakeTwo256>;
14+
type PreDigest = crate::PreDigest<FarmerPublicKey, ()>;
15+
16+
#[test]
17+
fn test_is_equivocation_proof_valid() {
18+
let keypair = Keypair::generate();
19+
let offender = FarmerPublicKey::unchecked_from(keypair.public.to_bytes());
20+
let slot = Slot::from(1);
21+
let solution = Solution {
22+
public_key: offender.clone(),
23+
reward_address: (),
24+
piece_index: 0,
25+
encoding: Default::default(),
26+
tag_signature: TagSignature {
27+
output: Default::default(),
28+
proof: [0u8; 64],
29+
},
30+
local_challenge: LocalChallenge {
31+
output: Default::default(),
32+
proof: [0u8; 64],
33+
},
34+
tag: [0u8; 8],
35+
};
36+
37+
let mut first_header = Header {
38+
parent_hash: [0u8; 32].into(),
39+
number: 1,
40+
state_root: Default::default(),
41+
extrinsics_root: Default::default(),
42+
digest: Digest {
43+
logs: vec![DigestItem::subspace_pre_digest(&PreDigest {
44+
slot,
45+
solution: solution.clone(),
46+
})],
47+
},
48+
};
49+
first_header
50+
.digest
51+
.logs
52+
.push(DigestItem::subspace_seal(FarmerSignature::unchecked_from(
53+
keypair
54+
.sign(
55+
schnorrkel::context::signing_context(REWARD_SIGNING_CONTEXT)
56+
.bytes(first_header.hash().as_bytes()),
57+
)
58+
.to_bytes(),
59+
)));
60+
61+
let mut second_header = Header {
62+
parent_hash: [1u8; 32].into(),
63+
number: 1,
64+
state_root: Default::default(),
65+
extrinsics_root: Default::default(),
66+
digest: Digest {
67+
logs: vec![DigestItem::subspace_pre_digest(&PreDigest {
68+
slot,
69+
solution,
70+
})],
71+
},
72+
};
73+
second_header
74+
.digest
75+
.logs
76+
.push(DigestItem::subspace_seal(FarmerSignature::unchecked_from(
77+
keypair
78+
.sign(
79+
schnorrkel::context::signing_context(REWARD_SIGNING_CONTEXT)
80+
.bytes(second_header.hash().as_bytes()),
81+
)
82+
.to_bytes(),
83+
)));
84+
85+
let equivocation_proof = EquivocationProof {
86+
offender,
87+
slot,
88+
first_header,
89+
second_header,
90+
};
91+
92+
assert!(is_equivocation_proof_valid::<_, ()>(equivocation_proof));
93+
}

crates/subspace-runtime/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
8484
spec_name: create_runtime_str!("subspace"),
8585
impl_name: create_runtime_str!("subspace"),
8686
authoring_version: 0,
87-
spec_version: 1,
87+
spec_version: 2,
8888
impl_version: 0,
8989
apis: RUNTIME_API_VERSIONS,
9090
transaction_version: 0,

0 commit comments

Comments
 (0)