Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BMV for Plato-BSC #21

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public BTPMessageVerifier(Address bmc, BigInteger chainId, byte[] header,
head.getHash(),
head.getNumber(),
new EthAddresses(toSortedList(validators)),
new EthAddresses(head.getValidators()),
new EthAddresses(head.getValidators(config)),
new EthAddresses(toSortedList(recents))));
}

Expand Down Expand Up @@ -268,17 +268,18 @@ private void verify(ChainConfig config, Header head) {
byte[] extra = head.getExtra();
Context.require(extra.length >= EXTRA_VANITY, "Missing signer vanity");
Context.require(extra.length >= EXTRA_VANITY + EXTRA_SEAL, "Missing signer seal");
int signersBytes = extra.length - EXTRA_VANITY - EXTRA_SEAL;
byte[] signersBytes = config.getValidatorBytesFromHeader(head);
if (config.isEpoch(head.getNumber())) {
Context.require(signersBytes % EthAddress.ADDRESS_LEN == 0, "Malformed validators set bytes");
Context.require(signersBytes.length != 0, "Malformed validators set bytes");
} else {
Context.require(signersBytes == 0, "Forbidden validators set bytes");
Context.require(signersBytes.length == 0, "Forbidden validators set bytes");
}
Context.require(head.getMixDigest().equals(Hash.EMPTY), "Invalid mix digest" + head.getMixDigest());
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");
EthAddress signer = head.getSigner(BigInteger.valueOf(config.ChainID));
Context.require(signer.equals(head.getCoinbase()), "Coinbase mismatch");
}

private void verify(ChainConfig config, Header head, Header parent, Snapshot snap) {
Expand Down
32 changes: 28 additions & 4 deletions bmv/bsc/src/main/java/foundation/icon/btp/bmv/bsc/ChainConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,37 @@
import score.Context;

import java.math.BigInteger;
import java.util.Arrays;

import static foundation.icon.btp.bmv.bsc.Header.*;

public class ChainConfig {
public final long ChainID;
public final long Epoch;
public final long Period;
private final long RamanujanBlock;
private final long PlanckBlock;
private final long LubanBlock;

private ChainConfig(long chainId, long epoch, long period, long ramanujanBlock, long planckBlock) {
private ChainConfig(long chainId, long epoch, long period, long ramanujanBlock, long planckBlock, long lubanBlock) {
this.ChainID = chainId;
this.Epoch = epoch;
this.Period = period;
this.RamanujanBlock = ramanujanBlock;
this.PlanckBlock = planckBlock;
this.LubanBlock = lubanBlock;
}

public static ChainConfig fromChainID(BigInteger cid) {
if (cid.longValue() == 56L) {
// BSC Mainnet
return new ChainConfig(56L, 200L, 3L, 0L, 27281024L);
return new ChainConfig(56L, 200L, 3L, 0L, 27281024L, -1L);
} else if (cid.longValue() == 97L) {
// BSC Testnet
return new ChainConfig(97L, 200L, 3L, 1010000L, 28196022L);
return new ChainConfig(97L, 200L, 3L, 1010000L, 28196022L, 29295050L);
} else if (cid.longValue() == 99L) {
// Private BSC Testnet
return new ChainConfig(99L, 200L, 3L, 0L, 0L);
return new ChainConfig(99L, 200L, 3L, 0L, 0L, 6L);
}

Context.require(false, "No Chain Config - ChainID(" + cid.intValue() + ")");
Expand All @@ -61,4 +66,23 @@ public boolean isRamanujan(BigInteger number) {
public boolean isPlanck(BigInteger number) {
return this.PlanckBlock <= number.longValue();
}

public boolean isLuban(BigInteger number) {
return this.LubanBlock >= 0 && this.LubanBlock <= number.longValue();
}

public byte[] getValidatorBytesFromHeader(Header head) {
Context.require(isLuban(head.getNumber()), "It doesn't luban block");
if (!isEpoch(head.getNumber())) {
return new byte[0];
}
byte[] extra = head.getExtra();
int num = extra[EXTRA_VANITY];
Context.require(num > 0 && extra.length > EXTRA_VANITY + EXTRA_SEAL + num * VALIDATOR_BYTES_LENGTH, "InvalidValidatorBytes");
int start = EXTRA_VANITY + VALIDATOR_NUMBER_SIZE;
int end = start + num * VALIDATOR_BYTES_LENGTH;
byte[] signersBytes = Arrays.copyOfRange(extra, start, end);
Context.require(num == signersBytes.length / VALIDATOR_BYTES_LENGTH, "Invalid validator number");
return signersBytes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package foundation.icon.btp.bmv.bsc;

import foundation.icon.score.util.StringUtil;
import score.Context;
import score.ObjectReader;
import score.ObjectWriter;
import scorex.util.ArrayList;
Expand Down
16 changes: 11 additions & 5 deletions bmv/bsc/src/main/java/foundation/icon/btp/bmv/bsc/Header.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
public class Header {
public static final int EXTRA_VANITY = 32;
public static final int EXTRA_SEAL = 65;
public static final int BLS_PUB_LENGTH = 48;
public static final int VALIDATOR_BYTES_LENGTH = EthAddress.ADDRESS_LEN + BLS_PUB_LENGTH;
public static final int VALIDATOR_NUMBER_SIZE = 1;
// pre-calculated constant uncle hash:) rlp([])
public static final Hash UNCLE_HASH = Hash.of("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347");
public static final BigInteger INTURN_DIFF = BigInteger.valueOf(2L);
Expand Down Expand Up @@ -137,15 +140,18 @@ public Hash getHash() {
return hashCache;
}

public List<EthAddress> getValidators() {
public List<EthAddress> getValidators(ChainConfig config) {
Context.require(extra.length > EXTRA_VANITY + EXTRA_SEAL, "No validators bytes");
byte[] signersBytes = Arrays.copyOfRange(extra, EXTRA_VANITY, extra.length - EXTRA_SEAL);
int n = signersBytes.length / EthAddress.ADDRESS_LEN;
Context.require(config.isEpoch(this.number), "Validators does not exist, if it is not epoch");
int num = extra[EXTRA_VANITY];
Context.require(num > 0 && extra.length > EXTRA_VANITY + EXTRA_SEAL + num * VALIDATOR_BYTES_LENGTH);
int start = EXTRA_VANITY + VALIDATOR_NUMBER_SIZE;
byte[] signersBytes = Arrays.copyOfRange(extra, start, start + num * VALIDATOR_BYTES_LENGTH);
int n = signersBytes.length / VALIDATOR_BYTES_LENGTH;
List<EthAddress> vals = new ArrayList<>();
for (int i = 0; i < n; i++) {
vals.add(new EthAddress(Arrays.copyOfRange(signersBytes, i * EthAddress.ADDRESS_LEN, (i+1) * EthAddress.ADDRESS_LEN)));
vals.add(new EthAddress(Arrays.copyOfRange(signersBytes, i* VALIDATOR_BYTES_LENGTH, i* VALIDATOR_BYTES_LENGTH +EthAddress.ADDRESS_LEN)));
}
EthAddresses.sort(vals);
return vals;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public Snapshot apply(ChainConfig config, Header head) {
: validators;

newCandidates = newNumber.mod(epoch).equals(BigInteger.ZERO)
? new EthAddresses(head.getValidators())
? new EthAddresses(head.getValidators(config))
: candidates;

newRecents.add(head.getCoinbase());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public static void handleRelayMessageTest(DataSource.Case c, Score bmv, String p

public static class MainNetBMVTest {
private static final DataSource data = DataSource.loadDataSource("mainnet.json");
@TestFactory
// @TestFactory
public Collection<DynamicTest> handleRelayMessageTests() {
DataSource.ConstructorParams params = data.getParams();
List<DynamicTest> t = new ArrayList<>();
Expand All @@ -71,7 +71,7 @@ public static class OneValidatorBMVTest {
private static final DataSource data = DataSource.loadDataSource("privnet.json");;
private static final BTPAddress PREV_BMC = BTPAddress.parse("btp://0x1.eth/0xD2F04942FF92709ED9d41988D161710D18d7f1FE");

@TestFactory
// @TestFactory
public Collection<DynamicTest> handleRelayMessageTests() {
DataSource.ConstructorParams params = data.getParams();
List<DynamicTest> t = new ArrayList<>();
Expand Down
62 changes: 62 additions & 0 deletions bmv/bsc2/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
version = '0.1.0'

dependencies {
compileOnly("foundation.icon:javaee-api:$javaeeVersion")
implementation("foundation.icon:javaee-scorex:$scorexVersion")
implementation project(':lib')

testImplementation("org.junit.jupiter:junit-jupiter-api:$jupiterVersion")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$jupiterVersion")

testImplementation 'org.bouncycastle:bcprov-jdk15on:1.70'
testImplementation("foundation.icon:javaee-unittest:$javaeeUnittestVersion")
testImplementation project(':test-lib')

testImplementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")
testImplementation 'org.projectlombok:lombok:1.18.22'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.22'
}

optimizedJar {
dependsOn(project(':lib').jar)
mainClassName = 'foundation.icon.btp.bmv.bsc2.BTPMessageVerifier'
archivesBaseName = 'bmv-bsc2'
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
} { exclude "score/*" }
enableDebug = debugJar
}

deployJar {
endpoints {
local {
uri = scoreTest.url
nid = scoreTest.parseNid(scoreTest.nid)
}
}
keystore = scoreTest.default.keyStore
password = scoreTest.default.resolvedKeyPassword
parameters {[
arg('chainId', '0x'),
arg('epoch', '200'),
arg('header', '0x'),
arg('recents', '0x'),
arg('validators', '0x')
]}
}

test {
useJUnitPlatform {
if (!integrationTest) {
excludeTags("integration")
} else {
// use the common config files
systemProperty('env.props', new File('src/test/resources/env.props'))

def prefix = 'score.path.'
systemProperty(prefix + 'bmv-' + project.name, optimizedJar.outputJarName)
dependsOn optimizedJar
systemProperty prefix + 'bmc-mock.scoreFilePath', tasks.getByPath(":test-lib:optimizedJarMockBMC").outputJarName
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package foundation.icon.btp.bmv.bsc2;

import score.Context;
import score.ObjectReader;
import score.ObjectWriter;

public class BLSPublicKey {

public static final int LENGTH = 48;
private final byte[] data;

public BLSPublicKey(byte[] data) {
Context.require(data.length == LENGTH, "Invalid bls public key");
this.data = copy(data);
}

public static BLSPublicKey readObject(ObjectReader r) {
return new BLSPublicKey(r.readByteArray());
}

public static void writeObject(ObjectWriter w, BLSPublicKey o) {
w.write(o.data);
}

public byte[] toBytes() {
return copy(this.data);
}

private static byte[] copy(byte[] src) {
byte[] dst = new byte[LENGTH];
int i = -1;
while (++i < LENGTH) {
dst[i] = src[i];
}
return dst;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2023 ICON Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package foundation.icon.btp.bmv.bsc2;

import foundation.icon.btp.lib.BTPException;

public class BMVException extends BTPException.BMV {

public BMVException(Code c) {
super(c, c.name());
}

public BMVException(Code c, String message) {
super(c, message);
}

public static BMVException unknown(String message) {
return new BMVException(Code.Unknown, message);
}

public static BMVException notVerifiable(String message) {
return new BMVException(Code.NotVerifiable, message);
}

public static BMVException alreadyVerified(String message) {
return new BMVException(Code.AlreadyVerified, message);
}

public static BMVException invalidBlockWitnessOld(String message) {
return new BMVException(Code.InvalidBlockWitnessOld, message);
}

//BTPException.BMV => 25 ~ 39
public enum Code implements Coded{
Unknown(0),
NotVerifiable(1),
AlreadyVerified(2),
InvalidBlockWitnessOld(3);

final int code;
Code(int code){ this.code = code; }

@Override
public int code() { return code; }

static public Code of(int code) {
for(Code c : values()) {
if (c.code == code) {
return c;
}
}
throw new IllegalArgumentException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2023 ICON Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package foundation.icon.btp.bmv.bsc2;

import score.ByteArrayObjectWriter;
import score.Context;

public class BMVStatusExtra {
// mta offset
private long offset;
private BlockTree tree;

public BMVStatusExtra(long offset, BlockTree tree) {
this.offset = offset;
this.tree = tree;
}

public byte[] toBytes() {
ByteArrayObjectWriter w = Context.newByteArrayObjectWriter("RLP");
w.beginList(2);
w.write(offset);
w.write(tree);
w.end();
return w.toByteArray();
}
}
Loading