Skip to content

Commit

Permalink
Merge pull request #43 from icon-project/bsc-bohr
Browse files Browse the repository at this point in the history
Support bsc bohr upgrade
  • Loading branch information
kornery authored Sep 3, 2024
2 parents 053bdca + f0e86f1 commit 998ca74
Show file tree
Hide file tree
Showing 13 changed files with 290 additions and 74 deletions.
2 changes: 1 addition & 1 deletion bmv/bsc2/build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = '0.6.1'
version = '0.7.2'

dependencies {
compileOnly("foundation.icon:javaee-api:$javaeeVersion")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ public class BTPMessageVerifier implements BMV {
private final DictDB<byte[], Header> heads = Context.newDictDB("heads", Header.class);

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

Context.require(_currTurnLength > 0 && _nextTurnLength > 0, "Invalid turn lengths");
Validators validators = Validators.fromBytes(_validators);
EthAddresses recents = EthAddresses.fromBytes(_recents);
if (head.getNumber().compareTo(BigInteger.ZERO) == 0) {
Context.require(recents.size() == 1, "Wrong recent signers");
} else {
Context.require(recents.size() == validators.size() / 2 + 1,
"Wrong recent signers - validators/2+1");
Context.require(recents.size() == Utils.calcMinerHistoryLength(validators.size(),
_currTurnLength) + 1, "Wrong recent signer counts");
}

this.bmc.set(_bmc);
Expand All @@ -75,15 +77,15 @@ public BTPMessageVerifier(Address _bmc, BigInteger _chainId, @Optional byte[] _h
Validators.fromBytes(_candidates),
Validators.fromBytes(_validators),
EthAddresses.fromBytes(_recents),
attestation));
attestation, _currTurnLength, _nextTurnLength));
} else {
Context.require(_bmc.equals(this.bmc.get()), "Mismatch BMC address");
}
}

@External(readonly = true)
public String getVersion() {
return "0.5.0";
return "0.7.2";
}

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

if (config.isTycho(head.getTime())) {
Context.require(head.getWithdrawalsHash().equals(EMPTY_WITHDRAWALS_HASH), "Invalid withdrawals hash");
Expand Down Expand Up @@ -384,7 +386,7 @@ private long getBackOffTime(Snapshot snap, Header head) {
Validators vals = snap.getValidators();
long number = head.getNumber().longValue();
EthAddress inturn = vals.get((int)(number % (long)vals.size())).getAddress();
if (snap.getRecents().contains(inturn)) {
if (snap.isRecentlySigned(inturn)) {
return 0L;
}
return DEFAULT_BACKOFF_TIME;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class ChainConfig {
public final long Period;
public final BigInteger Hertz;
public final BigInteger Tycho;
public final BigInteger Bohr;

private static ChainConfig instance;

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

private ChainConfig(long chainId, long epoch, long period, BigInteger hertz, BigInteger tycho) {
private ChainConfig(long chainId, long epoch, long period, BigInteger hertz, BigInteger tycho, BigInteger bohr) {
this.ChainID = chainId;
this.Epoch = epoch;
this.Period = period;
this.Hertz = hertz;
this.Tycho = tycho;
this.Bohr = bohr;
}

public static ChainConfig getInstance() {
Expand All @@ -51,14 +53,14 @@ public static ChainConfig fromChainID(BigInteger cid) {
if (cid.longValue() == 56L) {
// BSC Mainnet
return new ChainConfig(56L, 200L, 3L, BigInteger.valueOf(31302048L),
BigInteger.valueOf(1718863500L));
BigInteger.valueOf(1718863500L), BigInteger.valueOf(1727317200L));
} else if (cid.longValue() == 97L) {
// BSC Testnet
return new ChainConfig(97L, 200L, 3L, BigInteger.valueOf(31103030L),
BigInteger.valueOf(1713330442L));
BigInteger.valueOf(1713330442L), BigInteger.valueOf(1724116996L));
} else if (cid.longValue() == 99L) {
// Private BSC Testnet
return new ChainConfig(99L, 200L, 3L, BigInteger.valueOf(8), null);
return new ChainConfig(99L, 200L, 3L, BigInteger.valueOf(8), null, null);
}

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

public boolean isBohr(long time) {
return Bohr != null && Bohr.longValue() <= time;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

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

private final byte[] data;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,28 @@ public EthAddress remove(int i) {
return addresses.remove(i);
}

public EthAddress set(int i, EthAddress v) {
return addresses.set(i, v);
}

public int size() {
return addresses.size();
}

public int count(EthAddress cmp) {
int cnt = 0;
for (EthAddress address : addresses) {
if (address.equals(cmp)) {
cnt++;
}
}
return cnt;
}

public void clear() {
addresses.clear();
}

@Override
public String toString() {
return "EthAddresses{" +
Expand Down
95 changes: 73 additions & 22 deletions bmv/bsc2/src/main/java/foundation/icon/btp/bmv/bsc2/Header.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class Header {
public static final int EXTRA_SEAL = 65;
public static final int VALIDATOR_NUMBER_SIZE = 1;
public static final int VALIDATOR_BYTES_LENGTH = EthAddress.LENGTH + BLSPublicKey.LENGTH;
public static final int TURN_LENGTH_SIZE = 1;
// pre-calculated constant uncle hash:) rlp([])
public static final Hash UNCLE_HASH =
Hash.of("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347");
Expand All @@ -41,6 +42,7 @@ public class Header {
public static final BigInteger GAS_LIMIT_BOUND_DIVISOR = BigInteger.valueOf(256L);
public static final BigInteger MAX_GAS_LIMIT = BigInteger.valueOf(0x7FFFFFFFFFFFFFFFL); // (2^63-1)
public static final BigInteger MIN_GAS_LIMIT = BigInteger.valueOf(5000L);
public static final int DEFAULT_TURN_LENGTH = 1;

private final Hash parentHash;
private final Hash uncleHash;
Expand All @@ -61,6 +63,7 @@ public class Header {
private final Hash withdrawalsHash;
private final BigInteger blobGasUsed;
private final BigInteger excessBlobGas;
private final Hash parentBeaconRoot;

// caches
private Hash hashCache;
Expand Down Expand Up @@ -92,6 +95,7 @@ public Header(Hash parentHash, Hash uncleHash, EthAddress coinbase, Hash root,
this.withdrawalsHash = withdrawalsHash;
this.blobGasUsed = blobGasUsed;
this.excessBlobGas = excessBlobGas;
this.parentBeaconRoot = Hash.EMPTY;
}

public static Header readObject(ObjectReader r) {
Expand Down Expand Up @@ -135,7 +139,9 @@ public static Header readObject(ObjectReader r) {
}

public static void writeObject(ObjectWriter w, Header o) {
if (ChainConfig.getInstance().isTycho(o.time)) {
if (ChainConfig.getInstance().isBohr(o.time)) {
w.beginList(20);
} else if (ChainConfig.getInstance().isTycho(o.time)) {
w.beginList(19);
} else if (ChainConfig.getInstance().isHertz(o.number)) {
w.beginList(16);
Expand Down Expand Up @@ -168,6 +174,9 @@ public static void writeObject(ObjectWriter w, Header o) {
w.write(o.blobGasUsed);
w.write(o.excessBlobGas);
}
if (ChainConfig.getInstance().isBohr(o.time)) {
w.write(o.parentBeaconRoot);
}
w.end();
}

Expand Down Expand Up @@ -232,6 +241,9 @@ public VoteAttestation getVoteAttestation(ChainConfig config) {
return null;
}
int start = EXTRA_VANITY + VALIDATOR_NUMBER_SIZE + num * VALIDATOR_BYTES_LENGTH;
if (config.isBohr(time)) {
start += TURN_LENGTH_SIZE;
}
int end = extra.length - EXTRA_SEAL;
blob = Arrays.copyOfRange(extra, start, end);
}
Expand All @@ -240,34 +252,73 @@ public VoteAttestation getVoteAttestation(ChainConfig config) {
return atteCache;
}

public EthAddress getSigner(BigInteger cid) {
public int getTurnLength(ChainConfig config) {
Context.require(config.isEpoch(number), "no turn length");
if (!config.isBohr(time)) {
return DEFAULT_TURN_LENGTH;
}

Context.require(extra.length > EXTRA_VANITY + EXTRA_SEAL, "too short extra data for including turn length");
int num = extra[EXTRA_VANITY];
int pos = EXTRA_VANITY + VALIDATOR_NUMBER_SIZE + num * VALIDATOR_BYTES_LENGTH;
Context.require(extra.length > pos, "no field for turn length");
return extra[pos];
}

public EthAddress getSigner(ChainConfig config, BigInteger cid) {
Context.require(extra.length >= EXTRA_SEAL, "Invalid seal bytes");
byte[] signature = Arrays.copyOfRange(extra, extra.length - EXTRA_SEAL, extra.length);
byte[] pubkey = Context.recoverKey("ecdsa-secp256k1", getSealHash(cid), signature, false);
byte[] pubkey = Context.recoverKey("ecdsa-secp256k1", getSealHash(config, cid), signature, false);
byte[] pubhash = Context.hash("keccak-256", Arrays.copyOfRange(pubkey, 1, pubkey.length));
return new EthAddress(Arrays.copyOfRange(pubhash, 12, pubhash.length));
}

private byte[] getSealHash(BigInteger cid) {
private byte[] getSealHash(ChainConfig config, BigInteger cid) {
ByteArrayObjectWriter w = Context.newByteArrayObjectWriter("RLP");
w.beginList(16);
w.write(cid);
w.write(parentHash);
w.write(uncleHash);
w.write(coinbase);
w.write(root);
w.write(txHash);
w.write(receiptHash);
w.write(bloom);
w.write(difficulty);
w.write(number);
w.write(gasLimit);
w.write(gasUsed);
w.write(time);
w.write(Arrays.copyOfRange(extra, 0, extra.length-65));
w.write(mixDigest);
w.write(nonce);
w.end();
if (config.isBohr(time)) {
w.beginList(21);
w.write(cid);
w.write(parentHash);
w.write(uncleHash);
w.write(coinbase);
w.write(root);
w.write(txHash);
w.write(receiptHash);
w.write(bloom);
w.write(difficulty);
w.write(number);
w.write(gasLimit);
w.write(gasUsed);
w.write(time);
w.write(Arrays.copyOfRange(extra, 0, extra.length-65));
w.write(mixDigest);
w.write(nonce);
w.write(baseFee);
w.write(withdrawalsHash);
w.write(blobGasUsed);
w.write(excessBlobGas);
w.write(parentBeaconRoot);
w.end();
} else {
w.beginList(16);
w.write(cid);
w.write(parentHash);
w.write(uncleHash);
w.write(coinbase);
w.write(root);
w.write(txHash);
w.write(receiptHash);
w.write(bloom);
w.write(difficulty);
w.write(number);
w.write(gasLimit);
w.write(gasUsed);
w.write(time);
w.write(Arrays.copyOfRange(extra, 0, extra.length-65));
w.write(mixDigest);
w.write(nonce);
w.end();
}
return Context.hash("keccak-256", w.toByteArray());
}

Expand Down
Loading

0 comments on commit 998ca74

Please sign in to comment.