1
// Copyright (C) Moondance Labs Ltd.
2
// This file is part of Tanssi.
3

            
4
// Tanssi is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Tanssi is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
16

            
17
#![cfg(test)]
18

            
19
use {
20
    crate::{tests::common::*, Beefy, Historical},
21
    beefy_primitives::{
22
        check_double_voting_proof,
23
        ecdsa_crypto::{
24
            AuthorityId as BeefyId, Public as BeefyPublic, Signature as BeefySignature,
25
        },
26
        known_payloads::MMR_ROOT_ID,
27
        test_utils::{generate_double_voting_proof, BeefySignerAuthority, Keyring as BeefyKeyring},
28
        BeefySignatureHasher, Commitment, ConsensusLog, FutureBlockVotingProof, Payload,
29
        ValidatorSet, ValidatorSetId as ValidatorSetIdType, VoteMessage, BEEFY_ENGINE_ID,
30
        KEY_TYPE as BEEFY_KEY_TYPE,
31
    },
32
    frame_support::{assert_err, assert_ok, traits::KeyOwnerProofSystem},
33
    pallet_beefy::{Error as BeefyError, GenesisBlock, ValidatorSetId},
34
    parity_scale_codec::{Decode, Encode},
35
    sp_application_crypto::{AppCrypto, Pair, RuntimeAppPublic},
36
    sp_runtime::{traits::Keccak256, DigestItem},
37
    sp_std::vec,
38
};
39

            
40
/// Create a new `VoteMessage` from commitment primitives and key pair.
41
3
pub fn signed_vote<TPublic: AppCrypto + RuntimeAppPublic<Signature = BeefySignature>>(
42
3
    block_number: u32,
43
3
    payload: Payload,
44
3
    validator_set_id: ValidatorSetIdType,
45
3
    key_pair: <TPublic as AppCrypto>::Pair,
46
3
) -> VoteMessage<u32, <<TPublic as AppCrypto>::Pair as AppCrypto>::Public, BeefySignature>
47
3
where
48
3
    <TPublic as AppCrypto>::Pair: BeefySignerAuthority<BeefySignatureHasher>,
49
3
    <TPublic as RuntimeAppPublic>::Signature:
50
3
        Send + Sync + From<<<TPublic as AppCrypto>::Pair as AppCrypto>::Signature>,
51
3
{
52
3
    let commitment = Commitment {
53
3
        validator_set_id,
54
3
        block_number,
55
3
        payload,
56
3
    };
57
3
    let signature: <TPublic as RuntimeAppPublic>::Signature =
58
3
        key_pair.sign_with_hasher(&commitment.encode()).into();
59
3
    VoteMessage {
60
3
        commitment,
61
3
        id: key_pair.public(),
62
3
        signature,
63
3
    }
64
3
}
65

            
66
/// Create a new `FutureBlockVotingProof` based on vote.
67
3
pub fn generate_future_block_voting_proof(
68
3
    vote: (u32, Payload, ValidatorSetIdType),
69
3
    key_pair: <BeefyId as AppCrypto>::Pair,
70
3
) -> FutureBlockVotingProof<u32, BeefyPublic> {
71
3
    let signed_vote = signed_vote::<BeefyId>(vote.0, vote.1, vote.2, key_pair);
72
3
    FutureBlockVotingProof { vote: signed_vote }
73
3
}
74

            
75
#[test]
76
1
fn test_session_change_updates_beefy_authorities_digest() {
77
1
    ExtBuilder::default()
78
1
        .with_balances(vec![
79
1
            // Alice gets 10k extra tokens for her mapping deposit
80
1
            (AccountId::from(ALICE), 210_000 * UNIT),
81
1
            (AccountId::from(BOB), 100_000 * UNIT),
82
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
83
1
            (AccountId::from(DAVE), 100_000 * UNIT),
84
1
        ])
85
1
        .with_validators(vec![
86
1
            (AccountId::from(ALICE), 210 * UNIT),
87
1
            (AccountId::from(BOB), 100 * UNIT),
88
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
89
1
            (AccountId::from(DAVE), 100_000 * UNIT),
90
1
        ])
91
1
        .build()
92
1
        .execute_with(|| {
93
1
            assert_eq!(ValidatorSetId::<Runtime>::get(), 0);
94

            
95
1
            run_to_session(1);
96
1
            assert_eq!(ValidatorSetId::<Runtime>::get(), 1);
97

            
98
            // Get beefy keys for our validators
99
1
            let alice_keys =
100
1
                get_authority_keys_from_seed(&AccountId::from(ALICE).to_string(), None);
101
1
            let bob_keys = get_authority_keys_from_seed(&AccountId::from(BOB).to_string(), None);
102
1
            let charlie_keys =
103
1
                get_authority_keys_from_seed(&AccountId::from(CHARLIE).to_string(), None);
104
1
            let dave_keys = get_authority_keys_from_seed(&AccountId::from(DAVE).to_string(), None);
105
1

            
106
1
            let expected_digest = get_beefy_digest(ConsensusLog::AuthoritiesChange(
107
1
                ValidatorSet::new(
108
1
                    vec![
109
1
                        alice_keys.beefy.clone(),
110
1
                        bob_keys.beefy.clone(),
111
1
                        charlie_keys.beefy.clone(),
112
1
                        dave_keys.beefy.clone(),
113
1
                    ],
114
1
                    1,
115
1
                )
116
1
                .unwrap(),
117
1
            ));
118
1

            
119
1
            // Check the expected digest item was placed correctly
120
1
            let actual_digest = System::digest().logs[2].clone();
121
1
            assert_eq!(expected_digest, actual_digest);
122

            
123
            // Check one more session
124
1
            run_to_session(2);
125
1
            assert_eq!(ValidatorSetId::<Runtime>::get(), 2);
126

            
127
1
            let expected_digest = get_beefy_digest(ConsensusLog::AuthoritiesChange(
128
1
                ValidatorSet::new(
129
1
                    vec![
130
1
                        alice_keys.beefy,
131
1
                        bob_keys.beefy,
132
1
                        charlie_keys.beefy,
133
1
                        dave_keys.beefy,
134
1
                    ],
135
1
                    2,
136
1
                )
137
1
                .unwrap(),
138
1
            ));
139
1

            
140
1
            // Check the expected digest item was placed correctly.
141
1
            // We should have the new "expected_digest" with the same validators as the
142
1
            // previous session, given that we don't have staking yet,
143
1
            // thus validator set doesn't change.
144
1
            let actual_digest = System::digest().logs[2].clone();
145
1
            assert_eq!(expected_digest, actual_digest);
146
1
        });
147
1
}
148

            
149
#[test]
150
1
fn test_valid_and_invalid_double_voting_proofs() {
151
1
    ExtBuilder::default()
152
1
        .with_balances(vec![
153
1
            // Alice gets 10k extra tokens for her mapping deposit
154
1
            (AccountId::from(ALICE), 210_000 * UNIT),
155
1
            (AccountId::from(BOB), 100_000 * UNIT),
156
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
157
1
            (AccountId::from(DAVE), 100_000 * UNIT),
158
1
        ])
159
1
        .with_validators(vec![
160
1
            (AccountId::from(ALICE), 210 * UNIT),
161
1
            (AccountId::from(BOB), 100 * UNIT),
162
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
163
1
            (AccountId::from(DAVE), 100_000 * UNIT),
164
1
        ])
165
1
        .build()
166
1
        .execute_with(|| {
167
1
            let set_id = 3;
168
1
            let payload1 = Payload::from_single_entry(MMR_ROOT_ID, vec![42]);
169
1
            let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![128]);
170
1

            
171
1
            // 1st case (invalid proof):
172
1
            // Equivocation proof with two votes in the same round for
173
1
            // same payload signed by the same key.
174
1
            let equivocation_proof = generate_double_voting_proof(
175
1
                (1, payload1.clone(), set_id, &BeefyKeyring::Bob),
176
1
                (1, payload1.clone(), set_id, &BeefyKeyring::Bob),
177
1
            );
178
1

            
179
1
            // Previous equivocation proof should be invalid.
180
1
            assert!(!check_double_voting_proof::<_, _, Keccak256>(
181
1
                &equivocation_proof
182
1
            ));
183

            
184
            // 2nd case (invalid proof):
185
            // Equivocation proof with two votes in different rounds for
186
            // different payloads signed by the same key.
187
1
            let equivocation_proof = generate_double_voting_proof(
188
1
                (1, payload1.clone(), set_id, &BeefyKeyring::Bob),
189
1
                (2, payload2.clone(), set_id, &BeefyKeyring::Bob),
190
1
            );
191
1

            
192
1
            // Previous equivocation proof should be invalid.
193
1
            assert!(!check_double_voting_proof::<_, _, Keccak256>(
194
1
                &equivocation_proof
195
1
            ));
196

            
197
            // 3rd case (invalid proof):
198
            // Equivocation proof with two votes by different authorities.
199
1
            let equivocation_proof = generate_double_voting_proof(
200
1
                (1, payload1.clone(), set_id, &BeefyKeyring::Alice),
201
1
                (1, payload2.clone(), set_id, &BeefyKeyring::Bob),
202
1
            );
203
1

            
204
1
            // Previous equivocation proof should be invalid.
205
1
            assert!(!check_double_voting_proof::<_, _, Keccak256>(
206
1
                &equivocation_proof
207
1
            ));
208

            
209
            // 4th case (invalid proof):
210
            // Equivocation proof with two votes in different set ids.
211
1
            let equivocation_proof = generate_double_voting_proof(
212
1
                (1, payload1.clone(), set_id, &BeefyKeyring::Bob),
213
1
                (1, payload2.clone(), set_id + 1, &BeefyKeyring::Bob),
214
1
            );
215
1

            
216
1
            // Previous equivocation proof should be invalid.
217
1
            assert!(!check_double_voting_proof::<_, _, Keccak256>(
218
1
                &equivocation_proof
219
1
            ));
220

            
221
            // Last case (valid proof):
222
            // Equivocation proof with two votes in the same round for
223
            // different payloads signed by the same key.
224
1
            let payload2 = Payload::from_single_entry(MMR_ROOT_ID, vec![128]);
225
1
            let equivocation_proof = generate_double_voting_proof(
226
1
                (1, payload1, set_id, &BeefyKeyring::Bob),
227
1
                (1, payload2, set_id, &BeefyKeyring::Bob),
228
1
            );
229
1

            
230
1
            // Previous equivocation proof should be valid.
231
1
            assert!(check_double_voting_proof::<_, _, Keccak256>(
232
1
                &equivocation_proof
233
1
            ))
234
1
        });
235
1
}
236

            
237
#[test]
238
1
fn test_set_new_genesis() {
239
1
    ExtBuilder::default()
240
1
        .with_balances(vec![
241
1
            // Alice gets 10k extra tokens for her mapping deposit
242
1
            (AccountId::from(ALICE), 210_000 * UNIT),
243
1
            (AccountId::from(BOB), 100_000 * UNIT),
244
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
245
1
            (AccountId::from(DAVE), 100_000 * UNIT),
246
1
        ])
247
1
        .with_validators(vec![
248
1
            (AccountId::from(ALICE), 210 * UNIT),
249
1
            (AccountId::from(BOB), 100 * UNIT),
250
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
251
1
            (AccountId::from(DAVE), 100_000 * UNIT),
252
1
        ])
253
1
        .build()
254
1
        .execute_with(|| {
255
1
            run_to_session(1);
256
1

            
257
1
            let new_beefy_genesis_delay = 5u32;
258
1
            assert_ok!(Beefy::set_new_genesis(
259
1
                root_origin(),
260
1
                new_beefy_genesis_delay,
261
1
            ));
262

            
263
1
            let expected_new_genesis = System::block_number() + new_beefy_genesis_delay;
264
1

            
265
1
            // Check the new genesis was placed correctly.
266
1
            assert_eq!(GenesisBlock::<Runtime>::get(), Some(expected_new_genesis));
267

            
268
            // We should not be able to set a genesis < 1
269
1
            assert_err!(
270
1
                Beefy::set_new_genesis(root_origin(), 0u32,),
271
1
                BeefyError::<Runtime>::InvalidConfiguration,
272
1
            );
273
1
        });
274
1
}
275

            
276
#[test]
277
1
fn test_report_future_voting_valid_and_invalid_proofs() {
278
1
    ExtBuilder::default()
279
1
        .with_balances(vec![
280
1
            // Alice gets 10k extra tokens for her mapping deposit
281
1
            (AccountId::from(ALICE), 210_000 * UNIT),
282
1
            (AccountId::from(BOB), 100_000 * UNIT),
283
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
284
1
            (AccountId::from(DAVE), 100_000 * UNIT),
285
1
        ])
286
1
        .with_validators(vec![
287
1
            (AccountId::from(ALICE), 210 * UNIT),
288
1
            (AccountId::from(BOB), 100 * UNIT),
289
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
290
1
            (AccountId::from(DAVE), 100_000 * UNIT),
291
1
        ])
292
1
        .build()
293
1
        .execute_with(|| {
294
1
            run_to_session(1);
295
1

            
296
1
            let block_num = System::block_number();
297
1
            let validator_set = Beefy::validator_set().unwrap();
298
1
            let authorities = validator_set.validators();
299
1
            let set_id = validator_set.id();
300
1

            
301
1
            assert_eq!(authorities.len(), 4);
302
1
            let equivocation_authority_index = 1;
303
1
            let equivocation_key = &authorities[equivocation_authority_index];
304
1

            
305
1
            // Create key ownership proof
306
1
            let key_owner_proof = Historical::prove((BEEFY_KEY_TYPE, &equivocation_key)).unwrap();
307
1

            
308
1
            // Let's generate a key_pair to proof that BOB corresponds to index 1.
309
1
            let secret_uri = format!("//{}", &AccountId::from(BOB));
310
1
            let key_pair_bob = <BeefyId as AppCrypto>::Pair::from_string(&secret_uri, None)
311
1
                .expect("should succeed generating key_pair");
312
1

            
313
1
            let payload = Payload::from_single_entry(MMR_ROOT_ID, vec![42]);
314
1

            
315
1
            // Build the future block equivocation proof using the generated key_pair.
316
1
            let valid_equivocation_proof = generate_future_block_voting_proof(
317
1
                (block_num + 100, payload.clone(), set_id),
318
1
                key_pair_bob.clone(),
319
1
            );
320
1

            
321
1
            // Should succeed as BOB is present in validator set.
322
1
            assert_ok!(Beefy::report_future_block_voting_unsigned(
323
1
                RuntimeOrigin::none(),
324
1
                Box::new(valid_equivocation_proof),
325
1
                key_owner_proof.clone(),
326
1
            ));
327

            
328
            // Let's generate a key_pair of an account that is not present in validator set.
329
1
            let secret_uri = format!("//{}", &AccountId::from(FERDIE));
330
1
            let key_pair_ferdie = <BeefyId as AppCrypto>::Pair::from_string(&secret_uri, None)
331
1
                .expect("should succeed generating invalid key_pair");
332
1

            
333
1
            // Build the invalid equivocation proof.
334
1
            let invalid_equivocation_proof = generate_future_block_voting_proof(
335
1
                (block_num + 100, payload.clone(), set_id),
336
1
                key_pair_ferdie,
337
1
            );
338
1

            
339
1
            // Should fail as FERDIE is not part of the validator set.
340
1
            assert_err!(
341
1
                Beefy::report_future_block_voting_unsigned(
342
1
                    RuntimeOrigin::none(),
343
1
                    Box::new(invalid_equivocation_proof),
344
1
                    key_owner_proof.clone(),
345
1
                ),
346
1
                BeefyError::<Runtime>::InvalidKeyOwnershipProof
347
1
            );
348

            
349
1
            let invalid_equivocation_proof =
350
1
                generate_future_block_voting_proof((1, payload.clone(), set_id), key_pair_bob);
351
1

            
352
1
            // Should fail if the proof targets an old block.
353
1
            assert_err!(
354
1
                Beefy::report_future_block_voting_unsigned(
355
1
                    RuntimeOrigin::none(),
356
1
                    Box::new(invalid_equivocation_proof),
357
1
                    key_owner_proof,
358
1
                ),
359
1
                BeefyError::<Runtime>::InvalidFutureBlockVotingProof
360
1
            );
361
1
        });
362
1
}
363

            
364
#[test]
365
1
fn test_mmr_digest_updates_after_session_and_single_block() {
366
1
    ExtBuilder::default()
367
1
        .with_balances(vec![
368
1
            // Alice gets 10k extra tokens for her mapping deposit
369
1
            (AccountId::from(ALICE), 210_000 * UNIT),
370
1
            (AccountId::from(BOB), 100_000 * UNIT),
371
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
372
1
            (AccountId::from(DAVE), 100_000 * UNIT),
373
1
        ])
374
1
        .with_validators(vec![
375
1
            (AccountId::from(ALICE), 210 * UNIT),
376
1
            (AccountId::from(BOB), 100 * UNIT),
377
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
378
1
            (AccountId::from(DAVE), 100_000 * UNIT),
379
1
        ])
380
1
        .build()
381
1
        .execute_with(|| {
382
1
            assert_eq!(ValidatorSetId::<Runtime>::get(), 0);
383

            
384
1
            run_to_session(1);
385
1
            assert_eq!(ValidatorSetId::<Runtime>::get(), 1);
386

            
387
            // Get beefy keys for our validators
388
1
            let alice_keys =
389
1
                get_authority_keys_from_seed(&AccountId::from(ALICE).to_string(), None);
390
1
            let bob_keys = get_authority_keys_from_seed(&AccountId::from(BOB).to_string(), None);
391
1
            let charlie_keys =
392
1
                get_authority_keys_from_seed(&AccountId::from(CHARLIE).to_string(), None);
393
1
            let dave_keys = get_authority_keys_from_seed(&AccountId::from(DAVE).to_string(), None);
394
1

            
395
1
            let expected_authorities_digest = get_beefy_digest(ConsensusLog::AuthoritiesChange(
396
1
                ValidatorSet::new(
397
1
                    vec![
398
1
                        alice_keys.beefy.clone(),
399
1
                        bob_keys.beefy.clone(),
400
1
                        charlie_keys.beefy.clone(),
401
1
                        dave_keys.beefy.clone(),
402
1
                    ],
403
1
                    1,
404
1
                )
405
1
                .unwrap(),
406
1
            ));
407
1

            
408
1
            // Check that both authorities and MMR digests were correctly placed after session change.
409
1
            let actual_authorities_digest = System::digest().logs[2].clone();
410
1
            let first_mmr_digest = System::digest().logs[3].clone();
411

            
412
1
            let first_mmr_digest = match first_mmr_digest.clone() {
413
1
                DigestItem::Consensus(id, val) => {
414
1
                    if id == BEEFY_ENGINE_ID {
415
1
                        match ConsensusLog::<BeefyId>::decode(&mut &val[..]) {
416
1
                            Ok(result) => match result {
417
                                ConsensusLog::AuthoritiesChange(_) => None,
418
1
                                ConsensusLog::MmrRoot(m) => Some(m),
419
                                ConsensusLog::OnDisabled(_) => None,
420
                            },
421
                            Err(_) => None,
422
                        }
423
                    } else {
424
                        None
425
                    }
426
                }
427
                _ => None,
428
            };
429

            
430
1
            assert!(first_mmr_digest.is_some());
431
1
            assert_eq!(expected_authorities_digest, actual_authorities_digest);
432

            
433
            // After running a single block the MMR digest should update again.
434
1
            run_block();
435
1
            let second_mmr_digest = System::digest().logs[1].clone();
436

            
437
1
            let second_mmr_digest = match second_mmr_digest.clone() {
438
1
                DigestItem::Consensus(id, val) => {
439
1
                    if id == BEEFY_ENGINE_ID {
440
1
                        match ConsensusLog::<BeefyId>::decode(&mut &val[..]) {
441
1
                            Ok(result) => match result {
442
                                ConsensusLog::AuthoritiesChange(_) => None,
443
1
                                ConsensusLog::MmrRoot(m) => Some(m),
444
                                ConsensusLog::OnDisabled(_) => None,
445
                            },
446
                            Err(_) => None,
447
                        }
448
                    } else {
449
                        None
450
                    }
451
                }
452
                _ => None,
453
            };
454
1
            assert!(second_mmr_digest.is_some());
455
1
            assert!(first_mmr_digest.unwrap() != second_mmr_digest.unwrap());
456
1
        });
457
1
}