diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 04c935a80ae..0cf43352580 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -46,12 +46,12 @@ - - - + + + - - + + diff --git a/rskj-core/build.gradle b/rskj-core/build.gradle index c103199df45..ff9496bf1b3 100644 --- a/rskj-core/build.gradle +++ b/rskj-core/build.gradle @@ -123,7 +123,7 @@ ext { jaxwsRtVer : '2.3.5', picocliVer : '4.6.3', - bitcoinjThinVer: '0.14.4-rsk-14', + bitcoinjThinVer: '0.14.4-rsk-15', rskjNativeVer: '1.3.0', ] diff --git a/rskj-core/src/main/java/co/rsk/core/types/bytes/ByteSequence.java b/rskj-core/src/main/java/co/rsk/core/types/bytes/ByteSequence.java new file mode 100644 index 00000000000..a771bbd200c --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/core/types/bytes/ByteSequence.java @@ -0,0 +1,49 @@ +/* + * This file is part of RskJ + * Copyright (C) 2024 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.core.types.bytes; + +/** + * {@link ByteSequence} is the most basic interface that represents read-only sequence of byte values. + */ +public interface ByteSequence { + + /** + * Returns the length of this byte sequence. + * + * @return the length of the sequence of bytes represented by this + * object. + */ + int length(); + + /** + * Returns the {@code byte} value at the + * specified index. An index ranges from {@code 0} to + * {@code length() - 1}. The first {@code byte} value of the sequence + * is at index {@code 0}, the next at index {@code 1}, + * and so on, as for array indexing. + * + * @param index the index of the {@code byte} value. + * @return the {@code byte} value at the specified index of this array. + * The first {@code byte} value is at index {@code 0}. + * @exception IndexOutOfBoundsException if the {@code index} + * argument is negative or not less than the length of this + * array. + */ + byte byteAt(int index); +} diff --git a/rskj-core/src/main/java/co/rsk/core/types/bytes/Bytes.java b/rskj-core/src/main/java/co/rsk/core/types/bytes/Bytes.java new file mode 100644 index 00000000000..ce988888068 --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/core/types/bytes/Bytes.java @@ -0,0 +1,167 @@ +/* + * This file is part of RskJ + * Copyright (C) 2024 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.core.types.bytes; + +import org.ethereum.util.ByteUtil; +import org.ethereum.util.FastByteComparisons; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.Objects; + +/** + * A {@link Bytes} is a readable sequence of byte values. This + * interface provides uniform, read-only access to many different kinds of + * byte sequences. + * + *

This interface does not refine the general contracts of the {@link + * java.lang.Object#equals(java.lang.Object) equals} and {@link + * java.lang.Object#hashCode() hashCode} methods. The result of comparing two + * objects that implement Bytes is therefore, in general, + * undefined. Each object may be implemented by a different class, and there + * is no guarantee that each class will be capable of testing its instances + * for equality with those of the other. It is therefore inappropriate to use + * arbitrary Bytes instances as elements in a set or as keys in + * a map.

+ */ +public interface Bytes extends BytesSlice { + + /** + * Returns an instance of the {@link Bytes} interface, which represents {@code unsafeByteArray}. + * + * @return the instance of the {@link Bytes} interface that wraps a provided byte array. + */ + static Bytes of(@Nullable byte[] unsafeByteArray) { + if (unsafeByteArray == null) { + return null; + } + return new BytesImpl(unsafeByteArray); + } + + /** + * A helper method for printing "nullable" byte arrays. + * + * @return {@code valueIfNull}, if {@code byteArray} is {@code null}. Otherwise - {@code Bytes.of(byteArray).toPrintableString()}. + */ + static String toPrintableString(@Nullable byte[] byteArray, @Nullable String valueIfNull) { + if (byteArray == null) { + return valueIfNull; + } + return of(byteArray).toPrintableString(); + } + + /** + * A helper method for printing "nullable" byte arrays. + * + * @return {@code ""}, if {@code byteArray} is {@code null}. Otherwise - {@code Bytes.of(byteArray).toPrintableString()}. + */ + @Nonnull + static String toPrintableString(@Nullable byte[] byteArray) { + return toPrintableString(byteArray, ""); + } + + /** + * A helper method for extracting "unsafe" underlying byte array from the {@code bytes} instance. + * + * @return {@code null}, if {@code bytes} is {@code null}. Otherwise - {@code bytes.asUnsafeByteArray()}. + */ + @Nullable + static byte[] asUnsafeByteArray(@Nullable Bytes bytes) { + if (bytes == null) { + return null; + } + return bytes.asUnsafeByteArray(); + } + + static boolean equalBytes(Bytes b1, Bytes b2) { + if (b1 == null && b2 == null) { + return true; + } + if (b1 == null || b2 == null) { + return false; + } + return FastByteComparisons.equalBytes(b1.asUnsafeByteArray(), b2.asUnsafeByteArray()); + } + + /** + * Returns an underlying byte array, which is backing this instance. Any mutations that are being done with the bytes + * of returned array will have direct impact on the byte array that is being wrapped by this instance. + * + * @return the wrapped by this instance byte array. + */ + byte[] asUnsafeByteArray(); +} + +/** + * The {@link BytesImpl} class represents a read-only sequence of byte values. + *

+ * Instances of the {@link BytesImpl} class are constant; their values cannot be changed after they + * are created via the methods that this class provides. But a {@code byteArray} instance itself provided in the constructor + * is mutable and can be modified outside the class. It's generally a bad idea to mutate a byte array that's being wrapped + * by an instance of this class, as the idea is to make a byte sequence immutable, which is not the case with the Java + * built-in {@code byte[]} type. + *

+ * Because {@link BytesImpl} objects are immutable they can be safely used by multiple Java threads, if the wrapped array + * is not being referenced and modified outside. + */ +class BytesImpl implements Bytes { + + private final byte[] byteArray; + + BytesImpl(@Nonnull byte[] unsafeByteArray) { + this.byteArray = Objects.requireNonNull(unsafeByteArray); + } + + @Override + public int length() { + return byteArray.length; + } + + @Override + public byte byteAt(int index) { + return byteArray[index]; + } + + @Override + public byte[] copyArrayOfRange(int from, int to) { + return Arrays.copyOfRange(byteArray, from, to); + } + + @Override + public String toHexString() { + return ByteUtil.toHexString(byteArray); + } + + @Override + public String toHexString(int off, int length) { + return ByteUtil.toHexString(byteArray, off, length); + } + + @Nonnull + @Override + public byte[] asUnsafeByteArray() { + return byteArray; + } + + @Override + public String toString() { + return toPrintableString(); + } +} diff --git a/rskj-core/src/main/java/co/rsk/core/types/bytes/BytesSlice.java b/rskj-core/src/main/java/co/rsk/core/types/bytes/BytesSlice.java new file mode 100644 index 00000000000..5210ff8eea0 --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/core/types/bytes/BytesSlice.java @@ -0,0 +1,131 @@ +/* + * This file is part of RskJ + * Copyright (C) 2024 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.core.types.bytes; + +/** + * A {@link BytesSlice} is a subsequence of bytes backed by another broader byte sequence. + */ +public interface BytesSlice extends HexPrintableBytes { + + /** + * Copies the specified range of the specified array into a new array. + * The initial index of the range (from) must lie between zero + * and original.length, inclusive. The value at + * original[from] is placed into the initial element of the copy + * (unless from == original.length or from == to). + * Values from subsequent elements in the original array are placed into + * subsequent elements in the copy. The final index of the range + * (to), which must be greater than or equal to from, + * may be greater than original.length, in which case + * (byte)0 is placed in all elements of the copy whose index is + * greater than or equal to original.length - from. The length + * of the returned array will be to - from. + * + * @param from the initial index of the range to be copied, inclusive + * @param to the final index of the range to be copied, exclusive. + * (This index may lie outside the array.) + * @return a new array containing the specified range from the original array, + * truncated or padded with zeros to obtain the required length + * @throws ArrayIndexOutOfBoundsException if {@code from < 0} + * or {@code from > original.length} + * @throws IllegalArgumentException if from > to + * @throws NullPointerException if original is null + */ + byte[] copyArrayOfRange(int from, int to); + + default byte[] copyArray() { + return copyArrayOfRange(0, length()); + } + + default Bytes copyBytesOfRange(int from, int to) { + return Bytes.of(copyArrayOfRange(from, to)); + } + + default Bytes copyBytes() { + return Bytes.of(copyArrayOfRange(0, length())); + } + + default BytesSlice slice(int from, int to) { + return new BytesSliceImpl(this, from, to); + } +} + +class BytesSliceImpl implements BytesSlice { + + private final BytesSlice originBytes; + + private final int from; + private final int to; + + BytesSliceImpl(BytesSlice originBytes, int from, int to) { + this.originBytes = originBytes; + + if (from < 0) { + throw new IndexOutOfBoundsException(from + " < " + 0); + } + if (from > to) { + throw new IndexOutOfBoundsException(from + " > " + to); + } + if (to > originBytes.length()) { + throw new IndexOutOfBoundsException(to + " > " + "length"); + } + + this.from = from; + this.to = to; + } + + @Override + public int length() { + return to - from; + } + + @Override + public byte byteAt(int index) { + if (index < 0 || index >= length()) { + throw new IndexOutOfBoundsException("invalid index: " + index); + } + return originBytes.byteAt(from + index); + } + + @Override + public byte[] copyArrayOfRange(int from, int to) { + if (from < 0 || from > to || to > length()) { + throw new IndexOutOfBoundsException("invalid 'from' and/or 'to': [" + from + ";" + to + ")"); + } + return originBytes.copyArrayOfRange(this.from + from, this.from + to); + } + + @Override + public String toHexString(int off, int length) { + if (off < 0 || length < 0 || off + length > length()) { + throw new IndexOutOfBoundsException("invalid 'off' and/or 'length': " + off + "; " + length); + } + return originBytes.toHexString(from + off, length); + } + + @Override + public String toHexString() { + return toHexString(0, length()); + } + + @Override + public String toString() { + return toPrintableString(); + } +} diff --git a/rskj-core/src/main/java/co/rsk/core/types/bytes/HexPrintableBytes.java b/rskj-core/src/main/java/co/rsk/core/types/bytes/HexPrintableBytes.java new file mode 100644 index 00000000000..d700c0cc6a6 --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/core/types/bytes/HexPrintableBytes.java @@ -0,0 +1,92 @@ +/* + * This file is part of RskJ + * Copyright (C) 2024 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.core.types.bytes; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Objects; + +/** + * A {@link HexPrintableBytes} is an extension of the {@link PrintableBytes} class with capabilities to + * represent it in hexadecimal format. + */ +public interface HexPrintableBytes extends PrintableBytes { + + Formatter SIMPLE_HEX_FORMATTER = new PrintableBytesHexFormatter(); + Formatter SIMPLE_JSON_HEX_FORMATTER = new PrintableBytesJsonHexFormatter(); + + + @Nullable + static String toHexString(@Nullable HexPrintableBytes bytes, @Nullable String defaultValue) { + if (bytes == null) { + return defaultValue; + } + return bytes.toHexString(); + } + + @Nullable + static String toHexString(@Nullable HexPrintableBytes bytes) { + return toHexString(bytes, null); + } + + default String toPrintableString(@Nonnull Formatter formatter, int off, int length) { + return formatter.toFormattedString(this, off, length); + } + + default String toPrintableString(@Nonnull Formatter formatter) { + return toPrintableString(formatter, 0, length()); + } + + @Override + default String toPrintableString() { + return toPrintableString(SIMPLE_HEX_FORMATTER); + } + + default String toJsonHexFormattedString() { + return toPrintableString(SIMPLE_JSON_HEX_FORMATTER); + } + + String toHexString(int off, int length); + + String toHexString(); +} + +class PrintableBytesHexFormatter implements PrintableBytes.Formatter { + + @Override + public String toFormattedString(@Nonnull HexPrintableBytes printableBytes, int off, int length) { + int bytesLen = Objects.requireNonNull(printableBytes).length(); + if (off + length > bytesLen) { + throw new IndexOutOfBoundsException("invalid 'off' and/or 'length': " + off + "; " + length); + } + + if (length > 32) { + return printableBytes.toHexString(off, 15) + ".." + printableBytes.toHexString(off + length - 15, 15); + } + return printableBytes.toHexString(off, length); + } +} + +class PrintableBytesJsonHexFormatter extends PrintableBytesHexFormatter { + + @Override + public String toFormattedString(@Nonnull HexPrintableBytes printableBytes, int off, int length) { + return "0x" + super.toFormattedString(printableBytes, off, length); + } +} diff --git a/rskj-core/src/main/java/co/rsk/core/types/bytes/PrintableBytes.java b/rskj-core/src/main/java/co/rsk/core/types/bytes/PrintableBytes.java new file mode 100644 index 00000000000..10b21f00c92 --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/core/types/bytes/PrintableBytes.java @@ -0,0 +1,35 @@ +/* + * This file is part of RskJ + * Copyright (C) 2024 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.core.types.bytes; + +import javax.annotation.Nonnull; + +/** + * A {@link PrintableBytes} is a sequence of byte values that + * can be represented as a {@link String} value. + */ +public interface PrintableBytes extends ByteSequence { + + interface Formatter { + String toFormattedString(@Nonnull T printableBytes, int off, int length); + } + + String toPrintableString(); + +} 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 96e2d3a2adc..743a1e5a903 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 @@ -19,13 +19,13 @@ package co.rsk.net.discovery; +import co.rsk.core.types.bytes.Bytes; import co.rsk.net.discovery.message.MessageDecoder; import co.rsk.net.discovery.message.PeerDiscoveryMessage; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.socket.DatagramPacket; import io.netty.handler.codec.MessageToMessageDecoder; -import org.ethereum.util.ByteUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,7 +48,7 @@ public DiscoveryEvent decodeMessage(ChannelHandlerContext ctx, byte[] encoded, I PeerDiscoveryMessage msg = MessageDecoder.decode(encoded); return new DiscoveryEvent(msg, sender); } catch (Exception e) { - logger.error("Exception processing inbound message from {} : {}", ctx.channel().remoteAddress(), ByteUtil.toHexString(encoded), e); + logger.error("Exception processing inbound message from {} : {}", ctx.channel().remoteAddress(), Bytes.of(encoded), e); throw e; } } 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 ab98a03f4db..5aef6eac662 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 @@ -18,6 +18,7 @@ package co.rsk.net.discovery.message; +import co.rsk.core.types.bytes.Bytes; import co.rsk.net.NodeID; import co.rsk.net.discovery.PeerDiscoveryException; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -179,10 +180,10 @@ public DiscoveryMessageType getMessageType() { @Override public String toString() { return new ToStringBuilder(this) - .append("mdc", ByteUtil.toHexString(mdc)) - .append("signature", ByteUtil.toHexString(signature)) - .append("type", ByteUtil.toHexString(type)) - .append("data", ByteUtil.toHexString(data)).toString(); + .append("mdc", Bytes.of(mdc)) + .append("signature", Bytes.of(signature)) + .append("type", Bytes.of(type)) + .append("data", Bytes.of(data)).toString(); } protected String extractMessageId(RLPItem chk) { diff --git a/rskj-core/src/main/java/co/rsk/net/handler/quota/TxQuota.java b/rskj-core/src/main/java/co/rsk/net/handler/quota/TxQuota.java index 5d333737d5f..ac1d0a11a10 100644 --- a/rskj-core/src/main/java/co/rsk/net/handler/quota/TxQuota.java +++ b/rskj-core/src/main/java/co/rsk/net/handler/quota/TxQuota.java @@ -81,11 +81,15 @@ private synchronized boolean acceptVirtualGasConsumption(double virtualGasToCons if (this.availableVirtualGas < virtualGasToConsume) { String acceptanceNote = forcingAcceptance ? "Forcing tx acceptance" : "NOT enough virtualGas"; - logger.warn("{} for blockNumber [{}], sender [{}] and tx [{}]: availableVirtualGas=[{}], virtualGasToConsume=[{}]", acceptanceNote, blockNumber, sender, tx, this.availableVirtualGas, virtualGasToConsume); + if (logger.isWarnEnabled()) { + logger.warn("{} for blockNumber [{}], sender [{}] and tx [{}]: availableVirtualGas=[{}], virtualGasToConsume=[{}]", acceptanceNote, blockNumber, sender, tx.getHash(), this.availableVirtualGas, virtualGasToConsume); + } return false; } - logger.trace("Enough virtualGas for blockNumber [{}], sender [{}] and tx [{}]: availableVirtualGas=[{}], virtualGasToConsume=[{}]", blockNumber, sender, tx, this.availableVirtualGas, virtualGasToConsume); + if (logger.isTraceEnabled()) { + logger.trace("Enough virtualGas for blockNumber [{}], sender [{}] and tx [{}]: availableVirtualGas=[{}], virtualGasToConsume=[{}]", blockNumber, sender, tx.getHash(), this.availableVirtualGas, virtualGasToConsume); + } this.availableVirtualGas -= virtualGasToConsume; return true; } diff --git a/rskj-core/src/main/java/co/rsk/pcc/NativeContract.java b/rskj-core/src/main/java/co/rsk/pcc/NativeContract.java index 45d34ea7526..281d498d66c 100644 --- a/rskj-core/src/main/java/co/rsk/pcc/NativeContract.java +++ b/rskj-core/src/main/java/co/rsk/pcc/NativeContract.java @@ -19,10 +19,10 @@ package co.rsk.pcc; import co.rsk.core.RskAddress; +import co.rsk.core.types.bytes.Bytes; import co.rsk.panic.PanicProcessor; import co.rsk.pcc.exception.NativeContractIllegalArgumentException; import org.ethereum.config.blockchain.upgrades.ActivationConfig; -import org.ethereum.util.ByteUtil; import org.ethereum.vm.PrecompiledContractArgs; import org.ethereum.vm.PrecompiledContracts; import org.ethereum.vm.exception.VMException; @@ -119,7 +119,7 @@ public byte[] execute(byte[] data) throws VMException { // No function found with the given data? => halt! if (!methodWithArguments.isPresent()) { - String errorMessage = String.format("Invalid data given: %s.", ByteUtil.toHexString(data)); + String errorMessage = String.format("Invalid data given: %s.", Bytes.of(data)); logger.info(errorMessage); throw new NativeContractIllegalArgumentException(errorMessage); } @@ -174,7 +174,7 @@ public byte[] execute(byte[] data) throws VMException { private Optional parseData(byte[] data) { if (data != null && (data.length >= 1 && data.length <= 3)) { - logger.warn("Invalid function signature {}.", ByteUtil.toHexString(data)); + logger.warn("Invalid function signature {}.", Bytes.of(data)); return Optional.empty(); } @@ -194,7 +194,7 @@ private Optional parseData(byte[] data) { ).findFirst(); if (!maybeMethod.isPresent()) { - logger.warn("Invalid function signature {}.", ByteUtil.toHexString(encodedSignature)); + logger.warn("Invalid function signature {}.", Bytes.of(encodedSignature)); return Optional.empty(); } @@ -209,7 +209,7 @@ private Optional parseData(byte[] data) { Object[] arguments = method.getFunction().decode(data); return Optional.of(method.new WithArguments(arguments, data)); } catch (Exception e) { - logger.warn(String.format("Invalid arguments %s for function %s.", ByteUtil.toHexString(data), ByteUtil.toHexString(encodedSignature)), e); + logger.warn(String.format("Invalid arguments %s for function %s.", Bytes.of(data), Bytes.of(encodedSignature)), e); return Optional.empty(); } } diff --git a/rskj-core/src/main/java/co/rsk/pcc/bto/DeriveExtendedPublicKey.java b/rskj-core/src/main/java/co/rsk/pcc/bto/DeriveExtendedPublicKey.java index 9592e402c44..989dad2004e 100644 --- a/rskj-core/src/main/java/co/rsk/pcc/bto/DeriveExtendedPublicKey.java +++ b/rskj-core/src/main/java/co/rsk/pcc/bto/DeriveExtendedPublicKey.java @@ -24,8 +24,9 @@ import co.rsk.bitcoinj.crypto.HDKeyDerivation; import co.rsk.bitcoinj.crypto.HDUtils; import co.rsk.pcc.ExecutionEnvironment; -import co.rsk.pcc.exception.NativeContractIllegalArgumentException; import co.rsk.pcc.NativeMethod; +import co.rsk.pcc.exception.NativeContractIllegalArgumentException; +import co.rsk.util.StringUtils; import org.ethereum.core.CallTransaction; import java.util.Arrays; @@ -126,7 +127,7 @@ private void throwInvalidPath(String path) throws NativeContractIllegalArgumentE } private String getInvalidPathErrorMessage(String path) { - return String.format("Invalid path '%s'", path); + return String.format("Invalid path '%s'", StringUtils.trim(path)); } private boolean isDecimal(String s) { diff --git a/rskj-core/src/main/java/co/rsk/pcc/bto/ExtractPublicKeyFromExtendedPublicKey.java b/rskj-core/src/main/java/co/rsk/pcc/bto/ExtractPublicKeyFromExtendedPublicKey.java index 16e2f3f975d..924af82a306 100644 --- a/rskj-core/src/main/java/co/rsk/pcc/bto/ExtractPublicKeyFromExtendedPublicKey.java +++ b/rskj-core/src/main/java/co/rsk/pcc/bto/ExtractPublicKeyFromExtendedPublicKey.java @@ -21,8 +21,9 @@ import co.rsk.bitcoinj.core.NetworkParameters; import co.rsk.bitcoinj.crypto.DeterministicKey; import co.rsk.pcc.ExecutionEnvironment; -import co.rsk.pcc.exception.NativeContractIllegalArgumentException; import co.rsk.pcc.NativeMethod; +import co.rsk.pcc.exception.NativeContractIllegalArgumentException; +import co.rsk.util.StringUtils; import org.ethereum.core.CallTransaction; /** @@ -40,7 +41,7 @@ public class ExtractPublicKeyFromExtendedPublicKey extends NativeMethod { private final HDWalletUtilsHelper helper; - private final static String INVALID_EXTENDED_PUBLIC_KEY = "Invalid extended public key '%s"; + private final static String INVALID_EXTENDED_PUBLIC_KEY = "Invalid extended public key '%s'"; public ExtractPublicKeyFromExtendedPublicKey(ExecutionEnvironment executionEnvironment, HDWalletUtilsHelper helper) { super(executionEnvironment); @@ -64,7 +65,7 @@ public Object execute(Object[] arguments) throws NativeContractIllegalArgumentEx try { key = DeterministicKey.deserializeB58(xpub, params); } catch (IllegalArgumentException e) { - throw new NativeContractIllegalArgumentException(String.format(INVALID_EXTENDED_PUBLIC_KEY, xpub), e); + throw new NativeContractIllegalArgumentException(String.format(INVALID_EXTENDED_PUBLIC_KEY, StringUtils.trim(xpub)), e); } return key.getPubKeyPoint().getEncoded(true); diff --git a/rskj-core/src/main/java/co/rsk/pcc/bto/GetMultisigScriptHash.java b/rskj-core/src/main/java/co/rsk/pcc/bto/GetMultisigScriptHash.java index c4524d3fcb0..a0b91cb143f 100644 --- a/rskj-core/src/main/java/co/rsk/pcc/bto/GetMultisigScriptHash.java +++ b/rskj-core/src/main/java/co/rsk/pcc/bto/GetMultisigScriptHash.java @@ -21,11 +21,11 @@ import co.rsk.bitcoinj.core.BtcECKey; import co.rsk.bitcoinj.script.Script; import co.rsk.bitcoinj.script.ScriptBuilder; +import co.rsk.core.types.bytes.Bytes; import co.rsk.pcc.ExecutionEnvironment; import co.rsk.pcc.NativeMethod; import co.rsk.pcc.exception.NativeContractIllegalArgumentException; import org.ethereum.core.CallTransaction; -import org.ethereum.util.ByteUtil; import java.math.BigInteger; import java.util.ArrayList; @@ -118,7 +118,7 @@ public Object execute(Object[] arguments) throws NativeContractIllegalArgumentEx btcPublicKeys.add(btcPublicKey); } catch (IllegalArgumentException e) { throw new NativeContractIllegalArgumentException(String.format( - "Invalid public key format: %s", ByteUtil.toHexString(publicKey) + "Invalid public key format: %s", Bytes.of(publicKey) ), e); } } diff --git a/rskj-core/src/main/java/co/rsk/pcc/bto/HDWalletUtilsHelper.java b/rskj-core/src/main/java/co/rsk/pcc/bto/HDWalletUtilsHelper.java index 9b7ce14c813..08073811b8d 100644 --- a/rskj-core/src/main/java/co/rsk/pcc/bto/HDWalletUtilsHelper.java +++ b/rskj-core/src/main/java/co/rsk/pcc/bto/HDWalletUtilsHelper.java @@ -2,6 +2,7 @@ import co.rsk.bitcoinj.core.NetworkParameters; import co.rsk.pcc.exception.NativeContractIllegalArgumentException; +import co.rsk.util.StringUtils; public class HDWalletUtilsHelper { public NetworkParameters validateAndExtractNetworkFromExtendedPublicKey(String xpub) throws NativeContractIllegalArgumentException { @@ -15,7 +16,7 @@ public NetworkParameters validateAndExtractNetworkFromExtendedPublicKey(String x } else if (xpub.startsWith("tpub")) { return NetworkParameters.fromID(NetworkParameters.ID_TESTNET); } else { - throw new NativeContractIllegalArgumentException(String.format("Invalid extended public key '%s'", xpub)); + throw new NativeContractIllegalArgumentException(String.format("Invalid extended public key '%s'", StringUtils.trim(xpub))); } } } diff --git a/rskj-core/src/main/java/co/rsk/pcc/bto/ToBase58Check.java b/rskj-core/src/main/java/co/rsk/pcc/bto/ToBase58Check.java index 9e413842a79..7b69a0b0cbb 100644 --- a/rskj-core/src/main/java/co/rsk/pcc/bto/ToBase58Check.java +++ b/rskj-core/src/main/java/co/rsk/pcc/bto/ToBase58Check.java @@ -18,11 +18,11 @@ package co.rsk.pcc.bto; +import co.rsk.core.types.bytes.Bytes; import co.rsk.pcc.ExecutionEnvironment; -import co.rsk.pcc.exception.NativeContractIllegalArgumentException; import co.rsk.pcc.NativeMethod; +import co.rsk.pcc.exception.NativeContractIllegalArgumentException; import org.ethereum.core.CallTransaction; -import org.ethereum.util.ByteUtil; import java.math.BigInteger; @@ -77,7 +77,7 @@ public Object execute(Object[] arguments) throws NativeContractIllegalArgumentEx if (hash.length != 20) { throw new NativeContractIllegalArgumentException(String.format( HASH_INVALID, - ByteUtil.toHexString(hash), hash.length + Bytes.of(hash), hash.length )); } 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 1c5cfb82024..6af54db63e1 100644 --- a/rskj-core/src/main/java/co/rsk/peg/Bridge.java +++ b/rskj-core/src/main/java/co/rsk/peg/Bridge.java @@ -22,6 +22,7 @@ import co.rsk.bitcoinj.core.*; import co.rsk.bitcoinj.script.Script; import co.rsk.bitcoinj.store.BlockStoreException; +import co.rsk.core.types.bytes.Bytes; import co.rsk.peg.constants.BridgeConstants; import co.rsk.core.RskAddress; import co.rsk.crypto.Keccak256; @@ -301,7 +302,7 @@ BridgeParsedData parseData(byte[] data) { BridgeParsedData bridgeParsedData = new BridgeParsedData(); if (data != null && (data.length >= 1 && data.length <= 3)) { - logger.warn("Invalid function signature {}.", ByteUtil.toHexString(data)); + logger.warn("Invalid function signature {}.", Bytes.of(data)); return null; } @@ -312,14 +313,14 @@ BridgeParsedData parseData(byte[] data) { byte[] functionSignature = Arrays.copyOfRange(data, 0, 4); Optional invokedMethod = BridgeMethods.findBySignature(functionSignature); if (!invokedMethod.isPresent()) { - logger.warn("Invalid function signature {}.", ByteUtil.toHexString(functionSignature)); + logger.warn("Invalid function signature {}.", Bytes.of(functionSignature)); return null; } bridgeParsedData.bridgeMethod = invokedMethod.get(); try { bridgeParsedData.args = bridgeParsedData.bridgeMethod.getFunction().decode(data); } catch (Exception e) { - logger.warn("Invalid function arguments {} for function {}.", ByteUtil.toHexString(data), ByteUtil.toHexString(functionSignature)); + logger.warn("Invalid function arguments {} for function {}.", Bytes.of(data), Bytes.of(functionSignature)); return null; } } @@ -370,7 +371,7 @@ public byte[] execute(byte[] data) throws VMException { // Function parsing from data returned null => invalid function selected, halt! if (bridgeParsedData == null) { - String errorMessage = String.format("Invalid data given: %s.", ByteUtil.toHexString(data)); + String errorMessage = String.format("Invalid data given: %s.", Bytes.of(data)); logger.info("[execute] {}", errorMessage); if (!activations.isActive(ConsensusRule.RSKIP88)) { return null; @@ -527,7 +528,7 @@ public void receiveHeaders(Object[] args) throws VMException { BtcBlock header = bridgeConstants.getBtcParams().getDefaultSerializer().makeBlock(btcBlockSerialized); btcBlockArray[i] = header; } catch (ProtocolException e) { - throw new BridgeIllegalArgumentException("Block " + i + " could not be parsed " + ByteUtil.toHexString(btcBlockSerialized), e); + throw new BridgeIllegalArgumentException("Block " + i + " could not be parsed " + Bytes.of(btcBlockSerialized), e); } } try { @@ -599,7 +600,7 @@ public void addSignature(Object[] args) throws VMException { try { federatorPublicKey = BtcECKey.fromPublicOnly(federatorPublicKeySerialized); } catch (Exception e) { - throw new BridgeIllegalArgumentException("Public key could not be parsed " + ByteUtil.toHexString(federatorPublicKeySerialized), e); + throw new BridgeIllegalArgumentException("Public key could not be parsed " + Bytes.of(federatorPublicKeySerialized), e); } Object[] signaturesObjectArray = (Object[]) args[1]; if (signaturesObjectArray.length == 0) { @@ -611,13 +612,13 @@ public void addSignature(Object[] args) throws VMException { try { BtcECKey.ECDSASignature.decodeFromDER((byte[])signatureObject); } catch (Exception e) { - throw new BridgeIllegalArgumentException("Signature could not be parsed " + ByteUtil.toHexString(signatureByteArray), e); + throw new BridgeIllegalArgumentException("Signature could not be parsed " + Bytes.of(signatureByteArray), e); } signatures.add(signatureByteArray); } byte[] rskTxHash = (byte[]) args[2]; if (rskTxHash.length!=32) { - throw new BridgeIllegalArgumentException("Invalid rsk tx hash " + ByteUtil.toHexString(rskTxHash)); + throw new BridgeIllegalArgumentException("Invalid rsk tx hash " + Bytes.of(rskTxHash)); } try { bridgeSupport.addSignature(federatorPublicKey, signatures, rskTxHash); 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 65a1fe491d6..a027e0fdd17 100644 --- a/rskj-core/src/main/java/co/rsk/peg/BridgeSupport.java +++ b/rskj-core/src/main/java/co/rsk/peg/BridgeSupport.java @@ -29,6 +29,7 @@ import co.rsk.bitcoinj.store.BlockStoreException; import co.rsk.bitcoinj.wallet.SendRequest; import co.rsk.bitcoinj.wallet.Wallet; +import co.rsk.core.types.bytes.Bytes; import co.rsk.peg.constants.BridgeConstants; import co.rsk.core.RskAddress; import co.rsk.crypto.Keccak256; @@ -212,6 +213,11 @@ public void receiveHeaders(BtcBlock[] headers) throws IOException, BlockStoreExc this.ensureBtcBlockChain(); for (BtcBlock header : headers) { try { + StoredBlock previousBlock = btcBlockStore.get(header.getPrevBlockHash()); + if (cannotProcessNextBlock(previousBlock)) { + logger.warn("[receiveHeaders] Header {} has too much work to be processed", header.getHash()); + break; + } btcBlockChain.add(header); } catch (Exception e) { // If we try to add an orphan header bitcoinj throws an exception @@ -253,6 +259,11 @@ public Integer receiveHeader(BtcBlock header) throws IOException, BlockStoreExce return RECEIVE_HEADER_BLOCK_TOO_OLD; } + if (cannotProcessNextBlock(previousBlock)) { + logger.warn("[receiveHeader] Header {} has too much work to be processed", header.getHash()); + return RECEIVE_HEADER_UNEXPECTED_EXCEPTION; + } + try { btcBlockChain.add(header); } catch (Exception e) { @@ -265,6 +276,15 @@ public Integer receiveHeader(BtcBlock header) throws IOException, BlockStoreExce return 0; } + private boolean cannotProcessNextBlock(StoredBlock previousBlock) { + int nextBlockHeight = previousBlock.getHeight() + 1; + boolean networkIsMainnet = btcContext.getParams().equals(NetworkParameters.fromID(NetworkParameters.ID_MAINNET)); + + return nextBlockHeight >= bridgeConstants.getBlockWithTooMuchChainWorkHeight() + && networkIsMainnet + && !activations.isActive(ConsensusRule.RSKIP434); + } + /** * Get the wallet for the currently active federation * @return A BTC wallet for the currently active federation @@ -1498,7 +1518,7 @@ private void processSigning( "Malformed signature for input {} of tx {}: {}", i, new Keccak256(rskTxHash), - ByteUtil.toHexString(signatures.get(i)) + Bytes.of(signatures.get(i)) ); return; } @@ -1509,7 +1529,7 @@ private void processSigning( logger.warn( "Signature {} {} is not valid for hash {} and public key {}", i, - ByteUtil.toHexString(sig.encodeToDER()), + Bytes.of(sig.encodeToDER()), sighash, federatorBtcPublicKey ); @@ -1519,7 +1539,7 @@ private void processSigning( TransactionSignature txSig = new TransactionSignature(sig, BtcTransaction.SigHash.ALL, false); txSigs.add(txSig); if (!txSig.isCanonical()) { - logger.warn("Signature {} {} is not canonical.", i, ByteUtil.toHexString(signatures.get(i))); + logger.warn("Signature {} {} is not canonical.", i, Bytes.of(signatures.get(i))); return; } } @@ -1567,7 +1587,7 @@ private void processSigning( } if (BridgeUtils.hasEnoughSignatures(btcContext, btcTx)) { - logger.info("Tx fully signed {}. Hex: {}", btcTx, Hex.toHexString(btcTx.bitcoinSerialize())); + logger.info("Tx fully signed {}. Hex: {}", btcTx, Bytes.of(btcTx.bitcoinSerialize())); provider.getPegoutsWaitingForSignatures().remove(new Keccak256(rskTxHash)); eventLogger.logReleaseBtc(btcTx, rskTxHash); @@ -2298,7 +2318,7 @@ private ABICallVoteResult executeVoteFederationChangeFunction(boolean dryRun, AB publicKey = BtcECKey.fromPublicOnly(publicKeyBytes); publicKeyEc = ECKey.fromPublicOnly(publicKeyBytes); } catch (Exception e) { - throw new BridgeIllegalArgumentException("Public key could not be parsed " + ByteUtil.toHexString(publicKeyBytes), e); + throw new BridgeIllegalArgumentException("Public key could not be parsed " + Bytes.of(publicKeyBytes), e); } executionResult = addFederatorPublicKeyMultikey(dryRun, publicKey, publicKeyEc, publicKeyEc); result = new ABICallVoteResult(executionResult == 1, executionResult); @@ -2310,19 +2330,19 @@ private ABICallVoteResult executeVoteFederationChangeFunction(boolean dryRun, AB try { btcPublicKey = BtcECKey.fromPublicOnly(callSpec.getArguments()[0]); } catch (Exception e) { - throw new BridgeIllegalArgumentException("BTC public key could not be parsed " + ByteUtil.toHexString(callSpec.getArguments()[0]), e); + throw new BridgeIllegalArgumentException("BTC public key could not be parsed " + Bytes.of(callSpec.getArguments()[0]), e); } try { rskPublicKey = ECKey.fromPublicOnly(callSpec.getArguments()[1]); } catch (Exception e) { - throw new BridgeIllegalArgumentException("RSK public key could not be parsed " + ByteUtil.toHexString(callSpec.getArguments()[1]), e); + throw new BridgeIllegalArgumentException("RSK public key could not be parsed " + Bytes.of(callSpec.getArguments()[1]), e); } try { mstPublicKey = ECKey.fromPublicOnly(callSpec.getArguments()[2]); } catch (Exception e) { - throw new BridgeIllegalArgumentException("MST public key could not be parsed " + ByteUtil.toHexString(callSpec.getArguments()[2]), e); + throw new BridgeIllegalArgumentException("MST public key could not be parsed " + Bytes.of(callSpec.getArguments()[2]), e); } executionResult = addFederatorPublicKeyMultikey(dryRun, btcPublicKey, rskPublicKey, mstPublicKey); result = new ABICallVoteResult(executionResult == 1, executionResult); @@ -2651,7 +2671,7 @@ public void registerBtcCoinbaseTransaction(byte[] btcTxSerialized, Sha256Hash bl } } catch (VerificationException e) { logger.warn("[btcTx:{}] PartialMerkleTree could not be parsed", btcTxHash); - throw new BridgeIllegalArgumentException(String.format("PartialMerkleTree could not be parsed %s", ByteUtil.toHexString(pmtSerialized)), e); + throw new BridgeIllegalArgumentException(String.format("PartialMerkleTree could not be parsed %s", Bytes.of(pmtSerialized)), e); } // Check merkle root equals btc block merkle root at the specified height in the btc best chain diff --git a/rskj-core/src/main/java/co/rsk/peg/bitcoin/UtxoUtils.java b/rskj-core/src/main/java/co/rsk/peg/bitcoin/UtxoUtils.java index 0704bd24f0c..2adf53a0913 100644 --- a/rskj-core/src/main/java/co/rsk/peg/bitcoin/UtxoUtils.java +++ b/rskj-core/src/main/java/co/rsk/peg/bitcoin/UtxoUtils.java @@ -4,6 +4,7 @@ import co.rsk.bitcoinj.core.Coin; import co.rsk.bitcoinj.core.TransactionInput; import co.rsk.bitcoinj.core.VarInt; +import co.rsk.core.types.bytes.Bytes; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -11,7 +12,6 @@ import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import org.spongycastle.util.encoders.Hex; public final class UtxoUtils { @@ -40,7 +40,7 @@ public static List decodeOutpointValues(byte[] encodedOutpointValues) { } catch (Exception ex) { throw new InvalidOutpointValueException( String.format("Invalid value with invalid VarInt format: %s", - Hex.toHexString(encodedOutpointValues).toUpperCase() + Bytes.toPrintableString(encodedOutpointValues).toUpperCase() ), ex ); diff --git a/rskj-core/src/main/java/co/rsk/peg/constants/BridgeConstants.java b/rskj-core/src/main/java/co/rsk/peg/constants/BridgeConstants.java index 0a629ec7431..8c3739a091f 100644 --- a/rskj-core/src/main/java/co/rsk/peg/constants/BridgeConstants.java +++ b/rskj-core/src/main/java/co/rsk/peg/constants/BridgeConstants.java @@ -87,6 +87,7 @@ public abstract class BridgeConstants { protected int btcHeightWhenPegoutTxIndexActivates; protected int pegoutTxIndexGracePeriodInBtcBlocks; + protected int blockWithTooMuchChainWorkHeight; public NetworkParameters getBtcParams() { return NetworkParameters.fromID(btcParamsString); @@ -197,4 +198,6 @@ public int getBtcHeightWhenPegoutTxIndexActivates() { public int getPegoutTxIndexGracePeriodInBtcBlocks() { return pegoutTxIndexGracePeriodInBtcBlocks; } + + public int getBlockWithTooMuchChainWorkHeight() { return blockWithTooMuchChainWorkHeight; } } diff --git a/rskj-core/src/main/java/co/rsk/peg/constants/BridgeMainNetConstants.java b/rskj-core/src/main/java/co/rsk/peg/constants/BridgeMainNetConstants.java index 1a023ac24af..df065348649 100644 --- a/rskj-core/src/main/java/co/rsk/peg/constants/BridgeMainNetConstants.java +++ b/rskj-core/src/main/java/co/rsk/peg/constants/BridgeMainNetConstants.java @@ -124,6 +124,8 @@ private BridgeMainNetConstants() { btcHeightWhenPegoutTxIndexActivates = 837_589; // Estimated date Wed, 03 Apr 2024 15:00:00 GMT. 832,430 was the block number at time of calculation pegoutTxIndexGracePeriodInBtcBlocks = 4_320; // 30 days in BTC blocks (considering 1 block every 10 minutes) + + blockWithTooMuchChainWorkHeight = 849_138; } public static BridgeMainNetConstants getInstance() { diff --git a/rskj-core/src/main/java/co/rsk/peg/constants/BridgeRegTestConstants.java b/rskj-core/src/main/java/co/rsk/peg/constants/BridgeRegTestConstants.java index 38d58ab24f0..884f0252221 100644 --- a/rskj-core/src/main/java/co/rsk/peg/constants/BridgeRegTestConstants.java +++ b/rskj-core/src/main/java/co/rsk/peg/constants/BridgeRegTestConstants.java @@ -144,6 +144,8 @@ public BridgeRegTestConstants(List federationPublicKeys) { btcHeightWhenPegoutTxIndexActivates = 250; pegoutTxIndexGracePeriodInBtcBlocks = 100; + + blockWithTooMuchChainWorkHeight = Integer.MAX_VALUE; } public static BridgeRegTestConstants getInstance() { diff --git a/rskj-core/src/main/java/co/rsk/peg/constants/BridgeTestNetConstants.java b/rskj-core/src/main/java/co/rsk/peg/constants/BridgeTestNetConstants.java index 0b51e75feee..60b4cbcd4a5 100644 --- a/rskj-core/src/main/java/co/rsk/peg/constants/BridgeTestNetConstants.java +++ b/rskj-core/src/main/java/co/rsk/peg/constants/BridgeTestNetConstants.java @@ -143,6 +143,8 @@ public class BridgeTestNetConstants extends BridgeConstants { btcHeightWhenPegoutTxIndexActivates = 2_589_553; // Estimated date Wed, 20 Mar 2024 15:00:00 GMT. 2,579,823 was the block number at time of calculation pegoutTxIndexGracePeriodInBtcBlocks = 1_440; // 10 days in BTC blocks (considering 1 block every 10 minutes) + + blockWithTooMuchChainWorkHeight = Integer.MAX_VALUE; } public static BridgeTestNetConstants getInstance() { diff --git a/rskj-core/src/main/java/co/rsk/peg/federation/FederationMember.java b/rskj-core/src/main/java/co/rsk/peg/federation/FederationMember.java index bc42ea29c0f..16d68d6f954 100644 --- a/rskj-core/src/main/java/co/rsk/peg/federation/FederationMember.java +++ b/rskj-core/src/main/java/co/rsk/peg/federation/FederationMember.java @@ -19,6 +19,7 @@ package co.rsk.peg.federation; import co.rsk.bitcoinj.core.BtcECKey; +import co.rsk.util.StringUtils; import org.ethereum.crypto.ECKey; import org.ethereum.util.ByteUtil; import org.ethereum.util.RLP; @@ -79,7 +80,7 @@ public static KeyType byValue(String value) { case "btc": return KeyType.BTC; default: - throw new IllegalArgumentException(String.format("Invalid value for FederationMember.KeyType: %s", value)); + throw new IllegalArgumentException(String.format("Invalid value for FederationMember.KeyType: %s", StringUtils.trim(value))); } } } diff --git a/rskj-core/src/main/java/co/rsk/peg/pegininstructions/PeginInstructionsProvider.java b/rskj-core/src/main/java/co/rsk/peg/pegininstructions/PeginInstructionsProvider.java index ea249c5f8fb..53b2b874c48 100644 --- a/rskj-core/src/main/java/co/rsk/peg/pegininstructions/PeginInstructionsProvider.java +++ b/rskj-core/src/main/java/co/rsk/peg/pegininstructions/PeginInstructionsProvider.java @@ -6,6 +6,8 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; + +import co.rsk.core.types.bytes.Bytes; import org.bouncycastle.util.encoders.Hex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,7 +36,7 @@ public Optional buildPeginInstructions(BtcTransaction btcTx) throw new PeginInstructionsException(message, e); } - logger.trace("[buildPeginInstructions] OP_RETURN data: {}", Hex.toHexString(opReturnOutputData)); + logger.trace("[buildPeginInstructions] OP_RETURN data: {}", Bytes.of(opReturnOutputData)); int protocolVersion = PeginInstructionsBase.extractProtocolVersion(opReturnOutputData); diff --git a/rskj-core/src/main/java/co/rsk/remasc/RemascContract.java b/rskj-core/src/main/java/co/rsk/remasc/RemascContract.java index a938f4c7d59..23c569d37e9 100644 --- a/rskj-core/src/main/java/co/rsk/remasc/RemascContract.java +++ b/rskj-core/src/main/java/co/rsk/remasc/RemascContract.java @@ -20,13 +20,13 @@ import co.rsk.config.RemascConfig; import co.rsk.core.RskAddress; +import co.rsk.core.types.bytes.Bytes; import co.rsk.panic.PanicProcessor; import co.rsk.rpc.modules.trace.ProgramSubtrace; import org.ethereum.config.Constants; import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.core.CallTransaction; import org.ethereum.db.ByteArrayWrapper; -import org.ethereum.util.ByteUtil; import org.ethereum.vm.DataWord; import org.ethereum.vm.PrecompiledContractArgs; import org.ethereum.vm.PrecompiledContracts; @@ -130,7 +130,7 @@ private CallTransaction.Function getFunction(byte[] data) { function = PROCESS_MINERS_FEES; } else { if (data.length != 4) { - logger.warn("Invalid function: signature longer than expected {}.", ByteUtil.toHexString(data)); + logger.warn("Invalid function: signature longer than expected {}.", Bytes.of(data)); throw new RemascInvalidInvocationException("Invalid function signature"); } @@ -138,7 +138,7 @@ private CallTransaction.Function getFunction(byte[] data) { function = functions.get(new ByteArrayWrapper(functionSignature)); if (function == null) { - logger.warn("Invalid function: signature does not match an existing function {}.", ByteUtil.toHexString(functionSignature)); + logger.warn("Invalid function: signature does not match an existing function {}.", Bytes.of(functionSignature)); throw new RemascInvalidInvocationException("Invalid function signature"); } } diff --git a/rskj-core/src/main/java/co/rsk/util/StringUtils.java b/rskj-core/src/main/java/co/rsk/util/StringUtils.java new file mode 100644 index 00000000000..a7c58a322b3 --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/util/StringUtils.java @@ -0,0 +1,40 @@ +/* + * This file is part of RskJ + * Copyright (C) 2024 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.util; + +import javax.annotation.Nullable; + +public class StringUtils { + + private static final int DEFAULT_MAX_LEN = 64; + + public static String trim(@Nullable String src) { + return trim(src, DEFAULT_MAX_LEN); + } + + public static String trim(@Nullable String src, int maxLength) { + if (maxLength < 0) { + throw new IllegalArgumentException("maxLength: " + maxLength); + } + if (src == null || src.length() <= maxLength) { + return src; + } + return src.substring(0, maxLength) + "..."; + } +} diff --git a/rskj-core/src/main/java/co/rsk/validators/BlockRootValidationRule.java b/rskj-core/src/main/java/co/rsk/validators/BlockRootValidationRule.java index 034e9d808c9..cad08aff0d4 100644 --- a/rskj-core/src/main/java/co/rsk/validators/BlockRootValidationRule.java +++ b/rskj-core/src/main/java/co/rsk/validators/BlockRootValidationRule.java @@ -19,11 +19,11 @@ package co.rsk.validators; import co.rsk.core.bc.BlockHashesHelper; +import co.rsk.core.types.bytes.Bytes; import co.rsk.panic.PanicProcessor; import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.Block; -import org.ethereum.util.ByteUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,7 +55,7 @@ public boolean isValid(Block block) { if (!Arrays.equals(blockTxRootHash, txListRootHash)) { String message = String.format("Block's given Trie Hash doesn't match: %s != %s", - ByteUtil.toHexString(blockTxRootHash), ByteUtil.toHexString(txListRootHash)); + Bytes.of(blockTxRootHash), Bytes.of(txListRootHash)); logger.warn(message); panicProcessor.panic("invalidtrie", message); diff --git a/rskj-core/src/main/java/co/rsk/validators/BlockUnclesHashValidationRule.java b/rskj-core/src/main/java/co/rsk/validators/BlockUnclesHashValidationRule.java index 3d25bc706c1..037aab5b5c7 100644 --- a/rskj-core/src/main/java/co/rsk/validators/BlockUnclesHashValidationRule.java +++ b/rskj-core/src/main/java/co/rsk/validators/BlockUnclesHashValidationRule.java @@ -1,5 +1,6 @@ package co.rsk.validators; +import co.rsk.core.types.bytes.Bytes; import co.rsk.panic.PanicProcessor; import org.ethereum.core.Block; import org.ethereum.core.BlockHeader; @@ -20,7 +21,7 @@ public boolean isValid(Block block) { if (!ByteUtil.fastEquals(unclesHeader, unclesBlock)) { String message = String.format("Block's given Uncle Hash doesn't match: %s != %s", - ByteUtil.toHexString(unclesHeader), ByteUtil.toHexString(unclesBlock)); + Bytes.of(unclesHeader), Bytes.of(unclesBlock)); logger.warn(message); panicProcessor.panic("invaliduncle", message); return false; diff --git a/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ConsensusRule.java b/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ConsensusRule.java index f4a4375f5a3..72b787537ea 100644 --- a/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ConsensusRule.java +++ b/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ConsensusRule.java @@ -94,6 +94,7 @@ public enum ConsensusRule { RSKIP415("rskip415"), RSKIP417("rskip417"), RSKIP428("rskip428"), + RSKIP434("rskip434") ; private String configKey; diff --git a/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/NetworkUpgrade.java b/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/NetworkUpgrade.java index e47ed0fe7d6..36670f845e0 100644 --- a/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/NetworkUpgrade.java +++ b/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/NetworkUpgrade.java @@ -32,6 +32,7 @@ public enum NetworkUpgrade { HOP401("hop401"), FINGERROOT500("fingerroot500"), ARROWHEAD600("arrowhead600"), + ARROWHEAD631("arrowhead631"), LOVELL700("lovell700"); private String name; diff --git a/rskj-core/src/main/java/org/ethereum/core/Block.java b/rskj-core/src/main/java/org/ethereum/core/Block.java index 178782b70cb..627662d0519 100644 --- a/rskj-core/src/main/java/org/ethereum/core/Block.java +++ b/rskj-core/src/main/java/org/ethereum/core/Block.java @@ -23,12 +23,12 @@ import co.rsk.core.Coin; import co.rsk.core.RskAddress; import co.rsk.core.bc.BlockHashesHelper; +import co.rsk.core.types.bytes.Bytes; import co.rsk.crypto.Keccak256; import co.rsk.panic.PanicProcessor; import com.google.common.collect.ImmutableList; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; -import org.ethereum.util.ByteUtil; import org.ethereum.util.RLP; import javax.annotation.Nonnull; @@ -208,7 +208,7 @@ public Coin getMinimumGasPrice() { @Override public String toString() { StringBuilder toStringBuff = new StringBuilder(); - toStringBuff.append(ByteUtil.toHexString(this.getEncoded())).append("\n"); + toStringBuff.append(Bytes.of(this.getEncoded())).append("\n"); toStringBuff.append("BlockData [ "); toStringBuff.append("hash=").append(this.getHash()).append("\n"); toStringBuff.append(header.toString()); diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockIdentifier.java b/rskj-core/src/main/java/org/ethereum/core/BlockIdentifier.java index 4a3729e18c8..2d7862d1016 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockIdentifier.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockIdentifier.java @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -import org.ethereum.util.ByteUtil; +import co.rsk.core.types.bytes.Bytes; import org.ethereum.util.RLP; import org.ethereum.util.RLPList; @@ -74,7 +74,7 @@ public byte[] getEncoded() { @Override public String toString() { return "BlockIdentifier {" + - "hash=" + ByteUtil.toHexString(hash) + + "hash=" + Bytes.of(hash) + ", number=" + number + '}'; } diff --git a/rskj-core/src/main/java/org/ethereum/core/Bloom.java b/rskj-core/src/main/java/org/ethereum/core/Bloom.java index b5061c63bec..ff3690d9208 100644 --- a/rskj-core/src/main/java/org/ethereum/core/Bloom.java +++ b/rskj-core/src/main/java/org/ethereum/core/Bloom.java @@ -19,6 +19,7 @@ package org.ethereum.core; +import co.rsk.core.types.bytes.Bytes; import org.ethereum.util.ByteUtil; import java.util.Arrays; @@ -84,7 +85,7 @@ public Bloom copy() { @Override public String toString() { - return ByteUtil.toHexString(data); + return Bytes.toPrintableString(data); } @Override diff --git a/rskj-core/src/main/java/org/ethereum/core/TransactionReceipt.java b/rskj-core/src/main/java/org/ethereum/core/TransactionReceipt.java index 913c4481186..71102b8d3f3 100644 --- a/rskj-core/src/main/java/org/ethereum/core/TransactionReceipt.java +++ b/rskj-core/src/main/java/org/ethereum/core/TransactionReceipt.java @@ -19,6 +19,7 @@ package org.ethereum.core; + import co.rsk.core.types.bytes.Bytes; import org.bouncycastle.util.BigIntegers; import org.ethereum.util.*; import org.ethereum.vm.LogInfo; @@ -232,8 +233,8 @@ public String toString() { return "TransactionReceipt[" + "\n , " + (hasTxStatus() ? ("txStatus=" + (isSuccessful()? "OK" : "FAILED")) - : ("postTxState=" + ByteUtil.toHexString(postTxState))) + - "\n , cumulativeGas=" + ByteUtil.toHexString(cumulativeGas) + + : ("postTxState=" + Bytes.of(postTxState))) + + "\n , cumulativeGas=" + Bytes.of(cumulativeGas) + "\n , bloom=" + bloomFilter.toString() + "\n , logs=" + logInfoList + ']'; diff --git a/rskj-core/src/main/java/org/ethereum/core/genesis/BlockChainLoader.java b/rskj-core/src/main/java/org/ethereum/core/genesis/BlockChainLoader.java index a0805dfe01c..c14eb12e018 100644 --- a/rskj-core/src/main/java/org/ethereum/core/genesis/BlockChainLoader.java +++ b/rskj-core/src/main/java/org/ethereum/core/genesis/BlockChainLoader.java @@ -23,6 +23,7 @@ import co.rsk.core.BlockDifficulty; import co.rsk.core.bc.BlockChainImpl; import co.rsk.core.bc.BlockExecutor; +import co.rsk.core.types.bytes.Bytes; import co.rsk.db.RepositoryLocator; import co.rsk.db.StateRootHandler; import co.rsk.validators.BlockValidator; @@ -32,7 +33,6 @@ import org.ethereum.db.BlockStore; import org.ethereum.db.ReceiptStore; import org.ethereum.listener.EthereumListener; -import org.ethereum.util.ByteUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -95,7 +95,7 @@ public BlockChainImpl loadBlockchain() { logger.info("*** Loaded up to block [{}] totalDifficulty [{}] with stateRoot [{}]", bestBlock.getNumber(), totalDifficulty, - ByteUtil.toHexString(bestBlock.getStateRoot())); + Bytes.of(bestBlock.getStateRoot())); } if (!isBlockConsistent(bestBlock)) { diff --git a/rskj-core/src/main/java/org/ethereum/datasource/LevelDbDataSource.java b/rskj-core/src/main/java/org/ethereum/datasource/LevelDbDataSource.java index 29458e43b94..5897e2cdd14 100644 --- a/rskj-core/src/main/java/org/ethereum/datasource/LevelDbDataSource.java +++ b/rskj-core/src/main/java/org/ethereum/datasource/LevelDbDataSource.java @@ -19,6 +19,7 @@ package org.ethereum.datasource; +import co.rsk.core.types.bytes.Bytes; import co.rsk.metrics.profilers.Metric; import co.rsk.metrics.profilers.Profiler; import co.rsk.metrics.profilers.ProfilerFactory; @@ -140,13 +141,13 @@ public byte[] get(byte[] key) { resetDbLock.readLock().lock(); try { if (logger.isTraceEnabled()) { - logger.trace("~> LevelDbDataSource.get(): {}, key: {}", name, ByteUtil.toHexString(key)); + logger.trace("~> LevelDbDataSource.get(): {}, key: {}", name, Bytes.of(key)); } try { byte[] ret = db.get(key); if (logger.isTraceEnabled()) { - logger.trace("<~ LevelDbDataSource.get(): {}, key: {}, return length: {}", name, ByteUtil.toHexString(key), (ret == null ? "null" : ret.length)); + logger.trace("<~ LevelDbDataSource.get(): {}, key: {}, return length: {}", name, Bytes.of(key), (ret == null ? "null" : ret.length)); } return ret; @@ -155,7 +156,7 @@ public byte[] get(byte[] key) { try { byte[] ret = db.get(key); if (logger.isTraceEnabled()) { - logger.trace("<~ LevelDbDataSource.get(): {}, key: {}, return length: {}", name, ByteUtil.toHexString(key), (ret == null ? "null" : ret.length)); + logger.trace("<~ LevelDbDataSource.get(): {}, key: {}, return length: {}", name, Bytes.of(key), (ret == null ? "null" : ret.length)); } return ret; @@ -180,12 +181,12 @@ public byte[] put(byte[] key, byte[] value) { resetDbLock.readLock().lock(); try { if (logger.isTraceEnabled()) { - logger.trace("~> LevelDbDataSource.put(): {}, key: {}, return length: {}", name, ByteUtil.toHexString(key), value.length); + logger.trace("~> LevelDbDataSource.put(): {}, key: {}, return length: {}", name, Bytes.of(key), value.length); } db.put(key, value); if (logger.isTraceEnabled()) { - logger.trace("<~ LevelDbDataSource.put(): {}, key: {}, return length: {}", name, ByteUtil.toHexString(key), value.length); + logger.trace("<~ LevelDbDataSource.put(): {}, key: {}, return length: {}", name, Bytes.of(key), value.length); } return value; @@ -201,12 +202,12 @@ public void delete(byte[] key) { resetDbLock.readLock().lock(); try { if (logger.isTraceEnabled()) { - logger.trace("~> LevelDbDataSource.delete(): {}, key: {}", name, ByteUtil.toHexString(key)); + logger.trace("~> LevelDbDataSource.delete(): {}, key: {}", name, Bytes.of(key)); } db.delete(key); if (logger.isTraceEnabled()) { - logger.trace("<~ LevelDbDataSource.delete(): {}, key: {}", name, ByteUtil.toHexString(key)); + logger.trace("<~ LevelDbDataSource.delete(): {}, key: {}", name, Bytes.of(key)); } } finally { diff --git a/rskj-core/src/main/java/org/ethereum/datasource/RocksDbDataSource.java b/rskj-core/src/main/java/org/ethereum/datasource/RocksDbDataSource.java index 12567a24f92..552bc3c863f 100644 --- a/rskj-core/src/main/java/org/ethereum/datasource/RocksDbDataSource.java +++ b/rskj-core/src/main/java/org/ethereum/datasource/RocksDbDataSource.java @@ -19,6 +19,7 @@ package org.ethereum.datasource; +import co.rsk.core.types.bytes.Bytes; import co.rsk.metrics.profilers.Metric; import co.rsk.metrics.profilers.Profiler; import co.rsk.metrics.profilers.ProfilerFactory; @@ -151,13 +152,13 @@ public byte[] get(byte[] key) { while (retries < MAX_RETRIES) { try { if (logger.isTraceEnabled()) { - logger.trace("~> RocksDbDataSource.get(): {}, key: {}", name, ByteUtil.toHexString(key)); + logger.trace("~> RocksDbDataSource.get(): {}, key: {}", name, Bytes.of(key)); } byte[] ret = db.get(key); if (logger.isTraceEnabled()) { - logger.trace("<~ RocksDbDataSource.get(): {}, key: {}, return length: {}", name, ByteUtil.toHexString(key), (ret == null ? "null" : ret.length)); + logger.trace("<~ RocksDbDataSource.get(): {}, key: {}, return length: {}", name, Bytes.of(key), (ret == null ? "null" : ret.length)); } result = ret; @@ -193,13 +194,13 @@ public byte[] put(byte[] key, byte[] value) { try { if (logger.isTraceEnabled()) { - logger.trace("~> RocksDbDataSource.put(): {}, key: {}, return length: {}", name, ByteUtil.toHexString(key), value.length); + logger.trace("~> RocksDbDataSource.put(): {}, key: {}, return length: {}", name, Bytes.of(key), value.length); } db.put(key, value); if (logger.isTraceEnabled()) { - logger.trace("<~ RocksDbDataSource.put(): {}, key: {}, return length: {}", name, ByteUtil.toHexString(key), value.length); + logger.trace("<~ RocksDbDataSource.put(): {}, key: {}, return length: {}", name, Bytes.of(key), value.length); } } catch (RocksDBException e) { logger.error("Exception. Not retrying.", e); @@ -219,12 +220,12 @@ public void delete(byte[] key) { try { if (logger.isTraceEnabled()) { - logger.trace("~> RocksDbDataSource.delete(): {}, key: {}", name, ByteUtil.toHexString(key)); + logger.trace("~> RocksDbDataSource.delete(): {}, key: {}", name, Bytes.of(key)); } db.delete(key); if (logger.isTraceEnabled()) { - logger.trace("<~ RocksDbDataSource.delete(): {}, key: {}", name, ByteUtil.toHexString(key)); + logger.trace("<~ RocksDbDataSource.delete(): {}, key: {}", name, Bytes.of(key)); } } catch (RocksDBException e) { diff --git a/rskj-core/src/main/java/org/ethereum/db/ByteArrayWrapper.java b/rskj-core/src/main/java/org/ethereum/db/ByteArrayWrapper.java index 8e98d96b5b9..6f203681c95 100644 --- a/rskj-core/src/main/java/org/ethereum/db/ByteArrayWrapper.java +++ b/rskj-core/src/main/java/org/ethereum/db/ByteArrayWrapper.java @@ -19,6 +19,7 @@ package org.ethereum.db; +import co.rsk.core.types.bytes.Bytes; import org.ethereum.util.ByteUtil; import org.ethereum.util.FastByteComparisons; @@ -69,7 +70,7 @@ public byte[] getData() { @Override public String toString() { - return ByteUtil.toHexString(data); + return Bytes.toPrintableString(data); } public boolean equalsToByteArray(byte[] otherData) { diff --git a/rskj-core/src/main/java/org/ethereum/net/eth/message/StatusMessage.java b/rskj-core/src/main/java/org/ethereum/net/eth/message/StatusMessage.java index caafa569b30..572864bab13 100644 --- a/rskj-core/src/main/java/org/ethereum/net/eth/message/StatusMessage.java +++ b/rskj-core/src/main/java/org/ethereum/net/eth/message/StatusMessage.java @@ -19,6 +19,7 @@ package org.ethereum.net.eth.message; +import co.rsk.core.types.bytes.Bytes; import org.ethereum.util.ByteUtil; import org.ethereum.util.RLP; import org.ethereum.util.RLPList; @@ -155,9 +156,9 @@ public String toString() { return "[" + this.getCommand().name() + " protocolVersion=" + this.protocolVersion + " networkId=" + this.networkId + - " totalDifficulty=" + ByteUtil.toHexStringOrEmpty(this.totalDifficulty) + - " bestHash=" + ByteUtil.toHexString(this.bestHash) + - " genesisHash=" + ByteUtil.toHexString(this.genesisHash) + + " totalDifficulty=" + Bytes.of(this.totalDifficulty) + + " bestHash=" + Bytes.of(this.bestHash) + + " genesisHash=" + Bytes.of(this.genesisHash) + "]"; } } diff --git a/rskj-core/src/main/java/org/ethereum/net/rlpx/AuthInitiateMessage.java b/rskj-core/src/main/java/org/ethereum/net/rlpx/AuthInitiateMessage.java index 687f25965e8..d0e419ebe24 100644 --- a/rskj-core/src/main/java/org/ethereum/net/rlpx/AuthInitiateMessage.java +++ b/rskj-core/src/main/java/org/ethereum/net/rlpx/AuthInitiateMessage.java @@ -19,10 +19,10 @@ package org.ethereum.net.rlpx; +import co.rsk.core.types.bytes.Bytes; import org.bouncycastle.math.ec.ECPoint; import org.ethereum.crypto.ECKey; import org.ethereum.crypto.signature.ECDSASignature; -import org.ethereum.util.ByteUtil; import static org.bouncycastle.util.BigIntegers.asUnsignedByteArray; import static org.ethereum.util.ByteUtil.merge; @@ -121,10 +121,10 @@ public String toString() { asUnsignedByteArray(signature.getS()), new byte[]{EncryptionHandshake.recIdFromSignatureV(signature.getV())}); return "AuthInitiateMessage{" + - "\n sigBytes=" + ByteUtil.toHexString(sigBytes) + - "\n ephemeralPublicHash=" + ByteUtil.toHexString(ephemeralPublicHash) + - "\n publicKey=" + ByteUtil.toHexString(publicKey.getEncoded(false)) + - "\n nonce=" + ByteUtil.toHexString(nonce) + + "\n sigBytes=" + Bytes.of(sigBytes) + + "\n ephemeralPublicHash=" + Bytes.of(ephemeralPublicHash) + + "\n publicKey=" + Bytes.of(publicKey.getEncoded(false)) + + "\n nonce=" + Bytes.of(nonce) + "\n}"; } } diff --git a/rskj-core/src/main/java/org/ethereum/net/rlpx/AuthInitiateMessageV4.java b/rskj-core/src/main/java/org/ethereum/net/rlpx/AuthInitiateMessageV4.java index 2b35c43e3eb..861c791e0ba 100644 --- a/rskj-core/src/main/java/org/ethereum/net/rlpx/AuthInitiateMessageV4.java +++ b/rskj-core/src/main/java/org/ethereum/net/rlpx/AuthInitiateMessageV4.java @@ -19,6 +19,7 @@ package org.ethereum.net.rlpx; +import co.rsk.core.types.bytes.Bytes; import org.bouncycastle.math.ec.ECPoint; import org.ethereum.crypto.ECKey; import org.ethereum.crypto.signature.ECDSASignature; @@ -111,9 +112,9 @@ public String toString() { asUnsignedByteArray(signature.getS()), new byte[]{EncryptionHandshake.recIdFromSignatureV(signature.getV())}); return "AuthInitiateMessage{" + - "\n sigBytes=" + ByteUtil.toHexString(sigBytes) + - "\n publicKey=" + ByteUtil.toHexString(publicKey.getEncoded(false)) + - "\n nonce=" + ByteUtil.toHexString(nonce) + + "\n sigBytes=" + Bytes.of(sigBytes) + + "\n publicKey=" + Bytes.of(publicKey.getEncoded(false)) + + "\n nonce=" + Bytes.of(nonce) + "\n version=" + version + "\n}"; } diff --git a/rskj-core/src/main/java/org/ethereum/net/rlpx/AuthResponseMessage.java b/rskj-core/src/main/java/org/ethereum/net/rlpx/AuthResponseMessage.java index e1bcd67547e..0f59e3391c7 100644 --- a/rskj-core/src/main/java/org/ethereum/net/rlpx/AuthResponseMessage.java +++ b/rskj-core/src/main/java/org/ethereum/net/rlpx/AuthResponseMessage.java @@ -19,9 +19,9 @@ package org.ethereum.net.rlpx; +import co.rsk.core.types.bytes.Bytes; import org.bouncycastle.math.ec.ECPoint; import org.ethereum.crypto.ECKey; -import org.ethereum.util.ByteUtil; /** * Authentication response message, to be wrapped inside @@ -74,7 +74,7 @@ public byte[] encode() { public String toString() { return "AuthResponseMessage{" + "\n ephemeralPublicKey=" + ephemeralPublicKey + - "\n nonce=" + ByteUtil.toHexString(nonce) + + "\n nonce=" + Bytes.of(nonce) + "\n isTokenUsed=" + isTokenUsed + '}'; } diff --git a/rskj-core/src/main/java/org/ethereum/net/rlpx/AuthResponseMessageV4.java b/rskj-core/src/main/java/org/ethereum/net/rlpx/AuthResponseMessageV4.java index da57466a9eb..38772ec3dee 100644 --- a/rskj-core/src/main/java/org/ethereum/net/rlpx/AuthResponseMessageV4.java +++ b/rskj-core/src/main/java/org/ethereum/net/rlpx/AuthResponseMessageV4.java @@ -19,6 +19,7 @@ package org.ethereum.net.rlpx; +import co.rsk.core.types.bytes.Bytes; import org.bouncycastle.math.ec.ECPoint; import org.ethereum.crypto.ECKey; import org.ethereum.util.ByteUtil; @@ -74,7 +75,7 @@ public byte[] encode() { public String toString() { return "AuthResponseMessage{" + "\n ephemeralPublicKey=" + ephemeralPublicKey + - "\n nonce=" + ByteUtil.toHexString(nonce) + + "\n nonce=" + Bytes.of(nonce) + "\n version=" + version + '}'; } diff --git a/rskj-core/src/main/java/org/ethereum/net/rlpx/MessageCodec.java b/rskj-core/src/main/java/org/ethereum/net/rlpx/MessageCodec.java index 7f087ee01a4..f38432e1a2b 100644 --- a/rskj-core/src/main/java/org/ethereum/net/rlpx/MessageCodec.java +++ b/rskj-core/src/main/java/org/ethereum/net/rlpx/MessageCodec.java @@ -19,6 +19,7 @@ package org.ethereum.net.rlpx; +import co.rsk.core.types.bytes.Bytes; import com.google.common.io.ByteStreams; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageCodec; @@ -136,7 +137,7 @@ private Message decodeMessage(List frames) throws IOException { } if (loggerWire.isDebugEnabled()) { - loggerWire.debug("Recv: Encoded: {} [{}]", frameType, ByteUtil.toHexString(payload)); + loggerWire.debug("Recv: Encoded: {} [{}]", frameType, Bytes.of(payload)); } Message msg = createMessage((byte) frameType, payload); @@ -159,7 +160,7 @@ protected void encode(ChannelHandlerContext ctx, Message msg, List out) byte[] encoded = msg.getEncoded(); if (loggerWire.isDebugEnabled()) { - loggerWire.debug("Send: Encoded: {} [{}]", getCode(msg.getCommand()), ByteUtil.toHexString(encoded)); + loggerWire.debug("Send: Encoded: {} [{}]", getCode(msg.getCommand()), Bytes.of(encoded)); } List frames = splitMessageToFrames(msg); @@ -227,7 +228,7 @@ private Message createMessage(byte code, byte[] payload) { return ethMessageFactory.create(resolved, payload); } - throw new IllegalArgumentException("No such message: " + code + " [" + ByteUtil.toHexString(payload) + "]"); + throw new IllegalArgumentException("No such message: " + code + " [" + Bytes.of(payload) + "]"); } public void setChannel(Channel channel){ diff --git a/rskj-core/src/main/java/org/ethereum/net/server/PeerServerImpl.java b/rskj-core/src/main/java/org/ethereum/net/server/PeerServerImpl.java index 4cff21586ba..bfc2fdd696a 100644 --- a/rskj-core/src/main/java/org/ethereum/net/server/PeerServerImpl.java +++ b/rskj-core/src/main/java/org/ethereum/net/server/PeerServerImpl.java @@ -19,6 +19,7 @@ package org.ethereum.net.server; +import co.rsk.core.types.bytes.Bytes; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; @@ -30,7 +31,6 @@ import org.ethereum.config.SystemProperties; import org.ethereum.listener.EthereumListener; import org.ethereum.net.EthereumChannelInitializerFactory; -import org.ethereum.util.ByteUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -73,7 +73,7 @@ public void start() { peerServiceExecutor.execute(() -> start(config.getBindAddress(), config.getPeerPort())); } - logger.info("RskJ node started: enode://{}@{}:{}" , ByteUtil.toHexString(config.nodeId()), config.getPublicIp(), config.getPeerPort()); + logger.info("RskJ node started: enode://{}@{}:{}" , Bytes.of(config.nodeId()), config.getPublicIp(), config.getPeerPort()); } @Override @@ -110,7 +110,7 @@ private void start(InetAddress host, int port) { // Start the client. logger.info("Listening for incoming connections, host: {}, port: [{}] ", host, port); - logger.info("NodeId: [{}] ", ByteUtil.toHexString(config.nodeId())); + logger.info("NodeId: [{}] ", Bytes.of(config.nodeId())); ChannelFuture f = b.bind(host, port).sync(); diff --git a/rskj-core/src/main/java/org/ethereum/rpc/Topic.java b/rskj-core/src/main/java/org/ethereum/rpc/Topic.java index e32e591ff2c..d7f6cffc98b 100644 --- a/rskj-core/src/main/java/org/ethereum/rpc/Topic.java +++ b/rskj-core/src/main/java/org/ethereum/rpc/Topic.java @@ -18,12 +18,11 @@ package org.ethereum.rpc; -import java.util.Arrays; - -import org.ethereum.util.ByteUtil; - +import co.rsk.core.types.bytes.Bytes; import co.rsk.util.HexUtils; +import java.util.Arrays; + /** * Created by ajlopez on 18/01/2018. */ @@ -79,7 +78,7 @@ public int hashCode() { @Override public String toString() { - return ByteUtil.toHexString(bytes); + return Bytes.toPrintableString(bytes); } public String toJsonString() { diff --git a/rskj-core/src/main/java/org/ethereum/solidity/SolidityType.java b/rskj-core/src/main/java/org/ethereum/solidity/SolidityType.java index ea0b8a2dfff..a4ef7aa34be 100644 --- a/rskj-core/src/main/java/org/ethereum/solidity/SolidityType.java +++ b/rskj-core/src/main/java/org/ethereum/solidity/SolidityType.java @@ -19,6 +19,7 @@ package org.ethereum.solidity; +import co.rsk.core.types.bytes.Bytes; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import org.ethereum.util.ByteUtil; @@ -375,7 +376,7 @@ public byte[] encode(Object value) { byte[] addr = super.encode(value); for (int i = 0; i < 12; i++) { if (addr[i] != 0) { - throw new RuntimeException("Invalid address (should be 20 bytes length): " + ByteUtil.toHexString(addr)); + throw new RuntimeException("Invalid address (should be 20 bytes length): " + Bytes.of(addr)); } } return addr; diff --git a/rskj-core/src/main/java/org/ethereum/vm/VM.java b/rskj-core/src/main/java/org/ethereum/vm/VM.java index f7568f1f067..2f829c8b426 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/VM.java +++ b/rskj-core/src/main/java/org/ethereum/vm/VM.java @@ -21,6 +21,7 @@ import co.rsk.config.VmConfig; import co.rsk.core.RskAddress; +import co.rsk.core.types.bytes.Bytes; import co.rsk.crypto.Keccak256; import co.rsk.rpc.netty.ExecTimeoutContext; import org.bouncycastle.util.BigIntegers; @@ -2175,8 +2176,8 @@ private void dumpLine(OpCode op, long gasBefore, long gasCost, long memWords, Pr DataWord key = keysIterator.next(); DataWord value = storage.getStorageValue(ownerAddress, key); dumpLogger.trace("{} {}", - ByteUtil.toHexString(key.getNoLeadZeroesData()), - ByteUtil.toHexString(value.getNoLeadZeroesData())); + Bytes.of(key.getNoLeadZeroesData()), + Bytes.of(value.getNoLeadZeroesData())); } break; default: diff --git a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java index 1fb34df8b82..33ef0607727 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java +++ b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java @@ -21,6 +21,7 @@ import co.rsk.config.VmConfig; import co.rsk.core.Coin; import co.rsk.core.RskAddress; +import co.rsk.core.types.bytes.Bytes; import co.rsk.crypto.Keccak256; import co.rsk.pcc.NativeContract; import co.rsk.peg.Bridge; @@ -581,7 +582,7 @@ private void refundRemainingGas(long gasLimit, ProgramResult programResult) { refundGas(refundGas, "remaining gas from the internal call"); if (isGasLogEnabled) { gasLogger.info("The remaining gas is refunded, account: [{}], gas: [{}] ", - ByteUtil.toHexString(getOwnerAddress().getLast20Bytes()), + Bytes.of(getOwnerAddress().getLast20Bytes()), refundGas ); } diff --git a/rskj-core/src/main/java/org/ethereum/vm/program/invoke/ProgramInvokeFactoryImpl.java b/rskj-core/src/main/java/org/ethereum/vm/program/invoke/ProgramInvokeFactoryImpl.java index 286c2662b19..258c3ecf416 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/program/invoke/ProgramInvokeFactoryImpl.java +++ b/rskj-core/src/main/java/org/ethereum/vm/program/invoke/ProgramInvokeFactoryImpl.java @@ -20,6 +20,7 @@ import co.rsk.core.Coin; import co.rsk.core.RskAddress; +import co.rsk.core.types.bytes.Bytes; import org.ethereum.core.Block; import org.ethereum.core.Repository; import org.ethereum.core.SignatureCache; @@ -118,19 +119,19 @@ public ProgramInvoke createProgramInvoke(Transaction tx, int txindex, Block bloc "minimumGasPrice={}\n", addr, - ByteUtil.toHexString(origin), - ByteUtil.toHexString(caller), + Bytes.of(origin), + Bytes.of(caller), balance, txGasPrice, new BigInteger(1, gas).longValue(), callValue, - ByteUtil.toHexString(data), - ByteUtil.toHexString(lastHash), - ByteUtil.toHexString(coinbase), + Bytes.of(data), + Bytes.of(lastHash), + Bytes.of(coinbase), timestamp, number, txindex, - ByteUtil.toHexString(difficulty), + Bytes.of(difficulty), gaslimit, minimumGasPrice); } @@ -190,20 +191,20 @@ public ProgramInvoke createProgramInvoke(Program program, DataWord toAddress, Da "difficulty={}\n" + "gaslimit={}\n" + "minimumGasPrice={}\n", - ByteUtil.toHexString(address.getLast20Bytes()), - ByteUtil.toHexString(origin.getLast20Bytes()), - ByteUtil.toHexString(caller.getLast20Bytes()), + Bytes.of(address.getLast20Bytes()), + Bytes.of(origin.getLast20Bytes()), + Bytes.of(caller.getLast20Bytes()), balance.toString(), txGasPrice.longValue(), agas, - ByteUtil.toHexString(callValue.getNoLeadZeroesData()), + Bytes.of(callValue.getNoLeadZeroesData()), data == null ? "" : ByteUtil.toHexString(data), - ByteUtil.toHexString(lastHash.getData()), - ByteUtil.toHexString(coinbase.getLast20Bytes()), + Bytes.of(lastHash.getData()), + Bytes.of(coinbase.getLast20Bytes()), timestamp.longValue(), number.longValue(), transactionIndex.intValue(), - ByteUtil.toHexString(difficulty.getNoLeadZeroesData()), + Bytes.of(difficulty.getNoLeadZeroesData()), gasLimit.bigIntValue(), minimumGasPrice.longValue()); } diff --git a/rskj-core/src/main/resources/config/main.conf b/rskj-core/src/main/resources/config/main.conf index e36939f99a2..da37b9165e1 100644 --- a/rskj-core/src/main/resources/config/main.conf +++ b/rskj-core/src/main/resources/config/main.conf @@ -13,6 +13,7 @@ blockchain.config { hop401 = 4976300, fingerroot500 = 5468000, arrowhead600 = 6223700, + arrowhead631 = 6549300, lovell700 = -1 } } diff --git a/rskj-core/src/main/resources/config/regtest.conf b/rskj-core/src/main/resources/config/regtest.conf index d0b95b4036c..d19ce575c34 100644 --- a/rskj-core/src/main/resources/config/regtest.conf +++ b/rskj-core/src/main/resources/config/regtest.conf @@ -13,6 +13,7 @@ blockchain.config { hop401 = 0, fingerroot500 = 0, arrowhead600 = 0, + arrowhead631 = -1, lovell700 = 0 }, consensusRules = { diff --git a/rskj-core/src/main/resources/config/testnet.conf b/rskj-core/src/main/resources/config/testnet.conf index d0da9e12899..cfe42731cd8 100644 --- a/rskj-core/src/main/resources/config/testnet.conf +++ b/rskj-core/src/main/resources/config/testnet.conf @@ -13,6 +13,7 @@ blockchain.config { hop401 = 3362200, fingerroot500 = 4015800, arrowhead600 = 4927100, + arrowhead631 = -1, lovell700 = -1 }, consensusRules = { diff --git a/rskj-core/src/main/resources/expected.conf b/rskj-core/src/main/resources/expected.conf index 77f384ff6b7..5b440a1367e 100644 --- a/rskj-core/src/main/resources/expected.conf +++ b/rskj-core/src/main/resources/expected.conf @@ -16,6 +16,7 @@ blockchain = { hop401 = fingerroot500 = arrowhead600 = + arrowhead631 = lovell700 = } consensusRules = { @@ -94,6 +95,7 @@ blockchain = { rskip415 = rskip417 = rskip428 = + rskip434 = } } gc = { diff --git a/rskj-core/src/main/resources/reference.conf b/rskj-core/src/main/resources/reference.conf index 2789bb1c7f1..8228aa620d1 100644 --- a/rskj-core/src/main/resources/reference.conf +++ b/rskj-core/src/main/resources/reference.conf @@ -80,6 +80,7 @@ blockchain = { rskip415 = arrowhead600 rskip417 = arrowhead600 rskip428 = lovell700 + rskip434 = arrowhead631 } } gc = { diff --git a/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java b/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java new file mode 100644 index 00000000000..93707953604 --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java @@ -0,0 +1,77 @@ +/* + * This file is part of RskJ + * Copyright (C) 2024 RSK Labs Ltd. + * (derived from ethereumJ library, Copyright (c) 2016 ) + * + * 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.core.types.bytes; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class BytesSliceTest { + + @Test + void testCopyArrayOfRange() { + byte[] bArray = new byte[]{1, 2, 3, 4, 5, 6}; + byte[] expectedResult = new byte[]{3, 4, 5}; + assertArrayEquals(expectedResult, Bytes.of(bArray).slice(0, bArray.length).copyArrayOfRange(2, 5)); + } + + @Test + void testCopyArray() { + byte[] bArray = new byte[]{1, 2, 3, 4, 5, 6}; + byte[] expectedResult = new byte[]{3, 4, 5}; + byte[] actualResult = Bytes.of(bArray).slice(2, 5).copyArray(); + assertNotSame(expectedResult, actualResult); // refs are different + assertArrayEquals(expectedResult, actualResult); + } + + @Test + void testCopyBytesOfRange() { + byte[] bArray = new byte[]{1, 2, 3, 4, 5, 6}; + Bytes expectedResult = Bytes.of(new byte[]{3, 4, 5}); + assertTrue(Bytes.equalBytes(expectedResult, Bytes.of(bArray).slice(0, bArray.length).copyBytesOfRange(2, 5))); + } + + @Test + void testCopyBytes() { + byte[] bArray = new byte[]{1, 2, 3, 4, 5, 6}; + Bytes expectedResult = Bytes.of(new byte[]{3, 4, 5}); + Bytes actualResult = Bytes.of(bArray).slice(2, 5).copyBytes(); + assertTrue(Bytes.equalBytes(expectedResult, actualResult)); + assertArrayEquals(expectedResult.asUnsafeByteArray(), actualResult.asUnsafeByteArray()); + } + + @Test + void testSlice() { + byte[] bArray = new byte[]{1, 2, 3, 4, 5, 6}; + BytesSlice actualResult = Bytes.of(bArray).slice(1, 6).slice(1, 4).slice(1, 3); + byte[] expectedResult = new byte[]{4, 5}; + assertEquals(2, actualResult.length()); + assertArrayEquals(expectedResult, actualResult.copyArray()); + } + + @Test + void testEmptySlice() { + byte[] bArray = new byte[]{1, 2, 3, 4, 5, 6}; + BytesSlice actualResult = Bytes.of(bArray).slice(1, 6).slice(1, 4).slice(1, 3).slice(0, 0); + byte[] expectedResult = new byte[]{}; + assertEquals(0, actualResult.length()); + assertArrayEquals(expectedResult, actualResult.copyArray()); + } +} diff --git a/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesTest.java b/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesTest.java new file mode 100644 index 00000000000..b60ca431ddc --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesTest.java @@ -0,0 +1,119 @@ +/* + * This file is part of RskJ + * Copyright (C) 2024 RSK Labs Ltd. + * (derived from ethereumJ library, Copyright (c) 2016 ) + * + * 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.core.types.bytes; + +import org.ethereum.TestUtils; +import org.ethereum.util.ByteUtil; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class BytesTest { + + @Test + void testBytesOf() { + assertNull(Bytes.of(null)); + assertNotNull(Bytes.of(new byte[]{})); + assertNotNull(Bytes.of(new byte[]{1})); + } + + @Test + void testToPrintableString() { + assertEquals("0a", Bytes.toPrintableString(new byte[]{10})); + } + + @Test + void testToPrintableStringWithNull() { + assertEquals("", Bytes.toPrintableString((byte[]) null)); + } + + @Test + void testToPrintableStringWithDefaultValue() { + assertEquals("xyz", Bytes.toPrintableString(null, "xyz")); + } + + @Test + void testAsUnsafeByteArray() { + byte[] bArray = {1, 2, 3}; + assertTrue(bArray == Bytes.of(bArray).asUnsafeByteArray()); + } + + @Test + void testEqualBytes() { + byte[] b1Array = {1, 2, 3}; + byte[] b2Array = {1, 2, 3}; + assertTrue(Bytes.equalBytes(Bytes.of(b1Array), Bytes.of(b2Array))); + } + + @Test + void testBytesToString() { + byte[] bArray = new byte[]{1, 2, 3, 5, 8, 13}; + String actualMessage = String.format("Some '%s' hex", Bytes.of(bArray)); + String expectedMessage = "Some '" + ByteUtil.toHexString(bArray) + "' hex"; + + assertEquals(expectedMessage, actualMessage); + } + + @Test + void testShortEnoughBytesToString() { + byte[] bArray = TestUtils.generateBytes("hash",32); + + String actualMessage = String.format("Some '%s' hex", Bytes.of(bArray)); + String expectedMessage = "Some '" + ByteUtil.toHexString(bArray) + "' hex"; + + assertEquals(expectedMessage, actualMessage); + } + + @Test + void testLongBytesToString() { + byte[] bArray1 = TestUtils.generateBytes("hash1",15); + byte[] bArray2 = TestUtils.generateBytes("hash2",15); + byte[] finalArray = ByteUtil.merge(bArray1, new byte[]{1, 2, 3}, bArray2); + + assertEquals(33, finalArray.length); + + String actualMessage = String.format("Some '%s' hex", Bytes.of(finalArray)); + String expectedMessage = "Some '" + + ByteUtil.toHexString(bArray1) + + ".." + + ByteUtil.toHexString(bArray2) + + "' hex"; + + assertEquals(expectedMessage, actualMessage); + } + + @Test + void testNullBytesToString() { + byte[] bArray = null; + String actualMessage = String.format("Some '%s' hex", Bytes.of(bArray)); + String expectedMessage = "Some '" + null + "' hex"; + + assertEquals(expectedMessage, actualMessage); + } + + @Test + void testEmptyBytesToString() { + byte[] bArray = new byte[]{}; + String actualMessage = String.format("Some '%s' hex", Bytes.of(bArray)); + String expectedMessage = "Some '' hex"; + + assertEquals(expectedMessage, actualMessage); + } +} \ No newline at end of file diff --git a/rskj-core/src/test/java/co/rsk/pcc/bto/ExtractPublicKeyFromExtendedPublicKeyTest.java b/rskj-core/src/test/java/co/rsk/pcc/bto/ExtractPublicKeyFromExtendedPublicKeyTest.java index ba09d62c288..1fbe8101c0b 100644 --- a/rskj-core/src/test/java/co/rsk/pcc/bto/ExtractPublicKeyFromExtendedPublicKeyTest.java +++ b/rskj-core/src/test/java/co/rsk/pcc/bto/ExtractPublicKeyFromExtendedPublicKeyTest.java @@ -79,7 +79,7 @@ void validatesExtendedPublicKeyFormat() { }); Assertions.fail(); } catch (NativeContractIllegalArgumentException e) { - Assertions.assertTrue(e.getMessage().contains("Invalid extended public key")); + Assertions.assertEquals("Invalid extended public key 'this-is-not-an-xpub'", e.getMessage()); } } @@ -91,7 +91,7 @@ void failsUponInvalidPublicKey() { }); Assertions.fail(); } catch (NativeContractIllegalArgumentException e) { - Assertions.assertTrue(e.getMessage().contains("Invalid extended public key")); + Assertions.assertEquals("Invalid extended public key 'tpubD6NzVbkrYhZ4YHQqwWz3Tm1ESZ9AidobeyLG4mEezB6hN8gFFWrcjczyF77L...'", e.getMessage()); } } @@ -101,7 +101,7 @@ void failsUponNull() { method.execute(null); Assertions.fail(); } catch (NativeContractIllegalArgumentException e) { - Assertions.assertTrue(e.getMessage().contains("Invalid extended public key")); + Assertions.assertEquals("Invalid extended public key 'null'", e.getMessage()); } } diff --git a/rskj-core/src/test/java/co/rsk/pcc/bto/HDWalletUtilsHelperTest.java b/rskj-core/src/test/java/co/rsk/pcc/bto/HDWalletUtilsHelperTest.java index 2e4dbad3eb4..7722487229c 100644 --- a/rskj-core/src/test/java/co/rsk/pcc/bto/HDWalletUtilsHelperTest.java +++ b/rskj-core/src/test/java/co/rsk/pcc/bto/HDWalletUtilsHelperTest.java @@ -25,6 +25,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + class HDWalletUtilsHelperTest { private HDWalletUtilsHelper helper; @@ -35,7 +37,7 @@ void createHelper() { @Test void validateAndExtractNetworkFromExtendedPublicKeyMainnet() throws NativeContractIllegalArgumentException { - Assertions.assertEquals( + assertEquals( NetworkParameters.fromID(NetworkParameters.ID_MAINNET), helper.validateAndExtractNetworkFromExtendedPublicKey("xpubSomethingSomething") ); @@ -43,14 +45,25 @@ void validateAndExtractNetworkFromExtendedPublicKeyMainnet() throws NativeContra @Test void validateAndExtractNetworkFromExtendedPublicKeyTestnet() throws NativeContractIllegalArgumentException { - Assertions.assertEquals( + assertEquals( NetworkParameters.fromID(NetworkParameters.ID_TESTNET), helper.validateAndExtractNetworkFromExtendedPublicKey("tpubSomethingSomething") ); } @Test - void validateAndExtractNetworkFromExtendedPublicKeyInvalid() { - Assertions.assertThrows(NativeContractIllegalArgumentException.class, () -> helper.validateAndExtractNetworkFromExtendedPublicKey("completelyInvalidStuff")); + void validateAndExtractNetworkFromExtendedPublicKeyWithInvalidXpub() { + NativeContractIllegalArgumentException exception = Assertions.assertThrows( + NativeContractIllegalArgumentException.class, + () -> helper.validateAndExtractNetworkFromExtendedPublicKey("completelyInvalidStuff")); + assertEquals("Invalid extended public key 'completelyInvalidStuff'", exception.getMessage()); + } + + @Test + void validateAndExtractNetworkFromExtendedPublicKeyWithInvalidLongXpub() { + NativeContractIllegalArgumentException exception = Assertions.assertThrows( + NativeContractIllegalArgumentException.class, + () -> helper.validateAndExtractNetworkFromExtendedPublicKey("completelyInvalidLongLongLongLongLongLongLongLongLongLongLonStuff")); + assertEquals("Invalid extended public key 'completelyInvalidLongLongLongLongLongLongLongLongLongLongLonStuf...'", exception.getMessage()); } } diff --git a/rskj-core/src/test/java/co/rsk/peg/BridgeSupportTest.java b/rskj-core/src/test/java/co/rsk/peg/BridgeSupportTest.java index ed1aac76bb5..c63c0c185ee 100644 --- a/rskj-core/src/test/java/co/rsk/peg/BridgeSupportTest.java +++ b/rskj-core/src/test/java/co/rsk/peg/BridgeSupportTest.java @@ -45,6 +45,7 @@ import co.rsk.peg.whitelist.LockWhitelist; import co.rsk.peg.whitelist.OneOffWhiteListEntry; import co.rsk.test.builders.BridgeSupportBuilder; +import co.rsk.util.HexUtils; import org.bouncycastle.util.encoders.Hex; import org.ethereum.TestUtils; import org.ethereum.config.Constants; @@ -56,9 +57,7 @@ import org.ethereum.crypto.HashUtil; import org.ethereum.vm.PrecompiledContracts; import org.ethereum.vm.exception.VMException; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -6229,6 +6228,259 @@ void receiveHeader_block_exist_in_storage() throws IOException, BlockStoreExcept Assertions.assertEquals(-4, result); } + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + @Tag("test chain work before and after rskip 434") + class ChainWorkTests { + ActivationConfig.ForBlock activationsPreRSKIP434 = ActivationConfigsForTest.arrowhead600().forBlock(0); + ActivationConfig.ForBlock activationsPostRSKIP434 = ActivationConfigsForTest.arrowhead631().forBlock(0); + Repository repository; + BtcBlockStoreWithCache.Factory btcBlockStoreFactory; + BtcBlockStoreWithCache btcBlockStoreWithCachePreRSKIP434; + BtcBlockStoreWithCache btcBlockStoreWithCachePostRSKIP434; + + BridgeSupportBuilder bridgeSupportBuilder = new BridgeSupportBuilder(); + BridgeSupport bridgeSupportPreRSKIP434; + BridgeSupport bridgeSupportPostRSKIP434; + + BtcBlock block849134; + BtcBlock block849135; + BtcBlock block849136; + BtcBlock block849137; + BtcBlock blockWithTooMuchWork; + BtcBlock block849139; + + @BeforeEach + void setUp() { + BridgeConstants bridgeMainnetConstants = BridgeMainNetConstants.getInstance(); + repository = createRepository(); + btcBlockStoreFactory = new RepositoryBtcBlockStoreWithCache.Factory(bridgeMainnetConstants.getBtcParams(), 100, 100); + + // recreate context pre rskip 434 for mainnet + BridgeStorageProvider bridgeStorageProviderPreRSKIP434 = new BridgeStorageProvider( + repository, + PrecompiledContracts.BRIDGE_ADDR, + bridgeMainnetConstants, + activationsPreRSKIP434 + ); + bridgeSupportPreRSKIP434 = bridgeSupportBuilder + .withBridgeConstants(bridgeMainnetConstants) + .withProvider(bridgeStorageProviderPreRSKIP434) + .withRepository(repository) + .withBtcBlockStoreFactory(btcBlockStoreFactory) + .withActivations(activationsPreRSKIP434) + .build(); + btcBlockStoreWithCachePreRSKIP434 = + btcBlockStoreFactory.newInstance(repository, bridgeMainnetConstants, bridgeStorageProviderPreRSKIP434, activationsPreRSKIP434); + + // recreate context post rskip 434 for mainnet + BridgeStorageProvider bridgeStorageProviderPostRSKIP434 = new BridgeStorageProvider( + repository, + PrecompiledContracts.BRIDGE_ADDR, + bridgeMainnetConstants, + activationsPostRSKIP434 + ); + bridgeSupportPostRSKIP434 = bridgeSupportBuilder + .withBridgeConstants(bridgeMainnetConstants) + .withProvider(bridgeStorageProviderPostRSKIP434) + .withRepository(repository) + .withBtcBlockStoreFactory(btcBlockStoreFactory) + .withActivations(activationsPostRSKIP434) + .build(); + btcBlockStoreWithCachePostRSKIP434 = + btcBlockStoreFactory.newInstance(repository, bridgeMainnetConstants, bridgeStorageProviderPostRSKIP434, activationsPostRSKIP434); + + String block849134Header = "0080b92c24f123130ae29e899f0cab72653722e54cdf3b30445202000000000000000000c72ead65a3b78ab637d1876c00414a77e47bcc5b52667ac1e573633563bea5a695aa7766255d031728d182a8"; + block849134 = new BtcBlock(bridgeMainnetConstants.getBtcParams(), HexUtils.stringHexToByteArray(block849134Header)); + + String block849135Header = "00004020bf67910b5d3996ee594848b482ee84d0e28c97a9a2d601000000000000000000865e218552bb92df36c962f5163e84a6c2542584fb36be2fa8b2a4246c73a701f1ae7766255d031791836b22"; + block849135 = new BtcBlock(bridgeMainnetConstants.getBtcParams(), HexUtils.stringHexToByteArray(block849135Header)); + + String block849136Header = "0000003a796f8b7a9d6ba6e13064e7c64e94570f877170262f1f0200000000000000000036b2ab17565a24a9be4626ca801cb31f91232034ba848295475f931a58dd5446e5b07766255d03173b01a491"; + block849136 = new BtcBlock(bridgeMainnetConstants.getBtcParams(), HexUtils.stringHexToByteArray(block849136Header)); + + String block849137Header = "00e00820925b77c9ff4d0036aa29f3238cde12e9af9d55c34ed30200000000000000000032a9fa3e12ef87a2327b55db6a16a1227bb381db8b269d90aa3a6e38cf39665f91b47766255d0317c1b1575f"; + block849137 = new BtcBlock(bridgeMainnetConstants.getBtcParams(), HexUtils.stringHexToByteArray(block849137Header)); + + String blockWithTooMuchWorkHeader = "006001207ca158816ffc9d45b9ecd6a49ffbf3038f3646cf13fc01000000000000000000e0182ce7cc10db785b5fb2fb4314053f5b12cd6116168797cb461aa339fc725078b87766255d0317ba5261e2"; + blockWithTooMuchWork = new BtcBlock(bridgeMainnetConstants.getBtcParams(), HexUtils.stringHexToByteArray(blockWithTooMuchWorkHeader)); + + String block849139Header = "00a0b625ffa2f7cbf95219fc74c3db38f84ae265784bc1417c71020000000000000000008e5b319a229376089f4a7b77c90ed90ac19a0532fc4c62426f5a5931ee7e3e8dd2c67766255d03171e91a015"; + block849139 = new BtcBlock(bridgeMainnetConstants.getBtcParams(), HexUtils.stringHexToByteArray(block849139Header)); + } + + @Test + void receiveHeader_beforeRSKIP434_returnsUnexpectedExceptionResponseCodeAndDoesNotSaveTheBlock() throws BlockStoreException, IOException { + // Create block with too much work parent with cumulative difficulty + BigInteger block849137ChainWork = new BigInteger("00000000000000000000000000000000000000007fffdc6f043e4a69ea179a7a", 16); + StoredBlock block849137ToStore = new StoredBlock(block849137, block849137ChainWork, 849137); + + // save parent in storage + btcBlockStoreWithCachePreRSKIP434.put(block849137ToStore); + btcBlockStoreWithCachePreRSKIP434.setChainHead(block849137ToStore); + repository.save(); + // assert that parent was correctly saved + assertEquals(btcBlockStoreWithCachePreRSKIP434.getChainHead().getHeader().getHash(), block849137ToStore.getHeader().getHash()); + + // assert receive header returns an exception and does not save the block + Integer RECEIVE_HEADER_UNEXPECTED_EXCEPTION = -99; + assertThat(bridgeSupportPreRSKIP434.receiveHeader(blockWithTooMuchWork), is(RECEIVE_HEADER_UNEXPECTED_EXCEPTION)); + assertNull(btcBlockStoreWithCachePreRSKIP434.get(blockWithTooMuchWork.getHash())); + } + + @Test + void receiveHeaders_beforeRSKIP434_savesBlocksUntilReachesBlockWithTooMuchChainWork() throws BlockStoreException, IOException { + // Create block 849134 with cumulative difficulty + BigInteger block849134ChainWork = new BigInteger("00000000000000000000000000000000000000007ffef81fa11393037c9df17b", 16); + StoredBlock block849134ToStore = new StoredBlock(block849134, block849134ChainWork, 849134); + + // save block 849134 in storage + btcBlockStoreWithCachePreRSKIP434.put(block849134ToStore); + btcBlockStoreWithCachePreRSKIP434.setChainHead(block849134ToStore); + repository.save(); + // assert that block 849134 was correctly saved + assertEquals(btcBlockStoreWithCachePreRSKIP434.getChainHead().getHeader().getHash(), block849134.getHash()); + + BtcBlock[] headersToSend = new BtcBlock[] { block849135, block849136, block849137, blockWithTooMuchWork, block849139 }; + // assert blocks until 849137 are correctly saved + // and blocks from 849138 are not saved + bridgeSupportPreRSKIP434.receiveHeaders(headersToSend); + assertNotNull(btcBlockStoreWithCachePreRSKIP434.get(block849135.getHash())); + assertNotNull(btcBlockStoreWithCachePreRSKIP434.get(block849136.getHash())); + assertNotNull(btcBlockStoreWithCachePreRSKIP434.get(block849137.getHash())); + assertNull(btcBlockStoreWithCachePreRSKIP434.get(blockWithTooMuchWork.getHash())); + assertNull(btcBlockStoreWithCachePreRSKIP434.get(block849139.getHash())); + } + + @Test + void receiveHeader_afterRSKIP434_returnsSuccessfulAndSavesTheBlock() throws BlockStoreException, IOException { + // Create block with too much work parent with cumulative difficulty + BigInteger block849137ChainWork = new BigInteger("00000000000000000000000000000000000000007fffdc6f043e4a69ea179a7a", 16); + StoredBlock block849137ToStore = new StoredBlock(block849137, block849137ChainWork, 849137); + + // save parent in storage + btcBlockStoreWithCachePostRSKIP434.put(block849137ToStore); + btcBlockStoreWithCachePostRSKIP434.setChainHead(block849137ToStore); + repository.save(); + // assert that previous block was correctly saved + assertEquals(btcBlockStoreWithCachePostRSKIP434.getChainHead().getHeader().getHash(), block849137ToStore.getHeader().getHash()); + + // assert receive header returns successful response and saves the block + Integer RECEIVE_HEADER_SUCCESSFUL = 0; + assertThat(bridgeSupportPostRSKIP434.receiveHeader(blockWithTooMuchWork), is(RECEIVE_HEADER_SUCCESSFUL)); + assertNotNull(btcBlockStoreWithCachePostRSKIP434.get(blockWithTooMuchWork.getHash())); + } + + @Test + void receiveHeaders_afterRSKIP434_savesAllBlocks() throws BlockStoreException, IOException { + // Create block 849134 with cumulative difficulty + BigInteger block849134ChainWork = new BigInteger("00000000000000000000000000000000000000007ffef81fa11393037c9df17b", 16); + StoredBlock block849134ToStore = new StoredBlock(block849134, block849134ChainWork, 849134); + + // save block 849134 in storage + btcBlockStoreWithCachePostRSKIP434.put(block849134ToStore); + btcBlockStoreWithCachePostRSKIP434.setChainHead(block849134ToStore); + repository.save(); + // assert that block 849134 was correctly saved + assertEquals(btcBlockStoreWithCachePostRSKIP434.getChainHead().getHeader().getHash(), block849134.getHash()); + + BtcBlock[] headersToSend = new BtcBlock[] { block849135, block849136, block849137, blockWithTooMuchWork, block849139 }; + // assert all blocks are correctly saved + bridgeSupportPostRSKIP434.receiveHeaders(headersToSend); + assertNotNull(btcBlockStoreWithCachePostRSKIP434.get(block849135.getHash())); + assertNotNull(btcBlockStoreWithCachePostRSKIP434.get(block849136.getHash())); + assertNotNull(btcBlockStoreWithCachePostRSKIP434.get(block849137.getHash())); + assertNotNull(btcBlockStoreWithCachePostRSKIP434.get(blockWithTooMuchWork.getHash())); + assertNotNull(btcBlockStoreWithCachePostRSKIP434.get(block849139.getHash())); + } + + @ParameterizedTest + @MethodSource("notMainnetAndActivationsArgs") + void receiveHeader_networkNotMainnet_returnsSuccessfulAndSavesTheBlock(ActivationConfig.ForBlock activations, BridgeConstants bridgeConstants) throws BlockStoreException, IOException { + BridgeStorageProvider bridgeStorageProvider = new BridgeStorageProvider( + repository, + PrecompiledContracts.BRIDGE_ADDR, + bridgeConstants, + activations + ); + BridgeSupport bridgeSupport = bridgeSupportBuilder + .withBridgeConstants(bridgeConstants) + .withProvider(bridgeStorageProvider) + .withRepository(repository) + .withBtcBlockStoreFactory(btcBlockStoreFactory) + .withActivations(activations) + .build(); + BtcBlockStoreWithCache btcBlockStoreWithCache = btcBlockStoreFactory.newInstance(repository, bridgeConstants, bridgeStorageProvider, activations); + + // Create block with too much work parent with cumulative difficulty + BigInteger block849137ChainWork = new BigInteger("00000000000000000000000000000000000000007fffdc6f043e4a69ea179a7a", 16); + StoredBlock block849137ToStore = new StoredBlock(block849137, block849137ChainWork, 849137); + + // save parent in storage + btcBlockStoreWithCache.put(block849137ToStore); + btcBlockStoreWithCache.setChainHead(block849137ToStore); + repository.save(); + // assert that previous block was correctly saved + assertEquals(btcBlockStoreWithCache.getChainHead().getHeader().getHash(), block849137ToStore.getHeader().getHash()); + + // assert receive header returns successful response and saves the block + Integer RECEIVE_HEADER_SUCCESSFUL = 0; + assertThat(bridgeSupport.receiveHeader(blockWithTooMuchWork), is(RECEIVE_HEADER_SUCCESSFUL)); + assertNotNull(btcBlockStoreWithCache.get(blockWithTooMuchWork.getHash())); + } + + @ParameterizedTest + @MethodSource("notMainnetAndActivationsArgs") + void receiveHeaders_networkNotMainnet_savesAllBlocks(ActivationConfig.ForBlock activations, BridgeConstants bridgeConstants) throws BlockStoreException, IOException { + BridgeStorageProvider bridgeStorageProvider = new BridgeStorageProvider( + repository, + PrecompiledContracts.BRIDGE_ADDR, + bridgeConstants, + activations + ); + BridgeSupport bridgeSupport = bridgeSupportBuilder + .withBridgeConstants(bridgeConstants) + .withProvider(bridgeStorageProvider) + .withRepository(repository) + .withBtcBlockStoreFactory(btcBlockStoreFactory) + .withActivations(activations) + .build(); + BtcBlockStoreWithCache btcBlockStoreWithCache = btcBlockStoreFactory.newInstance(repository, bridgeConstants, bridgeStorageProvider, activations); + + // Create block 849134 with cumulative difficulty + BigInteger block849134ChainWork = new BigInteger("00000000000000000000000000000000000000007ffef81fa11393037c9df17b", 16); + StoredBlock block849134ToStore = new StoredBlock(block849134, block849134ChainWork, 849134); + + // save block 849134 in storage + btcBlockStoreWithCache.put(block849134ToStore); + btcBlockStoreWithCache.setChainHead(block849134ToStore); + repository.save(); + // assert that block 849134 was correctly saved + assertEquals(btcBlockStoreWithCache.getChainHead().getHeader().getHash(), block849134.getHash()); + + BtcBlock[] headersToSend = new BtcBlock[] { block849135, block849136, block849137, blockWithTooMuchWork, block849139 }; + // assert all blocks are correctly saved + bridgeSupport.receiveHeaders(headersToSend); + assertNotNull(btcBlockStoreWithCache.get(block849135.getHash())); + assertNotNull(btcBlockStoreWithCache.get(block849136.getHash())); + assertNotNull(btcBlockStoreWithCache.get(block849137.getHash())); + assertNotNull(btcBlockStoreWithCache.get(blockWithTooMuchWork.getHash())); + assertNotNull(btcBlockStoreWithCache.get(block849139.getHash())); + } + + private Stream notMainnetAndActivationsArgs() { + BridgeConstants testnet = BridgeTestNetConstants.getInstance(); + BridgeConstants regtest = BridgeRegTestConstants.getInstance(); + + return Stream.of( + Arguments.of(activationsPreRSKIP434, testnet), + Arguments.of(activationsPostRSKIP434, testnet), + Arguments.of(activationsPreRSKIP434, regtest), + Arguments.of(activationsPostRSKIP434, regtest) + ); + } + } + private void assertRefundInProcessPegInVersionLegacy( boolean isWhitelisted, boolean mockLockingCap, diff --git a/rskj-core/src/test/java/co/rsk/peg/BridgeSupportTestIntegration.java b/rskj-core/src/test/java/co/rsk/peg/BridgeSupportTestIntegration.java index de8c31376b9..50b7b0d6465 100644 --- a/rskj-core/src/test/java/co/rsk/peg/BridgeSupportTestIntegration.java +++ b/rskj-core/src/test/java/co/rsk/peg/BridgeSupportTestIntegration.java @@ -938,10 +938,29 @@ void addBlockHeaderToBlockchain() throws IOException, BlockStoreException { BridgeSupport bridgeSupport = getBridgeSupport(provider, track, mockFactory, activations); BtcBlockChain btcBlockChain = new SimpleBlockChain(btcContext, btcBlockStore); TestUtils.setInternalState(bridgeSupport, "btcBlockChain", btcBlockChain); + + Sha256Hash merkleRoot = PegTestUtils.createHash(2); + + co.rsk.bitcoinj.core.BtcBlock prevBlock = new co.rsk.bitcoinj.core.BtcBlock( + btcParams, + 1, + PegTestUtils.createHash(1), // hash from its previous block + merkleRoot, + 1, + 1, + 1, + new ArrayList<>() + ); + BigInteger prevBlockChainWork = new BigInteger("ffffffffffffffff", 16); + StoredBlock prevStoredBlock = new StoredBlock(prevBlock, prevBlockChainWork, 1); + // save previous block in storage, so we are able to build next block from it + btcBlockStore.put(prevStoredBlock); + track.save(); + co.rsk.bitcoinj.core.BtcBlock block = new co.rsk.bitcoinj.core.BtcBlock( btcParams, 1, - PegTestUtils.createHash(1), + prevBlock.getHash(), PegTestUtils.createHash(2), 1, 1, diff --git a/rskj-core/src/test/java/co/rsk/util/StringUtilsTest.java b/rskj-core/src/test/java/co/rsk/util/StringUtilsTest.java new file mode 100644 index 00000000000..2cd58093e6f --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/util/StringUtilsTest.java @@ -0,0 +1,55 @@ +/* + * This file is part of RskJ + * Copyright (C) 2024 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.util; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class StringUtilsTest { + + private static final String STR_64_CHARS = "ee5c851e70650111887bb6c04e18ef4353391abe37846234c17895a9ca2b33d5"; + private static final String STR_65_CHARS = STR_64_CHARS + "a"; + + @Test + void testTrim() { + assertNull(StringUtils.trim(null)); + assertEquals("", StringUtils.trim("")); + assertEquals("a", StringUtils.trim("a")); + + assertEquals(64, STR_64_CHARS.length()); + assertEquals(65, STR_65_CHARS.length()); + + assertEquals(STR_64_CHARS, StringUtils.trim(STR_64_CHARS)); + assertEquals(STR_64_CHARS + "...", StringUtils.trim(STR_65_CHARS)); + } + + @Test + void testTrimWithMaxLength() { + assertEquals("...", StringUtils.trim("abc", 0)); + assertEquals("abc", StringUtils.trim("abc", 3)); + assertEquals("abc", StringUtils.trim("abc", 4)); + assertEquals("abc...", StringUtils.trim("abcd", 3)); + } + + @Test + void testTrimWithInvalidMaxLength() { + assertThrows(IllegalArgumentException.class, () -> StringUtils.trim("abc", -1)); + } +} diff --git a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java index 00f4affaff2..0593855f481 100644 --- a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java +++ b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java @@ -43,6 +43,7 @@ class ActivationConfigTest { " hop401: 0", " fingerroot500: 0", " arrowhead600: 0", + " arrowhead631: 0", " lovell700: 0", "},", "consensusRules: {", @@ -120,6 +121,7 @@ class ActivationConfigTest { " rskip412: arrowhead600", " rskip415: arrowhead600", " rskip417: arrowhead600", + " rskip434: arrowhead631", " rskip428: lovell700", "}" )); diff --git a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigsForTest.java b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigsForTest.java index a1b080a2b57..dc85d0e61ed 100644 --- a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigsForTest.java +++ b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigsForTest.java @@ -191,6 +191,15 @@ private static List getArrowhead600Rskips() { return rskips; } + private static List getArrowhead631Rskips() { + List rskips = new ArrayList<>(); + rskips.addAll(Collections.singletonList( + ConsensusRule.RSKIP434 + )); + + return rskips; + } + private static List getLovell700Rskips() { List rskips = new ArrayList<>(); rskips.addAll(Arrays.asList( @@ -341,6 +350,29 @@ public static ActivationConfig arrowhead600(List except) { return enableTheseDisableThose(rskips, except); } + public static ActivationConfig arrowhead631() { + return arrowhead631(Collections.emptyList()); + } + + public static ActivationConfig arrowhead631(List except) { + List rskips = new ArrayList<>(); + rskips.addAll(getPaidBridgeTxsRskip()); + rskips.addAll(getOrchidRskips()); + rskips.addAll(getOrchid060Rskips()); + rskips.addAll(getWasabi100Rskips()); + rskips.addAll(getBahamasRskips()); + rskips.addAll(getTwoToThreeRskips()); + rskips.addAll(getPapyrus200Rskips()); + rskips.addAll(getIris300Rskips()); + rskips.addAll(getHop400Rskips()); + rskips.addAll(getHop401Rskips()); + rskips.addAll(getFingerroot500Rskips()); + rskips.addAll(getArrowhead600Rskips()); + rskips.addAll(getArrowhead631Rskips()); + + return enableTheseDisableThose(rskips, except); + } + public static ActivationConfig lovell700(List except) { List rskips = new ArrayList<>(); rskips.addAll(getPaidBridgeTxsRskip()); @@ -355,6 +387,7 @@ public static ActivationConfig lovell700(List except) { rskips.addAll(getHop401Rskips()); rskips.addAll(getFingerroot500Rskips()); rskips.addAll(getArrowhead600Rskips()); + rskips.addAll(getArrowhead631Rskips()); rskips.addAll(getLovell700Rskips()); return enableTheseDisableThose(rskips, except); diff --git a/rskj-core/src/test/java/org/ethereum/core/BloomTest.java b/rskj-core/src/test/java/org/ethereum/core/BloomTest.java index fc09358c9ab..798775ba7e7 100644 --- a/rskj-core/src/test/java/org/ethereum/core/BloomTest.java +++ b/rskj-core/src/test/java/org/ethereum/core/BloomTest.java @@ -19,6 +19,7 @@ package org.ethereum.core; +import co.rsk.core.types.bytes.Bytes; import org.ethereum.crypto.HashUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -30,10 +31,8 @@ */ class BloomTest { - @Test /// based on http://bit.ly/1MtXxFg - void test1(){ - + void test1() { byte[] address = Hex.decode("095e7baea6a6c7c4c2dfeb977efac326af552d87"); Bloom addressBloom = Bloom.create(HashUtil.keccak256(address)); @@ -44,10 +43,9 @@ void test1(){ totalBloom.or(addressBloom); totalBloom.or(topicBloom); - Assertions.assertEquals( "00000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000040000000000000000000000000000000000000000000000000000000", - totalBloom.toString() + Bytes.of(totalBloom.getData()).toHexString() ); }