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 = get_authority_keys_from_seed(&AccountId::from(ALICE).to_string());
100
1
            let bob_keys = get_authority_keys_from_seed(&AccountId::from(BOB).to_string());
101
1
            let charlie_keys = get_authority_keys_from_seed(&AccountId::from(CHARLIE).to_string());
102
1
            let dave_keys = get_authority_keys_from_seed(&AccountId::from(DAVE).to_string());
103
1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
261
1
            let expected_new_genesis = System::block_number() + new_beefy_genesis_delay;
262
1

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

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

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

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

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

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

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

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

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

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

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

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

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

            
347
1
            let invalid_equivocation_proof =
348
1
                generate_future_block_voting_proof((1, payload.clone(), set_id), key_pair_bob);
349
1

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

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

            
382
1
            run_to_session(1);
383
1
            assert_eq!(ValidatorSetId::<Runtime>::get(), 1);
384

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

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

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

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

            
426
1
            assert!(first_mmr_digest.is_some());
427
1
            assert_eq!(expected_authorities_digest, actual_authorities_digest);
428

            
429
            // After running a single block the MMR digest should update again.
430
1
            run_block();
431
1
            let second_mmr_digest = System::digest().logs[1].clone();
432

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