Skip to content

Commit 998ca74

Browse files
authoredSep 3, 2024
Merge pull request #43 from icon-project/bsc-bohr
Support bsc bohr upgrade
2 parents 053bdca + f0e86f1 commit 998ca74

File tree

13 files changed

+290
-74
lines changed

13 files changed

+290
-74
lines changed
 

‎bmv/bsc2/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version = '0.6.1'
1+
version = '0.7.2'
22

33
dependencies {
44
compileOnly("foundation.icon:javaee-api:$javaeeVersion")

‎bmv/bsc2/src/main/java/foundation/icon/btp/bmv/bsc2/BTPMessageVerifier.java

+9-7
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ public class BTPMessageVerifier implements BMV {
4444
private final DictDB<byte[], Header> heads = Context.newDictDB("heads", Header.class);
4545

4646
public BTPMessageVerifier(Address _bmc, BigInteger _chainId, @Optional byte[] _header,
47-
@Optional byte[] _validators, @Optional byte[] _candidates, @Optional byte[] _recents) {
47+
@Optional byte[] _validators, @Optional byte[] _candidates,
48+
@Optional byte[] _recents, @Optional int _currTurnLength, @Optional int _nextTurnLength) {
4849
ChainConfig config = ChainConfig.setChainID(_chainId);
4950
if (_header != null) {
5051
Header head = Header.fromBytes(_header);
@@ -53,13 +54,14 @@ public BTPMessageVerifier(Address _bmc, BigInteger _chainId, @Optional byte[] _h
5354
MerkleTreeAccumulator mta = new MerkleTreeAccumulator(head.getNumber().longValueExact());
5455
mta.add(head.getHash().toBytes());
5556

57+
Context.require(_currTurnLength > 0 && _nextTurnLength > 0, "Invalid turn lengths");
5658
Validators validators = Validators.fromBytes(_validators);
5759
EthAddresses recents = EthAddresses.fromBytes(_recents);
5860
if (head.getNumber().compareTo(BigInteger.ZERO) == 0) {
5961
Context.require(recents.size() == 1, "Wrong recent signers");
6062
} else {
61-
Context.require(recents.size() == validators.size() / 2 + 1,
62-
"Wrong recent signers - validators/2+1");
63+
Context.require(recents.size() == Utils.calcMinerHistoryLength(validators.size(),
64+
_currTurnLength) + 1, "Wrong recent signer counts");
6365
}
6466

6567
this.bmc.set(_bmc);
@@ -75,15 +77,15 @@ public BTPMessageVerifier(Address _bmc, BigInteger _chainId, @Optional byte[] _h
7577
Validators.fromBytes(_candidates),
7678
Validators.fromBytes(_validators),
7779
EthAddresses.fromBytes(_recents),
78-
attestation));
80+
attestation, _currTurnLength, _nextTurnLength));
7981
} else {
8082
Context.require(_bmc.equals(this.bmc.get()), "Mismatch BMC address");
8183
}
8284
}
8385

8486
@External(readonly = true)
8587
public String getVersion() {
86-
return "0.5.0";
88+
return "0.7.2";
8789
}
8890

8991
@External(readonly = true)
@@ -300,7 +302,7 @@ private void verify(ChainConfig config, Header head) {
300302
Context.require(head.getGasLimit().compareTo(MIN_GAS_LIMIT) >= 0, "Invalid gas limit(< min)");
301303
Context.require(head.getGasLimit().compareTo(MAX_GAS_LIMIT) <= 0, "Invalid gas limit(> max)");
302304
Context.require(head.getGasUsed().compareTo(head.getGasLimit()) < 0, "Invalid gas used");
303-
Context.require(head.getSigner(BigInteger.valueOf(config.ChainID)).equals(head.getCoinbase()), "Coinbase mismatch");
305+
Context.require(head.getSigner(config, BigInteger.valueOf(config.ChainID)).equals(head.getCoinbase()), "Coinbase mismatch");
304306

305307
if (config.isTycho(head.getTime())) {
306308
Context.require(head.getWithdrawalsHash().equals(EMPTY_WITHDRAWALS_HASH), "Invalid withdrawals hash");
@@ -384,7 +386,7 @@ private long getBackOffTime(Snapshot snap, Header head) {
384386
Validators vals = snap.getValidators();
385387
long number = head.getNumber().longValue();
386388
EthAddress inturn = vals.get((int)(number % (long)vals.size())).getAddress();
387-
if (snap.getRecents().contains(inturn)) {
389+
if (snap.isRecentlySigned(inturn)) {
388390
return 0L;
389391
}
390392
return DEFAULT_BACKOFF_TIME;

‎bmv/bsc2/src/main/java/foundation/icon/btp/bmv/bsc2/ChainConfig.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public class ChainConfig {
2525
public final long Period;
2626
public final BigInteger Hertz;
2727
public final BigInteger Tycho;
28+
public final BigInteger Bohr;
2829

2930
private static ChainConfig instance;
3031

@@ -35,12 +36,13 @@ public static ChainConfig setChainID(BigInteger cid) {
3536
return instance;
3637
}
3738

38-
private ChainConfig(long chainId, long epoch, long period, BigInteger hertz, BigInteger tycho) {
39+
private ChainConfig(long chainId, long epoch, long period, BigInteger hertz, BigInteger tycho, BigInteger bohr) {
3940
this.ChainID = chainId;
4041
this.Epoch = epoch;
4142
this.Period = period;
4243
this.Hertz = hertz;
4344
this.Tycho = tycho;
45+
this.Bohr = bohr;
4446
}
4547

4648
public static ChainConfig getInstance() {
@@ -51,14 +53,14 @@ public static ChainConfig fromChainID(BigInteger cid) {
5153
if (cid.longValue() == 56L) {
5254
// BSC Mainnet
5355
return new ChainConfig(56L, 200L, 3L, BigInteger.valueOf(31302048L),
54-
BigInteger.valueOf(1718863500L));
56+
BigInteger.valueOf(1718863500L), BigInteger.valueOf(1727317200L));
5557
} else if (cid.longValue() == 97L) {
5658
// BSC Testnet
5759
return new ChainConfig(97L, 200L, 3L, BigInteger.valueOf(31103030L),
58-
BigInteger.valueOf(1713330442L));
60+
BigInteger.valueOf(1713330442L), BigInteger.valueOf(1724116996L));
5961
} else if (cid.longValue() == 99L) {
6062
// Private BSC Testnet
61-
return new ChainConfig(99L, 200L, 3L, BigInteger.valueOf(8), null);
63+
return new ChainConfig(99L, 200L, 3L, BigInteger.valueOf(8), null, null);
6264
}
6365

6466
Context.require(false, "No Chain Config - ChainID(" + cid.intValue() + ")");
@@ -77,4 +79,8 @@ public boolean isTycho(long time) {
7779
return Tycho != null && Tycho.longValue() <= time;
7880
}
7981

82+
public boolean isBohr(long time) {
83+
return Bohr != null && Bohr.longValue() <= time;
84+
}
85+
8086
}

‎bmv/bsc2/src/main/java/foundation/icon/btp/bmv/bsc2/EthAddress.java

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
public class EthAddress implements Comparable<EthAddress> {
2525
public static final int LENGTH = 20;
26+
public static final EthAddress EMPTY = new EthAddress(new byte[LENGTH]);
2627

2728
private final byte[] data;
2829

‎bmv/bsc2/src/main/java/foundation/icon/btp/bmv/bsc2/EthAddresses.java

+18
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,28 @@ public EthAddress remove(int i) {
5757
return addresses.remove(i);
5858
}
5959

60+
public EthAddress set(int i, EthAddress v) {
61+
return addresses.set(i, v);
62+
}
63+
6064
public int size() {
6165
return addresses.size();
6266
}
6367

68+
public int count(EthAddress cmp) {
69+
int cnt = 0;
70+
for (EthAddress address : addresses) {
71+
if (address.equals(cmp)) {
72+
cnt++;
73+
}
74+
}
75+
return cnt;
76+
}
77+
78+
public void clear() {
79+
addresses.clear();
80+
}
81+
6482
@Override
6583
public String toString() {
6684
return "EthAddresses{" +

‎bmv/bsc2/src/main/java/foundation/icon/btp/bmv/bsc2/Header.java

+73-22
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public class Header {
3030
public static final int EXTRA_SEAL = 65;
3131
public static final int VALIDATOR_NUMBER_SIZE = 1;
3232
public static final int VALIDATOR_BYTES_LENGTH = EthAddress.LENGTH + BLSPublicKey.LENGTH;
33+
public static final int TURN_LENGTH_SIZE = 1;
3334
// pre-calculated constant uncle hash:) rlp([])
3435
public static final Hash UNCLE_HASH =
3536
Hash.of("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347");
@@ -41,6 +42,7 @@ public class Header {
4142
public static final BigInteger GAS_LIMIT_BOUND_DIVISOR = BigInteger.valueOf(256L);
4243
public static final BigInteger MAX_GAS_LIMIT = BigInteger.valueOf(0x7FFFFFFFFFFFFFFFL); // (2^63-1)
4344
public static final BigInteger MIN_GAS_LIMIT = BigInteger.valueOf(5000L);
45+
public static final int DEFAULT_TURN_LENGTH = 1;
4446

4547
private final Hash parentHash;
4648
private final Hash uncleHash;
@@ -61,6 +63,7 @@ public class Header {
6163
private final Hash withdrawalsHash;
6264
private final BigInteger blobGasUsed;
6365
private final BigInteger excessBlobGas;
66+
private final Hash parentBeaconRoot;
6467

6568
// caches
6669
private Hash hashCache;
@@ -92,6 +95,7 @@ public Header(Hash parentHash, Hash uncleHash, EthAddress coinbase, Hash root,
9295
this.withdrawalsHash = withdrawalsHash;
9396
this.blobGasUsed = blobGasUsed;
9497
this.excessBlobGas = excessBlobGas;
98+
this.parentBeaconRoot = Hash.EMPTY;
9599
}
96100

97101
public static Header readObject(ObjectReader r) {
@@ -135,7 +139,9 @@ public static Header readObject(ObjectReader r) {
135139
}
136140

137141
public static void writeObject(ObjectWriter w, Header o) {
138-
if (ChainConfig.getInstance().isTycho(o.time)) {
142+
if (ChainConfig.getInstance().isBohr(o.time)) {
143+
w.beginList(20);
144+
} else if (ChainConfig.getInstance().isTycho(o.time)) {
139145
w.beginList(19);
140146
} else if (ChainConfig.getInstance().isHertz(o.number)) {
141147
w.beginList(16);
@@ -168,6 +174,9 @@ public static void writeObject(ObjectWriter w, Header o) {
168174
w.write(o.blobGasUsed);
169175
w.write(o.excessBlobGas);
170176
}
177+
if (ChainConfig.getInstance().isBohr(o.time)) {
178+
w.write(o.parentBeaconRoot);
179+
}
171180
w.end();
172181
}
173182

@@ -232,6 +241,9 @@ public VoteAttestation getVoteAttestation(ChainConfig config) {
232241
return null;
233242
}
234243
int start = EXTRA_VANITY + VALIDATOR_NUMBER_SIZE + num * VALIDATOR_BYTES_LENGTH;
244+
if (config.isBohr(time)) {
245+
start += TURN_LENGTH_SIZE;
246+
}
235247
int end = extra.length - EXTRA_SEAL;
236248
blob = Arrays.copyOfRange(extra, start, end);
237249
}
@@ -240,34 +252,73 @@ public VoteAttestation getVoteAttestation(ChainConfig config) {
240252
return atteCache;
241253
}
242254

243-
public EthAddress getSigner(BigInteger cid) {
255+
public int getTurnLength(ChainConfig config) {
256+
Context.require(config.isEpoch(number), "no turn length");
257+
if (!config.isBohr(time)) {
258+
return DEFAULT_TURN_LENGTH;
259+
}
260+
261+
Context.require(extra.length > EXTRA_VANITY + EXTRA_SEAL, "too short extra data for including turn length");
262+
int num = extra[EXTRA_VANITY];
263+
int pos = EXTRA_VANITY + VALIDATOR_NUMBER_SIZE + num * VALIDATOR_BYTES_LENGTH;
264+
Context.require(extra.length > pos, "no field for turn length");
265+
return extra[pos];
266+
}
267+
268+
public EthAddress getSigner(ChainConfig config, BigInteger cid) {
244269
Context.require(extra.length >= EXTRA_SEAL, "Invalid seal bytes");
245270
byte[] signature = Arrays.copyOfRange(extra, extra.length - EXTRA_SEAL, extra.length);
246-
byte[] pubkey = Context.recoverKey("ecdsa-secp256k1", getSealHash(cid), signature, false);
271+
byte[] pubkey = Context.recoverKey("ecdsa-secp256k1", getSealHash(config, cid), signature, false);
247272
byte[] pubhash = Context.hash("keccak-256", Arrays.copyOfRange(pubkey, 1, pubkey.length));
248273
return new EthAddress(Arrays.copyOfRange(pubhash, 12, pubhash.length));
249274
}
250275

251-
private byte[] getSealHash(BigInteger cid) {
276+
private byte[] getSealHash(ChainConfig config, BigInteger cid) {
252277
ByteArrayObjectWriter w = Context.newByteArrayObjectWriter("RLP");
253-
w.beginList(16);
254-
w.write(cid);
255-
w.write(parentHash);
256-
w.write(uncleHash);
257-
w.write(coinbase);
258-
w.write(root);
259-
w.write(txHash);
260-
w.write(receiptHash);
261-
w.write(bloom);
262-
w.write(difficulty);
263-
w.write(number);
264-
w.write(gasLimit);
265-
w.write(gasUsed);
266-
w.write(time);
267-
w.write(Arrays.copyOfRange(extra, 0, extra.length-65));
268-
w.write(mixDigest);
269-
w.write(nonce);
270-
w.end();
278+
if (config.isBohr(time)) {
279+
w.beginList(21);
280+
w.write(cid);
281+
w.write(parentHash);
282+
w.write(uncleHash);
283+
w.write(coinbase);
284+
w.write(root);
285+
w.write(txHash);
286+
w.write(receiptHash);
287+
w.write(bloom);
288+
w.write(difficulty);
289+
w.write(number);
290+
w.write(gasLimit);
291+
w.write(gasUsed);
292+
w.write(time);
293+
w.write(Arrays.copyOfRange(extra, 0, extra.length-65));
294+
w.write(mixDigest);
295+
w.write(nonce);
296+
w.write(baseFee);
297+
w.write(withdrawalsHash);
298+
w.write(blobGasUsed);
299+
w.write(excessBlobGas);
300+
w.write(parentBeaconRoot);
301+
w.end();
302+
} else {
303+
w.beginList(16);
304+
w.write(cid);
305+
w.write(parentHash);
306+
w.write(uncleHash);
307+
w.write(coinbase);
308+
w.write(root);
309+
w.write(txHash);
310+
w.write(receiptHash);
311+
w.write(bloom);
312+
w.write(difficulty);
313+
w.write(number);
314+
w.write(gasLimit);
315+
w.write(gasUsed);
316+
w.write(time);
317+
w.write(Arrays.copyOfRange(extra, 0, extra.length-65));
318+
w.write(mixDigest);
319+
w.write(nonce);
320+
w.end();
321+
}
271322
return Context.hash("keccak-256", w.toByteArray());
272323
}
273324

0 commit comments

Comments
 (0)