diff --git a/rskj-core/build.gradle b/rskj-core/build.gradle index 93699114c2f..e6d4856093b 100644 --- a/rskj-core/build.gradle +++ b/rskj-core/build.gradle @@ -60,21 +60,20 @@ tasks.withType(AbstractArchiveTask) { ext { slf4jVersion = '1.7.25' leveldbVersion = '0.9' - scastleVersion = '1.52.0.0' springVersion = '4.2.9.RELEASE' junitVersion = '4.12' mockitoVersion = '1.10.19' powermockitoVersion = '1.6.4' rskLllVersion = '0.0.2' logbackVersion = '1.2.2' - bitcoinjVersion = '0.14.4-rsk-4' + bitcoinjVersion = '0.14.4-rsk-5' nettyVersion = '4.0.56.Final' } dependencies { + compile "com.madgag.spongycastle:core:1.52.0.0" compile "io.netty:netty-codec-http:$nettyVersion" - compile "com.madgag.spongycastle:core:${scastleVersion}" // for SHA3 and SECP256K1 - compile "com.madgag.spongycastle:prov:${scastleVersion}" // for SHA3 and SECP256K1 + compile "org.bouncycastle:bclcrypto-jdk15on:1.59" compile "org.iq80.leveldb:leveldb:${leveldbVersion}" compile "org.fusesource.leveldbjni:leveldbjni:1.8" compile "org.ethereum:leveldbjni-all:1.18.3" @@ -117,7 +116,7 @@ dependencyVerification { 'aopalliance:aopalliance:0addec670fedcd3f113c5c8091d783280d23f75e3acb841b61a9cdb079376a08', 'ch.qos.logback:logback-classic:48ade385bbae0222b2934b65738892117d8cb8366b3a3df442d3826c11cedff1', 'ch.qos.logback:logback-core:280be7a9327e7434d214d6b9eb881c083c3e057a22d0ed7663a7ce81a718a494', - 'co.rsk.bitcoinj:bitcoinj-thin:4535d56e40a2cc67dde3b807a2dbb3e6763b996ee0d4184c4d87aa141edf7c19', + 'co.rsk.bitcoinj:bitcoinj-thin:dd2cc70c2b37c2d76467bbc2bc69c926df388181ccb9aa333ec2b6b433ea1490', 'co.rsk:lll-compiler:a645fdb272f56721761f65dd32caa952453efc07d98d292259d99353b6f647d0', 'com.fasterxml.jackson.core:jackson-annotations:4caf3936315439b509b8c3ef494d4e47eaa6d25c3b5299aadb0eafb3944ed32f', 'com.fasterxml.jackson.core:jackson-core:256ff34118ab292d1b4f3ee4d2c3e5e5f0f609d8e07c57e8ad1f51c46d4fbb46', @@ -130,7 +129,6 @@ dependencyVerification { 'com.h2database:h2:b1cf34c64871014aa73580281cc464dfa72450d8860cc0752fc175e87edd6544', 'com.lambdaworks:scrypt:9a82d218099fb14c10c0e86e7eefeebd8c104de920acdc47b8b4b7a686fb73b4', 'com.madgag.spongycastle:core:07a401edbe26e1028e2324754557b741cc57306008df7b71a9e12ec32d65be8f', - 'com.madgag.spongycastle:prov:becbb70797b0103517693d2a97ce93174cc4d1f732897ed965a24e32dd99503e', 'com.squareup.okhttp:okhttp:b4c943138fcef2bcc9d2006b2250c4aabbedeafc5947ed7c0af7fd103ceb2707', 'com.squareup.okio:okio:114bdc1f47338a68bcbc95abf2f5cdc72beeec91812f2fcd7b521c1937876266', 'com.typesafe:config:b5f1d6071f1548d05be82f59f9039c7d37a1787bd8e3c677e31ee275af4a4621', @@ -147,6 +145,7 @@ dependencyVerification { 'org.apache.commons:commons-collections4:b1fe8b5968b57d8465425357ed2d9dc695504518bed2df5b565c4b8e68c1c8a5', 'org.apache.commons:commons-lang3:8ac96fc686512d777fca85e144f196cd7cfe0c0aec23127229497d1a38ff651c', 'org.awaitility:awaitility:a02982e89585a52c1c84296a895bfeb86ea250cca1a53bcfc8a14092fffa87c4', + 'org.bouncycastle:bclcrypto-jdk15on:7d03ba37df4d0ddc4ea40d56554324c6f18062a930edadb0a1b3acbbbea28efc', 'org.ethereum:leveldbjni-all:18da00444c77080d4422b16c9d4750c4addabda350b702b4a6d628b86658e585', 'org.fusesource.hawtjni:hawtjni-runtime:74fe9764e1fb1ef20b159dbca2d29abd6de292082ce3fcf538f81ac912390416', 'org.fusesource.leveldbjni:leveldbjni:05fe3a006d030aaf8d1e43f6c640a85f9f6b967c4499ce1ad5055ac236c3b944', diff --git a/rskj-core/src/main/java/co/rsk/DoPrune.java b/rskj-core/src/main/java/co/rsk/DoPrune.java index de7cf712bad..f4eb94d83ea 100644 --- a/rskj-core/src/main/java/co/rsk/DoPrune.java +++ b/rskj-core/src/main/java/co/rsk/DoPrune.java @@ -35,7 +35,7 @@ import org.ethereum.vm.PrecompiledContracts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; diff --git a/rskj-core/src/main/java/co/rsk/GenNodeKeyId.java b/rskj-core/src/main/java/co/rsk/GenNodeKeyId.java index a0e4983b12e..9ec4f7569b8 100644 --- a/rskj-core/src/main/java/co/rsk/GenNodeKeyId.java +++ b/rskj-core/src/main/java/co/rsk/GenNodeKeyId.java @@ -21,7 +21,7 @@ import org.ethereum.crypto.ECKey; import org.ethereum.crypto.HashUtil; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.nio.charset.StandardCharsets; diff --git a/rskj-core/src/main/java/co/rsk/blocks/FileBlockPlayer.java b/rskj-core/src/main/java/co/rsk/blocks/FileBlockPlayer.java index 8258182fd69..72841a26e7b 100644 --- a/rskj-core/src/main/java/co/rsk/blocks/FileBlockPlayer.java +++ b/rskj-core/src/main/java/co/rsk/blocks/FileBlockPlayer.java @@ -22,7 +22,7 @@ import org.ethereum.core.Block; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.io.BufferedReader; import java.io.FileReader; diff --git a/rskj-core/src/main/java/co/rsk/blocks/FileBlockRecorder.java b/rskj-core/src/main/java/co/rsk/blocks/FileBlockRecorder.java index 11cfe17e5ba..65bd6d8dd54 100644 --- a/rskj-core/src/main/java/co/rsk/blocks/FileBlockRecorder.java +++ b/rskj-core/src/main/java/co/rsk/blocks/FileBlockRecorder.java @@ -21,7 +21,7 @@ import org.ethereum.core.Block; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.io.BufferedWriter; import java.io.FileWriter; diff --git a/rskj-core/src/main/java/co/rsk/config/BridgeDevNetConstants.java b/rskj-core/src/main/java/co/rsk/config/BridgeDevNetConstants.java index 61b93e16b39..b57bfa425a5 100644 --- a/rskj-core/src/main/java/co/rsk/config/BridgeDevNetConstants.java +++ b/rskj-core/src/main/java/co/rsk/config/BridgeDevNetConstants.java @@ -25,7 +25,7 @@ import co.rsk.peg.Federation; import com.google.common.collect.Lists; import org.ethereum.crypto.ECKey; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.time.Instant; import java.util.Arrays; diff --git a/rskj-core/src/main/java/co/rsk/config/BridgeMainNetConstants.java b/rskj-core/src/main/java/co/rsk/config/BridgeMainNetConstants.java index c5221dcb09b..3087fd648dc 100644 --- a/rskj-core/src/main/java/co/rsk/config/BridgeMainNetConstants.java +++ b/rskj-core/src/main/java/co/rsk/config/BridgeMainNetConstants.java @@ -7,7 +7,7 @@ import co.rsk.peg.Federation; import com.google.common.collect.Lists; import org.ethereum.crypto.ECKey; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.time.Instant; import java.util.Arrays; diff --git a/rskj-core/src/main/java/co/rsk/config/BridgeRegTestConstants.java b/rskj-core/src/main/java/co/rsk/config/BridgeRegTestConstants.java index c4d571e7a57..3f71597a3e9 100644 --- a/rskj-core/src/main/java/co/rsk/config/BridgeRegTestConstants.java +++ b/rskj-core/src/main/java/co/rsk/config/BridgeRegTestConstants.java @@ -26,7 +26,7 @@ import com.google.common.collect.Lists; import org.ethereum.crypto.ECKey; import org.ethereum.crypto.HashUtil; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.nio.charset.StandardCharsets; import java.time.Instant; diff --git a/rskj-core/src/main/java/co/rsk/config/BridgeTestNetConstants.java b/rskj-core/src/main/java/co/rsk/config/BridgeTestNetConstants.java index 6e27803010a..d935be31890 100644 --- a/rskj-core/src/main/java/co/rsk/config/BridgeTestNetConstants.java +++ b/rskj-core/src/main/java/co/rsk/config/BridgeTestNetConstants.java @@ -25,7 +25,7 @@ import co.rsk.peg.Federation; import com.google.common.collect.Lists; import org.ethereum.crypto.ECKey; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.time.Instant; import java.util.Arrays; diff --git a/rskj-core/src/main/java/co/rsk/config/RemascConfig.java b/rskj-core/src/main/java/co/rsk/config/RemascConfig.java index 46c050ae126..b6d1622c09b 100644 --- a/rskj-core/src/main/java/co/rsk/config/RemascConfig.java +++ b/rskj-core/src/main/java/co/rsk/config/RemascConfig.java @@ -86,4 +86,5 @@ public long getPunishmentDivisor() { public long getPublishersDivisor() { return publishersDivisor; } + } diff --git a/rskj-core/src/main/java/co/rsk/config/RskSystemProperties.java b/rskj-core/src/main/java/co/rsk/config/RskSystemProperties.java index 08162bcdace..e86f1a810b1 100644 --- a/rskj-core/src/main/java/co/rsk/config/RskSystemProperties.java +++ b/rskj-core/src/main/java/co/rsk/config/RskSystemProperties.java @@ -84,7 +84,8 @@ public RskSystemProperties(ConfigLoader loader) { @Nullable public RskAddress coinbaseAddress() { if (!isMinerServerEnabled()) { - return RskAddress.nullAddress(); + //todo(diegoll): we should carefully handle the case when you don't have a coinbase and want to execute pending blocks + return new RskAddress(new byte[20]); } // validity checks are performed by localCoinbaseAccount diff --git a/rskj-core/src/main/java/co/rsk/core/BlockDifficulty.java b/rskj-core/src/main/java/co/rsk/core/BlockDifficulty.java index 5dff3634885..0601f164226 100644 --- a/rskj-core/src/main/java/co/rsk/core/BlockDifficulty.java +++ b/rskj-core/src/main/java/co/rsk/core/BlockDifficulty.java @@ -30,14 +30,6 @@ public class BlockDifficulty implements Comparable, Serializabl private final BigInteger value; - /** - * @param bytes the difficulty bytes, as expected by {@link java.math.BigInteger#BigInteger(byte[])}. - * Since we previously converted an empty array to ZERO, we'll do that here too. - */ - public BlockDifficulty(byte[] bytes) { - this(bytes.length == 0 ? BigInteger.ZERO : new BigInteger(bytes)); - } - /** * @param value the difficulty value, which should be positive. */ diff --git a/rskj-core/src/main/java/co/rsk/core/NetworkStateExporter.java b/rskj-core/src/main/java/co/rsk/core/NetworkStateExporter.java index cd43b050a2a..98e30e81f5f 100644 --- a/rskj-core/src/main/java/co/rsk/core/NetworkStateExporter.java +++ b/rskj-core/src/main/java/co/rsk/core/NetworkStateExporter.java @@ -30,7 +30,7 @@ import org.ethereum.vm.PrecompiledContracts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.io.BufferedWriter; import java.io.File; diff --git a/rskj-core/src/main/java/co/rsk/core/ReversibleTransactionExecutor.java b/rskj-core/src/main/java/co/rsk/core/ReversibleTransactionExecutor.java index 25e18caa386..89b60b1635a 100644 --- a/rskj-core/src/main/java/co/rsk/core/ReversibleTransactionExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/ReversibleTransactionExecutor.java @@ -66,10 +66,10 @@ public ProgramResult executeTransaction( byte[] toAddress, byte[] value, byte[] data, - byte[] fromAddress) { + RskAddress fromAddress) { Repository repository = track.getSnapshotTo(executionBlock.getStateRoot()).startTracking(); - byte[] nonce = repository.getNonce(new RskAddress(fromAddress)).toByteArray(); + byte[] nonce = repository.getNonce(fromAddress).toByteArray(); UnsignedTransaction tx = new UnsignedTransaction( nonce, gasPrice, @@ -108,9 +108,9 @@ private UnsignedTransaction( byte[] receiveAddress, byte[] value, byte[] data, - byte[] fromAddress) { + RskAddress fromAddress) { super(nonce, gasPrice, gasLimit, receiveAddress, value, data); - this.sender = new RskAddress(fromAddress); + this.sender = fromAddress; } @Override diff --git a/rskj-core/src/main/java/co/rsk/core/RskAddress.java b/rskj-core/src/main/java/co/rsk/core/RskAddress.java index 9ff4bbe8fd7..802bff3392a 100644 --- a/rskj-core/src/main/java/co/rsk/core/RskAddress.java +++ b/rskj-core/src/main/java/co/rsk/core/RskAddress.java @@ -21,7 +21,7 @@ import com.google.common.primitives.UnsignedBytes; import org.ethereum.rpc.TypeConverter; import org.ethereum.vm.DataWord; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.util.Arrays; import java.util.Comparator; @@ -39,7 +39,7 @@ public class RskAddress { */ private static final int LENGTH_IN_BYTES = 20; - private static final RskAddress NULL_ADDRESS = new RskAddress(new byte[LENGTH_IN_BYTES]); + private static final RskAddress NULL_ADDRESS = new RskAddress(); /** * This compares using the lexicographical order of the sender unsigned bytes. @@ -75,6 +75,13 @@ public RskAddress(byte[] bytes) { this.bytes = bytes; } + /** + * This instantiates the contract creation address. + */ + private RskAddress() { + this.bytes = new byte[0]; + } + /** * @return the null address, which is the receiver of contract creation transactions. */ diff --git a/rskj-core/src/main/java/co/rsk/core/Wallet.java b/rskj-core/src/main/java/co/rsk/core/Wallet.java index 019bb91f9f4..e3daf9811ee 100644 --- a/rskj-core/src/main/java/co/rsk/core/Wallet.java +++ b/rskj-core/src/main/java/co/rsk/core/Wallet.java @@ -26,7 +26,7 @@ import org.ethereum.crypto.Keccak256Helper; import org.ethereum.datasource.KeyValueDataSource; import org.ethereum.rpc.TypeConverter; -import org.spongycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.KeyParameter; import javax.annotation.concurrent.GuardedBy; import java.io.*; diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 4cd74949a60..ce7c074463f 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -28,7 +28,7 @@ import org.ethereum.vm.program.invoke.ProgramInvokeFactoryImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.util.ArrayList; import java.util.Arrays; diff --git a/rskj-core/src/main/java/co/rsk/core/bc/TransactionPoolImpl.java b/rskj-core/src/main/java/co/rsk/core/bc/TransactionPoolImpl.java index 711d6b38525..2ebf3564333 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/TransactionPoolImpl.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/TransactionPoolImpl.java @@ -399,7 +399,7 @@ private Block createFakePendingBlock(Block best) { // creating fake lightweight calculated block with no hashes calculations return new Block(best.getHash().getBytes(), emptyUncleHashList, // uncleHash - RskAddress.nullAddress().getBytes(), //coinbase + new byte[20], //coinbase new byte[32], // log bloom - from tx receipts best.getDifficulty().getBytes(), // difficulty best.getNumber() + 1, //number diff --git a/rskj-core/src/main/java/co/rsk/crypto/Keccak256.java b/rskj-core/src/main/java/co/rsk/crypto/Keccak256.java index 46c8a0cce20..7e194d5e4e8 100644 --- a/rskj-core/src/main/java/co/rsk/crypto/Keccak256.java +++ b/rskj-core/src/main/java/co/rsk/crypto/Keccak256.java @@ -22,7 +22,7 @@ import com.google.common.primitives.Ints; import org.ethereum.rpc.TypeConverter; import org.ethereum.util.ByteUtil; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.io.Serializable; import java.util.Arrays; diff --git a/rskj-core/src/main/java/co/rsk/crypto/KeyCrypter.java b/rskj-core/src/main/java/co/rsk/crypto/KeyCrypter.java index 508e5e41d5b..89566074382 100644 --- a/rskj-core/src/main/java/co/rsk/crypto/KeyCrypter.java +++ b/rskj-core/src/main/java/co/rsk/crypto/KeyCrypter.java @@ -36,7 +36,7 @@ package co.rsk.crypto; -import org.spongycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.KeyParameter; import java.io.Serializable; diff --git a/rskj-core/src/main/java/co/rsk/crypto/KeyCrypterAes.java b/rskj-core/src/main/java/co/rsk/crypto/KeyCrypterAes.java index a7fc30a0cf4..a429396afa2 100644 --- a/rskj-core/src/main/java/co/rsk/crypto/KeyCrypterAes.java +++ b/rskj-core/src/main/java/co/rsk/crypto/KeyCrypterAes.java @@ -36,12 +36,12 @@ package co.rsk.crypto; -import org.spongycastle.crypto.BufferedBlockCipher; -import org.spongycastle.crypto.engines.AESFastEngine; -import org.spongycastle.crypto.modes.CBCBlockCipher; -import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher; -import org.spongycastle.crypto.params.KeyParameter; -import org.spongycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; import java.security.SecureRandom; import java.util.Arrays; @@ -77,7 +77,7 @@ public EncryptedData encrypt(byte[] plainBytes, KeyParameter key) { ParametersWithIV keyWithIv = new ParametersWithIV(key, iv); // Encrypt using AES. - BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine())); + BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine())); cipher.init(true, keyWithIv); byte[] encryptedBytes = new byte[cipher.getOutputSize(plainBytes.length)]; final int length1 = cipher.processBytes(plainBytes, 0, plainBytes.length, encryptedBytes, 0); @@ -106,7 +106,7 @@ public byte[] decrypt(EncryptedData dataToDecrypt, KeyParameter key) { ParametersWithIV keyWithIv = new ParametersWithIV(new KeyParameter(key.getKey()), dataToDecrypt.initialisationVector); // Decrypt the message. - BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine())); + BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine())); cipher.init(false, keyWithIv); byte[] cipherBytes = dataToDecrypt.encryptedBytes; diff --git a/rskj-core/src/main/java/co/rsk/db/ContractDetailsImpl.java b/rskj-core/src/main/java/co/rsk/db/ContractDetailsImpl.java index 3170349a106..e0e3a09f241 100644 --- a/rskj-core/src/main/java/co/rsk/db/ContractDetailsImpl.java +++ b/rskj-core/src/main/java/co/rsk/db/ContractDetailsImpl.java @@ -34,8 +34,8 @@ import org.ethereum.vm.DataWord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.pqc.math.linearalgebra.ByteUtils; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; +import org.bouncycastle.util.encoders.Hex; import javax.annotation.Nullable; import java.util.*; diff --git a/rskj-core/src/main/java/co/rsk/db/RemascCache.java b/rskj-core/src/main/java/co/rsk/db/RemascCache.java new file mode 100644 index 00000000000..bf8a9f0ec82 --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/db/RemascCache.java @@ -0,0 +1,12 @@ +package co.rsk.db; + +import co.rsk.crypto.Keccak256; +import co.rsk.remasc.Sibling; + +import java.util.List; +import java.util.Map; + +public interface RemascCache { + + Map> getSiblingsFromBlockByHash(Keccak256 hash); +} diff --git a/rskj-core/src/main/java/co/rsk/db/RepositoryImpl.java b/rskj-core/src/main/java/co/rsk/db/RepositoryImpl.java index 8707590188b..9e522a5bd48 100644 --- a/rskj-core/src/main/java/co/rsk/db/RepositoryImpl.java +++ b/rskj-core/src/main/java/co/rsk/db/RepositoryImpl.java @@ -36,7 +36,7 @@ import org.ethereum.vm.DataWord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import javax.annotation.Nonnull; import java.math.BigInteger; diff --git a/rskj-core/src/main/java/co/rsk/mine/GenesisMerkleProofBuilder.java b/rskj-core/src/main/java/co/rsk/mine/GenesisMerkleProofBuilder.java new file mode 100644 index 00000000000..00f37f43e4a --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/mine/GenesisMerkleProofBuilder.java @@ -0,0 +1,103 @@ +/* + * This file is part of RskJ + * Copyright (C) 2018 RSK Labs Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ +package co.rsk.mine; + +import co.rsk.bitcoinj.core.*; +import org.bouncycastle.util.encoders.Hex; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Builds Merkle proofs with the format used since Genesis until RSKIP 92 activation + */ +public class GenesisMerkleProofBuilder implements MerkleProofBuilder { + + @Override + public byte[] buildFromMerkleHashes( + BtcBlock blockWithHeaderOnly, + List merkleHashesString, + int blockTxnCount) { + List merkleHashes = merkleHashesString.stream() + .map(mk -> Sha256Hash.wrapReversed(Hex.decode(mk))) + .collect(Collectors.toList()); + int merkleTreeHeight = (int) Math.ceil(Math.log(blockTxnCount) / Math.log(2)); + + // bitlist will always have ones at the beginning because merkle branch is built for coinbase tx + List bitList = new ArrayList<>(); + for (int i = 0; i < merkleHashes.size() + merkleTreeHeight; i++) { + bitList.add(i < merkleHashes.size()); + } + + // bits indicates which nodes are going to be used for building the partial merkle tree + // for more information please refer to {@link co.rsk.bitcoinj.core.PartialMerkleTree#buildFromLeaves } method + byte[] bits = new byte[(bitList.size() + 7) / 8]; + for (int i = 0; i < bitList.size(); i++) { + if (bitList.get(i)) { + Utils.setBitLE(bits, i); + } + } + + PartialMerkleTree bitcoinMergedMiningMerkleBranch = new PartialMerkleTree(blockWithHeaderOnly.getParams(), bits, merkleHashes, blockTxnCount); + + return bitcoinMergedMiningMerkleBranch.bitcoinSerialize(); + } + + @Override + public byte[] buildFromTxHashes( + BtcBlock blockWithHeaderOnly, + List txHashesString) { + List txHashes = txHashesString.stream().map(Sha256Hash::wrap).collect(Collectors.toList()); + + PartialMerkleTree bitcoinMergedMiningMerkleBranch = getBitcoinMergedMerkleBranch(txHashes, blockWithHeaderOnly.getParams()); + + return bitcoinMergedMiningMerkleBranch.bitcoinSerialize(); + } + + @Override + public byte[] buildFromBlock(BtcBlock bitcoinMergedMiningBlock) { + List txHashes = bitcoinMergedMiningBlock.getTransactions().stream() + .map(BtcTransaction::getHash) + .collect(Collectors.toList()); + + PartialMerkleTree bitcoinMergedMiningMerkleBranch = getBitcoinMergedMerkleBranch(txHashes, bitcoinMergedMiningBlock.getParams()); + + return bitcoinMergedMiningMerkleBranch.bitcoinSerialize(); + } + + /** + * getBitcoinMergedMerkleBranch returns the Partial Merkle Branch needed to validate that the coinbase tx + * is part of the Merkle Tree. + * + * @param txHashes the bitcoin txs that were included in a block. + * @return A Partial Merkle Branch in which you can validate the coinbase tx. + */ + private static PartialMerkleTree getBitcoinMergedMerkleBranch(List txHashes, NetworkParameters params) { + /* + We need to convert the txs to a bitvector to choose which ones + will be included in the Partial Merkle Tree. + + We need txs.size() / 8 bytes to represent this vector. + The coinbase tx is the first one of the txs so we set the first bit to 1. + */ + byte[] bitvector = new byte[(txHashes.size() + 7) / 8]; + Utils.setBitLE(bitvector, 0); + return PartialMerkleTree.buildFromLeaves(params, bitvector, txHashes); + } +} diff --git a/rskj-core/src/main/java/co/rsk/mine/MerkleProofBuilder.java b/rskj-core/src/main/java/co/rsk/mine/MerkleProofBuilder.java new file mode 100644 index 00000000000..49e98e14ac3 --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/mine/MerkleProofBuilder.java @@ -0,0 +1,39 @@ +/* + * This file is part of RskJ + * Copyright (C) 2018 RSK Labs Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ +package co.rsk.mine; + +import co.rsk.bitcoinj.core.BtcBlock; + +import java.util.List; + +/** + * Builds Merkle proofs for inclusion in the merged-mining block header + */ +public interface MerkleProofBuilder { + + byte[] buildFromMerkleHashes( + BtcBlock blockWithHeaderOnly, + List merkleHashesString, + int blockTxnCount); + + byte[] buildFromTxHashes( + BtcBlock blockWithHeaderOnly, + List txHashesString); + + byte[] buildFromBlock(BtcBlock bitcoinMergedMiningBlock); +} diff --git a/rskj-core/src/main/java/co/rsk/mine/MinerClientImpl.java b/rskj-core/src/main/java/co/rsk/mine/MinerClientImpl.java index c44154112c2..b80ba2fcf1d 100644 --- a/rskj-core/src/main/java/co/rsk/mine/MinerClientImpl.java +++ b/rskj-core/src/main/java/co/rsk/mine/MinerClientImpl.java @@ -21,8 +21,8 @@ import co.rsk.config.RskSystemProperties; import co.rsk.core.Rsk; import co.rsk.panic.PanicProcessor; -import org.ethereum.config.blockchain.DevNetConfig; -import org.ethereum.config.blockchain.RegTestConfig; +import org.ethereum.config.net.DevNetConfig; +import org.ethereum.config.net.RegTestConfig; import org.ethereum.rpc.TypeConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/rskj-core/src/main/java/co/rsk/mine/MinerServerImpl.java b/rskj-core/src/main/java/co/rsk/mine/MinerServerImpl.java index ce8cbbf7f52..b38c8583e44 100644 --- a/rskj-core/src/main/java/co/rsk/mine/MinerServerImpl.java +++ b/rskj-core/src/main/java/co/rsk/mine/MinerServerImpl.java @@ -18,7 +18,8 @@ package co.rsk.mine; -import co.rsk.bitcoinj.core.*; +import co.rsk.bitcoinj.core.BtcBlock; +import co.rsk.bitcoinj.core.BtcTransaction; import co.rsk.config.MiningConfig; import co.rsk.config.RskMiningConstants; import co.rsk.config.RskSystemProperties; @@ -33,6 +34,9 @@ import co.rsk.validators.ProofOfWorkRule; import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang3.ArrayUtils; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.util.Arrays; +import org.ethereum.config.BlockchainNetConfig; import org.ethereum.core.*; import org.ethereum.crypto.ECKey; import org.ethereum.facade.Ethereum; @@ -41,9 +45,6 @@ import org.ethereum.util.RLP; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.crypto.digests.SHA256Digest; -import org.spongycastle.util.Arrays; -import org.spongycastle.util.encoders.Hex; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -57,7 +58,7 @@ import java.math.BigInteger; import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; +import java.util.function.Function; /** * The MinerServer provides support to components that perform the actual mining. @@ -79,6 +80,7 @@ public class MinerServerImpl implements MinerServer { private final Blockchain blockchain; private final ProofOfWorkRule powRule; private final BlockToMineBuilder builder; + private final BlockchainNetConfig blockchainConfig; private boolean isFallbackMining; private int fallbackBlocksGenerated; @@ -131,6 +133,7 @@ public MinerServerImpl( this.difficultyCalculator = difficultyCalculator; this.powRule = powRule; this.builder = builder; + this.blockchainConfig = config.getBlockchainConfig(); blocksWaitingforPoW = createNewBlocksWaitingList(); @@ -148,9 +151,9 @@ public MinerServerImpl( config.getAverageFallbackMiningTime(); // default if (secsBetweenFallbackMinedBlocks == 0) { - secsBetweenFallbackMinedBlocks = (config.getBlockchainConfig().getCommonConstants().getDurationLimit()); + secsBetweenFallbackMinedBlocks = (blockchainConfig.getCommonConstants().getDurationLimit()); } - autoSwitchBetweenNormalAndFallbackMining = !config.getBlockchainConfig().getCommonConstants().getFallbackMiningDifficulty().equals(BlockDifficulty.ZERO); + autoSwitchBetweenNormalAndFallbackMining = !blockchainConfig.getCommonConstants().getFallbackMiningDifficulty().equals(BlockDifficulty.ZERO); } // This method is used for tests @@ -386,9 +389,13 @@ public SubmitBlockResult submitBitcoinBlockPartialMerkle( int blockTxnCount) { logger.debug("Received merkle solution with hash {} for merged mining", blockHashForMergedMining); - PartialMerkleTree bitcoinMergedMiningMerkleBranch = getBitcoinMergedMerkleBranchForCoinbase(blockWithHeaderOnly.getParams(), merkleHashes, blockTxnCount); - - return processSolution(blockHashForMergedMining, blockWithHeaderOnly, coinbase, bitcoinMergedMiningMerkleBranch, true); + return processSolution( + blockHashForMergedMining, + blockWithHeaderOnly, + coinbase, + (pb) -> pb.buildFromMerkleHashes(blockWithHeaderOnly, merkleHashes, blockTxnCount), + true + ); } @Override @@ -399,9 +406,13 @@ public SubmitBlockResult submitBitcoinBlockTransactions( List txHashes) { logger.debug("Received tx solution with hash {} for merged mining", blockHashForMergedMining); - PartialMerkleTree bitcoinMergedMiningMerkleBranch = getBitcoinMergedMerkleBranch(blockWithHeaderOnly.getParams(), txHashes); - - return processSolution(blockHashForMergedMining, blockWithHeaderOnly, coinbase, bitcoinMergedMiningMerkleBranch, true); + return processSolution( + blockHashForMergedMining, + blockWithHeaderOnly, + coinbase, + (pb) -> pb.buildFromTxHashes(blockWithHeaderOnly, txHashes), + true + ); } @Override @@ -412,18 +423,20 @@ public SubmitBlockResult submitBitcoinBlock(String blockHashForMergedMining, Btc SubmitBlockResult submitBitcoinBlock(String blockHashForMergedMining, BtcBlock bitcoinMergedMiningBlock, boolean lastTag) { logger.debug("Received block with hash {} for merged mining", blockHashForMergedMining); - //noinspection ConstantConditions - BtcTransaction coinbase = bitcoinMergedMiningBlock.getTransactions().get(0); - PartialMerkleTree bitcoinMergedMiningMerkleBranch = getBitcoinMergedMerkleBranch(bitcoinMergedMiningBlock); - - return processSolution(blockHashForMergedMining, bitcoinMergedMiningBlock, coinbase, bitcoinMergedMiningMerkleBranch, lastTag); + return processSolution( + blockHashForMergedMining, + bitcoinMergedMiningBlock, + bitcoinMergedMiningBlock.getTransactions().get(0), + (pb) -> pb.buildFromBlock(bitcoinMergedMiningBlock), + lastTag + ); } private SubmitBlockResult processSolution( String blockHashForMergedMining, BtcBlock blockWithHeaderOnly, BtcTransaction coinbase, - PartialMerkleTree bitcoinMergedMiningMerkleBranch, + Function proofBuilderFunction, boolean lastTag) { Block newBlock; Keccak256 key = new Keccak256(TypeConverter.removeZeroX(blockHashForMergedMining)); @@ -448,7 +461,7 @@ private SubmitBlockResult processSolution( newBlock.setBitcoinMergedMiningHeader(blockWithHeaderOnly.cloneAsHeader().bitcoinSerialize()); newBlock.setBitcoinMergedMiningCoinbaseTransaction(compressCoinbase(coinbase.bitcoinSerialize(), lastTag)); - newBlock.setBitcoinMergedMiningMerkleProof(bitcoinMergedMiningMerkleBranch.bitcoinSerialize()); + newBlock.setBitcoinMergedMiningMerkleProof(MinerUtils.buildMerkleProof(blockchainConfig, proofBuilderFunction, newBlock.getNumber())); newBlock.seal(); if (!isValid(newBlock)) { @@ -506,84 +519,6 @@ public static byte[] compressCoinbase(byte[] bitcoinMergedMiningCoinbaseTransact return Arrays.concatenate(trimmedHashedContent, unHashedContent); } - /** - * getBitcoinMergedMerkleBranch returns the Partial Merkle Branch needed to validate that the coinbase tx - * is part of the Merkle Tree. - * - * @param networkParams bitcoin network params. - * @param merkleStringHashes hashes for the partial merkle tree of the bitcoin block used for merged mining. - * @param blockTxnCount number of transactions in the block. - * @return A Partial Merkle Branch in which you can validate the coinbase tx. - */ - private PartialMerkleTree getBitcoinMergedMerkleBranchForCoinbase( - NetworkParameters networkParams, - List merkleStringHashes, - int blockTxnCount) { - List merkleHashes = merkleStringHashes.stream().map(mk -> Sha256Hash.wrapReversed(Hex.decode(mk))).collect(Collectors.toList()); - int merkleTreeHeight = (int) Math.ceil(Math.log(blockTxnCount) / Math.log(2)); - - // bitlist will always have ones at the beginning because merkle branch is built for coinbase tx - List bitList = new ArrayList<>(); - for (int i = 0; i < merkleHashes.size() + merkleTreeHeight; i++) { - bitList.add(i < merkleHashes.size()); - } - - // bits indicates which nodes are going to be used for building the partial merkle tree - // for more information please refer to {@link co.rsk.bitcoinj.core.PartialMerkleTree#buildFromLeaves } method - byte[] bits = new byte[(bitList.size() + 7) / 8]; - for (int i = 0; i < bitList.size(); i++) { - if (bitList.get(i)) { - Utils.setBitLE(bits, i); - } - } - - return new PartialMerkleTree(networkParams, bits, merkleHashes, blockTxnCount); - } - - /** - * getBitcoinMergedMerkleBranch returns the Partial Merkle Branch needed to validate that the coinbase tx - * is part of the Merkle Tree. - * - * @param networkParams bitcoin network params. - * @param txStringHashes hashes for the txs of the bitcoin block used for merged mining. - * @return A Partial Merkle Branch in which you can validate the coinbase tx. - */ - private PartialMerkleTree getBitcoinMergedMerkleBranch(NetworkParameters networkParams, List txStringHashes) { - List txHashes = txStringHashes.stream().map(Sha256Hash::wrap).collect(Collectors.toList()); - - return buildMerkleBranch(txHashes, networkParams); - } - - /** - * getBitcoinMergedMerkleBranch returns the Partial Merkle Branch needed to validate that the coinbase tx - * is part of the Merkle Tree. - * - * @param bitcoinMergedMiningBlock the bitcoin block that includes all the txs. - * @return A Partial Merkle Branch in which you can validate the coinbase tx. - */ - public static PartialMerkleTree getBitcoinMergedMerkleBranch(BtcBlock bitcoinMergedMiningBlock) { - List txs = bitcoinMergedMiningBlock.getTransactions(); - List txHashes = new ArrayList<>(txs.size()); - for (BtcTransaction tx : txs) { - txHashes.add(tx.getHash()); - } - - return buildMerkleBranch(txHashes, bitcoinMergedMiningBlock.getParams()); - } - - private static PartialMerkleTree buildMerkleBranch(List txHashes, NetworkParameters params) { - /* - We need to convert the txs to a bitvector to choose which ones - will be included in the Partial Merkle Tree. - - We need txs.size() / 8 bytes to represent this vector. - The coinbase tx is the first one of the txs so we set the first bit to 1. - */ - byte[] bitvector = new byte[(txHashes.size() + 7) / 8]; - Utils.setBitLE(bitvector, 0); - return PartialMerkleTree.buildFromLeaves(params, bitvector, txHashes); - } - @Override public RskAddress getCoinbaseAddress() { return coinbaseAddress; @@ -661,14 +596,7 @@ public void buildBlockToMine(@Nonnull Block newBlockParent, boolean createCompet final Block newBlock = builder.build(newBlockParent, extraData); if (autoSwitchBetweenNormalAndFallbackMining) { - if (ProofOfWorkRule.isFallbackMiningPossible( - config.getBlockchainConfig().getCommonConstants(), - newBlock.getHeader())) { - - setFallbackMining(true); - } else { - setFallbackMining(false); - } + setFallbackMining(ProofOfWorkRule.isFallbackMiningPossible(config, newBlock.getHeader())); } synchronized (lock) { diff --git a/rskj-core/src/main/java/co/rsk/mine/MinerUtils.java b/rskj-core/src/main/java/co/rsk/mine/MinerUtils.java index 1bc47f8e249..96439190f50 100644 --- a/rskj-core/src/main/java/co/rsk/mine/MinerUtils.java +++ b/rskj-core/src/main/java/co/rsk/mine/MinerUtils.java @@ -26,19 +26,21 @@ import co.rsk.core.bc.PendingState; import co.rsk.crypto.Keccak256; import co.rsk.remasc.RemascTransaction; +import org.ethereum.config.BlockchainNetConfig; import org.ethereum.core.TransactionPool; import org.ethereum.core.Repository; import org.ethereum.core.Transaction; import org.ethereum.rpc.TypeConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.Arrays; +import org.bouncycastle.util.Arrays; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; import java.security.SecureRandom; import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; public class MinerUtils { @@ -132,6 +134,21 @@ public static co.rsk.bitcoinj.core.BtcBlock getBitcoinMergedMiningBlock(co.rsk.b return new co.rsk.bitcoinj.core.BtcBlock(params, params.getProtocolVersionNum(NetworkParameters.ProtocolVersion.CURRENT), prevBlockHash, null, time, difficultyTarget, 0, transactions); } + /** + * Takes in a proofBuilderFunction (e.g. buildFromTxHashes) + * and executes it on the builder corresponding to this block number. + */ + public static byte[] buildMerkleProof( + BlockchainNetConfig blockchainConfig, + Function proofBuilderFunction, + long blockNumber) { + if (blockchainConfig.getConfigForBlock(blockNumber).isRskip92()) { + return proofBuilderFunction.apply(new Rskip92MerkleProofBuilder()); + } else { + return proofBuilderFunction.apply(new GenesisMerkleProofBuilder()); + } + } + public List getAllTransactions(TransactionPool transactionPool) { //TODO: optimize this by considering GasPrice (order by GasPrice/Nonce) return transactionPool.getPendingTransactions().stream() diff --git a/rskj-core/src/main/java/co/rsk/mine/Rskip92MerkleProofBuilder.java b/rskj-core/src/main/java/co/rsk/mine/Rskip92MerkleProofBuilder.java new file mode 100644 index 00000000000..65132130ee8 --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/mine/Rskip92MerkleProofBuilder.java @@ -0,0 +1,75 @@ +/* + * This file is part of RskJ + * Copyright (C) 2018 RSK Labs Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ +package co.rsk.mine; + +import co.rsk.bitcoinj.core.BtcBlock; +import co.rsk.bitcoinj.core.Sha256Hash; +import co.rsk.bitcoinj.core.Utils; +import co.rsk.peg.utils.PartialMerkleTreeFormatUtils; +import org.bouncycastle.util.encoders.Hex; +import org.ethereum.util.ByteUtil; + +import java.util.List; +import java.util.stream.Stream; + +/** + * Builds RSKIP 92 Merkle proofs + */ +public class Rskip92MerkleProofBuilder implements MerkleProofBuilder { + + @Override + public byte[] buildFromMerkleHashes( + BtcBlock blockWithHeaderOnly, + List merkleHashesString, + int blockTxnCount) { + Stream hashesStream = merkleHashesString.stream() + .map(mh -> Utils.reverseBytes(Hex.decode(mh))); + return mergeHashes(hashesStream); + } + + @Override + public byte[] buildFromTxHashes(BtcBlock blockWithHeaderOnly, List txHashesString) { + return buildFromFullPmt( + new GenesisMerkleProofBuilder().buildFromTxHashes(blockWithHeaderOnly, txHashesString) + ); + } + + @Override + public byte[] buildFromBlock(BtcBlock bitcoinMergedMiningBlock) { + return buildFromFullPmt( + new GenesisMerkleProofBuilder().buildFromBlock(bitcoinMergedMiningBlock) + ); + } + + /** + * This takes a full PMT and slices and rearranges the needed pieces for the new serialization format. + * It would make sense to re-implement this as a standalone algorithm, but reusing the PMT is enough for now. + */ + private byte[] buildFromFullPmt(byte[] pmtSerialized) { + Stream hashesStream = PartialMerkleTreeFormatUtils.streamIntermediateHashes(pmtSerialized) + .map(Sha256Hash::getBytes); + return mergeHashes(hashesStream); + } + + private byte[] mergeHashes(Stream hashesStream) { + byte[][] hashes = hashesStream + .skip(1) // skip the coinbase + .toArray(byte[][]::new); + return ByteUtil.merge(hashes); + } +} \ No newline at end of file diff --git a/rskj-core/src/main/java/co/rsk/mine/TxBuilder.java b/rskj-core/src/main/java/co/rsk/mine/TxBuilder.java index 761d8c45dc5..ba453e1e43f 100644 --- a/rskj-core/src/main/java/co/rsk/mine/TxBuilder.java +++ b/rskj-core/src/main/java/co/rsk/mine/TxBuilder.java @@ -30,7 +30,7 @@ import org.ethereum.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.math.BigInteger; import java.nio.charset.StandardCharsets; diff --git a/rskj-core/src/main/java/co/rsk/net/BlockCache.java b/rskj-core/src/main/java/co/rsk/net/BlockCache.java index 20f05cdcc4a..6d2eb34f08e 100644 --- a/rskj-core/src/main/java/co/rsk/net/BlockCache.java +++ b/rskj-core/src/main/java/co/rsk/net/BlockCache.java @@ -20,41 +20,30 @@ package co.rsk.net; import co.rsk.crypto.Keccak256; +import co.rsk.util.MaxSizeHashMap; import org.ethereum.core.Block; -import java.util.LinkedHashMap; import java.util.Map; /** * Created by ajlopez on 17/06/2017. */ public class BlockCache { - private final LinkedHashMap linkedHashMap; + private final Map blockMap; public BlockCache(int cacheSize) { - this.linkedHashMap = new LinkedHashMap(cacheSize, 0.75f, true) { - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > cacheSize; - } - }; + this.blockMap = new MaxSizeHashMap<>(cacheSize, true); } public void removeBlock(Block block) { - linkedHashMap.remove(block.getHash()); + blockMap.remove(block.getHash()); } public void addBlock(Block block) { - linkedHashMap.put(block.getHash(), block); + blockMap.put(block.getHash(), block); } public Block getBlockByHash(byte[] hash) { - Keccak256 key = new Keccak256(hash); - - return linkedHashMap.get(key); - } - - public Block put(Keccak256 key, Block block) { - return linkedHashMap.put(key, block); + return blockMap.get(new Keccak256(hash)); } } diff --git a/rskj-core/src/main/java/co/rsk/net/NodeBlockProcessor.java b/rskj-core/src/main/java/co/rsk/net/NodeBlockProcessor.java index 2208bb87510..de1b66e4b08 100644 --- a/rskj-core/src/main/java/co/rsk/net/NodeBlockProcessor.java +++ b/rskj-core/src/main/java/co/rsk/net/NodeBlockProcessor.java @@ -27,7 +27,7 @@ import org.ethereum.core.Blockchain; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; diff --git a/rskj-core/src/main/java/co/rsk/net/NodeID.java b/rskj-core/src/main/java/co/rsk/net/NodeID.java index e2475f0fa61..136165fd43e 100644 --- a/rskj-core/src/main/java/co/rsk/net/NodeID.java +++ b/rskj-core/src/main/java/co/rsk/net/NodeID.java @@ -18,7 +18,7 @@ package co.rsk.net; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import javax.annotation.Nonnull; import java.util.Arrays; @@ -27,7 +27,7 @@ * NodeID is a wrapper over the nodeID byte array used by Ethereum. */ public class NodeID { - protected static final char[] hexArray = "0123456789ABCDEF".toCharArray(); + private final byte[] nodeID; public NodeID(@Nonnull final byte[] nodeID) { diff --git a/rskj-core/src/main/java/co/rsk/net/NodeMessageHandler.java b/rskj-core/src/main/java/co/rsk/net/NodeMessageHandler.java index 03fb026ff57..725fb3e1e26 100644 --- a/rskj-core/src/main/java/co/rsk/net/NodeMessageHandler.java +++ b/rskj-core/src/main/java/co/rsk/net/NodeMessageHandler.java @@ -34,7 +34,7 @@ import org.ethereum.net.server.ChannelManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; diff --git a/rskj-core/src/main/java/co/rsk/net/discovery/PacketDecoder.java b/rskj-core/src/main/java/co/rsk/net/discovery/PacketDecoder.java index 513fa11b4df..327f3706aa2 100644 --- a/rskj-core/src/main/java/co/rsk/net/discovery/PacketDecoder.java +++ b/rskj-core/src/main/java/co/rsk/net/discovery/PacketDecoder.java @@ -27,7 +27,7 @@ import io.netty.handler.codec.MessageToMessageDecoder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.net.InetSocketAddress; import java.util.List; diff --git a/rskj-core/src/main/java/co/rsk/net/discovery/PeerExplorer.java b/rskj-core/src/main/java/co/rsk/net/discovery/PeerExplorer.java index 59fc22f8059..daab67da8e7 100644 --- a/rskj-core/src/main/java/co/rsk/net/discovery/PeerExplorer.java +++ b/rskj-core/src/main/java/co/rsk/net/discovery/PeerExplorer.java @@ -181,7 +181,7 @@ public void handleNeighborsMessage(NeighborsPeerMessage message) { if (request != null && request.validateMessageResponse(message)) { List nodes = (message.countNodes() > MAX_NODES_PER_MSG) ? message.getNodes().subList(0, MAX_NODES_PER_MSG -1) : message.getNodes(); nodes.stream().filter(n -> !StringUtils.equals(n.getHexId(), this.localNode.getHexId())) - .forEach(node -> this.bootNodes.add(new InetSocketAddress(node.getAddress().getHostName(), node.getPort()))); + .forEach(node -> this.bootNodes.add(node.getAddress())); this.startConversationWithNewNodes(); } updateEntry(connectedNode); diff --git a/rskj-core/src/main/java/co/rsk/net/discovery/message/FindNodePeerMessage.java b/rskj-core/src/main/java/co/rsk/net/discovery/message/FindNodePeerMessage.java index 764c473bf96..100764ea687 100644 --- a/rskj-core/src/main/java/co/rsk/net/discovery/message/FindNodePeerMessage.java +++ b/rskj-core/src/main/java/co/rsk/net/discovery/message/FindNodePeerMessage.java @@ -21,7 +21,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.ethereum.crypto.ECKey; import org.ethereum.util.*; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; diff --git a/rskj-core/src/main/java/co/rsk/net/discovery/message/PeerDiscoveryMessage.java b/rskj-core/src/main/java/co/rsk/net/discovery/message/PeerDiscoveryMessage.java index 90bf55a0a92..fe798db42dc 100644 --- a/rskj-core/src/main/java/co/rsk/net/discovery/message/PeerDiscoveryMessage.java +++ b/rskj-core/src/main/java/co/rsk/net/discovery/message/PeerDiscoveryMessage.java @@ -26,8 +26,8 @@ import org.ethereum.util.RLPElement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.BigIntegers; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.BigIntegers; +import org.bouncycastle.util.encoders.Hex; import java.security.SignatureException; import java.util.Optional; diff --git a/rskj-core/src/main/java/co/rsk/net/eth/RskWireProtocol.java b/rskj-core/src/main/java/co/rsk/net/eth/RskWireProtocol.java index 48d963eb9af..569b88fbf24 100644 --- a/rskj-core/src/main/java/co/rsk/net/eth/RskWireProtocol.java +++ b/rskj-core/src/main/java/co/rsk/net/eth/RskWireProtocol.java @@ -43,7 +43,7 @@ import org.ethereum.util.ByteUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.net.InetAddress; import java.net.InetSocketAddress; diff --git a/rskj-core/src/main/java/co/rsk/net/eth/WriterMessageRecorder.java b/rskj-core/src/main/java/co/rsk/net/eth/WriterMessageRecorder.java index b4cafe18a67..e213dbffe0a 100644 --- a/rskj-core/src/main/java/co/rsk/net/eth/WriterMessageRecorder.java +++ b/rskj-core/src/main/java/co/rsk/net/eth/WriterMessageRecorder.java @@ -22,7 +22,7 @@ import org.ethereum.net.message.Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.io.BufferedWriter; import java.io.IOException; diff --git a/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java b/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java index 201aa2e4521..5231727cb99 100644 --- a/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java +++ b/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java @@ -24,7 +24,7 @@ import org.ethereum.core.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.BigIntegers; +import org.bouncycastle.util.BigIntegers; import javax.annotation.Nullable; import java.math.BigInteger; diff --git a/rskj-core/src/main/java/co/rsk/net/handler/txvalidator/TxValidatorIntrinsicGasLimitValidator.java b/rskj-core/src/main/java/co/rsk/net/handler/txvalidator/TxValidatorIntrinsicGasLimitValidator.java index c70fe2c4dd0..ae8a4a1f02e 100644 --- a/rskj-core/src/main/java/co/rsk/net/handler/txvalidator/TxValidatorIntrinsicGasLimitValidator.java +++ b/rskj-core/src/main/java/co/rsk/net/handler/txvalidator/TxValidatorIntrinsicGasLimitValidator.java @@ -20,7 +20,6 @@ import co.rsk.config.RskSystemProperties; import co.rsk.core.Coin; -import co.rsk.core.RskAddress; import org.ethereum.core.*; import javax.annotation.Nullable; @@ -42,9 +41,9 @@ public TxValidatorIntrinsicGasLimitValidator(RskSystemProperties config) { public boolean validate(Transaction tx, @Nullable AccountState state, BigInteger gasLimit, Coin minimumGasPrice, long bestBlockNumber, boolean isFreeTx) { BlockHeader blockHeader = new BlockHeader(new byte[]{}, new byte[]{}, - RskAddress.nullAddress().getBytes(), + new byte[20], new Bloom().getData(), - new byte[]{}, + null, bestBlockNumber, new byte[]{}, 0, @@ -53,7 +52,7 @@ public boolean validate(Transaction tx, @Nullable AccountState state, BigInteger new byte[]{}, new byte[]{}, new byte[]{}, - new byte[]{}, + new byte[]{0}, 0 ); Block block = new Block(blockHeader); diff --git a/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java b/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java index d92e77c1faf..b0650d48e36 100644 --- a/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java +++ b/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java @@ -25,7 +25,7 @@ import org.ethereum.util.RLP; import org.ethereum.util.RLPElement; import org.ethereum.util.RLPList; -import org.spongycastle.util.BigIntegers; +import org.bouncycastle.util.BigIntegers; import java.util.ArrayList; import java.util.List; @@ -51,7 +51,7 @@ public Message createMessage(RLPList list) { byte[] parentHash = list.get(2).getRLPData(); byte[] rlpTotalDifficulty = list.get(3).getRLPData(); - BlockDifficulty totalDifficulty = rlpTotalDifficulty == null ? BlockDifficulty.ZERO : new BlockDifficulty(rlpTotalDifficulty); + BlockDifficulty totalDifficulty = rlpTotalDifficulty == null ? BlockDifficulty.ZERO : RLP.parseBlockDifficulty(rlpTotalDifficulty); return new StatusMessage(new Status(number, hash, parentHash, totalDifficulty)); } diff --git a/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java b/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java index 3ebb82e6521..6ae6e91672f 100644 --- a/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java +++ b/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java @@ -4,9 +4,9 @@ import co.rsk.net.NodeID; import co.rsk.net.messages.BodyResponseMessage; import co.rsk.scoring.EventType; +import co.rsk.validators.BlockCompositeRule; import co.rsk.validators.BlockRootValidationRule; import co.rsk.validators.BlockUnclesHashValidationRule; -import co.rsk.validators.BlockValidationRule; import com.google.common.annotations.VisibleForTesting; import org.ethereum.core.Block; import org.ethereum.core.BlockHeader; @@ -20,8 +20,7 @@ public class DownloadingBodiesSyncState extends BaseSyncState { // validation rules for bodies - private final BlockValidationRule blockUnclesHashValidationRule; - private final BlockValidationRule blockTransactionsValidationRule; + private final BlockCompositeRule blockValidationRules; // responses on wait private final Map pendingBodyResponses; @@ -61,8 +60,10 @@ public DownloadingBodiesSyncState(SyncConfiguration syncConfiguration, super(syncInformation, syncEventsHandler, syncConfiguration); this.limit = syncConfiguration.getTimeoutWaitingRequest(); - this.blockUnclesHashValidationRule = new BlockUnclesHashValidationRule(); - this.blockTransactionsValidationRule = new BlockRootValidationRule(); + this.blockValidationRules = new BlockCompositeRule( + new BlockUnclesHashValidationRule(), + new BlockRootValidationRule() + ); this.pendingBodyResponses = new HashMap<>(); this.pendingHeaders = pendingHeaders; this.skeletons = skeletons; @@ -88,7 +89,7 @@ public void newBody(BodyResponseMessage message, MessageChannel peer) { // we already checked that this message was expected BlockHeader header = pendingBodyResponses.remove(message.getId()).header; Block block = Block.fromValidData(header, message.getTransactions(), message.getUncles()); - if (!blockUnclesHashValidationRule.isValid(block) || !blockTransactionsValidationRule.isValid(block)) { + if (!blockValidationRules.isValid(block)) { handleInvalidMessage(peerId, header); return; } diff --git a/rskj-core/src/main/java/co/rsk/peg/Bridge.java b/rskj-core/src/main/java/co/rsk/peg/Bridge.java index c8b1ba0bfdf..cebf9f93907 100644 --- a/rskj-core/src/main/java/co/rsk/peg/Bridge.java +++ b/rskj-core/src/main/java/co/rsk/peg/Bridge.java @@ -27,7 +27,11 @@ import co.rsk.peg.utils.BridgeEventLogger; import co.rsk.peg.utils.BridgeEventLoggerImpl; import co.rsk.peg.utils.BtcTransactionFormatUtils; +import co.rsk.peg.whitelist.LockWhitelistEntry; +import co.rsk.peg.whitelist.OneOffWhiteListEntry; import com.google.common.annotations.VisibleForTesting; +import org.ethereum.config.BlockchainConfig; +import org.ethereum.config.BlockchainNetConfig; import org.ethereum.core.Block; import org.ethereum.core.CallTransaction; import org.ethereum.core.Repository; @@ -40,7 +44,7 @@ import org.ethereum.vm.program.Program; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import java.io.IOException; import java.math.BigInteger; @@ -84,6 +88,14 @@ public class Bridge extends PrecompiledContracts.PrecompiledContract { // The goal of this function is to help synchronize bridge and federators blockchains. // Protocol inspired by bitcoin sync protocol, see block locator in https://en.bitcoin.it/wiki/Protocol_documentation#getheaders public static final CallTransaction.Function GET_BTC_BLOCKCHAIN_BLOCK_LOCATOR = BridgeMethods.GET_BTC_BLOCKCHAIN_BLOCK_LOCATOR.getFunction(); + // Return the height of the initial block stored in the bridge's bitcoin blockchain + public static final CallTransaction.Function GET_BTC_BLOCKCHAIN_INITIAL_BLOCK_HEIGHT = BridgeMethods.GET_BTC_BLOCKCHAIN_INITIAL_BLOCK_HEIGHT.getFunction(); + // Returns the block hash of the bridge contract's best chain at the given depth, meaning depth zero will + // yield the best chain head hash and depth one will yield its parent hash, and so on and so forth. + // Federators use this to find what is the latest block in the mainchain the bridge has + // (replacing the need for getBtcBlockchainBlockLocator). + // The goal of this function is to help synchronize bridge and federators blockchains. + public static final CallTransaction.Function GET_BTC_BLOCKCHAIN_BLOCK_HASH_AT_DEPTH = BridgeMethods.GET_BTC_BLOCKCHAIN_BLOCK_HASH_AT_DEPTH.getFunction(); // Returns the minimum amount of satoshis a user should send to the federation. public static final CallTransaction.Function GET_MINIMUM_LOCK_TX_VALUE = BridgeMethods.GET_MINIMUM_LOCK_TX_VALUE.getFunction(); @@ -138,8 +150,14 @@ public class Bridge extends PrecompiledContracts.PrecompiledContract { public static final CallTransaction.Function GET_LOCK_WHITELIST_SIZE = BridgeMethods.GET_LOCK_WHITELIST_SIZE.getFunction(); // Returns the lock whitelist address stored at the specified index public static final CallTransaction.Function GET_LOCK_WHITELIST_ADDRESS = BridgeMethods.GET_LOCK_WHITELIST_ADDRESS.getFunction(); + // Returns the lock whitelist entry stored at the specified address + public static final CallTransaction.Function GET_LOCK_WHITELIST_ENTRY_BY_ADDRESS = BridgeMethods.GET_LOCK_WHITELIST_ENTRY_BY_ADDRESS.getFunction(); // Adds the given address to the lock whitelist public static final CallTransaction.Function ADD_LOCK_WHITELIST_ADDRESS = BridgeMethods.ADD_LOCK_WHITELIST_ADDRESS.getFunction(); + // Adds the given address to the lock whitelist in "one-off" mode + public static final CallTransaction.Function ADD_ONE_OFF_LOCK_WHITELIST_ADDRESS = BridgeMethods.ADD_ONE_OFF_LOCK_WHITELIST_ADDRESS.getFunction(); + // Adds the given address to the lock whitelist in "unlimited" mode + public static final CallTransaction.Function ADD_UNLIMITED_LOCK_WHITELIST_ADDRESS = BridgeMethods.ADD_UNLIMITED_LOCK_WHITELIST_ADDRESS.getFunction(); // Adds the given address to the lock whitelist public static final CallTransaction.Function REMOVE_LOCK_WHITELIST_ADDRESS = BridgeMethods.REMOVE_LOCK_WHITELIST_ADDRESS.getFunction(); @@ -150,6 +168,10 @@ public class Bridge extends PrecompiledContracts.PrecompiledContract { // Adds the given key to the current pending federation public static final CallTransaction.Function VOTE_FEE_PER_KB = BridgeMethods.VOTE_FEE_PER_KB.getFunction(); + public static final int LOCK_WHITELIST_UNLIMITED_MODE_CODE = 0; + public static final int LOCK_WHITELIST_ENTRY_NOT_FOUND_CODE = -1; + public static final int LOCK_WHITELIST_INVALID_ADDRESS_FORMAT_ERROR_CODE = -2; + // Log topics used by Bridge Contract public static final DataWord RELEASE_BTC_TOPIC = new DataWord("release_btc_topic".getBytes(StandardCharsets.UTF_8)); public static final DataWord UPDATE_COLLECTIONS_TOPIC = new DataWord("update_collections_topic".getBytes(StandardCharsets.UTF_8)); @@ -159,6 +181,9 @@ public class Bridge extends PrecompiledContracts.PrecompiledContract { private final RskSystemProperties config; private final BridgeConstants bridgeConstants; + private BlockchainNetConfig blockchainNetConfig; + private BlockchainConfig blockchainConfig; + private org.ethereum.core.Transaction rskTx; private org.ethereum.core.Block rskExecutionBlock; private Repository repository; @@ -167,13 +192,20 @@ public class Bridge extends PrecompiledContracts.PrecompiledContract { private BridgeSupport bridgeSupport; public Bridge(RskSystemProperties config, RskAddress contractAddress) { - this.config = config; - this.bridgeConstants = this.config.getBlockchainConfig().getCommonConstants().getBridgeConstants(); this.contractAddress = contractAddress; + + this.config = config; + this.blockchainNetConfig = config.getBlockchainConfig(); + this.bridgeConstants = blockchainNetConfig.getCommonConstants().getBridgeConstants(); } @Override public long getGasForData(byte[] data) { + if (!blockchainConfig.isRskip88() && BridgeUtils.isContractTx(rskTx)) { + logger.warn("Call from contract before Orchid"); + throw new NullPointerException(); + } + if (BridgeUtils.isFreeBridgeTx(config, rskTx, rskExecutionBlock.getNumber())) { return 0; } @@ -222,6 +254,12 @@ BridgeParsedData parseData(byte[] data) { return null; } } + + if (!bridgeParsedData.bridgeMethod.isEnabled(this.blockchainConfig)) { + logger.warn("'{}' is not enabled to run",bridgeParsedData.bridgeMethod.name()); + return null; + } + return bridgeParsedData; } @@ -237,18 +275,39 @@ public void init(Transaction rskTx, Block rskExecutionBlock, Repository reposito this.rskExecutionBlock = rskExecutionBlock; this.repository = repository; this.logs = logs; + this.blockchainConfig = blockchainNetConfig.getConfigForBlock(rskExecutionBlock.getNumber()); } @Override public byte[] execute(byte[] data) { try { + // Preliminary validation: the transaction on which we execute cannot be null + if (rskTx == null) { + throw new RuntimeException("Rsk Transaction is null"); + } + BridgeParsedData bridgeParsedData = parseData(data); + // Function parsing from data returned null => invalid function selected, halt! if (bridgeParsedData == null) { + String errorMessage = String.format("Invalid data given: %s.", Hex.toHexString(data)); + logger.info(errorMessage); + if (blockchainConfig.isRskip88()) { + throw new BridgeIllegalArgumentException(errorMessage); + } + return null; } + // If this is not a local call, then first check whether the function + // allows for non-local calls + if (blockchainConfig.isRskip88() && !isLocalCall() && bridgeParsedData.bridgeMethod.onlyAllowsLocalCalls()) { + String errorMessage = String.format("Non-local-call to %s. Returning without execution.", bridgeParsedData.bridgeMethod.getFunction().name); + logger.info(errorMessage); + throw new BridgeIllegalArgumentException(errorMessage); + } + this.bridgeSupport = setup(); Optional result; @@ -257,7 +316,12 @@ public byte[] execute(byte[] data) { // If the user tries to call an non-existent function, parseData() will return null. result = bridgeParsedData.bridgeMethod.getExecutor().execute(this, bridgeParsedData.args); } catch (BridgeIllegalArgumentException ex) { - logger.warn("Error executing: {}", bridgeParsedData.bridgeMethod, ex); + String errorMessage = String.format("Error executing: %s", bridgeParsedData.bridgeMethod); + logger.warn(errorMessage, ex); + if (blockchainConfig.isRskip88()) { + throw new BridgeIllegalArgumentException(errorMessage); + } + return null; } @@ -431,6 +495,24 @@ public Integer getBtcBlockchainBestChainHeight(Object[] args) } } + public Integer getBtcBlockchainInitialBlockHeight(Object[] args) + { + logger.trace("getBtcBlockchainInitialBlockHeight"); + + try { + return bridgeSupport.getBtcBlockchainInitialBlockHeight(); + } catch (Exception e) { + logger.warn("Exception in getBtcBlockchainInitialBlockHeight", e); + throw new RuntimeException("Exception in getBtcBlockchainInitialBlockHeight", e); + } + } + + /** + * @deprecated + * @param args + * @return + */ + @Deprecated public Object[] getBtcBlockchainBlockLocator(Object[] args) { logger.trace("getBtcBlockchainBlockLocator"); @@ -450,6 +532,22 @@ public Object[] getBtcBlockchainBlockLocator(Object[] args) } } + public byte[] getBtcBlockchainBlockHashAtDepth(Object[] args) + { + logger.trace("getBtcBlockchainBlockHashAtDepth"); + + int depth = ((BigInteger) args[0]).intValue(); + Sha256Hash blockHash = null; + try { + blockHash = bridgeSupport.getBtcBlockchainBlockHashAtDepth(depth); + } catch (Exception e) { + logger.warn("Exception in getBtcBlockchainBlockHashAtDepth", e); + throw new RuntimeException("Exception in getBtcBlockchainBlockHashAtDepth", e); + } + + return blockHash.getBytes(); + } + public Long getMinimumLockTxValue(Object[] args) { logger.trace("getMinimumLockTxValue"); @@ -605,7 +703,7 @@ public Integer addFederatorPublicKey(Object[] args) try { publicKeyBytes = (byte[]) args[0]; } catch (Exception e) { - logger.warn("Exception in addFederatorPublicKey: {}", e.getMessage()); + logger.warn("Exception in addFederatorPublicKey", e); return -10; } @@ -623,7 +721,7 @@ public Integer commitFederation(Object[] args) try { hash = (byte[]) args[0]; } catch (Exception e) { - logger.warn("Exception in commitFederation: {}", e.getMessage()); + logger.warn("Exception in commitFederation", e); return -10; } @@ -691,19 +789,43 @@ public String getLockWhitelistAddress(Object[] args) logger.trace("getLockWhitelistAddress"); int index = ((BigInteger) args[0]).intValue(); - String address = bridgeSupport.getLockWhitelistAddress(index); + LockWhitelistEntry entry = bridgeSupport.getLockWhitelistEntryByIndex(index); - if (address == null) { + if (entry == null) { // Empty string is returned when address is not found return ""; } - return address; + return entry.address().toBase58(); + } + + public long getLockWhitelistEntryByAddress(Object[] args) + { + logger.trace("getLockWhitelistEntryByAddress"); + + String addressBase58; + try { + addressBase58 = (String) args[0]; + } catch (Exception e) { + logger.warn("Exception in getLockWhitelistEntryByAddress", e); + return LOCK_WHITELIST_INVALID_ADDRESS_FORMAT_ERROR_CODE; + } + + LockWhitelistEntry entry = bridgeSupport.getLockWhitelistEntryByAddress(addressBase58); + + if (entry == null) { + // Empty string is returned when address is not found + return LOCK_WHITELIST_ENTRY_NOT_FOUND_CODE; + } + + return entry.getClass() == OneOffWhiteListEntry.class ? + ((OneOffWhiteListEntry)entry).maxTransferValue().getValue() : + LOCK_WHITELIST_UNLIMITED_MODE_CODE; } - public Integer addLockWhitelistAddress(Object[] args) + public Integer addOneOffLockWhitelistAddress(Object[] args) { - logger.trace("addLockWhitelistAddress"); + logger.trace("addOneOffLockWhitelistAddress"); String addressBase58; BigInteger maxTransferValue; @@ -711,11 +833,26 @@ public Integer addLockWhitelistAddress(Object[] args) addressBase58 = (String) args[0]; maxTransferValue = (BigInteger) args[1]; } catch (Exception e) { - logger.warn("Exception in addLockWhitelistAddress: {}", e.getMessage()); + logger.warn("Exception in addOneOffLockWhitelistAddress", e); return 0; } - return bridgeSupport.addLockWhitelistAddress(rskTx, addressBase58, maxTransferValue); + return bridgeSupport.addOneOffLockWhitelistAddress(rskTx, addressBase58, maxTransferValue); + } + + public Integer addUnlimitedLockWhitelistAddress(Object[] args) + { + logger.trace("addUnlimitedLockWhitelistAddress"); + + String addressBase58; + try { + addressBase58 = (String) args[0]; + } catch (Exception e) { + logger.warn("Exception in addUnlimitedLockWhitelistAddress", e); + return 0; + } + + return bridgeSupport.addUnlimitedLockWhitelistAddress(rskTx, addressBase58); } public Integer removeLockWhitelistAddress(Object[] args) @@ -726,7 +863,7 @@ public Integer removeLockWhitelistAddress(Object[] args) try { addressBase58 = (String) args[0]; } catch (Exception e) { - logger.warn("Exception in removeLockWhitelistAddress: {}", e.getMessage()); + logger.warn("Exception in removeLockWhitelistAddress", e); return 0; } @@ -747,7 +884,7 @@ public Integer voteFeePerKbChange(Object[] args) try { feePerKb = Coin.valueOf(((BigInteger) args[0]).longValueExact()); } catch (Exception e) { - logger.warn("Exception in voteFeePerKbChange: {}", e); + logger.warn("Exception in voteFeePerKbChange", e); return -10; } @@ -764,11 +901,6 @@ public long getFeePerKb(Object[] args) public static BridgeMethods.BridgeMethodExecutor activeAndRetiringFederationOnly(BridgeMethods.BridgeMethodExecutor decoratee, String funcName) { return (self, args) -> { - if(self.rskTx == null){ - String errorMessage = String.format("Rsk Transaction is null for function '%s'",funcName); - logger.warn(errorMessage); - throw new RuntimeException(errorMessage); - } Federation retiringFederation = self.bridgeSupport.getRetiringFederation(); if (!BridgeUtils.isFromFederateMember(self.rskTx, self.bridgeSupport.getActiveFederation()) @@ -781,4 +913,7 @@ public static BridgeMethods.BridgeMethodExecutor activeAndRetiringFederationOnly }; } + private boolean isLocalCall() { + return rskTx.isLocalCallTransaction(); + } } diff --git a/rskj-core/src/main/java/co/rsk/peg/BridgeMethods.java b/rskj-core/src/main/java/co/rsk/peg/BridgeMethods.java index 590806c3563..9b21196ac5c 100644 --- a/rskj-core/src/main/java/co/rsk/peg/BridgeMethods.java +++ b/rskj-core/src/main/java/co/rsk/peg/BridgeMethods.java @@ -17,6 +17,7 @@ */ package co.rsk.peg; +import org.ethereum.config.BlockchainConfig; import org.ethereum.core.CallTransaction; import org.ethereum.db.ByteArrayWrapper; @@ -37,7 +38,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 13000L, - (BridgeMethodExecutorTyped) Bridge::addFederatorPublicKey + (BridgeMethodExecutorTyped) Bridge::addFederatorPublicKey, + false ), ADD_LOCK_WHITELIST_ADDRESS( CallTransaction.Function.fromSignature( @@ -46,7 +48,31 @@ public enum BridgeMethods { new String[]{"int256"} ), 25000L, - (BridgeMethodExecutorTyped) Bridge::addLockWhitelistAddress + (BridgeMethodExecutorTyped) Bridge::addOneOffLockWhitelistAddress, + blockChainConfig -> !blockChainConfig.isRskip87(), + false + ), + ADD_ONE_OFF_LOCK_WHITELIST_ADDRESS( + CallTransaction.Function.fromSignature( + "addOneOffLockWhitelistAddress", + new String[]{"string", "int256"}, + new String[]{"int256"} + ), + 25000L, // using same gas estimation as ADD_LOCK_WHITELIST_ADDRESS + (BridgeMethodExecutorTyped) Bridge::addOneOffLockWhitelistAddress, + blockChainConfig -> blockChainConfig.isRskip87(), + false + ), + ADD_UNLIMITED_LOCK_WHITELIST_ADDRESS( + CallTransaction.Function.fromSignature( + "addUnlimitedLockWhitelistAddress", + new String[]{"string"}, + new String[]{"int256"} + ), + 25000L, // using same gas estimation as ADD_LOCK_WHITELIST_ADDRESS + (BridgeMethodExecutorTyped) Bridge::addUnlimitedLockWhitelistAddress, + blockChainConfig -> blockChainConfig.isRskip87(), + false ), ADD_SIGNATURE( CallTransaction.Function.fromSignature( @@ -55,7 +81,8 @@ public enum BridgeMethods { new String[]{} ), 70000L, - Bridge.activeAndRetiringFederationOnly((BridgeMethodExecutorVoid) Bridge::addSignature, "addSignature") + Bridge.activeAndRetiringFederationOnly((BridgeMethodExecutorVoid) Bridge::addSignature, "addSignature"), + false ), COMMIT_FEDERATION( CallTransaction.Function.fromSignature( @@ -64,7 +91,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 38000L, - (BridgeMethodExecutorTyped) Bridge::commitFederation + (BridgeMethodExecutorTyped) Bridge::commitFederation, + false ), CREATE_FEDERATION( CallTransaction.Function.fromSignature( @@ -73,7 +101,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 11000L, - (BridgeMethodExecutorTyped) Bridge::createFederation + (BridgeMethodExecutorTyped) Bridge::createFederation, + false ), GET_BTC_BLOCKCHAIN_BEST_CHAIN_HEIGHT( CallTransaction.Function.fromSignature( @@ -82,7 +111,19 @@ public enum BridgeMethods { new String[]{"int"} ), 19000L, - (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBestChainHeight + (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBestChainHeight, + true + ), + GET_BTC_BLOCKCHAIN_INITIAL_BLOCK_HEIGHT( + CallTransaction.Function.fromSignature( + "getBtcBlockchainInitialBlockHeight", + new String[]{}, + new String[]{"int"} + ), + 20000L, + (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainInitialBlockHeight, + blockchainConfig -> blockchainConfig.isRskip89(), + true ), GET_BTC_BLOCKCHAIN_BLOCK_LOCATOR( CallTransaction.Function.fromSignature( @@ -91,7 +132,20 @@ public enum BridgeMethods { new String[]{"string[]"} ), 76000L, - (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBlockLocator + (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBlockLocator, + blockchainConfig -> !blockchainConfig.isRskip89(), + true + ), + GET_BTC_BLOCKCHAIN_BLOCK_HASH_AT_DEPTH( + CallTransaction.Function.fromSignature( + "getBtcBlockchainBlockHashAtDepth", + new String[]{"int256"}, + new String[]{"bytes"} + ), + 20000L, + (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBlockHashAtDepth, + blockchainConfig -> blockchainConfig.isRskip89(), + true ), GET_BTC_TX_HASH_PROCESSED_HEIGHT( CallTransaction.Function.fromSignature( @@ -100,7 +154,8 @@ public enum BridgeMethods { new String[]{"int64"} ), 22000L, - (BridgeMethodExecutorTyped) Bridge::getBtcTxHashProcessedHeight + (BridgeMethodExecutorTyped) Bridge::getBtcTxHashProcessedHeight, + true ), GET_FEDERATION_ADDRESS( CallTransaction.Function.fromSignature( @@ -109,7 +164,8 @@ public enum BridgeMethods { new String[]{"string"} ), 11000L, - (BridgeMethodExecutorTyped) Bridge::getFederationAddress + (BridgeMethodExecutorTyped) Bridge::getFederationAddress, + true ), GET_FEDERATION_CREATION_BLOCK_NUMBER( CallTransaction.Function.fromSignature( @@ -118,7 +174,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 10000L, - (BridgeMethodExecutorTyped) Bridge::getFederationCreationBlockNumber + (BridgeMethodExecutorTyped) Bridge::getFederationCreationBlockNumber, + true ), GET_FEDERATION_CREATION_TIME( CallTransaction.Function.fromSignature( @@ -127,7 +184,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 10000L, - (BridgeMethodExecutorTyped) Bridge::getFederationCreationTime + (BridgeMethodExecutorTyped) Bridge::getFederationCreationTime, + true ), GET_FEDERATION_SIZE( CallTransaction.Function.fromSignature( @@ -136,7 +194,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 10000L, - (BridgeMethodExecutorTyped) Bridge::getFederationSize + (BridgeMethodExecutorTyped) Bridge::getFederationSize, + true ), GET_FEDERATION_THRESHOLD( CallTransaction.Function.fromSignature( @@ -145,7 +204,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 11000L, - (BridgeMethodExecutorTyped) Bridge::getFederationThreshold + (BridgeMethodExecutorTyped) Bridge::getFederationThreshold, + true ), GET_FEDERATOR_PUBLIC_KEY( CallTransaction.Function.fromSignature( @@ -154,7 +214,8 @@ public enum BridgeMethods { new String[]{"bytes"} ), 10000L, - (BridgeMethodExecutorTyped) Bridge::getFederatorPublicKey + (BridgeMethodExecutorTyped) Bridge::getFederatorPublicKey, + true ), GET_FEE_PER_KB( CallTransaction.Function.fromSignature( @@ -163,7 +224,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 2000L, - (BridgeMethodExecutorTyped) Bridge::getFeePerKb + (BridgeMethodExecutorTyped) Bridge::getFeePerKb, + true ), GET_LOCK_WHITELIST_ADDRESS( CallTransaction.Function.fromSignature( @@ -172,7 +234,19 @@ public enum BridgeMethods { new String[]{"string"} ), 16000L, - (BridgeMethodExecutorTyped) Bridge::getLockWhitelistAddress + (BridgeMethodExecutorTyped) Bridge::getLockWhitelistAddress, + true + ), + GET_LOCK_WHITELIST_ENTRY_BY_ADDRESS( + CallTransaction.Function.fromSignature( + "getLockWhitelistEntryByAddress", + new String[]{"string"}, + new String[]{"int256"} + ), + 16000L, + (BridgeMethodExecutorTyped) Bridge::getLockWhitelistEntryByAddress, + blockchainConfig -> blockchainConfig.isRskip87(), + true ), GET_LOCK_WHITELIST_SIZE( CallTransaction.Function.fromSignature( @@ -181,7 +255,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 16000L, - (BridgeMethodExecutorTyped) Bridge::getLockWhitelistSize + (BridgeMethodExecutorTyped) Bridge::getLockWhitelistSize, + true ), GET_MINIMUM_LOCK_TX_VALUE( CallTransaction.Function.fromSignature( @@ -190,7 +265,8 @@ public enum BridgeMethods { new String[]{"int"} ), 2000L, - (BridgeMethodExecutorTyped) Bridge::getMinimumLockTxValue + (BridgeMethodExecutorTyped) Bridge::getMinimumLockTxValue, + true ), GET_PENDING_FEDERATION_HASH( CallTransaction.Function.fromSignature( @@ -199,7 +275,8 @@ public enum BridgeMethods { new String[]{"bytes"} ), 3000L, - (BridgeMethodExecutorTyped) Bridge::getPendingFederationHash + (BridgeMethodExecutorTyped) Bridge::getPendingFederationHash, + true ), GET_PENDING_FEDERATION_SIZE( CallTransaction.Function.fromSignature( @@ -208,7 +285,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 3000L, - (BridgeMethodExecutorTyped) Bridge::getPendingFederationSize + (BridgeMethodExecutorTyped) Bridge::getPendingFederationSize, + true ), GET_PENDING_FEDERATOR_PUBLIC_KEY( CallTransaction.Function.fromSignature( @@ -217,7 +295,8 @@ public enum BridgeMethods { new String[]{"bytes"} ), 3000L, - (BridgeMethodExecutorTyped) Bridge::getPendingFederatorPublicKey + (BridgeMethodExecutorTyped) Bridge::getPendingFederatorPublicKey, + true ), GET_RETIRING_FEDERATION_ADDRESS( CallTransaction.Function.fromSignature( @@ -226,7 +305,8 @@ public enum BridgeMethods { new String[]{"string"} ), 3000L, - (BridgeMethodExecutorTyped) Bridge::getRetiringFederationAddress + (BridgeMethodExecutorTyped) Bridge::getRetiringFederationAddress, + true ), GET_RETIRING_FEDERATION_CREATION_BLOCK_NUMBER( CallTransaction.Function.fromSignature( @@ -235,7 +315,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 3000L, - (BridgeMethodExecutorTyped) Bridge::getRetiringFederationCreationBlockNumber + (BridgeMethodExecutorTyped) Bridge::getRetiringFederationCreationBlockNumber, + true ), GET_RETIRING_FEDERATION_CREATION_TIME( CallTransaction.Function.fromSignature( @@ -244,7 +325,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 3000L, - (BridgeMethodExecutorTyped) Bridge::getRetiringFederationCreationTime + (BridgeMethodExecutorTyped) Bridge::getRetiringFederationCreationTime, + true ), GET_RETIRING_FEDERATION_SIZE( CallTransaction.Function.fromSignature( @@ -253,7 +335,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 3000L, - (BridgeMethodExecutorTyped) Bridge::getRetiringFederationSize + (BridgeMethodExecutorTyped) Bridge::getRetiringFederationSize, + true ), GET_RETIRING_FEDERATION_THRESHOLD( CallTransaction.Function.fromSignature( @@ -262,7 +345,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 3000L, - (BridgeMethodExecutorTyped) Bridge::getRetiringFederationThreshold + (BridgeMethodExecutorTyped) Bridge::getRetiringFederationThreshold, + true ), GET_RETIRING_FEDERATOR_PUBLIC_KEY( CallTransaction.Function.fromSignature( @@ -271,7 +355,8 @@ public enum BridgeMethods { new String[]{"bytes"} ), 3000L, - (BridgeMethodExecutorTyped) Bridge::getRetiringFederatorPublicKey + (BridgeMethodExecutorTyped) Bridge::getRetiringFederatorPublicKey, + true ), GET_STATE_FOR_BTC_RELEASE_CLIENT( CallTransaction.Function.fromSignature( @@ -280,7 +365,8 @@ public enum BridgeMethods { new String[]{"bytes"} ), 4000L, - (BridgeMethodExecutorTyped) Bridge::getStateForBtcReleaseClient + (BridgeMethodExecutorTyped) Bridge::getStateForBtcReleaseClient, + true ), GET_STATE_FOR_DEBUGGING( CallTransaction.Function.fromSignature( @@ -289,7 +375,8 @@ public enum BridgeMethods { new String[]{"bytes"} ), 3_000_000L, - (BridgeMethodExecutorTyped) Bridge::getStateForDebugging + (BridgeMethodExecutorTyped) Bridge::getStateForDebugging, + true ), IS_BTC_TX_HASH_ALREADY_PROCESSED( CallTransaction.Function.fromSignature( @@ -298,7 +385,8 @@ public enum BridgeMethods { new String[]{"bool"} ), 23000L, - (BridgeMethodExecutorTyped) Bridge::isBtcTxHashAlreadyProcessed + (BridgeMethodExecutorTyped) Bridge::isBtcTxHashAlreadyProcessed, + true ), RECEIVE_HEADERS( CallTransaction.Function.fromSignature( @@ -307,7 +395,8 @@ public enum BridgeMethods { new String[]{} ), 22000L, - Bridge.activeAndRetiringFederationOnly((BridgeMethodExecutorVoid) Bridge::receiveHeaders, "receiveHeaders") + Bridge.activeAndRetiringFederationOnly((BridgeMethodExecutorVoid) Bridge::receiveHeaders, "receiveHeaders"), + false ), REGISTER_BTC_TRANSACTION( CallTransaction.Function.fromSignature( @@ -316,7 +405,8 @@ public enum BridgeMethods { new String[]{} ), 22000L, - Bridge.activeAndRetiringFederationOnly((BridgeMethodExecutorVoid) Bridge::registerBtcTransaction, "registerBtcTransaction") + Bridge.activeAndRetiringFederationOnly((BridgeMethodExecutorVoid) Bridge::registerBtcTransaction, "registerBtcTransaction"), + false ), RELEASE_BTC( CallTransaction.Function.fromSignature( @@ -325,7 +415,8 @@ public enum BridgeMethods { new String[]{} ), 23000L, - (BridgeMethodExecutorVoid) Bridge::releaseBtc + (BridgeMethodExecutorVoid) Bridge::releaseBtc, + false ), REMOVE_LOCK_WHITELIST_ADDRESS( CallTransaction.Function.fromSignature( @@ -334,7 +425,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 24000L, - (BridgeMethodExecutorTyped) Bridge::removeLockWhitelistAddress + (BridgeMethodExecutorTyped) Bridge::removeLockWhitelistAddress, + false ), ROLLBACK_FEDERATION( CallTransaction.Function.fromSignature( @@ -343,7 +435,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 12000L, - (BridgeMethodExecutorTyped) Bridge::rollbackFederation + (BridgeMethodExecutorTyped) Bridge::rollbackFederation, + false ), SET_LOCK_WHITELIST_DISABLE_BLOCK_DELAY( CallTransaction.Function.fromSignature( @@ -352,7 +445,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 24000L, - (BridgeMethodExecutorTyped) Bridge::setLockWhitelistDisableBlockDelay + (BridgeMethodExecutorTyped) Bridge::setLockWhitelistDisableBlockDelay, + false ), UPDATE_COLLECTIONS( CallTransaction.Function.fromSignature( @@ -361,7 +455,8 @@ public enum BridgeMethods { new String[]{} ), 48000L, - Bridge.activeAndRetiringFederationOnly((BridgeMethodExecutorVoid) Bridge::updateCollections, "updateCollections") + Bridge.activeAndRetiringFederationOnly((BridgeMethodExecutorVoid) Bridge::updateCollections, "updateCollections"), + false ), VOTE_FEE_PER_KB( CallTransaction.Function.fromSignature( @@ -370,7 +465,8 @@ public enum BridgeMethods { new String[]{"int256"} ), 10000L, - (BridgeMethodExecutorTyped) Bridge::voteFeePerKbChange + (BridgeMethodExecutorTyped) Bridge::voteFeePerKbChange, + false ); private static final Map SIGNATURES = Stream.of(BridgeMethods.values()) @@ -380,12 +476,20 @@ public enum BridgeMethods { )); private final CallTransaction.Function function; private final long cost; + private final Function isEnabledFunction; private final BridgeMethodExecutor executor; + private final boolean onlyAllowsLocalCalls; + + BridgeMethods(CallTransaction.Function function, long cost, BridgeMethodExecutor executor, boolean onlyAllowsLocalCalls) { + this(function, cost, executor, blockchainConfig -> Boolean.TRUE, onlyAllowsLocalCalls); + } - BridgeMethods(CallTransaction.Function function, long cost, BridgeMethodExecutor executor) { + BridgeMethods(CallTransaction.Function function, long cost, BridgeMethodExecutor executor, Function isEnabled, boolean onlyAllowsLocalCalls) { this.function = function; this.cost = cost; this.executor = executor; + this.isEnabledFunction = isEnabled; + this.onlyAllowsLocalCalls = onlyAllowsLocalCalls; } public static Optional findBySignature(byte[] encoding) { @@ -395,6 +499,9 @@ public static Optional findBySignature(byte[] encoding) { public CallTransaction.Function getFunction() { return function; } + public Boolean isEnabled(BlockchainConfig blockchainConfig) { + return this.isEnabledFunction.apply(blockchainConfig); + } public long getCost() { return cost; @@ -404,6 +511,10 @@ public BridgeMethodExecutor getExecutor() { return executor; } + public boolean onlyAllowsLocalCalls() { + return onlyAllowsLocalCalls; + } + public interface BridgeMethodExecutor { Optional execute(Bridge self, Object[] args) throws Exception; } diff --git a/rskj-core/src/main/java/co/rsk/peg/BridgeSerializationUtils.java b/rskj-core/src/main/java/co/rsk/peg/BridgeSerializationUtils.java index fa53f294ea9..2b339001815 100644 --- a/rskj-core/src/main/java/co/rsk/peg/BridgeSerializationUtils.java +++ b/rskj-core/src/main/java/co/rsk/peg/BridgeSerializationUtils.java @@ -21,9 +21,11 @@ import co.rsk.bitcoinj.core.*; import co.rsk.core.RskAddress; import co.rsk.crypto.Keccak256; +import co.rsk.peg.whitelist.OneOffWhiteListEntry; +import co.rsk.peg.whitelist.UnlimitedWhiteListEntry; import org.ethereum.util.RLP; import org.ethereum.util.RLPList; -import org.spongycastle.util.BigIntegers; +import org.bouncycastle.util.BigIntegers; import javax.annotation.Nullable; import java.io.ByteArrayInputStream; @@ -35,6 +37,7 @@ import java.time.Instant; import java.util.*; import java.util.stream.Collectors; +import org.apache.commons.lang3.tuple.Pair; /** * Created by mario on 20/04/17. @@ -293,20 +296,43 @@ public static ABICallElection deserializeElection(byte[] data, AddressBasedAutho return new ABICallElection(authorizer, votes); } - public static byte[] serializeLockWhitelist(LockWhitelist whitelist) { - List
whitelistAddresses = whitelist.getAddresses(); - int serializationSize = whitelistAddresses.size() * 2 + 1; + /** + * Serializes the data stored in the Tuple. + * @param data data MUST be composed of a list of {@link co.rsk.peg.whitelist.OneOffWhiteListEntry} and the value of disableBlockHeight obtained from {@link co.rsk.peg.whitelist.LockWhitelist} + * @return the serialized data + */ + public static byte[] serializeOneOffLockWhitelist(Pair, Integer> data) { + List entries = data.getLeft(); + Integer disableBlockHeight = data.getRight(); + int serializationSize = entries.size() * 2 + 1; byte[][] serializedLockWhitelist = new byte[serializationSize][]; - for (int i = 0; i < whitelistAddresses.size(); i++) { - Address address = whitelistAddresses.get(i); - serializedLockWhitelist[2 * i] = RLP.encodeElement(address.getHash160()); - serializedLockWhitelist[2 * i + 1] = RLP.encodeBigInteger(BigInteger.valueOf(whitelist.getMaxTransferValue(address).longValue())); + for (int i = 0; i < entries.size(); i++) { + OneOffWhiteListEntry entry = entries.get(i); + serializedLockWhitelist[2 * i] = RLP.encodeElement(entry.address().getHash160()); + serializedLockWhitelist[2 * i + 1] = RLP.encodeBigInteger(BigInteger.valueOf(entry.maxTransferValue().longValue())); } - serializedLockWhitelist[serializationSize - 1] = RLP.encodeBigInteger(BigInteger.valueOf(whitelist.getDisableBlockHeight())); + serializedLockWhitelist[serializationSize - 1] = RLP.encodeBigInteger(BigInteger.valueOf(disableBlockHeight)); return RLP.encodeList(serializedLockWhitelist); } - public static LockWhitelist deserializeLockWhitelist(byte[] data, NetworkParameters parameters) { + /** + * Serializes the provided list of {@link co.rsk.peg.whitelist.UnlimitedWhiteListEntry} + * @param entries + * @return the serialized data + */ + public static byte[] serializeUnlimitedLockWhitelist(List entries) { + int serializationSize = entries.size(); + byte[][] serializedLockWhitelist = new byte[serializationSize][]; + for (int i = 0; i < entries.size(); i++) { + serializedLockWhitelist[i] = RLP.encodeElement(entries.get(i).address().getHash160()); + } + return RLP.encodeList(serializedLockWhitelist); + } + + public static Pair, Integer> deserializeOneOffLockWhitelistAndDisableBlockHeight(byte[] data, NetworkParameters parameters) { + if (data == null || data.length == 0) { + return null; + } RLPList rlpList = (RLPList)RLP.decode2(data).get(0); int serializedAddressesSize = rlpList.size() - 1; @@ -315,17 +341,34 @@ public static LockWhitelist deserializeLockWhitelist(byte[] data, NetworkParamet throw new RuntimeException("deserializeLockWhitelist: expected an even number of addresses, but odd given"); } - Map whitelist = new HashMap<>(serializedAddressesSize / 2); + HashMap entries = new HashMap<>(serializedAddressesSize / 2); for (int i = 0; i < serializedAddressesSize; i = i + 2) { byte[] hash160 = rlpList.get(i).getRLPData(); - byte[] maxValueData = rlpList.get(i + 1).getRLPData(); - whitelist.put( - new Address(parameters, hash160), - Coin.valueOf(safeToBigInteger(maxValueData).longValueExact()) - ); + byte[] maxTransferValueData = rlpList.get(i + 1).getRLPData(); + Address address = new Address(parameters, hash160); + entries.put(address, new OneOffWhiteListEntry(address, Coin.valueOf(safeToBigInteger(maxTransferValueData).longValueExact()))); } int disableBlockHeight = safeToBigInteger(rlpList.get(serializedAddressesSize).getRLPData()).intValueExact(); - return new LockWhitelist(whitelist, disableBlockHeight); + return Pair.of(entries, disableBlockHeight); + } + + public static Map deserializeUnlimitedLockWhitelistEntries(byte[] data, NetworkParameters parameters) { + if (data == null) { + return new HashMap<>(); + } + + RLPList unlimitedWhitelistEntriesRlpList = (RLPList)RLP.decode2(data).get(0); + int unlimitedWhitelistEntriesSerializedAddressesSize = unlimitedWhitelistEntriesRlpList.size(); + + Map entries = new HashMap<>(unlimitedWhitelistEntriesSerializedAddressesSize); + + for (int j = 0; j < unlimitedWhitelistEntriesSerializedAddressesSize; j++) { + byte[] hash160 = unlimitedWhitelistEntriesRlpList.get(j).getRLPData(); + Address address = new Address(parameters, hash160); + entries.put(address, new UnlimitedWhiteListEntry(address)); + } + + return entries; } private static BigInteger safeToBigInteger(byte[] data) { diff --git a/rskj-core/src/main/java/co/rsk/peg/BridgeStorageConfiguration.java b/rskj-core/src/main/java/co/rsk/peg/BridgeStorageConfiguration.java new file mode 100644 index 00000000000..480c2802485 --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/peg/BridgeStorageConfiguration.java @@ -0,0 +1,36 @@ +/* + * This file is part of RskJ + * Copyright (C) 2018 RSK Labs Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ +package co.rsk.peg; + +import org.ethereum.config.BlockchainConfig; + +public class BridgeStorageConfiguration { + private final boolean isUnlimitedWhitelistEnabled; + + public BridgeStorageConfiguration(boolean isUnlimitedWhitelistEnabled) { + this.isUnlimitedWhitelistEnabled = isUnlimitedWhitelistEnabled; + } + + public boolean getUnlimitedWhitelistEnabled() { + return isUnlimitedWhitelistEnabled; + } + + public static BridgeStorageConfiguration fromBlockchainConfig(BlockchainConfig config) { + return new BridgeStorageConfiguration(config.isRskip87()); + } +} diff --git a/rskj-core/src/main/java/co/rsk/peg/BridgeStorageProvider.java b/rskj-core/src/main/java/co/rsk/peg/BridgeStorageProvider.java index e86eeed3369..d836e7010ff 100644 --- a/rskj-core/src/main/java/co/rsk/peg/BridgeStorageProvider.java +++ b/rskj-core/src/main/java/co/rsk/peg/BridgeStorageProvider.java @@ -22,8 +22,12 @@ import co.rsk.config.BridgeConstants; import co.rsk.core.RskAddress; import co.rsk.crypto.Keccak256; +import co.rsk.peg.whitelist.LockWhitelist; +import co.rsk.peg.whitelist.LockWhitelistEntry; +import co.rsk.peg.whitelist.OneOffWhiteListEntry; +import co.rsk.peg.whitelist.UnlimitedWhiteListEntry; +import org.apache.commons.lang3.tuple.Pair; import org.ethereum.core.Repository; -import org.ethereum.rpc.TypeConverter; import org.ethereum.vm.DataWord; import java.io.IOException; @@ -39,24 +43,26 @@ * @author Oscar Guindzberg */ public class BridgeStorageProvider { - private static final DataWord NEW_FEDERATION_BTC_UTXOS_KEY = new DataWord(TypeConverter.stringToByteArray("newFederationBtcUTXOs")); - private static final DataWord OLD_FEDERATION_BTC_UTXOS_KEY = new DataWord(TypeConverter.stringToByteArray("oldFederationBtcUTXOs")); - private static final DataWord BTC_TX_HASHES_ALREADY_PROCESSED_KEY = new DataWord(TypeConverter.stringToByteArray("btcTxHashesAP")); - private static final DataWord RELEASE_REQUEST_QUEUE = new DataWord(TypeConverter.stringToByteArray("releaseRequestQueue")); - private static final DataWord RELEASE_TX_SET = new DataWord(TypeConverter.stringToByteArray("releaseTransactionSet")); - private static final DataWord RSK_TXS_WAITING_FOR_SIGNATURES_KEY = new DataWord(TypeConverter.stringToByteArray("rskTxsWaitingFS")); - private static final DataWord NEW_FEDERATION_KEY = new DataWord(TypeConverter.stringToByteArray("newFederation")); - private static final DataWord OLD_FEDERATION_KEY = new DataWord(TypeConverter.stringToByteArray("oldFederation")); - private static final DataWord PENDING_FEDERATION_KEY = new DataWord(TypeConverter.stringToByteArray("pendingFederation")); - private static final DataWord FEDERATION_ELECTION_KEY = new DataWord(TypeConverter.stringToByteArray("federationElection")); - private static final DataWord LOCK_WHITELIST_KEY = new DataWord(TypeConverter.stringToByteArray("lockWhitelist")); - private static final DataWord FEE_PER_KB_KEY = new DataWord(TypeConverter.stringToByteArray("feePerKb")); - private static final DataWord FEE_PER_KB_ELECTION_KEY = new DataWord(TypeConverter.stringToByteArray("feePerKbElection")); + private static final DataWord NEW_FEDERATION_BTC_UTXOS_KEY = DataWord.fromString("newFederationBtcUTXOs"); + private static final DataWord OLD_FEDERATION_BTC_UTXOS_KEY = DataWord.fromString("oldFederationBtcUTXOs"); + private static final DataWord BTC_TX_HASHES_ALREADY_PROCESSED_KEY = DataWord.fromString("btcTxHashesAP"); + private static final DataWord RELEASE_REQUEST_QUEUE = DataWord.fromString("releaseRequestQueue"); + private static final DataWord RELEASE_TX_SET = DataWord.fromString("releaseTransactionSet"); + private static final DataWord RSK_TXS_WAITING_FOR_SIGNATURES_KEY = DataWord.fromString("rskTxsWaitingFS"); + private static final DataWord NEW_FEDERATION_KEY = DataWord.fromString("newFederation"); + private static final DataWord OLD_FEDERATION_KEY = DataWord.fromString("oldFederation"); + private static final DataWord PENDING_FEDERATION_KEY = DataWord.fromString("pendingFederation"); + private static final DataWord FEDERATION_ELECTION_KEY = DataWord.fromString("federationElection"); + private static final DataWord LOCK_ONE_OFF_WHITELIST_KEY = DataWord.fromString("lockWhitelist"); + private static final DataWord LOCK_UNLIMITED_WHITELIST_KEY = DataWord.fromString("unlimitedLockWhitelist"); + private static final DataWord FEE_PER_KB_KEY = DataWord.fromString("feePerKb"); + private static final DataWord FEE_PER_KB_ELECTION_KEY = DataWord.fromString("feePerKbElection"); private final Repository repository; private final RskAddress contractAddress; private final NetworkParameters networkParameters; private final Context btcContext; + private final BridgeStorageConfiguration bridgeStorageConfiguration; private Map btcTxHashesAlreadyProcessed; @@ -86,11 +92,12 @@ public class BridgeStorageProvider { private Coin feePerKb; private ABICallElection feePerKbElection; - public BridgeStorageProvider(Repository repository, RskAddress contractAddress, BridgeConstants bridgeConstants) { + public BridgeStorageProvider(Repository repository, RskAddress contractAddress, BridgeConstants bridgeConstants, BridgeStorageConfiguration bridgeStorageConfiguration) { this.repository = repository; this.contractAddress = contractAddress; this.networkParameters = bridgeConstants.getBtcParams(); this.btcContext = new Context(networkParameters); + this.bridgeStorageConfiguration = bridgeStorageConfiguration; } public List getNewFederationBtcUTXOs() throws IOException { @@ -318,7 +325,13 @@ public void saveLockWhitelist() { return; } - safeSaveToRepository(LOCK_WHITELIST_KEY, lockWhitelist, BridgeSerializationUtils::serializeLockWhitelist); + List oneOffEntries = lockWhitelist.getAll(OneOffWhiteListEntry.class); + safeSaveToRepository(LOCK_ONE_OFF_WHITELIST_KEY, Pair.of(oneOffEntries, lockWhitelist.getDisableBlockHeight()), BridgeSerializationUtils::serializeOneOffLockWhitelist); + + if (this.bridgeStorageConfiguration.getUnlimitedWhitelistEnabled()) { + List unlimitedEntries = lockWhitelist.getAll(UnlimitedWhiteListEntry.class); + safeSaveToRepository(LOCK_UNLIMITED_WHITELIST_KEY, unlimitedEntries, BridgeSerializationUtils::serializeUnlimitedLockWhitelist); + } } public LockWhitelist getLockWhitelist() { @@ -326,11 +339,24 @@ public LockWhitelist getLockWhitelist() { return lockWhitelist; } - lockWhitelist = safeGetFromRepository(LOCK_WHITELIST_KEY, - data -> (data == null)? - new LockWhitelist(new HashMap<>()) : - BridgeSerializationUtils.deserializeLockWhitelist(data, btcContext.getParams()) - ); + Pair, Integer> oneOffWhitelistAndDisableBlockHeightData = + safeGetFromRepository(LOCK_ONE_OFF_WHITELIST_KEY, + data -> BridgeSerializationUtils.deserializeOneOffLockWhitelistAndDisableBlockHeight(data, btcContext.getParams())); + if (oneOffWhitelistAndDisableBlockHeightData == null) { + lockWhitelist = new LockWhitelist(new HashMap<>()); + return lockWhitelist; + } + + Map whitelistedAddresses = new HashMap<>(); + + whitelistedAddresses.putAll(oneOffWhitelistAndDisableBlockHeightData.getLeft()); + + if (this.bridgeStorageConfiguration.getUnlimitedWhitelistEnabled()) { + whitelistedAddresses.putAll(safeGetFromRepository(LOCK_UNLIMITED_WHITELIST_KEY, + data -> BridgeSerializationUtils.deserializeUnlimitedLockWhitelistEntries(data, btcContext.getParams()))); + } + + lockWhitelist = new LockWhitelist(whitelistedAddresses, oneOffWhitelistAndDisableBlockHeightData.getRight()); return lockWhitelist; } diff --git a/rskj-core/src/main/java/co/rsk/peg/BridgeSupport.java b/rskj-core/src/main/java/co/rsk/peg/BridgeSupport.java index 0b8a3feccf4..1a487f0c96c 100644 --- a/rskj-core/src/main/java/co/rsk/peg/BridgeSupport.java +++ b/rskj-core/src/main/java/co/rsk/peg/BridgeSupport.java @@ -31,6 +31,10 @@ import co.rsk.core.RskAddress; import co.rsk.crypto.Keccak256; import co.rsk.panic.PanicProcessor; +import co.rsk.peg.whitelist.LockWhitelist; +import co.rsk.peg.whitelist.LockWhitelistEntry; +import co.rsk.peg.whitelist.OneOffWhiteListEntry; +import co.rsk.peg.whitelist.UnlimitedWhiteListEntry; import co.rsk.peg.utils.BridgeEventLogger; import co.rsk.peg.utils.BtcTransactionFormatUtils; import co.rsk.peg.utils.PartialMerkleTreeFormatUtils; @@ -43,7 +47,7 @@ import org.ethereum.vm.program.Program; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.Hex; import javax.annotation.Nullable; import java.io.IOException; @@ -62,11 +66,17 @@ public class BridgeSupport { public static final Integer FEDERATION_CHANGE_GENERIC_ERROR_CODE = -10; public static final Integer LOCK_WHITELIST_GENERIC_ERROR_CODE = -10; + public static final Integer LOCK_WHITELIST_INVALID_ADDRESS_FORMAT_ERROR_CODE = -2; + public static final Integer LOCK_WHITELIST_ALREADY_EXISTS_ERROR_CODE = -1; + public static final Integer LOCK_WHITELIST_UNKNOWN_ERROR_CODE = 0; + public static final Integer LOCK_WHITELIST_SUCCESS_CODE = 1; public static final Integer FEE_PER_KB_GENERIC_ERROR_CODE = -10; private static final Logger logger = LoggerFactory.getLogger("BridgeSupport"); private static final PanicProcessor panicProcessor = new PanicProcessor(); + private static final String INVALID_ADDRESS_FORMAT_MESSAGE = "invalid address format"; + private final List FEDERATION_CHANGE_FUNCTIONS = Collections.unmodifiableList(Arrays.asList( "create", "add", @@ -81,10 +91,10 @@ public class BridgeSupport { private final FederationSupport federationSupport; - private Context btcContext; + private final Context btcContext; private BtcBlockstoreWithCache btcBlockStore; private BtcBlockChain btcBlockChain; - private org.ethereum.core.Block rskExecutionBlock; + private final org.ethereum.core.Block rskExecutionBlock; // Used by unit tests public BridgeSupport( @@ -102,11 +112,11 @@ public BridgeSupport( executionBlock, config, bridgeConstants, - eventLogger + eventLogger, + new Context(bridgeConstants.getBtcParams()), + btcBlockStore, + btcBlockChain ); - this.btcContext = new Context(this.bridgeConstants.getBtcParams()); - this.btcBlockStore = btcBlockStore; - this.btcBlockChain = btcBlockChain; } // Used by bridge @@ -117,15 +127,20 @@ public BridgeSupport( RskAddress contractAddress, Block rskExecutionBlock) { this( - config, repository, - eventLogger, new BridgeStorageProvider( repository, contractAddress, - config.getBlockchainConfig().getCommonConstants().getBridgeConstants() + config.getBlockchainConfig().getCommonConstants().getBridgeConstants(), + BridgeStorageConfiguration.fromBlockchainConfig(config.getBlockchainConfig().getConfigForBlock(rskExecutionBlock.getNumber())) ), - rskExecutionBlock + rskExecutionBlock, + config, + config.getBlockchainConfig().getCommonConstants().getBridgeConstants(), + eventLogger, + new Context(config.getBlockchainConfig().getCommonConstants().getBridgeConstants().getBtcParams()), + null, + null ); } @@ -141,27 +156,58 @@ public BridgeSupport( rskExecutionBlock, config, config.getBlockchainConfig().getCommonConstants().getBridgeConstants(), - eventLogger + eventLogger, + new Context(config.getBlockchainConfig().getCommonConstants().getBridgeConstants().getBtcParams()), + null, + null ); - - this.btcContext = this.buildBtcContext(); } - // this constructor has all common parameters, mostly dependencies that aren't instantiated here private BridgeSupport( Repository repository, BridgeStorageProvider provider, Block executionBlock, RskSystemProperties config, BridgeConstants bridgeConstants, - BridgeEventLogger eventLogger) { + BridgeEventLogger eventLogger, + Context btcContext, + BtcBlockstoreWithCache btcBlockStore, + BtcBlockChain btcBlockChain) { + this( + repository, + provider, + executionBlock, + config, + bridgeConstants, + eventLogger, + btcContext, + new FederationSupport(provider, bridgeConstants, executionBlock), + btcBlockStore, + btcBlockChain + ); + } + + public BridgeSupport( + Repository repository, + BridgeStorageProvider provider, + Block executionBlock, + RskSystemProperties config, + BridgeConstants bridgeConstants, + BridgeEventLogger eventLogger, + Context btcContext, + FederationSupport federationSupport, + BtcBlockstoreWithCache btcBlockStore, + BtcBlockChain btcBlockChain) { this.rskRepository = repository; this.provider = provider; this.rskExecutionBlock = executionBlock; this.config = config; this.bridgeConstants = bridgeConstants; this.eventLogger = eventLogger; - this.federationSupport = new FederationSupport(provider, bridgeConstants, executionBlock); + this.btcContext = btcContext; + this.federationSupport = federationSupport; + this.btcBlockStore = btcBlockStore; + this.btcBlockChain = btcBlockChain; } private RepositoryBlockStore buildRepositoryBlockStore() throws BlockStoreException, IOException { @@ -354,7 +400,7 @@ public void registerBtcTransaction(Transaction rskTx, byte[] btcTxSerialized, in boolean locked = true; - Federation federation = getActiveFederation(); + Federation activeFederation = getActiveFederation(); // Specific code for lock/release/none txs if (BridgeUtils.isLockTx(btcTx, getLiveFederations(), btcContext, bridgeConstants)) { logger.debug("This is a lock tx {}", btcTx); @@ -432,9 +478,10 @@ public void registerBtcTransaction(Transaction rskTx, byte[] btcTxSerialized, in sender, co.rsk.core.Coin.fromBitcoin(totalAmount) ); - lockWhitelist.remove(senderBtcAddress); + // Consume this whitelisted address + lockWhitelist.consume(senderBtcAddress); } - } else if (BridgeUtils.isReleaseTx(btcTx, federation, bridgeConstants)) { + } else if (BridgeUtils.isReleaseTx(btcTx, getLiveFederations())) { logger.debug("This is a release tx {}", btcTx); // do-nothing // We could call removeUsedUTXOs(btcTx) here, but we decided to not do that. @@ -446,7 +493,7 @@ public void registerBtcTransaction(Transaction rskTx, byte[] btcTxSerialized, in // When is not guaranteed to be called in the chronological order, so a Federator can inform // b) In prod: Federator created a tx manually or the federation was compromised and some utxos were spent. Better not try to spend them. // Open problem: For performance removeUsedUTXOs() just removes 1 utxo - } else if (BridgeUtils.isMigrationTx(btcTx, getActiveFederation(), getRetiringFederation(), btcContext, bridgeConstants)) { + } else if (BridgeUtils.isMigrationTx(btcTx, activeFederation, getRetiringFederation(), btcContext, bridgeConstants)) { logger.debug("This is a migration tx {}", btcTx); } else { logger.warn("This is not a lock, a release nor a migration tx {}", btcTx); @@ -496,10 +543,9 @@ private void saveNewUTXOs(BtcTransaction btcTx) throws IOException { * @throws IOException */ public void releaseBtc(Transaction rskTx) throws IOException { - byte[] senderCode = rskRepository.getCode(rskTx.getSender()); //as we can't send btc from contracts we want to send them back to the sender - if (senderCode != null && senderCode.length > 0) { + if (BridgeUtils.isContractTx(rskTx)) { logger.trace("Contract {} tried to release funds. Release is just allowed from standard accounts.", rskTx); throw new Program.OutOfGasException("Contract calling releaseBTC"); } @@ -997,9 +1043,19 @@ public int getBtcBlockchainBestChainHeight() throws IOException { } /** - * Returns an array of block hashes known by the bridge contract. Federators can use this to find what is the latest block in the mainchain the bridge has. + * Returns the bitcoin blockchain initial stored block height + */ + public int getBtcBlockchainInitialBlockHeight() throws IOException { + return getLowestBlock().getHeight(); + } + + /** + * @deprecated + * Returns an array of block hashes known by the bridge contract. + * Federators can use this to find what is the latest block in the mainchain the bridge has. * @return a List of bitcoin block hashes */ + @Deprecated public List getBtcBlockchainBlockLocator() throws IOException { StoredBlock initialBtcStoredBlock = this.getLowestBlock(); final int maxHashesToInform = 100; @@ -1034,6 +1090,27 @@ public List getBtcBlockchainBlockLocator() throws IOException { return blockLocator; } + public Sha256Hash getBtcBlockchainBlockHashAtDepth(int depth) throws BlockStoreException, IOException { + Context.propagate(btcContext); + this.ensureBtcBlockChain(); + + StoredBlock head = btcBlockChain.getChainHead(); + + int maxDepth = head.getHeight() - getLowestBlock().getHeight(); + + if (depth < 0 || depth > maxDepth) { + throw new IndexOutOfBoundsException(String.format("Depth must be between 0 and %d", maxDepth)); + } + + int currentDepth = 0; + StoredBlock current = head; + while (currentDepth < depth) { + current = current.getPrev(btcBlockStore); + currentDepth++; + } + return current.getHeader().getHash(); + } + private StoredBlock getPrevBlockAtHeight(StoredBlock cursor, int height) throws BlockStoreException { if (cursor.getHeight() == height) { return cursor; @@ -1561,14 +1638,29 @@ public Integer getLockWhitelistSize() { * @param index the index at which to get the address * @return the base58-encoded address stored at the given index, or null if index is out of bounds */ - public String getLockWhitelistAddress(int index) { - List
addresses = provider.getLockWhitelist().getAddresses(); + public LockWhitelistEntry getLockWhitelistEntryByIndex(int index) { + List entries = provider.getLockWhitelist().getAll(); - if (index < 0 || index >= addresses.size()) { + if (index < 0 || index >= entries.size()) { return null; } - return addresses.get(index).toBase58(); + return entries.get(index); + } + + /** + * + * @param addressBase58 + * @return + */ + public LockWhitelistEntry getLockWhitelistEntryByAddress(String addressBase58) { + try { + Address address = getParsedAddress(addressBase58); + return provider.getLockWhitelist().get(address); + } catch (AddressFormatException e) { + logger.warn(INVALID_ADDRESS_FORMAT_MESSAGE, e); + return null; + } } /** @@ -1581,7 +1673,28 @@ public String getLockWhitelistAddress(int index) { * in the whitelist, -2 if address is invalid * LOCK_WHITELIST_GENERIC_ERROR_CODE otherwise. */ - public Integer addLockWhitelistAddress(Transaction tx, String addressBase58, BigInteger maxTransferValue) { + public Integer addOneOffLockWhitelistAddress(Transaction tx, String addressBase58, BigInteger maxTransferValue) { + try { + Address address = getParsedAddress(addressBase58); + Coin maxTransferValueCoin = Coin.valueOf(maxTransferValue.longValueExact()); + return this.addLockWhitelistAddress(tx, new OneOffWhiteListEntry(address, maxTransferValueCoin)); + } catch (AddressFormatException e) { + logger.warn(INVALID_ADDRESS_FORMAT_MESSAGE, e); + return LOCK_WHITELIST_INVALID_ADDRESS_FORMAT_ERROR_CODE; + } + } + + public Integer addUnlimitedLockWhitelistAddress(Transaction tx, String addressBase58) { + try { + Address address = getParsedAddress(addressBase58); + return this.addLockWhitelistAddress(tx, new UnlimitedWhiteListEntry(address)); + } catch (AddressFormatException e) { + logger.warn(INVALID_ADDRESS_FORMAT_MESSAGE, e); + return LOCK_WHITELIST_INVALID_ADDRESS_FORMAT_ERROR_CODE; + } + } + + private Integer addLockWhitelistAddress(Transaction tx, LockWhitelistEntry entry) { if (!isLockWhitelistChangeAuthorized(tx)) { return LOCK_WHITELIST_GENERIC_ERROR_CODE; } @@ -1589,19 +1702,15 @@ public Integer addLockWhitelistAddress(Transaction tx, String addressBase58, Big LockWhitelist whitelist = provider.getLockWhitelist(); try { - Address address = Address.fromBase58(btcContext.getParams(), addressBase58); - - if (whitelist.isWhitelisted(address)) { - return -1; + if (whitelist.isWhitelisted(entry.address())) { + return LOCK_WHITELIST_ALREADY_EXISTS_ERROR_CODE; } - whitelist.put(address, Coin.valueOf(maxTransferValue.longValueExact())); - return 1; - } catch (AddressFormatException e) { - return -2; + whitelist.put(entry.address(), entry); + return LOCK_WHITELIST_SUCCESS_CODE; } catch (Exception e) { - logger.error("Unexpected error in addLockWhitelistAddress: {}", e.getMessage()); + logger.error("Unexpected error in addLockWhitelistAddress: {}", e); panicProcessor.panic("lock-whitelist", e.getMessage()); - return 0; + return LOCK_WHITELIST_UNKNOWN_ERROR_CODE; } } @@ -1628,7 +1737,7 @@ public Integer removeLockWhitelistAddress(Transaction tx, String addressBase58) LockWhitelist whitelist = provider.getLockWhitelist(); try { - Address address = Address.fromBase58(btcContext.getParams(), addressBase58); + Address address = getParsedAddress(addressBase58); if (!whitelist.remove(address)) { return -1; @@ -1790,10 +1899,6 @@ private Pair> createMigrationTransaction(Wallet origi } } - private Context buildBtcContext() { - return new Context(this.bridgeConstants.getBtcParams()); - } - // Make sure the local bitcoin blockchain is instantiated private void ensureBtcBlockChain() throws IOException { this.ensureBtcBlockStore(); @@ -1817,5 +1922,9 @@ private void ensureBtcBlockStore() throws IOException { } } } + + private Address getParsedAddress(String base58Address) throws AddressFormatException { + return Address.fromBase58(btcContext.getParams(), base58Address); + } } diff --git a/rskj-core/src/main/java/co/rsk/peg/BridgeUtils.java b/rskj-core/src/main/java/co/rsk/peg/BridgeUtils.java index 30aa39951bc..46c8857117f 100644 --- a/rskj-core/src/main/java/co/rsk/peg/BridgeUtils.java +++ b/rskj-core/src/main/java/co/rsk/peg/BridgeUtils.java @@ -33,10 +33,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; /** * @author Oscar Guindzberg @@ -47,7 +44,7 @@ public class BridgeUtils { // power of 2 size that contains enough hashes to handle one year of hashes private static final int MAX_MAP_PARENTS_SIZE = 65535; - private static Map parentMap = new MaxSizeHashMap<>(MAX_MAP_PARENTS_SIZE); + private static Map parentMap = new MaxSizeHashMap<>(MAX_MAP_PARENTS_SIZE, false); public static StoredBlock getStoredBlockAtHeight(BtcBlockstoreWithCache blockStore, int height) throws BlockStoreException { StoredBlock storedBlock = blockStore.getChainHead(); @@ -157,6 +154,15 @@ public static Optional