Skip to content

Commit 8fef20b

Browse files
authored
Merge pull request #21 from EYBlockchain/Jiajie/de-register-proposer
Jiajie/de register proposer
2 parents dcdaab9 + 5e38e89 commit 8fef20b

59 files changed

Lines changed: 1140 additions & 337 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

blockchain_assets/contracts/Nightfall.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,7 @@ contract Nightfall is
511511

512512
// Load the hashed value and shift right by 4 bits
513513
result := shr(4, mload(freePtr))
514+
514515
}
515516
}
516517

blockchain_assets/contracts/RoundRobin.sol

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,75 @@ pragma solidity ^0.8.20;
33

44
import "./ProposerManager.sol";
55
import "./Nightfall.sol";
6+
import "./X509/Certified.sol";
7+
8+
69
/// @title Proposers
710
/// @notice An Round-Robin implementation for choosing proposers
811

9-
contract RoundRobin is ProposerManager {
12+
contract RoundRobin is ProposerManager, Certified {
13+
1014
mapping(address => Proposer) public proposers;
1115
mapping(address => uint) public pending_withdraws;
1216
mapping(string => bool) public proposer_urls;
17+
// When a proposer voluntarily deregisters:
18+
// Record the block number. Enforce that they cannot reregister until a certain COOLDOWN_BLOCKS window has passed.
19+
mapping(address => uint) public last_exit_block;
1320
Proposer private current;
1421
uint public start_l1_block;
1522
int public start_l2_block;
1623
uint public proposer_count;
1724
uint public immutable STAKE;
1825
uint public immutable DING;
26+
// When the current proposer voluntarily deregisters, a small but nontrivial penalty is deducted.
27+
uint public immutable EXIT_PENALTY;
28+
// This is the number of blocks that must pass before a proposer can reregister after exiting
29+
uint public immutable COOLDOWN_BLOCKS;
1930
uint public immutable ROTATION_BlOCKS;
2031
uint public escrow = 0;
2132
Nightfall private nightfall;
22-
address private owner;
33+
34+
// instance of the certified contract
35+
Certified private certified;
2336

2437
modifier only_owner() {
2538
require(msg.sender == owner, "Only the owner can call this function");
2639
_;
2740
}
2841

2942
constructor(
43+
address x509_address,
44+
address sanctionsListAddress,
3045
address default_proposer_address,
3146
string memory default_proposer_url,
3247
uint stake,
3348
uint ding,
49+
uint exit_penalty,
50+
uint cooling_blocks,
3451
uint rotation_blocks
35-
) payable {
52+
)
53+
Certified(
54+
X509Interface(x509_address),
55+
SanctionsListInterface(sanctionsListAddress)
56+
)
57+
payable
58+
{
3659
STAKE = stake;
3760
DING = ding;
61+
EXIT_PENALTY = exit_penalty;
62+
COOLDOWN_BLOCKS = cooling_blocks;
63+
require(
64+
cooling_blocks > 0,
65+
"Cooling blocks must be greater than zero"
66+
);
67+
require(
68+
stake >= exit_penalty,
69+
"Stake must be greater than exit penalty"
70+
);
3871
ROTATION_BlOCKS = rotation_blocks;
3972
require(
4073
msg.value == STAKE,
41-
"You have not paid the correct staking amount"
74+
"You have not paid the correct staking amount during deployment"
4275
);
4376
current = Proposer({
4477
stake: STAKE,
@@ -47,10 +80,20 @@ contract RoundRobin is ProposerManager {
4780
next_addr: default_proposer_address,
4881
previous_addr: default_proposer_address
4982
});
83+
escrow += STAKE;
5084
proposers[default_proposer_address] = current;
5185
proposer_urls[default_proposer_url] = true;
5286
proposer_count = 1;
53-
owner = msg.sender;
87+
}
88+
89+
function set_x509_address(address x509_address) external onlyOwner {
90+
x509 = X509(x509_address);
91+
}
92+
93+
function set_sanctions_list(
94+
address sanctionsListAddress
95+
) external onlyOwner {
96+
sanctionsList = SanctionsListInterface(sanctionsListAddress);
5497
}
5598

5699
// we set the nightfall contract address later because we probably don't know it at the time of deployment
@@ -69,10 +112,14 @@ contract RoundRobin is ProposerManager {
69112

70113
function add_proposer(
71114
string calldata proposer_url
72-
) external payable override {
115+
) external payable override onlyCertified{
116+
// Enforce cooldown only if they have previously exited
117+
if (last_exit_block[msg.sender] != 0) {
118+
require(block.number > last_exit_block[msg.sender] + COOLDOWN_BLOCKS, "Cooldown period not met");
119+
}
73120
require(
74121
msg.value == STAKE,
75-
"You have not paid the correct staking amount"
122+
"You have not paid the correct staking amount during registration"
76123
);
77124
require(
78125
proposers[msg.sender].addr == address(0),
@@ -83,6 +130,7 @@ contract RoundRobin is ProposerManager {
83130
"This proposer URL is already in use"
84131
);
85132
escrow += STAKE;
133+
86134
// we add the new proposer behind the current proposer, so it will be the last to be called for
87135
// first, insert its address in the linked list
88136
address current_address = current.addr;
@@ -126,10 +174,26 @@ contract RoundRobin is ProposerManager {
126174
proposer_address != address(0),
127175
"The proposer address cannot be zero"
128176
);
129-
require(
130-
proposer_address != current.addr,
131-
"You cannot remove the current proposer"
177+
if (msg.sender == current.addr) {
178+
require(
179+
proposer_count > 1,
180+
"Cannot deregister the only active proposer"
181+
);
182+
require(
183+
proposers[proposer_address].stake >= EXIT_PENALTY,
184+
"Insufficient stake for exit"
132185
);
186+
187+
proposers[proposer_address].stake -= EXIT_PENALTY;
188+
escrow -= EXIT_PENALTY;
189+
190+
// Rotate to the next proposer before removing
191+
current = proposers[current.next_addr];
192+
start_l1_block = block.number;
193+
start_l2_block = nightfall.layer2_block_number();
194+
195+
last_exit_block[proposer_address] = block.number;
196+
}
133197
Proposer storage this_proposer = proposers[proposer_address]; // don't forget these only create references
134198
Proposer storage next_proposer = proposers[this_proposer.next_addr];
135199
Proposer storage previous_proposer = proposers[
@@ -195,5 +259,6 @@ contract RoundRobin is ProposerManager {
195259
return;
196260
}
197261
proposer.stake = uint(new_stake);
262+
escrow -= DING;
198263
}
199264
}

blockchain_assets/contracts/X509/X509.sol

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import './DerParser.sol';
77
import './Allowlist.sol';
88
import './X509Interface.sol';
99
import './Sha.sol';
10-
10+
import "forge-std/console2.sol";
1111
contract X509 is DERParser, Allowlist, Sha, X509Interface {
1212
uint256 constant SECONDS_PER_DAY = 24 * 60 * 60;
1313
int256 constant OFFSET19700101 = 2440588;
@@ -31,6 +31,9 @@ contract X509 is DERParser, Allowlist, Sha, X509Interface {
3131
mapping(bytes32 => RSAPublicKey) trustedPublicKeys;
3232
mapping(bytes32 => bool) revokedKeys;
3333
mapping(address => bytes32) keysByUser;
34+
// Reverse mapping to ensure one certificate is tied to one address
35+
mapping(bytes32 => address) addressByKey;
36+
3437
bytes32[][] extendedKeyUsageOIDs; // this is an array of arrays because each CA has their own set of OIDs that they use
3538
bytes32[][] certificatePoliciesOIDs; // this is an array of arrays because each CA has their own set of OIDs that they use
3639

@@ -412,6 +415,10 @@ contract X509 is DERParser, Allowlist, Sha, X509Interface {
412415
uint256 expiry = checkDates(tlvs);
413416
RSAPublicKey memory certificatePublicKey = extractPublicKey(tlvs);
414417
bytes32 subjectKeyIdentifier = extractSubjectKeyIdentifier(tlvs);
418+
console2.log(
419+
'X509: Subject Key Identifier: %s',
420+
uint256(subjectKeyIdentifier)
421+
);
415422
require(
416423
!revokedKeys[subjectKeyIdentifier],
417424
'X509: The subject key of this certificate has been revoked'
@@ -437,11 +444,24 @@ contract X509 is DERParser, Allowlist, Sha, X509Interface {
437444
checkCertificatePolicies(tlvs, oidGroup);
438445
// // If we get here, we're good so add this user to the allowlist data, unless we're only checking the certificate.
439446
if (!checkOnly) {
447+
// Ensure one certificate is tied to one address and vice versa
448+
require(
449+
keysByUser[addr] == bytes32(0) || keysByUser[addr] == subjectKeyIdentifier,
450+
'X509: This address is already linked to a different certificate'
451+
);
452+
require(
453+
addressByKey[subjectKeyIdentifier] == address(0) || addressByKey[subjectKeyIdentifier] == addr,
454+
'X509: This certificate is already linked to a different address'
455+
);
440456
// Before we finally add the address to the allowlist, just check that the sender of the allowlist request actually owns the
441457
// end user cert. We do this by getting them to sign the Ethereum address they want allowlisted.
442458
checkSignature(addressSignature, abi.encodePacked(uint160(addr)), certificatePublicKey);
443459
expires[addr] = expiry;
444460
keysByUser[addr] = subjectKeyIdentifier;
461+
462+
// RECORD reverse mapping for one-to-one binding
463+
addressByKey[subjectKeyIdentifier] = addr;
464+
445465
addUserToAllowlist(addr); // all checks have passed, so they are free to trade for now.
446466
}
447467
}
@@ -472,6 +492,11 @@ contract X509 is DERParser, Allowlist, Sha, X509Interface {
472492
);
473493
revokedKeys[subjectKeyIdentifier] = true;
474494
delete trustedPublicKeys[subjectKeyIdentifier];
495+
496+
// CLEANUP: remove bidirectional binding when revoked
497+
address addr = addressByKey[subjectKeyIdentifier];
498+
delete keysByUser[addr];
499+
delete addressByKey[subjectKeyIdentifier];
475500
}
476501

477502
/**
@@ -495,5 +520,10 @@ contract X509 is DERParser, Allowlist, Sha, X509Interface {
495520
);
496521
revokedKeys[subjectKeyIdentifier] = true;
497522
delete trustedPublicKeys[subjectKeyIdentifier];
523+
524+
// CLEANUP: remove bidirectional binding when revoked
525+
address addr = addressByKey[subjectKeyIdentifier];
526+
delete keysByUser[addr];
527+
delete addressByKey[subjectKeyIdentifier];
498528
}
499-
}
529+
}

0 commit comments

Comments
 (0)