Skip to content

Commit e2268eb

Browse files
committed
Add real BLS12-381 drand verification test and set GitHub secrets
Contract tests (21 total): - Add test_real_bls_verification: full pairing check with real drand round 27448023 data, dev_mode=false — verifies the entire BLS12-381 pipeline works with authentic cryptographic data - Add test_real_bls_rejects_invalid_signature: confirms invalid signatures are rejected with dev_mode=false Infrastructure: - Set TESTNET_SECRET_KEY and TESTNET_CONTRACT_ID GitHub secrets - Auto-fix 3 lint warnings (unused eslint-disable directives) - Install @vitest/coverage-v8 for coverage reporting
1 parent 9fcc65e commit e2268eb

4 files changed

Lines changed: 408 additions & 97 deletions

File tree

TESTING.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
## Running Tests
44

55
```bash
6-
# Contract tests (Rust — 19 tests)
6+
# Contract tests (Rust — 21 tests)
77
cargo test
88

99
# Frontend tests (TypeScript — 33 tests)
@@ -18,18 +18,17 @@ bash scripts/testBLS12381.sh
1818

1919
## Test Coverage
2020

21-
### Contract Tests (`contracts/gene-splicer/src/test.rs`) — 19 tests
21+
### Contract Tests (`contracts/gene-splicer/src/test.rs`) — 21 tests
2222

2323
- **Minting**: splice_genome, multiple splices, insufficient balance
2424
- **Finalization**: finalize_splice with mock entropy (dev_mode), double finalization, wrong round, nonexistent cartridge
2525
- **Input validation**: wrong randomness length, wrong compressed sig length, wrong uncompressed sig length
2626
- **Batch queries**: get_cartridges_batch (with missing IDs), get_creatures_batch
2727
- **Admin**: admin getter/setter, config getters, set_skin_count, set_drand_public_key (valid + invalid length)
28+
- **BLS12-381**: Real drand signature verification with dev_mode=false (round 27448023), invalid signature rejection
2829
- **TTL**: extend_ttl permissionless call
2930
- **Constructor**: rejects wrong pubkey length
3031

31-
Tests use `dev_mode=true` to skip BLS verification. Full BLS verification is tested via `scripts/testBLS12381.sh` with live drand data against a deployed contract.
32-
3332
### Frontend Tests — 33 tests across 5 files
3433

3534
- `src/services/entropyRelayer.test.ts` (19 tests) — hex conversion, G1 decompression, drand API fetching with timeouts

contracts/gene-splicer/src/test.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,3 +567,136 @@ fn test_set_drand_public_key_wrong_length() {
567567
let bad_key = Bytes::from_array(&env, &[0xff; 96]);
568568
client.set_drand_public_key(&bad_key);
569569
}
570+
571+
// ===== Real BLS12-381 verification test =====
572+
573+
/// Real drand quicknet public key (192 bytes uncompressed G2, CAP-0059 byte order)
574+
fn real_drand_pubkey(env: &Env) -> Bytes {
575+
Bytes::from_slice(
576+
env,
577+
&hex::decode(
578+
"03cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a01a714f2edb74119a2f2b0d5a7c75ba902d163700a61bc224ededd8e63aef7be1aaf8e93d7a9718b047ccddb3eb5d68b0e5db2b6bfbb01c867749cadffca88b36c24f3012ba09fc4d3022c5c37dce0f977d3adb5d183c7477c442b1f04515273"
579+
).unwrap(),
580+
)
581+
}
582+
583+
#[test]
584+
fn test_real_bls_verification() {
585+
// Test with real drand quicknet round 27448023
586+
// This verifies the full BLS12-381 pairing check with authentic drand data
587+
let env = Env::default();
588+
env.mock_all_auths();
589+
590+
let admin = Address::generate(&env);
591+
let user = Address::generate(&env);
592+
593+
let xlm_token = create_xlm_token(&env, &admin);
594+
xlm_token.mint(&user, &100_000_000);
595+
596+
// Deploy with real drand pubkey and dev_mode=false for real verification
597+
let pubkey = real_drand_pubkey(&env);
598+
let contract_id = env.register(
599+
GeneSplicer,
600+
(&admin, &xlm_token.address, 10u64, false, pubkey),
601+
);
602+
let client = GeneSplicerClient::new(&env, &contract_id);
603+
604+
// Mint a cartridge
605+
let cartridge_id = client.splice_genome(&user);
606+
let mut cartridge = client.get_cartridge(&cartridge_id).unwrap();
607+
608+
// Override splice_round to match our real drand data (round 27448023)
609+
// In a real scenario the round would be assigned by the contract based on ledger time.
610+
// For testing we need to match the round to the drand data we have.
611+
cartridge.splice_round = 27448023;
612+
env.as_contract(&contract_id, || {
613+
env.storage().persistent().set(
614+
&crate::DataKey::Cartridge(cartridge_id),
615+
&cartridge,
616+
);
617+
});
618+
619+
// Real drand round 27448023 data:
620+
let randomness = Bytes::from_slice(
621+
&env,
622+
&hex::decode("f22d19a3d8cd3a181fe8155d051fe006a726b1fe0b18043bda3a2fe4c6c1e5d8").unwrap(),
623+
);
624+
let sig_compressed = Bytes::from_slice(
625+
&env,
626+
&hex::decode("967e8a7aa839aa8f672800bb50b1ee29dfa4757d120112c7b858b1f625193a41fb156ad7c69fefc644b9719f88d60313").unwrap(),
627+
);
628+
let sig_uncompressed = Bytes::from_slice(
629+
&env,
630+
&hex::decode("167e8a7aa839aa8f672800bb50b1ee29dfa4757d120112c7b858b1f625193a41fb156ad7c69fefc644b9719f88d603130165791da033fb75626a46b01aeb3e1207d87423db1b5de2dabeb60ee4cc227f750d10de8ec1f77dedd4f311586e5c3e").unwrap(),
631+
);
632+
633+
// This performs REAL BLS12-381 pairing verification on-chain
634+
let creature_id = client.finalize_splice(
635+
&cartridge_id,
636+
&27448023u64,
637+
&randomness,
638+
&sig_compressed,
639+
&sig_uncompressed,
640+
);
641+
642+
assert_eq!(creature_id, cartridge_id);
643+
644+
// Verify creature was created with genes derived from real entropy
645+
let creature = client.get_creature(&creature_id).unwrap();
646+
assert!(creature.head_gene.id <= 14);
647+
assert!(creature.body_gene.id <= 14);
648+
assert!(creature.legs_gene.id <= 14);
649+
}
650+
651+
#[test]
652+
#[should_panic]
653+
fn test_real_bls_rejects_invalid_signature() {
654+
let env = Env::default();
655+
env.mock_all_auths();
656+
657+
let admin = Address::generate(&env);
658+
let user = Address::generate(&env);
659+
660+
let xlm_token = create_xlm_token(&env, &admin);
661+
xlm_token.mint(&user, &100_000_000);
662+
663+
// Deploy with real drand pubkey and dev_mode=false
664+
let pubkey = real_drand_pubkey(&env);
665+
let contract_id = env.register(
666+
GeneSplicer,
667+
(&admin, &xlm_token.address, 10u64, false, pubkey),
668+
);
669+
let client = GeneSplicerClient::new(&env, &contract_id);
670+
671+
let cartridge_id = client.splice_genome(&user);
672+
let mut cartridge = client.get_cartridge(&cartridge_id).unwrap();
673+
cartridge.splice_round = 27448023;
674+
env.as_contract(&contract_id, || {
675+
env.storage().persistent().set(
676+
&crate::DataKey::Cartridge(cartridge_id),
677+
&cartridge,
678+
);
679+
});
680+
681+
// Valid format but WRONG signature data (all 0x11)
682+
let randomness = Bytes::from_array(&env, &[0x42; 32]);
683+
// Build a fake compressed sig that matches fake uncompressed x-coord
684+
let mut fake_compressed = [0x11_u8; 48];
685+
fake_compressed[0] = 0x80 | 0x11; // Set compression flag
686+
let mut fake_uncompressed = [0x22_u8; 96];
687+
fake_uncompressed[0] = 0x11; // Match x-coord byte 0
688+
for i in 1..48 {
689+
fake_uncompressed[i] = 0x11; // Match x-coord
690+
}
691+
let sig_compressed = Bytes::from_array(&env, &fake_compressed);
692+
let sig_uncompressed = Bytes::from_array(&env, &fake_uncompressed);
693+
694+
// Should panic during BLS verification (invalid signature)
695+
client.finalize_splice(
696+
&cartridge_id,
697+
&27448023u64,
698+
&randomness,
699+
&sig_compressed,
700+
&sig_uncompressed,
701+
);
702+
}

0 commit comments

Comments
 (0)