Skip to content

Commit ca20a2a

Browse files
0o-de-lallyRosina Von LegatoFranci MartenNora Hindsasuke0787
authored andcommitted
[vouch] permanent vouches for non-validators (#430)
Co-authored-by: Rosina Von Legato <[email protected]> Co-authored-by: Franci Marten <[email protected]> Co-authored-by: Nora Hind <[email protected]> Co-authored-by: sasuke0787 <[email protected]> Co-authored-by: Mariella Fitz Hind <[email protected]>
1 parent b559152 commit ca20a2a

File tree

5 files changed

+292
-86
lines changed

5 files changed

+292
-86
lines changed

framework/libra-framework/sources/ol_sources/ancestry.move

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,12 @@ module ol_framework::ancestry {
221221
// for each account in list, compare to the others.
222222
// if they are unrelated, add them to the list.
223223
let target_acc = vector::borrow<address>(&list, i);
224+
// check if the target account is initialized with ancestry.
225+
if (!exists<Ancestry>(*target_acc)) {
226+
// if not, skip it.
227+
i = i + 1;
228+
continue
229+
};
224230

225231
// now loop through all the accounts again, and check if this target
226232
// account is related to anyone.
@@ -229,6 +235,13 @@ module ol_framework::ancestry {
229235
let comparison_acc = vector::borrow(&list, k);
230236
// skip if you're the same person
231237
if (comparison_acc != target_acc) {
238+
// check that the comparison account is initialized
239+
// with ancestry.
240+
if (!exists<Ancestry>(*comparison_acc)) {
241+
k = k + 1;
242+
continue
243+
};
244+
232245
// check ancestry algo
233246
let (is_fam, _parent) = is_family(*comparison_acc, *target_acc);
234247
if (!is_fam) {
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#[test_only]
2+
module ol_framework::test_permanent_vouches {
3+
use std::vector;
4+
use ol_framework::vouch;
5+
use ol_framework::mock;
6+
use ol_framework::epoch_helper;
7+
8+
#[test(root = @ol_framework, alice = @0x1000a, _bob = @0x1000b)]
9+
fun vouch_persists_across_epochs(root: &signer, alice: &signer, _bob: &signer) {
10+
// Initialize genesis and validators
11+
mock::ol_test_genesis(root);
12+
mock::create_validator_accounts(root, 2, false);
13+
vouch::set_vouch_price(root, 0);
14+
15+
// Alice vouches for Bob
16+
vouch::vouch_for(alice, @0x1000b);
17+
18+
// Verify vouch exists
19+
let received_vouches = vouch::get_received_vouches_not_expired(@0x1000b);
20+
assert!(vector::contains(&received_vouches, &@0x1000a), 1);
21+
22+
// Simulate multiple epochs passing (more than old expiration limit)
23+
let i = 0;
24+
while (i < 50) { // Beyond old EXPIRATION_ELAPSED_EPOCHS
25+
mock::trigger_epoch(root);
26+
i = i + 1;
27+
};
28+
29+
// Verify vouch still exists (permanent vouches)
30+
let received_vouches_after = vouch::get_received_vouches_not_expired(@0x1000b);
31+
assert!(vector::contains(&received_vouches_after, &@0x1000a), 2);
32+
}
33+
34+
#[test(root = @ol_framework, alice = @0x1000a, _bob = @0x1000b)]
35+
fun vouch_revocation_still_works(root: &signer, alice: &signer, _bob: &signer) {
36+
// Initialize genesis and validators
37+
mock::ol_test_genesis(root);
38+
mock::create_validator_accounts(root, 2, false);
39+
vouch::set_vouch_price(root, 0);
40+
41+
// Alice vouches for Bob
42+
vouch::vouch_for(alice, @0x1000b);
43+
44+
// Verify vouch exists
45+
let received_vouches = vouch::get_received_vouches_not_expired(@0x1000b);
46+
assert!(vector::contains(&received_vouches, &@0x1000a), 1);
47+
48+
// Alice revokes vouch for Bob
49+
vouch::revoke(alice, @0x1000b);
50+
51+
// Verify vouch is removed
52+
let received_vouches_after = vouch::get_received_vouches_not_expired(@0x1000b);
53+
assert!(!vector::contains(&received_vouches_after, &@0x1000a), 2);
54+
55+
// Verify given vouches are also updated
56+
let given_vouches = vouch::get_given_vouches_not_expired(@0x1000a);
57+
assert!(!vector::contains(&given_vouches, &@0x1000b), 3);
58+
}
59+
60+
#[test(root = @ol_framework, alice = @0x1000a, _bob = @0x1000b)]
61+
fun vouch_update_extends_permanently(root: &signer, alice: &signer, _bob: &signer) {
62+
// Initialize genesis and validators
63+
mock::ol_test_genesis(root);
64+
mock::create_validator_accounts(root, 2, false);
65+
vouch::set_vouch_price(root, 0);
66+
67+
// Alice vouches for Bob initially
68+
vouch::vouch_for(alice, @0x1000b);
69+
70+
let (_received_vouches, epochs) = vouch::get_received_vouches(@0x1000b);
71+
let initial_epoch = *vector::borrow(&epochs, 0);
72+
73+
// Advance one epoch to ensure difference
74+
mock::trigger_epoch(root);
75+
76+
// Alice vouches for Bob again (should update epoch)
77+
vouch::vouch_for(alice, @0x1000b);
78+
79+
let (received_vouches_after, epochs_after) = vouch::get_received_vouches(@0x1000b);
80+
let updated_epoch = *vector::borrow(&epochs_after, 0);
81+
82+
// Should still have exactly one vouch from Alice
83+
assert!(vector::length(&received_vouches_after) == 1, 1);
84+
assert!(vector::contains(&received_vouches_after, &@0x1000a), 2);
85+
86+
// Epoch should be updated to current
87+
let current_epoch = epoch_helper::get_current_epoch();
88+
assert!(updated_epoch == current_epoch, 3);
89+
assert!(updated_epoch >= initial_epoch, 4);
90+
}
91+
92+
#[test(root = @ol_framework, alice = @0x1000a, _bob = @0x1000b, _charlie = @0x1000c)]
93+
fun lifetime_tracking_with_permanent_vouches(root: &signer, alice: &signer, _bob: &signer, _charlie: &signer) {
94+
// Initialize genesis and validators
95+
mock::ol_test_genesis(root);
96+
mock::create_validator_accounts(root, 3, false);
97+
vouch::set_vouch_price(root, 0);
98+
99+
// Alice vouches for both Bob and Charlie
100+
vouch::vouch_for(alice, @0x1000b);
101+
vouch::vouch_for(alice, @0x1000c);
102+
103+
// Check that Alice's given count is 2
104+
let given_vouches = vouch::get_given_vouches_not_expired(@0x1000a);
105+
assert!(vector::length(&given_vouches) == 2, 1);
106+
107+
// Alice revokes vouch for Bob
108+
vouch::revoke(alice, @0x1000b);
109+
110+
// Check that Alice's given count is now 1
111+
let given_vouches_after = vouch::get_given_vouches_not_expired(@0x1000a);
112+
assert!(vector::length(&given_vouches_after) == 1, 2);
113+
assert!(vector::contains(&given_vouches_after, &@0x1000c), 3);
114+
assert!(!vector::contains(&given_vouches_after, &@0x1000b), 4);
115+
}
116+
117+
#[test(root = @ol_framework, alice = @0x1000a, _bob = @0x1000b)]
118+
fun true_friends_with_permanent_vouches(root: &signer, alice: &signer, _bob: &signer) {
119+
// Initialize genesis and validators
120+
mock::ol_test_genesis(root);
121+
mock::create_validator_accounts(root, 2, false);
122+
vouch::set_vouch_price(root, 0);
123+
124+
// Alice vouches for Bob
125+
vouch::vouch_for(alice, @0x1000b);
126+
127+
// Test the core permanent vouch functionality
128+
// 1. Check that the raw vouch exists
129+
let received_vouches = vouch::get_received_vouches_not_expired(@0x1000b);
130+
assert!(vector::contains(&received_vouches, &@0x1000a), 1);
131+
132+
// 2. Check that the given vouch exists
133+
let given_vouches = vouch::get_given_vouches_not_expired(@0x1000a);
134+
assert!(vector::contains(&given_vouches, &@0x1000b), 2);
135+
136+
// 3. Verify that vouches persist after many epochs (permanent aspect)
137+
let i = 0;
138+
while (i < 10) { // Multiple epochs to test persistence
139+
mock::trigger_epoch(root);
140+
i = i + 1;
141+
};
142+
143+
// Vouches should still exist after many epochs
144+
let received_vouches_after = vouch::get_received_vouches_not_expired(@0x1000b);
145+
assert!(vector::contains(&received_vouches_after, &@0x1000a), 3);
146+
147+
let given_vouches_after = vouch::get_given_vouches_not_expired(@0x1000a);
148+
assert!(vector::contains(&given_vouches_after, &@0x1000b), 4);
149+
150+
// Note: We avoid testing true_friends and is_valid_voucher_for here because
151+
// they apply ancestry filtering which may filter out vouches between test addresses
152+
}
153+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
2+
module ol_framework::validator_vouch {
3+
use std::signer;
4+
use std::vector;
5+
use ol_framework::epoch_helper;
6+
use ol_framework::vouch;
7+
use diem_framework::stake;
8+
9+
friend ol_framework::vouch_txs;
10+
11+
/// How many epochs must pass before the voucher expires.
12+
const EXPIRATION_ELAPSED_EPOCHS: u64 = 45;
13+
14+
/// Returns true if both addresses are validators
15+
fun both_are_validators(addr1: address, addr2: address): bool {
16+
stake::is_valid(addr1) && stake::is_valid(addr2)
17+
}
18+
19+
/// Filter expired vouches for validator accounts only
20+
public fun filter_expired(account: address, list_addr: vector<address>, epoch_vouched: vector<u64>): vector<address> {
21+
let valid_vouches = vector::empty<address>();
22+
let current_epoch = epoch_helper::get_current_epoch();
23+
let i = 0;
24+
while (i < vector::length(&list_addr)) {
25+
let vouch_received = vector::borrow(&list_addr, i);
26+
let when_vouched = *vector::borrow(&epoch_vouched, i);
27+
if (both_are_validators(account, *vouch_received)) {
28+
// Only expire if both voucher and vouchee are validators
29+
if ((when_vouched + EXPIRATION_ELAPSED_EPOCHS) > current_epoch) {
30+
vector::push_back(&mut valid_vouches, *vouch_received);
31+
}
32+
} else {
33+
// Otherwise, never expire vouches
34+
vector::push_back(&mut valid_vouches, *vouch_received);
35+
};
36+
i = i + 1;
37+
};
38+
valid_vouches
39+
}
40+
41+
42+
/// Garbage collect expired vouches for validator accounts using signer
43+
public(friend) fun garbage_collect_expired_for_signer(user_sig: &signer) {
44+
let user = signer::address_of(user_sig);
45+
let (outgoing_vouches, outgoing_epochs) = vouch::get_given_vouches(user);
46+
let (incoming_vouches, incoming_epochs) = vouch::get_received_vouches(user);
47+
garbage_collect_expired(user, outgoing_vouches, outgoing_epochs, incoming_vouches, incoming_epochs);
48+
}
49+
50+
/// Garbage collect expired vouches for validator accounts only
51+
fun garbage_collect_expired(user: address, outgoing_vouches: vector<address>, outgoing_epochs: vector<u64>, incoming_vouches: vector<address>, incoming_epochs: vector<u64>): (vector<address>, vector<u64>, vector<address>, vector<u64>) {
52+
let current_epoch = epoch_helper::get_current_epoch();
53+
let i = 0;
54+
while (i < vector::length(&outgoing_vouches)) {
55+
let vouch_received = vector::borrow(&outgoing_vouches, i);
56+
let when_vouched = *vector::borrow(&outgoing_epochs, i);
57+
if (both_are_validators(user, *vouch_received)) {
58+
if ((when_vouched + EXPIRATION_ELAPSED_EPOCHS) <= current_epoch) {
59+
// Expired: remove vouch
60+
vouch::remove_vouch(user, *vouch_received);
61+
}
62+
};
63+
// else: do nothing, non-validator vouches never expire
64+
i = i + 1;
65+
};
66+
67+
let i = 0;
68+
while (i < vector::length(&incoming_vouches)) {
69+
let vouch_received = vector::borrow(&incoming_vouches, i);
70+
let when_vouched = *vector::borrow(&incoming_epochs, i);
71+
if (both_are_validators(user, *vouch_received)) {
72+
if ((when_vouched + EXPIRATION_ELAPSED_EPOCHS) <= current_epoch) {
73+
// Expired: remove vouch
74+
vouch::remove_vouch(*vouch_received, user);
75+
}
76+
};
77+
// else: do nothing, non-validator vouches never expire
78+
i = i + 1;
79+
};
80+
// No need to return filtered lists, as state is mutated directly
81+
(vector::empty<address>(), vector::empty<u64>(), vector::empty<address>(), vector::empty<u64>())
82+
}
83+
84+
#[view]
85+
/// gets the received vouches not expired
86+
// TODO: duplicated with true_friends
87+
public fun get_received_vouches_not_expired(addr: address): vector<address> {
88+
let (all_received, epoch_vouched) = vouch::get_received_vouches(addr);
89+
filter_expired(addr, all_received, epoch_vouched)
90+
}
91+
}

0 commit comments

Comments
 (0)