From a391b1861daecc0807023d8f02e6e1a6e725ebe1 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Thu, 18 Jun 2020 18:44:09 +0200 Subject: [PATCH 01/64] Remove prefix when signing --- .../internalresponse/EthSignBodyProvider.java | 4 +--- .../jsonrpcproxy/EthSignBodyProviderTest.java | 15 +++++++-------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignBodyProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignBodyProvider.java index 14c979a73..aee7c9c44 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignBodyProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignBodyProvider.java @@ -66,9 +66,7 @@ public JsonRpcBody getBody(final JsonRpcRequest request) { } final TransactionSigner signer = transactionSigner.get(); final String originalMessage = params.get(1); - final String message = - (char) 25 + "Ethereum Signed Message:\n" + originalMessage.length() + originalMessage; - final Signature signature = signer.sign(message.getBytes(StandardCharsets.UTF_8)); + final Signature signature = signer.sign(originalMessage.getBytes(StandardCharsets.UTF_8)); final Bytes outputSignature = Bytes.concatenate( diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignBodyProviderTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignBodyProviderTest.java index ad45243fb..e803f6449 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignBodyProviderTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignBodyProviderTest.java @@ -29,6 +29,7 @@ import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; import java.math.BigInteger; +import java.nio.charset.Charset; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -44,6 +45,7 @@ import org.junit.jupiter.params.provider.NullSource; import org.web3j.crypto.ECDSASignature; import org.web3j.crypto.ECKeyPair; +import org.web3j.crypto.Hash; import org.web3j.crypto.Sign; import org.web3j.utils.Numeric; @@ -137,12 +139,11 @@ public void returnsExpectedSignature() { final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_sign"); final int id = 1; + final String message = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Tubulum" + + " fuisse, qua illum, cuius is condemnatus est rogatione, P. Eaedem res maneant alio modo."; request.setId(new JsonRpcRequestId(id)); - request.setParams( - List.of( - "address", - "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Tubulum" - + " fuisse, qua illum, cuius is condemnatus est rogatione, P. Eaedem res maneant alio modo.")); + request.setParams(List.of("address", message)); final JsonRpcBody body = bodyProvider.getBody(request); assertThat(body.hasError()).isFalse(); @@ -151,9 +152,7 @@ public void returnsExpectedSignature() { final byte[] signature = Numeric.hexStringToByteArray(hexSignature); final ECDSASignature expectedSignature = - keyPair.sign( - Numeric.hexStringToByteArray( - "0xe63325d74baa84af003dfb6a974f41672be881b56aa2c12c093f8259321bd460")); + keyPair.sign(Hash.sha3(message.getBytes(Charset.defaultCharset()))); assertThat(new BigInteger(1, signature, 0, 32)).isEqualTo(expectedSignature.r); assertThat(new BigInteger(1, signature, 32, 32)).isEqualTo(expectedSignature.s); } From 8405a848d387569d8bd18721810c1a37d24a9ee2 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Mon, 22 Jun 2020 14:22:17 +0200 Subject: [PATCH 02/64] Alpha version 1.0.3 --- acceptance-tests/build.gradle | 2 + build.gradle | 1 + .../internalresponse/EthSignBodyProvider.java | 5 +- ethsigner/subcommands/build.gradle | 2 + .../subcommands/CaviumSubCommand.java | 93 ++++++++++++++++ .../ethsigner/subcommands/HSMSubCommand.java | 100 ++++++++++++++++++ .../subcommands/CaviumSubCommandTest.java | 72 +++++++++++++ .../subcommands/HSMSubCommandTest.java | 82 ++++++++++++++ gradle/versions.gradle | 4 +- 9 files changed, 359 insertions(+), 2 deletions(-) create mode 100644 ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommand.java create mode 100644 ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HSMSubCommand.java create mode 100644 ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommandTest.java create mode 100644 ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/HSMSubCommandTest.java diff --git a/acceptance-tests/build.gradle b/acceptance-tests/build.gradle index 9ba365229..507c1eec7 100644 --- a/acceptance-tests/build.gradle +++ b/acceptance-tests/build.gradle @@ -21,6 +21,8 @@ dependencies { testImplementation project(':ethsigner:core') testImplementation (group: 'tech.pegasys.signers.internal', name: 'signing-secp256k1-impl', classifier: 'test-fixtures') testImplementation (group: 'tech.pegasys.signers.internal', name: 'keystorage-hashicorp', classifier: 'test-fixtures') + testImplementation (group: 'tech.pegasys.signers.internal', name: 'keystorage-hsm', classifier: 'test-fixtures') + testImplementation (group: 'tech.pegasys.signers.internal', name: 'keystorage-cavium', classifier: 'test-fixtures') testImplementation 'com.github.docker-java:docker-java' testImplementation 'org.junit.jupiter:junit-jupiter-api' diff --git a/build.gradle b/build.gradle index c99a8eca0..06f269121 100644 --- a/build.gradle +++ b/build.gradle @@ -119,6 +119,7 @@ allprojects { repositories { jcenter() + mavenLocal() mavenCentral() maven { url "https://consensys.bintray.com/pegasys-repo" } } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignBodyProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignBodyProvider.java index aee7c9c44..cf687cc5d 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignBodyProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignBodyProvider.java @@ -34,6 +34,7 @@ import org.apache.logging.log4j.Logger; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; +import org.web3j.crypto.Hash; import org.web3j.utils.Numeric; public class EthSignBodyProvider implements BodyProvider { @@ -67,7 +68,9 @@ public JsonRpcBody getBody(final JsonRpcRequest request) { final TransactionSigner signer = transactionSigner.get(); final String originalMessage = params.get(1); final Signature signature = signer.sign(originalMessage.getBytes(StandardCharsets.UTF_8)); - + final byte[] hash = Hash.sha3(originalMessage.getBytes(StandardCharsets.UTF_8)); + System.out.println(Numeric.toHexString(hash)); + System.out.println(originalMessage); final Bytes outputSignature = Bytes.concatenate( Bytes32.leftPad(Bytes.wrap(ByteUtils.bigIntegerToBytes(signature.getR()))), diff --git a/ethsigner/subcommands/build.gradle b/ethsigner/subcommands/build.gradle index 84fa1eae6..d451e554a 100644 --- a/ethsigner/subcommands/build.gradle +++ b/ethsigner/subcommands/build.gradle @@ -19,6 +19,8 @@ dependencies { implementation 'tech.pegasys.signers.internal:signing-secp256k1-api' implementation 'tech.pegasys.signers.internal:signing-secp256k1-impl' implementation 'tech.pegasys.signers.internal:keystorage-hashicorp' + implementation 'tech.pegasys.signers.internal:keystorage-hsm' + implementation 'tech.pegasys.signers.internal:keystorage-cavium' implementation 'info.picocli:picocli' implementation 'com.google.guava:guava' diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommand.java new file mode 100644 index 000000000..24d647933 --- /dev/null +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommand.java @@ -0,0 +1,93 @@ +/* + * Copyright 2018 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.subcommands; + +import tech.pegasys.ethsigner.SignerSubCommand; +import tech.pegasys.signers.cavium.CaviumKeyStoreProvider; +import tech.pegasys.signers.secp256k1.api.SingleTransactionSignerProvider; +import tech.pegasys.signers.secp256k1.api.TransactionSigner; +import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; +import tech.pegasys.signers.secp256k1.cavium.CaviumKeyStoreSignerFactory; +import tech.pegasys.signers.secp256k1.common.TransactionSignerInitializationException; + +import java.nio.file.Path; + +import com.google.common.base.MoreObjects; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Spec; + +/** HSM-based authentication related sub-command */ +@Command( + name = CaviumSubCommand.COMMAND_NAME, + description = "Sign transactions with a key stored in an HSM.", + mixinStandardHelpOptions = true) +public class CaviumSubCommand extends SignerSubCommand { + + // private static final String READ_PIN_FILE_ERROR = "Error when reading the pin from file."; + public static final String COMMAND_NAME = "cavium-signer"; + + public CaviumSubCommand() {} + + @SuppressWarnings("unused") // Picocli injects reference to command spec + @Spec + private CommandLine.Model.CommandSpec spec; + + @Option( + names = {"-l", "--library"}, + description = "The HSM PKCS11 library used to sign transactions.", + paramLabel = "", + required = true) + private Path libraryPath; + + @Option( + names = {"-p", "--slot-pin"}, + description = "The crypto user pin of the HSM slot used to sign transactions.", + paramLabel = "", + required = true) + private String slotPin; + + @Option( + names = {"-a", "--eth-address"}, + description = "Ethereum address of account to sign with.", + paramLabel = "", + required = true) + private String ethAddress; + + private TransactionSigner createSigner() throws TransactionSignerInitializationException { + final CaviumKeyStoreProvider provider = + new CaviumKeyStoreProvider(libraryPath.toString(), slotPin); + final CaviumKeyStoreSignerFactory factory = new CaviumKeyStoreSignerFactory(provider); + return factory.createSigner(ethAddress); + } + + @Override + public TransactionSignerProvider createSignerFactory() + throws TransactionSignerInitializationException { + return new SingleTransactionSignerProvider(createSigner()); + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("library", libraryPath) + .add("address", ethAddress) + .toString(); + } +} diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HSMSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HSMSubCommand.java new file mode 100644 index 000000000..057e9e3f1 --- /dev/null +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HSMSubCommand.java @@ -0,0 +1,100 @@ +/* + * Copyright 2018 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.subcommands; + +import tech.pegasys.ethsigner.SignerSubCommand; +import tech.pegasys.signers.secp256k1.api.SingleTransactionSignerProvider; +import tech.pegasys.signers.secp256k1.api.TransactionSigner; +import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; +import tech.pegasys.signers.secp256k1.common.TransactionSignerInitializationException; +import tech.pegasys.signers.secp256k1.hsm.HSMTransactionSignerFactory; + +import java.nio.file.Path; + +import com.google.common.base.MoreObjects; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Spec; + +/** HSM-based authentication related sub-command */ +@Command( + name = HSMSubCommand.COMMAND_NAME, + description = "Sign transactions with a key stored in an HSM.", + mixinStandardHelpOptions = true) +public class HSMSubCommand extends SignerSubCommand { + + // private static final String READ_PIN_FILE_ERROR = "Error when reading the pin from file."; + public static final String COMMAND_NAME = "hsm-signer"; + + public HSMSubCommand() {} + + @SuppressWarnings("unused") // Picocli injects reference to command spec + @Spec + private CommandLine.Model.CommandSpec spec; + + @Option( + names = {"-l", "--library"}, + description = "The HSM PKCS11 library used to sign transactions.", + paramLabel = "", + required = true) + private Path libraryPath; + + @Option( + names = {"-s", "--slot-label"}, + description = "The HSM slot used to sign transactions.", + paramLabel = "", + required = true) + private String slotLabel; + + @Option( + names = {"-p", "--slot-pin"}, + description = "The crypto user pin of the HSM slot used to sign transactions.", + paramLabel = "", + required = true) + private String slotPin; + + @Option( + names = {"-a", "--eth-address"}, + description = "Ethereum address of account to sign with.", + paramLabel = "", + required = true) + private String ethAddress; + + private TransactionSigner createSigner() throws TransactionSignerInitializationException { + HSMTransactionSignerFactory factory = + new HSMTransactionSignerFactory( + libraryPath != null ? libraryPath.toString() : null, slotLabel, slotPin); + return factory.createSigner(ethAddress); + } + + @Override + public TransactionSignerProvider createSignerFactory() + throws TransactionSignerInitializationException { + return new SingleTransactionSignerProvider(createSigner()); + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("library", libraryPath) + .add("slot", slotLabel) + .add("address", ethAddress) + .toString(); + } +} diff --git a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommandTest.java b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommandTest.java new file mode 100644 index 000000000..9604b9f74 --- /dev/null +++ b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommandTest.java @@ -0,0 +1,72 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.subcommands; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.logging.log4j.Level; +import org.junit.jupiter.api.Test; +import picocli.CommandLine; + +public class CaviumSubCommandTest { + + private static final String LIBRARY = "/this/is/the/path/to/the/library/library.so"; + private static final String PIN = "pin"; + private static final String ADDRESS = "0x"; + + private CaviumSubCommand config; + + private boolean parseCommand(final String cmdLine) { + config = new CaviumSubCommand(); + final CommandLine commandLine = new CommandLine(config); + commandLine.setCaseInsensitiveEnumValuesAllowed(true); + commandLine.registerConverter(Level.class, Level::valueOf); + + try { + commandLine.parse(cmdLine.split(" ")); + } catch (final CommandLine.ParameterException e) { + return false; + } + return true; + } + + private String validCommandLine() { + return "--library=" + LIBRARY + " --slot-pin=" + PIN + " --eth-address=" + ADDRESS; + } + + private String removeFieldFrom(final String input, final String fieldname) { + return input.replaceAll("--" + fieldname + "=.*?(\\s|$)", ""); + } + + @Test + public void fullyPopulatedCommandLineParsesIntoVariables() { + final boolean result = parseCommand(validCommandLine()); + + assertThat(result).isTrue(); + assertThat(config.toString()).contains(LIBRARY); + assertThat(config.toString()).contains(ADDRESS); + } + + @Test + public void missingRequiredParamShowsAppropriateError() { + missingParameterShowsError("library"); + missingParameterShowsError("slot-pin"); + missingParameterShowsError("eth-address"); + } + + private void missingParameterShowsError(final String paramToRemove) { + final String cmdLine = removeFieldFrom(validCommandLine(), paramToRemove); + final boolean result = parseCommand(cmdLine); + assertThat(result).isFalse(); + } +} diff --git a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/HSMSubCommandTest.java b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/HSMSubCommandTest.java new file mode 100644 index 000000000..090e7eb32 --- /dev/null +++ b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/HSMSubCommandTest.java @@ -0,0 +1,82 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.subcommands; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.logging.log4j.Level; +import org.junit.jupiter.api.Test; +import picocli.CommandLine; + +public class HSMSubCommandTest { + + private static final String library = "/this/is/the/path/to/the/library/library.so"; + private static final String slot = "slot"; + private static final String pin = "pin"; + private static final String address = "0x"; + + private HSMSubCommand config; + + private boolean parseCommand(final String cmdLine) { + config = new HSMSubCommand(); + final CommandLine commandLine = new CommandLine(config); + commandLine.setCaseInsensitiveEnumValuesAllowed(true); + commandLine.registerConverter(Level.class, Level::valueOf); + + try { + commandLine.parse(cmdLine.split(" ")); + } catch (final CommandLine.ParameterException e) { + return false; + } + return true; + } + + private String validCommandLine() { + return "--library=" + + library + + " --slot-label=" + + slot + + " --slot-pin=" + + pin + + " --eth-address=" + + address; + } + + private String removeFieldFrom(final String input, final String fieldname) { + return input.replaceAll("--" + fieldname + "=.*?(\\s|$)", ""); + } + + @Test + public void fullyPopulatedCommandLineParsesIntoVariables() { + final boolean result = parseCommand(validCommandLine()); + + assertThat(result).isTrue(); + assertThat(config.toString()).contains(library); + assertThat(config.toString()).contains(slot); + assertThat(config.toString()).contains(address); + } + + @Test + public void missingRequiredParamShowsAppropriateError() { + missingParameterShowsError("library"); + missingParameterShowsError("slot-label"); + missingParameterShowsError("slot-pin"); + missingParameterShowsError("eth-address"); + } + + private void missingParameterShowsError(final String paramToRemove) { + final String cmdLine = removeFieldFrom(validCommandLine(), paramToRemove); + final boolean result = parseCommand(cmdLine); + assertThat(result).isFalse(); + } +} diff --git a/gradle/versions.gradle b/gradle/versions.gradle index a8cca8aad..b739dab3f 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -69,8 +69,10 @@ dependencyManagement { dependency 'org.web3j:core:4.5.14' dependency 'org.web3j:crypto:4.5.14' - dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.2-SNAPSHOT') { + dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.3-SNAPSHOT') { entry 'keystorage-hashicorp' + entry 'keystorage-hsm' + entry 'keystorage-cavium' entry 'signing-secp256k1-api' entry 'signing-secp256k1-impl' } From 5121271f83fc68a1e736549e24ae7e4002bd4aa1 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Mon, 22 Jun 2020 15:40:16 +0200 Subject: [PATCH 03/64] Initial commit --- .../tech/pegasys/ethsigner/EthSignerApp.java | 4 ++++ .../internalresponse/EthSignBodyProvider.java | 9 ++++--- .../subcommands/MultiKeySubCommand.java | 24 ++++++++++++++++++- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/ethsigner/app/src/main/java/tech/pegasys/ethsigner/EthSignerApp.java b/ethsigner/app/src/main/java/tech/pegasys/ethsigner/EthSignerApp.java index 0fa525a42..d2b85406f 100644 --- a/ethsigner/app/src/main/java/tech/pegasys/ethsigner/EthSignerApp.java +++ b/ethsigner/app/src/main/java/tech/pegasys/ethsigner/EthSignerApp.java @@ -15,7 +15,9 @@ import static java.nio.charset.StandardCharsets.UTF_8; import tech.pegasys.ethsigner.subcommands.AzureSubCommand; +import tech.pegasys.ethsigner.subcommands.CaviumSubCommand; import tech.pegasys.ethsigner.subcommands.FileBasedSubCommand; +import tech.pegasys.ethsigner.subcommands.HSMSubCommand; import tech.pegasys.ethsigner.subcommands.HashicorpSubCommand; import tech.pegasys.ethsigner.subcommands.MultiKeySubCommand; import tech.pegasys.ethsigner.subcommands.RawSubCommand; @@ -34,6 +36,8 @@ public static void main(final String... args) { cmdLineParser.registerSigner(new HashicorpSubCommand()); cmdLineParser.registerSigner(new FileBasedSubCommand()); cmdLineParser.registerSigner(new AzureSubCommand()); + cmdLineParser.registerSigner(new HSMSubCommand()); + cmdLineParser.registerSigner(new CaviumSubCommand()); cmdLineParser.registerSigner(new MultiKeySubCommand()); cmdLineParser.registerSigner(new RawSubCommand()); diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignBodyProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignBodyProvider.java index cf687cc5d..14c979a73 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignBodyProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignBodyProvider.java @@ -34,7 +34,6 @@ import org.apache.logging.log4j.Logger; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; -import org.web3j.crypto.Hash; import org.web3j.utils.Numeric; public class EthSignBodyProvider implements BodyProvider { @@ -67,10 +66,10 @@ public JsonRpcBody getBody(final JsonRpcRequest request) { } final TransactionSigner signer = transactionSigner.get(); final String originalMessage = params.get(1); - final Signature signature = signer.sign(originalMessage.getBytes(StandardCharsets.UTF_8)); - final byte[] hash = Hash.sha3(originalMessage.getBytes(StandardCharsets.UTF_8)); - System.out.println(Numeric.toHexString(hash)); - System.out.println(originalMessage); + final String message = + (char) 25 + "Ethereum Signed Message:\n" + originalMessage.length() + originalMessage; + final Signature signature = signer.sign(message.getBytes(StandardCharsets.UTF_8)); + final Bytes outputSignature = Bytes.concatenate( Bytes32.leftPad(Bytes.wrap(ByteUtils.bigIntegerToBytes(signature.getR()))), diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java index ff2422de2..02d78c163 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java @@ -57,10 +57,32 @@ public MultiKeySubCommand() {} arity = "1") private Path directoryPath; + @Option( + names = {"-l", "--library"}, + description = "The HSM PKCS11 library used to sign transactions.", + paramLabel = "", + required = false) + private Path libraryPath; + + @Option( + names = {"-s", "--slot-label"}, + description = "The HSM slot used to sign transactions.", + paramLabel = "", + required = false) + private String slotLabel; + + @Option( + names = {"-p", "--slot-pin"}, + description = "The crypto user pin of the HSM slot used to sign transactions.", + paramLabel = "", + required = false) + private String slotPin; + @Override public TransactionSignerProvider createSignerFactory() throws TransactionSignerInitializationException { - return MultiKeyTransactionSignerProvider.create(directoryPath); + return MultiKeyTransactionSignerProvider.create( + directoryPath, libraryPath != null ? libraryPath.toString() : null, slotLabel, slotPin); } @Override From cda7d0931e510722538315e91046d1918edff5fc Mon Sep 17 00:00:00 2001 From: mkrielza Date: Mon, 22 Jun 2020 19:49:30 +0200 Subject: [PATCH 04/64] Some fixes --- build.gradle | 2 ++ .../tech/pegasys/ethsigner/core/EthSigner.java | 9 +++++++++ .../jsonrpcproxy/EthSignBodyProviderTest.java | 15 ++++++++------- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 06f269121..f9f4a9604 100644 --- a/build.gradle +++ b/build.gradle @@ -211,6 +211,7 @@ allprojects { jvmArgs = [ '-Xmx4g', '-XX:-UseGCOverheadLimit', + "-Djava.library.path=/opt/cloudhsm/lib", // Mockito and jackson-databind do some strange reflection during tests. // This suppresses an illegal access warning. '--add-opens', @@ -386,6 +387,7 @@ applicationDefaultJvmArgs = [ "-Dlog4j.shutdownHookEnabled=false", // netty specific module warnings "-Dio.netty.tryReflectionSetAccessible=true", + "-Djava.library.path=/opt/cloudhsm/lib", "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED", "--add-opens", diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java index 53ee4cd37..a405d43c7 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java @@ -91,6 +91,15 @@ public void run() { vertx.close(); throw new InitializationException("Failed to create http service.", t); } + Runtime.getRuntime().addShutdownHook(new Shutdown()); + } + + class Shutdown extends Thread { + @Override + public void run() { + transactionSignerProvider.shutdown(); + System.out.println("Shutting down EthSigner"); + } } private HttpServerOptions applyConfigTlsSettingsTo(final HttpServerOptions input) { diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignBodyProviderTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignBodyProviderTest.java index e803f6449..ad45243fb 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignBodyProviderTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignBodyProviderTest.java @@ -29,7 +29,6 @@ import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; import java.math.BigInteger; -import java.nio.charset.Charset; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -45,7 +44,6 @@ import org.junit.jupiter.params.provider.NullSource; import org.web3j.crypto.ECDSASignature; import org.web3j.crypto.ECKeyPair; -import org.web3j.crypto.Hash; import org.web3j.crypto.Sign; import org.web3j.utils.Numeric; @@ -139,11 +137,12 @@ public void returnsExpectedSignature() { final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_sign"); final int id = 1; - final String message = - "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Tubulum" - + " fuisse, qua illum, cuius is condemnatus est rogatione, P. Eaedem res maneant alio modo."; request.setId(new JsonRpcRequestId(id)); - request.setParams(List.of("address", message)); + request.setParams( + List.of( + "address", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Tubulum" + + " fuisse, qua illum, cuius is condemnatus est rogatione, P. Eaedem res maneant alio modo.")); final JsonRpcBody body = bodyProvider.getBody(request); assertThat(body.hasError()).isFalse(); @@ -152,7 +151,9 @@ public void returnsExpectedSignature() { final byte[] signature = Numeric.hexStringToByteArray(hexSignature); final ECDSASignature expectedSignature = - keyPair.sign(Hash.sha3(message.getBytes(Charset.defaultCharset()))); + keyPair.sign( + Numeric.hexStringToByteArray( + "0xe63325d74baa84af003dfb6a974f41672be881b56aa2c12c093f8259321bd460")); assertThat(new BigInteger(1, signature, 0, 32)).isEqualTo(expectedSignature.r); assertThat(new BigInteger(1, signature, 32, 32)).isEqualTo(expectedSignature.s); } From 9ce589601144d92b18aa046ec6fa128001b397bb Mon Sep 17 00:00:00 2001 From: mkrielza Date: Tue, 23 Jun 2020 14:36:10 +0200 Subject: [PATCH 05/64] Licenses --- gradle/check-licenses.gradle | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gradle/check-licenses.gradle b/gradle/check-licenses.gradle index 85b91cd89..c4bf54360 100644 --- a/gradle/check-licenses.gradle +++ b/gradle/check-licenses.gradle @@ -46,7 +46,8 @@ ext.acceptedLicenses = [ 'CC0 1.0 Universal License', 'Common Development and Distribution License 1.0', 'Unicode/ICU License', - 'CC0' + 'CC0', + 'IAIK of Graz University of Technology License' ]*.toLowerCase() /** @@ -69,6 +70,7 @@ downloadLicenses { ext.cddl = license('Common Development and Distribution License 1.0', 'http://opensource.org/licenses/CDDL-1.0') ext.cddl1_1 = license('Common Development and Distribution License 1.0', 'http://oss.oracle.com/licenses/CDDL-1.1') ext.epl_2 = license('Eclipse Public License 2.0', 'https://www.eclipse.org/legal/epl-2.0/') + aliases = [ (apache) : [ 'The Apache Software License, Version 2.0', @@ -152,7 +154,8 @@ downloadLicenses { (group('org.glassfish.jersey.connectors')): apache, (group('com.github.jnr')): epl_2, (group('javax.mail')): cddl, - (group('net.jcip')): apache + (group('net.jcip')): apache, + (group('com.cavium')): apache, ] } From 932a59106f6af1d5a5da1c6b1f2b80335fc78eb3 Mon Sep 17 00:00:00 2001 From: Fernando Llaca Date: Tue, 30 Jun 2020 17:05:42 +0200 Subject: [PATCH 06/64] Adapt pipeline to Adhara repos --- .circleci/config.yml | 80 +++++++++++++++++++++--------------------- build.gradle | 17 ++++++--- gradle.properties | 2 +- gradle/versions.gradle | 2 +- 4 files changed, 55 insertions(+), 46 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b388a0973..c09095467 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -96,7 +96,7 @@ commands: jobs: build: - executor: executor_large + executor: executor_med steps: - prepare - run: @@ -127,27 +127,27 @@ jobs: paths: - ./ - acceptanceTests: - executor: executor_machine - steps: - - prepare - - run: - name: Install Packages - Java 11 - command: | - sudo rm /etc/apt/sources.list.d/google-chrome.list* - sudo rm /etc/apt/sources.list.d/heroku.list* - sudo add-apt-repository -y ppa:openjdk-r/ppa - sudo apt update - sudo apt install -y openjdk-11-jdk - sudo update-java-alternatives -s java-1.11.0-openjdk-amd64 - - run: - name: Acceptance Test - no_output_timeout: 20m - command: | - ./gradlew --no-daemon --parallel acceptanceTest - - notify - - capture_test_results - - capture_test_reports +# acceptanceTests: +# executor: executor_machine +# steps: +# - prepare +# - run: +# name: Install Packages - Java 11 +# command: | +# sudo rm /etc/apt/sources.list.d/google-chrome.list* +# sudo rm /etc/apt/sources.list.d/heroku.list* +# sudo add-apt-repository -y ppa:openjdk-r/ppa +# sudo apt update +# sudo apt install -y openjdk-11-jdk +# sudo update-java-alternatives -s java-1.11.0-openjdk-amd64 +# - run: +# name: Acceptance Test +# no_output_timeout: 20m +# command: | +# ./gradlew --no-daemon --parallel acceptanceTest +# - notify +# - capture_test_results +# - capture_test_reports buildDocker: executor: executor_med @@ -199,25 +199,25 @@ jobs: workflows: version: 2 - nightly: - triggers: - - schedule: - cron: "0 11 * * *" - filters: - branches: - only: - - master - jobs: - - build - - acceptanceTests: - requires: - - build + #nightly: + #triggers: + #- schedule: + #cron: "0 11 * * *" + #filters: + #branches: + #only: + #- master + #jobs: + #- build + #- acceptanceTests: + #requires: + #- build default: jobs: - build - - acceptanceTests: - requires: - - build + #- acceptanceTests: + # requires: + # - build - buildDocker: requires: - build @@ -229,7 +229,7 @@ workflows: - /^release-.*/ requires: - build - - acceptanceTests + #- acceptanceTests - publishDocker: filters: branches: @@ -238,5 +238,5 @@ workflows: - /^release-.*/ requires: - build - - acceptanceTests + #- acceptanceTests - buildDocker diff --git a/build.gradle b/build.gradle index f9f4a9604..10fa806ac 100644 --- a/build.gradle +++ b/build.gradle @@ -36,6 +36,8 @@ if (!JavaVersion.current().java11Compatible) { " Detected version ${JavaVersion.current()}") } +def dockerRepo = "adharaprojects" + def bintrayUser = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') def bintrayKey = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_KEY') def bintrayPackage = bintray.pkg { @@ -121,7 +123,14 @@ allprojects { jcenter() mavenLocal() mavenCentral() - maven { url "https://consensys.bintray.com/pegasys-repo" } + //maven { url "https://consensys.bintray.com/pegasys-repo" } + maven { + url "https://adhara.jfrog.io/artifactory/maven" + credentials { + username = System.getenv('ARTIFACTORY_USER') + password = System.getenv('ARTIFACTORY_PASSWORD') + } + } } dependencies { errorprone("com.google.errorprone:error_prone_core") } @@ -478,7 +487,7 @@ tasks.register("dockerDistUntar") { task distDocker(type: Exec) { dependsOn dockerDistUntar def dockerBuildVersion = project.hasProperty('release.releaseVersion') ? project.property('release.releaseVersion') : "${rootProject.version}" - def imageName = "pegasyseng/" + repositoryName + def imageName = dockerRepo + "/" + repositoryName def image = project.hasProperty('release.releaseVersion') ? "${imageName}:" + project.property('release.releaseVersion') : "${imageName}:${project.version}" def dockerBuildDir = "build/docker-" + repositoryName + "/" workingDir "${dockerBuildDir}" @@ -497,7 +506,7 @@ task distDocker(type: Exec) { task testDocker(type: Exec) { dependsOn distDocker def dockerReportsDir = "docker/reports/" - def imageName = "pegasyseng/" + repositoryName + def imageName = dockerRepo + "/" + repositoryName def image = project.hasProperty('release.releaseVersion') ? "${imageName}:" + project.property('release.releaseVersion') : "${imageName}:${project.version}" workingDir "docker" @@ -511,7 +520,7 @@ task testDocker(type: Exec) { task dockerUpload(type: Exec) { dependsOn distDocker - def imageName = "pegasyseng/" + repositoryName + def imageName = dockerRepo + "/" + repositoryName def image = project.hasProperty('release.releaseVersion') ? "${imageName}:" + project.property('release.releaseVersion') : "${imageName}:${project.version}" def cmd = "docker push '${image}'" def additionalTags = [] diff --git a/gradle.properties b/gradle.properties index b80d4a916..46c4450a4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ org.gradle.jvmargs=-Xmx1g -version=0.7.1-SNAPSHOT +version=0.7.1-ADHARA-SNAPSHOT diff --git a/gradle/versions.gradle b/gradle/versions.gradle index b739dab3f..39dd0e568 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -69,7 +69,7 @@ dependencyManagement { dependency 'org.web3j:core:4.5.14' dependency 'org.web3j:crypto:4.5.14' - dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.3-SNAPSHOT') { + dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.2-ADHARA-SNAPSHOT') { entry 'keystorage-hashicorp' entry 'keystorage-hsm' entry 'keystorage-cavium' From 3394fffc7000718afb9743b3953e3f8a95aa8b31 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Wed, 1 Jul 2020 18:32:23 +0200 Subject: [PATCH 07/64] Manually add cloudhsm jar --- build.gradle | 1 + gradle.properties | 2 +- gradle/check-licenses.gradle | 1 - libs/cloudhsm-3.0.1.jar | Bin 0 -> 210472 bytes 4 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 libs/cloudhsm-3.0.1.jar diff --git a/build.gradle b/build.gradle index 10fa806ac..779f1148f 100644 --- a/build.gradle +++ b/build.gradle @@ -439,6 +439,7 @@ dependencies { distributions { main { contents { + from("./libs") { into "lib" } from("./LICENSE") { into "." } from("build/reports/license/license-dependency.html") { into "." } from("./docs/GettingStartedBinaries.md") { into "." } diff --git a/gradle.properties b/gradle.properties index 46c4450a4..ce2b56a0c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ org.gradle.jvmargs=-Xmx1g -version=0.7.1-ADHARA-SNAPSHOT +version=0.7.0-ADHARA-SNAPSHOT diff --git a/gradle/check-licenses.gradle b/gradle/check-licenses.gradle index c4bf54360..2045ccb80 100644 --- a/gradle/check-licenses.gradle +++ b/gradle/check-licenses.gradle @@ -155,7 +155,6 @@ downloadLicenses { (group('com.github.jnr')): epl_2, (group('javax.mail')): cddl, (group('net.jcip')): apache, - (group('com.cavium')): apache, ] } diff --git a/libs/cloudhsm-3.0.1.jar b/libs/cloudhsm-3.0.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..5bf682bb12cfbb9598bf996cab1ee18e7eae542b GIT binary patch literal 210472 zcma&tV~l7)m?q%1Z5y|3+qT_(+qP}nwr$(CZQI^EJDZ)!X0zE!>YPeW>TkWL>Zwm& z3K#?h00II60GTvi9^k(Ke`?$r&raoYzHyi&ev zH;l)Y_gl6zzP;Yx;bk4vRePrQ#l!tv6JC{j-`;o?F0m>???Z`;OwJ}Lyx~E0w@)c8Nz~Z-0d#BH_=$sJjCPFg`KKh1;I-Ut{%&qmqa+XvV>5A8f;*<6 z;ZqzRE_jHypbN`6n-rqTSG@;FO2Tf%DW}B1i5X8{NyfA2_%QYFVVF1NO5zD1Gii~LZ6()?!GDz z0_|t2C}1@YQfc)nVIb922RNU4LL|~~tTAW9@gT%geOrtM<1>o4~ESCZAoxJh&YAaqC9^^8P(VAPi4tXgJ z>L;5%1`&`DZ#;GZ2`Ho(Py`|)Sp-ay^$@h;|Mf^larfz+Lcc;?naZA3pv{JMPvNwm ziiWfsNAz;Of>DiWj07`BXPJA`>Ult=ouX*=pBrfht|J8*5=6DNIut;&#k&wnsYi!J zCuNL}jHc;Lmp_G%9fik#kNKu2BD0ZI*?@abTE2*_-95VXCj9+bZIxRg`oM(2Z}Y^C zVK#*9)7+j>J8rV9XOA5i^$_tksTmW^QhT(=J0Bp56U0lplm zM`|o7a1k`tiJ<2Yi?!k8?Z%J)Yh5)1UYQn#t8Gm;CXy9Zl9|+dpO_7 zrj|HTpJ5a5;al&1Nx%LxIDwKi>Q>bVKo;?ucmL5CL%8iha_z+(pdN>xHmaDo}mu*me+yxC%Mi|17Z8_7hUWQSX@ziAof(HXL^ zTg>}BU1*2J5Ce(GvKBkZf8$cuPT99#NsdByy?({t4o|+Xem zpu;c8mZaJ2S<4ebUF#7IR_VV$I1Nl;L+7H%%=Il>!X(G!yHLhoNiG)wRU9w5$z{EA zeg4k%mu|)Che!jR{^eFZGSeMw>Sj)wiq!W0+kuA=9*8_lI!$|y-1iiX`vpGbI)`lR zWj8dsINdaAoq|2k;CZ=l-cGc5+NpQ_pdWT~oi)vhF(PJldP&mR4}%^0-RE{*H;bcK zt;CK<&vR|x+-8K=DcY*&N-*AD?-SmxrnB=dQJ2r1%W%xambYPWcu+!q(IM1-$>3~#-XPUu>&+dq8 zuk)aa@#B>RcN9w__fh!8Z^viv*JUyAnOybxc9s)$FxU-{2gTXAPJ$0*3F49xM_48A zz7AAi&Po0V^N`PXR~>*HRk`b*_EU0KFv6Fs)F2xWtF=G(YgGOLx$GZT&_qu`HNM7) zkCs*rN&an}+M(sV64a%s?=U%?5OUHh+?^*?+G;vJAkK%@>wS1%WFB50vc&dpbAsRB z@sUDeIwmcC#^l^M%bhF7dv1j0*hy`=Xi`FDe{dhbxyQZw?np+6oX13zTZ3Zyz7i!6=t1ObTG_UF=F+iz#Up$A^-4LAAb8#R zAeubH=ahj?lLEwTVsAVlpNIut>~}eQCY;__vyC)^?9EKy$XD6OK&gF-*DJ4XnCAZl z?4_;=eMAgpyE;F`&HRAo+DT+EZiQTLt#F?gxBHVq!NHFX;5r;c@V6)CKy1sy<%InF zln?b>rKHTpU-tel<#klz5r^RX*!;n*U%n}S8hdeRCiRer-Q{$2+A~jrm#T(8fo_j# zlwaDYiGvYqyFFLcBFY2fwj_&6VPX5E4vJa>`lY?zC%8ixW?{B=V}r4aOn?_6lL>qt zjw}^Y9$2VzM;xQBtMBgOXsn~H!Yi@G^?|{};jBZ49W|kMg>k#$x&bp@dh$#aZht3a zP9!Eup0N0iKnW6S*IzD;U*L_6_;%=lxnQO{e7I!h;3^ zU%>{tU>e`9WjwjTJ-(;!_&x8QEV#Xm?{oEp%YtY1?2)|GH;4v{kS-=Q3ECF|*qsnG zy@}PQX8}06jz5FbV&mpl?*RUL3Y-Be^tDHfa*lB%84$&Ju~}L1%;z+o-&{|DF1vHN zL=l6uDDKNuq}%4G5jkJD@%5G0kT?Uv9G)n66ycOO0~SJ3mV94Yc^zUTzKhtH>-XcE z8Y2i)JyB-)5uUPSgDs>a@x3?Z$fIE4^UM~l3WaY$Ke~KcQw92#9aEfY!+Ckx_V%}R zp(sP523L=RNl!-hg8N}OEoZmU&|zKffk;|>9ze^J1dO5a%%%BmR}sxg2TAsH;;Hc- zB{{^)kYkZ%3Dc@UFJPoC<~!m(j}V`sd}I6d>n0Q56K1D`TSlR66#?%(M3SgmI!c)~ z*V#&O+xL6ndw|U*4>Jtz@}GC-BG*5Wu|IJeW|>Jf8u(i1z6X?|-Q_I@#eM#fCh3RG z>Na+=uinnW7~4V(yAt}d zg^5gEz0Pu-=hx54u#WKf5T~|xIl4v#T*p%-%;grB8)1Fa&RWtBHbHpSYiN$k=iA*Y zXlXZ@v)J%s%(?@EKLExc3wHpSAQ?D`^I@MqsJp!9W&%A_`0#(A>|5J+bDiC~uBLK= zw=yGcP$=Vs5AE71Q)q(`bQj7n1Yun$b3h<^YxZgzR(1!ku2~2^E4`6{NT#Pq<`_YG zX)8P?m^cGuRbc&OIf&R2_*4!;?tNU&>2zFYcdK}!1$mF?7q1T6sMo-lBNLUt&`V2) z3W&ral(lbBMZe-QR+TuAHg|8I2Xz>{Qrlf2`<+%h@cKa3bIZ3F|1$8hq&I60iBi5I zzYeZ3-Pm%#-@LJN5j}cYFuyqDn%+Gh0(HCuE5ad|#u@;x#nRaTH!)MUX`u0*4-jTqC#_C!2l8?vf&Ynuio!kCt< zf?ie}Wk$B+5@N@Hd-0f_f##BSBo-!Bd|`&Sy_^5s_I8qIWmtIeijYI^F|>5qLT^Z+ zK+^+pXqw&d^s<^8+pcVz4b);OdbOdZx$eE0e@pVId4GZ;ZUU1Y)O4q%3Ls`ymuAqT zLu`Jj5D_8mFnH1m5sW{rr7=#S7y~z^AwiB=t}#sG6id{SqbC7j&wFeZiYvyGF*DI!LPzi63Tuv_ zkDF-HPCu@8QCoW6f7#D%?P|NyeNMI#;T#@QcPc=Vw=#zFXV^H8=L3xn#CrEESYUIu zLs(C@5P^;ve}PFriftlVD9fmROP&0d5FNcNw!8>^Q30p#P;}!?7UDr$Med-k#91DS z>ot28pfaRgEVkq{r^nhxPIo`8aXJEIqE{(yw) z*QnSI*Wo>6H6xYO;-)PORhDmM4z|Ed4E<@_70VHrByt!fS3?D6afDeO0D^)D3~4Ah z?PoODZD?svN096RG{{n&ke(CFsPTAQ=iIg(;YnW+XnTO9#!1e`$ZNFL&zN2hxc4lS zs4-@qDor&yRSeIZ(7d9t_N*GUeruS%>}8rJpeqT~L$4{u?zt_-hJlx6ABEsLm8!xB z;HI*g@X{V>3;l-Fyk}pM+cgbMea2RvPD=!Ouk44FH3>~%S6m+t^X6(*J;8K1$3|NM zysun)x8EP}<>$3Oxh*4TY?F&xVX1)-uZZsUaDD)uAnB>)*_(lW_5%f#NS-?V4IR1F z&^teeXFg9Rx%ha%yV)uXiV_t!GWCN1A4&GzLL2;OGL!89wLO;wz!Tr9roWSbb4!}# z%(Un0TEwg&g$fc1R&_ zTzvTMrx(;CKCX*iny3MePl|vLJlv2`EN16M!Sh{)2{>zwzUSh}c`k6xN~exu7!4YDJtCw5@dD) zNW}RnFdh^9(Nh(Ii=idyO_=8Q%PEKotdU3bbiHg0?}B;Bbvc3r47DUkb-m`)yMMXe zO-K+3<*AyS_ORBI;3Usi?ylv~o>H1kR015DC<%bhi-Pq-WhC6k_JlDIF*#`(c^uS?vft%-?ULsi(*WBygF82Xk zo(>-@LL>ij>;UlbV+Q(4z=&994f=3F}{G6dyvT=Q3Det~!22wAE)Gq}#3HiC-+GFX7 z5By#<+0De(ORG|fAFV-WWNro#uR{!3Tq8L%lCzU84>YB$A1n}@u>8=9x_e%$)^t|$ zDMnpd?rUQiF$@r%VjN;d0ETBhkduF3d)8od>1FVL9PipeKToyIec?wmdF)eWjG(9l1Z1hoSCsr0%rSn889*5JB zW5s&VNPr#^c#pq)>Rf2DMxy$k;vWg2htNeY9$N5*)fwJqDzB^&?j-uUk~)u&a!BTA zFrjwQDkj|A8Ll2H# zK{0qbZ#ZzZreZMJi5W#WsY-K7pW6=-GJK86_D4l!WX;2((ju?FoIT!h`qXzAf2 z+`?}8%b*9r{MNE}K|87YWEL1E)0FF_4WT$665Ck%-cs^y9hRZz<03@Tv%DZrB+d?Yr*X(D1Ndyni9vWX5kZGXOn%2Jp*FraWj=~=VSShNT&cxid{USehG&I7p?=u z7l`0`5VF)Gp<`nUBhjO|$q-3)5#)!JY?Vg!IO^gJU0rd>1OP!7)bz9n>tc-*cFHpa zI`MScopQOLMA}Tl=G@%<_fUNDZlh#`W#QE@w`shr7Dkd?ZHF|8)wN?Ze6g=2p2q4& zbkSS1x-6Pt53x*X&EgUiQ^yTSd;1ydr{Kd-z>)f8i|-j(R`0Dx7s)8%ZN@=ObJ8Rs zX#mT0E2v}952?r2TNfo|@Do)e^!PKa#!ey?ERrJ7~ zyxXtp=*6j@(V9#(6G0$Xw{k;^a-nbE)+4>ki$2^NUbo?N;}+JYD*#;CP*7TJ?B*^c zAbP`lRLWG7%Eyqd(j(;atFsCdVkj>nX+V>qd54N&OI!f$O_$3T<4Hl=?f6VlbHSFr zcwBLy`ldo`xzbs28aOE}deir-Q?+j0?Yqub7J)275Qq7c+*~R^_PI*IrEUD@#H)w{ zsZSEM`?r|GWC+K)4cKVXS!<*z)FTB6yR$3vu=c*;d#q5ZSns*R}xCbE+} zkM2K`R{d>U1q zqeFfiUei^iw|t-Tucqqg+FSBh6y)*)ODxf&l)wRqJG-}wNn~idwGKz8Hb$%MJ~d2_ z%foUNc*8s?{meX|!K;5lalB0V^ZKvvn3a$=omvsQedrPHNk9S|p!}}XNqvYHWN`=E z+~+YGxf9qTE8|t`mN@sEW_Lia+B`fGPOJi3(_nt5mC`NLK%?tZ^6QsMsXY@}CT7m# z#c|>BJZdLY(y{9KCSB^@7hLvBcMu|)Iu4;N*d{olng@A9G~1JABceC$IqBTz1oA!o z{sPKNfkQ-i>qS^#002Dm0RYJV|5=5AjDV(`tdKOVl4!E$_)Mi)!*y4YM<~wG9-f9q z+Dux)3;;|To-}+S@kb9hw%f<$w$aK?r_UFcDQgOj9&-KP;RycO#gW_-!k@v2PzKvf z-(Rqw%ePtIGwOTrpq!t)2j+i_LDD;(Xp8@F0{=H7ggdR&?d#PRfOAdFB!Gw~ep$Gs z-sNEum_w?$66p79)OPgD#$Lw;7^SASJ_V-67UY5bkGh_`JTsT3W-ZB_Vk@bS! zo@5%3-XeQgDjh@P?JIK<;Zq6cTcAcy)$3>V{+9scs- z?wj-H)z&}@INx{n6bs{bi_z}=gMSfHWi@v)fI#^7vDG&?nJ=cd=4H1xTs1I*Y0I|1 z`EQo4`qspnBrS~m_93(XPK_x1Bihc$@vo2BHa<4O+@ViQCOzwy)2#d}J1x=B1+z-l zERl=iWKIsTa__9^I(r3A0MV-Cs^1QoE(Xz~xz~BOvq=aH9o)z0r(z2)Qfn z7-I*-S$K_8MHMjy#C6aCZws@WI}4D}a>z01ZG#)ZDM@%JkEj1s9_@$*ghlPE-N5sM z^dtfuTQcIhE^)b^nFBhwMdst^%Y+*!5?|Ja!M9H^O7*A$^$^wL^?lBH^8p8PDeMtQ z`GN#A=Gj)6lslUjHjLZNn{ zQj4y0mPkxTa?A^nwd-=7sI6&fXAP9kMcGW1?R9e&mvMS}OJTlaiW2vyQy9*{GNu2_58k+eAhB7uZETG! z3?+msY=ax(hmg4vzY3VLXTp^Um52pIJGdcZ&H1L&^7BJOolJ}Obsm0ym}6eCO!GG} zUOks>NemGMw>yy}?L*<|{AKF#i@PdgpQq`myRwLGX?5~zOV47>TMmiNuS5Qi-^GC&!h)A!c-5?(ppL*L(K`JvFh1=hwt^wnL+IP-v)+ zaO8#_0 zdlx=_nx~171?+3Tc4V$+?0WZ#$J;Ur+ynCPM#}6q-KZ0(Nz)Hod-P!T;l3?;FhMNO zRA*c0bPLTxVm5ypDEo;+v8PX*+XyZa1|Y4jzc^k8%Z>~;yv(A2$IA5C-!p4BN*OVU ztLeDV?qfMTch?uL+?4&g3Opm-4+)^W);NdX&jW_45m^{kQf#*55$Z81EJUm#cd9qk z?6#=1ro^&J7!)jn=6$Fyg_!{^yO2ccT+E1ZJ*ce@DSB(t4t%^f;4PQ8du0}vZ+0KN zq}f!%Ri(8zel-*#Z-HlZgfKixQwo_ry`WL4k42=8f8XQ1iUt3KPt?AHA$Rjz!uH{qwv)7KDSMf{PwY@@l} zxf+D;$xc`-8uLJ-y549vUDziuW%LlYk{=Mqw&F3s_W1B6_M*#fS z{G?MMV5Qe!sI|0n4F191^9i~tMJmTG06eQmy|cmY1wlxQr2oSK0nSEl#|6jYv@Vx* zhtok*1B_$da^?4+8G}T7Z%uMOfw+MIjBa{u9`H{AAllE2cMlhjp zPIE4)DO=9HHRT1yEvEr`X3zd@ZK)M&EP3sZt?Fw(k~l0G4#;i$)Jd^NVW16TpiQ#p zi$C@`CjED#_>`jmCKu|d9L3)G##69zaAZt{4+_ua$ErDi58ibMltuAUc2R){5m*-X zsFYsg+|}}t-{T>_Nl#u6`-z_GubNl4Gr-**xDW--?F=mO0uu>ndP_ahEU3E_D0p)z zo2>1GgXfk0#%qB&*56(q8ftZR=pccj4N1F7Bo~D2&xJ}(#f0bt|ZMt3F^V*(uv=GyM^A4>a}kkvttbHS}K0= z>Y@ww-k>kSez`7IX0}&uX2|qZ937G}-I+37Bk&fwrVcZhSD?YRW(%c=d`asag|=Q< z@jD_BW{JdpK^B^R3t#k%KL~WZDT<{n9u(Kp7wPGK*>~R)BzhE7IF7qlPvaoT zA1U^B!l;V$)~92<2K*eSj)EWeX=FLDUFD&0rO#IS z&F=abR_3Dp2{^t%zqY1%@Uz~3;gG&jtJbB$TX_&FnhoE@sSfV3GOPWf{mUKmx7#Fq_AK!GCGr$*aS z&;`X}4(<}mO|V8XQ_cRNFnZ_`K{m$lnxqMS@AkWq=4s?K#hgRmF^#drRCUfNr+5k) z-nG^+_$J2GX1m>@Ni4HRA26LwPddCB|2os6Bwg@xbSaZQfmNZ=ML!*BgoA_wpktR) zYu3Do3re-VP~D5=e~CrW_uy^ne*1rjg{192u@C`2>#*6jyB!qcp$;9GKaf<=?~zvR zaUo*sack)5heytHnQ=pp^-0|@Tdj(PxlZQ9hAmt9ftaNU5D&kum2Z=Y6^pDrD=WX! zbXk~O>0=8{k90{7*T>6T0mVxJLcY|NKb+WGt6t?FJTh)tWU|`+OC%ha{q|NN&}F7g zn1@j}*vE6^077u4en89`%pXM>D7HOFGBrKE5a@Yudu3P~;0ZRug{rNxo_dOkEl)S6 z>K11ftCK+V9NVtBDLVPYtPHP%H2Dbk^Fwf`vtbgE3h7m^Q`*|xfGNYDVU(X{-L_Cw zM1B^*lLth7c|E| zCE*)pOk<-oz{GvA*Sdc#!f}5I^^?%-Q?&kRxi&C^1ckb|LZtYIl2O||QQ#Va3_Hu2 ztxTKkb0mEmnP#W=skLUaz3fq%tMz(GW|?54xnBZI!xY+Rrz3dEgP)2UqJZ(^Tlvw4 zqP5T4`(;CDE>3t3b|5fAYdQ~;=sJFyS8o=BhF-;*y-(Kig#0RWwzh89p`>4#(qxiq z)VXG|4u-o~65OA%`b9%fYDPxSZPd$+F37+2#=W()Of%jXd&s0RG?Cpcl-u)9hX@U? zDO}K1%enbjjHb_%OHh8U3v|z#k$_CYXIESKjy>hW<)__f!> zjya1HuoJMe{CvR(57+$KmIrrt@(t&~O>aQ20_)Z4Ml_H_GjvF@kz! zvpk@{ahA@n4A>Zv2jLQd-G4v;*yv9i3p;Mv1vktG`o$ z*PLqOO{Z1( zr--S7QFd*lXw0Y-`8?2>&OO^O(yUFx_NWNP996pa45AZvyPDdNkNT;|*(JnZ$1O@H zCEDyW%W^=6vmm_0zaC`p?s_PT7^j>cP*+NHhC@82W$Za5q^3I`d0btMW>rUqCDcXV zu{~&?Tc5VzK#@-84DC}bQq-;18riwI%kDOClM%D+u}#~o{E#%Z4W997_O*(7I)f@* z53cR*!-nZgz`%n;w3EfcmTnNcs)+Q(Ki-pC+de$tPPxuKNiirg>v3-sc=5hq-H-ek zDZQOx(MK{l-NV|p!t%A~R7rk`5kKI32j}1pr2xT&=&h3Q5zO%(9w}+^7Hvs5WfglQ zZ?bR^;u>ZQczQOj855PAtb@VR^N=1n$y@QBE%HonQo+t2cp0!6RMoSea6L zspM`orlto0pXv4vHL!}N0i~Iu)eI)OlehB`?2k3{Z2uBL-d}&|iM^WIz8vpQb5fb^ zjRAB`lQJav+4GfW=%B)og_mFoBQ@rzq%Q^Fw%;9kJ!3imrEG6HgDTnAn#H27r1??N z97T9)V(q&kP&81lXT2Z%PBXv9Y|XSV)V5UKemoG;XnvlU(g*0RF*!57EyG@rc{_}U zt2K6Or00SVJUsVgeMS8?@-fC8ONNc3m5p89Krip2US?+QaO}&^Ts*?WbvA%dQTG14D*SiB#;#fy#&A zcavj!pAYPTLA&yd!_zfmBFB7-Glxfv$_=A>SIHdd;nxL67)jSvXri(SpnE#c9bNAs zjgSjj`=f`Q7=J$sG7~f)u}3LBJAL@WKeGz7VhMjc0fY9iXkL2qe$oD)UO|@zAf$l7 z_5@?fY?*gpJ*7S4gTJ)0B#n?ePl%;g=fl12Qaq@~thnY=@b>8h;Ft#^(#csW~b~6w-+WmE3Nn~JVn=zAU7S5V2dIL? zpYQPEJ$y%RyrlaS;d4cP`(hR2z8Al@-{XSVM!oEU)H#}scmIfUagr|ZKA15AC}DnE zcC{M$G`^=y`kLZ>#JnQrNE#34?9ef|(lvun!!pGY(mqt|FYn$J$LsU5UOOEvr@g|{ z;KXT$!b=uDe9*=r+g5SH$knKWGT|M@`e4l?gZlpSbg5<7%9bVSCM%m%2xliI-cHHt zlbW80bLv=hJX9%zr1=2YthMyRR-LCZG zwYBA@Hx0~Kmuc{)jzu2`?>#Zw*iqD0m=eWe)}$L+V{BpgXn8kb!%7Au8L%A0O!mN>)5s?t;|1h-h{9{IF{WRX688P>-LZ-UgvoqJ<#v@Y-e% zu=GjGWJJeNF8c(8`8Mj%my;GfkHS-?jZ3o)r@<~8TK zM!65!W%n#zPwoRpMZ}4B<=;Ka>Nq~KWzYW*VzZRn8mfz+kP!h zItsin`Z4=BJh%w0MT^q}cv3y=S~Ly@nbkE*-DzW=EZn_73tA0f;=CVbO9Lv@X;s(l zqe##n=8>lzn-PgY8#?LqiDXf4vS7cOd3MgLaEpMb6XgPE|#wAmsf`gtmVecm08Xyx=eZoJT_W7Y)@WNhWYg_Ry6|V3=hI zdvqwe%bwq5IlCzWLKQ@LjUql9C>_9JVG+4OQT@5d=1rvv1lQfWeasemt#;C|ZLrvC z-ntUTTIPYI-u>WsJDSUea8;07T(#~9@!%E=Yh&44hoEQM<~LBdiyT_&-R z4tVx;PnS4KW47(gj4s~-zR$T#*Jn=%3#W&gC9dOb5(IhGf*mwRN|shRL5&Q$6~uhj zY=dbp z$5HTpuEqo#+6$yixLl+H?ht_thr#?OHB0$sdM_o-jH#%aIFs`-@(KccC~B_UKPU3<+4&Yce5o;uCFbvl+^g=3O&Lrl(c}oPx?DwBiQ? z>NaloEa*0lnLl1S!wx6m^KxGVWD~`6UeUv!CG8Fv$lU#n~%e?wQF(EV=Y@ zgk`o8#?HqDSP-`G>rW+NUrQj5%q{!qJm;&n`+VKs@b;Qer+52`x|L8Z~%+LR%iYe+30P}xT5&i$93e^8z zR`I{7LQzS8$?U+bZ=`(rWd0>PH9p&10C`j-kSwNAIw~sU3#`j33(^`ds(gxCI;si= zYzrbd3OZvA4~v;W>R;E<>zf)H8yXs7L@o=!Ej!2pg=Ug>KLio70hVPwkw;@SD4Em8 z2o*{jU!RvB;|CZrKtF*IpBh)Qo1z$JOJa!w3%7@i1W7%icrzhGKSozWOD#GzwhJDU*}T=0h$@&u`9F7m&2m=dew#e`_}CuPVK$z;I5JLJsr8?5B@V=kB^?+D!#WhK0cew zva$u~fW(wr)RC_99r=Q~i<=oy6q!|%vu@va@m)(iQ2H(_%w0bv-=dMBytwbFMxb#l z^f;7S%6;AB6jY*XP8Ewf7W>p;O-4ecL5QD!{G-xok^UOLb-B3?<7}KF)%|Na-AS9* z^62LIJP|>f$=KGutj0HWr%+*Ly!kBn(s^oi!v+caistg=wP)UaH5H9CQjQPnnZ(gb zZXe0(+i1HOUwiILzLTI!eo~c<*miV~4VzKxotg`Ae$VG^H7J#TDm@hi6BUWA1&fsl z140;HdmV~QObiXpgdFJyh)=I>{EmtF8~9cQ4`U?Y#f!#(7H=JE3VmDI=}@1reBtEj z;n(MYN+kvC9@oSOP0xuXtcdSz&gH&-J1E>6Ci3sEQdxMKvkj&|FHU5NzewQ^MRyr9 z1=B1cB95`g_de*RDi;SM!p!|ePmNq@cX*yd|;(+yeG8zUr%g9e_a>sN2@ z+sWUfQ*fT#>1Oj+R{axpfFb9&ss2CoC=Vh2go-Bov)F-i<1$g3{_5apby55=+)9ST zgO|@I)DERU$Bc$-UKhVD%Su0M3F%dvnYiI7e%I|vmbt-CenpY$vJHzCFVMX@}8YQi1s|0)kEo(1sk9RbYt@DcO z^6<2mj$D;jI<0tNlC^oAOnyJV-#zlK#8^?J>y(x$oUy5_gLkXh(Rkrv0(~Bgi?gx# zS1mOltL4K;!g>7DRaUDpz`OcZR-bAjZEUt%xKWyJ>EnWPcPWjNhAPGQe3BNWM$^Ol zvoKJkONzE8Ars}Gu&=M(47jwgh+(i~kl`}R$Lfk#h3Ptkzq?)Pse%cWyaqiv-IQxe z8!_X>60}jfHI+c=#5@8r%UbDb^S?o@c6Cw89Sx?l&PqbRiv>N0=luM2r~^M z)!bdDwn+P|t7uhi8@V(RDfaF7*TJ;~ef`@qRyboMKhu0$rQ;(MiOfCnE3!_hYF2zB z^>F{)(ce}3Qd`15x1h#Fmg~fE3boQ`uH~X?-M_5? z23|`lam)RQNk2&{8kA|%5(Vg{H?seT{ik4w|n$5~aE42Ul**ErSVZq-d zG6qH>X@Fi#vFgfrPNZ;)URvh=oTY;@vC(VjY;jCJPs!S4vTR?kZ3KHq_0qC`H!XWP z{7r{Tm81Bcq^WA4*rI6vI1o0dgpAR1byJt?Sk29KJ}I^l$!#kRrCB_@v#k|sVJ^bM7$8Sb-i>jDUGuvOcZnHdR+D~sff4|RX0de}6zA;AX*+pnW4k=~B zZo4AR?|And?NR6p|FXvTaHERhM;uh62x;h(#&Bds?H>Z28oF_YC^2{ug)B07Dc&iA z-eMBMpqd_*83=f#31%VxWr*1haKktau2a^QuN!BypIEN9(U3)oL5HB zW9~lR#Mesj^e`7=d;v0LJE;!QFr&$4$!@T3nTdxboh{SJV$JA(jXOq@nROTSLY2O4 zxYl069d$w3%Df_?&2_z+6g-{FDMf|Rv?pvUbzw-ta5h{9Twq8 zw{bt^uv~_TRY+&7@|euO?-^7be8`bUkiAJi9BD9FWGFYJ{j;WPW#6B`VcL#%U4jj} znOKQgX|0pvykfVxnb7_`kFcbb*gdMU`oze1_8nzGF$TJA!1}R0BTkZze37;H(ptM) z-Hsqkq}!k+JDt@aY=@j)elFqNZo(RT=ot)-HNsG`53w+{UGK!;$*8kbneN1^mo0hv zzm4VG)A@eH~+e}{p$ zs$ZC&A1+5BE`yCet&oa|@q9I3vfoiCGisADW45i^gjL8gs*Nztcu@le?x}*n(}XcB ztuT8L>_D4MrvlA%e6b{59EX~iiUNcF?9i;Z5V`EsQ&!sexcoIWU+B@2K~ zoBE8}Vb(^&qg0I|nY_N9{x|(1UD94w?XC~fJICX^K{tB_wL+VfT=nGHDW7pOW*N>x zWwr8s5i4P>uwL$J_3qCL)ml4ifa_7{aK%1j$}m*TaxnX!p~Nw>Hw^XCOfCi+x=zC; zZ&{SBMa*dyZpV2eXBf8^2FG=|*gq5hE`@i-7pHNwl2BDx(RyPR~fww z37ATxJXvq8zCPRVicCI~1(6X>LS!*#F54gV#NbHQ(@N&KTG$(OJ>DbS)g3WwF96@3 zALOhC>s(WHZIlxYgWnVQ;})hz$UPNWo_x!Vg)~YEr+A8ZRh*^o$um|`YxmSz&X&I= zDBah*1RJFL_<8I4Ue-mKL*66)MR|W|>gJ5lI5dk)huq8WmGXvj&vAert|t-nxg%)A zE4^T5&YK`_OQf0+`vdd+SQ!>M>yJH$g#E`xzs?C6yOxx7dx&Qh36g?Ms$FEFTG1x) zK~{4hPVTA~R-CBakaKEbOE^6+;&3ibEZsFH95ZK3!$YetReoSqz8{)BoGNV;-VTax zpIKK3wRZ@!OY92)lQmE8lPyM8^f-3)kmTi4!01)p=-K?7^m(^a$At-87`#uuU|R_$NdEO@7^5$g*8m=-%&~QW7aUw z2Mw6L#+Fe3hsk!k+|js=y_{os)jgP|tVJ^`Tq&xYI$!TLv#?{S7&?{9Xmd=r{_N zo(LV>4v=s^R|;Q|j#u&D%U_{);Z(=`7DvO?{3_=gSEEm!^`E&w3X%?Igr99drCj}R z7xe>&r&ySyT=s!a_PJe5lnQQJDr@K7p5G~vF4z@?Snbg{&5Pc+k6N!oRHUs-W1gR3;&kOQsC9mPHo?~*snqw!TMP?(z{6@&$JJC z8E?NLBvHw57irRBif-!(7-`%)nQltyXvb2qH|g;O_1`FHZCk}~;tI31Qmc`ohGs*D zTp|Z<(&X45`2Wbw{ICy(ihsE&3-kYqKK%bn^obeU7&{m^**g4Z>=mn8DFML>ua1=iF_u~e64WnRxy?tL<}F73^;UDTVCHHA^) z%=S4?{V9{3?RE#2E|N7cx!v*H$vMq(oISbq_4x$uL(r{wL$F6hPCJUFKrLmc)|Vf4 zCNY}2dk%rZY-Bu9E=x`pO9~z>hSEz>^mp-CmN{=8z_E_;I8h}D}H@*vw(67o^ zCS!ihxiEU+YBHGwPe4$_Zp2*c#?PUh%Sqe~JyJ;vj#gdQy(`+Jqu!)VYH)HS8NgAc z(Pl>N>Ssci$o$lbAT?T^gqR!2IAl3NANd|lCk_w)Oc=y#6bD?}V5z{P=w^G_)H|=v zaEV}IZphU=12z7ci2(U_8MnX2ky;nYQey{G{BD{?8Zuy zt2q8-Or*kKWY)W{H_5BX+yx6=0s67ClIzNQhcZG+Ml7%fOt87(5$;grn#X$5q}5ZT z8E7rkhfOz-{}RhCQtrcodj{Gi;Hwq7@XkurHD)!@3lVQL?rm%(E6&yS_KMxw)5a}$ zN7q*6*%xU_!1wP2TbHYwqZ53lhbSuO@>;C^RjIIlUXK`+&9!VD6Io#Vj){0=XZLBS z|MCDgiJPCk6&3;ebn0;Bs?9lIz;PCZ?iEAa)Ydghq0Kj~@$j;^GW7PipddY9+p{vA za*|<&qSU5|Vv`5q*Iz z@yLL)N?ZDz$8=%E0e0HG7L&=+K)zQZj>0Ny3a&vI*@%|yvEEXq%OPlD(EP^Tv0+U1 zc>Nw;FnwVD1_Wixa@(KH6uV{MT(LRwUOo_2?2Gw+gc;)0+rL}i+DQ^{%56nx1xwl}V zal))>w0p;)@@kl&8e~3Bq^F>+_R9BWJ1U%+MLQz2@>!dkxn}#;K55#5#CwvuPGvj{ zPX+;$d)PG}9dqG#g4u5Q#(6N?cr!twHN@~9(f}L73?HY`%y+;1bcUWNC30GQC9py9b0YurV|Bjv(Aq5SE&kfzeZ?BNbVO%ZGX7J4jzBPz z&KAt!)Pqa(*H0MnTXwmfOmDjxPLZ8fP5CiRc51cufV=&?1bCVXQu@1lA9g`Xu<|YZ zSMJrGMRIlT$RFJWKYirEKR~=+;v-(>1y-sf@TeE|o1`~rZD@Dlv)B3{|NgPP7UekM z7CQ%t`iA{alO`JHM^gv?@naJ9e{0hJZ9fqL?Hev=>yc_yffD6dkAH*IJ;_6VM zfV9D`r`yk%>6hKxZQmccb!VipuP6@cRED#|;tMb@Dm^F$V^9_1XPtzRF=t38oW#C) z(am~LTStJ%xK9jMwEVdlv228bW+|U=mt6F1p%Ut8FZXSL zIvyo@{u!^g#k3U`%uEo2{8>~|k`KFyv|Yn)2=9{Q@k zsXD%`Ph3b6Ec-r=e%}$L5uY`JuxT;VfBq`6H<7*il6)v9a2nBDkm9t4n2(v*HNhrJw2wS?UdZDd!>6O z0ZruINWCA5N#y2OF<|IA?Dx#1{Liax$ne#T0QvDl?ce_Se_uUW|I7T9u{AZfku|U} zFg0-?6LByxHnDLsH?UH+b#^c^`9Cu`S=m~CO#zD^6NbJi;*YYv)zqYQp7O%!7LnKx zletol0vYl9FB*UnD7nxL;sqi#h2v`d;PkuVc#fwDwp>FHhr8VLz00(>$Mv+Hp6`#9 z-#kB{$7yu+Ma5*qltiH{H!5k<0MxBhtQ=?)_FiQ~6ZXnNLHj8IRKsd4x~!!d4aJx= z*dpIOOzD>^q0Q^gmBp)9&SxBf#U0g~qa0O`aFbY(;)EB035bp5{SB}?f!|{tR@>RfCTVv83tS-&5W<+xep?>>I;TmUs*Rds` zM80}6Ek47Mp=hJ?1t|{tmUy-f{9)@tl!9>?-s<^HU|Lr}c24QcZ<&%^%}w1>1kb=e%#LF+ZY3rLUuMfqM}`B z40ahKKtCR*fep#VZ{~3(dq-TjZjXk`gq(CYL$k=iK%Pn0hVr)fMs zGTQ(?y}>5SH-?kkL7vo-q?G&|jV01tiRfd3ddN3Wv7Pp7Bhs@+Z2wc?ikpB5|9{o} z0rtODILZG{h5ye_raH8CmTCI;Uq^4&eoUhIIAbD6Nr|7JL5)MraRupiZ1ZrSCK5^s z03Ls68Ccef3BRRH3w{g4Dsg@b!>;u`WOia438P3{I@R-X=jEkMXXizoSPvD;CbhMT zZ+BBa=$YS}A8F5CSLfc{Ugyb>gb=lo?mD$Q$`AsVM_##b&*P(@42=frfL!Jqjda1| z0Y6+hFJ1kZksEMOJ+>S2^T!^oEUvR5=BQSC_AAtl{eXKD%I<_vRIKEEW=^?1AxgQn zTU9>e9Rto3=wTBq&Kq{huhnpb)ohnNP0H%T!ytvXcGTEb=5XlU*U~Jn{&3r|(H7(T zG(6w+Xy0Dfphefsk>Tfq3wzG)SYPM8?SmIclEd{ZH{k&fr4T}xiNCze2Vx*!B5d#S z=VCh;Af2Mkf-EZBk!-jbHSzeRhW!Z)qug zE{Bv~qW!mVUb2M^yjj-pqf3n9yX_2_R?$_NKyTgHG`2A%i~dl9i|hR*YEuii3?#v9t>XR&S1rzjQTpgkz^Ebw=u!4HQmLhr5q?JKKhOvN#Bs{c*?6DyKa zd@mmEB_MnEXbRw~<(bDngQ9c;W*Y`4YVk%=h$lxm`pfSOHN((DzR)&KB|D&zcVk6N zk@aQwW-Fx`0^HMBpJ=Hn?)?}StJ3|>P^=XOx-a__!dQq2 zBQ^x$9r1VKWjv@ipT;lu4jZc&xRWlIn`?B8!1PWI89HEiK*ue-&MXLX6qkoI z9f7p1a&3y*MLzR8OPoBbTp9+%SC1FbcFylVyyA$#Yj@aH7PLw0& z@rnUN=1VN_t8#1EvRjlE%^ZRZPW2_wsm0X*E3#`3-FJ zOv40kSjWj)Atc$0L3Wt&DN{yNbGVGKT?o{!6Xx3}xXlwKbphO(?y}c}Q!jR8(Gvad z(E(n&%y42jn#vqxu9;=b%kYQSME<}}rx7+1^h{YdSDqzt&&3vY4z`dr&@cT~)`hvP z)rETA1^pcjRNo;z89vH{ONi(u>NJTB3Ckn`88T^83!`#2493m$vuSDsT!@5lf2v6w zS!}nh0az}pJ<(3%ov85CTNtA!J}6V!B{Na~CO}|PeX;Y~nhJ)AN!nS5`X7p!go3>x zwIa>2th|lDTbuZ1fsvMi#qI8S6%G^5?U?*$-43j$~=O zWnC;z972|4t=7YE4a8lh^NKPVh(OJ}yTQ89!4g~GC%Y<$&At&%*g20Q!k`3;CJzy! zb4wYFJr`uTj}KL+T;GugcE}BgGQA3cvQChvOcNBLGF1+Ao)z_)lZ%@rU5Rrl< z%PCYWd#X(NEzTGgh=2Q-Aq-tV21|XdJus2%M0>AmtQ*{HCbhnqo57+#SV8MqT~*{A zFD_0YeQ32QBz%2S3v_ck`>?b zC(o(61QhxeR0}7EJP0ICzgLQ61hizn+cwr3s+lD;djpEg)oly-2XDYnqw6#2!ot^s zj@8AAKy&kI<9YiMRVKr-p)U~R34+Oi{=O}V&JIS+X)_zcFE|QG%lY7(m)F1<@@d^> zc4xsD~86wJViiUaxs z?I?tcz&@X9`XJh&Chm9k4ZYL`#{JkwshXPQ2Ced#91XXJjqL1#RnH|7{^v7y>dw2? zguUpEu6QT9ELyQVft(15Qae7sK~g4!{F{GFHL0EZM!^W~^u}haYBwqqVnNz`fPwY`-LD+s={{yR zuu7%hh^lfbvF|Z`5#zPk>o2S3dFTcXu=A+9=HLuPu71#0-Ev{#r2}Ni9%$#cvW5a; z8`miB>#YH~wQ763P`maXH}9Hlls>;YMq z-HO^yQLshic9wb{eo*pAL7&n?JSnj~Q@o{O=qi&mYyqOq&B$o(7`X!|uqxTvXN>$H zH6<@#QfGL&!(@QmK9)CI_v}r}XC68IK_-|pI3b!v3Eg+H58V#aMd(U_pz&7C0<}f$ zudDgbQn-Y-t;v zx21Eu={asXBk)!ZIS1S9uDvd(0W)3S`;KFPW`aEO_YBaLnM>17J%3V)W2@J*4o z5ysap6Ieyul$(vc(n9wN@aeFeo9E|u|MfzkeO^WAZgTCq6G~0fuhbA}{!~ewn>BT; zyENQp=o;@2ENY&#ucktPH0h#TWPL!punt0KxoCcQze5xF`68fGOb6ueTD2%`&?Q_7 z>vbfK^Azs!0Uz#CROgx8-N^prCrQm`<7LGaPbgf-vO!a`8$802bhf~`nq3=Q6OTT= zt`VAZDvxUXe1kij7TKGQ?lQF1qt!bYpJoZI@n4;a!+&)EAG$-)1|M1!_Rh=4E2b!T zl?Y2r&snJJjtAeoZ}#9|W|+*`>0*t-XQOA z0M8~%dZ&Uq=|~>2Act1*W6}v7P@%)7%F0oOVWrq%A8b)HT0O+DI7G7@bH1RvQCe+) z+eye-VQF9Cfq$itEfaTc-x9GSa`xj&S!Zp4o@q)cscjdF(+>7KDQI77Dlct6vX;&+ z+eD7%NEmhn53vi~c3f;>P$eTjG`KrKQM)bKc7|nI4rkNFxLw%P@SdpYO342#n|Qzp z*ip0la+@yeZ?eiVZMexL(obwkGkW_}1`|u66ut?-QXk}__Mc!3)bIwh=PR>NVUR7J++N01y zthb2}+a7rMkWHn0EgakqyLh%Klk=Ph=t9(5+|+zRQP&%iY8Q8(j(|MjWIv>9J%|i1 z`7@NA!T$N1UAV+MQGM4){O(7J9a_Vs>IEQ$IYm0AUv<*XWqGw+^uP$m9-pRgUZ^RD zm8GV*DDSFd0+A^ov`)nvVtm0=v0OkrJyyTqtxx7&iDR>q%MSBkyQAUCE0*Kkvt;|3 zJxLwU_NB7X_`VWT7r)?}s=*5ks7kwtM8*;_V|5a$uZlR`KNH+>`$gBkxhhc2wBF19 zN)dCGB5QfI6igZWFI1vpOit-uJpJ$Xh(*~Hit*Z;)Y&p56fmAqoqj3QPCms168h9? z7Ctjb@o*siXe_)OZkjI~;tS>>Yy2Mi&I_gZE&u2gIfsHSeT#bEX>jkW+{)rxibIe* zGdF&6S}*xuEKRC`ocK^5*E;CmVA;X|Ce|W&vz#qhX3(H0_T5Q3;!GNA>!a}=VWW@U|?tFP9|b*XJ+CcZ}%T6;vWU? z_&>mh%C6ix0E?eO?gC61uE@!p+6svXI;pY9Nvj{Lmsw|t&A@&PtqFytOj?Ir&KR$; zdzipjy2yF0vFz_Vo@b2twyXVO5Fl|+;`qQz;Cbts;Qej$ukQ!^4uenBKg#euU~#eFWP*DLVF1LLAX& ze}wZ9A=(0&OfpSqutS-f3jaUXyGNO3d>*dNbMU@eM~p3`%`k6rUNYbGZFGva$64G& za%Q$mwRvtE|DWSaUhaP1MR*>{?oIDo8fSYi-9F>q#+Zo+MN7Pe*eHI|O0-G;2wk?S zNrQQJ^AUEoMDq)sG)a{ZQyePzwh+(qWrn8#baK&KJ`Q^5mYk^0)0OniGkaavx%64_ zak1lc5&mSI>9U8l;TXM^SS=OBfyTvSlqjdyB5&ZaX0vNT!_VzUxx4h)^$iz(&Xstk z_wZAxbP;I#asJzjyyN+^cs8|#OCT+tDi)TpN7vI+82p6-EOf>Eedbj6_y^L`E(*4htOT6|H26CmFPPv;e*=+oiqyDtckW8;;4mR~tS~t4gBMBXg zm4W;a`i=QoKT&vTIO-6S5cCk$5H^tz;_=#{+89yj)DWWtBQX zh@{btZ+0pJ%Nv96#Psilai&f)pJxvIPxrSgvku8Es9&9E4m1acacaW4@Z9(g6bDu# zmc%uz=gV9cnnGfD+#3Nw>xCHMuTl@hpCNTEVIRT~F%Ska8y(VLvVG5A%!;xyt0%jJE|y`B=)EMlLTd8%EQRaB*hFOS9;b8TaOb5C;1`y| zVB_P0u`q=rVVH>riqeo0g-;@ol!av~gtajY#RQ*qkS#dabkWaPKR43R&N2uv&)O9n zJ%mef-(K_zNxgZz;oD;EE08aZ`YZ<8Fr~x%9F9CI1Q_g#S!6 z*ty?4@cj>RGZ_6eEe!e(cK*i?{@=05|HchUTHDz=I7yqh|DVsXOB32nW%2PFpCys= z{w|J*=@-+lp5R|7f1r?2dMJo@dW@N2vFi5558|VyP)sT4G%H#eoo!VYnl-h|HzTTK z0~C9g+8Zr&%+77JIvCdgmq$yESDum-{a~-%e?RZN+ur$~>Ha!Sb=khmiX-yD=_7lW z?lSU}-=u`J@V`{V{N26gak!Bq*c$XC_z>wAbauN^%|~$OA@Qt`6AFhI{X9cVkg=2Rg($l)QW9vE5K19^ z`8>TksSA{w5`~*ikmrqf|khc`|NF<|rOGI`)TeqSsSfAx5l$pG|z2q_bew zfG4J?KRB9{$%QG<6R!&zV`g zSfn#pVf9QM;LONm9wj=(LWO#gCOSp3OS=Fa2cL3s+DxU&h=1{fE8W6bQ_>lyYR;@F zH0GGNvq0HzX~f;K2tDbKZUn^YB9&+&8l}W6ooFOll~UtqyR)!cml)+)*d9OeLiN04 zqfm(a(~PNTt8!3cGy#t)sq!#TGbz+#7Qnw~wh$l6S~eT#?QWLR3xqjckW2~X7;R=i z)Lkgm8h&k*C}D!_W67eZ5=$4@3{+!_IBAtB&l~g`*E*Smtbg5!C$U)y`EEu7$6IdLI!cHqQ(&5&N4&t1 zeeEj$-f5#ZV}SF-E1F@(o>Hf@AerHAW)Mf`&%SQ^f=KGA6Sud29B#O)vj%kEg+AY` zs?AeczDZsy-3Wymu$#6xgL_$vQUw$b67SqKljn=qw6rEw zAMBlpMFj2K7 zHH{u9fDsvu?4W?7Ep-~arlsIS0}3Vl$U)`e2KNYWiveT)n2X&&@8(+i^*EB_3+@vj`)+f!7#S$LlU53Y zqX{x{2{N)PqcDHtv*AK6)3y4UkAR8T^$CXsa<5K0`e=x(kqw#xNLckCFNmtuheK&VD)bG zRY~FfLwK{J;Tq0;(tBPqsvMe-`jpWuyp!xf$KKNq}VV^t$9cQLmC6t$;kY;^_0!#SznC=m_6X`5? z`1Tx|9XgJLV@?jXatjk@OZNP;Z+*Hzn?ihu&EK4kXUCJG$PN7wSWd9s=}h#NZ~&&q z@mmCurp5E0){lL0{?|JlL84E;^9-Dk85!34O4ghrr+7N3y3450m=h(^VUy*f!$)9mQEc)dL_0u*@KNQ9xg>ft}}AoV@{fq(l(4=OA__m<{aderRiw? zDsqSV{rm6mG1v8m7{@R9-6gyhA)4)z1*;=+j=O!L=GlYjq}fI(g_Cpw9>iLto*bP2 zBxj}}sFL$NtS`_8c(mBlyY5r!q}`doud;Lkoq60Cr!jsz!;D@vRmcEmTKG2#@5nMt zRSayWlG0NK1}q9!TsI^{Ep>eD%@sT| z{_Y$m%Ovfw?2uq(zuoyO<`dmFj@=nHq{6_J5R)$`#%TN!jr=4(56o>lxc1m?qZ!vz zpTKT$Z-__7UJuW;Jdic(8Bn&7#s5%qZ zGe&VCW`w7U2cOYmGak}akf|0?5LY@NUuzv1GFb$!Ghi8^k&C|z*A1Dm&V9I>8yP_` zZN_Q`{v(3PHR`~MP(2eX1dLzC6K+lV+43PwXuE`v>5Ov$iGud0BI}}#F6tG`jmDWb zDdUPb{2MVpaAw~rP)L3z5#m!qQ?R%M&O@`5pL9^Z&>w$js7Td{k zrS{O&xsf#%ILjOSXG#qu#HXCoyXRY=kZ;mb?n#SR!}x@KQ^gr(v1sCG`n`3T-EX!J zQ-5Su1vZRf(&O*ot%Sk9Gbh$^A^cu(8|y*9y9ZnU zui9d1t;I2{eKUK3QC7VO(-wNnVkWaP_vS;LZF0f$Q~hhg;Uj=$6n8Ksi>9MeMTZ30 zltmol+7~ptc|8 zJtp1e+t50Bi14XmUP{(E909r6c?pF(09V9Wsp7MMKp79=AtX!P=CnFDIy@Fqc(2gC z5-vn|y-F^3pT1DsfR6dI6O+U$ex?GBLIjK7L8GRfneoPXv>5X0O~5%Ek#EY|!_kY= zI>XBM`oQ`^N9?kC_~X2Qrql>Tf7>sAa`Q{MftD~;^aAj~4}!6~FhvaLf5=J8AMPqQ z85rpXycXg}Kz+UmOC*v)?vU`3M|0X8lFP@Qq8{#(V-C&w6g?eZhh?j>w}|q#L4quR z1RJouwvgxL1xqO|lMZ5uF*flm0+$&5z!g6lkJz=d9;NN@n`8cW#Q zV_y}_idy5m2z$Jp+LEwULc5}(Bh_d)*^05FF&k`G+AP@Sb;&Y=&OHT#rboVxc7A$- zV)gesF|`2ZHGT>ZyQx+^NcTSMUBFS@c23ahJ>ZHy>w4B$97n9w|7wB6^&p)XC3Z2T zf@ZQD{FerRnjV+!de;0jV3e{lNonMgMS%UV7N}T3UF97hLX_Xv($z>kp4JfK=xM#N z)<}&9vku9f6{-|9L;QRjlzrV;`7@s76Gjm?b`5M*m;l3BCXKs)lx7krfYjE2A-5Z9Q|v3Y0sZbeJY#vd1>SK+te25A!Ti)sNTGPXTB16rH2UAJ}U18 zxE*dzt-GDJlH2tU!X~H)e%l`)l9X&7du|Y zs_~DX;+K1zXY1ylfoE;OEmk;qu&UNfoK7lrX~@+`JW}vzewI~ zL7v+a$YjM*EkkPuUAAJW?V@;Lr|vOm4_S0)O+JHvpsDT17JqT(d_t)u##ox+R2Qwb z#*E#HYDVFvN1mHg+vF+RqdM;ac|i62KL`ZS-3j#*LioQv`kw8@w~{2;ah|3ohe`QP z4UrwNU0}%w|>B}T?H=Htzn{^ zwef_^C=~D1gL-^b!Ys=TjHBrnUZ;Kyu|Tr0n;>YWq;UV@E}!Tf*(OZei_G8 zz%oG~c=+%l9){3bxYDYG1Js!!4L7GAB?(L%Y1jKuO3`k@UH$x*QjLCIXF|J)m|xCc zQdzXA+QXzi!{TRRXucfio_qyDS2ICBa@h~_w?YLk;5%mj?=;$tigPd^?tIyc9kjGa zO5UHh#OntwZI_SQSEZIBS6I6XHtsG! z7Wdd+*DM=;nJjtB)0(E@NAYENq`-RBV-_e3eZ+nn#{Mg0u+=4Ns}AKA2OX;>g636V z^V+Ig0=2*-Tdq<0^bW=cnCp&)?kitT4=i5;Vht#~1kS+EDBC~Qo1iZYA8)1c`pr255RQF!&EP_sdB94!-d}0WprCY?p z3WR=&9x;7tQ3nDy*@%ZG@dfsgG%?(!_W1RJ3EnG#&?mwR7t{i)1@6YKxY;=!nk|HKsI2kB+}3Bz*S|z*AuY_XzM{}uiW8p9SD-TUHVW;}ks`62|Gf7W-p?QoYAns(4hI@N^~8Vo1?QnmI~W(OSUfzQ2*^s6jz zL=T!(cvuI*6ggA>6y-dAK0kufL(g`#FudxYRfMKc2KE-ITuoYD-lEX2D4-G;QzN^i z5OL-D5ntMRjJ!_%<4-_qq|EiAf=)%{tyYtPtT9mRvVAH<29`MKedZj5Ua}0o)udPx zHe`%fq#{YKTnZz|S9?O)UJ*9**t-|y&La(kb4Epwt-{k6Q2dCNGNkKz+s{lO$}HWT z5;FRn6;IaDj4)3?Oi^27R76?Kks|0ay^1iUGPQ+^bu zYwwFCj@oNO1?EaS9$HC~XF$xx9~#V-meHa?2-V=9JrOL<{40^HuKRT9Q7@Y!b#)6% zg^}#9+)Kvh86?ur%LUoy7(}s|!?g&Q#IZDS(`8j*%IiPN2OP?U*;~|RS=byZHRNeo z_67uEq)8?s-A0j*An{N|(@IGvR_Wl>a#LZKxzQQ^T5eRy0R@*wFG&BT%)RQE?^(I} z*>6$VPfa5o2Oz7{8DoN10qtWSHoA{*etthr>aS#gwBADdtB_AIL;aXI}^ z1GH*HtG#lsXzQepPg-6SA33~lZk9M4>{ezKG+`VZp}A?}1(02t$-S%aA~2_0(sueV z?pyZ}Ra-uLkGgsb+9=q~!#XVq`ww5!XA|EZ4@GqT+{``h5TO6U3S41E@r5IQlqLOD zAtd1F!?EFn0BX%E?M*P84a=3S9aeBclJaHqE|a{4bBC2OM894;D4JkU&WwFa-jxX0_ zOg$5f6ii^ff|5kH)*ybp-3>ipHyf}0Iwbb0S7wd9uXkb=&seBEHT!;K%`-?guEl&X*{9oIj}Tnk-e ziQQeH;OSvn_PBcAR#j|9EU>I2R}Vy;4@m1kM#uwkJ1}p;!v`vw)6mj_VJ;#;e+~~6 z3jSc(2e!QjxMsUgZVwF1IY+qKV|&~#zjgE6xf7HFuPBFB_KzV1M;xDuUDqATsab8y z7kQL|{k- zU<#5@Tv$J4e_|;OP9G$~$pH8049YAmtt*_%VCnPr0*YXpswL4@8!aL=tF#s^niefM zJ6anp${squO-acVQaHc6e7C)yb3CuSp9w$q;=6tTBd~F%2AW{1mF>Gi_`JmX;ae3E zIL%wJGugecw^flBZuqU@6RT*J%2p_jTSi{199m^XOFlNM_guhTsall>b#SDL+VfZP z_iW&|hvmQp6>de^zDT*h>%7`D5IPQya!XH?>|z6QEztHJQ6Z+c((wPl26)HYik~^(0c?As=!|7<8Uo#a_LgaMRc|vQmW#HwOWj0z zGv`|I=RUSA++c0TPl4Pk44-m?$uK@OJNV`U9W-Bg|89Nh^{Dx(+)#qZbFD1_2f>Dx zM2)P`fhE{hEi?y)RC5%*rqxxY4WdA0H#sU4sVw5S>)b}|9#vH9rhnoBVdM&d)w;C^ z_VkK=ZAC--2Id6}HQgu`7%F;T*={k-nn=%_{85k%uc8ZCe z_CcVjls}+@*vy$icvd1S@fBb?0h0k;q1v+?7eywFzjUX-_j}eoUV_b_7%X(LioIhZ zqE>Re!PBhe3UMY%^mHD^Y|DWbL$g6qKa1@~onj0=qbnEZz?caHraJK|J=}Pryy2uc z^Ct>2{o%{pkeL7Nmwyw_TVcDfufC#7v8l|vuWBWk2R3?4!cW?j3}3XZDLC)K|+D*7y%=I@5%N<(a)IU*jkXRLUxHYWcU=z0(K&_sl<^ROt>WKH)wpzEGv= z4I}>dPzldczKFh!cq2Qu+)#8ay18`YI!_gRHXyE$eb2lAUdy+WKLf614u*7gDt*=9 z^aV~4ADP>Uq?k130Qn=Fm2?OJtz5k{vQ0=P+zBI0#pdvbozi1{urD{1~ICaVCr zBEGod!%`NlK$gQ~j(Ew2*AWdNjCRSyQX8jvYilt~LoXQGo_Wgj{_$Y5_& zi%J5?Oc-xSN*1$Rh*(E9}l%COiD?i3?Zpnf-o|y zKz4XYM&l^U~*yxM9mMnDV9b;8~$OCL!nI66O%d1Fp^1} zzFlZw_%K9C+;VRljSxK~%gILLwVg_{{B2ZqS551(RgCGRNYRTF;%DEvR=jVh{U>a+ zJ&k0{Sv$N!ou(m%t!enrc{Z<=OicMsMhrGOJ1oZZ2$(twu)IHEGzp?qc#Yk8DiS{k9B9-EQ1aE; z_DZ9-ZOgX?km#G5&S>`1HM=77ej_`7Qw86V98HPQ5>Wog2XsX1?-DT2pbrx%e_6E? z-4@=E94|RM;9inA*glswi{<87i6BrvpTu1R*hafSq_aP8f#^6aCg(4u7L|J zX5ANCdr?v}>qdoqR$q_K>^b{Ho!meLl z^!}a-d!f-fpA(3Em8!=eOP`0lim!W|hDQYzL4Zilk3 zkP*fsJ@Lc$Z~3DO>z2NAnw5VcT0?MlzBC77J+V{7U1v~`)2~p-0#jMGp*ZM?v*GEK_U_=nzO`gMVCaQzlx7T!4{5#=iJ)0iRTK4^ezm;GQ7H%l{7M^3 zpP;@+tl*NGS~1GFafP&ZY4#eUbIkK?NZrQ)Z=&8tr{e*5e~FM6kalABzzWWH^>?q% z5~zJHeQQR+ur41uxRt)vZoO!~!V4x>l~>JlvY<0>gbQiSYUVcRa<^`4#fE?@Cm$)L z{Aq~NmE)su)x*n1(Z9R1jEzdu3^tPu$+3kVJ9IR;p~R)VP!q$(3x*UJJ$sp6VZ`eF zkeF5K{v0EMm(&%)Y>J82?8o32rTx_B7&lcaymA?PcWrpeW9HI~FtnQjYGvyc(jprj zEV>?Tm}Wt^vREM|T*%|GjtsnRQ|zn0B|ox69q-91NyUqhsT0CBQteyPQ1r1j;C?~p z%eScu-}BlVjD6fI?Z^?TcDG;-7I-s!B^evg7myZ+t=A$*_406mR*d+w57_?`QxZxFEF{yArOEnKufNown+N@sM4*(hg`LaEHVG(T&-jG@4 z9g-MK)H3O?v!a{*yZx1(YpnGUhDui}q>}jn;p$Baj8y!dnJSdcIpB3I0o=A)Goc7( zq8aHFmI$bhgPo^$*S`?E)$m@`2zu_dk_Y_>f!BnPw)eQNLRDvv-s1hR;nk{&& zf0Jvfx?WV< z%i4hV<)onBimpE*kf~-oA55Zo)7g?iE0&^p(>C$!P^=@ICnL@2Yu!xS%0hXbIvJ$i zOgrUI7U87vv|AHM4=b_t2V7+j4x%UgR=v7(5YU>sr%hHG^rkG9AaORD0-4x^(QA8# zTPDa_&C(k?;E$A;POMj>GCbZ*^~&)YyByZWR#rPxOzy3!riqKM0;<4ViOX^x9d9xq zmNy|TIizB&NXIM)ueUci24yAtk&47jY*QRZ;)PFq;BlLJm#qg#e)`v@f$FIp>F>62 zDx+1Cp!-2e-iE5jd#14=_ViU-C`im0Y{kdTS>jmTug zydv8Qh0)t}j*gmF!vAPIw28L=M*Z`CA7K7(o6Uc7=lp+VSpTECQVVxPUB>?Ar!!YG zl(jPOU-r}B4H{7&>P1T2L#s8xJ$Q*QkqIBKOIex==%g)7?mb0L*fLF^;1@=mYxFNB zmlamthuHhIF1hv)d%~RE>?>4~J=*xWFyKMJM7cj@6*G)X+*@e*BxN>@=8(e?lpaIN0@fZv5k1a(DTw7DD=CwOJ=65ih@N0vn9$?c}v3nc-*Jdh((5c-ns(GmL6k9vf`VRDFUG zTBQ>O_5rm?kAya7LL6C@HG$gX!&DfjQP}hK8gs&NaFe|w#)#jmx;eJ7CP#BAs|1UN z*pnW=GtVt{L83G*E+*nKT#{biriL3E@d-iHu*3obnH`Jyi>J=Gaf(fO*7}=sev8Rc z9yeD_sH&Mrn6Fm*L+Qbkr|?USVI`qdvaV@+uH-oMj^?C{62JhNSoC4n|A-P0!XPq( zrLwEB2=~ZeC{@6UrI6h@fHq5}SK=(9LIiSh=_kHayADM?XUjihGZU;Ma|q$WPmk{VLqC)*V|?%uc1rcX3+b@$Mx_c2A(E z0+&80Z&K%5%^opZivp^vsJ&j$MK04pmen3cX0*x3oG-hNE?{5|$PqX~7q}%E+-K8- z8mgOhOVAXNFQ~Xu#P0(REaQmv8B$Y(qbo|wlHBDY9sShEKF|acnPQCKGRJ^{X8|Zm z783qavi0i}gn9gfSv*yPbfVKV?aUR$4{j2(^yHarj%BSZMbza&U_J{2Y=c?<4qlUK z4_3fMx5rOuM5HTDWDS}!ClhjLuZ$Ea!SSYGsgQP0#H*OH53+E>o=%jrW+co-1tlll zI95Bn(}bsMFFCA~C;=~`5*7)U$vTJH&IK#U$U9>=!L$=^U^}UIQ`1uIvVDhcvHFLE z3H|`YI8pey`O%kIl3owL=IXmN0p`kNbUSJ&TYc~#oaCL+qR@$4a#?bywDSjF36j_P z@dnh%N7htABErH6zQA9iidqA; z3$!RQSt)iH1OLv=z|nBA8=9u%UsfZqgwR*Kyod5cNI6cDISUhUwX%5Wd?YAF>IMZ@oh?kS^Kh7-=hGy2(jLj>G3F5T?>X-9fNuYgddQ`Md}t?{Bq5sQPZA?O<^YFb50`w1#juA(-LEK5 zy2<~5U$BUQgYBzz*^2NK^2rg!Gw}N?rVYvSP1Drr(zl}J` zAAAJ&u`;%D_+v}(tOWwCfnUq6+Ym1zz(?;z2inJ>cU;5T%f+^|z6&Arp0gv_JBy*k zWpk??nRS;D2rbssq+f6d_c(-_C4yK=kX^S>I}iU}_X5`M6J%i?b>!~`_l|i47Oyhr~?eut;=d%T4A=@R*a9ZWBTh8n?JQ(u3lkTM?9#|XY$ zuqW|xC)ej_*F$62b<;OQZVtDRljMfKm4z12*y(BuxcplepoFS+-W2z_^JLjwpf{1X zvitRXQ8h!P?;i=`Pc-{8RpMWu;IFtJf5SugI~nnhJDTp}7pZON37m8ceGO}=w15~H zx{m_?F4oF|T^~ht(cpR!Udr*a zI;&+VQ0>2ur>(k>w(WKy{*$`lU-0AK*o+Sl!hg|8P18kxh>o%dv$BK=c{J(H_IJm84ad#4KJK;V+xVtLgCLLV;&f#^Hgf~<+y!#3E0EPD;g}1u` zEIrw5*fU5uu!pDtcr)C~VLh@ZpH-O-m9|bn_5=j~P7W-r1Rf#aQEX(7LSbVS@;Y;8 zK-UVu_OgJ-3Gf5~o+Q9i6+ET4rwwc(olmS_12-1~Z&|{9z`@;B7S|wLlMwl0#dsjj~L(tDcX3wFSJzwDZ^2>yiLD%1!703Z_p08mQ<1QY-W2nYZUC6Q1700000 z000000000Q0001EZ*4DQVRmVCZ7*YHZ89%abZKlZV{Bn_bIrU5U{po^IR2S8^U7{E z$!3!UHU%P`6gp8tkt8G#NFxOZs9{MK2&9=pu@{s(J1Q!Qf>^L{9-dxxoe2bJgG)p94w) zj>UCK41+r;cTy5(D20@}45iq~R`YnBBV>;9YfN~e!QGT68Qeoa$p%lM+-oQc3_ge+ z2OCPc!H3YJkCIeFsnmIz6K3&rDrZnK)KE52K8%t~N>0)Fa3>V=5tNK{LM0zXc#o#G zF_erolnn+SM=QwEc{V~I8F`Kq77=PmJeT^6kK+^gL`o*-LI6jp|Tf|4v z(=>zU(I}@n4$BT$PPY>);*h&)17<{FEEs~PF~21bUxSM#ZFjD3|q@f z3_g$Y`7}VO!I1(B36nCNms3>*6)Fv0MfoB_xj^UDI7#J9K`w&N~g><%C;#x6XSAYCTQ1LFXHFj{LZjcR1m8evXr$ z%g=MdE`Gj~U%)Rk_(eLu*vT*9ml91j6KG+-U{5|v=i5<^`K{FRHo|R(&TlvP z9Zq;o`Mtq+8hn=%UL*NP;&)QA+u(N@{BE7!gY_f`ukm{gz6Ujn-$#Y}DS3b(A2j%0 zo&U%Q@A8Lq{$nS+Px(G4d`S7jPWYJe{Z9ClKVLlO+(t zKXx)VUq^#{V(?F${4@S5g8a3?f1~r?I@w^D!P0oG%A5Q*H{w1;IUk3lT&cD(*865t#!M~v{N2%~FP4*o< z9m7NbC5#e<67FQD3ZV;?a*YxldkO#&AtZ%JGK7cnWK5*B6e))C3guo($QX&il#neFJ``n)zuA!btNUm)eW_kl?^rJ1!ZLnGRjI9)s@bzDK4t3F0HL^sHrSk zh+`I2R#X%h)uOY2IfP0~pqh%g4Yj4^#g%mu5Ro6&KOjzDSxsPt3`X=1R#lAn&8=Kg zL7+tp#+(4Opt`oSpsb;!pthilz~}ahX?4+JJEq0`qCkKVD3lyOW?c=oS5($ElvU1~ zS3I|&R038p7!^HcaZOEWWktjMf{M9i#R#{!psaK*0WV@Ogji=)nM(o{{e%+AsWoBEUuA6 zC|+7xQ%h@jg@K!z%aJ-Y)r&)OUttX!tS>4nE~u!h3bkKpBTN-8zo5LZbY2~<69;9P z44iWV9i4$je|MmXfv>u!t-HB3u(-Lad1XtWpslUF+uz-c%7?@+TkBu%pU~n*r49E% zmCZESc>)rxkR%!!hO4M-D5@?-1`&g68cHhbD&`W@#Sx=PN)(h4*XnJexkA!1JqnJ- zs|iUY!dz+*rnsn~uA-){s;aWO76(8kFD|C`%NQ8tbEh@b)>lahVhkKL^9v@0Dij8B zSTT88UZ{$jRZ~uz8mbaz)wD^ILshEPadH?+LmMUsudXR*s9IQ5gO<)3J6JOyOf$&t{g;F(=L9UbkR$e($Ewm@fdV@;s3Gtgbovbw#qxqD43vNL(P zjh{8$ooI`Zm+>SfBzGoDjvYJpEmo4M6_pjBP8C$tQ{^!R3E|4QXs&2tKv4_V78Rof zC@C!}s72$H60Q&HUj%qnb?IURHS2BQsFs%aGH?-gL{h@TUc}|wi*u=%5GocI$%;5z z1?pO;G_Sa#xEe7sXQG9;BgQV8N_C!y35q49lP!V;>!whpHv&%+aF~6*#l;nKD^bOZ zOG+!yK3yaUmr_$(SzR!%80Eak)Ev1)R!D+lHI*Pi=96q$ ziRj{JMnR6SsJgzYwz8qT7@J5yDhrCMq#amNT~I}6IZ%zFfe>RmjJp=q$PN?TzLml7 z{@PKJ(B#Z5t(ZrU*P-L-7m|dR>=OZAkJ1?pIIpOjnzo=Vwl@*c8!FI52JvBeXwa#H z9Cb-)?fiyi#nqLj=_{L8i3E!!_bH(+6?WJm*3>bY;zK>mvR9%K4QB{spoi#MTvS1L z6K5++(XT7Zu_Xi5w_%crrUa@eiU1l$?PMfQ3_8OX07=VX7yYRf$-I z8d+XUlZS_~P?ZXG+%Ts^AbSFs5V|LT2_YOCCb6ipyc`P^1?9!oB8t#r)mGQrs**^T z%E+aM>r!iK$;zPqRaI9mF0Cjkwl#VytLGI|lrA$Bu)q2`j7!XVU22t_is%(GY~ecT zZ9RuSwgt9%htg*dyLZg3ub5HApb%9M2isU0U z&Y)4^Yr6f7>&pEd!Oq1S8v`9yu;FM5v;?{Xa-fvef$s7^SC@ZvU~cp3Kv#F|rVfM` zV@t=C#%QhZw_>XoTVpA@6T#>EyVjHi+R(~G_mzB8u`4NO(i(qTQ%j&|RqNyue{)Mu zCk{I`Vo{CQL|d2UwsL=)pEfY1$InS?^@fM4S%ZGLuxHgO+7Trp0$5qx+1cJ%8`#*5 zr0&9Hx2?x*!r0@7iZM7+z=7MT#&xtPiF+wc=vqyF9|JVA+8A59ywF^g10j|dE-$2> zVW?Jrqik9b1al(Tf*@SHoS~wt;*ktEbc~8V=$fA93^HQ!FEgFVs<5)KUNaL z_Ma4}IGhbT6t!b0EF~UMM5%CDD9U(K%`Foa2O7KE zJCQ#+&(^t&Et^-j`MaeQjXNH4=JC{-ExGH9K{w(cp*2aeu-k@1s(s|5_LdeL95rgj zG6FBrd5$jT=^QP}_}G9k(O4#BfL0sQ;P3zu9PeM_P0cGZ0dX@Ct2fDAoJCaZgUiW| zIY}7t;)gLx+pJ+psIXX8kQ#cV_&Cno)o0kN4qR~PejezDGn>l~xA`6u1` zgcuP6JcLt8b7xn#jSEA@#;`ccrPd^N1^k_jYY?mkhYw7aHj(~?<$ZxE@d=TTsE(0< z7MR;qjDl{QWo1uypo=tgLC~ly2O~S2owS&kt-2>rkt|d(DKx=GCQb z-2v&UV)#d$C!jbqcU84Fw{=&x74>wkM{&pu3fXa!64;D!%NUG}C5JJz5kd70o&FBV z#YrdNebj`OIH2tGw1xU#e1iT#4U15`fjk9v#Qkz;mmKmIF$qtz=zJ!FnI~FCzb(>+ z(~8`vJ5qVbMVkt8&Hw7cr^0Y>bJQWMfkV8=ak9 z{?K$a{Y+;!ncr(pb4HxPHsjqVm?Y#U{#Q74LRE@KtP5K#8CYs~hnT4ZtphsU=w3r;WNA|5i=3$~PkR6KqSsAfrO(kLrAi&OgU3YiOBO zo$c$Jn`n=jIyQQcU>kO>#Hc8^39JdUtfEaypecezcl|$4BMK8$T~z5}5lVMsX8^IY z#mEI?7xb$p;b8W}TI-fuP;-4U>XAF-1)-gEa&U_p0a9>Ma(klK3iV2kEF-a^(x!;A zc{XLk42QF#MFywD3@C%+T4w6BJ1PEo7hhn8M{`FC8|}{ zVH8G};Y#zkD;i3kZ6(Rd5Ec?c0t|x{Zjw6?8V+qlc*7ohM>U?wD)v2 z21=UgoWW6t+pzJ}$)#k`8^Ph>0ZMUayxQMV(777-j&0qcGb$8`;Cn*(%A~HWOc&KI zQ6p+`5K-q6i^URM6uHDwQSTDVlp2>NluOFVCrK{(G;Px4VC&?mF1C@O>`yFhTkmgaZp!Qmbaf%xnO)r| zcKMm-o}ZK1>hD~K#K~--1FXzu6HpgtxkQ6lf!kP@@Qal$(I}ea)+rLFqLT8-A!5*q zk&dU%YwynVXCjf2QkhN7UAX0G!_94GfX=sY&iu@BYtAzJoTw={TY+r@I70ArD%r*Y5HJSdP^UuVUv^IC4 zz&EbRTp8%z5D2uHAl;if0&=#M@^mk=tNEOOOPQk-xRgR=u1hIa=DEZwv4p|kXaOzk zX>LJ->=LWR8WjG_W<;!~rG@4S(2<)jOx(mQ9!hG6O9E5?+_GKv|C}USb|@Waeg8v}e*o393$$ODU(;;U&i( z#T^Ivkuue#R4Qn0r~WsGZzM14TuQZarc2qRoa0hvqCO~fs3Xd`%6W*xz~-8UXmN>F zvDPKpM7v9LQ13NLvrE~a;I4OgK}$2T_uTW5+x{;3Ao+v&Dp~_RxWw54{i8ejBnBA} zuA=kPngEinRcUjHF43(E3oO8 z({Ew!66c6>UE(~YhQUzcO6E$7`=|xoJ)P*NsT$Kz7niuirDC%#E^~>?#TK+Uv1~X4Hj#S%0C%_{VwPC6le3cY1&JD>^18qtv3^cZ{$M9uk z3pvxUMG{xyl2Mw@XOI?sZY8H*PdABBj4n_vLT5SP_Jl!Zf37?T;!-v%=ed-NDcMXD z4~aUrlJk;PK-q}Zw2sEOn0k6bCuFuhNs*Q7NRqB{iL1pmF6A->**K#AQ!(2x13@+U zyZyQ-bBSw7R2I0zbz&zsWz=t5}1a)HO49{ zSFWXl_$Il^+myFmVu!dr7?7Os%rA&d%y`cw?jUCFbctQ!PMUkSF79%PyTv`IUZW;X z-WW84axEqjy117jfjus9pSa(p{1Q#Rcz_6UowC&>9;Bxmlx?Uxk<(fMN2D_DCGW-k zSnC81=~av_vq!UP0X~n#T;B}XRhT6)7#T91rAVie*#v9PzpA+b8 z@9;N~VRk8ZC_61t*f^oFb5lom`-GzAjy0$T>{a%fOZ=F;(LV7ogVB*hsc5h1Xjc9i}@%K&!BkkO$TL_lu1XalrpP|uU(ejl>cY>B6 zSLNoSpki+Mypl;(!9~SUYp9fAhs7;MG<0P9w7e6x;q4U?UIYy(G_m& zo+Q1@v*Jl~0_2%=Q9(<4&ZRuByuctU?5BfAM7WJ?S%q-~9TH_W+E0nnB{7=YC(ypU zfVLu~Z5=&?OTgdi63>eljtkzR&$2>9)Wy$S;zjXNq-J+mr&htRB6PqtDweGcHrq@s zo9q%Vi=Vs1E8^ATdPSX1hQq$mCxo-9d9L*LOFG-p&!h1O2DWzU*(l}`uZdsi;+HP* zIyw!-r5NT7QIva90eOTvvfrs~zW+okv0FEn;-NVb!*j@+RW9)cMj1+qVz`t?m7lnj zLG;Q&Ol_A2{HRJzehdNIaEmM65*Vz!BT%BBQJ&SsBI@v-OS~^W zaET8okokxL#gAz-lVZ0@AxD9mN^BdA!?sYU6n)^9N!~cK5%=S>r2c+F+VYC>s!Mr~ z7VrTj?^8mPDX%NjDZD{ue@fx8Pcd{_!)IcHOL>d1`4zS)X=b&OVdd#oo+@*+IFhAx zZR^_FH?(Co(%DmHb5njM?bCma>p)M~-Auxj*@hmGcI|}OZ-}AQ$|tB*qRs@}^g)K<7Ncf;{R87UXGAK=!LobfdA9k_Ey2WYpD? z2;nL1CXJxI_OJoI3Biq5M8H+(Z?ZgsO|?x^+-8}d;O(m9XzM3!;;0y5a?Bk_BwWNj zvExRGwq+%LI!Qp~^eyl2@SQzSb5sf-Au-zE4lb$+s1@SS@1`W~{+G(Hy^?wN_ z@+O=&79d?6YwoHtuki(}3sB3<_a~+D6P(5X*$&Bhx)~60YJU8EIP-#1jd}P;Cw&fi z|IWPMg+rsT#CHX{?Gq;14qt;O5xU&1SFWOqLlO5Dg6GYsYa9HXZ3A9SGq*P8I(ypa zFt{0aD{@;r(9OPO)M-$x(`9+PrMb(x9H;p^I?ynpLpUi;SVw8QIsXckh&#$=gi zY~+PCdDTxHYu>LR8Iae&Xs6ZG6vg1+!k3vWsOd(*q6<7tft5Wt;h85zw*Rh1BG(*J z_(5ascd~+pfUc~p@po0oOOT>XW|35|MnnA3u(hub6tuL2Z^uR7ccXQx0aGKU|Cei% zS6$6B6TVs+)=1e4AvDB3LFjlu_SeuyuI^rAa>sgQVRr+YQ0QB6N%HWvvAqN3Co2Zq zN@G+>=6hqU9_NS)gzvgBn9v`U`n^i0(cMUsan?t7^199cXEJiqa@0uY?ovl==e9E5 z#8qoVL9!D1*w8&zvW&rZSb`iH2~FrOMWoA^Atyjz)J%%3%G0T^9!J4O-k1sc7m`ow zxV^x98`U2eNrXk&AwfZpevKAp-ATT7TO1`n$FnI?KIyWn+zrT^b8#m9su3<=-lHou zsi>0qZ;Cuqvu~nh97=XYo(*)-eX!%Ns{{|EB5qGGC_3Sb%99y`7UDb3LDrz=n%>d9 zQc|e4z1qLQ(!sQTFU8Sv6T;*oc)Ac-XxDtIBqdqZ-iZUmwlK?M2ylNF(hF9tYHkZG zwHNAxS9`5P7hFZ)>>hthSLy1u_Rc_&zbnv>3rEWr*WFI9Z#qpV*UIKLf9EFi=v}hr zq_oYv7}{ODvAYw48S}tXj9nHPBKXN;$D{AFLybMj7w+a|lhV+w>tKK(Zw3x{6(S-a zBX0C`G|4DRYqDar!O@Cm8iGzzQTZfaD|^jn+$pt5{DVF?!K@FSJ4>Tt-F0#Vo4QSw zmA5zbw2(Wc*Vg8qrFf%xrf%JPrmX`hP@>s0bW+V=#z`!-pXhRiymw&kEhDaMoNA4U z2Ftb!ANVo}I_Ibe)Os+5F&Mf^{TX+Hz;=j5()KQluhs|b@03#eQ9SdD3$vg6*t@f* zQGSwYj4ce3oh)V%y(c1tv@4UWq>sm=s?2*EMgBI7JsbTk<{hTsg{B^gA0mU1;Fgwx z5$ssoOz@rvkhvzE;>|(oYKu&ul(Eq;664~2ZSnDBi{?%!`kx`aaC0|R$j@r}y^do= zECl8>UwWCJ$^<`YFliDO6i9iW#2MI#(qx%MwXp@ImTZkBreVgV$NHcq9K5XL+JBOVMjGZSx<2@hC7lg|& z9EPw9ZS5Cf`C?o75-eY8D{sc~Ww!F=Sl(hQ{{YKBw3V;G@|CvoRam~-R=x(y*V@Y0 zVR@^qd_9(Lu$6Db@-|!fCM@4~{%NyKU!wQGArvi^<)PzjnLDi9Y2|TN?y~ZD zD^IZUL@Re&d6JcTtUTGuQ>@%;<%6tzu$2#yc`ChIz85I7%va3NxH5sZV2 zVIo`tQ{hr&B0>pJl5}N~e8Nx}(U}}V=PV1IaUpaPEOa8#NF+4eh(;KNR9qjxbvWo& zMBxVT!i{hWY=g0Ilbn_VONy9viZT_rob^JBUInQS5@lgF(M}Q!U<&@tAp%i5{+)_{ zh4?oe|4Q(0hPqeVtM4`Ty0yK0ZwQ;Apd;DG!BmzFc`O%BV-p~sO@f(h3Y^ZSNsJ88 zrZW7e#9*j{F4tP= z3Z*$Pk`=-jHW#v33FNZ*Fp({QDXc7r(ny8=#YE}U5XDXgC5aB9=1$q`&fM!x-|L>Z z*FEQWDlJ8nmLW=KAWCN;N-Gehm55T)NuYF^GTlUpoh})R4Hin@MWIAYZNtAi@b4Al zkWtUw*|qh;olsk^YPI#6T3fHXQ)~CR9kOi&wOx*F?)lg#s4-AmZy=;w(=pKz-ff8Q z39o)nc$S&oyba0QfkM{_?W_lOvW@U6T9ae!f)IIAC*Z#rv(g=$K*kYj9eTpnVUIzk0+Ds6MAfBjhxl+QY(MfL0hf6xoPk@` zwQLJ?;8yhx186ot#Avw0e0i|axZQvd)dwKB-;+p;P&wqcDp2`6SQyOjEs!r z2-9M}o-(>%j>LSdQ|*NH?ri!50&{%D3QbIJWJKpH^YpT@I0 z(cIhzv)O}C$o4`R+b0oJfuE+@aAOW-7TTB?dW9{smD6LjpvKKn3M?Zq7gs|XmYUiJ zBc6je%tof|B;?R;k0PTTMlOL?&VB}o>_rT%UJg2fL`Tdy3Y8)da*k?*A`Kp$n%)Pa z(+?RH=m_$KPoOJE&CczEF?p&_cva6>bOyO3`pia*H$i1@frGsb3G5w6WACCWyl11p z@hf-I=LZspVx`2wVJWs!Sdx?KMf#8SibF=eN@!?4jnEjD>%-!>Jl&^xbx#%|;!~qB z`5kENb8xZWLlXM~WU?<{9Q)${m=r7XBqsBfQj6k?h-R3N$$Ac)=n1p8!%(93LCC2; z0J-QN$9pDV(hCz~k^ess$G(Qq>}Zhuaj7xMzd%`NE&Vct5}41*@=WT3$=QcRkiSzb zr(B*xPC2^|rq0gE@Jz$SXY&b;8*hiW9QU`LJS=B~?Y#^Wlz7mT1k{W~7^Ea&Q0Re? zN-|_CDKJIx!VC-+O?^@ZeG4OXrHq}3PKa*#e}nF+gzjlk=uVG??r=nR1fn|<(H)KG zjzM(CBD&)c-7G{m_xqrWY@MuB$b3BIh<>F~WubpJ#NmGcD^Qd2XS!1k8Rcmx@%Wc7 zC{ZcVDA6e~#35(CBQ+=2Cw!_;^XWdrhq2L&JSX!yJu`7F2VquyWHTRs0A`1Ub27Ma zg)=}^W@3~+3lfyskc_H7M41B_sOZBLj1^EBb5JSfD8(>eDH&jOmC7Qys%oXiTGfqc zdC(7G-x++Samb0z%v*`hY*=Nz$}8(NtrC5iyRtl|BKL5f;CZT7^@`n)n(I}^=V@sy z)vK{SIDN&;%_`e->^Esq;R9a;3P&@+YhW0!g=xGF=3rjJmq@-00_7OSfEr`EQj7mH zj1j)VwVha`>O$BbvUsw9Blc)8XS004@>Q0vv3#8vV2~wn_#9*j($X`OFtz(&PM*{2 z+>Z9Zfeb0=gF>Id$A|HkE{FRO|CNY;BWSz{4898D_-aVtYaod?LkeFDKHdUDc`KaC zJ76Y18;W_iTw5lvV^EArjsJ8wX8o@$mMbm8g$}UFCCXCdJbCwHxW;6x`#qn@ag9n| zc#X`Gvpy)I)!Ygy-^=sjyv{rq&x=nIWryNS8YFn*ysq7FFA5pfR{9c3hJj2Q;7i$mT>8pnLWltM|t5 zMWS2-55uGAqfKVqNL>^mx7CLhD3fDc=+YuN$)kDsPl5BNkw?!WkDfywJ&*i(0r~SY7@{_D`!a>{da29pscXehG8vjd@no*mFR%0$`8UK%&RL< zfs{Oz=V^@7;#p1~)Z}Si+)!h(7kyA|zV6c>gE|J=VLCl6X7D5|$v4t&g#@4R6f8y1 zJ;y%E@)^BQ{}?P&P(b5zWvLgIFWGbK>8#wx;0y(p;D90G6oMOx^C4Q1kHF18hGhN; z4C0@n1^Enx^;eM1e+}dLZ&5gZ2aEaVP|yDWtNEYcZ2ngi(!axb{2!7~V?jLz<3ZOI zKMGF0uB>EaIFEv=D~(6V%{6@ugHd?;5u892$yO<=t!>(aRxowgB3T)qorUtp&?VQ* z&e#qfl7lldkoQ@S!C3+@rfJv?ag?cd!ysy1K?Nxse%#dWqmtMKY1zCNRuT;{IyC9W z=y4wp^1`dByJ1AOSCep!!~qHC+$@+^%TqUNXdXWF3LMOv?Nx6DUF<lCW@UfKtgOWV5cs#qkz?Q#3{r#wLj;F0LWMk` z!91ZusW4!XaKLimghmkut3*6B3pccgBxn~N*eH_WQjr2z3NKtE2Ep}McZ*1aI|R-q zhQfZ436F|XBpK{OJOh^CIST2LQ_6Q>AOnY?s#jvuG2#vCfP_N6Dr7aZW5`7j ztOaEIvY1DSYm{c|n5mYw6l8R>p2s+QAI75rvEqA`c94DpLJ4aEa+STRx^YZWm6^OCXtT z%kn0m$Yuo1TYN9HV{YzdI}Gq9;I1z14v6z6q}^@0*hFtao}1?-nJzYt%&Etl=ymUg zr+hBc#d^rN;--Vb4uk9}1|M-g7g^UNpU0aNGOTCghC$ET1FEMpgo4XvVO>E-oJfx= z$OY%=JTKXsOy%8BhGsfhdg2u6iBrfEdu651+Xr3wgM5RYM}q|!d%%-F7^n0NehhjP z*aLCs{Rfe!-kgl;_3)l!v%N{^ec3QJk>x?RB^r;TwE-~?xR{Tku>c%mAu_BCJfZ@; zq7oUm2*sotnO6f7MIFo(OQA&6qslBpsjG*zVmWLQXTZ6l0WJ_L;1c17%S0nwC7NKX z2%x&Gf}LUw91?5cS+Ne@6)o^9%s&^c@E6ere-mfJ*P;`?6Ftl+Hn0@2kqr~)vTSi4 zn<&m_Q^X}KPh7}O6Bn^UaWN|vm$Gt%T`Vpq(Pcv+@fdE>6QpO6UWFX-ao(jJAnqv4 zWQ?43@<6~Lw|xxeoIEsYa|gnbI-hhOZuZfYv(t~_9{oF*B%wOddK|-mYS2lJV?uP2 z@#s2A#Ax~o^b^Dv-BE6$2i=4jUwn?x89_nU~AlLEr1gJh73#4babt&AG~X)(O-~Yb!Q6DHCQ1 z+KQ!DR^;;Ww813%qWV-M@=%}1?Su7snosp=o(*)KA)-wyIzaIxn$@QuS^N|o+S4#v zJcAT^F5-OYT;=?JaJoRb(BgDHntj@{4+`$iQ_+-fq}}-@>wMpw*F#X^9h~=FND%KK zcitahD|&%)k(}#dgD58(MG$wgABXdmh z1W`TTLbdofR4+Esxg-{(f$wle8%nS=IM{K2Vmq%1@A;S^IZaCQ) zH50Kr1+g0eN$N=OsiR?-ItIq7V-dq~1IXEB$`*;&50oETam%Nc_3B26`avK3ur(<#eZrO($k6Y_Kx8)pw9Y+vP16E^Dy4{judB~$;Fr=OaL)7U= z`+OL!&V+1rHcV6Jz#O#z7OI7?Se*-N)Dmb`=SfLV1La#7tt(gP%9Y2UfE>$JSR%W1 zbxfhZM!D9C@`qzBotoVLeF$JKbt@6TT~99E4Nx0#2nI_E*yReuoftf%nk0PH3UcUK?WeE{w`o@zVb_7K&iM^LMgYPCqU zI;7fSq*^^vZ5dK+Ia2KmSfHK-i_``n7!9Q|Z~6)@zhc%y-25g0HL}rdQVcJ&*LlqqkZOKd~CJqam{& zgI)%n0}^(=RqB(ae5+)RlVg=0lck|n>2cc}2ZKY(y`))pC|tKB2=_!7NiA4+I9QjB zJ51&|(g#nHQ0}Wok$j3o@Bhv z>P^tC-V7J3x4<>(ZLm$<0lU?maKE|>deyt&3EV-ysNM%}srSR%>I0J7iC`oRaeo8V z$;rtJ=}0JOh4=O2*=|v`Ta4I&&VpVr;s!}Qd0u27Lf#B*XB4S&o`b157DGC*Q^sFjsvZYSkBDmHIR2P+x>i>Pv9B`U>2vzKUk#HF!XM9S*8*z_aR` z@QV5tysy3sU#K6!SL!G5jru8kt9~YVm<*1ikfJNM4jDAnHDY37Wsr{sD#|{1E+!|P z^fL+8%iSjHko(10Y&=d{X#;G%|5AC98!9oQm_T>C>8(V1lKZ-y;KXd}F3^+Q*Uyr# z$tbu&qu}uAJ~iDZGU$B#=XnmF=5=^pL6?m1TXsPXjpcT)z=51To%@`eD*9lx&%lD& zNG0b!gui|lvPL%nHt!2PVNYk++$}?M1L_}8X}&<7~347J5 zpyLP6L3j<_!Y?eB`OC1J3OX_{m0>$1kZL%1o@)=Zn|cwf)2ME_sSw*Ck<`QK9!8ZV zrgDTTy;M0VtSGky8{E_|L@LeerU%MF@AC#Jk5`qV5Jx%6f`J^%lY$dZ=O2dGl`Rpb zFn{-vGH9NKJ_pcSye zS`izn&1GY>Vm3=FVe_s2x}Zq*qjWETMfmh$$0e-VFJsrTYCeX+M6&;dkZFO zZ^I1j9hCm}pj>+&sZU= zVU|7z>hwZMF%3k5D=lL{fxI_Fq{mSpiAd)T7){~A{uuOGC~xWgtV{~liPuKlp!Fd? zc!bc%J-l84gY-&VUKQl%)i6n~fdzUUEYcT4v%WNV1u7?F=zzlDQ1&SIS&A2joybt# zb^?C69kX+S?)gu_c-Hf0Nex$y=Pyzj94Z-@@*K}!Wzp%@s5~pj^Eb0u7RTp!{%#g^ zDh|u>{KG67Ve9m#kP>Bh1&d&H35q`GO~~p1vU)WP)tk{euZ0QvIw;UvV4mIzReBpN z(c58#-T|xhv!Pw@gdV*MF3`JSv%Vg->YF63Jm5?~Yn9BB)Wn2@gp;yY_bU%rWE+p- zLEG1Z-$y*$4mxv;rX-pZla%rJ6r!b$ei_SQVq(P{tO!vP%M-C2A}5wJu^ghOemp&; zQPR9RixRH|NDR;~L-D#C#cK|4%% zhU|Ln|5L(vC+PZa3>EH%A^N?j{1_(b_rVPPeiXFnF^dF%>K7@+;V-(1J zuu*>)1@aNNQtu5?EQP|0*s_S!*^82Ao^k)nlDzAX68~?ClXP+VAyD7SUr#N)C6!6B?u|;;rXDa*p2}3DW56aDb)C0E-6T6j->Myuxj?tmuQCVS>qf zQu;rT?EeI}{v~AS|3)*k}$otI(gRKUK9fk-7UK#XS zYe@drpq!K;`*A4nnt|fv#Jbm!*(UWK_@4$|TPW~)4&rFN-<#47pMxT%Fs1dAq;x3a z^6E(77!qkr2B$Fvh8a^~vM~*27V=2`wilR;Nf6q!$K)%Gb5Tc?SaTAzO8n70W$o<8`dY!4)s<1%xCFYa)A_-%GC zd{>^sH7O!=1ILBfAGKDJMa(j4Kp3^48B4)soB@eOJ-Cf!;4_v(nsFv%8fU>Mqd{UF zk6W+#y0ZT$U95`rb(NET>CHm17S)JQ9LdU&TgU3TD$}e!1~v5}yN|J&KBnLwFSos5 zknxxC15k`B5S6Q7ka0Dn8dt(lEDtxf#w1Ixa=@Ci1f`UW{m`t(m|#$r^(1^)p3}?J zitOAzrqRy7kLlsrR~jgrWKn0?cJOataqmW|!1Tu_XE3jlWNM4-mape;0 z<-koy5R^Cg0N3(y8Imoo+pr2hSXWOxlJH3eRkx$Q!b@f1?*Y1Fr8z+*fM zrx?${XybX9WV`@Vj2B_H@e-68FGH>Ib7(MLfwjh~&||y?7a6~VEynApgKxqv<1Kj5 zcpLU({*>_!Jd1TN8t=nv#s}~V<3o7Q_{ch+QjQ@Csyv-KD9@%2>B1Dos z`3Aj(H^LMmea&a7kPXviZo77Ed;x~>N7UIrqoVu;#u60sRpJM6TLCxK_Sz&vQeQ}Uw)E{6&kAy6!R67R)k3#{UgF_lVpW@IYjtRiO zqtn6X7)$yJ)Zp7hmVuK$ue@NYAioy1VlX223vP-Ry~|2w!wx`8AIn53J0;(I;g$Uu z8_od5?Eti5OYl}w_ET)ccF4|pijBM#21_oCLNy-U%f`shW!S#`Y;3+WnT^Yj+XE#C zo@Ih(_Y0mw;JE~D0c1x2SX$yVRiuxNm#W}$YqEYnn}F71BDU;jlXB&gx}QxZYlV;6 zem2FY%13=ao9ff#qp_b&BUzd#M~d%bd8AoxP3dK)`4W2B^!!9$0vdo|$!(#K*vIlo zOSHXXbf#UlHCz?jX2rH$vF)T{S8OL0CwFXAY*lRAwr$(^Qcs`mw{gzXJ>K_x`H^w` zz2{hat-02md#}|v#d+dbu}8dmD*=p|ldmWqw5d2O_1gC&oBlUDa~}CCM&X5PHSG#QDE^E@sR2h2&fff!@^**3$J0byj<-K38}*Mw=+ z^&-znTNI=V{FR(qpJ0pukPCyXqm+M0#2|ey1Lzgbv~B_s{2r%FKySpfZV?gwn8V1A z9|oi~?8f|w2CU!q*m3LFp*AQ*304l0pzk>Wo4oxV3;W~&+WkN-I2n=bLmR@P94(uL zKO%ACFTh1nJr31+I^;+FbHX@qYN#QTiEKyDL-EBF61; zO{y40fw}rC1JlmchU?hpDob!_PDDhhn)@dQ&+Kce!Eta;O#IZQfvA5edqrzqPoFbxMwgK@doG-L^<;;JM$#< zDT@PKO|KWvC7qH*{gFVqOH{4#{fk4+8oK;S-yR<1J(ubol}MC%_bYG0D{tqJeZ+Zz zWRx?>fICE0E??9=$pAP6V=Z5lEXjZgL<|aK4EAk)uPr8d1dUC~b`X8PEodB9nLlU0 za>bM_tOI*lP+&AEPSm zOS`MGHVEe^$S%o<=w#4PpEiky`pS*^`PRsJw`qS6{oEykA%_<#Hv*1$;*h+kN`h_?mHR{l$|m z28UU?gMiEIGA0g83>i>`RGdIg(6BC#d__M{8Qh~(${8*L=vB?3Wwfbol1-b;*V6pN zSzgHg?6Hq`#Ti6hL|x~x^l&}x16}2J?2ZXvY{@lXEtS!JYR31@sgQiPjlmvc9JkE)I_Y7zCt85P6{vw-@)=4&+Qd{dl29#-j(MVPct*9J>$PA41$MGptCf1GO1lQah)_{}8o^}1NKVzJ(U~>nHUw2Rg*rMfnVEWFYlL(0e?yLB)^R8)ZO2vp9)dqf=|29!11KM z5to`aC(i)$E)p2%%^GbWN(OyeC>GVNSa$LKRkokrFE16lh^5qLI2XpC$*391|M4$K zUX#&0tAZxERzJ|uk|nZZdd~Su9dUhdI`-_1t+fwOFt6sjrVn{N{3@#Ue1Bq~n-Ouo z*pC~OHulz*_HCi)`q>H4FJoZYZehc4H(3c?fY)7FIqWuz&ay={!%OvG0A%;IK4N3F zvXca;9090WfRFd<%n8TmHu;T|M@@*xire+-$Usk4k8$3bI2>uGqHfxzm)0QBg_;3M zwpr=m;+(SBoFg#^QsG&SN-GH^+)K|RZ-O`$t5<(1g;Cnb3Z`~^w!~C1#)SKMF{%=H zovsA7lns@YLpVPH1Cr8@@6QIP9u}o~6nz>h-)pLrMYxuYQ%BPonif<=B4Z3Jq@b+;!3phouMfTafqGPGMO{N}RkKR9a&w)dz zk&dr$3x4{a#q`+ZjDm zSi8EKT1Zt=B|S-LZmy8twu!fvs#asZVM}6s)x{?~iLnt0I~mV?55Baq<5w4;gO1v0 z`zVn$kxr55&ZCmpCKwEb^maY5$gVU)^3fS_TEf=&ebjY4aoYqv2Q<4IImu=kR;O^=3B-*4a4-TH3epNyHP|-1zkwm69*XS$_}MU@|8Id|VPO0_ zV7Q>Vz9M*HBO`CuS5zQ@g!Xi0nv!KhWM{HY-W+4;^|By@-B%$|bN>iJ>Q^Eh>-g!4 zAL!b42LFjiNovRPfINvjg`B|<{EB#7FfeQz4CaY=DF9|SfIFZ>$g2F)ew`Lv252N7gu;MotK6H%l-oQmh z?AMLhnLaR{ZxkYyUnEVD%wxt(HE2p0oA{gaAwwquMGh2-g(V`KVDGSh1Cz|Ei}UY+ z!TX;g=5LS$s60C(_oKeeCA3~uvFbvA(DQH93^1zo5Y+Vg_o31GypS_uZ0C--u_U19 z##8=+37nX!k)C1|$9J7(mR_nMwp;>3;tR%3t=Yd2d=Gddc+D!{c{L`sP)8fAkrUq*E8N06^lS{xMm&Ox>h#kVP2^)q*gepF!WOD){>Y1`QjV~&?Ld|7PMPI!kG zx&Fu(m%guG$I@@NIT}yWVKmi;(-HJ299D8q?$e?B<0KLTVOv*C zM79gSz@B8`@OYWsO6$28vA9+W_(Q*&h8eE3m^7}LQ7I$Tc665b<5Pu2Nz!rfqj_8 zex`o+sQqzi$Dw7gf*uJgCG<%&VBUO@L8b)~wziXXZIYZ;*tII~O!p*}#Vp{ui+19y z=mL^qR+(VPBmnoU&Asw7l}TOznVl`5Id`&eGqi5N4Nj{-PZ{wwcK}*8O$@Y}oGV^h zEj_C4UaSZY=a6D;NeZgmebCm}YjFl$Ny3Y=7hrY0j~?XcM7fP*?^*H7)Nx3E;JJSc za@m0U1^h&5uvdgzabR&Pm}qqoMV>#`nsbL1If&krkoKouD$6Ct-OrDa_gHYTA5daB z2ycV`d8ljF-$kLE*oM7?y2-+GD*3b^zOoILXR);;H&?iT0Tw2e2W`#|_bHZ12F z+okBsU=QZa&jXRj5MIa^pAf!nI;^^c)0+P2w9SF&wK3uHpAS^LGZn6bO|hW!_EXtX z+fg$MmAQ!0c|ubXj{7kIH8!on(lH(ZNE~}1jNu>ac@Q1Re#OPAiAPn@U_+m|ve|dS z8lKaFwG@>eL1HJT~gG)!X+MCA{KSBHNEG3E2kcuG9jf9ZG!*s2d&%_sZ&k0xAPAFFX>v-BNO;7JG8Sc4Jkt&gX_ zeb5NI=NP;kU}C~KVbXSnhF|wq&n;!NPHprc1OzFYgtsMyY(|B z2=Qe8VnacVFy_h=lTLd;!cQoB1QXUM_i3bO|A7m)W~BKWzyueTh0(i|2D#`tlmCDkajyDTxMTNZp+?f|CPIlR=P%<_J0%`AFcQyfv{ zM{6(Ssj0rqfh>v8+HvxgP+Jl)AqCa=W|I*nKV+kUV?glyZUV1e6=P7`$xMZr5qI*e ztgR5RX|X5okukJ3!>o0w-K>6$rp{wvD8z_o*}$a_u7QN^i|^#NR48TRv*mC#S@2iZ z#BMm59Y%D+Hr2)ZHcGN$g{_8dOjxcGYE|3U!`*u&Fto?A^brlRG--~y?f5~V*(v$U z6SVc`DPV5O6?fEF${!Apzz(HK-0HFss-a>+wwkCQ-%F-6$d!L`s$eTqHqz^oJS>Gq z#~207PUw^hDCFFRs;!zgCg?i}R>Bos63u$N=dFKP7^6$}d=Z;|tnDG}o+R}Ee$L7Y z-NwS%;4=@#VgfFFt5g`@%6tk=VY3@2z%h3BucjS(9QAd~Qa8=Qobj(4$z+{jgk6UP zu;3Uqe|EGWy0Fd3(8O-6O{U_-5v&2Zp_{RXV`r0}|91Y2@?=AE`C@MxyzVO>nl;g{ z#sbWge_OZC&oqx^GBM-je%e`LuhxC2LCIWiUttAz>|RbZAzHXvUy&6UV!3^BroPfX z!4^4cS7W3%A5_y4FM`wWS?ukLS0ul8mgCb=&(0^_p0;&_;8Z1Z)~#N~H;7Bb-r2)7 zz$3e{{DLivgtpt^_V5sfJ-<3=K-;P^vy zo_v;zB*UfoEYlW`rh8P=sOLOc$IG0C)$xI;X?79#L?Jz7nwTHCrmR#ln z7;E%}{3eViOQrg-X|8dNkHd5z;@Xbo6OgR&>4?v9FJihN8CV>N&G|zg=YFHFclTu97y>&!^B0W1Fog;Q;@_DYeYFaM=_wY zG957X6LWd3)j7roLg-xzO_K@vEkcu1n$)16GJ{*MjGYzI4B}N-N3Fre=5Ovl?t!!k z+qLtVbh1ty%h~`K+S@eiauF6^N84>vg*1dU$CXH?(j^F6#g)BO5|o@Tm=6n{CKAj} z)a?SYa;>k2&gB>tP@l3S4xATxwQ+xc+(W$bl3XRDaNz3F$-i757?$E|7k+IoTG-ld zh=sw@+8J`Hl`vsGPwOVa&pH$76-gdWSIVt@`(y98IGj}QF!-lW(Qtppm;UL&+kYUE z41c2rmNL@NpY;&DjMbax=JR>j8_2v|TL+pLAq~I^i#J3{OHB z6~{-e8Cb^WUq5F;&kKz4W4S_uLJJGz7epMbkmX{%r9|P0EJw+u6dSNd-jZ_D^zL;4 zs)M&;VMLDvhXifqE49PGPm`m5wKhQbk`dt!^g!&$IQ!13<6hCr6DqZH(->aZ%ND-W zA`e?gk-lNEHT0r5dYUCl8K%9Z?JIPTF>)>fq@}|eRcw~q6I?(!*c@Db^_xS0b~6+(lUa?B?4v) zJ4!DChPM9_rz8UAl`+zy)c&zfY9>3XCl@CZLe<90Ox~_olQfuUD^)E)Jq7z@)6_z5 z>t|AFV!T=c1d7bq_^d3LcGv6<-Mt>s%`=>F(`j7nD{;%tB{;MxjP~?|WRQ%}9KAMof zt;+dJj}{QsDbf8EA@E&#iFnSmM1?0fJAElrBy>#(0+c~c=-X|2zPXwil2|NnJi6>> zc-|lIBx8Xpy?$q#8H*X##!h#y+qbYC6s*#v%(}YDgDEj}A&$t{Fnx_uhF+bS&Q0(z zwO*V^Frz+FJ&uU*!cOQg{X1&N(eP6P_=wY&sNaQk;AWp-VAIjm$!YVpoS!?D&@O52 zt=RWf*A~SihshzY2$4l)JDGJ&gUn6>QTGBPZO2>3cNYR7m;16~NT```-ZM$B;m+_^ zF;m|q_h|3r3eM$3!E+YT4uMwhvgE-qB9sN(v%=7bEu-LZ(Al}dEQ0Df1=!LJ|M6Ee}P?CP)L#g19*mig8hFrYaaiRHII)@kWMf@ zQYS`ga(begf<|2N@ANq^@FgZSqa*`HB_|^_!8{jVBPO}KyJM(hd>}KFSQTcvAaeMt z2$;0*5#o1sW^R^l3>3^HA6}5pB3}%Rbb(_|zNRAqBS-aibmaForTLfF7h4`jr(K82yj*$){EtIr$NWjDL#Qf0`sdip)QO_CF+v8gO_*e_sr8!ZJ76 zX_iuE$XxM^q0oXK;P@RuNG5_MTm1JI<0s>6OkB@zmrr1sLDI-nuo!B}Rr$G=`l~%) zezvq9u}c3(tp3V~01>qRf^4hRBlr=k-3%OaLOXuUSE4-*9swG^W0hCX?{}<97Co$b z(Z>sW47XyBJ;pn`&29fBS|=6hVKJ8Uu$uJ(8aE9*w$}FTEV}(I4de%r5-=mY2f*P9k1BSHOvWB?uWAA7 zlv&=_BD1{&33bVp%P8@h&Jr`}-Z{C_04$A+-_eQ(zjyZUlLXDbPZEF4*?-B);VO%o z$f_vs;p(I?IfMYLXvPNJPb?oR%4n6*STMdq0)n+j2NA#oNymV)Yipm|mUoX8@t-_B zH#_gl<5fyd&EJ#XfM36RJ0lrtrJ0^7NZXml#GFJfC97@ zBi>e{FA0GwXM=;gd;_$FJeXEYBPy(6bco(COf*U{Q%|5m3Nsc-z?CX-Kq3kWsmp!T z_wXzcA-Faz4&kJ(D$u`IyjmB=MbWGqZ*JPv&bQLv`#h-;s8-)V%hEs#2`&E{kA0mE z-IIRJS$lF^=1uxIhVXn16kKls>@rS9{1?T@l$&>I;V^-~a@@%@NWc!veIbAJo|-13 zfpjm^Q_Z1t?>JWVrIj~V9H*EM4-z4YbOOSJnSL-kYTr@6S)x*W-0mXgb`-^zUxf?O zh;e6bb(O9P8FM71uBpKN7WNv>u7El0=e#rRwMY_>DK_+H7?|U%tBWqZmw;fGGhaZci+C?d z3pIhO7}-+ko`wbmbc(&II1{w+_^h#mXtRsB!^6h#r`0nR2FoU<7?sV z$sc=TXt0(mRV+&rlvW`Jo_YE_8YC7jKoLhNh;nNF#|DG$+7A1U{fpNKf(~-#)R*rd zs0ps^@wZoaT&5()OXw+6t069l$&x7Fh)fnr4~-(XBn!oVT1}Dfd}CFufdcHOxPDx? z>SXW4)tstp5tyU0qwYb1oRnelcnH=W0O0BQ-By<16-&6+9cXZ9(#Zsu)#38&jt8Wu z>2#f6_}er>&0Y9gjs_!c3YKJb4PJ>A(v*ZfS_g5Yc-(8oB&3BN3-d7P^?*+2qLwX_ zZHw#p4-f;f9=ncX;3QV0B2zs^fy}17cQY2Ogjpevo_z(TL>FbEB98V_6<%TzK5>UTqxxqzk+ptmQTs6nC*(Mke`N=q;2?day<;l zNLcoerU#6n`XlT$mAqmJykeIVc);3e8RA7ZiFtWXkBXT@jils9nA+eC4cx1(U^abr zWX5WW2&m8LEU9S+(A8gJw|01&!M+0BO`K^n>*8C(U$p&10zWiEPv?hcRn$jCTOnW~ zCZaV62rD3={j#VaX_1+T^bY6S)6lw8q9iICa5~&I?<0Nx+(fj1kXxU%A)UvB0}Gy= zrRj%KWCmds7MQ*1N{vO}Y~TOueByx!ZGPTyC8Zy!bL$HF$PZ#fSkMQ_z^tD;VbpO* z{t*i+VWZp{5o+YI!szqQw;p<59EOaphW3OC=d1{3{u56rE2NUiNHiF^C-!aL92li&>-TJ#!v0q%%d5uC`E z=fyR&Cs-;GV3MUbWR)=@&Pe4V`TQydmdM!^(AQzO^c0Z&?|HgMAG{b@F3@b4aLiNA zV`4b+CgIqg1(xp?Jf*#}3wuK3ShLJv)Uxv#ceG1`fly_UY3&)Sq?O{}DO;^Mc>^yDJa_ZB_HL{OW%3MSwimXREXDo>{9q zfz*fjQ1Nz)VxtrMBCBU{q8MTe?=AWD(b(8zolkrH(@kZ$!LWazB?Xs)2B(wb34^IB zA>t#8-x7zXej)4>gNZ4OaMwA&rKdVbmzktL>Cz+V<9&>z2r_x1C%eKq1NzsR0Rb-} z;&nU_d-=(BAel5y=Y~e?AqL)ywdeNx=hwCS*RzYA zXLvjio2Ddr2=F$`U7J|n^MOZWDyfIEm9}1hrtMGPWtxU=fYw9$$FZg`YqRdvTA(pI zQ~B6E>b^UeE>NRa&b>yadOtY(s<2SuiwG@(8m3+`1^~avjH_D*U@D|Qg6Gx*QL~eS zMrdACpvJQs_7b@`K;d=6=Vo@>xqS0_le;>t+<aEsyUF|}XYOE*C?pmEnW!r4>T%ZgX6mX(eei(pqB?cj>dzGKo3LU)ezc z8UmfBBiLcT?!mTFLIT~jbenRjnZg%OZyJ6!Y0M2wD+48XS=Y3ilS}#c`f2pEyB^?J ziiuLt&(`*G9n^xi2<2KMN@Vq~qu=yxAu>u0^XlkniX*W|#6VI6@SbP#@pi!asMG40 zcBC&E}6)=k32wo~mtKf!E$Wbdb_a?XIG1$V3w#Lzp9$8={*Q4+D{8B);XEDg^Xh zD^jM>X&E)Plnx+YKh-t0>b=5&(jCLyLg7kumH_2xM0sU%xL&$#Q_L>bmxWtHJ$PPO zQB=72cIWVwkp}T@;otPt5c{R*s-d6l<|_$Quf#*yA03e&PU~Bp2aM^m%Y>ju_b92J z&#=%wA|)mrJ9uJO#mHSBBXO!e*JRG5SmA+sBa$;E)h=jQ_i$zQ=5kRNOI|Z^0s$Mk zF3I%g)1=Uej6i8`-~Q5I&weDYQO&Gk!V7x@uep6I%=m5r1t!Q)uD5+?Mo8+CP(WW} zq^rgb8Tj-K0Z6iH77Ny#*IhA_uBZIXCr%8$-f_emm3+K4`V;!htVzgFJRwolm*QSi zW>D|#CRrUx>q0)`!e3Fm%?jEv$lDy)Q%kTM4vceT%ds=i$Ejqbbqt9YYIfJ2K5}xT z=}qE}eDUS7Eq0c~%m?T3fj{GOdUqD>K)p@gG&i9!nlj(7;7Z6)A|GmIy)c)itAHFr zQ1Y^otx#3F$H_U3^A###(Tf|G`apvL)4{n}{*+h#A;4ge)vj90RZDwvrR}Wpc;8z)*Or8y%2)G+YnF7m z5#%lzQoB=$&rM9{thNoz>DJ1iss~JT3$wqH11bu$5fY0QB0`lOCUT&tA`o;5>GG~O zV=s#J0lCXEoW%(xvcxJpBVx!GAFF^bZP-!t&cxz>z?^>E6>zN)Q}Qnc5XzV812-wT zum>Z_5BX*kshHSIU|-zF5J{KsBC~i3nuxk0Zw7+m zr^iWJ4KyWIZjsYt?jV#4**j?D2|lSehV*2)>g1z{A^tL%9eF}3QX&H_>W*O|Qo2yq zc1Nv6kv3_fzYZFU2V~Y4ao0v1)U{rtCs-F54x{o9!tI-gEjfNT2V;^DnwOTNrysn<4r6#*C-zTw;?Qnvg1b@voTn zQ-|K;F)%il*QW>m@HP%HsIA1QR$&lv+;%4T* zmzDG)teAXSIrcf(;)Xu;2p^&v^R|ELu4cP-kWo1>)`vNo8M9l|ynqyO!1pXt#zi~u zo|I)&#lcNQn-R?|5=-)l5MRAF@a}-0>s$@s;O?ibl^lSQtaO4I8@PJug_yE`1%B}T z?FW@mZK=mMuKu31#kr?=>g7Jg<5#Dc)c^1tu5IJ#hQLd2DG~oAk`p>?Akwoc&m~ki*^l; z9!f=ccc{DU5Z161I3a70H7f&VSm@{5LX)BDyuRYa`u_0!49X7yn$;D@I<|w+R}eN& z^F!R*IqR1m{*Z`#`mZPiYGPTFh%cO&*EQlv28IzP^!*QMx^?o?G5HI|yt;?1%8tn+ zM1=zh^!|k3;`GE>-2?smGv)X@zOd-nB!OkKB&6($jzZ+qjh54|`^QqTuLeH7CQWLw zR&Nj}IelzXb8Q+$-D6O4ZMvqq+T{*BlAj5aTnqeeH@;Q@ZU3n3fsto7#kN1;(xhje zr+k03wouUCGjxfR-56&|M>I6d0H2X%Tb4`b9YOlSE$x7+<5g=#j=FchfwEg5CK7UeN(khTdcWOQcrQ z-`gDsE|&;i6c{K!6B%npS&WfD(wBPEqTVD?zI_HK)ql&~=#%b9gr;8rm;Pm}fX}F- z4I_hf!-53ok&h#k7)O&8r~X&83zkzeNmw6B*mrF?{Sk4D=|kL((UsPKV@+2(Y~$@a zVt?o=0t;C)!=TSu$_+?f@-zjA%uU$%LHeA!1OP;8r_-($f1i-pYlwufjqgyHld5pG z3z1-zw%eMVy-33+n@}U;qK??aWXpIMv}cf(MF-xEPu-^kP4oO+Zb>0$2t+rCdtsuE zVgU&LLS4Vq)oAm^Qyi&lS7Mv0Px>iVbJ!iZhwszH4qxn^>R+zaw@;uv-zD6h{c~Fk z#N3Y9V`z;=cG9A9O%!6`!Ku3|0Tt=V%gufV4{K{V4d_rBFE{8Tfh)iL)*zjvpFO@ZH7x|Dt@8>={ z@A%CHkWcHtw>Kn}O{2XFRflDi?r%yn*&CAQ*$D87>Op(I{6ThL7o`_bq*w z8Xmna<51l((+6Bs7Oq6>%X9Q2ShMV5dB_~Bh(^{|G4%&{q7d7baoe~#PhQH254Fr8 zdvQIK6qWK4-%_dME68@ocLnqWD(Q2+?O-F76V1+FhBFUip=pRidom9TWV7c5#^P-QBEJE7hkGXeLh$&}8V_@0}Z+~x` ztj|*Zl<&1!uLfdBfovLk>p5Er|A&kKJNhv>g#PrY6XVZ1HM;+>WgwQeHZ=Onj4)nN z&w5H1!Ao1ilwuXk;wW(XP{>km%5y??qvvCF3@g&GSTMd+QM>~M2}Rs`9<{AV&Z3ET z+~t~;;@+KJi}ab@J+JflYU{jCd;2TM1ba%Oq&&K3{=EEYSl- z#&77vD3~$E+qZAet#c)uehq9d8}x;+>(0I@CgCDu!C4D6LBQR0q*m-h zwF7$T%dU8u#$t{~*4WLf)nN*4*Hx}^Y?yUxqw%B+l`T=#iOimDVwGqtl`{^WCVV(I zshO6y+*e}qLvx~m=o;4(DQbZxkDlE);AQJ{$Jg6GSO!ap6t%pc;Mr&wgFqvwHypdHPf&_l-|v>zd?(a}eX~YoAt30kL(Vn3u#lfm3yi4a_6N z;DB7ufLO106p55sTGJK^#WlojSmSpGc3{wkS#NU8tSp#HClCg=Ysniv`){SUH9-oMBuk$;g*mc@R{ zCY%U=C!4hWy==nx7ulqYq@(T+-Q+WP)DDmYJ^jHa)FYt3m!0L9hT&#DKw+A zum6PGf2*62ivZk^2vB;yBHGTnrCx_B86SIqkVOB#NVEq(aQN5>rrFk<|$F+5ui8hCLAXEy62Xy*J z7H)newn`Eu*gNe->U2^k+U|O3XE^e5yW-heD za*zn7F1rZiX#k@}R1H6`%~$0h8d)CuzVoPZ~v|9RRKQWs>RKAag-cBFc zB$jduvu+`O4^lwD63qU+Zt@?7?7vR1{{IYF3%&pKko}v|*Y@X1pJZIf#`H(Tvi@nr z(*OBE|1UehKM77s>x$UQ7(8smixpZ>vkKybprH~-0`d)Rc1l9hQ zYCkO)2WinVo@Cj#bFAAg}C5 z{qoW-L082_HE5NmwSo+e=}^l$hWR#a>pW6t68>pX6etl;t3i~ZX{p2pEn>i;I)3@d z*5?9uxpKAiE4{qL*#t_HdqRiHqEjhNdA0fN@t&2Yn?`~r1Aank`aag({Y@Bg3`~KB zOsp#F2V+}Ay1yE9cb$ScNf4w7G-t(E8cCP!@<%NtAE0N@rslQK;F-_Y(B`gqx_cG; z900TP5V$Wgf6VraMN=dabp&|%cqZ)IEIo|f&$9XYGH-LdBrH-};V-i%PB;0HQ{Z%; zpBm?xS31e!vIs(DQCDW81f}@31wWTy0g~h@^7U}Bfu>DNf8KLRy6OF@VpgK1ry@B3 zE;+olyqCl8!w|@Ai3uF9D9Q>;>UcR0fWrg^DY_crdwhUM}ErwA?5gd2svRm z3)E_dvX1@+KBpj*G0F&~i|E9U;acdVSn(M3-6z^LcY?cqbbTg;lesPLURrSUyotdL zfhUlqNeOZ+&b|eq7czg52pQDa8z_=7*j?>_u3CX3nsg=CD^TD$HBVr+%2Ua-XoY>5 zyr=J;Mc#T+mxtO%s4nZ)w=eC#(;?KiLN#~b50VJ_XBC{`-yZD$#_|vl^;f4!3f~{Y ze3Xx2zJa+MH{kD#CjVN65Ldd{s`111H-K>|WQGv`Kwi0hcW|HPWAGTCa;_xT?%H~` zg&?U(y}$#QnoWfY-~X88$7cJ^{V~aR5aSC${z#+Mf0|1FQ`5x1lBT~e{(Yu7znxVs zMf~CZc*6h3S*89@&Z>kzoK@?KAH{$5FU4QW;-^}*p03tl;D@tH8d>whSv6wl`r)jK zz{9|5^&rR4@JtRlro;{2qQwgvtR&6;xO!zOCPk)D$nqhfu8cP&3ESCt_hkYmWDrE69tnLpD{mx@BCBn|HJD4-w-d^R=|e? z@Qt-k$o>uWE;~MY8kFPL&R08t?}E_je8Uhx{0nJYV!ZAq@QUDIxUWk%J8<_s8}PL| zkIpBLEW@pC--y(ZmJlGnNktz(JEnr8duJxsrUWAj8D-RTkIs-OX#$r5HJcQnEZl6k z5{|802%nADN*mWvJ(rfMB@iN6w1s6aKYd*5#QqvjZ)@%P-nH7PomkF5$GhuuKc2T| zOs7}#<;c-(W$RZ|il-};Fiu#%eDPASa0FfEFyxx;)$xfMF>qdrMPd?3HNRW%-#LO) zCAMNb>s+!c`3PQWv()XCen8>kAkFJZEj%0hUS{$ZV?MYKvmR4}L>1q&iO- zv4`h{>v^25`orT>=>kmOkrjc9jOCPfB5roPFmKzDsRfXVQ-%3{{}P?WFhZ7^#e#&{ z{%eYX?kwjr4#%KxSUw#DS-;6gD12u)Yw1TP%yn__V{Md&3C5^!{91sQ%eMR47Zh={ zc;(LL@kwrd(|*^ZKFPlD425w|^4=L=xZ$pklw`ql<0V(^2!3Ba2V?SnVGd1ZIz=~- z=R5fe><}zXCQAHmYR|_m2_U$v*ppA*G`yv%79Fjr~?yVX69H z@weODGloNekt6(!NT}3|*x&f2(Q#KklvbRVZaV6d52ZCT0!~DcW{RZgR4@mI-c99q zhp-enLyFIyTbz?$fXBY?JUCD3Rp9?NimjYTTUpbeYN@DX)vB6(_kvAgDK|6m0`fcD z-RCF;{=L-tZ{{X{X=?wG>Ut_^IU_5htgEKOBd7|NyIvgiZXk5wr}|HHYCt-&m>Oel^s>{h17n%|JH?2lN8Ugzh-~O;l6G^ zKB7bC%>!hES`X`?ue;(O5K6ix*!gu*2#EyQNJ=PO963>r!Di7vgbFMl#^XC-KnWOc zsK7kTH*}07M@=hwx=eHxz`fb`Q)vqNs=c}$w}q9cH6C|^L_EbhoItDPHv?l-Jmt{H zVjwejd$JopVXlgj&AEgd5QXp#r}UXqo_H~JFe`e`Lru{$P^;!$rdA>z=MpO23OFjB zT#4c=dZ>%lQ?$!)rvnbV#qfq|mgeoi(Yz&ngRIU9T>tcz8UtqwAA6Z*bz7yDDLez5>rMNWDjTdEFf?ibzUR3<0F2X$!+8dvjdGJGqo{#i#V2Y~4nY9nLDuHuImDm~6As(V7#r0YvtD&pNRk42Z7I+Z_ql#zzjPsNSjTZGO_}t<|j4Vl=qc0DP z_5QrAADn0v^3dXg!mrirx;bw5i3sSxegT!EC1Zm~c52f3x}xs*M4()mWz6jE!PAC; zu?=lX@I;bzT_q#1;d$2AC^?am$AlRz&h2*x?F>{vm;q3t_a7GW{4uF56Tu1~9(rbQ z`oa1Hb1;d(h_%^9q#EN?=kV!OmiQPM93zPif7kmf#ryL{&DgFi28Fr0)-Zn81LmP= z8Xdl6LS)1E(KCP`3!|?HxWR{iWk=K?g)E6+o|?A@M2HBEeuE|$ zZw5TtMPRw~Bc#H2gd%Q?&wyVVH(?hAVGLwb8E_lFh>A*+@fwBi~-Oq7B8b89a0yzqUu>;iw zKE`4mF~{h~g@tY?A;jzDfxWLRBnTtH*H)>_){iH+>Z+o7j3J1lcf#mu9P`mlqE)Xe zbUF3hoO$pX<}-s*-!lgXy zTElfQ4X6%0kSH-|hH+C|=-O1Uu)Pni_}*`ir9XWYBFW+P-St$WAFVlon{wwWxp=mw z@doE|Cn>`Y7uh9qn_y0fdn)>Cm=LdSQ}!lD!b!4V=Y>L`uwAk7QT(YyR-0z26yv0K{JzW>VQ0^T81Z?jT@snTI=v)6U{u}z%>YcU7>aRNMTHlFs0dhXCm8|5|bX z`3A~A`|gxHEq}b#>`Kd|{*C@% zH3vMf?+ZS3;GUfER+E&dz8iWt>COU^Jj^vQ4)QcC>UXk_`zL`gaO1;f)F1axxcp3M zhi?+V*;21dHvET)LjiV_PqP0-JNsF7`XdjEymbabprOk`KjyJ&CI|1C#=~%L8LgNt z@6ZXfaJmz!zkTI~KOF+Bf9G;g>=*3d zHDPwchMkZ=J+~Zyq_rZ95vyYhYx*;wM_byk@t|3jB=z4^20D+&WJHB*$(4uB+o2-` zp-IuGSNd>)ooOuehkqD!3eWy9$qKQAR<)TGfK>)-PYiom^Pe2|x_o#~)Lm%$vMp;E z!s#+7TWHvCId0UtPu6}3;xaPPu#xgK-!1j@y^t}GgmBvPDMt?OB_Y^X?JBsSN8wpD zHERmBYIIv{sv~jVKMtjPWWKywmJ5tXT4_a96h@c~V&xO|4GHB$2}FLs#9a0?rE7l9 z8s>t@68$$q%@hze`3}Nj#RH<9C^DVBD}^atit86RIjj1$kR%~D{9p(uPreO_Lp>(Kp?lC zWPhJs=Sr8st&aer_|vmX{*PzZ+{pFsPOY7plb(Z-q><}?X^yJz)+qg`?_r+K&Gp*L zy6pY?Uu`=&Hf?yj(1S1pHfh1&Ec$gbK?`7yZ-o--wQKJj~z@kJt`6~BOGWYQc$Dby3JgFbmA8BO?RO0jPZ;o{&_~#A}U>yKoW#JG2#Vbaj~dLu`Cvf)pwXq zb5aF&r1*#kTp-2Bhe~sk3S+#ZLzrGJd;|y;Q-XqlG!>;xmhinbVfpab3}?)Wf~E6# zfJ3w8V8^BgSfE@@ob5ukqOYF&1eQvn;ih9Y)K+jLiV>Uq=8TPA!!rfZpSL0~ zc&5Mk#h@Dh0ph-4Fa)npYL>T;h<-q#h4e$hN6LRaw zWN}WR$l+p}vR-hn@d|bG6%4Fqv7Al;-4j)DL9Ac~sMEnlVP(&%-gBmSHAoIOpg-z0xd9e#+x0BdDfp+`%1-mN6*Or1>ZCy@{+J~$jv<7Q(YNX`^`+VZ?K zWE}hqVKkMTD*UEk0l2o%%=Lh9zPB2ri~6`H8wTsFp7V2sPTWPki^_rXfLhEYp4B18 zYS+1hvCE6R^)U4pwx%?kmtbIj zz*UxsNw)K#8|tx*OfTMva%Fl3p8n*RK5%F_J6VOPn|Ex25sVp{C9QR~l_3tNDG)TW z24%cciiO{u_l#+F6ti>zp}UQCFZ_idB%gxqXY1kz25pr~g%^Gvg@r^NN0|m`a5>Vuv%++K8HY+StH5=h! z0%U$blTv%63rK30m@tAGmj?mFx(92th4zknD)W816^8+R+%Fq=^VXP1X?Q{QGySwK zzD@4m2pMdAka$p#OnRzGIN96ea^2Ts=%YP?@M%ohW@4 zd%hA-vccR*D%w;T%qn0)x(~q&*d?^25^2A_9&QpA=D{n9?@L=gikS0^(@fNIDy-tt zDW)C_hB^}?h(8XjNZYL0?~7W7TUi(cJiw+V4n%i0L&rEpk~q24=eX7cW;iaj?N^S- zxNQ~`FtIMRY;R7!IUNwVJZUod`0nzwo)KoJ2TL^hQ0m{ehtcdoas3FCt$J&Rv#Ru_ zt6DmG5OmwfT*A6_H_+jYkgohR*HJ7WteC~NBNF2gs1U{P5p@@ryr<|VKtjE*%I~s4 z^1g)e{83VH3p{*vs|my*#zMenk7_0OeE{K;(4MJ^B7a8pJSU-5yjqPL33c#&w=;WD>|7;LYJP z_7*LXbFQem;#)Qbvo2oSM%7vfv*w9qs1XU2$hr>xa`F3H?{l6=8v- zqoRxH0rR{|Rk@|rU7)9T*@RCZS4&vXVpAyVVP(MG4@c}jJg-^VUmo{g@lyD&Hqb9T zv1`CHwto`ose<48z$pbgEg*D7_q=u4=t4c#3XZR`g(#~4zQ=Hq$TWgsK?TuO=oIuy zG(n@t_`LiFrfP>hl!Bi?YR0zJ=-Bt&J*I5K+tFWo^gP3^1?W|-uEdxs=vPl%S;l1J z#T3R$ZV{!X&0!e63L>bC^ZG%j^z)`_MnOuWaw+oK!wxYgY)a%TI>FMyClDMP&x>zKM}H;UlXI%!`){tPcM4yAL#9R7Lo?? z!wo*O%=}ZPQeVnbWLFRBjCm{$zdz2>c4=ugCp6`+jH|@S9YZ|pim4)}Vazy~FsKHQ zoAFq1{RLAp2-(B-*!=C>Nx4GnTw_*?Y~wdkhptJYc5rGx!Yr@iE+`s!`{SDVb5}>9PCua^PIT{2Y|RpHS7fH0diJWpo{jP84^5R%l``kXANxAC1}xNJ_3a9=xQy zx(Xhp(|>ghS?tQXW)RqmyhmDo{a&L^KXRay0er5LmzMv8d?WId?p-22FS6lzhp)IN z+UYj&tWD~(cn3#Sbc9Jzs5b#EB->{UO3k%dE1jKOA*+xxkuyG0n%WM$xWoa8ZNet+oF-#9GYdVbqGTS!}L-$4vEsKDH@1cW-ot`bZ}I z(42MxPX3pF$r@B6nMt&;m{!!$Veji7YAE5>*i3GSA3tUh{@pr3`L9M2Q6p<3dou$? zBLjOQ$Nx2z1b%O`$oweWbJlH=+Sw)4^-2e0z0S~7KsM+J*~y9K5eupJR;@Cp5^a*s zMFFp}@1UKhlG2{n;LmvxE{#MKV(EsLDeEZ?mqY7#yj@=4)xq4rO^nUHoEV1H$vsGk z3F`93$|-H9`U`x*L#a_tIKKp0kaC9JtPn+Xw!12MjrCxIPWrn45r%`t(H!tFbYB8l* z(b;Ht?}C7jgd)=Wo?WR#z+;J81<7;03g{P*Z^VM_}mbVW#PF6*|A6u?LDGLQB#Fbx)~o(n~(g`xtD?eI!%#tR7s z2VWb;bR`Qnyt2jOWf~@Q!z=sRGet6PGjR?W_Np@8F}RK)2g0=T^Fj*GNMmw&qB^V4 z$%8OERFP6Z>O?2$ebTiru5z%)0d8&WO0!TU0vtn~1c0O=F>iUnAfOct{hz44%=(o> zP!4EN@Z$W0RBLGlrs{he1b{H-)JEL0k6)t$vR1vAhmdI1{H%9mIw2QG45-t%ea{et ztPhZwZ`ehZ&nM5$Sj4p)NQZFPpwgQkp&}(5gKVz>!zj;%V6PJ7 zM{76^(CHP#D607lkRMd13cdIXvTBMl#-LJ7@$N~!Qxi}{jm_;z()TpU(J9+Qy=Aj4ib@An zww4K7$_~011yXETuR{)J{sRIm!vbrzO7{?3iGwL}!@pH!P?l`^WQ^E~cgh?h4SaM^ zornOU9f@MH>utqHU^qvFH1|~sL#w}N<{*taaW={FfE%eE2PKSEc)OVE3csuC4nNr( z`eN9za~u!*ho5nJH@9zG!`v>cFmbB)-|AwaP_1`YaYpQ-+!W`7xj)A_v9t(emaT)W2&w)t$WvOC)uXrP}dvf6DD3Zxze7BCf1&s1~kDT+~vq3M^jz zOk$E$(;>+M!4u@kaWtB9gP3n43BTZKp>X-AEdf?(0_4d?j z@(70q(}2AkUOejaDxD`T3i90d>H%2%(5P0Vsy0QyuyfBfzru0Nk0?S=QtjZ->_YFy z=1bD9A-xn^;y&X{VTlOq6Z=h=9%q(*69tGllhE8-`OkEb3klxA2|&i~@FKX8Yw*09 zamif&i9)YLIA8c7Rt2n0noTwm2id@gdkWzq1R|tm{=?QExAZ5_OS*9t2pv#%91-CP zcAQT83Ib%I0+Rw|8P;dG?e@C`0QBM#8Q(UI(LSyjA)7x_YfO#-#(vxdZVx7$`e zog|=sW4gdtC+Ma#HtwHL(@1Bl!cR7AU$=af!hn3B@t%LSWF)$!iqUW7X!Unn{?`m2 z=l_k4wg7v8m64;7{r~0*z9I6vMa%uqFBEN!{zn*Sfy%~zK<1Y=&`LEG9zPaOaakeTRykm+m|nlURiF&2P}V9zRT5#xm*-rFn&-fFP(>vXU=HCw~n^?099j(1c^e@JcAa>8A}@Ev`}MoOzVDh%nF7j^vS^n z1^V)mG4}#=o&B<%TJut2YX?TIQ!1UEvc2^Xlv`rOrlar@lQj$up7Cjw%JE;0k#crU ziE*Qo&wj1tLC~c!@FtduthWKH8Ar~A3N%SlX;X+Sd0RWbRuCXK3L_E^*#tebSY2}! z(hm;vIW)6T-6E2tD@zw_Z_) z(Q5@_ddo9Ng zN(_k7QW9j8YJTLEVbMdU!U?`Hzn~lapb}Br!%?3~j~$t6>`?ozp+RDD2I}XvS}@T- z%bE!--DP{s+QqHyS#XvpoBu!%NBgz0k;Z1hgc27L-Yl!pBt%JzlH|3x8WLfeEWfR9 z&WmxeXly9hf>cpJ{4U=1f}vy!cYxyQW{1S1c!t1Jd3w{`l-ldpww9Gg+UU|GI2ve&uJMGOP5-+75 zfw7W%tOrrN<~l4E|yw49E*W&sOI}@6UQiu!M!} z>bnD;KnD|d&*(r#w?_cD959V*~XT z;}iUdh?ynx(iY+zoDqe8wbL4OlbWXDpLzzFC9)w1{Jv2w+Q$oWE5_DClwMxT6QoGv z-*wCoIyj{h%`dyBSbxt{3An1uz;!XXCg1O&K6`7lzcf2HKfP^@0M8=yfT|#Js~JyheK9hPR_G#7Mi%mRkYs=>Jb4No;=lNk~h$Ih3=#A=8Ys^P8Ik&t(r!g70p%mlY3jh3*i$iP z`c?@?b^@LwRl>dLpw0{u&R&UhAZj09Bxszf1)nW#nX-HSKW!bzQubA%4OX8SHM# z>2mpT`Q-KVRN?Y^x3%R3pN+Ug1Tv!<#=cAmz@yPK)$xL!?~?=e&=YNeNMCHj z;EUKVV3uy#@Fs7OGq?NVJlT%=6bUj9OS5+R>IgECe>*+ZyNIY%sIRU4?%uLf!%z?q z7nTjsPs}+9CHcwNXSPv~vT>>$tNty1rJ?81?x>XKyKzoaz+GF|*jT7zoL$7TI*I59 z8DG^DGb<^{(EDhOB?zp;4k|Ao7F@ohTg`RlOr8LVxTUj*(L>y*?S##Lz*UB8HXfM7F@)i9=|o zz(^Q}BW7+M%+MVR)IO0~sg4CY4kyLk(3vCyTLcd2<`!I7XUf9d9g#807fZ%ykeG{$ z4}H!&kl0I=UGgXOMDF$V8PLP`cArwfl1QIis%9z2CoQYeRF87DAQib?f9$@hKovB%htbv8x}zNR@0fW%T2R+kO(g& z>upme$voobFEmLG;*9enQi|f$+V#I-mE;lo#IS%8QOqkqZHhtW zQt0LV&k7f$pi4_>exKG)jFLaWF5Dt?A+^CNRKe6Qp*6=8Lycr|DUx_BN!zEY&3B?w zF3*(5GOZ!Iuj)m$L2okeD^&GSWZzVtNGUHS%E$*??t_IYxAf2Zmnh?tqO?zlDz(ix zA1P#BHvHM}yL&pf`BMv-d&k`EpAtSIH4)LwnOB~&A)~6~s(No&tD>`Q&y$k4R~_By z;&)GdCzHk*%-Q-T(k0J=wa$oza=^qKLYokCt7=XxPI43)j+83y zY4W&FlbBfMPK95aDRW&cY*)wvs^pl!rfLyUe8OW1v;+pIUH820U^?aFKM~qN$!VTV zBKRL&Md%$+jQD$WWya){)~lzM`3$}j!Yzd$Sz>U~Y9J~c?UUsikP=zye+#tnu?P5= z)`|6##4PIpTa5hV6zy)m*WtC&qcldFq8Z82;DMB@s4#>78HSwA!{LDEyCMt6t_NJ= z{KpmN_hi=Z{T~gz2EeTn5<-b$vO-Jqg$9vw}CXH~d8>ym0hb(kJ1 zl69+y<8N8ES9MeDp%Uo?rr&3^Odtl-@3$e(2jf`wTd?gZh5V=@fSuFP!M@D$h9sW} z?`aMqxl9Ita_z<>{0x%63aoa6uqkDO1bWw|Gm&KwBT2_7Xx6!|FppqYtHpC*)4{iL zs4@Ed&J3FWX1-NHFWu?i{JPQooun)Dho=6ZS!%>{fTbK|zy}bsed7w{=T@Ih48N~w zU-75<&&6>vfhRI_)e+RuhUC!li7J|-z#6#!q5~(kN?X$@62)}PGAe# zf#w5rB=^@q2V2&g>Mfg>$$s;h>A>DTEX^Ajw9WcXg5{|V%HJ)s#xog%>GsId<{@32aI`lI{Tn17E2_%}+EA~^Tun7hJmGB%B?U-jsBD7W z1ck0Ooy>zVjm~s7tEdcBN_u!;HL0%%7X4_yK2w*!8S?+5Q3@mf=cP`);}mJQWM1$*=O5>%{~QFilWdsH?nVdcanK}(yd zngIAK6P7GwQCD4p7olO<9+%>upGPw^ zf9{4$O^2exSlTgqlykI@M>X{^ROpa;iv8$O_mbu4Lz|Sz=yK~{qOO~1cnXxH02=&% z!!XuEwzW~~#KJSupsdN04T{HFQ(&IZ-4!d7#8choG!Dc8^%)V-M@zBfCk(^c<#!YH zACM+`V1nJ7*+zB&u`nx97rI8rAy_5M4t-L_(qS@hxWpJ*YR7p3Ir1)nKv;^6eMR9d zMwc5*Np*(uL%fZyn@!q9-3U=wLDwonuA6&_CwHhplPkGMx{Gtul3k6Udv;tChJ|8;v!1ug_{%EinLJbgK)3sn= z30xqL2S5`uW9wogu2s}$&>Y@Xijs}h1je!wTRy6CKjqcBt>07%)Ng+AHUy^rEiL7 z`89}<3Kx>DuCtd|we1CqrzhSMj44G0jgS}=cRXv0Ar8B7oTIZ)4b&RbhRN`)yVZUZ zgR+FRP+OYzl?mb>n;@pfNXQS8K78_wYfxN>q5)NAkPoxwSmlPfxG_kh>%+N}?07`a?$wr{n1~#6Vg90JyfcQm|qj^Qvt8-p7v>nrDh$NP3Eq zm729{BzuU`KZ1hfMP-W1VplpPiX0<+)=lvM-z^98IMttVUJ|(}Bgh@0)KvZz1;L7z zn3-D~7T+CqD((}=3^+efJac;Lp=&5SHj^3ts-j^c zV|^Iak{DDMO-2KV3j!3_6T^x7)34sOVotFYfyr$0Y{1V6CJ33`$((vxf%gufFQ@!2 zs*57qMy+GXTRAbESx&(k+j8qFsV_yLHG}8FMg6lTru|3#^JuQ9x;q4&BI(Ynh|ft8VSYS=C`^>o7|4ejV#r4Mf<(fh za2>oNau&BYu({8P$DdK=>mJ6aK@P`7%1S5r;?)R?KKy-u-^kDMZ#*>2E)#7y_!;ymdu z@q9nN4t@|7b}>HBpYe*aa%R4jj5jD3?)pzn!-7mCegqzLHEsTh{$-Rm56O$9^MXEd zXBz(;=Z-!z-$nT#{}K50HEn7YKrtn>Ba^JOZa)nIn%K zxQ&>S|34WcCfnRU`QMC@1myou#z;t5@n3_XvXv^9Fp76jyJhPJACg!CSiO>F9Ew;& z1+|~Dj)E2>hTYcWK(aQiZYBH1KkNRPl&eVhgX}TA_om>nj#r|iP8|6+`2n`G z@#Xs4`q8A9@$FPs7caOivL?Rg?>#W<(U85w0Qdn{&E55P^3yfq z+Tj%&SRFfQ0n(~`iga3%g^84P125gFc*nBj8LgrEusAF%ea7;Z)|#ebRjwZt>b-se z*!UASVSTV02b?hel+0tyW1bu3srxKe>WyWrby{p-8&N5Su8bv}6lZ)fZ5kB{H8}R# z+fEbjQ62$`xy!|EXUkhm6Y4`XIA8FWI$2~aZKz5b&p1(-DSWVQL7hE?#d-Tkc%OY&kwlJpP>H%|7HPSEI( zk{n`cou>$3bMQ0G%c~N#`s0Kv3e2pv|I0;;f!!-D3H_3YQ+Uoy+$j|uN&Eq(Ky=M{ z!QL$1?a7m<*Hy<#r1cCyW(3~M9j zgKLr+zk9TVyE6aJJVVX=7hl6tM(Dl;>*x)8Rt*aCF)7X{oucq!%e2JHdFD#aL7EPp z5ou-V67-xUDj6GPc=2nX%^w?fHq2rcNCmC~!z=-S+796|c@R@rp;`HMJ$mN=+^j2v zPT*Slc4^%dF5k7|J`kFjaoNZ0VEWm9G321@6KT>(zF`8XsSELdF(xz!BQe9DgL9!` zrjAsXf5G7p(Ynw!&@oz4sxiSQLnJ}gJ67-1^c#>@S7WaKrcB2Bkj6waP?ia-2eD`r0;*9a)XS$( z?Trc=iRt+b#|unNKjN!TkkFj(Bl&~Cd)NnGfKR^l=Y9qP@i`|LxcPu2SBW6YbR77w~_m*?yk7i^6`RPU-hyfq%_YbNu&%n6ThC z=m^|Qj0Xm{xPL?A*K-*-8KFrR4;$+Dh=uY1ZFg+6Ec+4*Qb37K-c-3;{=Sf<7 zs0|Tu5sc;C4jW)To+XC?bGX5c-ZoPO3l%8QwA!(uakCMO$-~O-=VA{rVWVXt*Sbmc z&FK#(IdS=$IHmWKvvVE{H~@7K#LxLa(G8%nLd{Y$S9liYG8+_WI&1h#%|PK zxG?6j9zf-~h*?yj{Juy_gJkiLCeOHR{1S)`)dZDZx1wuOr$iAEL7F!sMg?GI6{+}L zAi7}1(v{L5yqU1ZxJJ3*22BnfEmA1%!noHQTVK`LY(+yy4acmpe^_qWT+EqC zy=K8!k&~Pd6WQ1nTDtaWT>90HpA1`qN@fRxyT4HB* zwzgi8V_Yb$i(-ED4DD0`j=no6!z^^bjL(g>XSAMPK;5`ZDqMtkRX-*Tm>M=l=wdN{ zrP5aU7vo6Nx^IS3z;bk~O@>j1U=8q3qyw#;KuY+5&>94pO*dNEwPm z`q`ce9R@Tp#|P3cA1f!_`hW@rr7=7LU7TV>7Z9+F2i=Mt6HM>PQhnIjN;$0%FFW@+ zct%?L2hNO2^T3{pXpQNsPInp}EH(^V)m zoirFy0i4BEIj;V4b!<3Mq{6*5JK*;%T1B&M4y~%_C0&uRaaXLnNj``PFw7ba!1*5} zs7A-}$140hU)}8_Fz-NJi|0t+tz8!=zYye+@4V6`W@EyNhyVABmw_Jn~o5shOxMNmTng;9lxjYEB{npgO! z_Uw@VHvJ)Pzt&fx!kxZ@4$`5{sO^`75(kDPN=X-F_lx<(m>kkSoKR59e83Qob?+mj zgu@u`apHXIw$GLN(jM2?e_*;kP1r`9L?*@dmpXM8=0KSpDlgcX=Cy)%@Fzo>k_O;3 zuq+)lJ+!2QyI%=>8BD82!#GTe!-K5Z|00G?o3W8;k1=z$JBT8!iV2oG z5r)7$@3;TuY!>(oxD;L$vC*pO%z67}lci5I(=tEHU)FiF4?F*5k2*gFjWi6K>PFIY zJMt_TfCu5+_urnnie$vM-)c)cvBsH!66RsUBfB^}6e`As+Am9kv7^$g+9uw&F5f}X z%Sz7e{j4~N@&adSwnIAd0L*%ZQkX&8{b0OY@1HIhv-w9iW#kU+9j+h6+koYIjT>#x z$P{7D2!$U2dqY)zkm(=p+Bpu7W&MBD1$#7M0Nr2Knt?mx@V1U&60mai9}URNqA?AX zSS#Q=96yrY=(yg^xUY@(IFPuAT)~jqiCo_xD*fd===2AE9(UsI8w0w$9u%lV;iOIR zFZcGkfn05wMGWvKI>G8{AQsf5IyLN73_#6IpONZUWrn>$CCixV53VZ_SUAnMCf@8J zSYKNK&!dXyc0h#ifOs1bs5~pCDb*N(&d8`6)K&#_i#^az0T-w}%XcK*9MV8ZtM+f- zKmpz6An7wyd$8?__huECX$LmVDGQVnj68QCqgGvYtaG`*AAHTeY%9It_;EDRF|&YO zU#+b@^n_la%}=$h0^B!K>Ls`U9N!a9fnvfa{aFsQg4^fsK3;qAAW@Cx!5}p9@!teH5%9DO`2Hbu4(%KS$T;wABVOuD z2#e^d-q3W)0S&P#ZTWDcDTIp7VGoK+%8mxBHy_S{q3dF|e;9Nm4szyX_+KgD*Fr<= z6;qrtng`rI?0cz36t(~;w8^&Ub8pJi7jbRRT^Bge{d#liae@ z%M~d%&?mvZ5ADG+?$K~-W24K#kgg$G@O2!x_(yr=*B`~bKQ;TrhrLnVFgM+zBup%t zBa4Gv_|06O#ts?Nr;gP=aZZE0@N)m71LgpXe6&ux%S#Q zZCKDC_*)A?cWAK*tU8QKdz8`MVy^^#?*nG;3!W2~L<{*#`%AH=Hc7T^Og`$Vq;y+n zl=rc&CeH=&z|HkBB5+QLOtM znsJKkNGIu#Cs#Lg=&;KJfnYrpfu;?;gd?gM-%JZ^S6+2xEm;~J85UCx$Uf=?3OWCXDO5VBmBf-r=B(}vF< z6`=f)L}H=Ye3Wo~j4(uTj>dipZPuz*XcxI%Yuc>Z$L!3u=CT_t$6NbmZyn+gh;u5x zo(?u#wp^}0E<0?V@2a*wf7n9d1=?Y#3N)7zAO_(r<)cI6lP@Wu?={na`UoGaSR_a9 zYLk-`h)LSz#pp{6I6|=YH3z29IqR!X9Ob*x20ZmON4k+;7Edcv1?UCTz=s1I1&Y62 za(VjV{B&isA?1Ez_UgI|d+Hmb{d1L88Y%ps(z~4&XF+AOhJipvD*VmAEr%Sn04-3d zjJ-&Wmtlq&dzMpUziBPp&)lH2MrmgUqVml`Z}hGCmsv+;(&j3y%k%I3np6Q4rffDE zg>Q{OKC)N!e%wy&lar&!(yNgnZSM4E98k_n^zp_y5w?^V^$+urH2X@_snRC(i7117 z8XBh8ejL+^q1`ED$b}vOGnTnQ-Pa_(kgH%GNF5G+I%bI-sTI?DLMWDiX?=PAA;TH_ ziS%R^oH&^vD$1+V%9N&LO%Lg1O&4@`whY?Upk~X_;wY_I)d_uTsd8<^MLCwZCTSOP z)|@qWndV*@v;aG)-DqQ2^>kzR!BH;q&B+nlnQp6+BR=qY$u z87Z=GuzyLE*)X+G3QYWPdjYT;Y~|TEi!GnXCav5uc^}G)RpIXCH;U86A$mvwBbWnWms1aA6O%W ztA52^yZeab-9oO6yvB?s^-G z3dSL9;o8Z2+B_tv`HJf=&5eoj???k*Kob3Tj%jLl;|Flgd`BZ`<24bsf6B%Q(ce%j z;LDaUxege5WR)|BJ6dB{YVj`bN@rd{I86v*V9zx~*XGo=4C;AGXEmLzVm7%RdkZOt z@|b?;#)*tK*|y3cwE3+3>IatKNkyHjlo*7qnH*Z~NHAHR^B(8o%{kQ4K7PEI&QgS% z54PTd`vA@9GCcRE%G#>?ir`ewWz6UbR*2!oTISrGaz1;;*Z}q=Jx(vF*9#bcbq0}O zle6xL>gIssF~YmO8+I3Zz7n(I^%lNpa(u;^^4>prGVOyWIwSqt4)^?7I9s*rm%D2v zr{x(%lW@TS^1`Y$av6*E6`~qB^UqMg1YP|KgzpD_mw!vr3IF%&@_*J`|JTp`8A=wi zb9yM=n-zI^j@`|WFiJ|cF!^M;BnUI(62B_dfA)zH6|`dnT5Gq$Wn_Kei5Tpv&VYnq zKGg9J;^{@WYaI4co4?O?9^G;su^(|?B;j5005wN_-(!ul#r?kD(kus!fkvz)P!$4( z=mpS9L=f!)L4*wT1110y)&iI={yI83nuR{?SRi+DQFI?T-y(l+^H1g zkgPOAO~BBhI#ur!1G@mD)-41DejynZZ>bm7Nvm3XIQ2#3finha2u(jTRD1N>V;_ zn91;XdZb$GjQARnx)hIdy|VV+I@X_wUx}Kk5cDYuKf}MN0n(Wl<&{_K*R+LN)1fqp zrM}PIe^7YMWB&BptMs(u#(xm8qUN-G@c62#Q4XZ?DAd(-`4Z9pBw z4tm#I`8yPg;da-<0R^zGaLvOLO;5h(4C8|08S)n;LZ3fpq78~?FjtLw!>O0a&Y2$kyhRrLD++{sY<%z#l zThDN+?9$ri5zop;&yarA*BSAajbti|51YQSYa`R2MM(>1OuuG~EdX1)?|Zh;hBull zXug2AFX%IWCWDwGp0;GCRKfz-;$_{F@#)21H4uq(ZM7G3HkHc~C)T5SQY2JVBD8d)Z8im^)1auE>CMMg``7}3*Y=o6uE@E<2T zGAZ4w3?n(N5mod4Pi?2K0Nll(MI|XWx7=)8c1oxsMXbk3 zOQc`cx7B3lTQ%^niE5$$zVrT1wBF}3`7*ip_PMub@|ID5tjT-FE z9RYlWL9l1Ta)A{+;&D4hkE{x$ra4ViJ7)c8BN{>R{E$lu!+1S`x^t)_dWic<$3^ma zrQuaDXyhT{ZLQ4HL`&}cuKp`r+T{!f%)=|1w6hs!*tBBe$&agbp_4F(6&w+MmpSuV z%#c7FjkwDnQp;J+&uRy%ua-B2OLxI_P5<};+sNbVcd8XEqaC66b9Byq@Rj4P8qq~` zQHdD!I0uLd0U~mfW=HMAft|{EnVCh``v z(hbrOnLZz5c)v^&=94X?!M!R^&UJTmelXouALd8qw9<|0d`{`>_;&evpd3z7ao)#2 z4r9VcM3q$i46>)z? zTMwFytepO50EiZ>osq$|ViU}nX%q29{BYN-(9ClW-u7RH&6CQI?m-f6MhB0p7L&`4 zkC(GI_#g1{v-F|lpmR{hj*dM_ZS8QtfiT9LW*LgX; zHFn8OPXE}IfD1p1A^FgH#+*cz;Y8E)@u!v!Ci%r!X6f-%B@m*WLn*(ZNPO(CiW%6vIq#duU>#dkI0%CUyVaeGc0k0~_ zAAaMy2kw^Id1Fb#42VhlpZ_>v{A99TuK!)KfbXZfe_ggb|99D%{D;>Q;Arzd?Ir)k z9`euc|M_%R@NLRl6G8BvL;wl0oYNpF=s=oU~x;Ih_(EJ|;Cc^&xNdLV#fo68$E%J zgwRhO!{sLB`Blr)Yery<5JPvjMh!_H18oc$L-t0h*8vh);Ap^zh@Kpu7$6}+Dw0M| z{a9sKk5ApHGkV+|5g^4_%V2yXA%b>D#4hMDSpRI< z(ma^qW>vw5#NJSL7i+SbI5ur;YZGf@df=BtYnSnTEsr~T<|4P$g44LA*!qsUA)j3% z#$#PRu9L1yH9ybbl+n{BRntLq2?)PuI`xUki?b|2AlRKMV`bL4UR&AaQ6jUVHNU9P zMP^ldzKm-`5^jn4Zxy5C!Dyn%ub14DRFu8|qm|5Gg`R!zBGjgdIk;;G0^m(B1mV>! zf&}(`wwx@s43At(IF~cpy+_(8f7R|;aNdGL{QyPSL(XK$?b^kId4 zf?_U)#MChwHfthBcFJSR$0@GFoZiW28)yTmR{!)b<|t$~>?$3xy?bVmWv-7vGpehe z3@x?x&PQdgC>B;;-I34Ow7$1f>LHN=)g(B#s#zEG1uPHQx9B)a*~m(3a%%Kwp`FjU z)u|oi4SFp(nzJNGlB(X77}79zy>dL6JG(RUCP_$;QB>J(k9~=HjQT|Ut+sx%{?%U<(V(?j$v}x1lHfZO3-^^PUaxfBqhnh zX8vZM*=16KZUlswtftlA2rfvZc8)0H6B*R}wJO2T4UE6a$P)mWE}vl)m+>7m_e(*$ z6Er+mtr4e8XcjiGBy;B2o~sY6h=(AXvR_(&$*o+e^Y;jP#$dy{_bv*Gsx|qg#T=lO zEhyJ#JNocdbxl@zWmD3Z_SabPzLP)2s8ScaRc7KRe$AuRoG+M3M8qJMtln zr~Rm2PlPr#Pk-~~DDyo=r_h;(xu{Op>%ta~z`N!QQXG=9AZ!kJy#T|^Hg&&z?};Xv zM!Q}7rP=Ju8h+Anjbm5Y?}3ZsQ|P0y`Civ9TA|wrIFiA~L<*J8I%V6hdZ4VPW!s0_ zKdn1_s%3cygajgDp<~aB=KmORbnvr&{Jfvu6s_Y8LJhM;&2tUMUVPuLBDkg179xTX zcZDF~$H~?UE?YQ(E}L@?sVtaWyW$JjbkiLQknaO=%9MQ?_W4JkPWTI--ZzfX{aa4u zKZRjgdUM)LyxH!~N>K$W>&eq~Uike!A5dVlM@t$((CIa9zvDXgWS*|w5H1}5cS0QWoDrCxj z>jyvbi0^8S?NxeZHg3vaGbE$^p3Za1XhSl)(LQK$EMMuG(qCG+WnP%(zlz3`$1(**@P*dS%BPainvkew z)F8MZ9nT^CHG6TPAtXRaA5gII3-KcpKz{m0LIWxxxnH#1G&3`GFzK*Vu5qv-s(|3s zB&S?CB$dj+^ItVna#l44r52?w>?wco@y$V|Di7zK9=eDZXTkA6QVj1z`UxRuX;Tw; zOwlVR8tqjeFR0PlW00O=mz)mQ8)!AY`U}{*J|}gmG`~Kk(^6XbJK3NLA!*&FcoWyL z0O-|*oM!e8F3B;WlInW)R$*lrxS@y2Etp+Xky^(Qohh1F9Mo!BDSS36LKx3dDO9v} ztI9FnKoOv60!kgFuNFgcin_5HkwvsM@mBU8Gx+asU#j**_zM%X;g+EJOxtFJz6xSB zyNcPQ4I_C`dmqk{BhSRG>8+AZXV{=67nN4+TxqWbaX_6;X7KHO3*8_#OlpD^`YtT& zpw*CUV?+^ID`koy|2fjD`ML_hfiEXzKn7S%7BR1oj1j+uJ~mW|S8Q6NRFDSqn%JjG z>rV$kZs>$%`Lh_hDcVttpv{ez#7zeAtDA5AI!GxgD|b2G{PR4_@GzP{~UrH zFiMg>axbC!NpbIxaO~qEWxSqxpakp)AH60k$1R$hM?KMysd45kggPcBq$y9^`57w? z7~;M~q(>!@xQsL%EBGT7W7ol}wQx>jai44d)D-+X8xqg?AF`*px%qP_9i1M=FoqYzU~ghcT{Xj-T%DQU5f8h+8(W+*a+p4tM3X~0cyF<+L2SdS zA{87JINUhhqi4ioUd^}$Ct`uDJ8^gk$G+boO@oUVi?=k2+@hDZKov7$X0_UXcBJG5p7vE30Q_{||)s7vlSu z*_g7KGqMPBXD0NCM6pmGv89ACe@J`;y`B>UC3+k?BLo9r7lh?E=RkoTtD75PFN}yL zzv6o6t39k2aw74l+;;rYHr*}uolCjNNQ{(bm!ea5+x@2J&(1O|@Ao$ISy#^5Z&yscSKu$fGD=&O-hiuuoASZo%q0>Hh1o>1YM94 zcVTZ&2pCd6CMJFXWbSTFSV|ZJ9d)atuu!k<-nXa_M^Ta0D?PzR}E#V)J0oBi5n#xLPNlU-Dt>9bd`QcSQfIm8e}4Y z#!nAz=_?&f0!pDbsU}qtMA~3|l+;gbK$zZYSyYg{5B7I!-({6{EH*hf(R%+xS!&~u zh^eA4V%`mo@~l0stn7&m7ypD-sa$RjttE+wH!B0-x#ys`rldY0g(U#Ny}#c_FZM#x zTjME&!5W0g50l1(FhwUXay8^DE8#Vwp*?*@B}alQ38DFy?3%f^>Evd}8@#`$J#*r* zC0EWaC}{N4kx#)|uhxxfn72Ww2F@#+=lGU{ajI}zbUNXMYN?^9?_`XTunGa;CXO*T z9vn^F=xWh%ZNXzR%5JK2EgM=yLZ_Vif*srCOf9H}6L&}^XOmug6xvN(l!CnrMzK?e z-yEg(!VRXj%JgX9>B&BJZ7T!214i$l(1nO!Zhx9C z9!NnWNnR%ap%cCDQ7BVFOaiKik@a;9xH=u5dQz zaB!jmg4$5#r>xhx?1fXJ!q^-$0O^#Qvp1}hKk+u@YPzI;kKJf%ptAj#x1%CYz&5Fj zoTN)aylfP?jP<>w9XZ#+xf+SMANA;phEwz?*(1`Fot94$iYD33=P^r4ijwfC*hRw0 z-epp*j+(jwEho2Pa*cR?Dq4BMJ9rEWq?8;;=IrWQf?Rx0wx;h!)CZOB4U{5RRe0D& z{T|!k^b?gRN_sD_jv5ODG~A#MV>qgxhRHGld{N#YS0Lf|*pVxgNmpanK{4fS8zzkDTe-?>>hM6Kful_o7-TNy)!XmF|K zMC`6ky#f0PhZZIaK#~rPB2I4KvFxtk7R8Sj>xKj6styg<8FxY=pdRR9gqOk%)QGYm z2I9DRv_wUz;R#T;aG#f5Txx>jew_~kru1Ul>)PMN6yJi< z9!S=C10_G)3QWVo)=MebN?S*`{7zyG}7TrQBxI8ro2`qkv^^ z$niK3Du#I5kO=howAlG^f7tOMP*R>sp}@(0w;AGYe581=y|f@LrjxxYzand&q60V} z2#o%cOpUG!+tO0|gTgHA8JVS1!>qo-?Qax|L7hDqR(_!(i?%@@nV2kRHv*HfzZhm7 zB{Mrr|I8aG%6pc^;HTu#^-Lg66huF$>4iy&g=MYZ8m2`%5M1JqoCu3pvHQ~OduE!} zZ~to|zNFhPspP5&qatnaLWQC{z27ag#Yf54!t`_sA+7Heo6OjJLPXf12e)(&2$#5~ zH9MtIp=FPEN^;7nBE8(T@QAEJh2DSB?P~c!ptAVNi#1GpbZLT|(v3tA2WA;6!Ue%18eKlU?EPz^v51Sn# z-ZcVK=-j*uWVYP_p|%rI6SR5#r;KCCv3%_-Tt$hixQH{0TgxFqIO^&Z4eEvK#|Whd zyNcF_ZI23HV3Wl0cO5Q1+MK48mqni`75D?DE%<9t%~;VHn6N|RXXvwQY93F2xLXHR zu|uqDRJe(?@mtqhm!$(u;3;WlhM|QnPHUGIahpsmR{qBg7JrzfnwuZ< zA(s_Y7Y84Vnb(~|Mp9o*rqwq9X)1^{0`e~O#-BqfCW|x9uRnE>a61dh=^&iL{Te6T#nQ9d^xq2oDNC~Eknt; zoX2W{UYg-8s~3Id?)-h{8;nT4;v3kBv=VRAv{*`2l-MQo(bR5y&L^xY;hN~>Q{ILJ z9%kqM^64Mtrnyy+2DWgOS6eYy_^|#N!nVze&xY@YR;1&3_2bqChrD zn(ZWnu%Jxy6AQJ!Uhrxmf&MB%cM;;cSYJq6 z_-|>4|BDjzwFCGP)BPu?P0aAG%Lj!u>n}%Wo^%VWR$|SuN)jIeG-H@%~v{IK+F6V7)F{6 z`jtVDix!4jWqE1?C_$o>4f`{~$BLw=Z>KijU*)Ul``w>3?@g}RCcOpX3AxoNC zBVIhWI=IifQ=$sd-tXxkq5)H=B+x41iIZd*-9$|rbRm85 z=g5OcYLbWM?e*2`A3t#;B1|}44eBZ6u}D&3`-#jfDnFejHCWSEZOZT^8=s^ipJyibhWcy9f84P;ovZ$SA}zHr)0qAQ-Br1um)Fb=h<7 zrw#RizrUtIJ4bIugV#)ny^?Gpe4qI3ZCkT3jH#bqZiaXSCuiwQYC7wuY`PJ~9!;6l zQzKOjWF8qr@*Q6~VjrAyo1NfS%mQJ={d_yy@7^{xXSri9(Hs8_%?x5S0vKosM1OY) zi-OAL_4kmzSD|UUe6eXJZ+^a9M|94D#q#GA6T2wK(4cOgF~bG&uE!tmUo89&aMq$y zg|n|^Qo`R_;{9JRS^uF`_|N_9p9Xo>K9;Iz@W=xEkxkRYerHsTqLuJ|kzj=&0y&jh ztjR_UE7Hzv;1Bo3cN%<7L6eR>NvW$c@6&h-59=H7Lb~E6x}=ZiJdYi`M_CTfui0Cl z-)xcihFe19An-=P^%O;sn+KM}7%%ZLdbKbfZFOvUtqbdaFb>kwCMBqn_QxeLga#w> z#>OBpM*rGos6#9w=8m0-oB_{*z$lK5r^o7L!f;4QX7D*eL?xzX47f^5 zI@vHW4I4N@PgD4v#pGxx9nMFAmfXrzPJUmmIl~qYwz1pqi0tp98eV5Kvi}nG8wtY*LEBBP`?;=ZA!`yfcFkg|TRv~J8z%*L;RW#3#Q?R>Z(w!aJ01@|>% z`~}OUB!2tY^njf9_n|Nlp=-T?0gSVRV=i5)ClwM5w@sYGlCUHOZB{7PU%K~ycuGsmL}<$9IHOM;-SeIE zAhq>`dW*nc8&zp{EP~3rXp+y2hVEDwA{whu#yfhlAIb+PC0Q=lAOL88m}IiKJz~>; z6%%{pZ)Tv7FK;5)J{Bwg15!6W|b{zuW&by8!PwxUGaM9%-%>D8xG?ho*THz zy;t6!Jctnoqxl0DZWjaHqMUfFewC4qem%TtC+2F1#PlX6WIDS6bp!?d(}5%DKM zU#bn_<_b!IlyMnGgYaRV5aZiYQr*#B?3c!-v@gI%#YZr zET3)(jG@{Iu_Q4f_YyS_hO=J`yIW+AfK>Jw;_B%TFub9;1vKth1*j$8fyEXT;WSv| z?3{8Ft2-E)CHZ*;;pTOw3~_Lip`&^(O@QBvntSLx>|hk>q&yJuenKj~pw7HWaUo1l zkCA!>c0?k5mjjwbjd@@@enKLv=hHI`W7h(7?evQ#^26Pg8LK1E%(n1wX?$#ak$M zFjE4~>>wXR8wdRpF`F;+b#u?wu`;%L1Dh9sg5LpxFo<`WPvV;|aDP?GsuKOn;jiY) z`?oX~_y4}X{BQC9e{H`Ot^Hr^7al(b3LLo1_O2>AfvH~J-}`g)V0EpLx^FkPGrBSpI|cnetbmhBj)+9+e3WDIVq zi2V^+bgD)-6kbDJ8FXc!O3Ohd)_n=uvgJ>7A*a6hB;A%dZvfePGvR3Dc#&5LIe{3K zM8ybRF2Sb`2VQ71#(me2?AYLJlQqNog&5~O5pS&P=~Y-M9ux`mRKtzGnk~I_ z>FV%tX$tR0A{FO)vZOH_`fslA#;4EPcA+5295KfeQ60(~ZW-r*m0d0fktF?CQ0`{K zs^4v|6G&D1pN|%wsVkUR?^Pv55lqvqq5VW^cjg}km+2xrFg2xl!WyN!CWKgLayTlQ z4Ymsd2+W!-s&+d&Y2irh64Z2kvS+ORSd_PZ0UA`)La)^sI7MN4p0P-wV7dDGtOdm+ zn4CN7L@V=8tJA1-aI2*tfZlWddp}t1$5nB}aTE+G63d~@h5UO8z3Z4M4?;4v0rWJy7vgJ~P zt>ltMBpidxSykV1v?a95bv)})bYi9LH$awhGdE5TB^RoZ%@9Dn*bN_;J%2p~! zV#r>s`lv~uBpu8?SL#@PEt0iO18h>jFaaru9uUfQs)I&)_34+^@aHES7hBhGPlb48 zRs)L;=C1>A?{YkyOcKPmd6gM}%uFulEZ0jWCg6UfDcPh+Xbfm*FdJ4TRcv712mLLd^QbHm@G7W~niZC-6oZ-@zBEqeRYUc5roLs1S z$|{RvWYbKg1}sYJuvkqfS8JC15vC6CJ8C?KA_wsjc}YH*hv{8N{t0Z&k|`Yaoa zit%w0Yw+R=^*kIk);dN*MF9ob!y>;wYI9T=q#BJ>BnE-iSVo8Sh;Xeo1qBX8^sEC* zWG(5{aLA^F--YRxt8ldCYseUE{jcRW2~}$7EZVtMC}?cGlW5q#U}?$(_A7x?6fxt9 zQPXIIPddsL>{6uP(>Bw%)}3TNP)Q{`qk~ae;(-TZIzP=X*SZ*_cNn{Rb#rns$Ils~<5HLp%zdw*s5~r~F7ZdET%_&TW8i|O`fTaMG z2+Lg4g`Eg3G)_C^3VW3y36BB;+($=x>SOxa5`QQMyrd`+RZ2^oF;zL+8a<3ZUS`YU zkuq@IMY($*yox)=bfzVHq%X&K&cePqxj{BR;LJLQtHxG+Mx6M`31lpLD7oCZ_VQ~=Whd^Q zG{2qCZVSg7=MJQ5U`Qj4-i!W4rdPZii{$-EGHG1%iCqF|*nO+wQU2swf6eDnKZEp> z#=4_fWYC+JcbZz;krs0e47UA+z6Ok6>}pLtIG|ehwh}q;FN)LT%%p;z?v3gg=CxM8 zW3-_>je{0mgoqDI6jueZUVU)_?B6#lgBu0vzNNK4rQctt$euG!9`w?R3>{INnh6EuTN z3m-6(dHujHbI_d4O1YTr;lL7~WNPuBV??XM^inT}n8l`_Ym+X|x2a71y>tH{JaqG` zFf6`f^I$z~lRWQ6!{fanWfBW_u-`#<-fcDeepuSWweHCmW?>^>mm!=}#;N}(RJYUY zN5L*(;Eq(T8#7cf?H|x{X^1v@Avsi7rxXE0&o}1`g@jaO0JVT|*SP{n3l^2ch6SD4I#x5iz?kJR-%1=n@@HOVVx#4*$7jJ9K zDGh#RZAQv`L1sp-lztkK>kV7XYp`nq2{}=S!tYHA$UAB;u8gqvc}@|A}&3|<4jv3vmSwQAIatK|`2{K+`Vm~*g}k@0?e`3;VHJjMW6y%co6h^6?xq3X!&SKsrW9m4j7qd>)CI1ZQZ?@FViEGM`t#SUY6G=G9{HL)U8UYMzo8oR z`EM1y->5NtSj@l83vAhI?IdHjuc0u55t+BFv7>V9raU_n1Py#1vvOaK*N8t@k|}N^ zLk;+6Iw-mI(a%6&MeQFr1EcvnT_UC(u@zcyy-)pekGX(G%ukcJZ5YTpEsCh`*;Ffd za2Doh!Asz_b0Kdfj%JY7;Q6I78IkcaIVuPVMvEvD_Y%sk#{YzJe&>G3q@O*H^64k6 z{|Jn@=9x;&fhrMc$QVZvr%pUatPndM!yHscN6sVu2@YeXImi-05Ue(iL^mB)iCC=% zOcyd%lKZP8nr*vZ+HsmBY6lUAYlW{vhQ)_Ns$G!a4e9r&(Gg@Qv=+Mypp3W0W- zM}jvKT{O(=ielY8>l5a$$CwL!R>bn9h$H+$mH)FM?%%?`|5B0Ynb;c{Ss7V7{>yOr zi^~!CKcD{9`cToq~&6+Oq z_JVMEp5cDkdh~Y0ceLrcI~V-$J!MJ~IV1At?R3~r63ZmJRKGF1Kp?25;THL?>Z!N*8bxH81KSgVrMxSG`@cSbMY05_Zb%;(UiXa4VcN1UELCy0C- zRf`FTG$Y~n)exY-`XYsT%`$LzL;j)PO=6&j+8cc~j|!9|c+?Zz-RKdeGJ17O%d~rS zokZFlgMe)4^VlAPmyU0KTSwCqxhKDL8gs{XOd z>1$G1roDl`qZFh*mmVY9@`_~V=N-E{8b@CM`{6*ibM-A^ZbXLQrMJe1uFLU;g>_+Zl z3?yd>AKfTUF#c5@$RaVgd>JU$G(cWOQ7JP>c`U8b4PPKe-ty!!r-n60JUZp#M3xgM~*Z%6F-GY zEvYlD-eZO7xsxQa%AytDfU%U`kUsNn6>n_5R?vLlKc9pL^xjkkil0&_RR>J&3U-Yn z4ob%RDiG^Ssgq+X$ThZz_N0_+fvA*{hLBOvuD6B2kxI`tyh0DkrHtZBe6TjWGTA79 z5|M>;^2{$`E&=+Nr96r+lT9Ok$1j-{1U)EnkNFS8C&ghvM_-L7VnSY_PfqjTx& zB||DK>qZ_F4G@&lPiZ?;UC}{F@rGVxY?!l5kFPEl_sra}5^Pkv3`5&1>pKI{56U0i zNR&(JD6n_GYl;U&t-gn1XR8oV@Pw`?u_mG*sMN0_7ljQjh4iYS*vV$|GFU`PpR1V> zB+23|y$Y9t`+po%R?>VeNJLRCRI@6qee1&6N)Ao?w(!Cc)ZJ9lFkbsviEGU$(gWmo(a*%`PRP&Cjh__Q;c2I4PZgOIy{x4evq<#AQcJ4#=$M zje}Y2=L8(#yD!S@O@8nNMn$?1O zhO@o2&97pkPDN32TVcKZIk^km?R;P#YG<-u0*Y9K7T02TO9RCOAwuYXl$9mUT-Dxx z&uMW`xyzhNu9F!UyHeJg;xVi}g+#!Tcc|EeApEA51`$Cv?%u%8Py_;CXo~?sGGpX9 z3w$JpCc|ml$J>F-c;DN7nxZn!Mx7BBIKo&mxOv^LxVSRKvrUoE@iUnVZh$d*;}z|; zfBH_Eg`v*Rhns%@jLfzX8=8)I0drsVUC%WUo zc}?eO#ncQ%f;E3~Ra2Tq(qhPo(l!ApHB+ial7rt0+U5sHt%a>4p`{}b8fKUHVR710 zKdl)8PAr4@1A^Un!^|x6rn$-Uu1*l2=!0b|Ni=1MsLvpVi~PNW?vxNaB6`lmEO4Lro92_(SgThc|<>K6N|Jkv+rTLXxOW} zVRQTX?GATugAa^fx~E5fVBy)r+?g;?Cyf3q6u{;SS$Vw_6uEVlRNhoFua7z5SfeMS zXh>KcM0G0kij9L;DOxFGwo*!KvY)e3g4Ad=2yc_`79WWiWe8~nlmRy_8F^1R)NQ>Dp=PEq}7zJUy?1V9>BTlz*;i+Vcnz9fgvzM#Vai)@exO=RTzWG zKjiop+fmZKYvZfv6rkMs8gh241%PBwgxG(B(O^h*-Vfp#UIF_dp{XHvt_v_-hik4b zV&yPum?ANLT~CF~i+d#PkV^0H^XQOlaEm0r=N#GUfpCeByGEVtStG>9qMjR@M+UH0 zK-{HgO)yGiY6zipGc(0Go8GizP%WiXVS%dn?ye5Crc2}ej<)?%F_S<6P-&NS&mM#e zp~j1=#)Igl&XHNIeOMFbvcxww#EojNJ^zRR+#q3|2rVlf5bTG*N3Hl9VGz(mSw?Vq zBRF9LZo&*-M7J-{{cIdNoLDJPYLbU*C=wr1^-*F>Y;Z-#{K+S|>#@w<(!gJL|LMh9 zm320L3H9xpA==*!6NLZMWcwvVXZji={y9Aa#!dXS10MQJZ>hCdj}9QEkR_*Ra6-f+ zLJ1W@$dlBdAi)nH5O1|wS88CcrE*tb>41>wL7^4c^mJAOIo=Jluc z&!45{t8chFxq&EHtk!EO12|aaE#*og7-m24$KGPx1dKbL%R{OkRa_InFG^fobcKusphEx7Zrp&HhIF8nTVAx*&lM;`(b(` zmD;c(94}!%hQ6!@CE036bYEx4Vf%gOXCM_GHoLl|--1YGKOB+8`UG&R{3*>zPhoL$ z&UK++e&Aw4uPn7+p(|HO-D8WveC@ZONpQ}_p>}UV!?<-oYGqFbcDIb^=QkD;BReeT ztQe4TJ)V8bnAVe6E8e@ap;1}tC*1Yiy;@dpgl`(O4>w@aa;{@5#4e067vpEyUD_TXJ% z`?GoV@v-T=>3)%qrUO(1i5IdQ1|RM9oidsS=phW8h)^Fgk6WL67pMv~C$xrufG{*6 z+}OZL8iOP_Na&`Zo@mcqzu*gR6{&* zM%#ct6@`F#{OoB~%YnObQSBj8xWVD3?28iO#znFX7;<*yp4@}@E)!;ITvZKFI1kP9 zQtxMCHy=X9-n*fWK4MP@FvyL`vrD-OH6tj-PWFEfia6-C_45nV7u z3MLer_zladQX#WLzmYGR_@G#zGV^9{lVWU4At$fWL@p;1A1`TA##RBwQDNuuFbrJB zlnbJemWFLlx+^)|DvfbFv#c6lZ)!S2$IXQ-o=W572M%t`o@~q=^f<#ung?J@4q1^&^T1ZN8mERapd^)gRn=sO@25`PN)OxD zyw1xi{wiXIY&7uqet-F6#_Wk$jP>?0STu`t z(HpH&9u-TD9v<;BDaSMqCq>EBYeC`OjJam&pxB|wNW6CFnApRC=V9*~vYX1j4&7d!*1hL3y3)O^bMW_LN}x z!>`-Egw}LvmVx6Ta!{Y?gTFYvDI=hJhr0IG@HeBXd~0PVKq}=u6{cE}-n9K7C(hQm zoQ^YlUAW~>{Q&9jTB9FRjixn4HJowRydS?b6J*f2KL~cRpOOPuH3xG^$qqJx5}0&i zu@7ewTld4u6?aA?5&#k0O0a*Zs}v+yGHl%vg#}?LHtqz8#WVL78_@4>IfKcZ6__t? z_YoJ^v(*bjjv$ZgrYM-I9oU?b@=33#1hIHb!hmHcFYE(6?QuXoC+ZB5*Ky2jF;ivp zX1fx^0=!xVA`@u@3CE9TiNHVSQ*i$@{!7|Q$9>;r0MH8k!BWJ$gXFV z`C~|6h;r&dmIG;Fis3i+S|l7blt@mZ{u$cGHj-`$Ig-L4f@~up{!3Ve6DEh{eIAfi zA*^lmGNSi1h$;HPv`h+NEO;eLL7y(9Q-@V_SBf}PrR-^c0IF96&_q>i_yYM7X26nS z8e7IRs0_w`elZ8y8UHHAhZSGW^i3wAzd+^dbX!Yg-{-XX5CZcgf6~P{h~wEA!a4aQpim5PKZ-_Z39g0r5h3&=)Oc1#R=>ML#eXxt@vA^F+?- zC7hBP7sw6npUtMYj*k_ciZZ++2ezXu&GOiyf(`@O^f|u3$WC)n>{0FX9M$5gI~pVy{W` ziFy@JU!g4Pp&oi_2Iym+-9j=A$*0`vS?1>IqhnPDVh9=OA}bjkD5fBc)1(i1QAQ*q zXmU#|jKb}il_|Trzk~BC0^jkQgIB43GH)JvZmoNdN3yI{+vWB_!DdmV$bu`p*WAOy0 zPto|y3CrZK0!x%xhD~D;-v!xHJSb#V6F>*&Y=Z^Z`q;K1K&%UwY(%WtLE#PvU=LI} z5^#hx+hJ|yufUmp1rB?La9tx@?icOSTC-jrQrf~fGh7l?KRQ6&_(ZwDJe7U!)_v+B zM1PCUJo+MLN$>>g4tlz&pgyQdOT!$W@CrP8w~tyMR&N+9&}~-Mqg2gKLW1-hZt7jb zi)VVuIQ^9NhO2O!RgLj_k6`mdp(`*35k2iHTSpdact_4s7?yz>$!ekiQ(J4>}x5cI}je%!JHZK`U{23h1WOr36mWQDsss7e!fG=C>fLY?$<@7J=UhPejP=ek(Z)%$jKPI|hFlRu3$=I6u_F~agokC|q0Ol16Cr~y<%cwxZ98Zk1^yAw~HOlg-+Kf+N z0T;UZS{uqn8cUK%JLjVhNmV2DFI9q5wFJ&~Vv7|UG{xj9tTVglLczNwtD^E7_BUFr ze}GWMq5RR;X6|T)$kSN;Keu&@!$#>bxFMPai*nLjIn-sX+DErR+zjD4dZ(+t3ayhQ z>Pg1)*79C%=-iG(h&;7g8ZKLlKaZT#8o5=F0VGMk(gcNi`_l98+blms+C2oJ5{T`F z)=Jcf1|-olllERMzruaGcnCMsO(;c3|1+KfLe*g(LK~b4Clndp~PwJmfkd(!pgaQCcK#$#}SOXlzAIrMD>R4xX zhSW(Zlj1nUECM=RX}?~$b1SKXSWD|D$U+$^>(Mle@{ZiA(MH&xszX5mK&%*(w=|`W zzd4#xCg7FX<3j8C)egc(q`L?K$E72|qPT~TS(>bOO6h3$fL2{; z7|JN4lxGGnGrGkLZTkp68ZxIf}i15PkA_U8=C7ZLHw}gekTJ zI>dt(`(`RNW=Y+=gErSoB#pWzV ze{}&a%e9Vnc+GXnwk{S{3i(aF(ac83An(o%)9g{K>%nL$c9ENAz!tvzS3S-E@9kL)7yIuvXLP0$ zu0U3jlUVQxY}Xh0?;F=w@H&DRA29kDCP4BSICOURH2s<+ces4jOc%vuE3oMj@zcJIsm0$z8Nabb4oEEnTb#jYNfi(X4 zvN1LQ{o1l)b9Ut#zxiR_P?;BP#fPr9{c6z0XnSCD1JRU$im`ZmNin8TjBte7SSkJ| zKSDl%brJEkXg&DbbX%tXt7VvK|7_4eYG*Zb{$ z?CM+6jp`u!Jbhk^7Z@a}5%o?`zyZ9qq-yL9hV=;eZc_jkBcixu3zgmi$p)03^dKj% zbD{#`@2*n9W5mo3pj=8duj>$&P+&1Bjl9tC?-?Tr%h|qf4l__8PjwSTGR$#B; z1xm8oJcC$fhzMdQA!lU_+VPKcp2rY^7{+YyAP0^Oc#X)_tQtv=YRQBW`r$lswe_0v zb%gZ1k(zly*D7=6`pXn^InyNDki-8ew1X@FLNdy5`~TF@{=!CaEbFl=NlnNTTZ^9Abz zJRspcPn)4-u7+F-?t{&ovFU|&TUNfj>FhAushk*XBB(Gl;!^qW9hq+)t1&7NdI@{Z zT-$*FMYFHNFWg0@7i~G_n!lISeqOp5#u`1iql}N>;@5X{QntB+jd8a!#bt^s-c1Ti zBWEqi(hg})jFTX_WiI$MqOVL7DA1*S5CF+UMQPem>WdWfLQ=dTe_xXyT`j=Y&Nc{r zux%N~S#z1NQ4fkN^AwCSw_uw9mjZnVG$@c4Afq9Fx=qa>1@n+MLAU4>^rtxf;29lD zw$>VTqwn5Oz7*V~lPhhn-oZbJX;#MJ^*$Y}dKC)=&>$SPra!#zHDKFHb{UkfV#Pvd znXb|=^pcB9h&G#KG)vmb^KIu$)0+ng*fZm}Hjk&0rWC8O&8%@0b+LUb&s)56@rp`1 zQ=@|vThFQ8Kd3XlYHAbqi3wYMIw!n}#AsBX9~n zIdS88Lp_7`+5L`SdVXe;)jrg!d?t$MBAp9wiDsURBUlP8H^fvG2+%IYSSPdwVvFg= zSs%c>jNQj%yc6^kmq)s$`w0Eg{l803z7R`Eq=3a=GgQHI@ox)onp{h8g-PVK%)Bm- z{hB9o1YU_6RN~|tpcZk@+rcY|J)24SbBiMcZa3b{#B(9>edoBYh>1}8?oD6h8v*L;!xOnd#~h~3QKe<&C#Ctsnc$e%oEzl-#I80SZgp^(kv zibN#^SZA6F!kMiu0!Z=}PaMU6)NwGGVF~o&2;22Gws_xAW#wBX<{y(y-N@X!%)c7P z8Pf~_cBD7moxL76w>FBrKmPow{^oXF83IAagYsPtfvmH{e>TLEon*Hmgo#j}FbmO6 zxqpr@3*n|BnU<`z-@&35hSFPa6bfbz$PwWPq`x!8cSnS2i2G^)lo*51{&RVp3l#>=R7ol4&q%#6^FpJn1fRt z)ax)G6%1Cj6)94y)fYz_!Yo!8cT%&Nla<&%rxlozC8aLnwbDc=e0?=`A`gC9^$r-B z#0TnrFk+DjdUysUf1YI+TwdnpH~~QjM1|4D@d|AiO2vqdglDDfnIh6GMkIQ#Si5J&h{&Q z{W@9VfZGXnIJS(JRB(HC z7An%{3xf|528>qR2r<&IkI$kbBnF8kq{AX$q;KL(EVwj;o>u3b)oXy;@peEr3|7nY zV{ykq^g_xw4nPvCfZK^bk)XLsrP(%%#1XZWDYv)VM&OI1?fF|=ZxU9vWzE_ca4!$# zG-QsJAQ$FdyWvgyOIX}fw3{dqywh;NQDJ0wnUR-pA!8)Su1%ZfOlD%*cHD|Bt!Eq za%z+(Y^h8)A!e7CEdSJRsdn@hwc6A;0a~Q-_*S9zEX%|eo;Wq=Ha!Xi7+Vt}oHI)^ zi^2$HFesF#D>^MLnxK0be-2Ld6E2Z{>4#hq=QxT{BF|AvMqiMc3kHik2TLyFXBWz3Hod6+jrV&Gf0mE zl)+1Shfk-SvGa^=rc7|lm@^5Ll!1DEHF!h6{GEPYiorV>G5=1=Fs^e9g_+umAO!W- z!61erj|*9{RQFnqy9Z#rBI0}M^@y-osO^7f7C$?gcEEgcT9>CRvzWW73)>y1R}5Pr zzbuN)z5d1Es7eR^VFmJ-9TIkvB|G3^-PubCCUVxF{o>% zNeU$~Nf;?nsbiyL08_6T4sowROfsH;XlW6*GDk6bkxXp$nv;tjGfy!fcwx`9OTvO5 z1pqw{;f%x(MEQja=sgaA$RMZB}5Ja z|6KyyPVRC1j+EVw3%$P+>h?uY6y=SsGz7I_)BW6QJTZxfc8)n|He!uDlFzdFFcb(y z@+PN5D5PZ=`9r;qVtTORodHbI!BK+qDsJmv#E=)*9-t42a(>76$ky)hIcfy4?z3U-Z_2yoQ7V?P-> z2l}Zr`Jbe-U|=(!F&m-WA0CmRiPErICH^j!#G&uALKjr*%%W}FvD%L<{&;9*r8M#k zT7{P$;iX+tgWCS$MpJvr9PZI_+eAqfOy||?i`IOfG~YQrb?Uw0b@P=L#Nawb2=B6Z zjBoFk4ixBWXr~_t(p>H-bJ~^lPckrDzb$r(Mb$9#CxI6<_n%J^-H?m09yJ%_om;r^I)L*1<;(9VadSDI(x{<$ z?#R`^=y;;g$y+;Ba>Tu z2|6}_wTgZ#cj0l87&&LbDTF(TAg8rNOf@BGQda(iFtJL;SSfkPU#>iNcm8LTBqedx z$jEL19r3r}@I%8E#TXD|x$l!`B%RkbM3 z$c>dycx>QCp)ANb5nhnaO0y^|-VJ(o_l6O$jZf?P=u0F|3524#^N;Kq*vq+<9stJ%0lYXk$5O(!wYmlMvpGsZnvngJ+~o?V9bBrd6tU zb`QqCH<2bwmDS%&TQK#rGrzaqp>C5PtsN4NaN2Z6^0wRK!75s4^iHKK@nmK~Z(N)U zypiRoj^$&iQigDzS2J9eCTLTXrw7nhB11Z8q!)rHV?ie8R$D&)#<>fsykmExF`@>y z46ewCI1iuFzB7P$7Kta~bZ@^HXFkd{j`Uwh4$DHR$rw>l$z-OKB1)oa0}-Z;D36;x zx0D-v7A~*Bf3^)v#ygwSUoxc1T#2gZyYaK|S}_Ui0(H)+x?j?gpCdO)5`dvLpVhH` z8g$q0Ak(oNxRrL`sW~*kc)#RQY*D5W8dRjPI7W4^1k9%>YVSrBoD(b!-Yv?P>9b@Z zUmVCP$|yqp2o7)yGlabu35Gq->6DBtE*Y zm!TI9sa)heSN}+F6j7ZvHMAUQ#-*$3dZ~DCs?AD_Zk`s`2py;1=GRpGtZ9aOQRs0C zd)FOzk>@?F2=6|NFR_nP@ia4GVtuak>oSFhog-`4{w-Bym-N*WtgiueKhrnI=eYeB zg2P5nvA(G9j+HF|hC2FAnW@Te=kW_@9?`?hfkwZE|G+kD$xZZfCU6j2C00eo(Id=xZH;RC_mBc8P1$kW$7m7`DW1xVfR)0PlFqv}+Vps)t)OcQ=;Zx#sDD zOgAgm>&LW+7ZrqOZBoMY;v9a?=-7pG2#IXcxrmZl7}*8edec+UqXuNt8T^nF@_U`% z4Z0+zTKES&VI#MQ*>V$NSNoc77`}a!uW=p?vbPjGMQ@B26WzTRl?b+&H0zKj)Qt+r zE|;yrGBJukQ$SN2<(jkF;qNtUA#l*x zIA*6k!PMAuw885lCc>7C6|oNFDvJJte;SPSP8*D3q z2wFzZ%%laz2ouyKFd71n0#1|A!8tuSK}ahY1b{eC7UEn4DXx^G-Wjq2b}xaKOsO#!zDRhZpMm5QOMPzw;0-B={d%?MSeRO%d#dtGCSwz`7^vh?R zvAZCwMtK4&vnQolRhfjH!d5~l9ynWKCjZc;MnkloJ%ThW9g}!-W#!-zGuTe}ahV?z zf+0L%W<!$V(IWv2YL!?m;4*lbzAI=Kv zVO9!xUh0!R8!Lu5t&rfJk^@H<9c#8!6pff-Jl&ZKyuE|Hy7Fml#`p%Bv(v?!mP~Ia z+Miv;kQl+uZ0iZYTM!+u5D-~ed2w0s|bD#4Y#QaC^wu!*izv4{eMP=kW8 zv%5vLP80ah{AU5dki1VhqwJ71^`oXJ6aaQwmAc+p7bi&@@uCv44I1)c3px83($;(% zkO@-f(B95jeYd(0=MdCxl6WaG{c`;^g;;$7RguHIbI2u{ zahMifI^IdUZC+lj%$6E81%={DO?C>C7_pYvuD_&8bx_H7g(*@8#5E?&w8(16t9qZLA}>J0)ZLzKvSBGLm0w`R$N5E)FA_2!vLo(Xy`|e$`&0yE5D1jc=$blW@s>Xp1wfsu@r7<;Gx)#3_;vl z?5*Jp+RZHX|8}9>oKfyR$^-yC-Ryt5!sgcmje^$!%K>aS_$?^(rX$%s6LL=2L$ay% zzIkICAPo_96->P%>h4*dUtn)TcnJB!+hdD>iG;Z$ztD+X0lUi*$Xrn+sP!R3pR?Wq z{}Cie!VpLwVoe)(aSOqjG)(p2&D#%i}ll?1Mv3EkDpAnOq-Lf*mOdq zv9SFu#=Y`|ZLTZJ$Q$>p4Lblu98#5~k^E6rGp9XKCK1kqSGM|$nJo(2P}3hdXe0fI zW_QP&fiog$YnLv2nMb_!7ik@%?rPQj@Hnx`#s?Vri_$Cds=jf62`~qV7=wpO0o4TF zF9PgwR;V;qKU`=ru86>vAT(9!uqT(}6Y?U+V4qVhCaXl=m)P!;Rtw&aQ6@mS)Hzbb zxmEgrbNckU=btTzq`PEg{^z2Q_8-%De*fu2Q`w6hNwoya8eQ`XKGr~$wu|Z-4 zILXzh*ofD(u-Hg9p?=a^aR}*GWXV6r$MQBjg!+^`l>3e!_voyDQfq-?8y-Pya^h-1 zQi;6~RE0V<`%~?N`%~M_dR5)pDhRqM_APC^v;|1J`^s&X=XMh}y(Ie;Tl?o*h`mY( zxT*IrSX7Yjv;lIm$}J@vM3x*iNVst@5r1dF&%%!uI=T~Q|B+7sSqdMQg|8(<4+AYG zH4T`Xm3v%_Kh3)~Q&~~4aJ|V3rpdsnN4hNkWiB>#ZXq>RVGlr^80?h8$EYEbDP0b9 z=0s9+Xx^3OcO36H9$kXrt7~jmf9m13Ak~H%2@=(Y`t_BGp&j_D6s5EjTucz)TwXguFQ^R0b2B#+`Hw{Q5 zy&wxy$d_nIPah(IY!Ih*QP9R-*+-e^Tg|X0^lXwK^uGR5o0q}@39*(nhV+oaSzh&( zfM)r%JpZfrk28 zT~liD<@jly%{n2cwJW$wLQ{{}|76e+5bvE{7x&+ryWR~>%QzAw%yqU_O(V5*j&n~2 zY5aT%0>9PpPY{oWRu&K%*_xlOEhae_P?so)BJP~D)Jd(IoTuFM`m>cjTtO+dayM#r z))|ErzcNU5rWBw6F!->H=EL+MtNOr+InK)LyJ6RF=RsLA{YAr;PBvlSU z%$`dMNzR;I1F8GH`&h@>b#Qi=zhbwLS#Xv8U^4At9_yCuv?;mKUvXNVYIebt>enW~ zk~w!~V&Gys`bvIm9AvLF95iM(e_0Zx)5{{613IB?8w&{{xHLmS&wRqVby}95sV%P6 z)FPOklwYLkC~;!)ht{e8d|tigK>M1$5oA5~3qV^H^=-H81WF<>)pmr!Dk`Ah0RWmRBS#RYqcL<_)Y#C@dgN z?un<{qe@i81Wu`)|G`3WEmr=|6N<^@Bafx$vPJ}E)dA7O2u&I{@nR1^NpyO<`Ee_* z>J#L~0icY!VmkKQp|5yY&fQ_(VSyla*@PU*(P*=hrrNBVNTGet82l>6u= zPtMfeG=1I}GN$oHWdt2+)itBn`Bi3v0oGRxT)u#aG=jzFZ5XPx(d8#B6geCA$(Qr@ zwG&TIMm{dd=_^_>pK;vRZ5j@;3Rr?!0?#hI<7TDKZ|ad;OZMq{G|GjSY^89CV43eN zw0p&KC9`zRQB(1p<7S<8QPlCqRz)S`4xSKG#+y=GEFK!C=G2CN8C-5hWjtJx>oPfL zxm%LvT%wol5^i8_)9%a)miSOYk%;GeqzH}2UlDDFojIZ>8XKyy=*>f}s4*ME%}95I zvOdAig^0q)mDx!i{z|vRI=<)7XB9R;p%?K!T_pvvmhnzDaXkGccP8F^$6T+G-pm&@hog%h$?11lL0n{c|A1h>QOQf2jeGAui&apQ_u%OP-NA~O6KkYl- z?Zezy)YDo8EvOeHNqAOEvbBK4%(qy$w3>O&`f>PBD| z2jQ=!^mp`roH{p;bx4d9H=Hj7m>ccl?zaubSY>i>HDWt8)yKkozdI!qnB=LtEq^U! z2^NP#@jxtA+b>kEeah>wtp$u2Hm=lugpw0Cw*C7L25baw(E)@OM(n#*>^-#Im)38> zS!Dnbu6$77vymM@6Ml%Etd;G5CC=D``ace3GZJNzefo}gyVow6Ncc5 zlk71rn+xF(e|n;D@BD|+4@|$~cA{Lz34oVLx)`%Pue(liI6t3GHsF2{x|JKkgWw3Z zR_(>aNTM(24NS(hQBHB=yxj2#7epYS6j1!A8J3#TRVjzuLk1_*LA`Ts-Zq;Bx~Jka zqc`Q0-a^0!QwB`6R-mfn+-!PhyBjU9G>Z+}P#%~}0{XyPX{k&D{{rBz7@IVY&eDnM z=N*M$r6AoQ$KZ(omv6fq3YCyosI(eRM@Y=UxCj4DV&Ep%LXe?{KDiJ?orzP2^4?P+ zVPOShMNeY4&17VvhDhxRE7#nlRbPY=>DaZP9aa0)vJYM`^Ir~ z3j3;aXd~(gUFKCsHYfOIu$8C7m6%XuHRAa8Mqh1p<-nOG_m2M!lEI#En$-W>zxeeX zD{4bA~uh@p5tb|AtS#eLy)U&D#O7TB4-p z4SvZJK4?>tMhlcxqTfgyCDqAMlpM)0`=pL%%*jz;nFmKvL>DLV2yk<$TBZHuBp%J^ zQvLm}(nTzd&-keyv&zYT3{$}UcfXDQ&-{kj|^LLjdHk5_#hE6v5X6=+Jnu z_{DmOaQ11CuoJ8YOJaHg{0bc6;_)hWgCndM5EB%&UZo>Ev<^JjL-Zhtkj21U;Yw5{ zM!+e;3h8Qcl9nEu;1UJtQQ}?Kc60`U9KaS0BEyS_10p1r#R(lA9Uy~0(l$AY^rpr_ z=1O9R5UjU9@N%v}FWBhojPN{k=vPF|E9O0ZH5+24(Ao`A^!_1zTJsE~QTmLQF(gsb zMhwR0sGp`F$D**7qD^`NhBS*SvtAmx&kfKU+)ReN8zdbX#0OxT#v zaN>5<4K&@?u%asxhTi7G*?Xmp)F`Ex%vC}0QR?>1l-0x|h#`KQdC`~<{_P3S1u7xZ zhAvCPg~@YHl32-db*U&N#xwOP@r|c)l7k8u3lXy5ZX?lU)#MX;busZYpsQ9S(iaH+ zEWS6-9o{!vo-I|*YP~2D(e#U#Vd+fojz|R}CS-V8(HJTxQmyV^%MuW(2u)RT=?gpK z+)wj}W2tI}CIdxJ@VyaAjGS@`iLtMC3Z|%=3aLHH%L+(MOx~Tck{fMtF^3cw<-2S< z#xNC5+}ZWe!8-ZJcV2Ni(+w>rG`=;!Zc&8n)O{vPe-b`Ab zgec6DRUAEucEV70Dm*+$kPXD142o1CCc_0%u_TQ zWCC~n3{^20{#x2s63SH3LK(EI(i~-(oL1#7GYaV~$Bei8U*O}_;>x^}(aN&ldw`Cn zdV+US=W01L591Tt#_2}N$@Jug8&FuE@!#dS111VQ{@7@soQxRO5uC>yMkJ@h2Y6@a z36p9bu@{k^lM{lm3)3OV^(AQ)-U&o^f)Lab@QydXW2}j6kwvi260|Jedg8n_6dDG` z8=N_VpS~bRUUJZCEHP^CK({j^=`!Km`yisyJc1ql&L^(MX};hT&vN}F)&3TS;za`a_|&29(Vv^lcwnC6K1 zuuhX#9b4RXCy2+UnbX}} zA3_sQD8Nb(74)aAdQboyeJo$%e|s zDP#LSZOto_m#yo}QS&gH*5%=FMnNrqkC>uosHwzf7C4v}6Eq zX0Q1Me-psqJZ_D{xiKT|P?)(2Lpqqel&^fH1<7$$O!4k^FD$-7z`889FZv4JHoc?_ z{tWtr^MnUR+_r+!z7uzo-vtfghYui%GQZvy4l3euQQt+~pQMGD-eob|xx*}t526w2 zEXoC@a=QudE5n@ZYFfNDC%k8bptx!8b0Y32pZAo6+@S4(xfT549gJPPH4OiQnv2V; zI50!(lb46uvv^DSp|Q_8ezoIqTO4BYA@ljx1?D?@EBYb6KZEFtFsh3Jo$jo^agH#1 zF`&Ot4udgQih(i;CCtGgC8_5kICy@Gu_cnb@JMbzx0!bwtm)++X8ewc-A&9@w{&UF z)DAMtY(?LU(Vd0^{Wi^*pH1Yzxr28Jj^#4R078R3UAIUa3@!1pftpNZ4ldO01z@O2 zw|rD!rMsx|o;2pul#$NlGsaNlV@`+Nm4jG7`>)py}qF7??L@h50_1TQDDv_CmG>C~bYA61Ye2Xx9?gBh%!-)>T zDAg7t4@exipJC|I&b4J`D+}=mvaQ&maY40`#x1$7bzF)Orq16nf<{NZp*n%z%8pKF zm5537MY4NFXD#YKxMQv&py~{&?HDRnh{~E{L!}P*7(bs98V~{$jCiw|n5HO zj^eG%=0P||KgIUrWtqT|0Hv;E{mFYM+- zubd=dpFvX`{!=BXErnKcVF0=vZ(x$)()|0yWqydN8J%A944bjh_S_)mR)Ss??I>TM z*VO^(yx(7pQ=pt`9%-N|Pm73CEsnkS|)Y8c@Zpox{`g&38_fnqwWcKae z=U~OScsTNdvwcYEaqtsAt~~U{Sf-eTE{;|Ar2+W}U}i_TcT)vsh2kySlMyRV=9pgL zg|)Mkwx)Js{+OobH-fhoPPDe zpFvKM;G8bALrI=wCOduh#`julj`*aFD7i_iZ7Da;3q@ICHaDy3FgSmuiKSqjTpI|- z-o8dl-e60wVzk^|hMVuF_5;GOeqdD7*^IszsXSVAHz)Qj`hqtv2V=&2!2lnIyfw}2 zzeYwoh*wT*m&m9b<}pYa@iftJs2SwdetXoMZ?KL$W@@bFxlUnBc8~Cg`U8a0BB|`i z!{U|M*#Jz|q;Tr@mr;tmpytl;tVj42-Pu_)7{e*YXa0?*w`qH_I?5*cHb?3vXtboD zeeUG1$~Gg#+{@w$aQ94rYk_01bo9~Qxs10_mrVK6$FLjje=0^iR^JrUOq#L2p2{K3 zrA7BGma}G%U33z6^w_uCtSLb;+Z=7$j4{S%xE|)y5ER~So2$$;^OD@qbNNVt*k7+w zzNQ`b3lB-(A&zWWsq(Fc!@5$fTjs#AWCli0#{5~b&XDx4M|b+dW%VXUKz&Yt)YHm! zcBdoYOu@DGD_Qg7EO}g$14zEHtH{fI~h4JpOC=X^c*pQWQgs*L)&O)~}l-zz5ywkng zxi29cCTNSJD|W*%#MFVwA&<01unphzy^6RlTRJS}6P?r=c%ULJ`G*TC%@-PEv?Cy8 zwFMqK|2~~mk6}bJBp8uAofIiWdm!@Al`1J-s9IZ%a=6d&m!TR!t?XAB52G{LI)shq z6qvSg5Skro!%8*ar1HX+*NR1#1u7w|dF7r=IE_2y@@Fs-2PO`N2(PMmdq|Q)Ed8@I z&<3T?n$;T7d}60l_>qx>8s>vs!I#3M(tbD=b%YFRc#b=)Jf$4^!jYaC=q@g6O8CbR zE}|Am(GJ7p7Rea4nh)eEd&C{!gwejput`WIt@)@Z9W^ho+MMT*y*OI&obp|MT|<2- zbYVZOS)WX*I={Oj(SYX2ik^W+PwIS()PbVM#@1|c?{^mC9f2(d(i*Q zo2{S)Z>dTc$|0w>f7wbn=3tTy^$l*fl81_?QV4f|fEj1J4Q>JZx*>7%C^W4AVB2Sr ztnX-T$y;rIT>HG&>u%3TXWBH#&oV9Bk{)*@FKIPBWQLBGwO+%vA8|9lWOF=hYxr_A zCet45dLY{kxT6-%^#EcskkyWMd+<}{T4aFnip>jgBSJznSp}ISPM?rfk-T;)YF*;QCu+2z7u5p%TIO5ftpg-}WrQKGnS9d>sZRqyo9bgO%8z zpMp|n`U=&74z#jJ?SP-8mBH-0HIkPP?b<}B zu`C=U9k5TE*c_#G#JBT=89d`n155+FeoaW+lem>z-b>ye$l=uA{mO8`uOVu2g)3e* ztIR~Cj)7A8)GAf6Jh+YAuHZJ92`})J(ZcsIH^i!&m#%F?&=w&BkY(_cMTn9$M99Iv zLT}44{PxJHI#+h+84d6WpLI)z_X)n`7V+yADeb-%@$l_?3DE6q6+i3G%pUs;G?x*_j4Al{h~Lz!U-r{ z;&LnqjLw`LZ_9X8(D6m1XO$n+KRFthc>86)f8Z752y>FkwsJsf@$LH$%y%Y&YEJ%v z_}Bk2FNg2n!F)k0TW2FNN9!Nn=)cfDk)WBep{0txm9w$j|0KlzYXyi_lvKo4M*fM^krGxh5|%nL_kgpR|frx1`b0*%|l@eo)OPF9|pg~c)PFpz~)uH3|M-YeKF)KZg2 zGW0S?1}~CVZNCM3Mbc`uKbyYa#3k~hQG0a%}v8rEavx&%;BVu2$p~`@7&caXhyFZ=XZwgMw2^o9h&FjjW>UDo4K#R}s z+ME}zk(uNxjj6J`0(ZXi$$jRM(N5=}ybQ%k#uv-3 zy+5=zY0d-peaqT_&hYgE=DQfls^YzPP^K)+Ce8`H%9me8G znmu>|KnrpaJ`-272aBrn4D0Zo5HDAqUBk$EAjLb)faaeeVa0VHv*jfk3oOpU44yJG zEnJx8v9dX0#@fn`Js=xw<$Qt8d&(i+x7Q|VT9sck;`YM_#_%2U+X@u5z?Zbd=eo{{ z-0Vw%Qz6Daq%BUb8deUinh9v`^yP=aZyBuxI~By0%jMPC&>OrkQ(6>4r5JT;)EXI9 z>XG*=ME5|Zm@uJG3C63H3YaPA-+K;L3V9*HS@Zun-xgSCd<4pf0Rxd6BPc;ww%2a(hZtAMeyL@;_#`3%X z;SgTqPe^+G0KG5l9}`l`C0PO+jDEo6zd7LA>i;Kk^%1{%R@BYk=0LbTkoX;)^_0eD zp0ri+<(AqzLid#ML*&na>pP&Y-Iwfp0`z-C%NovgBN+R#fhDyYth&&@9Ao!7-)q}H zjxpsQP5i&73~2t{f64z)21NgS61H)2`0pGpg$)^Get7PwYX&-4wD?G>2!a-4`bQ{R zblj|9U@d@#_VBy17V|v&X411WeD&{$P#6djA3r?f${g{ij8ew*^7D*yj*|DsmK#@H zfXjRY18B(u@Y~VM5r7myrHRSJUz7mLfR&6=rsEdzPRUbF7TY*Y=&SStLRlG;5C`o9 zW6Cf`1Lzcg7X9+JQ_TNJ+E+dBlt9Nu-X>#S(k~ts&4%b73I3JPg32LJ*SrGNqPTFA z67gMBU=wbNZ1RpC`XTRydm>zK|GzYx=NN z;v()E;fo{k!NI?l$Bs`TO#3Zqh|)iA6kMX-Ir%uHpYwC=pq0-w1TuP55bRfg53)fvA!QBNL9iLO50m4rxsUhH z15&?|(y9Egjcbj2xqh73&Laku@zxP|^gN^-mmrq#1Taooj8)2zHZgI-wnm&Bll-ur zPl1LToKKVi%dMjyKRL$n2>}k;^Ox-eQU&b8E8SnWK?}7 z$WGLOX3zE@({QOv$B?UcC5&(V*;6`X0%sv3$||9Vu1a6BM;=}&a)t#Imi=^m*Sc-9 zt|K7aVl9^eagY<=XHN}pyS2xICgozoy5v?3s1tW?2l7Qn#o;oB@XKq+4MkDo%J))( zsS#o&uh+B(gx&5~%2o-+3<0|*h`=7gqFMRzXoQeFmV8s8tg}Jb0iZJBN>BmLj3gJ5 z5PjzeP$ivFnrI76wFr8qbENkTmBh4Y);TvaQ*8(d!m$-BoXS~?WHb5u)^G-sUOLGd z?9ShI5dif>zCpQQG)M+HotXMsh4Wr^kjq|f5n2zN_qvbpTUm@91N;gyl7 znY!_Ry#U^SIQd!2Z@!*9I|L7bDl}21ngh@^!1$ivjzjlH-tAA`e+Fj}XIN(UXK-Zx zW8fO~za5g9m^J5)OjbD~eyePztHfZ?fmDavMEMFUuj2sWvun%mU;&R*OtwPL*m zvjP3XDIffS{%8ot>!<_fmFMJjJ}`Z4Gq{6dJ$fth*G+$I%74K^s!4xEL;AMZ-;w(0 z$bZEH{aWbtZQC30#d^hKUEcFuI)DK>*VP zDx)h(;4?rsNT^e4n~PADwpaX>o`;JoSPoNWe?XC5)NP8TU^%KKc%06#Lk(q==dqYx zFSe&!cuLE1Xu#@W6izOQ5|KJS#-K<;Z`4Lki99NysDe?y9({3FN&W;`-j^hzm?5QZ zDA7P}DSemjP)2?srEWX!xmnLo!+7W*SWRbC(2Eo{R2jsJDM3V z7)Q&52??jk(}W%lQ}z-v&~3-1|F+J&X<}d%K1fhz9dQ31!s9=IhgS>CxIsuynTE@5 zMC>6Dny+QQbffwgmr_Jw*@m)dgoDJa)Rq^U1Myy1kZ31cB5vXs=nK@7-aL3AT`Zg2TR$Wl7Qbe!lQ;Pw|*^7dOV4uSC_s}m@bk@bb-8r(whtu z!jhQQ6D^9ttZ?$XnJyQY8Kc0wIK&EN@=Rh-QALe>8$}{Wq%tX)F36|UZn2{~%v{ov zIatLUX*EJoQL~(n!E)YR*p20~L@h+ieAESSl?{^%Ub0xAu6Bui)Q*z8QfKb2jJyf5 zyq!?+M~aZGr*zf*tCL!K!LmL&2t#yWEPmOHsBl3qUXUd_Ldg5ejmZMbv;kKCMaGgz zKNtf9D4=NwzeNhgGBaAcg%w9qSfsLBjG>>-CVFVUEDovr!LhbpO6hdNWMWiRbti71 znK}ix4lx5BzAq^nYOW-E(v}TAXD!8P8qYByav$KN_O*L10$ko zqTLJ#gtTf`fp@YWR;2PN4qA{#Dguq^2ts|@0Bull!L1CXg|Qh*6-SUZn|f|>JuPOW z%&9Es3{!yA5oy|UwVsGGNvp}v+NWNYfZ%z!XoBC*HVEn3ID$X<~x~!Yv zc+R$_O#zWZlb$g@k`x7s_Zw!7ny9le9F{JaY^ z&A3={xwy(dlj<(0Tigvj8t;d}mh!C+UMO5v_V@*>Uq<=e|A-1Wj0VE(1|F*KzC?+u zJ$zJo`)|hteob%d&20O`1{u?iFqt-Wlr)t@*KtE2pPs48TlhL(Z*5^hU)o+lIDt0M zb@lPVJunG27Zfm}TV0twjQY_Hrb_+M41t!h_bJV0-ksX zCl#%#jVT+`Iz0}^_X7HhyTTsgPDDW%U@ZK02&N{rkBc!Bh}$NQsJO52ug~DO`cOqx)B5IFD_?%fD~=X!}-AB9W*Kh z2+0y;A4btN$jE{wKT(XuRt$G**z70i&Lo~QAi2oGks1{3ET7(J@*cHM8egd3cBt4K zE~B{6F_oJ1NH-}=H9eLNczWFYbn7K-IdFuYB}mlDL4^reT0rJKs;gAyT7!tQI{fTk zy)_o?eb1_!$y}@{<2Fn9vs}uWA{84mjO1i`Qp%Wke5>kjVQB-62%;I;kU#3(Wz5A5FJcsn5N} zt?3lkSdfgfR!}a}dfz_@a@x&6VuNV8I?m#DgoKf~r=IgjYu0Tq^jF{h*)&q(g79=H zdr|FGvjH;O`jyv7;o@XK;;qrQTxj$3=3-_y=?R|-v|8vqa$gwHhKPvb$m3dE$%zK@ z>s16%LRpr*iS-=gj}{+3=TaT1!yQvbaeisx&m|`uPBHOQT94=WlSCc*>L$X$`AukL`=_LRzQDfH$1T>&00H3Q^!b^9vLjLLCsOES@OE1flED7iC&K8?O zDU@-@o(kJI39ZHOHAQ~>CbLqfox2r@vD6OR)GQaYp?=DCUxEs-#36Eb2G;6~2ZiFe zi6+I!%%P*uffh#;RQKI5$N?s^_2)#pJf{*C6j#*`j{uHJ-p^@TO#4=*Zuj5F)|q0h zu-wPfMYGn8>#?^ZeF@=(PIG6ZxeGOR;>J{GjQAEe0nW;Wt<_ze>NhHbHlePme1$1x zOogXSZI_A~hsTNZ4r;u412*&t<*oA{Xl-pkRG|d>``cnv@roXQ{cO`DXb7{N<7id$ zb6ye<9ZZ^2#B&9}65PoczegHfOCD9-ylYXblxfqf#;(C8AI6bpg&8Mux8$VnTEkS~ z;KHsz61ku&y(NyMDPO$X8*_%TH8eqBvsga&aB-)i$qf_z8-UTGJq_=J7rJ;s)U1|7 z><-DoV-zHyUQnD0V-8*9A$e3I7f5e(BH+yFx8K7&fCTxIe1Q$lkAClpOwqgOr7_xW zw>{4R@3sVjx33u2#9J2g!?iVqLSwWT9Lp*) zIhSkuB{rqXJuWGfwGtr9Dp>=f^eHYW^7^&kY5{y={xMB~Px_+gFL*S2X$1X@TIwKHH8h^!82Gdzx7$?1$Ts_IoGB*xTQD z`QCXg$+{B7| zjlJ)kJ(TYb61V&I45PdI6nD+(JNc}ryYcNwOPvbhGX2m47c+mZ=l6Z7CZl~?Kzr|> z=A|q(-0t>0ys;^RUAPNl%!dr&RRMoQZieh}wjiD-sW8`@Hl~W)*R%5(?QO+e-K$@w zt3UM?RigLn^e+xkTk`ff--EX~AqaOlXr7_1NjiqiGkQ!z8VdDR9jt0xGh+-PM^fbc zD=hdUE+{p%@AHM$a=`)x-Yt{z+S1iI@)wytbSDn)pnds9e^<}m>kV7;iq8KjI+@Oa zp4P|DHklC+&pO7wH?z34W6sypFY5{??igUcrdNDtpWinDobaq=)$e-3;=*~4e3{+7 z*5Hn$mQ?c?gtN}Oa1XV?tCSs@lU64;w)Br(os5#QL~w&t$e+iUl-U%Ubts=}z2I2n zcgy}B71w&<1)w?xl3B+v%&tBd@%ss>3KY`%g`z)m;oQJl-bzDETWR)TQ}M)!wLRlm zHd<4VTbQ-cyj+^RuMD2bRi)C_X{ML+EMJFUrZi7(F|sh2WN4VKR6ADB{eCE3)KreP+u@nhK)#QXORSWVF=Y@Ne2q{kjR2a~@3tCp2C9}v5M!V+-8Ape3j&#| z#Tl=W!86_h!iD;b==H!=w;@P$KNCOzctD8pY_KI)5*^m z^_F4_(_$ocz5#pG1X2gKX_r>2nIVa?OBg1T_{_w z9&+okZqj~n^`jm9?1xwX2>m8^ptc0rtOoZGibUt z(LDBQ8N#!(!>{T8Dw@YHvJiIn-5rn8DZGUuLY+L^J4Iwj(wHP}gpCW;LN1DkE=9ss;>go&c-EM)Vm4nGK1uYxH;n*MikjqMnDz(G zx+3z11hw9KWJ@>krIof%WMxG-I6@lIU--N($%Q-c8(T+9K=czj_Zo=&1w3jve@sGk z4xP04Fz;i+Dz=VTb}!ZTS*tGm+aJX0{SUg&?&&8&WmSiJ5B#TVA9UTc4DIdyC%lD^ ze}ZUQhaaZ^U&9bO6#s8Z>%R$R|B^gX{vmlRE`I(ornoY3H-Ls?l+ZV3p-n&{fe`77 zmmq_dKw%sx>z|NjHfBaO1KHEmvMO(ka;;rat;Dso5v`PDgsECtE3>I|t!+|iUsJnmyEK1ny{SJ$!h^B~dub3-@0BJf) zrYiyfW2XU#Bw0ji7tydz_Vy1ZnR8FZ+qtG>3E310)#Is4xN5Lzk*5V6%|J7|RY{T4 zOe$G)N!db50Ywa2elozKYaaBBHmK{SaxwgNs-ygD># z<7lr1V9VjN(m+Q@pGf}7Idp4Rhjf(J=%A{CX>OpCb4pNE(R8_UjF2AD#P$72*Wb!5 zeeG>L==0m5`@^Z1-xIWPW?bF)t zMf#y+mEd=vzQ4WDxZD+kos06*AZfsg0$Y+zTnMS>o4)@sXP`l~-qRhCk{dv2zxHQM z8!^faLgV*5rflUE0wBDH(M`CVdaBjbDTbbV&f4)NdN`u;)0yxh!G1z%l_u#K2Wz&E zZhtC0yiRb3En;qqS{&17k%T)DF@k~U$@j!6nm2WH>+G68Y4R4(!S_wiJqUbryPL=d z33-p{%gfuR_^eylCLvuT1{E`DXs^s+bXMCf!^Mb0pu0#`daOC(o-TgEj5};K*{YhU zMON(uMsCg6g5H%v%g7jJetI@t5S`YMkLtnnhUR)T+(vIRxvq^PWX z14|3jGe)71$tkgv>ldRS&`}(+y_GONqPf*H_iQ0ua`xmA9`_P-GpN_KW*)C!7Au%* z=P{E_jbNXS3T_*ET2@j_Ss@z+$Kj$QnO{*r-P%FVppCqUQEdNejx_lg98R`S!-U+U znNZ-jT>jxPJp02?dp6vyM6P*fGG60pnlEixV6E zo{!^`c1fIEpz=#^`loDTacLt3d@>jHV$*j0*B7R-=Jt?5vDe1rc-|LHOiGH?1GZ__^% z*8z38v)5P8SpyzZCx)QF0KR$Gm0cc&?@{3CC?@;SgzaPnd2D?@bkK9JcPE}FuU`L^ zLDz7S?IvcZgZNK!gagWBu{nA0p~Z=4;{xqp-4lX?DoZ!cJ^2$>l9A{+kQjnIroO<4 zH9#m9Y$GYbe170nKMtP|BpY|IS_`)+Zf#O8aaOV@4GoE^8Em(C#*9~TCyUOOr<<9C zW_rOm6Ufu$&x&aqs=Y2CN(@2%aK>ox;+8U3Db?Rl4mRFcO7&rFtGZ%=h+-&MC)LyQ z(y$xW-GA1K+vc zFX(FZKMaf_lO1g|QQJ8jeNjf9WP>kod<#hD@J@XvxGP!D8~i12-)|_3(KLJ4NHX|6RiHQypRmA}dEigMF$*$xZde79 zs3%zaeSE8GzS#*^-za`YzpbBoT&P@RD`ZDjwamU2QcKVlKDXCOq=Sug-}D>!R&c#q zo6jCn=}bB`%vI^5Xefk{!KIN8a`_!y6WyVUc#NR$;Nqq%{j;76rE7u9&@vSuQ+BGCZC z-2S1A^6y^J{6BNkA}N3B>krV%W|R2ez+b@)Y|XU1j*liYo19sz-0ZgIIvR8HXFF?a z$2VsiTN7s1uul;~+``C^+jv4D9tww)8h+p*zHahx!U>IL9V0xoxHNSWI8ZXV){_>N zE8g66#UFx*c5>S>gYnN#7!p19ltJ^1f2X$aS9JIURiAv>*Bm~-zlQ9n*cGM5XFHGBXKe@-)RCR*q8jl z#qrrf$am6!iVyT`=Rfm`0FFqSl$ASvEl*+XK)>x7KZXarZ=)SOJX-k{Z}l<87i z$t>(qnX%i99jj-CZ)Ut)+pw&~TuPzTSPnsiBe!FwIEZN=Oz<2JU7J?_*|SmIcL^hI zCcBP+Zkqc@vF`kBt9wXKm45mbm6493*|Wflz#Bfzg0yC^Ty@RF57H0GTmh?lOwS;W zb<^gfG+&iAHWq3ee83{cy}cGOZ~DVFt7p>R+!-yLPWq1%0k&7n&i>7*(Q(Vv<*E`M zlf%+2eE#mOe&;w^19eG9@8K~%4L40Y{8_KSjYSm>@cW!UT3>RJ_g?%sC)W-J&+!n| z>`dWZ3TZOSFnM_sBZsWdC}^4~Fo~JcHIyWJm%+4WNxs@r$PGVi>IU!I!CHUxesU*c zjFRSh>nJeR|H)n_J&Dlt8^*E+!ER?Q9 zT0txZp3R7vy5K58Bj_BWkV|Fi>2wq)HEF`Vsn~nHB0inBxZSWV-b9z znVfrkM&1CZy!)CYRdW7hItPYX>}268i&Jr$3O!-_4U3E+Y_Mk^o)O^{Ikega05|g@ z6t=ytHRE^lSCk*oL&Y&?sdh@`>}T=(l*fT-859;x4M&aZMg2kjhL@@!Td8bsN)I9@ zMt%DNi)M{R=M%s7lczE)YK$Qq)bS&BL&#B~Y)9JJYV}|p*!HT}`y?~zdro?BZ@ebl zpg*Q!i|$mJh3NMww31XaQd2i&SY5>aSQ(OpVGY}X3)+So`oPK2(V<4OAeEw<+_H#k z>QNDPcBLm=e*|p(lreV)43-{Keo#{Kyo?fwQX;LPUoMwxJ*exg*cA!hndRF1dB+oK zR)p2T=}k6TCUB*o>v?OVYKSLAsrX>}YMWR+gm#EIOL5C6w?z{|P1NbSPW-90C#ed9 z-}&Tnv>%rDa0Hla*bT@MZCLaAg>^e+j!xIVT(ECUE%ST>1bTD2-y2$1VnM)@R?~M+ zsjAqyts*X=A1qVL4QklKvbUeh6&C5bl=hGOXoBzW!gP^a*F}rT%_&#M4)7c)ymil& zHsOvHH?Z0avv>%35~gKvL?`Gu@LoB!R-dnQP3@?}tPIh5^Un3(1+Ih(HtYyB z?8+4>f*(@RR?@Uc_in;;mG2NF`hoKQ%m}=)`E~t~M>^t#_(-y0=yXYp6hRKP$dH(K4=ASI)J6NufBSe8q&J(NqxznH3+2w`C^%ptJT8@W*3A1_ z*7Na}ZmI6p#TcHjr_pzNqAfl=GG%QX*2U>-_yk0^!ifEvZ$gYB7YeQQJ`LkAxW!U+ zP8l!Y21`^@?@D&HNgQw(=(|9HUyC=_fp~u+gZu(HeimSc_ zXt^&gujF@6@%!@e_d=ZSmvqj5Te&n|N$s=kd8`Q(N5fOLA7OlhOdnk)oe-=+qx40K ztHq1khlZUzOkFjLC@BU=)ENwpJoKwK3P6|ir@LZqUmYb--Y7GGme)gPrONK;cEO^A z-8eIVTGc^kWy|geb~))L*0a%CsMym)#-W~D~19XKsQvEz;DDJ7(d=bP)+BPyNRpiH3Tupp+a*!FAZdYO(=vDXP| zSu=(=!OV_{*SccaOjysuW7JO$J^QBG6Ndq*v(kC9!q*auw)^Du-IUWOSa~N*{esQ- z$6UR$js*s3v-QROP~hQ`!q_&$%{(6BYOw`&MXvEE1CNpWq@%qv{;a|`_Z9cU&`#T)n%qR}E?W|FH$xA6v^nQio1(Q>d< zHMXZ4u*m>uKTpg7(0vSAeOVyNe{QTYstC4;hR(VX!YYiP6lD0!iR6dsh`#p=(nOZV zm(P}IbqE-ajC6vQeSGN%105-_P&=^% z98_#}oNzQ){qx%>(Cb&$BX|5YK%r`AW`uT<_4daNZPbfm zS5h*W0RekPoW4C8rtu*=cZfxGL)l&R%edWjT?$#6hUhdkD~TjdRz#jh3uV-Vg^VeD z+VcUOV zlKsAKjIF^BD`St2w+_6AtakNEPA$|d)m83d|B>wN2i46uEut95Tk8*mH{jMu7yTjc zOWe5A70?Q8Xr{6eH;u6GRG9*K;e)ttg1g(GLeK%umLj))Yr*Mh@w;=(qW)nsReL0C=nP)+PM z#|){eK*ketWK6?76#ni&Hf8s+mZQ|?u(3V0n~&%@+@EH|HQS*AChdOH*FIu`7m}#1 zq5*;fJj?OwdY%jNjSv;?i$huUt6+N&NLnz(wFwG=zbpGmkAtK zvs}GqG;+=zXpZ&P9YXl*wr9gch|^+bv3p&p3r}0xA^Ac_?>SB9P&?GzQSN}$bVdEv zuf+~DAMr^yaWx^?yP&An%oyi7e3Jgg1Y_Pw=u7ot+ariQa%jOabu{^wJwuE%eX~Dp)GLqnC*(hjiLA`O`Vi5-eM6)BPde-WKnMMom-P!g^M8gS$b{`|?Coq#Y@MC{ zOK(-Sk)Kz<;&VG&szSrMVua|As0&aK@tBp}$Vx}T6f7r;@;$L69;_ra(K^t3RL>o73wD%X$nT~lY%SZEt~L~LJ_#jt!|krIHlYQ4mNI{5(hP% zv^AyCW`u>Y|KhV#8EZ3H`mL$)sZV&qb@MznV(TThS+O>Kk$)2Pyx_5x2FJH&uy5pK z@uml?ZMyCcmv`5!6)1)~ zJUqv9ZM4-05$!&7zqfcH>jY6TafAjuX}CGoF~!*AcaEfuftC!RV1)j|RxD7}2oxqb zibNY0JM<@;aw*|~&uzGV$2Wo~r$23%___?g$LQwVSY-%H6S4Oz!GibcqnqsWLXzmC z8|@>FIQux_m?fZgDT;dSqtvtF^(?{8VZF_+2~mw&WZ9{fR3!w02J|}R-4}f=9%(_% z;;T-5yu(Hvec*iCKs;O-JR^6GY4^D5_%{LqJ?bV&h8M`X`F+BafoUJb3UWv&A}QZb zP()y%=IABl72nYY)?b`ZV~tXLf~HpF8oaU1;sjJgBA#Xj|1Ks zz@MMP%BYXl_mw9j*Tc|ug)Ce-C*TBsWe($K6$=go#bg@dh*&_e5MxRBrzdv}zjhJu zW#l&gLIV8nX72x$x%97Oynh|^5JfF{R6(@AMa?>=pp8v4iYb{ruCR2Vc36$2@eRsG z+Le;qMP~(74eiEmQYD`QdOPJ^3ty6szrdKgsk9*p3P;A>WcIilgOl&y*S}yr#0V}M z<9isFW(k9i7_{mthO!YZ2R$Q!31Ba+E^X|pG7ax0gYIuxB-Nc zm*WV8(IPlssoKT_9$l1#EXk9mhVh}Dj_{C@Sl-+TRTFlx4j zvTwPAgDA$LY0Z|gLC1GDCiF1_Ydz-KlV-?860I`o$wPuvhCO*%$QhL6d%W+x2z&rY0#M|$!t5mh6a_ayNpdJ;Q&t* z1UEPntreYxPMe9BI26rba4mxOPHox4>3 zw{!QuhXn+kogFOWZ-xEylmhh+Z1+lmZT7*0n1%q0YP1Il=#Ym z+a-BtC(w{SN!iXhZexP-v&}7>lY`Bz&j^^?hpuKVBThH0nA`h%bePljNoM4)T|qb} zTvab1SA5fhuy|b~gVNd^bhF7ps`IRvEZQvNV4pLB$3R|8OuTziKoT%7h<%0@hPIz8@J3`fR@@uCQO-jE{zoR&X z*d?+#30EZ(EgEn%%RrvkZvdT{Z8`!7sZzm`iU&S}Lwt~Hxx6W3 zHXeh6Z0%MqZeTwY9Qb^j#cx;=zhArD)<)(Php%r}!@Z&>gllU5^2_0wY1aZWi|WYR znRR#nK^@gpP&VDn8OFo_JHw)9QD=!%Se;+SRR9eHJVfKbl(VwLku6%YS0WzP;TWo% z-!aw^E>LtZv4PR$;$}-&FiKmTL}!m$M$nx){NS7)s<&VRsDv3}HJ#lkGyh%;?XAj| zEPP;MgLN^H!JjjXKK+1PU|K|1Ld~y2>LV9x4hmGZr799n`T8~ShSbf0JwvMS#;MSB zzF9!o&@&h8N!adeldwE+^|$?I+w?q!f$;-HSgzjQGO8vKCgI$i0cjyXK{bZp{Ff4F zRKGWp>lc`FcB6<*OLLtBr86`qU$e=}_e5d8ga__-Z$O3Fs|;NaDDMIgdvGFHc=~xl z`yRcuraxsn$4J9Ui%TfA?~8++bSOIC0_Yq(oE7(ps9-NEY{qSUK*KKA`37OpcsKQ$ z$oRzCGU_7 z4pwSw=egN)N=F<8AdDyHR>)k`tT>hcv<2-$UX`u;iM0na~@*$0hL1g5;A+ zLitcF+IHaRE`B0^4U+LI5Ma`Da4Bj{69}p`XTFStQPtrzsbc>AL#oRS=#MeT&JLv_ zB;Ur1sxg^_i#%&I?V@}J$!IArS)5eUQT5;_U2V-2&D9hqVs|PCRPxuW%=U#2 zz6=D2h~r6X8zyFBEe&~zBZ1Z1Czdm}=0>Z7=UZv|bk$ikHNHk`4OU@xU4~ocv2&wr z?=e$V?k0P7w%(^7J?xe&gTV%a5iUQY96iO^g^y2C@2TMym%5RKx4bL%j3vN!yXsUP zB6@n*Xaz6rUd%-z7DoX}W3rz8o9-wfUB20|?#lKspmu?{*F)QZbj=1ggZeE5+kyU7 zkNAwsyr%STPI=ze4RlMtrVMZcTo?Nb*!0=z$3c4q@W`rzTr%(K^!%Y79agLRI}Nbs zsM!wo@&}{XfZZ_b`vZmmu!rKb?)&ILM%{w%7!HQ?e*#?d?@Xs%JJmz`fw<2EKY^7~ z@P5Z*+cnUi3>Nnnmwm?2_d(V}e+QuZXy@d`U}|pU33 zOb}w)&>QY1hVX(W=SoE***H!iS@|WMPALuk>@-QOU{Us%UFCP{(@?$ZeQaf5ZhN*Zqh7aJdQP=46|B;=>#toy1dJr!!%D#^rDqo)IJv z#5I`U>lH`XIx)81sGu(F1o@YB(t>k_X1a);@nbK`%15$SCM@^bAN>up& ze6Rjj@2+B{hANKb3n4^N5tWuB(I6E+2Z6L?C4HkP`5it~8a^+_20xX!!rFCTH}Bq; z&!+oPYn^bWx`{8$oKO7Yi0ebPv$K9SFr4l9eBvuO$ZqqjC52flI&NNM z9ETbz$P%}HKTi&8oHu&v5OBp^l6Qgji;Vs-H;gXCYUen{Ky@c>yY)IU>s!uv(q>r= zXeutaH^)AQ4WbK4m@=Aq5fwmsc}uJ_sNP9?X+KzPms7nv6>%69@mUK-m(xl*3ITdR zBL5^Z5bD>W18lR)eV#nFi?A1Q<+&;vGd+G%T}^yd+1!r$>{31)E_+}VVem)dVpLraN1B&XRoNf}U*$9p8s#;Ha1RTmO|8Ep7d@z^3U=tA6HLHVfkMwM z#1dyL#f;kbJ6RTvVoPteqnD(T$I&5)PK=TiX1-r!$aA&Yr0pmp)-5e1V+^k2sOTmK zmXDIfd{H@<){&M`xdQdnMB7R$!K8_2?xXY~fWqTeRt{;cRO*%8pDypP=(f2fz+0sT zq|6(cZ0{m{)FP;fLMoGsvzK(;+YPPAmGH!>&pT6XPLf**9aZ^Go-XXxADx=Bl88Ss zv2;aAA9HZ4XJ1{~a&hBxKNp&3Ux{8VVrN&s!Qmjzb`#sLfvkD*ysGsAt#Yapy42xi zyGyhP!YpLON=Wb5@CqN0&-2uxXT9$u2-sKv&5uWYjzJw&kjG5^hTY0prV|S+NX}Z zW+e_g!Jb0-+0 z`#%e2#Hwn}#uqv__CLmB|C`KV;AG46L&?O!#l+UgMA^XXU!~GTNlR(|C)(e+)S{RG zp;oO0cfm|YYBlsIbHS|M(zIF`Gk3TXP0aOzB3zNrNcpa}92QMeV=>0&7 z`>D~@E1u;|z3y%w$eLhycrZql16C1z3Z`&s3)%AYUrgnKAsIh)Pr0a%P}L>$e*vU% z_S92%GHGljeY6H60|^+^^9zY|*6JTCJASzH=@~THcLoL;XDs7MKNBeFXMdw5_ zy++78yC-ztCTg*NPmLj*yn|SIPBeWlr@`{OLcaOOoI=VqWBML*R4;;VGg8&v%-}9Sm6o>Q13Jl92XDPzOJk_TsO{t)&cX#bL8rvD>In&kiAI{fn{2w6BgeTgu(X8(N^bQH$rK))vM zLw4D6a!B~Ek@xyQV)7$+q9gRRk!6Eh98^L>lNs?t5Vi{=4EOq49`mD{Q3Hnyt`XXy&M#g@T{iZT820`rOuhjm^TfqXRRjQ~cmyfQt&D>B}%^`h0v59k?u z&|r>!D@nNh=7Fck5!WZv$-y$dq^mERKy)#$ogPh;b|8&rq&@^QK|NKYvh&ZEiayoE zGTW~ac!IAT{NLr;e{1~yxqJUo(uD17olU;5g#W#TCh~mJV1h_KnQ=UP`L!qmr!b=Q z+MuqHM3{=;u&8r(?854!lc$@u*LE7v7=OR{C3CvPsTG&IcX-A2J5czfNar6 zralkGO=9Xso>e1&pR^(S!y~;Z4fc(wabBi`GWOY44(aTmolG4|-GEFGPay5VQrcP* zPeZ9k!RLh%=gWA;tTb5@nbHApBte$SV~?5*Ik_1NsMjUj*|@K4N#VSs9BxLkp5O=B zK4)Vn&y2kF?fIV=fe=|uhpVsiE&R2P|N9#L8v*y9YbbBzY~rl+Z+9SGRvwfIDRjFJ zw2ml;#5V_PzZX2ZJc1)S!jKnRa=?C9SxVS4r+ntY&0~x>X`)#}K?6nk*^YXmYSkHWJ#JaU$X;Zs}w=@f^QVI3aE21hnu1Z8KJu z=S&LRs&wI@3MX)67yjzL^ot*Ch9U7#P%1CZsk@5ckSWNCb}OXeSV|ntD$AnW=GO1^ z*8Ki8h{JFds%THz4rjHT%Wg@eWm#bUTDlalD+Ue_!(gcFxpLaPGXrSiFeu)OTUh5M z58)*yGbW2LUW5B7?Dia!`T#%K%&7iC0&e+}HAfscC|(1KIF37Sf2@lgtHLPtv)lX- z)GAcb5N;Dak-N%akzHl~x(2EqU|u*ucg}6WVD0uhF*g(61#;Rh4(N1CyGU*$fUsE^ z%)$^qLy}dDYsDAo98n*p2Iy2gLhmB6EC`GHo?8x+yaNosN2v#X+$L=?UxWSQ`zN;~ zNYCMG`$+!dHNg2FikP61+<#$1LJ~I>=M|8KKf_Xef6;*kQqhDHqNON+i;i*5hxQCe zDp@BYxcwI9RO)YlR#MC!Rv8A|+hlyfvkI_^0Bu%OMXbVWk95*il%p4|VeP+b z+ea9kWOkNLQ*&32u^eM>Mp>*k<2wf99pTg%Xb&ux1aR7{2qdNy(6eotVp5f)=ll&% zWobSJE;xrnZ3zILr$#j~f+^oE9pC@BB|>z_`4Z@#w`{XF}bMNPoZ&1Tc%~WxVz}kEv=- zE`))>pU=D)W^mG#23U|jvj_}ekqWT=R7Va!eN5GB4j?LrPqc@;%0zjGxfh9qO>4CV zyw1@RL}LxM;$8vM)qw_j`)LNENjRs2oN{;f+Rse=Yu!^_y~5ei5?!>0R;&y>XHr~p zygB%bw|?SOokI0#xfH4K3lilf2fuPq0f~3QOC~a3k!URi*KjvBA!ZTBXxWrq`PZN~ zd3|mXX=q*I-g>(xZG=?s@y`i;v;*ZZ0u;JR&(JqYA%AB351?n;Wl~jMnYmtYw|l!N z@>>P3Ku7)g3Vsz9bv#QmRKK4OAQp5H^KB(cYMT^XGmqZGWJW0CzBKv&^F^%%n*u}m z>s)^OkAJZE|HIi7wKcLcHgWt{o9Ux!<%Da7?z`lbWW1P)Z>Pv*E4BXJShC6xRb)@g z=yqd*Vb@TPTRQMV@(vw5bQp8#pu~PabTc)Ye#a#{LWWld_N-l>=9M zY7BbalB4yN{)p>Eo8`ilGw}f(i&o0SKmRx4p)3B-^$JSp}iUf3e z3Q%sslGf-1C8rXFfh==O>#;{biy^JLG{zzuXxGza#&RHzv?-EBi*DF@uGys-ZbCcw zcSW6h@kg$S?M57k$on&y9MHWtZd!jRP6Kb2>3G=8VIdB4@RqCAo8ZpZxHgCMc$g%% zro>fd{5=m{`WUvHwuoroyL5sisqrg)$)mT9s_ zmm&!^O~V)8Lx5pFvc@EdpiKZfjk)PHYf~0pMY}rQ-Br9vd#dVKs{9P4FUG_b6i${E zSvI`ZqERYO)2J^C+&r8vQqN#bZ8B>8WJu;wHu@x?akv zlTuQ>SG>hu>?6Ii$BGz+O0GfQNa#~XhXzb0>FTq_2tf{lgRGg!&oI*6gZ1Xo69Igf}=*g9CWc$VeaM9;CRJ78GPqT%&D*Di9KoDdc63 zJx_?r^q-K~M!@f_@`>|ku|_CCEkA>&?~Kckk88Q*N<*mF2s-xmHbRF7sSESi4k0wD zZ;P5{hKheInh}Qh#VoB~>kWh8b|*@%$#W&4#J`C+21VkjovnhS3pdUyWn{ZM+$xFs zTK=I&j&0Nz4k?})bOB&@{q$#z4oWv;hkfRB4Btc9>&O$Um0_r2Tt+um z*iq3aXb5Ijt&L5q>HV#r0~Yy++~zVe_2);p-OMcIMjO6?`>Zd3z!kTuR%N*acWu#U zVQ~>`IFnkh(9sU{@UQ8ZUXf#Z|2|b%2_5$`Vau$KjZ*<--E^Os{Gpn~3)>R&m?4j9 z=OxhjJ3@=FQ)V|I;Ja6dseGLqem#?$3_*KTQ^wRI4-^?m!+DMav;D{o+UC^|cDGu@ z0UO4F)_`wEb_uJfn{Kx4X#cKp1N#^QN3ytW9;GD}SdgVcBkqO0Lggf4{dTkK-+E%n zu(KqG76-uF=;U1=kuzgdzqXP7< zFX(-xd?nfa2D#kBmZ!C3jybFW+liCw#>WU(6*gNcSB_)b`^MXYY4(VE!*9>*Pj)+Q z>`}$wQI#_VKCA@x7S33xys^EsY^2O0d(WBU7jJI4>o(f)D4PM*8$#{FvR;*Yn@GHKvV}D2coGM|<^hFj% z2O-omk^PX_wpeb59zajoWHav-(XFVCD<1qp_L9ku?%5|w+N)o&A-`@VUV}3`;BJDC z2=@f@1Kz){%3^0X@m7tt* z2?neOqVvrY zBK@6xy$S@V+pSSVIOvjfqd@vpeN#>J^3>8=sM|wFwzd(zEyJxGJ>~P2X;?xPq<1$W z4rN;8PsHhn2BOu?7E0B6ya>V;EG8ZdSoY_#u!>r#d1U>1!Ug3`ZI--;Nczze`59PU zh3=}!kN9#TEJgnd-tN+XE*aZ9UvZtOtDsW z0JzqO4UwFC^W8pYb~5_kK*oi7A z1e%-pbm6b>QbdBA4P@p~3@BlK21PspPu4K*3dwRgVN4dN*;w0H35SVR-lid?_Feq_ zn;Jeoh}oEjn@GPbpL;?!g~D`xmtHajiA7^Qn3Z{3s-a^vl>%?FCPSC^-@&KaPkxpD zAtoH|@_~qYzOJ=X6ru@w@n^ud#bjq(2#6Lw|1nx1{2PzVPT$Z{-_)4e+{VQAFQZFG z)I#>qp$E_^V>!7a@o3m zk%T5L5Yp9ib8;?qw`4#?M=PjKxEfmuPcNQ}`MoEnY3sm~I?{(xUFn?FVc&!p;&K@^ zGDK?8D?FUWm96?s@!X-Xl-{|jqz>x-2Nm7R_=Ek(is(6C+SP&m8Q(cxr4XyVxePMgI!a+;M#n4}@h`aLJLmqM8m?J4v@D;jj$j!+O`V9N5& zgi8tEzM(xMympmvG|d8ZLQ<8Pq;A}Cu0qy$PQvKe> zVG#y19)8z!7UIHNESc`e1}7p?;{kK3#6I<|!zO8Yz8GVKT8{SCxKDrDaYaD=(E|Wk z+kb>C#^2cSzYHiGB`XO;58uCeH)*3?P7UrisPpDZ2fjh&N1hetm$XrTELY@&v^@Uc z>5j++7^Et<(?nD*bn-0p(nep^yG)|IIo5Md^S2 zsWVj}-2T{Nq{|TRE=39-^A(JgAw+LTK%k#IG!zjSGt!ro3Z`^k|A54uE)~O6XQ{hz zMY$50b&61dT4id0`YVvxX_a%ObGhZIjrFWMaAKbUAA%7i$>aCqWsZ(+_LVQJ(7Zp<2qEs ziPJI~ic~AlbK$aui!pb~myI;PMBegvyhMB6NqFqK^%JQWBF>)!mfbEJsd(3iexfRy zy_Rn!n{yhU0#0nTdY2yz*C9__2#)hA1Sxul9w&MF1ZWTLvO%p6Mdw^{I4dy`0PcpqiMx z*%?UunEwc?!6+@!HO07JKR*|#DAM;Z#Gg?{xTM;>z8VYb|A@kb0ijOftvR9k z!DK$cZfpHJM8kww3Y#cl8r6}TGYWU(>JGHWbqfOwFaig|6h^dE-f>>Zkw|}3&2oHB z&L!Yl^tWn2I?6Svrpxv+FkUcdlLVuh@Q!+!1ckdx7;NGdNJ_x6%;SAB3sYEP`Y8 zr%ptejNg1l@zrD*R0evi_0_~~+9l|fZ+I}ea=XG})C`o-1| z%&=;?A=ys<+`rsH&t~$C;l))2^&tZFq4cn|Od7rphrv(0C$KmSU7`QpNUR!UCM;}eAW?2EZnz7d z7*Zl7Q%VI<4Jo=1kh)7H%n>6U(8nQGz|+~p6fsH6%s=&F!-$Mbh^P9?h@ZJQ#8osu z7G3fD6gIs)Qf&=SVYx<3F&pY~hx^%cGZ5}7H^w(rlZ0F{I0B9sHJ28b`I|{Q#!$*M zoqh-l=V4huy<#`)%Uc(^qSe3?rYZfE@X9j#_#$rTGY3&_&QsMMWg9h5m_n?bS!VSt ztqPT%)`f}jCk%VTU%Ed`6VPkDXb#+jIjwANcD6y7cfj4%`- zDt}?kl#G=eVi-erCK6-i1Whjk(D7Dy;il;>+za(*&675`U%2bdmAmvjt3r36O$F(b zRX;nmG^4Tkhp*Jw1(w`qLZB@0Qt9n`1)7vlU-3zNH2IO0Lu}&nLQ3vb4sJaqrookB zcz+--B*w}W*$kDZNNHvUg>e182vyz=`dw-$+n~2Tgbg{eI#J?r$y^@&?y*-a`m#y9 z#z-j3Tk5EJNV!F18b5O@YddS_jPm2ySPxp6NIVtxR)v8r;k~$6;@XfwQl3E>cCD_w zPy(WQVt`2ybT?a0fn`eJHzr$Zv`8wbonu(FIAkIzsw7(rx$FK0Aw6PYBhMgDh-93n zaHt>sn@kyO$Oa?Pw7-vgTCdu#T)oP2iCgidM~`l#mupI`;IBtPRXo4Xd~QjMKG4gD zrYz~Zu*0xh-=ATcFz)(D%8jRzj2S`z)?!(RkeF@Nk{}<;y$6blj7hGgnSidn8b^W(@14p$F~v` z8ion@(%}!3yk4mpJC|x^buA}s;TE>45M_+xA5zxhn;D?-+pkv1f!Vf_SI-zWWMpn< zLd_^LgFI2yaQ0_HYr^+TvQ)m}Zw8FL=LiRtZ2GJ54T+6l*Cv;~>+mh%K>m7T z2Cx-ZkOwQ>l}rLLd-pQHTPkblp}Q8(gT^JF&xq)uR~%5=AE~CK7}NY!I6~^6I?aRJ zQmS2pM`Ufc`Y`qIZ6eF@>1uoOIV-wvQn5j_5$<|8-iK%L3ZY=!%gRfN$Azbv%$a6O zakyB-1hc;sO0h&j;+3vVw9l}vU-bL+l2xr{=%8`=3G~lEEdPJ#U!C ztG$#Z9%QW?yi4%A=;p$A#V;RJqw@Zbo0!w9;S;F_f| z>2Pd(>{6v7Kasg5DNb80I=$9cj&+;nfhY9#8k9z9IR*CJQEHm0@{I1Fdz-Hv2H(M#0w#p5%J1vG$E~WLh zx<-t!rPzImvdGC^;|M7rgS!@rQ5e_1JSrg({vt1Z%WTlKG7;c*_G&KyOU&H(Dxd7H zU@H)>0!mWfrtIKu0au(o2C16M2i`(~eD$f|*8=_Af}L*j|pS% z%-wvhk!s3sC0-~~WFZ=a;We2tDq245(e6Y?W<6No49kcqiRDE+jsgozk>tm+UYC-| zlL6F2zOOyc?hoULyX@myype=c(Ec0|{VAq}u`(wAlNJhJpgxxvXbz#b$9N1lJw)7s zgWfr>0M6(9in8e3*z#%&*;syA#)QeK=0gUlvKIr={#pAYW9k?-V8m{e%hRxqLsFN8 z`Z`G^@zQ2IY3LpSP+wSuRqOVzv&iA7%WylCm91sqO${q?oNl8$Oi-O%4^}}Pze9Bk z(rKs1lj##zyQw-xtbPsjUvor5*;2T!O~Kw0+46DM0$+Zyb;FbKa6}?H)LSi5E`KgR zYgKmN5g(P4U30l??NnRSu=YeZ-p4I+AMSTgBd2vfG;Wv$i#2vR<9QQuZ$h&LNZhZ^ zwH=%aEBTEdxqtGgD*ZGm$t3liz)u5Z z#cv}5KO8^C4^h_(bCF+UZFx$YSCt>*4xallH?Q3?feG$$%@gidE92ws={Z1w?;?e+ z#w_2L=Iey{y&|1lxIf63K~Ug<+zAu9RI${^ZXjnhQ>1V_NBfDpJH}dq#73Z+QiOT~ zM?v&5d@GG^yh#kxh3WDfdPXY7;dTzBGv7@;&$9J`#opEMB>xsSShR z$1g0`c$Yi+b)1*54UHN~Moc0DHe-}~9P!sv2oHe1KJmGnSd59UUdz1=|bi!-j zJ*bA_6!n6Z0OEdcGd4a{7UyI!(>vVnMB_@?xzf2vuTM}#2IV4dG`Q^%C_-ePyC^?x z|1i#ERV|MUAlzF1$B^sq>RU3-R#yMVFwwG-y?lR!r?PqU8vJrxZtz?sU})W%!tniy z$NZ9)vrgZMhO3C%-*vKp;e5XECa^((bZfiydPZ+jy}sW*gKUAmV!SL#*CrbXiP`jv zw-DV;(jEkK`vc~Mm=R9M7&g}2N1zpwE{8`u>t`1*KQkrmN2sb+cbJ^xo@-h|!McdE z`9#r(86VDs#63LAHAw7}dfP<#x<-63*EF;x+D;i3gz-@|kzM3c+_0|33q>}v(Wi|mICHlvc zqW|wOcF?ymvbCoDt6f9U*wER*SmDo))Y$1+6=X#Jm3xfQ{sY!Qo3k(c=|w7lUC_d4 z8Zp59cfJS~1|^~8;Z8)m9DM=pP^VQwsvkKvZIEq8V0Za>yQmu=Fk_xw(K`U10n2PavgRm4!a z5hy*?N7a1drFl-QA4;DY_=15fPz%9>{GcA&$)FX;K9uYTZ5OYjsjo*n;5*~<1nQg^ z2tqEO;IW%v)wYu&x*fH3%(#w~vVUPHHq3SHZ$s>=d-+q2#w3$|#{n?P?|(E5#eZm+ zzbs;{bgnonhW=^&T{?7oA71#_9r=Eu>8JdKa(GE)-aR?_SCgNzl5GoS=|*ud!K}NR zyQ-P3loR&(oA3EH7s~}JkS;~9XFR_Sy?>-ff4<-1{+3;uvj@EuO!=*hLt1s*5(SbO z#d{L)QL&5mR<_Lw!^*TamutuqUKbb6QP)!FBFPbCr}?}#ZKpcOcEGH}q=YdbfWX$# zNa8njy*9qW4f+tG-*VELyH~ZgQz-^b_)H&kr#J{VY!b|G3#onxmULUJhfa=c37slD z=)9-kwMMvGwuo=#)NkK!F}H!#4Z3L=ORBvj|5Rd^4v7d>ccTCq#;LYcNp7?hx8@^% z=yS$*P9H%w02e^{jV6#pNN{;EtR1CJmQQj>s9IA8dC6c|ewnn~Ycq5usV1MV!m)X~ zv6|=bb?rCP4XDGDN-sCiV{OH@uWxV(M$6AjCN736P8Fs(moN z6X(`0yU*XKT?O0pQl%v(;qB6n+zy4asNP#fa1BOVE#ne<4VZ*_L!q)N?t|m)S{$#0 zwzEe!tfd`;ZU%bV^G2n{m{_1RyPiRmoj=oU(4V`|#HQ75=iSIvbWcXTox$us4~Apq zJnetW9b-SwoPkkq5>(wGR-JRBH5m;df|XI@&dg`hAgg+S*49a>dP()?#Ew$f&UQ+9 zZO1|fYE3*UV~H1|q=gxzHyEPVW<$IRkKYre0xAJ^3__Jfk0X%vrKPHt@pM_IFe~g0c=UJoLUx zZBvnU+tKi37V}QWKcq4tlg$$;RTMf0Junhu40q2EEUltM{U1-%8OC|;pbhCL*PMzZ*8%$>7<#oP(x z$la0;zL21WYHy;sgbrV=<_Hygxq{68$M}L|!ftvi>!61k8CbZ-62AJ&nqb$e=L7hz z((W-_b|=F%c(ZwCWH8U;pP9WU7KtP&fLzq`ABD;P&(g7fOP(U;cK;HNM#e}>ev=0< zXMFxbPGKSnirBP(=3bFBg259)P#PPD5LRnca?AEupO@HO`bq<&#vRa${B-Jft;VAs zZ@A0W$qarMzNyzu3Adsg>2d=!3Aui9<{fHxhnLV!y`CtItrsc^yr69+=EsZD&h%ya9jCum%zw`IX&Twu2qxl zk1tOwy+uIM{Y&-h8{C?q;`l)rRnA2oeW4rd3kYb-y5TR)6V7ZSoT>GBD;Hup$WOPY$dwHQ6 z6&o3gAAez=b=l(GlGkbb5NqM~+s=nIoW&J0-mEULGO)xBQX)~RF<+z0N>$_SpP5ts9?SX@G92)UD>_*j_0*6MRuyIe*AB4&DqIojxN9fQ0 z6k@X~rU-H*-Pyt`Xd%JuFK&h}Y!-W=ea+`hvYwbHaG6NBnnCdH$v-)sPK^xCsDvgi zRH_wS8mG3rpSF*|FuO{zqu{oiT?}>3=B+vi-qOCu+T4~kQ`75W#n7)T?kwi!B$v|pwOa#i3`}xH6s+GhC3Fm zPxL;QOZY^PHwRDoUM57KFYjf0Z0*!>qLb(2_HohnOYqW6ZJHpcC1e<+1mp-L1;>K3 z6`h%A585=nU|o~zaE!(9SJNTafXVjS#l!B_dDqhTChgqfXm#k=-j!IKk0v-GJWSBfd(p(Fykz z!D@Y-m|9;d7MPBHKVM-g47GtUUst!usgzw$I3NGe?B^5Wp!d&^tf^+aS<6zfc7~Jw z{8(s0o|Hf__r(=ttg@+N+fp)^FlhE3R;6=%aaMdyTC>0z^iWR8Neu0DrpH(^sp(EW!Z2fwVgUo=;`wmHf{X035Xocd)?1(%UY#6; z0b04n!*)8a97f;hzo1|?GJEnzadqtf0~8q9{vHaW61@Ogm{qliZ0g#)51k_vnG4JT z*Fb?%MpJ$?=ZMS~FWMvUNr2Ku(R%voli*e?LCr%4paYu;_Q!+0`}1dz->9g}QbsWY z1%BT_W+6?G_n5~lQVhk;A!P$Rp|5*JPz3XcPcsNK5pa3K*DxPx$I=8d3--mLC$@Ve z_6Ir`VFvH4b~{{pEA4^_9Gz^hO~?z-%u-Cnz_=>J+O^0=1=QJ#&t-x|5-~hV?BSU) z*G$z&RVCYkj5k7unDgY=eC@ZyPMe6+7S}$vO4*e$o>6&GEsDKLrK_{fY;9~aZ}91? zeaNkF11v3sHJV0eEn2V!NH9*1y8WVf?oQEv=R^@epQ3k@_llrxzF(C$zen(r(N}yB zy@=?(NK_XPa>wg4Td)-3-FT1*jkL=ZEz!Y6ek6Is0wE7kk$?9DRd|JnS(GwnmCUg1MGJRsVn3OJ=}AGB3K3hfPN04mk+8ZGp?2z zb23NTxkUDamwuyz;ktT}<#Uv~PW!e?LvIXsTeCYO;tZTK6`hd)u5y85RlIR-MFyix zv0(lPVCpPe2o?-3T}4beHTP`Oh0s~5J@MKUmtCQ^CgQjyuGiIEMp*<#uYiW;MZszowjk)JRn)dJnGK#<+;N`?~CyTG>k>)CFtjeUwS{yulgL| zIifFo#Cqk^{vc6Ru+JJU!DLNm2yyE~!Z+3?S2R-lDf2^u8a!rArdUu$gUz9SACG?8#mGE6WfjX5oV?kM6 zKZeOk&*5S1;$C;*ywhK|0lO;IJg3SegZ8NFShz{>>NJnw#Z&Zndu)tPiD4!I(oG3X zrbakbIP-pn{QwT8vf+@zF1by!`|ldzs-`=s)sxUA#$B& zFcw|}i(&z1G20AW6_>{eH*^iyPZ9tUH~~c9(lcx0iEMi-f__uP>yK}_FA{B({g9oiVNUKp_kPi9W3e>T3#R08 z$P!BI+e`WOjmS{a!o-~L{y_wudor+MpWcBI2)_j8bpS*lYx)i9&o-;C-6}=}|05AF z|Ib7qEyVoyFtAwx@Idb<-&zpq-n87zOil=XLTQ;vVwnO^WdN{KY=NgCsHwuL{~OOh=K zhP_{J4gKVCBXBTE=ykav93$p%Lv;%a6Y8b_2hlnRn|WeqTMigHc+Y||dUZi<05_WG zIOuIm`60RpTRT*5V%Bj&8GC7`e2z0QjnSjoc?Lz_j6IJ82SH@egLlPgcna6!@|mPH zZD_kP)YQ#p2ZTk+D$mRSY8Bcm^m6mO_Y|8&-#3&24CKe7SCe}I9%2@q66b(I!TK5(Hs^xZE+fYAA1awB<6a2fL%Z;Kdh<)miJ#N(LMCmWV;eDHr!$ zri1t;pN6V`)WAs!+(Q`rrLM+oDs-1Y$9abtO$7o;2aQ3N?HUDbek?Q5c z6IGHk;z6@bo3-g-Q>ErAXImP}<=WcJ+T3#E?BX0LzeTLvj9Fh?5h`P|Wo$wgoD~}% zNM~smPp)3>q^%IQPbF_gn1_N1Til&f8iaPZ2Q_FItwfPQdM`h>xkSms`N9jctioe4 zqQ2wr*)p29h^U`LWc4tfLGTJD|-WjtrKz9=n_2;;4LroTP6NVoaX!^6)6*s7#DDNjmov?aC00#S}{LK;2%N-JDNp5!IAWpk1XoSloyf1F4#X z2;3#YEF^xVCAf_BX5Y;O+x;Y4sB-lM1ca<9&l7I;YP7m-av!xpHhVk~Q6tJv!~jhz zN|R-J1k9MFVtkPpMG}1T5mxi4RRawrG##HTg7dAF(gUu@UTB+&SKcunKz9^R1U`KU znHe9}BBmpFBLjARZ6vMe&7=_@c6_o@S6*N~R6>s-2V{sAovUe!Wjejkr7pf@8dNzZ zP;&E#ZJ3h6gb_w8_(<__jv!z4Z)L9;L3jsg)#{P(?;nL#JusUasxtIWI;b%niE`3F zV?&Q2RH86v;s`)~A-j!Xi6(*pMdR60U_*qwRL%_slx(9DF!jE-otT!ESt^74>ZwL~ z1O26Z)n(?g)@=kEh&-cUKPU|v z148i$o&nv2v7j7+J2kB4!NU3>1O|SYD1w{4VM*=ooCE~Z1H*t4So(DbK&5f|;&rIz zWo^Vcps50q-?%uD-@g7xxF!SnF`#Sorrf;=d;>}t&!6p6M$v*o4aotFxEQ^=N^a3M zPRFTu&DJ>$gp`q$Nc3s^rrphZB@3~ry{~o=5ll8;fg=pz&b?s{v2I1eSHQ@ao%72| z0B2BYy)9$U$3DN|Q9Na|ev~!RA8fkEQcIUBdo18!G2Ot#fUoC)V^c|R@m`8|>!jL? z(gbqV5MpeLk5fYJPP37Z4{oWrjLiz-EW?HJGX0+ysYo+QwJK!$75!5v?$gj+y}HTQZ9? zNRo)o9hnAb*A>?^v6yd^T!;%=>s?ZM4gn)wk7thisJN5@9#H+OGxtJ0dWgst*Dbia zolRV53A19UL}FZQ14PXC3}Zoq=9q@;mtAdW@$%%*Muf^sro?$^saOFS$Em$xp(t!I zO!p;j_#}ucKQ}|*q-v>%ib=qRJ&2T~IJGg6fb3^$bU+C!muR~*_8Tqe$IH41U!C;B7&oP`6MU_<9D}CVB^-CkF$W4}@*+JAf)a7Nj=y?+e#v zLur~;*~(35YiaF%K^zl}ny`SO@tpu$NW`JS5I?fZ*)!sYaKR-DvXZZQ`&fYeq-Jh**@W3!+ zOPq&UcZc$Z&32NBC$KrT0GKbZ{sV|<=*ejOGer*;!*yusJ3YP88ATRzdkEj{!o(MC z;T)W``wXn_$f7HaW zU85yzf3lw3jsPoop&yh~V#!FAR+3T4y_zNJd1tqTpLuJ!b=BD+maIonlv9>uC5<{c zE4x0Q5UjCjP+xJT<$9^fi9Ll3?S>odgIiQ4Mx#ay>sXB*w4p;AMlLCC59z8H$1c=y z>*HYc9H5Y!yaN6iOk8fDmRH!k#o4C%zkYLYNfkuV4eRuAvxSL<#PSUVzA+o)>7jfz zK=k{Ncynn8>jc%6DKi&J9jTHq?h%a9x5dW#!7BEBiiy@$bY53>5by_VCpJ}bY+;@Sg()zofTdGa4 z(4Sjj6*EWgF6ogeJ5WL|rbiuM(@~I4GZg5? zK+%sgl;}mphTArtm!`~dwsZrsbpVfY4{}lE zD5^Oq*iCc!7+V4d!ZI}zUv^(aa<`RH6UGpF9z;*0;!ID3?X7A!H$uw8j}T6k%SoLq z^Z8UDJWt0q2|>ng$H$pWxDcO>!-$OsE=umJ6I1g$Gvx|i8>%}FNoDOcgdSEqzY(KW z3cnkt2Ud9ac0QHFa?7ANHZEzZGNu_|mY)oxeif2Cs!w-h(foZ-hXoBec_8J+CiObN z-dq6Li;M#!3IrBYi?|kDKor`NS=@Snh9gKR1KmTxV43QGVmc_0^0DO1sV{aOK`=Y& zuqHWocCz&-RmH$U8qK&d*-$<>lq5=$gKCfMhQkWksRKkb7%Doy#1MlrAv$Ulu&~m) zL$J769TW%9$BEN^O-0h=Zr@_2!K3S~VIsX4AEhPsaN>rCchnMQNi1Yrr`jkA@eaSp zR(1LWj(7S5%qj+hlTATbIUm8!GkeY6we_Shs5+w8tbd3YWSc-Nc#cEa4+bqN%HmaVB4lTKP+LzutNShLbN>ZQWj%D4(_O)4Ly*&p+ur}dB?%bT;Tbc&hnSj~c`~od$g;3PjRC;?JA=&eJ3pBUX?fFyW9xgDKorDld{5 zi^~kDpxCqE8Nz~(3sdskRNsvI!FGk_Vb)G&lgqsGmK-n2`sUkuJ3i4rkw$SvbzzV;i~z3>|5e$xPP(-87?=$6h8t|oog zJ?b4B?4vu-J_-#t48^;Awj9Z=4rb{sMvl(=-Oxk((lz7C<<9WE zzd1?k{AW6R6k&v60PM3-+~=PH4GaPBo;rZS`;RGX|6X`N{vUDSzj#pLk2&GUKCsXl z?Fc3a#v!&PQ!tJ6^1M$;M;h^%OG5)M^I8MWtwomUT~r^`yy9eRFbG4XlfghBJ1+hXpc$e!P_%zz~&XUL#Xm5N19kR5rtWCzlPX z6AJ*;i;xndALGsPqKs}zuC@doJhe9+*EWJD(z#W8RbW z@9IwEc=Ih`ix_&UjjUXUR(Z2P=i1X;nEP9_M1TcU{wVK2F_-68F9@@$<~)bc-=%%z zQslR|9A-ixV%Ph<9A$J6h({E~m~TI>9$%X6Ad`?RSTaB;zK_?yAsNcL`}`ScvQTq{ z9^T>f8+$=N2U4I?NHQW>KHDG?&VE;_iE^yNfY)HvEY&H3-7%}$P-!gio0)FzkMF2R zs!uHP-ygf8iq(wUgG{ZuskSY?dz=e7r3;4_RyDa)i0o%XbVq*)uJj!6t{khr{&NBq zN&w%)Dj+ZN&m)lW{ySitoy@HqX%)>qjFkbe{#7MawpPSaM)t8`(N8w^{aKjUp>zZ# z&0=myJ>S%!^vyg{It;O-prJ6`2*THhwcZLIoA*KWsoOqd;;A2wZ30=uf@4?oM+9%q z?rQ5Oiyj|Nis$2o=Y%`pA?fpeTTJ>>-i$#=3*|Zs8m{WvSgFq$;xhb+ZvbX3!5+$x z!3#Z^-d7yM)GnPCZzM%Pp|2qT1EUjc^81H_Xpc4q9>KbJaJ2B6;ZaH2ew__Hw5e2i z#?*<1y|V1pXqf!L>yZZGZb(Gzm0N$wQarQ(@ypL{PU|G6pbe@1sY_N1koB}hNaZ#J z>U#YIOlMGC;>@TbjJ7o_!Oc1h5Agh!ES3`(PSs~WlrBH^B+ ziDeCY?(%u<**ME1P{H5IW#$hSgzDdmVKfhYM3Up6>$(b2&i9%sZDV+23yoG&;N{r#M>Le+-yZ!z}2FO)-JGaF<7IpwdnDq|}$;hkd|4 z;uiu2$NRWJhh9c z58KH9rlV9D>*Wlx*IdHpAO$$l?g-sXWpin2gr-Vq;XJ`O)#&U1g1I%p+|-%MLn};w z3s@U>xHP^jB)49!ma$E~Ni}mp;*-d;^&CdCIyS~ceZ^(E!j`clb~ma(w#DJ-v`$)Q zSWzo8xl^(J@UwiiJ}noAO~lD)?W_HJLCS}#nO=%Wq!>`7hktQqSKCp#hVKG=W={j- ziuVc45?qvC$^z`+4I+m<>SaqF(bWtxu*bvpFc^0`{5RUI_bzGE1g9Vk4#BD@WN=Ww z`{f~?T$rFNLnFK{FMyr{d5749t`789g}PS|uM!N3~vm#RMcjh*~f{VaN9b&|#>gZ|3R3 z=)OYYOmdy2{_c7`Wo^Wuh~<0a@wk2e(7x;3VWlm6wK%G?YZX5-+QJ$cmbofSP!4qK zC}okP_z`m1Adp4O`_pqz6BM!TYIcYtQeDC!=_#A2+My*3WasJYJ0W#kwL75Ur#jlK zhs%ZhCVv-hDB`{&sQQxGLpUtBNRHAImrPROH!aV-hp9ZIP}< zCipYSOL5;4*@LMbwyroCzu+Jou5SZITotG_^VDJv%O9x2LJl3+0K1Ls6& zz(6fcyIOC-cz8=Zmu+{q=yV)Gf9(pqV<26)e$)F$z@ zgZP2x9Gi^j1!iz%F`VeZ3t-a$%;FS!plUbB9A}8VS@nZQGUSEO?v=5jzM9nhb@v-z)f+0BqHl))F@(A zfSs}kx*O?0fm-iCu0IXc35-JW$1`hS0=8Q&8$HPp6dDA76BQTA=e7(S7!`iK=(aQ1i114XI zFzkL>+Hf^wizFldN(Gpej+Owa`x`0=m2gc&Gm)HK(l+5^jYQe`9A?)^(tI?JO1Si9 zS$S9ck0d)rKPnq>wNU(EWzfXIA0_EJ^#|=9LFkvH8)V`jLmaCj^E9RFvKix;f(Fx9 zF!#Rls^LldC5`KMol>VG}m*PG9>)TCLnaMdU#;bJ_kdLpc(XzFiAaw3tKp6St6f!25bL z-ezkpKMFm`m{z1|$zV3TwhcYIY$&D9!_r>sCgYtIjPE0`T&1ufX9~%H&9vk=!O=C3 zAs+LJGJ($qMph71R zqPWw-3j+xAO^R+KTfv$$423#UwBeu#D0#(YwBf`U6$Zd7WoG`tZ zcLIOSPq<)sZxg_*LVTou;gN|T%h|zIc zkFcvtQ1yK$h0x0${xfu_>-lt%%~z~xs1s~QE2o7vMuGs8?I)VxS!ibOmr7Q#GcJ2A z{MvBzKEzP$nB^(X8S4Nu|LgfXpJ1pyyPQX0M#BsS=@&(Y*H7aIXOiDuRoxVi32|AC|D;-hMcM$Uw9yJ~#FE^bnW6KzS>bX6To(ece z1{%s1KeT`=VGLRdGt``MH*u~m>Rn^|LlnHO{4ENd`)y`lZ|R)Qi(c1$z_s!S9BF@@ zk}Q{RTCdVXW=pEj7JxHtZmj}!XibEIyWpt8gS7}{s*Q-;o6#T7 z*_%+ziK$qgvZ^ow?XE8;Yt&r)=Gg+|N_0<|)4=i?L!_5JGIh9y@ffWZDYDeyh>S zZ@|2XHQxDai8Zsn^p$$KMt<=sP0(jBZxK9pLMC^Vu{i(M`-QB|{M&8zNPQ1b?^k0v6`@L5*oQqc90#p!J6 zFf+=a{E^*X)!0GH2qi>ob86r2S!8LJW)tL~{;DuwDErBLHt!0c_Y4y*i;oo$?=?pV z?kWQ2^(hS%{v4{kVh1QYeUr4Z-w{HKk1#Q|xNU#W+pT?E6D@+9iC1#VFBKbFAsvV8Y+~m~$GObzgQf@N=C<>=`*N z$xC8vGJ3)u^31IfAFNAxG0wR(m~9Q-5s|Tq+b`C+?vf4S7*ZxP+;!oXNC(k;p8OI2 zh=RtK_|Jk_#nnPt8_It!XKC)hd_xw-T-umg9lsB6XoBMvV|2WfE(rLu0$N~*0Z1yI z@k@Dd#ZRA=p=R2>3O(^WM-=!#le(MrRJvhbJ}g0d80GZ8iSL-hYC2U>L$}a_e*NuF z6cg0@x}gKpm3mxCo9DwRA}BBV1WTp=h-Ux(V+n(}g`v*_uH&lE`&A(6#z#y!RcZWU zmE5*Hv^^G92Q2SHUb)@A)HXo7d4pYXXyYXZV19RZXaB&d3_uC$eG;RW-Dnw02#qXQe34CET4YFtKHXrV8y?Dz3 zd`Aa4c4Ws|M}$_*5f1zZHqQ)qJ#VB?T)zAJbe%Go*0W^tbAjJ*KIE*Z^J(~E34Ml3WKg$YLZ{J&ktW{3DpSY;h1gpX1m z#&r!$sy6#&W8VBdL*DcnKU~zLqjQZo-yECKn5?390RB=kK@T&0Q|&KfhP$K7s2zRZElm}dJCBD2XRR`Y02my#8O_6Zn!vhl2Zuptj@^jy<(+Ite{y*wr5ObH5R1{4w$06)fPl zhKbErO+1JhBY?lcV#x6X7!Pa2g9m+^&@W2}wwYv3yIt?I=%m@f;Pv$+-4DWgvbp3M1T4jOhIfs`eLV zD-oJ9)H66+(=8#9-H%=<$fH$dScsd0ET1XADVN`wZ{v}K8DA9ktdU=sXMg%hIWyDj zi6IvpaZ~T9A+t!d9|*@c1DK>YaNlv)sRoc~s%wJt%RvUhSLc81hB6=RcTat%F3t2(XB+`Abw^VS^penJ|&_H@UQLj z68rGhhlzf+?^b6JkDjO}`+N8v_dhCmdo6Jo6QknXa^k>FERAvLP*#E&QNn9%MWpyb zJY8_#<)vaza)@Ob2>lZ%k0ZIH`AVl{4K%-*xXNzaW36Brfdy-81^^8-xBlW zicqxox^NIvBnn#imvEZz}M-e!Z-zejkz`7#pRZup##;n^{t(04o%$VgHDeaw)c+O@#rSd?-_P z%aCp%%T_E3WtEB#lDCFuS_YW!k zkPGLUKp3NA8~e~!AK090UQuY+sO%e28gHdgW$u}e`6H@yQ*=zz^ku_k$h(a5viyBh z&%d`#!sh9(y~7rild1&`~ii5;n+3bQ9z9| z2i6hMqT4w@r4Oh|bE?h_Fx32*&0Xh5uuP-*(y=1clY&*?iybN+)hM-2=w=@*yv!AFQPsUTC zAbylv0?*IEIfgEDq}H7w%s4^Swt%2lZz!~&{y{z*fo~u?LC;hwuJ7_bsMPmyA|%u| z2Lr{-asvIA)8z6x5@Ck+&!6I0cu>|QWz4QM{HjRvN*e6B!2T08G7D?!$uy%ni-jZj zsTEfSyD4VqcPYcj5^;WMbE_P` zHO`4{ME(XLSL#Gro7%B&>T8;XC6gOst0C4YF(&*ukea19UFWE5VnHsytH z&4N8Qst>8C#)t!BM?QI@?RhBPC518stw^%NBVd75RUpQKO(E53_Vl+-1Tq_ukw5jS!M7$|Nkm zauMjroGtwaN)nj_+&(I14!21ycQs0I4p0)tNOry*AEpNrds7@t`6v?zFWyfzBz7*X zgp$fMlI$KA7Z*%nL`V&zC~RKG4D1fhK_H(Ga%IM)$vBZCzRMAX=eX?J_$|A+)1%AS zeRU`4>4%+BG0X=QWO=`MC?J|C`0C&EbTjdxf1;f6*jS>{r^Zt}< zWM84K&)%3eHD`b#x7Apw6qO0gU}6?}5JBA#bZ>VmBR$ED=FDM7(*6Z4@x~>#hoFt^ zs<~{52oc-Y*)PkM)89O2EeO2!nSQ~E*{06o4b)ur+2a8|;?(3lU|r@`2NTYCMgsy> zU>cqGOd5%Q*nhK1D$hh;{fg7KIr~#SICm!D42tF%f{X4ArG_!p=F9#ch@ZO3spD?@ zl2QUT?M3&4K3`O6jFFkBg#)%H-)gwV6&jV&GA1XlI2ftf0Q&s+BG(zFy4HHR zWV@g3J5!Lr#O%A?#7t+f4~7m@Fza0e7Da=XJ0!13fX-lkW}e6+!p(|R4tYZ}V0DRW z6?ds!S7WSy6Uj0KVz4GjaoQ=EU84Nq+GkeX^tYC2l_W0?65Z&(GSv1O9 zf1kh_K>HH9wp+^dEk8~mY}0R)&b~2yMoP4kfFS)JXsOx<> z5Zk~D!bdSV_xMZsPjiESe|2Lh89z=kKTF;1XGiwG&qGN6r+LVyFM*kn-T&E$@!mTu z2|z+ZqC)yJK$=6^yF(JVL+VRFa%4_)P5^i%Ak7gbnIs@Fj$V`0Bq05BCpy)7X4i}6 z3*rWf@baJ|fs|4?v|K%nRNM>nsALpax++N-X=iE~DJj}TsmWR?BcR^?-r*6V%&gqX z+$^n>?-`W_+6n2ZRk2kG@xm~oo79wo+-uywh)A2DOTyGasFvhGB;csiT|~VDUtA%< zY2!@6|0Mk^h!V=siwv>i1>ch(N{s)BFXj9dorS6F3wn7VyCad6VW4H)P=PRbnG`Bj zKTLBt+`kWn&x&ob^7$PY|8a7}`+t8alYbBL7#)qAjqJ=m&xHSilGN=zoQ)hm6#@VA zud1V&$$!1Is>&;(Dx>k&Ubv1i`L0If<&_!o5`jb0sHqWU#Hoo#mYLo2bk6s=ugaBD zh0E?5IPHOl8L+}-wy10>~aabZxeYfsuViTs`M zDnZR)$bA5$P0hGoE!0VQ=5)3Iu7C5pdjS_7gx^?{q=LK&Op+@xjl;rc6;APFSzwTl z)$3ZyqRf(0CfTU5=P*-|HXlb4kEKpYp(3HoyqMMXQu| z`q#8#U|EC+-KGJ-z&y{TfY0RJot~u$ft+5sEDYNtf(sce?6A{=T7&Z6i8On z`kqovZ&X}BSK7NqlK|X3fY&^$g5p{ph&7_6rjMD(& zHsg*a*XFlrFw%b0h7})|%eJG-&5hii?zb;Y;VVqupkuf~gZ>}EPyk4BzNEM$CNyDM zVCFS6=_V6SAC@W19(=qjL#?T7#xnz`R}F|!Y`vhn;jDS*oDDaCM>@Z|zLpR>KSGMf z9BeK>?rOjUJ(`aWsa$UF2yD)>x+(TH%jO+__D<(hUMt7x)R0ZB9O4i)P|=h1QM?T5 zF1#88{<7#$|GozVZP0DWdYRH^RN}5hwqLkNd5VCsbU?P~B{oW@k?B+};s-IN>Bsns zZ!w=Gw?&l{C5vdxl)yd@0j=z4O1cKKnHkYxia*!@!D{RUSjGe*3}=faRc$d>r(A_s zXl}@@Na!0;DmO2^=Crh|D$jO_y2e>{3jvA#8u5q&BR4T{xK(%kojBWGXdIEgSD__ zM8?80-v+Sh_oPJA!qHZl;kCVvgYQ0*Vzh`F{9}kScSn3!IpBxlE6K3@V;W6iuc`B| z4v&ZPm9ET}+odJKFFD&*U><12!xhy^_A5?l(1DH)D98nxe#F?cS!?JuzYxwB5Kt3Ok)<<+qrw z_%>~5Y{lLW9jhH`AZUuv#KcsThl8E@Z2zNF0h zf!4n>uOo|!jHXr*4W}8OYhN4|RNV4tCL5c5pE;tD=(1nY zi8!!#aiAHID2mk!WAj33QIG_>Zj*7s`^YZ(F|yr%^!>q#G+K1cXuv+?TWb#!24Ih< z$pB)Rvj^Zy#3v|D>JGy`daj|V&)2MW@9LG@+bn{+@^IMJ4s&LiwCNSByHQX5TqHC^ zI_ZBqwmeAw4n@koHPBAyK4TZ<)qL)t4H?(sV6d3wi8exkkcX2XlCRFHF0ULrvo@>7 z$Vt&{SFsCGjT3JIH@yzPV)LS$YtK7H8&?|DV?KcZeOlExJ){4GwkHio!}1oXPQ7yt z-IQc3Og|~T$N8==OahaZF;XW-hKk-TS|-7&{3aH5T<@U5PLotAQZ+FfE^u?qIshJF z*s4$|=Gn`-&y4hy(=qYxi>4?%i60bHaO^RU*e>dB=&?7!?;*-tI@}kqlS$zbDxni$ zM`AkUIdOQQLHb!SvGz!k2DLBH88Z^!f{aFxDM0g4msxlOT@&w+|LPA*5-}s(KZCpQ zAFDmYuVMd7HVG~D3S6Z5uHZ5{rU+?dsen@%(hO4>k zhneY&#QMvEd0E<-QhN2GpvL*JzspIbna05#RA^ftuXd1t&tbKz58J;0FKV$S5iy~e z#Pwxe9B}hX6>xTb5dWQMp#R_wip$nALRPoPX1ztHGZ`!&mU zjv)TfrL^#Y`AZZHn03;K#ya6*%Ohw5u=-p#4u?vUHpXBD5Kx?060k<)hKey6rM?%* z;oDa*JVezwpO%=ft*d5AbWE<5N_{Fo&{&7H>|kFWUM{a(=d-@U$^IJ7Haj*QFMWyc z2{XSprnx9$p(Cla>ihYif{HsB@!YPjV;^i$l*Fj6{SIx??FnXbZGg;XCYdPhblc}R z-;g@-lqXv|8P4IK;S6hqK|F_;mLvDem5e@Yi+C}l zF^|xW#QQE&7jwW`FG}M(%>pH!FR)P(1zyuC83xJoCmt-a6T*@-q9eN)@r;L~&}-O$ zz-}(`n&hq(9ct!Sz1$t#KBo_H;g@2lzJma9IZWms0Q)#H;#u{$xO_07=_q&J`uKGn!-nF`CK#A)5csPG!&s##im2>5+Sj?!nxd*vJrC z6gq||P7ctILq?Yo{3G6$!uBhAsA&Q`8LN>w*g%t3BTb$gW?f+Gb|8-KCo$37blvFp zccr_c$>+to@{0dqvDk8xd#lUEf(1LyAmHjF;C*}T@@dMW`!?I}dZ9M=3hKxAg5j?L zy%&g&{FhRN=A{vww4QLkd(L`wZkk;?@vpW|vR|JVp!mW)TwIW9FH{^k75`rE>qGe? zU8y?NM!cwX=5k&h-9w@IDh-C>#f2cOI#qkhee?t{M^8j2qmid5+typ(J4Cx+)5!dLm{SN)Tt!!+>2ZQ0k0 z7sN-1ccDJQlD+ZgKe1|jQ^8qE*9L07A~ z26J6LuGU{g`Zzz`Lm;-Cw;6Bj1kN=PU)kS*l0E7-9(+&ypm*UxLFW%jDBY2y4=Ba= zs%>7H_mI#4HzCs6p2}Sxz7Iw8581&FjeotVD&2^p3#we3Ks9btzCNjItwwL4TCF%v}_naRA~ivAe&QonBM z!Wxr_XFufPg273ko^^qYIv^&VwfDp4J4Y^Bq8jsebXB$ZaU!pX>xQt-@(5pbG^zq6 zbk(N`68jS75m$TjGUE~wbLPO62r7+1JgB>E$_Oq^bQk<n%uazth%1GPclP%R=1 zXO|q&tXM`KF=|e;M-tIw)_!qBWnzp<<8T@V!*{icT@ip*y&AfDMVWgTPD*#;9dtoT zCeNlD6)C3GXC-%L#%q0yyM*weF0N30{K7|3;?GoKa? zCYFDG1;v_zOe@?lA9WI(^u46Uk!V$~aehnwhX(!xHyT3V?1_zi330y8(!y+i3twpk z^}O=@{utpkXTf5~d4*}aZh6h$~-$^q_n&{yh zMZW5oD;$)Q({uQc!uLa-Y-sb93^g@GPU6ZFnr|sq4D&J+(+OIlwj(S{vM1PKP(te#%F+!VBL+oix2AXGUSBl!Z5;^vA^I z4tVI(vrw1ahWO#XmkCZ3A-m&-%bBq{`T7%Kg)xX7kMSpR61FD{ z8!$kS7Dm`>%q`SU5$Yf6EGf|Hya%8T*e+SEz6SKdz3q>GI51Qb*0=#h<=>@>s(8ox ztM+TCb+U&8kII&3_phQLMTE5pN@RBmrgw#=mlRZ_4I6WJfb3#^^0*`ZBa}%cmC3%f zB&$^Id0&1oxj{&;M1!9N1WnTwE@1r7M8=gZWgU~l67HMuv8TzY9#)s)ia(LtClm`|D!=gA`}Nq0r2%t zB*jU~BJf==y#!9?BMt>R#05g}ECcDxA~P~GPaBiWIqXp5m)D69`serUN`FCnRhnd0 z77MV0``Z32~Nml>Yf*RZVBtu0)NpJ0{p?&SN*2eoc{#=8vif zKS>sI>~Jh&B!|T?7hD<1y$LURFDoxT@0xdzJ=smQb~IpRbwaYickE6&H&53dHkC6& zE#90^UC02L->Paj0fgF+Ucs8b;Gu3_yVc8t4*`=kTwv3q#%>4by3sT zEn6$8abRhU!N0nXLWNz(COY&=WRU1b#8Ok0ltvu4?iJzE!e};yyfsopQ{NElO&Lp} zb62uGs@6ynM}ZO=tcm_8-9sg4yn#+uF+Uos1B zcgOJ7<$O>&8*Pr+X;mu=c7AJ&Wq+q$P=3CClE09kI(z1xvNiV=k7BBaD(6%VZojWjjm> zhmY!~9f31Kp<7XP$b4qwsY#xvcZGMN(A)>s!U4Z1mlrmt;Q16m%*PTV(oCH4^!Th1 zW0;!1nc}UCBZ;U-W&FYKj!Pv+Hb;J?-l&4ZQ!(Z3G<_N(b*0rB z#W{G=vtJWlTgdzB*y57e@{Ch8&keZkdhe!tc@AQzXwCW&v;g#&Y2JrNZ|(ozyC^3ACj1n%}X@N5RnqEuc<^a@Wn zB9_a;GY9E$UcnVq)=R^$B6Qdv13?m%lxP$v?a>gWUqv{AC)1Ms9m^OqOa9iFaq}DWL}nF9Gj@i1mg41!Bu{G%C_Nds(O67f z#QxP7T;I&sN{qFNcXv!ImGJ~Q3dUgPe^4Skq>tM2izm6x4adw;$O2(zMQGlNYPz8B z=sKO`a(W+ioR5PIf?>76Hfkji28FU&gHXhMm{!3bC*MSghRe)A^nj>U8LUNyP=$RgI~ z4&js-!3D$cm>4?Ce9ueryV(~Gv{$nF!>GI{Khe%f;Co4Sdq%BS(FwGeb~5We%$H&3 zfuP|yxTcOA*edJeh{*$!;?cl3S`(y2zLVR^YOG^yUGkjn`5|&R zwkQJlv~I-r`d~v5Ge=Q!0I0(zNC|L}gV_Les^v}j;3%v>rJsozo>F{r2xqP6o8*1Q zJ?a+0xMtDgG&qZkbf4-=8!{6KRPkjngQ%IomU#e1wSlkld?W-5117spfBeH|x=0YM zcVTYG;#v1GA)I>!1RU+b2anT`Kht(qn48rpz>c6W~qDR&hCFA9_Do& z=G=&e(8Lq9J4M9xh+Zg8qqU9DRKmv zZzMBXqp2<$>DiP+Jsa_5JwX93(4l@Cc#bZR+(~g`XtFG*&7(TRy3WtL&GkpQIy)}& z6b6mu@)86lfLNBU>fi>Kzj(HXjeO!2f~KQV$%iKkh?;%CfP*3X#mW9cP1qT1*RTP~0+8gkyoT(_}LdfON7{rCUYxbRg#m)$$dBjzwaXDR4`<<-vPqZAW4-C>ZsFa zm{X7oS=(9<;edf(VOd%^9M)~b<-&`x8O$~gaE{uArZF$=uV4D*?Ox|ZVB=++6wIu> z_~tA!oh5wCvgy0EF<_fs{P2(tY7~4;O#E{6B}#;V*gQiD~s%H$Du}TTHGjp!6H{V@rip^n*clcSp`BEXqvNQ^HE~f}nP|Qfxt+x=k_Qn| z2kE#ZLQJ9k@}jQv`9RuNWiC(m$FWvWXU_8E!PLEgdoCK6pNq_Q=Horc^tCSg-3|6d!cP|Kl1l8satNU1f@VI4Nl~=MT$DkHZ~dZ)n5E$d8xhV?pLc zLH5G{R`0PQvyZl!fUgE)?}U!Cd=IpB&aW~iv`ZyrP%$b8SqTBao#W`|!A4_I-bwU~ znoM&U)`M5ZjA*5`5TR z+PB-dYVb{`aBvOoFDQ?VO?!BBDh0HGqC{Uet73b97(2d$Ta2!ww}?3p$ShvI^uxew zT!%X$kI>=%TIiLJ$>vw04J}992&#I?Q~OA&$367ikYO|hL{)E@IWTDGx%Rw|N6Z}q zPlK3-k|oyg>@x>1>RQ7JQ0yhcqfcCM&~UKSk4C@^U1AC*JIe}7XHXnheooc>?LP)A zROkFKk|eI2;Vwa1ANH)ZBW#tkp6#-4!W?4B8?+-;QS0Xr`J0NqhSb{#NS<=9lr+rx zGUS5hjPqjHd0VA_eZBar{3-;-z6ptffc0`qs~`(Yij=JR{mqq_S5N8yu6!sY%tyaq zA5WH1&UwvS3VvzlJJ(MFnUT;gq!Ohy5j$GXd<1jRFNb~JTG058#KFhqoc%P=AadlN zZjR()J2ciU{T!_r?!7HL?l>2LXWPQ91Oz{Eceq<&{1x7G>ayn@pkT%nR8n-5dOf7? z>_K#`_`d@?!UK7)@mf(!`}MTo9<~Rs`E7wOj{N6?49lS#b%eDcP>$b}J?M)Nrgtg= z6(CATNm}kOrRr%Q4do&jF(4#qxMYNvzUX-!dsIH@_03S`|5#iHk4pvvpij_@B!Vea zNcH>u81R8_M_|X1J%*qa^^Yy5bJqsvB6`rWn&2JE>5Cbj#BYZ+JL3(5qA8k5CBT>* z9kWs^o4ITtuSm#NAGLE^(aJ7Cqt!1LX`>4q?vo5WQXvSImL#s7IcbvaRH4GImfd-0 z+J6x4V-{$g=28ibJ8G?dW2QGuFL7~AFKJ%BY#>aM2)t@RW+~(Umbh&-QrHch^Ar1; zE-9f?7rKkg%&!iBD9XvF2yjEYEbvF_6aw6n@O>RFLMsY-#A!bOO|V_a=iCtOF+nLc zqBVc-EnOc}F|R*e6! zBr+u`)*Z)rU0}}&zQ)^MTX=VSg#8N0hw{ZVkMF4> zk8S(;df>KC>R=2(N$ADvOZVLsBkU`fid3?c7sa?>;hkVry<17rZ^{z6KHVT{-eV%% z`-4r8(hOYWQ;w{ybbk(hs=Wip-#&;{px?8w~`AoWMq#c9KUxC|RkI+y5RB%ZVME&@Bc#l;0BX7o{GL>|2kL~OsZ-W|exkn9`q0}J^Yf*rgRy#0{_&E=K zv4VD%+`*n4$u(QC37vr`7^jl)qrv*Tgh|(Jt~9B`-pIYtU>`$*Nm*&>^k%*~c;p-R z=09AhFrB6MVBajnjL`%?` zB!dZSOL7V3;Bpl17zprD z4=}MKEHcv%(#_eMg1np<>}DZKCml1yS7Mz8;GZVcVMb$rb3hTLzAHHEFYevlb1mI- zWqM~#I>BI%2gb-s@zY52r1YCs0r?{MV6O|7p9q9MW;#-{;u-tUzwsa);l?&4GF_W2 z{IY$l4l)(Rgk`~^@5rP2Ah#^1$Z7gJchua)r0>jXcxlPH!EnRz!L3Djbm6&(uZv)> zEBery#&!uvxR%X_^>ak2NqngL`W}R_t82=wZ-{(COwfQ=`TFWtx1%+itPM3UY_L?l^O9gPfFN zEV!p3w0p!MLD)amM#6L{nTL19I#4JDo_`OAFAi|s(C{nh((es)DUDgyav+agD; z5=Tvb*B2(zQCpHxJ-JGn+s2}31f=tHu(H_q&1z$@5}k3xdm*WL70+MFp4JnoTQ3dS z?dX{PbU#Hy;DM0e?9)X1M(-gbDQ?$xK}Bz|ac+J|S+6Ec=tv34ehJ3yrG^X;*=C+n z4IqS+L?-n<^2M(jm?sMqE;KHEdA%h7Mm*z_oRBLrh+4Eb?I*$ltUS~(n5rM{%?exG zNds9&eLzW=?$x`e@G=J}i4Da(_M$avrLDNAXQ<%k&~FBrfcxmtvRG70J?nW+ibDTn zrH{02#{OsG>y@Ng3Q&*4wIKN;5UpnSdn@&d`fHR>YVo&?9QbF*pA3rfgMUqFHYsTQ z#jj~q3O3`To?E`Pv#_7ykCuH%m3@d^SJp8@SWB|hZV^u}Pz!rd1a(X9b^^0f5xr5M z!sd@w^c~r>&zqt(ic`R5Uf;*{9p}-sVCf;U6c2S^y`m*ToLt*i{0Z4!zewzaqJ{d4 zMLaR*_mFAqlYH1iErx1-XLgvHPZr3X@A~1oQH)cy*JoY-d}ImgoXWg7$0i6o0$ z@6v7UWxIpiePv5#@seXKt=jg~7J)oN=ZulSoSNVg=Mtxxu%w-R=^C3HJ7rf+vxK)W zKrqVP;(8>RJ7`@4)GQZa^3iU`)l}=l?a;Py)_kw5aIEpXtxzj_5z^6F)2QFNcIJH7 z$FI*YdJ)l4jlNxg>yKD?1ILn_n~3lTz=*qLEaWo@uM?@R13BmSqKkx_iQ}v*yfI{4 zp@X)GnehlPu^pvMMRKveGQv{6Dn$ir4p3PMHuOHTyHa^l?`p=C_u9D*X(1j)w-fR| zXm`p5h(>1i132UrnG!t$g4XH@fc*78j=89~JEW(*bv0n`zH1>KFEjzxTKV%1l^Eju z(Jsl4p1Vwv-j5dqc~hwDU!1fKn;$ytx*WIk%l3mFhQ?4khr0NS9cIVPAFvU}1V)5W z7Co>Rx>eOacCoNe-{+OW7lZ@%D-lSYh>^NKzUZmMNTgC_)mO-2Y2FefF^`GX<5E3y zqp{%sP;a0*_Fbmy9=Ug@LOkstz;2fnoT|Qy19`_L#FBV^(I4{GRZxa~SUZu^`xM56 zh7!X-3uk7MY&EJG0TvtM(V=oX)?W)na^Zp*gSkeAJH~QB5uhY)r=@{0M)G;;dG7q4 z@1capTuF8CrDPC|#wS}!Fxd}0hah5D;be*$!1%u$0j*s4F#cd@IxPJOs5pd1(* z4xxgP3w4Ai$EZ2&0l=H5G3euQzP+>fK_@TL7b7rwpo;k+7bbkr^EK_U=O={)Gd;bE zqn>wR;CDS(j%Z2+j#7@+SPE=iWk?QXh3ohE>sWFSBgG^6x2?cxNZ%YIx4BNgYcBrR zzM|8vyfA$_2z9lYW)7D%e2I{XXoycu3TdAe@e|kyfB7XN9R3O&IUN2JL)zH>#OThS z!Y|w9M^fw2#gf=63>Q(VD&t-dyu0MrLS$By=8uS%I zmd15yG=VxnBtFk#Pv~Zv>YKhcjB-3*aPRfz?)3OO3|X#t*ep}Kt4lF+79Z%a8jr9| ze*dEW%Qpx2`^0Cqb0nxpPOIt%iZQtya%G+pTfba=hV3nm7C8g(^AV?EGPNJE9^(>K zz*C7$+N+eR$@5UICbNo@HW@Ce#q4a>2M_471m~w)Jm48qDd+-6Re+vFf-Gy86>JSo zuFX@SuWcOLLG$r#;;8d4MAMkpBNJYJ(Xkg|Ohl(Q!X1@S0HQZi?=eohU;b027%?ug zLcad=*P#m%Z_DR`EDWa$di;_fsed@KtBkQEOx@z{Ac*x``>Eb>swqA#Mhg(jlqpFd6M+kZA5e>ToV z>#Cth(>wu;t}6+?2^+HSM_Tp$82q4ONB)TQ5!uE(GW4MVMB@*I_Pt3a{dEzKPIe8f zx#cK72z%=`5v-xGcwt|O0hiOiXmG0!iA_wSp{pyQ9AjvK&SUlA;EI*jhybmibu)qQ1|VvFUAB1W`D@={9i}Bviu^y9j?IWQ$9SC zrfXs1xs=~T`aBIwJ7MmJbr|t{UMTtNq|Qk~HWAJS2*Q+l#Mj}V2K7b9sO7lBVA?Xa z17fV}OzLl}O{gTW`ugt~8T)8|3nP%r>lX>sm^G}^5D@Kn$ECe9nTAV}`Hy*LX^`r! zhy`%&VeEy#A*_S6ND6etx!`Ll*2adVnGgdha`LJ-%3GRUsL2Kw30A$sng!%_7>8+9 z?5g*?&x{_O{wy9@+A!K(kXrQsYGsHo&Jv5?yfXB%S>8XSRbCmNt=#3UAO0ko4(92~ zI@a9NuUqqt@_}G^Z_VVL{0nal%pmISv|gF-bd+|<72;NW$jyI9kdbANe2_|rK!lVR za~-n-I&&%1l?HxK_|>9NuU+$)Y$X?&t}6}!Xv6P&lyT_K_8_>EbbIuT-E@3p&i^cZ z>4=xMNkWNb54fGj|NUZ!LWulJkR<&HYbZi-q}Pu|ngEKcSEzlM5~}tis$T3JBTb29 zuYdmj5kH8g$4F}!hfE!FpAeY4fcAHui8=(5oW@kBL^|A_p_{DK7XXBVZ5mt}X#FZ3 z5?MaNCNnD<8}I{K=>~F6R)B#P`3r8ApdkFmJG@p*KVw+A10VRWT;R$SXST!NejQq` zN7*Ar6HrUG!!WQX0{oRn;5GV|vwp{6543fHwJXA!9IzY8(!POP6z`CL*A3BjEt>}8 z01|mbSL?4e!hVHZ>~}EYe~oe9pb9xG`S|xLdYXBg6ED#xS|jmKAm#rzH}?OBr7Za` zmXan+3pp%Mycm%bVek_hBmNgQMtAZbmh!)0W0}`R$;yT-E9d+!f6v=I9R3dfX59M9 zHzfDXg9IzgP-#dHTa@K;6f5P^g%z=Al_^l!!4r89YsgHFN$l5CV7u00Qwuj_3A1aN zTjh@PxZd7E33P&Ou+}GKGbW%)DJEbFEyzhfmu&`JBC&`?)})UDuje%f7@u4PI%+&g zB*&#)%p=TLnpV1*WEwFQS6g&Ze&k$T>kT^owQZ?6?sp9Cp!&;_qZNATY~mBX)(wo3 zG$m@WfnH-D&5V+cxc8o89tb?SV6OUu|Lc3^?~?+O`6_K)wR_iiO`4LX@{GSJjuf36 zBX=kva;TA=O#tj&0_k#R8`T-T;ZW~)N3SCB@9z{9>YJ8K@UML~%l%4?*(4sG@~!?p`0*@BOp;Cos%xs!u&O>21&r{>)M~5N~8G z8Hel&Su)(}Q}mUz#kJPG#5Ei-lh>gOqac)}u_$4^_B`X!Eo9W&nHN!ETZ zzlXDs)&_noa-Z>EEKb5q#6sF9i*x*sEY5$k^!dLy;!kXnRr8ah|6lPk%@+?`N%Z%{ z1oq5Ho_h4sXjRI%>Gp;Lu|a!WlH?!NR8xQz=T3hycU8BpoDmsQ>6PSH=vXknX(&G- z9in`t9Qs%=7-Zf!4(X?_Q#+;a)URLh#%((|s&1A11pzBtBk$8#sLR z#2TlWDwC>6Gfgx0%-W#?p~6!GDcLhx*yKRwX)8dxZJen-3}AckqEj!C*6y=*pN z&8l^IOfjgGDq+04k9;nex#gT9%8b|lxDfTm|zvdQWvt*-xChmFs8(sqz``Ehhgm=Hs_OOG$u zAM4ghFZNAPd0HFcU|m>=Qmu-_arHy>$Z$FF~3Jn;<$6qa|ZH9D+g;mT!4kYjJo+%Wi)9|p*;@-ef;u?*3&SJ1?v&KMdZEM*7;=Na*9k!nNt8QVuyj~agG6)PbZefKz;z4#V?LUrr&=R2S5F)s zWt&o3UF9l{7ELp#V(7Bbkr`j!fy+_>Wfmw2FN&5e!JGM8nW+(yXRirOvaECYfFx&3 z30-r%Ne$z!kuDI{9qJU_q9W*IfydJ_ap;34xDoFAP_+@8VD!b?GIcaxL1H$sd;!r94=1L*@VokvpAr02#->6XG=LWvt910rc^86 z5doH*vPY|-p(cLn0H2^qw>yM9J3)d?u_}SD6Jj*k8L;vUeF~t6saM_{sl@(&2zv+j z%DXm7vnr~bsA4-Oww+XL+qP}nwryJ#+jdfMDz-6szi(!)p6Twl|AljY``T+i&sz7Q zL5b9iD-l|1aJz06%T%zH=_wu3n8oiAIc?=`Vy8ceKX5YY{K3oDxc9AV1h;rt8T4Tn zfeQ*;#PHH#fmeO;=R9bPH7%F$%`Fpy-}xQ5VEbEmckku3_cxoly~os~7>I^zFY3S7 z#PQn6rf=VPFb$07PE{t2n}WO=LC!JLDA|PN3t)Zs8b`l%(RBOLDzl^&WV-@z0UuvY znU9NwCC^#2g({)K1fIZMtzIL`ZqRp*^fg+8kmHNk&8+3ZBmID`t2i#9@xj?>Fx&w; ze>SNn$-CJ|G}v=1;41<#blrV7H{%j+ zq1GL#Bi`zSK|N@NdVrg8vX=0DcKt{T!&@y_xlIfg-Cexi5Q2!S*9;+z#73sEHf$k5iqvE^eGit zF?bJ#LqJlGt(UIHHa_kjoDs=~FgI<}GZbt<@6TC# z@JW!MPX*SizbM$PIB(E!6jwgR7>_!9E=7;NXXiC{9ELTP54wYuuW%rI>JCYHQxyXG zAF@GM2{YHs)*SnO6Ja_uD`8wwI>1giyPfz$E;AZ<=!p0b64#F?=e~#Izvh+z{-dP8 zl)9!-JE;D9)w|JJZ}D{3za3dXZej|AwiI>x?VhcQI@k0&VR~h?C)A*r&<$|H2rc2C zeue>?2!;ZZ?C8sqqyXjQ&@U1L?n*Y2_98{zpG;k_x~Rc@g7N<{@>|5V0@*8zq>4 z>;`r%2^7D-DIy3J$?5|5Jn^33ewR*hHTzU?%|&1Qh3i;HKt4^Jd>{uaI#LCE2dW?0@3${0aTp)rA04iV*6<)|n27L>;i6szez8@jikQS#D zHW6MDl-avSdEo-&tqp7MK;4af=Qm++&;l&^tZ+2WQB%Pwe!Avqw$zF@3C-$Q(6maa_yW!O^j=i zyV=CN=c;e;m83Xlk|Quq_d#uEC4 zwzydD@BxTZCIt={ml#~*J5PPwi@@_*A(F&b^1DPU+-b2VgzS_n5bea72>h57U}vNX znOX5gtmVKvjyct@a(W>Xkx$jW3RkJVQ`Z0iD?b+;`-KNi6*X+sJ6WYs`XXhiWfH?I z=Q8G9`+Db*a){=G4D3T`6??>&YdHXKuZ-K9WZ(2PG@yGx1JOIEg}5`oh4?L9$|qp^ z<|!+1d+#UW$H>Mrk~YxPTrn5%BeEMlFkXF(L8ea!qk!UaW+XWe^^-(EO7saUVt4!c zC*X6S8~BFxxl>D}vv9+bBo%H|sZ%Qlf` zJtiv0|LW+Z%7V;jjQ0nUuVJNYs)?K0Elo`oGUk`Ps&uK`-1O*79JZ+K8WCdvfVoe4 zpRHQZA0oU7PQ2VtH&46y8{y-j1yB!WqC~MGrcK#=H+_ul*{Y|0TL>O(%GhZp4MB|_ z%le`P!+U zWBaSaB3gmPgog&(dX8OLgfPlDD>s$LOmcl=w}S_82#%FsnVqC%kg z5JUP^Ns~l*mUdzJL;g*A+@I{=9yNzX#p2}WN{w@K^})mz)P67>;FxHD~BLp8Cr1F8V43&xJR{mexG-ZSq&_WsS^P9~#gpt;=4*fL27KbrSZ$(d)+nqYNnHIK&> zqy&niD!L8(z1D~E7V`aqs|+g>@9q4}02 z5!+^~kdzP3)TRWIj!AS3AZnj5hhNyHFeB@`HKBtEUSFoRagROesEG(}FHmKBE_+y(tRZU$SXfx4zY<6ncuz^*l_h_?BjjEtz|Gc@%q zs>*RK=SrCteXT{dU?hujEh+6IFx1D(p{C?#MobIIC9wfaW}}I+m|~4pxN0qQ#|t!# zP3JL^mg{y>SsDGPCEyTwP{8lW+hFAJo;UOLI!)xu5{_**3KLC^4vvSEY zJ4Ql-oY0-P_?8mbb_G53@*g(M2w}|-)E9M~s8g{E2+O{xmBQ?xcU8oId zM3-H-jUbOYXb(G9+#zmb7?S|rNMT&PDTQku)Z3LaYM+?gevlI&WTx2ByukL*Xc&`0 zEm;X96-K{&j1LUyC3)Y3H6jOViM&ZgsN~>0S8%fa^Q-7?r=}QoAx6NiuK~*ZvDrx(Fo+8Db3L4Q|27rpUK-nMKc@NL}!a5V^_g zCs#K&A@+*9TZgGkOHSTp8uSiG=_+Kzq&~1@l#pGq@U}=kjgDY%H5Xf>Q!br5a>OXv zB8{YHTssN1dsg_yWQQv0(;lHJnDdgRc6R=hS3u=T7)Q-3ymBMdYc0cBk#7^j+|Wq5 zU8b~twSem8isIgZ+7qd5a9F?cBTr*U*M*%UcHy20BF~GIrKIpRM&DtBqUH4L$!%aR z8e|Tw{EJRL#2th3?$`WjSc6r&;J8zq`oL*z2R3{+!x&j5e;*P6$%PC;1fBrW9whSB zWETR3o!8kKfyURZ;n1`lZH1fw=EP1(La{S43U4}`U-B281ItX8z1i4?_SP)yJpLES z^kmyUJ@Yo<#rl9(2%y^QIr{Jl`# z9-Y1#+fD%94LzYGNk*5;H234iX2#NMSg4D=-&hTuq3(?~wTNVDCKo7(@#1{k8 zp_;jjcNiSNPdo7wQ2U(Ud+~-c@mwayU z?qR(YST?AK8;qi`XdaE9m4`?z=D0RhbLO*r1^S2(W7nBl_tFZb`p8z(wOn9%3|`pV zk-hpWRZ8sB zp_ZS~%9b5BVG29~G$(h0E3J{8RBG&FIxtsZ41%?iqJC`>%&-fNyq0gP=4fuRqp&)% zTx4SIwT9EL6|>S$8eHr!K6(pgAv69n&=0V2#)t}g{}hv!OwLO=L+%a;_*?%^;>13M4*a7 z6(KJP`7Pp=5fkL7<4jQ=eiJrJF~P8!M#>?6!3|~c355{(`RY|51@di7I-W3KIZfaE zN8omm;uH;S$%u>lwrB2~IcNUPGxdLd?*iU89RI>S{|dY_8xu2d{u6ko2HA$=Z>K}t zH@=I(8tO+Y|8L;^e|;trHU1$$41cZ{OJPAJ?pA?N zpnc_nH5dM$xceA7L=~4Ke>;Dn6oc7VPs@K95DP#5Amxl)12v0BLg~^hA2PX*TxdF0 zb-KIX!LGoj5nEGw-obD~(UIwa`l|dtgyUU9XLVhH1R?YYdoc2Kipt65?GM^Ux|jkb zyDL)LweL;^Tf1@~BqQc-A6m?f3t~jL^aDp!>o+YyvK$_0QrJb)#L( z1`X2biKpx&HY^cN!y9_&V`f-`f5^R+`hcc{^5V#X>`EpO7uCuAWhaYYPEl|!5%c-2Jroff0+y!Bb}Qa&CZY+V$1xIi|MlZ{>i!* zo5o}_M1DXEr`Bqf$juKY)Q@u$S$=O)YOuN>cPd!j2Oh4=;bAh?Bi=M~^>1$BwH@== z_Ilg79aav)RB8UbOh&HGUu{hnw@?WdLS~{~d1kt|Yc8)Qv!ST4ShUUApKCIw%;ZO1 zU=jT^g5j_w4RO?798J++X^#fz_)pH}M45O!R5DNjt1Nco?zJMQ%{^Z@9MZmU>7}SC@cNK(-*x)GZY2JHO|t)=jf8@m z)feW+-pt^CAXWaCYBH$us)M|Y@(D-9*>79mU)_$fT?VeKX3-Q?Iad#Ta(pAIEEZSD zF`=Ctw3+7Q9PGPPY?e_N18j0@q_;wvSHW$n5?c!z2z)sDzYOe*mWZjTY=hploiJ4%78rseM{eVC&RGv?|)01O$ZT^B{oo( zRCo;mKII^gYJEg@6orxTRAoA%f?R!~aD6%yRsS|1ON!zOS%qm3(SFBneB7>bCFFU$~J2?<+ky*l>h5ho1A?plX#yOglI8a_WSwb~uD#LSx zTH5xtl1C1IOaMcK)fnPlzEDO|n|@H6vi`%eGxl9gs3U>Tfh5x-y>^MLHVK=V_)eSjK$(Ib@eQGR2qo}WJ+mc95}+ZQsz&nHE9%2q*WuiajRDtiZHyb z>r8WTCvL^b-go6ArDcqknj*msWcbhpR;8y>93R;8&j;E~rg=^+;4By0PP}(pu`MlP zrCu+f{rO$vSmQ~=3Rrprx&&*L>~o~O8I1{)3Y;!L@(TAdn01sDG>8vf4KT50 z21cep)&#P&L#r?G3V`N^D6SGTu5qEK*91 zKc}mTKeCDI&t>#2H1p31TO=QZ?a#3*)}0e-&z>uSU<{tVZDPynPaqw((9a^*MHSv= zm|jf;(H>Ip`jcXxcR$4tY>bK8 zBDVsi-f-8-CBvDzk(2y%^b>hm_{D=cM#7-L^+H&$fYLVBfWn(C^AliFAUVNKf_U87 zwK-gccm)E}{2EuYPznFWOmH_vvrBf#zS!tCQJX|iK06It~L?@FiWwgMXEzK z40?aiA!M{KN#QWyQ^j0U4hirI$}~F4DkJo>5Q;M9*q~IqsnoW8z5?p`R~xv){&1_i z#$fXX1HoG2?cru2wi!aOApy#CB|h_?tE0B@->a{?ppE2I;%A z($o8Je7G5GSY{ECfP0-aolxxbtau+s{YdKU#KEwY1WwG`FkNMe_(oyM>8~#AY65UH z)*TIo<0Q|)x(odRuP8ac+uvG!JKxuv$^MAoF%3EWQbV&Y@FqpT7Zv0WZ~ z$tz1M?JvqVabi2-M(0=0Eu4T&v1P$Ss|uRVbR;DO=XyYx##}C^X_*!YdDzH_;K={8 zdtI4>gCOqH^yu^5NtV^=XXT`K*Y%RfN%6V2l%^?bQ^@ARsQ3y+IAj$%{}TmV-gljX zEWnpSc^pWNc2UA_Vpg1S#i0_;m?2D`!z0`vVfBJAs*DqzOskXiuq5vgO}a2>WZJ!6 zq2}29mBmntA*DUN5lW;QQV#4mL)}EHFJLJ~msGuXXVnnSVf#p zu_6xlR9%)PB#3rZgUHcVTp-AwVGayB0ATEK0G-l(-M!enD=I=Ze8)B?d3bB&;fh=~d1n zxRhZsY(@U^K_&@zN7X|{0ZnS%U{yi z*x5CIhzxcwF_N?vPDcMHjeNMH_(I^(2n3U7bSrRA$M+9zz0YY9EFwS6#Xu5 zwzYt=?(-`Z5mw|N6wyq(XA7l7aeAEm<G;pI0$Za zlA#_n4S2k?Y^k(7!&@ki2=ez@1*Ka3#*dq3sE18Uu8YAV>c&uZPtaMbT@7%Zwqw0> zSTcPc@5f)SgBi?r4x>|Qf^;IN69q^qq*wM&LN8G+|w=G=7;-^R$PhaHRXTsPipPGD9Ip{+ac4Y7qfV zU)PUV`9$bzivKzHlzQZN*!|D-o2Wjzj`y`s!2bDf@2r25mH$P}{O=)3n_sjU_z&p> z7UD13OgB{NTPQgBcVybieo=w6|3Z}7z934f|9NadYz=YN>htIhkkX z)tIh&c5QAPZUxtE;!E`X2cq=+1yN#%G;XZ&yh7$h_Za%E@BX9ulI$OdQq4_(=C`GS zFIm}P+)5A}NQFRlma%b9y-T7O?6_&>L84RVk8Mz1XVR1*0p68+I9^fyTb9L~dAzzJ zxud@;4Ic%e$SZ!IzmlU1J>D;5j_#~I|5e)*^C@B*0N-EvmkEV&!nxVW>;g<)w$vZY zm_DV4>k!B#HiJP;C;0{)8QbUlKAO&@mZ*7wp$A?1ZL+zoge6irA|d@e(0w~oWdXkC z0^?$;_i6BI2l|83;?{)P*wiHaNF%-@-00Mtq-q`=HEzD~&;Dy^%wk{rOS}A5X)tLK za*dMu1+qcgj32!t23XYk-%$Atdpg2{31){6lYHh#eBn<#_+BmG3~2C{>?slVYTo1g zxT!vb=obDsL> zVoZpzd7L?*VEZlN-2V+w63`o=EB*o~(WJxRc1wQ#2cR@>-VkN~B`Q<@Ra8>^`OxSt@RxNW)*omSm=gPB5a{DBEv)zyVYi5Yob4X#yUw(v|v?!Bz(93w8B< za5Z^n6}UpP`NqF-*?Fu?0CowuwO?44NQn4m{Oj1%R_@y-?%OKqhiEUxU&Y5%nrlAB z&c<64U+c21PPf-P${6e)w(Q!dbs7Usgj)l)U7@TIt`VVFf}KhOQ7|?JuAKc+)?nAs zYAc~G?bkG&>ry(bw};#?mnhdf!H>7IKQ^B5t?l{)xILc;wmegJhzNPcRCo@D_|M9E zhOYq#9-Fqhc>Sap)=mYuEn%%$kFP=-y(t&_0UaBv;oLAT5v?IR+6XzjBDjAhy{zP@!1{qYJSRg$Y(rOM%Qxu# zc04>g!r;>O9(YHSlbvqBp-9z@3Gcn<+>T!v>d_R+8*BvWu2Ltt`Pe zE@GN93AnnvvX*tMhZn;z^yaXVRUVI(nwN95RH`Z-;+f~fM-~aMvu&-`mkGK&ko~2a z7@rc+n|#`T5f2kuC? zIsd3cvcMESBQBxilA6L(Kva)RAk!79`y-ji3ddlH_$QJ?UW~!mTfpG^$<_{z`2x^) z8WKLN{z8!py@8sfe!$gxh`NNqT~VvXu4ciJ!a&`)QO4Tl=SY^iyWpbA6{FZhHrBDl zPoxq*X{gfht%qN1l>_>MF}`E^LtqQ`e&0c8Y%FcG7jaKcR%^YLTDvMJ(bw>G<#x;^ z#t+)=H~bWqRdne0UW`RTt@*V z4&GPmo~v<;gPe%`uF8EDwP8E#0G%Ng00{$PyeL4GeQ~_E5CwGpJ+r$Wq?|KgC(7?P z0Xz0FHshgZ*o0oLJ%5m%B`G`MC>l=*{CzFTjXx)cY*TuBpmT*G&9YXP`SEE1^&4^MdVIN$Kv9H|PG`I{n*>IqhM+;GvpU9X%%XWxHD!yLH z-k#T35v(Pkin=jB^S<3+t1(L0VvQvG0+0b!3i z%BY&?@ojCYssfDeG)9=wU+f~l^g3!(>y7V4Z(Wqv_}hVtcBI_4Q~|@CE{m5!6Q5P8 zadkmAyWb06j>uJ_+Zd%*OfLfYW8yeM$S}D`CT!2O&i_@r2Ufh9nJsV!7+z>JYj4`$oF^R83 za>O$E?jX#ho|Aj3e=4kQIEHq(E#>-@sEZ4J*`kwYh?t*9pRUD}t2g5%4cF4`mSqhDA*nj%|0P3(Hg$5vDWO|Gy`sVp z+2pBY3oeQ?D27KFl0k8`zrb{TF%aPW;p$FgsGAC@?Pvi{?;i6piGG?n371_4%@=;Cmdw7(CGhI7E^jr$DA27+U~EYd9KTmooe? zXJ|r}s3H68hydKlX@lk+Q2DiMGDZ!YCx}E0W%%$(z5nBk*;e}aj5`F*Ej7HHtg};UPt^_$ol%ZWT_s(Uu(8;L zCOzDnf!*iFNSlZ^5n|Ki1jr#!ou0$-j!yN2fMv7l&Aq#YqClVXmwhM9Ogit}no6H6 z5FQbh%4Gws8Xs=p%Q&`Sfp(;~I?Ow~S(MiGvXiYLTCMYxMWa}e#ZEpg?OLkWTlV=6 zc^o6Mr{)y!?VHQLE}*FXMM4TF%Ky(3NyY!xe}D0Vt0?`)0-;KCcb8D0{C#22)Q}R0 zApu0dv64A06FUtto!IM|H-=D>*Y6)>du`xE@RCnJEBlMT4l+3oQx0oiK-ouJUvMhQ z<6!|kVU$wz1p4Yf2)LB7SAzIReHqAyW&S3vG_~Xyw1)c_@)g$Wu;Vat-d@{0jg!Tj z)~QW1F6&WqQn8yEe2Wpei;V%fidCr2=)my`hhRUamPyw9CEUlB^DYvCwDfh;b-n!r zpS$h|jm7b!d#^5Vttke)ZRvTFQO55-S)b%oxeY}RT_`~m%(eG9-?Au&3e+C*u$n5* ztHkFWeyd*enp{jn3ZuBt;ZMYdgE$7$4_l;3V_OU7t;0-+lkO*@H3?jdlaDp zC37_xZY&_vh~x0C$BQLO3h5RBY#B@z?akH6d_Uacap{x4$0RdW4v@GnDXoqP!ZGvz zxb+UD{7z!*&jT|zPlep>N6=*|X%U~iAfKKoal}BXyA5g5DxsWb`fJz=bBg=sWdw;z z;D`!(pn8tOK8d(wzn=8CV~i<8Zz6~B;F!w&*EE%Jc&uRf5_4C?z-zDL^N+%x!LwL3 zSX~N0nOOC>aPGClj0iEQG2#wU3wg9OL2M>(C?QrvEyK^^Ou_UsL+5ntZX(FYK2}bRN<@yurHH!7X--pW)90;6L{e_@3M__Mk}U z)PhB>QzLJo3^S4X1$OVi!D9@>xuhgq`TG)}-k$tH&_2ZckaUo1bmFfQQ)wxlOgz$9=W7b&y-$Rfl}WpYZG^CHSjyLQ8U> z0zPWcy-9*o4Bs?+$De`%br2V18Olu5k>Jf0KQu_4S z*oe0e8W|k0@y7lgluw6#4@=d4vM!gszx`~FMX~(&18hzW zSd%Tu-VfQlHsoqa(XFW96|8M3*5}YOGvO+8iHXLZXY^XD1FIo(k8e96AR*b1M?2|M z;>=?gaWun5p165~8#xz6K zC$lQacjrJe83inB!?p;iB!)Ps-?^qFvmonkASyRJ=MM*rs;W*?$A-)_h7KPL)bIj# zRE#}q=jYNC6{n3ghV1*yWeTj z&ml7lrWZ+0%QL0rr&nv*N(;d-QpAgvJ%MTyS*jhU%H9MfhYq6(Sis!|I^BYVAOgHZ zCFs%}Ua*7y*oD>BGcR};to!-d+Gw2i?0|A_o`ZVDZ8d`@(`yNsQZ3YQB$at<5d$G8 z5xyEL>g&{m^#5M`-P=bzfd=PfZ#zSJb*{UVMHrMz$^@1H70C6%@L3Q3o~$kx$NO^v zP;M*o1cd$N%V1o@3dDEso+_JxS0$PziC5T_h~sVyx;fN=Lc3jL=f$`#tio~<-Nh~wMC_Z5>rHud=amOfeX2lA5oW+WLBHoJT zZ;qLD4DQkWiun*pm}ZQ56gpeIaaK}QuN}ld4E5ST^qYd55@3T_aeY|PM7W>QUc$i~nF3G4M+`ja=)TkRL!94hW%S|h%^hl5I` z?*9rgv4U^OApcXKl!HcdnUQS-V1Cgw|4XGQ{AX>|F$7fk0)G|7RzSD>HBVY}d|3VO9m$hIHN^_FZ}~zIYGu?i%jw`iWd>8R!u(Tx7YZ&+oNS^ggGO@RG<)h}>4Uz8?-?S__11P~9qCCQ6&g?n zw3uRDU%7ONL_%?iOg*BLjq?1-0DO%s+CP~Jk<>QX1|k@BtHm@lS8K7>eJTqL&5|U8 zxLZns;~#@Tb>8$SdO~px{vJ|w_xfmTr~L2KyMbe&);N=fcZM{M{FM=?fy>0&tX&;# zwcM?q9{o<^ zQbuDbj%1_$q%yYUs{?LLs(SW{XG#?4zX$~(sAP5RQAW_|#7I@eW*w_%L6ZxeK2475 zGWN?0u1cY`P-u9G4rQysBMqWI<5(rJY3X0|3UY(0bjjzUgN-LsLu%iqET}T(MO2-n zdlpckk|Qh51Fi!hDGCOe{+wYmoaBeb>UUlBJu#gY#^HvvX2aap*;f!9 zHP0SnoC;=6d^)1i_D_PeSvh5?RMt&qrBKg9oPog`%Mrd=t}R<3Gg|KpPG>I}1jk2n zFowc15UFCJm3g(9HoaJ!r%owT#h9#1A26shnJA-Qw8Ydt?yhQcCq!Z8_ziA)4v3;l zw9&PQR0Yq(Ui3r5)aP25(!AWui+%{Fhw2~X79BsrTbPHtyDHCAk3+sdDADowkA)0~ z=u;IBOKy|*mAh*&r05xkRI{9STMJ{+LyITa9j6CLmmwgF!1%Ff({MVQ ziA|2g79F-EUw0OmuC>nPAlBmY+=15WHJOe+o54cZB7;TcNZ3aDwCx-n5qA!0F~kJC zIYTRYgQ$C2+o!ZzJq+u`tZX#b-pe8;(~u!#bdl=2+zDb&Wrr%9*m1#;UW)C6Zc>Mw z6FsdCVPcEDQiVfs8S61+4{>5@ZQ8 z%iNf`P6rcL>Hg%2R!Hqzr<=@imbo(NO?Xqf0vFyx!_e00dv^`DX&vFz zIlMOG3aI%b@saYcQzyRH~xVg~(NpyNub9bvm5;EBiV z={wHw&%e~JQ@*m)3+)({DY42DSPK}I@mI+hPk)svv-pw%BcJ8jf6)7#QrV*pTm(;0 z=C;~kH|>?4T6aQ@I`dr|KDxhRBr2bE?NN=n-f{aYiMTLqyvG5UuAKMSSYirByMjXO zEbnEQrE#?Mat2s(e9W=Geyd7by3)*D?CE&saZh`X+y#ZPEl$vw@j;95l7&M#%TAUs z99*HZcEyx&mar^-m|={kcLiKG3KG+Ih%!RaDy109y-~I`^5b`0lLC;j^pX&W;@fvP5wb z*CodA*1W9On`_sv7!h_fvW^Y!RVLE-h_j9(1l@*Lae7pi{^noZOos*ylPwOj((z6`>4-{p0i8Dz=eZgtg#| zzv?hE&i(!!vi4ySNtP!xJI>8R>%h0NKu+KrhHaR@(igr*(wU(~_?=zj@caq7LOAPC zNv^=Uk!tS12eEgaP{3JmLrZw+G#mB!3mbXsOhn03;rKJ7Znmf8U9%OnOwt8K_LtffiZOX%*yE!ZJJ|-I|Ep*kvxF?o`6`Id$y}DnE z16wE*Hgc~#d-N8!vTm|RRA9pqm3ft`x=G~*>D|tYLxPe|s=<-Z_9Fc(5@r!vcXottXGHws;&M&*c$MP^O<^>Vf~2F3pT+3|k}h|W*GeL>|o zgbL>+RlCScF$4*L0`~qmx_S0^o_W3UmMCuWRAu6q);R#XL02nDeE@B>h$}~Oy#j(Gg&m&x;=WoE6`uzFNYMSg@V~WLB%#9QK z-&WKAHi7xdsQ=Fs7*CJG+f9HE>05Od7qUyU}m z#M5NqO9WA=qe_ktfCee2$d-W~G~B9h#ztlmV1;KdLo?R8^kLi}D~&-=kmZGgOfm#Q zChx`?VQT5aJLJd;84I3p-Yj(d|vCNIfo`OxPY(NWr*VUAM^Is{jY61D6 ze7F-D@rM2AnysvzZALFEVI0>*#DHj(_T?o*EgB4Eo-)Y+#rZ88lF-`=x`5r-Ub*hd zv7kG)peF^Ryv;BtU+eOw8J>`SJmM@;uslb6@k^}UDGU>Pb|D9($<}Qx*4U^h5cNZm zB8|tubom3cJJ+LYS{Cx&7lPWQ5RtawI#1O7jjniPPPUL>8Ya!6$^8f?=LY}K9Q}>^ zeO1yX@%XVho#BEqi#Tp2A=$#oy7otNaC);6vIWs%E#+JsF5AkD>>$q1Be{n^dV>Km z9k|MNX}G34zZXSdwtmYK0t~Dp>0nUS{~ojcUbVhxe##vV@k!QQ(HxPeOb`_5OI@-v zSIb)CJC%CR9gMA6d@Y?ROFSMvvcrK(?x~g3hO53@C;m3qI19rEM+3i0sJ0$h7iYh> z1yJ4l0N6*-j`%dP21)=W3VX5R??6g~P7BGy_4knM@eXs3-bnar-ay|Y`?wbJmMj|R zYj!dJg24KKW*D|5@^&5M{By3cX%wa=zrGRNfBi=OPu6}#`TwNd^8RInYuS zHI%Oip%zLM34UI_sVY=F`Iibv9#w9va;qhcp5AIr2kF zF(wvm52UF&Q{+B$J)L-P3pE`WF}FLsR@OC(CApL5->*JU-Qou18JcVHt5Tx11!iX+ zToHfThICX|4wFQw+l`adC^I^*a>ddmEvWZoUvK8lcLmQvb6rSxADu71k}>> z8yxl$R)C>1+z8@(Sl)!2Ah2SLH$G-TQ5s8gX%o|o@{=fyj64bR-zBLJg$HV1B!L9Qx#sSF@nZ{0a1SR`imbm(_nboHsxdtqx%p{QAKcttuc&Q2n17JSR8c>?iRDiz zBpw3%tI03YFUT*6U1D{ZI2U{bQNERYY)+P#^$EkAp%nJKh}vNulwtO6n$~s}>#@OX zN~_mLg;zZ3s|9*>uBoIM%tx+J7tV}ws+w>uI%r^7x+2Io*uw7_LW&%qP`yxHzS7qW zQ>_1^IGiwhJ$ZW_LjQo-He+CUO;V zgkfg8j_Cdhs4G|8V5T}8SWC= zmMu8$dD8n{%kGSgVq?j*(0+D@h_@$c%_*}atF-vLBu{zCrWc=}H>{FG`h5oET35J` zZDyeS^kjeblvkt*gnZ^ zje9I@ka^eT$6u9=Lul>eqT)C}*2WdEw!p~DsZW1rU4f~zgQH@W`8XSE-B0Wp2nKu! z*W&g1k^T^1T5@mXDR~2BraU7?`R74ZGrVO`V22lB3vy6E*lX6f@iWgdulp+vzW0>0Tc-R=SzY*=}=V*@PcJIfP%&{xPfr(&t-mNqc zp^YaFl5{Oc!0V1FmqfRnz}xH)p&$ZOwFmOtu+eEwwmVpEEpT+36qq2o$gN2jb+yUz zVG$q&3@w{!r5CpEv@?jxgk)VFlAaa%-1L|SdhFIIWWFNMs}IY}a(?LlVW0Yu%WQ22 z^1Yc{F*t&WAjIrf^g`x)lwR4+8OJDKqmL{m<$`Vchzk9?gUlq-C#ffGP>! zK#@d|fVj|a%taBZ{PhdLL2_t;QG3z;>nqXNoJ{XnJb4+Y1YaqNoL^mg#t7wvPC{L+ z4o4GRCKv2PpD(w2*uS7rFc2>yYAJ{Um@z1pgdTqJ%k>EaPbBZL@306cZnD>sB(pSj z`WXlN?ZK|G^0~7HM7qi1sqfjX)0um~kZ*1u!Hjc84|l|^AN7h<@B)D*9H7%s1->^0 zpWGYDULGvl%Nkb)H-~%c2wrY())W0Syiid9f%dgiNXuCm>Q~!k%t2b<4nlzCi=6K5 zW3(&1QVWUn{s~SOW>HA4;auS}6%MuQfM4sfXdKYk>X88SllE_+7S*)qyZgNwsicMe%0M94@~C`F`%%Bau~2@#3#vniu8 zqJ&cVf24F@?wtR(=brB2@p`@YXMetjVv^rlZ8le}4pXNmJ1;FtqsU=N@pA zaf@K8EOMZKenjTcd&Q4*Q_;3VevGQSq<4IsKE0h7+ zGtuu^JWZ-kMlDE{@Nz!qj(jwtLH&$%n^Zl0!wA)9&4U(w!>lP)&7y}Kd)xN;TI9+5 zops==wz(X1Xf{=p=Pe1`uSSw)jtwrkx3r$S&N4!#WSAyNLe6EZ=iux1yi?WgwJrXS zuN2sPXNIF~4mJdsEQa4Nn9wUPc71pu8{0cLN!s7&cdX!6-iJeH(k5J^{Pf7N%6lFh z34avLdc*qOj`*fg8g#ts!EX5-_sTBUiydyNH?-WJXHM+}mmfEpj14eLeE00vP>L}1 zYer^xmA#%tW6xRhj_J&Jvqjnn=b(@%tVLc2TY&NONBIr`eVQHhhs+I_7Pg zVc4wawabUjT{L`tzUb-^k&xVb$aw7$tP$^#QIu)w&hxWw#@7p}pU(&g{d}cw+8j=q zeux{Kya|&Bw~M*lQ0!##1-FYi-rgUWeim6`N|&<3pvZxnjp+$ocyp4**PD!{@cv`h zGfaiI{JvdGOKuyAg-(^PT$hi{!<3!xh#h&7A1#SO;ZglGHru=z`S`_qKHHuuR%>pI$AqqeN8 zo0m*^ZY0lRy`R7LU49aoU0AwatRE8lGy;jHpvS-Ruz+vN?_kx&V$oPHaSRrXL1Mj8 zNUug4cl0F#=EX@{sTPC0qc4o!Wxp^Io1h4b4nMYu^s=m#l_sl++kIB+qb(zr3;lDK zQxPdr#%Y%4XKx*F+4SY5|J6~B9|>%YC~)x5$kL?xhRZ+R0iN4GIm7cZvbRu_6;AR64weP89$}m9Nh7VZ zxa`R7WOmluirE6bGbSeI(&ZC5CMV9nD~d4VlO6Ccb}p8CaVFBu8SZ?A$<>;cF3G2- z+Cla$Z`XtUcE1pnqzf0uyUKaQ44!cQVxew1S%NbDI3rQ7Fd$|}=eMWlzyZtW_Fb7d za^2K#;6CU*nL}wF?4B1Z>uJthX0yj)r391cgviE&vBy<+H14YX>@Kr)SL=f+9@a0m zX=Jjw>Muxk>DfL9Tbr{!H1%_FF4xv!cN3()52ugL`Jq+tVc}( z>S1fip3?5?QgN}8GZPI+XYGp(d14K^-SS4~Z+AtWvsjo?7duz2G)bCM*S+iAnEI34 z;Zyge=t?AC#L)al75nC7X-DPTC_lD1SA%oAjZ};CxL>7>80~Okt)-7`V~ketO!IQ+ zp1yz>Vr$KvcnM>FrdJb`cF{e2ihCdR&ib^Hmnkj%g1lWie66413-$6e&GodfsrHm^ z)}Mj}X)`=V+*VSGZ|=NcCL0j%@ZO$UDydrN9DSle^3FlgOts_F9o`+2e$sa{Q1?B< z$>FEmV+BrHL^x92mTtvTjr7#WW!f@swY27X!P(q!_5^}y*WQ;03w0~pj+jnPlMuRM5xlasSmzn_XvTXoha=wVpnm{N!=Y!TXz9Z({ONmbj2KJ*c&E0R&mkw+pK1qxTkj*>6wy`)27=W!u3>YLbkf7P5R`% zydL68Qz&icH!#TG^zwc{_MPN0ipp65`-ZFt8h`q~ZkfbzDP;}uWH1>kJ9(bY6dx1f@AA9)mZabrtI}h?SC}htXtuVZ z`#FBpbp!D#9mh3u&X4H@j^&0rWeV<5eyUN(8=>Mj`nWE&$(z1)tklMLBq?T3P1Rd} z4(|jE-H7veKtoTD*teYg7S7EUTtY*fxu^qGqb{GjQ-a8Q^S12J=$Tg;TxhG0{5W{6 zOrz87Y~-`7%~KcRXUb(;O%rT$N!xeV`sBZ-tw&c^{6c*5=tw1Nd7x)K^+JL^Y`iS~ zu7~~E=*jvK{VAPnRMs|hXD(}vV4AMZ0LGj);Kq@~xaryGH|?M?cjLJyU{*HznsE z5Z*DZBl-I1fsFSRTN$IpNsAUPFUr(DJ=zhe_E_eGm~<%nS9_9usk_Ef3I;o4`&6b* z*{Nn(Z1#+rP#Y^zJ%<*0`t-RUiP-!XKJrrz?=}ykg6f|dZ_6(Zy78iel=h8{;@0?q zq2l?O!AA61+OzXYJwrheVGTw%i{w>w9ZL2dnx(esACJCyzIk&Ec8jNfX-8kyRyz^( z;?%7nRr`fnB7DyVDZ6~B9;iKhXAh>Dl}e6dciFh)G#jlzl*kx2xAS)$HYZ-+=If!C z$K;N?93ee`;U4&@opbo7EW`WEZW#iKYEp%uY#QjUq2ZAgrb6YK^aDi`Hu5_ z1rzmy)`{&GMdAc>bV7o^{&bjAj2Sz4BK5s6n_P_%gX2E(P>)LM>gEf*BhJ;~9n?pD zJrQE~Sy`sfCBmN2c0@+(TX;D`-ZfNDS3^WbdDnl(B%F_dEdxWcOr|!y9QTpiA`)x8 zwju(lZn_3a3%E5WioAL3FDl}1_})t0{-B)mjZ8Mu;4RGIGpb~}vWxgh_atwj7-Lfj zdj!i6ye?|MM&~v&|$ojhc#E&#^8FXm2L5 zWmXbn5ExS^RmGli43f?)yI1{$>jm#(yB(BArtaPtOZ&UqB62E7Pw0#_=Z!i!-Kg*y5|1pm%+EWD zNr>`@eop!zUeScsN`@w$-4vd5-+wo2N(gy?>o>{{o*$1C3#t!M#_j(2y8bHX=fG;m z^P}gPhwo8XZP~A=1i!B8d*o0^i*dZk$%4xq6%{8dZq&|y8~?$&cywP=?a<8p9OHao zjLbr7F;ywmkxD7i#l`t&-<D zKT9go*D6MZ3v_R@(#WZ?H~jTl;$cw=@iE`busWOUJMWcB%5SPdpMquUd|FnGcK91xJBt6 zg@DOe5WoM>BU-&jksPFL2G|ekzpi>~%(mS))5lgo)4efjbe`oDp`@tL1r83^KDXCp zn7q@IG|e0y-XFF%et$ZsW;UI*IpV9=6Kb-W+Uq)f_gtRdofeRpHM%^IbK=06z2x+Q zG5bT_mHHafg|&FAyG-_ZUk{_vqYbCwmp&pyb~gjOM)nF>Q9iw@vw4SZbyl0axNGG6 zS;u^So*NzwI&x3G$1I$D@?xM-o=oH_?T4^A%{-%-;h?ArvJc7_&$o9=2DMOnmVtxJ zmh`MLVrgA6Vw4XHefxLiCk9M%hooFkw&|FTJm*uMStk{mI%|4cDCTx2DUxF6_nwBS z+l9V{yIwps&h*&5sp0g?x!v{wT#db*FRN@>GT^Q!?IgsT-tZ9+M$ z`O{7YLR+jSG8?DTH(8&Hiz1^sdd8UI$d&NjF87~dHhFolHMJ<0CZ_x-%&~NRe=fGh z|Ip5}4l}=RweU5(mOoAfgHZ_J&ccxLK?e-mR|X9F9*$_7;evZYq46_GChfTs&y%dX zLbLPrs4L@Q1dj35Mw1y1|Ci8yfPtl!{OczaDoH$l^l{2t)<3m$rvXK}adg`L zZxuX`b}2nv%>Q-b){(0}NS7uSN;XLgeE^m?0wxzs4L6Z7!6+#yVXFsMY%8`h#iEa+ z9FSNXdlg(fguW!n8u{tlcD=Gr@nZ&EGm>#{7;y~^v^H}W0c?1a`qbJg|VxB^`GaTOJep{ zD|U{uYI}Y=-j$^&gTX{4Y`!uQ=ysyLBzlqas*f5?-dq9KXn@xI!K2Po&YdM^hXcM_ z?X6>Any+|g78X1xn70AaGySHIKauZL(}||=f`_Yp^=7tQ`|=-;ty1)43{={ zCEVPT95RrG&=j}PXZO7Bo-VtoDU)>b8CcAd@5x&aI*Ye>}fZ* zy(Uqlapvt?B~$AxFbprm1{|2xNpuU~r@K|$59hY^IEV!ERK5P{rha>(?wY!bXO6SM zHF1XNC#Kw66e6}7mhdrIS50I*z4mHI>%qHs?2#e*L+OtP!hf9DRXpkOfMr&&`#Xa5 z#zIk>^wM^$+)_4;YTI-Z)iC081@a$HcJk2ZX&yhtmr0}E{pr5K zMda7#e4EUoJoxXE=mpzlFjKz_?$Z~VePrKWEExAQJY!tz>8r~#hIg)#8cXM2P`uD_ zsz&pcCPT<}<)|w2>lg#G?dZ7ck1y=tdHDibb9Os=}I()dcJz)TNt&H4YSxVwI)VV*S`-kAyQn^0y=xpGE&y&ohBJW@^Z z&G6XjBUUeEjIptlabq{dD2pAB5AN=7D*ocy&ZF7RGfO3FBVGC6&c%ebL}4TJHm{Mpi%t>CVDd$hZRJ>odZ2mI&gE-eAqF_PByvqxgQQD~3lMda0h+zoq~ ztiitcM$q#-c6Rif+MS38y2yZkNKUi^E$k7;3l&yevQ0Z=Itp(FWc{rdvoc5s+ zOSReF4dLarTDtwcmM=pA*biW&rFGDMPXu^v#cJvHp`B13MhFjt6B5f0$08k&9^NQ~ zn;F^%YmdZFpquH>UrPgn>Ft2Qc>ke;Z%+E_6X3^t9yj|+6GU$#@DWttV5_xwaI8PZ z8|?&MBH--LzkI;J1kC>-mfYoqpP*bZi2pH&eKoKk1N9frp|u41?ie)I+YsrGAM7x9 ze?=^aq#-a7$qmt746_!@4CUm3@b-8)wbLmv=$9lcl^>>3%=@6 zfrD)?7hy2q9-K!caxI>PH_8n^j?k|9gj(>|)F3kXaZq5ef)@}RcZ9vf@^q}3o`$rX zyb;14KVqu99S45I8j!O%VF^K$laeNgs872us(}IqO9b0|c0y$AZyY$Q{zr^s zIiQFTs9EPHyatj>uOX1`T9-f@u4SgN+(;h;Q~T+0cn z->a=K*zSLB0N)0Bh;a~sjwByAir)d&?}$R{X%Ls42HY6b4I08q&=(Me$o^_*T>>J4 zJARb@Ob(hcA<&FTKyXQ?H-^h^gmxg%gqg}T(7pkn3Lt9aAmGR^>w^D{8jx2+G) zfWjX)muJ?cScw}@O^I+ACPxo|@&uD#qXz5jx*UJiAnx9+hi_U3g2uTL^mim7mNK4O zm*S5ae@h4c^5Ctf7R?BRF9sjb6U2Fh7Kov5Aa`%P79n8+(3Ri=t#GM7h`}#cIO2ZZ zW0_Rg0t8>{1LnnbIsW>9xSNBY>EyEj6-vPeT0{+htxExXuvA2e#%f@@s3!g*@eD?S z+ctasb?3j901yyw29CP^N^${F^cqAFG@191u1m0Nq`$2AgD67)vkiY1kdSWxd?WH_ z`c2Xaf?|XLIzR(Sfnr?>W3&g-3gzt#^w1!Z%&#WTHf{$VegrHjZ= zOtGlr2yZ0eKDR)U5(hAU5D;7pf=Um<7BA}WQ5VYI3~7%=67I}1NOly|g9hd<5Lplc z906)KUSMswsn0Ptls!RV<4GeO;y|2#0*$#c1T%|s4JHPGMS%N9k=TF60Kh4^1qRcAkW`5hC0V&#b;CN5 zd<@W~0d%)OzaDD5HVLA1VC&O{i3ZNNa_Iq`QW;>dJrKPNWr-4L!vT*L+8u%V=O+RK zyz{ol3g|&J_^z?v!d{|0%i3tTIiazjhPZDaCU)oSJ@yM^N&ukLhNu+)CrYW7dh(9}HUx-BAqbkFi9m}`_d%izznx-=Kw*EY zMR<;ECRWdm9jKuNOa@KXDm$VitBTNi%E;&BnvH=7O9tT#<r=qXX5( z9_Rw5Q*j;!G*KozHpAb?%cLI5kpr2?3#=xJ!vTYx_FlvCFB4b#6@;X}eP!(BBpAIq zu&f?{F!`>*ML1!RNOv$n`X?EIyEXp)2*22ik`{rRfY{2w4ZIMs5#ej#m&FEICj)@- z&Y4{FOVrn2_2Lh(roD)e(63R{ngnALf8UC znK}e9v7P`T+zsvHpy%Zdr2Gxc4|hh|yILaLe2~NjJw4X9DVOF&mfqVT4q@wUBEYsH zKmXDgH%paZJ$W&+sDp|GOIpYxTgTQ)VDVXmb_LA(7)I9{>qHssj|}htLVU@BokO1f||k-G@z?O zZaGX=I~6~07iVEx8t|P1h-YXfKUr7d)>NGgItVDj%R@@S4C(2E^sq;oBb@NVRau_9L)!VAgT9CkVoHvei??f)UN>3 zF%YH`e0XqhK#L)>7Rlt83)0>judi(#Y2{Qw3`ORb8KG1;|n#y)Ot1fgV&FU{-$ zFi*h;x?-?h9s-3$csPKq9m_ESdXQiZ{=n}aSu05?95B|!ulT&Y!G(RGo(6)V4IO9P zR@fLkeq1s!`UN>4ayj@w8}kFo8^hH>VF-_ z@WDa9-&<1uM0;E z8psRXO~h*hLG=NjSR1r*HI77v&y!eMFq7lA_XVBlbZ z9?*9Ze4t=i_ISXScN6J5;8#;9Io4JhwCaptb%7URJVhiPus?xBSktICyFVELvn^=~ zbw;@x9z2}G@tQu4j)Xc4+tN;cxCBtY07;=z9`VG73g&y^XdloV6X<|EIQ+=I6KD$- z8&>Vc(>sm_5A?8*2>0~_WD|28x(brB6$lBP9mo1Y0ROy|4)b^2=TDSdc=&>?ZX1~S zk9=uUUI2(VLCXcLX#oNFh?W~hFdzU&#Rb|smIAH?Z+l*Hu zo_ihBwsdy+{Tj(a0U|wfE3*o+k6zWvhGQyzC@wp+V;=T(KngAyr5hhp(&JA(kzVHg#7MfQO=2)3e z$0=&~i!Sb~hoCyN))2;Dj{J>g7w+Bk&@OK73&I@0yGy>nd8N_n6u`YP9NLbLw-Uz1 zchCK5WZb*lpz(OSoiOssU10c4jeFS`R9uf?!k{39h&mDXiYn+t74Ilv_htuZSPM)MMqa6oL{oF=7!&Sc zFfjhbd3e5%61p)0SJ0(%W4NcJz?>k?a{x?9EH{~}PLbh4EXjd;76Uq#4~A{}1M#=( cigUQi)NnKC7=c3;;NJr Date: Wed, 1 Jul 2020 18:50:02 +0200 Subject: [PATCH 08/64] Better option --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 779f1148f..dc6598043 100644 --- a/build.gradle +++ b/build.gradle @@ -434,12 +434,12 @@ startScripts { dependencies { compile project(':ethsigner:app') errorprone 'com.google.errorprone:error_prone_core' + runtime fileTree(dir: 'libs', include: '*.jar') } distributions { main { contents { - from("./libs") { into "lib" } from("./LICENSE") { into "." } from("build/reports/license/license-dependency.html") { into "." } from("./docs/GettingStartedBinaries.md") { into "." } From c0fe8c95d61c584ffb92edba49c51a387bec83b1 Mon Sep 17 00:00:00 2001 From: Fernando Llaca Date: Tue, 30 Jun 2020 19:51:47 +0200 Subject: [PATCH 09/64] Added multi-dockerfile builds and cloudhsm Dockerfile --- .circleci/config.yml | 16 ++++++++++++++- build.gradle | 24 +++++++++++++++-------- docker/cloudhsm-entrypoint.sh | 33 +++++++++++++++++++++++++++++++ docker/cloudhsm.Dockerfile | 37 +++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 9 deletions(-) create mode 100644 docker/cloudhsm-entrypoint.sh create mode 100644 docker/cloudhsm.Dockerfile diff --git a/.circleci/config.yml b/.circleci/config.yml index c09095467..c14c837a9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -169,7 +169,16 @@ jobs: command: | mkdir -p docker/reports ./gradlew --no-daemon testDocker - - notify + - run: + name: build image - cloudhsm + command: | + ./gradlew --no-daemon distDocker -Pdockerfile=cloudhsm + - run: + name: test image - cloudhsm + command: | + mkdir -p docker/reports + ./gradlew --no-daemon testDocker -Pdockerfile=cloudhsm + - notify publish: executor: executor_med @@ -195,6 +204,11 @@ jobs: command: | docker login --username "${DOCKER_USER}" --password "${DOCKER_PASSWORD}" ./gradlew --no-daemon --parallel "-Pbranch=${CIRCLE_BRANCH}" dockerUpload + - run: + name: Publish Docker - cloudhsm + command: | + docker login --username "${DOCKER_USER}" --password "${DOCKER_PASSWORD}" + ./gradlew --no-daemon --parallel "-Pbranch=${CIRCLE_BRANCH}" dockerUpload -Pdockerfile=cloudhsm - notify workflows: diff --git a/build.gradle b/build.gradle index dc6598043..d28efff16 100644 --- a/build.gradle +++ b/build.gradle @@ -486,29 +486,36 @@ tasks.register("dockerDistUntar") { } task distDocker(type: Exec) { + def tagSuffix = project.hasProperty("dockerfile")? "-" + dockerfile : "" + def dockerfile = project.hasProperty("dockerfile")? dockerfile + ".Dockerfile" : "Dockerfile" dependsOn dockerDistUntar def dockerBuildVersion = project.hasProperty('release.releaseVersion') ? project.property('release.releaseVersion') : "${rootProject.version}" def imageName = dockerRepo + "/" + repositoryName - def image = project.hasProperty('release.releaseVersion') ? "${imageName}:" + project.property('release.releaseVersion') : "${imageName}:${project.version}" + def image = project.hasProperty('release.releaseVersion') ? "${imageName}:" + project.property('release.releaseVersion') + "${tagSuffix}": "${imageName}:${project.version}${tagSuffix}" def dockerBuildDir = "build/docker-" + repositoryName + "/" workingDir "${dockerBuildDir}" doFirst { copy { - from file("${projectDir}/docker/Dockerfile") + from file("${projectDir}/docker/" + dockerfile) + into(workingDir) + } + copy { + from file("${projectDir}/docker/cloudhsm-entrypoint.sh") into(workingDir) } } executable "sh" - args "-c", "docker build --build-arg BUILD_DATE=${buildTime()} --build-arg VERSION=${dockerBuildVersion} --build-arg VCS_REF=${getCheckedOutGitCommitHash()} -t ${image} ." + args "-c", "docker build -f ${dockerfile} --build-arg BUILD_DATE=${buildTime()} --build-arg VERSION=${dockerBuildVersion} --build-arg VCS_REF=${getCheckedOutGitCommitHash()} -t ${image} ." } task testDocker(type: Exec) { dependsOn distDocker + def tagSuffix = project.hasProperty("dockerfile")? "-" + dockerfile : "" def dockerReportsDir = "docker/reports/" def imageName = dockerRepo + "/" + repositoryName - def image = project.hasProperty('release.releaseVersion') ? "${imageName}:" + project.property('release.releaseVersion') : "${imageName}:${project.version}" + def image = project.hasProperty('release.releaseVersion') ? "${imageName}:" + project.property('release.releaseVersion') + "${tagSuffix}": "${imageName}:${project.version}${tagSuffix}" workingDir "docker" doFirst { @@ -521,18 +528,19 @@ task testDocker(type: Exec) { task dockerUpload(type: Exec) { dependsOn distDocker + def tagSuffix = project.hasProperty("dockerfile")? "-" + dockerfile : "" def imageName = dockerRepo + "/" + repositoryName - def image = project.hasProperty('release.releaseVersion') ? "${imageName}:" + project.property('release.releaseVersion') : "${imageName}:${project.version}" + def image = project.hasProperty('release.releaseVersion') ? "${imageName}:" + project.property('release.releaseVersion') + "${tagSuffix}": "${imageName}:${project.version}${tagSuffix}" def cmd = "docker push '${image}'" def additionalTags = [] if (project.hasProperty('branch') && project.property('branch') == 'master') { - additionalTags.add('develop') + additionalTags.add("develop${tagSuffix}") } if (!(version ==~ /.*-SNAPSHOT/)) { - additionalTags.add('latest') - additionalTags.add(version.split(/\./)[0..1].join('.')) + additionalTags.add("latest${tagSuffix}") + additionalTags.add(version.split(/\./)[0..1].join('.') + "${tagSuffix}") } additionalTags.each { tag -> cmd += " && docker tag '${image}' '${imageName}:${tag.trim()}' && docker push '${imageName}:${tag.trim()}'" } diff --git a/docker/cloudhsm-entrypoint.sh b/docker/cloudhsm-entrypoint.sh new file mode 100644 index 000000000..236dd90eb --- /dev/null +++ b/docker/cloudhsm-entrypoint.sh @@ -0,0 +1,33 @@ +#! /bin/bash + +# Set HSM IP as an environmental variable +# ENV HSM_IP + +if [[ -z "$HSM_IP" ]]; then + #statements + echo -e "\n* HSM_IP env variable must be set \n" + exit 1 +fi + +# Configure cloudhms-client +# COPY customerCA.crt /opt/cloudhsm/etc/ +/opt/cloudhsm/bin/configure -a $HSM_IP + +# start cloudhsm client +echo -n "* Starting CloudHSM client ... " +/opt/cloudhsm/bin/cloudhsm_client /opt/cloudhsm/etc/cloudhsm_client.cfg &> /tmp/cloudhsm_client_start.log & + +# wait for startup +while true +do + if grep 'libevmulti_init: Ready !' /tmp/cloudhsm_client_start.log &> /dev/null + then + echo "[OK]" + break + fi + sleep 0.5 +done +echo -e "\n* CloudHSM client started successfully ... \n" + +# start application +/opt/ethsigner/bin/ethsigner $@ diff --git a/docker/cloudhsm.Dockerfile b/docker/cloudhsm.Dockerfile new file mode 100644 index 000000000..20dbe1e70 --- /dev/null +++ b/docker/cloudhsm.Dockerfile @@ -0,0 +1,37 @@ +# Use the amazon linux image +FROM amazonlinux:2 + +# Install CloudHSM client +RUN yum install -y https://s3.amazonaws.com/cloudhsmv2-software/CloudHsmClient/EL7/cloudhsm-client-latest.el7.x86_64.rpm + +# Install CloudHSM Java library +RUN yum install -y https://s3.amazonaws.com/cloudhsmv2-software/CloudHsmClient/EL7/cloudhsm-client-jce-latest.el7.x86_64.rpm + +# Install Java, Maven, wget, unzip and ncurses-compat-libs +RUN yum install -y java wget unzip ncurses-compat-libs + +COPY cloudhsm-entrypoint.sh /opt/ethsigner/bin/cloudhsm-entrypoint.sh +RUN chmod +x /opt/ethsigner/bin/cloudhsm-entrypoint.sh + +COPY ethsigner /opt/ethsigner/ +WORKDIR /opt/ethsigner + +# Expose services ports +# 8545 HTTP JSON-RPC +EXPOSE 8545 + +ENTRYPOINT ["/opt/ethsigner/bin/cloudhsm-entrypoint.sh"] + +# Build-time metadata as defined at http://label-schema.org +ARG BUILD_DATE +ARG VCS_REF +ARG VERSION +LABEL org.label-schema.build-date=$BUILD_DATE \ + org.label-schema.name="Ethsigner" \ + org.label-schema.description="Ethereum transaction signing application" \ + org.label-schema.url="https://docs.ethsigner.pegasys.tech/" \ + org.label-schema.vcs-ref=$VCS_REF \ + org.label-schema.vcs-url="https://github.com/PegaSysEng/ethsigner.git" \ + org.label-schema.vendor="Pegasys" \ + org.label-schema.version=$VERSION \ + org.label-schema.schema-version="1.0" From dc392003b3cd15a92b1130660fc274a03d23b551 Mon Sep 17 00:00:00 2001 From: Fernando Llaca Date: Wed, 1 Jul 2020 18:09:20 +0200 Subject: [PATCH 10/64] "which" is required by ethsigner start script --- docker/cloudhsm.Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/cloudhsm.Dockerfile b/docker/cloudhsm.Dockerfile index 20dbe1e70..7b26983d2 100644 --- a/docker/cloudhsm.Dockerfile +++ b/docker/cloudhsm.Dockerfile @@ -9,6 +9,7 @@ RUN yum install -y https://s3.amazonaws.com/cloudhsmv2-software/CloudHsmClient/E # Install Java, Maven, wget, unzip and ncurses-compat-libs RUN yum install -y java wget unzip ncurses-compat-libs +RUN yum install -y which COPY cloudhsm-entrypoint.sh /opt/ethsigner/bin/cloudhsm-entrypoint.sh RUN chmod +x /opt/ethsigner/bin/cloudhsm-entrypoint.sh From 335dc463e0e559c3ceca901114e7e8ea577ad83a Mon Sep 17 00:00:00 2001 From: Fernando Llaca Date: Thu, 2 Jul 2020 11:48:09 +0200 Subject: [PATCH 11/64] Disable slack notifications --- .circleci/config.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c14c837a9..2aa620591 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -113,7 +113,7 @@ jobs: no_output_timeout: 20m command: | ./gradlew --no-daemon --parallel integrationTest --info - - notify + #- notify - capture_test_results - capture_test_reports - save_cache: @@ -178,7 +178,7 @@ jobs: command: | mkdir -p docker/reports ./gradlew --no-daemon testDocker -Pdockerfile=cloudhsm - - notify + #- notify publish: executor: executor_med @@ -190,7 +190,7 @@ jobs: name: Publish command: | ./gradlew --no-daemon --parallel bintrayUpload - - notify + #- notify publishDocker: executor: executor_med @@ -209,7 +209,7 @@ jobs: command: | docker login --username "${DOCKER_USER}" --password "${DOCKER_PASSWORD}" ./gradlew --no-daemon --parallel "-Pbranch=${CIRCLE_BRANCH}" dockerUpload -Pdockerfile=cloudhsm - - notify + #- notify workflows: version: 2 From cea250d7be282d0b297469badb4159217372fe23 Mon Sep 17 00:00:00 2001 From: Fernando Llaca Date: Thu, 2 Jul 2020 12:05:11 +0200 Subject: [PATCH 12/64] Remove cloudhsm dockerImage tests CloudHSM cluster is required --- .circleci/config.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2aa620591..589994970 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -173,11 +173,6 @@ jobs: name: build image - cloudhsm command: | ./gradlew --no-daemon distDocker -Pdockerfile=cloudhsm - - run: - name: test image - cloudhsm - command: | - mkdir -p docker/reports - ./gradlew --no-daemon testDocker -Pdockerfile=cloudhsm #- notify publish: From c8e5873f53916e2bbcb69067ae6e1a3bc97acbc3 Mon Sep 17 00:00:00 2001 From: Fernando Llaca Date: Thu, 2 Jul 2020 13:38:48 +0200 Subject: [PATCH 13/64] Remove bintray publish CI step as not needed --- .circleci/config.yml | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 589994970..2d200643e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -230,15 +230,16 @@ workflows: - buildDocker: requires: - build - - publish: - filters: - branches: - only: - - master - - /^release-.*/ - requires: - - build - #- acceptanceTests + # TODO: <02-07-20, fllaca> # Adhara doesn't need to publish mvn packages for now + #- publish: + #filters: + #branches: + #only: + #- master + #- /^release-.*/ + #requires: + #- build + ##- acceptanceTests - publishDocker: filters: branches: From c66340c1f630cc0c61d34f4b12fc92d6fb47eff8 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Tue, 21 Jul 2020 10:29:36 +0200 Subject: [PATCH 14/64] Amend cloudhsm entrypoint --- docker/cloudhsm-entrypoint.sh | 41 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/docker/cloudhsm-entrypoint.sh b/docker/cloudhsm-entrypoint.sh index 236dd90eb..fc026a086 100644 --- a/docker/cloudhsm-entrypoint.sh +++ b/docker/cloudhsm-entrypoint.sh @@ -5,29 +5,28 @@ if [[ -z "$HSM_IP" ]]; then #statements - echo -e "\n* HSM_IP env variable must be set \n" - exit 1 -fi - -# Configure cloudhms-client -# COPY customerCA.crt /opt/cloudhsm/etc/ -/opt/cloudhsm/bin/configure -a $HSM_IP + echo -e "\n* HSM_IP env variable not set: skipping CloudHSM connection \n" +else + # Configure cloudhms-client + # COPY customerCA.crt /opt/cloudhsm/etc/ + /opt/cloudhsm/bin/configure -a $HSM_IP -# start cloudhsm client -echo -n "* Starting CloudHSM client ... " -/opt/cloudhsm/bin/cloudhsm_client /opt/cloudhsm/etc/cloudhsm_client.cfg &> /tmp/cloudhsm_client_start.log & + # start cloudhsm client + echo -n "* Starting CloudHSM client ... " + /opt/cloudhsm/bin/cloudhsm_client /opt/cloudhsm/etc/cloudhsm_client.cfg &> /tmp/cloudhsm_client_start.log & -# wait for startup -while true -do - if grep 'libevmulti_init: Ready !' /tmp/cloudhsm_client_start.log &> /dev/null - then - echo "[OK]" - break - fi - sleep 0.5 -done -echo -e "\n* CloudHSM client started successfully ... \n" + # wait for startup + while true + do + if grep 'libevmulti_init: Ready !' /tmp/cloudhsm_client_start.log &> /dev/null + then + echo "[OK]" + break + fi + sleep 0.5 + done + echo -e "\n* CloudHSM client started successfully ... \n" +fi # start application /opt/ethsigner/bin/ethsigner $@ From bede6042f6e59e59b6329bcb024c8ae1ecadb8bf Mon Sep 17 00:00:00 2001 From: mkrielza Date: Thu, 6 Aug 2020 11:08:24 +0200 Subject: [PATCH 15/64] Working --- build.gradle | 2 +- .../internalresponse/EthSignResultProvider.java | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 7c8c420f8..d72233712 100644 --- a/build.gradle +++ b/build.gradle @@ -439,8 +439,8 @@ startScripts { dependencies { compile project(':ethsigner:app') + runtime fileTree(dir: "${rootDir}/libs", include: "*.jar") errorprone 'com.google.errorprone:error_prone_core' - runtime fileTree(dir: 'libs', include: '*.jar') } distributions { diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignResultProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignResultProvider.java index 714902a81..173392c22 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignResultProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignResultProvider.java @@ -61,10 +61,11 @@ public String createResponseResult(final JsonRpcRequest request) { } final TransactionSigner signer = transactionSigner.get(); final String originalMessage = params.get(1); - final String message = - (char) 25 + "Ethereum Signed Message:\n" + originalMessage.length() + originalMessage; - final Signature signature = signer.sign(message.getBytes(StandardCharsets.UTF_8)); - + // final byte[] message = originalMessage.getBytes(StandardCharsets.UTF_8); + // final String message = (char) 25 + "Ethereum Signed Message:\n" + originalMessage.length() + originalMessage; + // final Signature signature = signer.sign(message); + final byte[] message = Numeric.hexStringToByteArray(originalMessage); + final Signature signature = signer.sign(message); final Bytes outputSignature = Bytes.concatenate( Bytes32.leftPad(Bytes.wrap(ByteUtils.bigIntegerToBytes(signature.getR()))), From bbdbca9cf7fb0d592d87100b01421ced2f22282a Mon Sep 17 00:00:00 2001 From: mkrielza Date: Fri, 7 Aug 2020 16:25:42 +0200 Subject: [PATCH 16/64] Backwards compatibility --- .../tech/pegasys/ethsigner/core/Runner.java | 6 +- .../EthSignResultProvider.java | 9 +- .../EthSignTransactionProvider.java | 85 +++++++++ .../EthSignTransactionProviderTest.java | 175 ++++++++++++++++++ 4 files changed, 269 insertions(+), 6 deletions(-) create mode 100644 ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionProvider.java create mode 100644 ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionProviderTest.java diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java index 3a5975752..8899ef095 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java @@ -23,6 +23,7 @@ import tech.pegasys.ethsigner.core.requesthandler.VertxRequestTransmitterFactory; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthAccountsResultProvider; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignResultProvider; +import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignTransactionProvider; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.InternalResponseHandler; import tech.pegasys.ethsigner.core.requesthandler.passthrough.PassThroughHandler; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.DownstreamPathCalculator; @@ -171,7 +172,10 @@ private RequestMapper createRequestMapper( "eth_sign", new InternalResponseHandler<>( responseFactory, new EthSignResultProvider(transactionSignerProvider))); - + requestMapper.addHandler( + "eth_signTransaction", + new InternalResponseHandler<>( + responseFactory, new EthSignTransactionProvider(transactionSignerProvider))); return requestMapper; } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignResultProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignResultProvider.java index 173392c22..714902a81 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignResultProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignResultProvider.java @@ -61,11 +61,10 @@ public String createResponseResult(final JsonRpcRequest request) { } final TransactionSigner signer = transactionSigner.get(); final String originalMessage = params.get(1); - // final byte[] message = originalMessage.getBytes(StandardCharsets.UTF_8); - // final String message = (char) 25 + "Ethereum Signed Message:\n" + originalMessage.length() + originalMessage; - // final Signature signature = signer.sign(message); - final byte[] message = Numeric.hexStringToByteArray(originalMessage); - final Signature signature = signer.sign(message); + final String message = + (char) 25 + "Ethereum Signed Message:\n" + originalMessage.length() + originalMessage; + final Signature signature = signer.sign(message.getBytes(StandardCharsets.UTF_8)); + final Bytes outputSignature = Bytes.concatenate( Bytes32.leftPad(Bytes.wrap(ByteUtils.bigIntegerToBytes(signature.getR()))), diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionProvider.java new file mode 100644 index 000000000..9748732a8 --- /dev/null +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionProvider.java @@ -0,0 +1,85 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.core.requesthandler.internalresponse; + +import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.INVALID_PARAMS; +import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT; + +import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequest; +import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; +import tech.pegasys.ethsigner.core.requesthandler.ResultProvider; +import tech.pegasys.ethsigner.core.util.ByteUtils; +import tech.pegasys.signers.secp256k1.api.Signature; +import tech.pegasys.signers.secp256k1.api.TransactionSigner; +import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; + +import java.util.List; +import java.util.Optional; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.web3j.utils.Numeric; + +public class EthSignTransactionProvider implements ResultProvider { + + private static final Logger LOG = LogManager.getLogger(); + + private final TransactionSignerProvider transactionSignerProvider; + + public EthSignTransactionProvider(final TransactionSignerProvider transactionSignerProvider) { + this.transactionSignerProvider = transactionSignerProvider; + } + + @Override + public String createResponseResult(final JsonRpcRequest request) { + final List params = getParams(request); + if (params == null || params.size() != 2) { + LOG.info( + "eth_sign should have a list of 2 parameters, but has {}", + params == null ? "null" : params.size()); + throw new JsonRpcException(INVALID_PARAMS); + } + final String address = params.get(0); + final Optional transactionSigner = + transactionSignerProvider.getSigner(address); + if (transactionSigner.isEmpty()) { + LOG.info("Address ({}) does not match any available account", address); + throw new JsonRpcException(SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT); + } + final TransactionSigner signer = transactionSigner.get(); + final String originalMessage = params.get(1); + final byte[] message = Numeric.hexStringToByteArray(originalMessage); + final Signature signature = signer.sign(message); + final Bytes outputSignature = + Bytes.concatenate( + Bytes32.leftPad(Bytes.wrap(ByteUtils.bigIntegerToBytes(signature.getR()))), + Bytes32.leftPad(Bytes.wrap(ByteUtils.bigIntegerToBytes(signature.getS()))), + Bytes.wrap(ByteUtils.bigIntegerToBytes(signature.getV()))); + return Numeric.toHexString(outputSignature.toArray()); + } + + private List getParams(final JsonRpcRequest request) { + try { + @SuppressWarnings("unchecked") + final List params = (List) request.getParams(); + return params; + } catch (final ClassCastException e) { + LOG.info( + "eth_sign should have a list of 2 parameters, but received an object: {}", + request.getParams()); + throw new JsonRpcException(INVALID_PARAMS); + } + } +} diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionProviderTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionProviderTest.java new file mode 100644 index 000000000..ae0e54e0d --- /dev/null +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionProviderTest.java @@ -0,0 +1,175 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.core.jsonrpcproxy; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.INVALID_PARAMS; +import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT; + +import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequest; +import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequestId; +import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; +import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignTransactionProvider; +import tech.pegasys.signers.secp256k1.api.Signature; +import tech.pegasys.signers.secp256k1.api.TransactionSigner; +import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; + +import java.math.BigInteger; +import java.nio.charset.Charset; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.junit.jupiter.params.provider.NullSource; +import org.web3j.crypto.ECDSASignature; +import org.web3j.crypto.ECKeyPair; +import org.web3j.crypto.Sign; +import org.web3j.utils.Numeric; + +public class EthSignTransactionProviderTest { + + @ParameterizedTest + @ArgumentsSource(InvalidParamsProvider.class) + @NullSource + public void ifParamIsInvalidExceptionIsThrownWithInvalidParams(final Object params) { + final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); + final EthSignTransactionProvider resultProvider = + new EthSignTransactionProvider(mockSignerProvider); + + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + request.setId(new JsonRpcRequestId(1)); + request.setParams(params); + + final Throwable thrown = catchThrowable(() -> resultProvider.createResponseResult(request)); + assertThat(thrown).isInstanceOf(JsonRpcException.class); + final JsonRpcException rpcException = (JsonRpcException) thrown; + assertThat(rpcException.getJsonRpcError()).isEqualTo(INVALID_PARAMS); + } + + @Test + public void ifAddressIsNotUnlockedExceptionIsThrownWithSigningNotUnlocked() { + final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); + final EthSignTransactionProvider resultProvider = + new EthSignTransactionProvider(mockSignerProvider); + + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + request.setId(new JsonRpcRequestId(1)); + request.setParams( + List.of("address", Numeric.toHexString("message".getBytes(Charset.defaultCharset())))); + final Throwable thrown = catchThrowable(() -> resultProvider.createResponseResult(request)); + assertThat(thrown).isInstanceOf(JsonRpcException.class); + final JsonRpcException rpcException = (JsonRpcException) thrown; + assertThat(rpcException.getJsonRpcError()).isEqualTo(SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT); + } + + @Test + public void signatureHasTheExpectedFormat() { + final TransactionSigner mockTransactionSigner = mock(TransactionSigner.class); + final BigInteger v = BigInteger.ONE; + final BigInteger r = BigInteger.TWO; + final BigInteger s = BigInteger.TEN; + doReturn(new Signature(v, r, s)).when(mockTransactionSigner).sign(any(byte[].class)); + final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); + doReturn(Optional.of(mockTransactionSigner)).when(mockSignerProvider).getSigner(anyString()); + final EthSignTransactionProvider resultProvider = + new EthSignTransactionProvider(mockSignerProvider); + + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + final int id = 1; + request.setId(new JsonRpcRequestId(id)); + request.setParams( + List.of("address", Numeric.toHexString("message".getBytes(Charset.defaultCharset())))); + + final Object result = resultProvider.createResponseResult(request); + assertThat(result).isInstanceOf(String.class); + final String hexSignature = (String) result; + assertThat(hexSignature).hasSize(132); + + final byte[] signature = Numeric.hexStringToByteArray(hexSignature); + + assertThat(new BigInteger(1, signature, 0, 32)).isEqualTo(r); + assertThat(new BigInteger(1, signature, 32, 32)).isEqualTo(s); + assertThat(new BigInteger(1, signature, 64, 1)).isEqualTo(v); + } + + @Test + public void returnsExpectedSignature() { + final ECKeyPair keyPair = + ECKeyPair.create( + Numeric.hexStringToByteArray( + "0x1618fc3e47aec7e70451256e033b9edb67f4c469258d8e2fbb105552f141ae41")); + final TransactionSigner mockTransactionSigner = mock(TransactionSigner.class); + doAnswer( + answer -> { + byte[] data = answer.getArgument(0, byte[].class); + final Sign.SignatureData signature = Sign.signPrefixedMessage(data, keyPair); + return new Signature( + new BigInteger(signature.getV()), + new BigInteger(1, signature.getR()), + new BigInteger(1, signature.getS())); + }) + .when(mockTransactionSigner) + .sign(any(byte[].class)); + + final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); + doReturn(Optional.of(mockTransactionSigner)).when(mockSignerProvider).getSigner(anyString()); + final EthSignTransactionProvider resultProvider = + new EthSignTransactionProvider(mockSignerProvider); + + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + final int id = 1; + request.setId(new JsonRpcRequestId(id)); + request.setParams( + List.of( + "address", + Numeric.toHexString( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Tubulum fuisse, qua illum, cuius is condemnatus est rogatione, P. Eaedem res maneant alio modo." + .getBytes(Charset.defaultCharset())))); + + final Object result = resultProvider.createResponseResult(request); + assertThat(result).isInstanceOf(String.class); + final String hexSignature = (String) result; + final byte[] signature = Numeric.hexStringToByteArray(hexSignature); + + final ECDSASignature expectedSignature = + keyPair.sign( + Numeric.hexStringToByteArray( + "0xe63325d74baa84af003dfb6a974f41672be881b56aa2c12c093f8259321bd460")); + assertThat(new BigInteger(1, signature, 0, 32)).isEqualTo(expectedSignature.r); + assertThat(new BigInteger(1, signature, 32, 32)).isEqualTo(expectedSignature.s); + } + + private static class InvalidParamsProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(final ExtensionContext context) { + return Stream.of( + Arguments.of(Collections.emptyList()), + Arguments.of(Collections.singleton(2)), + Arguments.of(List.of(1, 2, 3)), + Arguments.of(new Object())); + } + } +} From bca25c3ae2b2afb07da650201cc09493d9179495 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Fri, 7 Aug 2020 20:47:46 +0200 Subject: [PATCH 17/64] Stick to eth json-rpc standards --- .../tech/pegasys/ethsigner/core/Runner.java | 10 +- ...nProvider.java => EthSignDataHandler.java} | 8 +- .../EthSignTransactionHandler.java | 126 ++++++++++++++++++ ...rTest.java => EthSignDataHandlerTest.java} | 17 +-- 4 files changed, 144 insertions(+), 17 deletions(-) rename ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/{EthSignTransactionProvider.java => EthSignDataHandler.java} (91%) create mode 100644 ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionHandler.java rename ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/{EthSignTransactionProviderTest.java => EthSignDataHandlerTest.java} (93%) diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java index 8899ef095..ddc4cfc08 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java @@ -22,8 +22,9 @@ import tech.pegasys.ethsigner.core.requesthandler.VertxRequestTransmitter; import tech.pegasys.ethsigner.core.requesthandler.VertxRequestTransmitterFactory; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthAccountsResultProvider; +import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignDataHandler; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignResultProvider; -import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignTransactionProvider; +import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignTransactionHandler; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.InternalResponseHandler; import tech.pegasys.ethsigner.core.requesthandler.passthrough.PassThroughHandler; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.DownstreamPathCalculator; @@ -175,7 +176,12 @@ private RequestMapper createRequestMapper( requestMapper.addHandler( "eth_signTransaction", new InternalResponseHandler<>( - responseFactory, new EthSignTransactionProvider(transactionSignerProvider))); + responseFactory, + new EthSignTransactionHandler(chainId, transactionSignerProvider, jsonDecoder))); + requestMapper.addHandler( + "eth_signData", + new InternalResponseHandler<>( + responseFactory, new EthSignDataHandler(transactionSignerProvider))); return requestMapper; } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignDataHandler.java similarity index 91% rename from ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionProvider.java rename to ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignDataHandler.java index 9748732a8..6e5e15e5f 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignDataHandler.java @@ -32,13 +32,13 @@ import org.apache.tuweni.bytes.Bytes32; import org.web3j.utils.Numeric; -public class EthSignTransactionProvider implements ResultProvider { +public class EthSignDataHandler implements ResultProvider { private static final Logger LOG = LogManager.getLogger(); private final TransactionSignerProvider transactionSignerProvider; - public EthSignTransactionProvider(final TransactionSignerProvider transactionSignerProvider) { + public EthSignDataHandler(final TransactionSignerProvider transactionSignerProvider) { this.transactionSignerProvider = transactionSignerProvider; } @@ -47,7 +47,7 @@ public String createResponseResult(final JsonRpcRequest request) { final List params = getParams(request); if (params == null || params.size() != 2) { LOG.info( - "eth_sign should have a list of 2 parameters, but has {}", + "eth_signData should have a list of 2 parameters, but has {}", params == null ? "null" : params.size()); throw new JsonRpcException(INVALID_PARAMS); } @@ -77,7 +77,7 @@ private List getParams(final JsonRpcRequest request) { return params; } catch (final ClassCastException e) { LOG.info( - "eth_sign should have a list of 2 parameters, but received an object: {}", + "eth_signData should have a list of parameters, but received an object: {}", request.getParams()); throw new JsonRpcException(INVALID_PARAMS); } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionHandler.java new file mode 100644 index 000000000..231d815e1 --- /dev/null +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionHandler.java @@ -0,0 +1,126 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.core.requesthandler.internalresponse; + +import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.INVALID_PARAMS; +import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT; +import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.TX_SENDER_NOT_AUTHORIZED; + +import tech.pegasys.ethsigner.core.jsonrpc.EthSendTransactionJsonParameters; +import tech.pegasys.ethsigner.core.jsonrpc.JsonDecoder; +import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequest; +import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; +import tech.pegasys.ethsigner.core.requesthandler.ResultProvider; +import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.EthTransaction; +import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.Transaction; +import tech.pegasys.ethsigner.core.util.ByteUtils; +import tech.pegasys.ethsigner.core.util.HexStringComparator; +import tech.pegasys.signers.secp256k1.api.Signature; +import tech.pegasys.signers.secp256k1.api.TransactionSigner; +import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; + +import java.util.List; +import java.util.Optional; + +import io.vertx.core.json.DecodeException; +import io.vertx.core.json.JsonObject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.web3j.utils.Numeric; + +public class EthSignTransactionHandler implements ResultProvider { + + private static final Logger LOG = LogManager.getLogger(); + + private final long chainId; + private final TransactionSignerProvider transactionSignerProvider; + private final JsonDecoder decoder; + + public EthSignTransactionHandler( + final long chainId, + final TransactionSignerProvider transactionSignerProvider, + final JsonDecoder decoder) { + this.chainId = chainId; + this.transactionSignerProvider = transactionSignerProvider; + this.decoder = decoder; + } + + @Override + public String createResponseResult(final JsonRpcRequest request) { + LOG.debug("Transforming request {}, {}", request.getId(), request.getMethod()); + final Transaction transaction; + try { + transaction = createTransaction(request); + + } catch (final NumberFormatException e) { + LOG.debug("Parsing values failed for request: {}", request.getParams(), e); + throw new JsonRpcException(INVALID_PARAMS); + } catch (final IllegalArgumentException | DecodeException e) { + LOG.debug("JSON Deserialization failed for request: {}", request.getParams(), e); + throw new JsonRpcException(INVALID_PARAMS); + } + + final Optional transactionSigner = + transactionSignerProvider.getSigner(transaction.sender()); + + if (transactionSigner.isEmpty()) { + LOG.info("From address ({}) does not match any available account", transaction.sender()); + throw new JsonRpcException(SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT); + } + + final HexStringComparator comparator = new HexStringComparator(); + if (comparator.compare(transactionSigner.get().getAddress(), transaction.sender()) != 0) { + LOG.info( + "Ethereum address derived from identifier ({}) is incorrect value ({})", + transaction.sender(), + transactionSigner.get().getAddress()); + throw new JsonRpcException(TX_SENDER_NOT_AUTHORIZED); + } + final byte[] bytesToSign = transaction.rlpEncode(chainId); + final Signature signature = transactionSigner.get().sign(bytesToSign); + final Bytes outputSignature = + Bytes.concatenate( + Bytes32.leftPad(Bytes.wrap(ByteUtils.bigIntegerToBytes(signature.getR()))), + Bytes32.leftPad(Bytes.wrap(ByteUtils.bigIntegerToBytes(signature.getS()))), + Bytes.wrap(ByteUtils.bigIntegerToBytes(signature.getV()))); + return Numeric.toHexString(outputSignature.toArray()); + } + + private Transaction createTransaction(final JsonRpcRequest request) { + final EthSendTransactionJsonParameters params = + fromRpcRequestToJsonParam(EthSendTransactionJsonParameters.class, request); + return new EthTransaction(params, null, request.getId()); + } + + public T fromRpcRequestToJsonParam(final Class type, final JsonRpcRequest request) { + final Object object; + final Object params = request.getParams(); + if (params instanceof List) { + @SuppressWarnings("unchecked") + final List paramList = (List) params; + if (paramList.size() != 1) { + throw new IllegalArgumentException( + type.getSimpleName() + + " json Rpc requires a single parameter, request contained " + + paramList.size()); + } + object = paramList.get(0); + } else { + object = params; + } + final JsonObject receivedParams = JsonObject.mapFrom(object); + return decoder.decodeValue(receivedParams.toBuffer(), type); + } +} diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionProviderTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignDataHandlerTest.java similarity index 93% rename from ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionProviderTest.java rename to ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignDataHandlerTest.java index ae0e54e0d..33f85f527 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionProviderTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignDataHandlerTest.java @@ -25,7 +25,7 @@ import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequest; import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequestId; import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; -import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignTransactionProvider; +import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignDataHandler; import tech.pegasys.signers.secp256k1.api.Signature; import tech.pegasys.signers.secp256k1.api.TransactionSigner; import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; @@ -49,15 +49,14 @@ import org.web3j.crypto.Sign; import org.web3j.utils.Numeric; -public class EthSignTransactionProviderTest { +public class EthSignDataHandlerTest { @ParameterizedTest @ArgumentsSource(InvalidParamsProvider.class) @NullSource public void ifParamIsInvalidExceptionIsThrownWithInvalidParams(final Object params) { final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); - final EthSignTransactionProvider resultProvider = - new EthSignTransactionProvider(mockSignerProvider); + final EthSignDataHandler resultProvider = new EthSignDataHandler(mockSignerProvider); final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); request.setId(new JsonRpcRequestId(1)); @@ -72,8 +71,7 @@ public void ifParamIsInvalidExceptionIsThrownWithInvalidParams(final Object para @Test public void ifAddressIsNotUnlockedExceptionIsThrownWithSigningNotUnlocked() { final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); - final EthSignTransactionProvider resultProvider = - new EthSignTransactionProvider(mockSignerProvider); + final EthSignDataHandler resultProvider = new EthSignDataHandler(mockSignerProvider); final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); request.setId(new JsonRpcRequestId(1)); @@ -94,8 +92,7 @@ public void signatureHasTheExpectedFormat() { doReturn(new Signature(v, r, s)).when(mockTransactionSigner).sign(any(byte[].class)); final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); doReturn(Optional.of(mockTransactionSigner)).when(mockSignerProvider).getSigner(anyString()); - final EthSignTransactionProvider resultProvider = - new EthSignTransactionProvider(mockSignerProvider); + final EthSignDataHandler resultProvider = new EthSignDataHandler(mockSignerProvider); final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); final int id = 1; @@ -133,11 +130,9 @@ public void returnsExpectedSignature() { }) .when(mockTransactionSigner) .sign(any(byte[].class)); - final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); doReturn(Optional.of(mockTransactionSigner)).when(mockSignerProvider).getSigner(anyString()); - final EthSignTransactionProvider resultProvider = - new EthSignTransactionProvider(mockSignerProvider); + final EthSignDataHandler resultProvider = new EthSignDataHandler(mockSignerProvider); final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); final int id = 1; From 356e34fd0dd0bd2c2d467a31a310d6c8bacd1c59 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Wed, 12 Aug 2020 10:58:25 +0200 Subject: [PATCH 18/64] PR Updates --- .../EthSignTransactionHandler.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionHandler.java index 231d815e1..828128f48 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionHandler.java @@ -23,7 +23,6 @@ import tech.pegasys.ethsigner.core.requesthandler.ResultProvider; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.EthTransaction; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.Transaction; -import tech.pegasys.ethsigner.core.util.ByteUtils; import tech.pegasys.ethsigner.core.util.HexStringComparator; import tech.pegasys.signers.secp256k1.api.Signature; import tech.pegasys.signers.secp256k1.api.TransactionSigner; @@ -36,8 +35,8 @@ import io.vertx.core.json.JsonObject; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.bytes.Bytes32; +import org.web3j.crypto.Sign; +import org.web3j.crypto.TransactionEncoder; import org.web3j.utils.Numeric; public class EthSignTransactionHandler implements ResultProvider { @@ -90,12 +89,18 @@ public String createResponseResult(final JsonRpcRequest request) { } final byte[] bytesToSign = transaction.rlpEncode(chainId); final Signature signature = transactionSigner.get().sign(bytesToSign); - final Bytes outputSignature = - Bytes.concatenate( - Bytes32.leftPad(Bytes.wrap(ByteUtils.bigIntegerToBytes(signature.getR()))), - Bytes32.leftPad(Bytes.wrap(ByteUtils.bigIntegerToBytes(signature.getS()))), - Bytes.wrap(ByteUtils.bigIntegerToBytes(signature.getV()))); - return Numeric.toHexString(outputSignature.toArray()); + + final Sign.SignatureData web3jSignature = + new Sign.SignatureData( + signature.getV().toByteArray(), + signature.getR().toByteArray(), + signature.getS().toByteArray()); + + final Sign.SignatureData eip155Signature = + TransactionEncoder.createEip155SignatureData(web3jSignature, chainId); + + final byte[] serializedBytes = transaction.rlpEncode(eip155Signature); + return Numeric.toHexString(serializedBytes); } private Transaction createTransaction(final JsonRpcRequest request) { @@ -113,7 +118,7 @@ public T fromRpcRequestToJsonParam(final Class type, final JsonRpcRequest if (paramList.size() != 1) { throw new IllegalArgumentException( type.getSimpleName() - + " json Rpc requires a single parameter, request contained " + + " json Rpc requires one parameter, request contained " + paramList.size()); } object = paramList.get(0); From 4462949cb7a5914e0c83ff23a67459b298d2fd6b Mon Sep 17 00:00:00 2001 From: mkrielza Date: Wed, 12 Aug 2020 19:33:28 +0200 Subject: [PATCH 19/64] Add test --- .../tech/pegasys/ethsigner/core/Runner.java | 8 +- ...er.java => EthSignDataResultProvider.java} | 4 +- ... => EthSignTransactionResultProvider.java} | 10 +- ...ava => EthSignDataResultProviderTest.java} | 24 ++- .../EthSignTransactionResultProviderTest.java | 189 ++++++++++++++++++ 5 files changed, 217 insertions(+), 18 deletions(-) rename ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/{EthSignDataHandler.java => EthSignDataResultProvider.java} (95%) rename ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/{EthSignTransactionHandler.java => EthSignTransactionResultProvider.java} (94%) rename ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/{EthSignDataHandlerTest.java => EthSignDataResultProviderTest.java} (92%) create mode 100644 ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionResultProviderTest.java diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java index ddc4cfc08..119d4f31e 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java @@ -22,9 +22,9 @@ import tech.pegasys.ethsigner.core.requesthandler.VertxRequestTransmitter; import tech.pegasys.ethsigner.core.requesthandler.VertxRequestTransmitterFactory; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthAccountsResultProvider; -import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignDataHandler; +import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignDataResultProvider; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignResultProvider; -import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignTransactionHandler; +import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignTransactionResultProvider; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.InternalResponseHandler; import tech.pegasys.ethsigner.core.requesthandler.passthrough.PassThroughHandler; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.DownstreamPathCalculator; @@ -177,11 +177,11 @@ private RequestMapper createRequestMapper( "eth_signTransaction", new InternalResponseHandler<>( responseFactory, - new EthSignTransactionHandler(chainId, transactionSignerProvider, jsonDecoder))); + new EthSignTransactionResultProvider(chainId, transactionSignerProvider, jsonDecoder))); requestMapper.addHandler( "eth_signData", new InternalResponseHandler<>( - responseFactory, new EthSignDataHandler(transactionSignerProvider))); + responseFactory, new EthSignDataResultProvider(transactionSignerProvider))); return requestMapper; } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignDataHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignDataResultProvider.java similarity index 95% rename from ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignDataHandler.java rename to ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignDataResultProvider.java index 6e5e15e5f..84804324d 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignDataHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignDataResultProvider.java @@ -32,13 +32,13 @@ import org.apache.tuweni.bytes.Bytes32; import org.web3j.utils.Numeric; -public class EthSignDataHandler implements ResultProvider { +public class EthSignDataResultProvider implements ResultProvider { private static final Logger LOG = LogManager.getLogger(); private final TransactionSignerProvider transactionSignerProvider; - public EthSignDataHandler(final TransactionSignerProvider transactionSignerProvider) { + public EthSignDataResultProvider(final TransactionSignerProvider transactionSignerProvider) { this.transactionSignerProvider = transactionSignerProvider; } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java similarity index 94% rename from ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionHandler.java rename to ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java index 828128f48..59d3602e9 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java @@ -39,7 +39,7 @@ import org.web3j.crypto.TransactionEncoder; import org.web3j.utils.Numeric; -public class EthSignTransactionHandler implements ResultProvider { +public class EthSignTransactionResultProvider implements ResultProvider { private static final Logger LOG = LogManager.getLogger(); @@ -47,7 +47,7 @@ public class EthSignTransactionHandler implements ResultProvider { private final TransactionSignerProvider transactionSignerProvider; private final JsonDecoder decoder; - public EthSignTransactionHandler( + public EthSignTransactionResultProvider( final long chainId, final TransactionSignerProvider transactionSignerProvider, final JsonDecoder decoder) { @@ -88,6 +88,7 @@ public String createResponseResult(final JsonRpcRequest request) { throw new JsonRpcException(TX_SENDER_NOT_AUTHORIZED); } final byte[] bytesToSign = transaction.rlpEncode(chainId); + final Signature signature = transactionSigner.get().sign(bytesToSign); final Sign.SignatureData web3jSignature = @@ -125,6 +126,11 @@ public T fromRpcRequestToJsonParam(final Class type, final JsonRpcRequest } else { object = params; } + if (object == null) { + throw new IllegalArgumentException( + type.getSimpleName() + + " json Rpc requires a valid parameter, request contained a null object"); + } final JsonObject receivedParams = JsonObject.mapFrom(object); return decoder.decodeValue(receivedParams.toBuffer(), type); } diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignDataHandlerTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignDataResultProviderTest.java similarity index 92% rename from ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignDataHandlerTest.java rename to ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignDataResultProviderTest.java index 33f85f527..a93026611 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignDataHandlerTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignDataResultProviderTest.java @@ -25,7 +25,7 @@ import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequest; import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequestId; import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; -import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignDataHandler; +import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignDataResultProvider; import tech.pegasys.signers.secp256k1.api.Signature; import tech.pegasys.signers.secp256k1.api.TransactionSigner; import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; @@ -49,16 +49,17 @@ import org.web3j.crypto.Sign; import org.web3j.utils.Numeric; -public class EthSignDataHandlerTest { +public class EthSignDataResultProviderTest { @ParameterizedTest @ArgumentsSource(InvalidParamsProvider.class) @NullSource public void ifParamIsInvalidExceptionIsThrownWithInvalidParams(final Object params) { final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); - final EthSignDataHandler resultProvider = new EthSignDataHandler(mockSignerProvider); + final EthSignDataResultProvider resultProvider = + new EthSignDataResultProvider(mockSignerProvider); - final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signData"); request.setId(new JsonRpcRequestId(1)); request.setParams(params); @@ -71,9 +72,10 @@ public void ifParamIsInvalidExceptionIsThrownWithInvalidParams(final Object para @Test public void ifAddressIsNotUnlockedExceptionIsThrownWithSigningNotUnlocked() { final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); - final EthSignDataHandler resultProvider = new EthSignDataHandler(mockSignerProvider); + final EthSignDataResultProvider resultProvider = + new EthSignDataResultProvider(mockSignerProvider); - final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signData"); request.setId(new JsonRpcRequestId(1)); request.setParams( List.of("address", Numeric.toHexString("message".getBytes(Charset.defaultCharset())))); @@ -92,9 +94,10 @@ public void signatureHasTheExpectedFormat() { doReturn(new Signature(v, r, s)).when(mockTransactionSigner).sign(any(byte[].class)); final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); doReturn(Optional.of(mockTransactionSigner)).when(mockSignerProvider).getSigner(anyString()); - final EthSignDataHandler resultProvider = new EthSignDataHandler(mockSignerProvider); + final EthSignDataResultProvider resultProvider = + new EthSignDataResultProvider(mockSignerProvider); - final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signData"); final int id = 1; request.setId(new JsonRpcRequestId(id)); request.setParams( @@ -132,9 +135,10 @@ public void returnsExpectedSignature() { .sign(any(byte[].class)); final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); doReturn(Optional.of(mockTransactionSigner)).when(mockSignerProvider).getSigner(anyString()); - final EthSignDataHandler resultProvider = new EthSignDataHandler(mockSignerProvider); + final EthSignDataResultProvider resultProvider = + new EthSignDataResultProvider(mockSignerProvider); - final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signData"); final int id = 1; request.setId(new JsonRpcRequestId(id)); request.setParams( diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionResultProviderTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionResultProviderTest.java new file mode 100644 index 000000000..cc019a0fb --- /dev/null +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionResultProviderTest.java @@ -0,0 +1,189 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.core.jsonrpcproxy; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.INVALID_PARAMS; +import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT; + +import tech.pegasys.ethsigner.core.jsonrpc.JsonDecoder; +import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequest; +import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequestId; +import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; +import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignTransactionResultProvider; +import tech.pegasys.signers.secp256k1.api.Signature; +import tech.pegasys.signers.secp256k1.api.TransactionSigner; +import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; + +import java.math.BigInteger; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.vertx.core.json.JsonObject; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.junit.jupiter.params.provider.NullSource; +import org.web3j.crypto.Credentials; +import org.web3j.crypto.Sign; + +public class EthSignTransactionResultProviderTest { + + private static JsonDecoder jsonDecoder; + private static long chainId; + + @BeforeAll + static void beforeAll() { + final ObjectMapper jsonObjectMapper = new ObjectMapper(); + jsonObjectMapper.configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, true); + jsonObjectMapper.configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, true); + jsonDecoder = new JsonDecoder(jsonObjectMapper); + chainId = 44844; + } + + @ParameterizedTest + @ArgumentsSource(InvalidParamsProvider.class) + @NullSource + public void ifParamIsInvalidExceptionIsThrownWithInvalidParams(final Object params) { + + final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); + final EthSignTransactionResultProvider resultProvider = + new EthSignTransactionResultProvider(chainId, mockSignerProvider, jsonDecoder); + + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + request.setId(new JsonRpcRequestId(1)); + request.setParams(params); + + final Throwable thrown = catchThrowable(() -> resultProvider.createResponseResult(request)); + assertThat(thrown).isInstanceOf(JsonRpcException.class); + final JsonRpcException rpcException = (JsonRpcException) thrown; + assertThat(rpcException.getJsonRpcError()).isEqualTo(INVALID_PARAMS); + } + + @Test + public void ifAddressIsNotUnlockedExceptionIsThrownWithSigningNotUnlocked() { + final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); + final EthSignTransactionResultProvider resultProvider = + new EthSignTransactionResultProvider(chainId, mockSignerProvider, jsonDecoder); + + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + request.setId(new JsonRpcRequestId(1)); + request.setParams(List.of(getTxParameters())); + final Throwable thrown = catchThrowable(() -> resultProvider.createResponseResult(request)); + assertThat(thrown).isInstanceOf(JsonRpcException.class); + final JsonRpcException rpcException = (JsonRpcException) thrown; + assertThat(rpcException.getJsonRpcError()).isEqualTo(SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT); + } + + @Test + public void signatureHasTheExpectedFormat() { + final TransactionSigner mockTransactionSigner = mock(TransactionSigner.class); + doReturn("0xf17f52151ebef6c7334fad080c5704d77216b732").when(mockTransactionSigner).getAddress(); + final BigInteger v = BigInteger.ONE; + final BigInteger r = BigInteger.TWO; + final BigInteger s = BigInteger.TEN; + doReturn(new Signature(v, r, s)).when(mockTransactionSigner).sign(any(byte[].class)); + final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); + doReturn(Optional.of(mockTransactionSigner)).when(mockSignerProvider).getSigner(anyString()); + final EthSignTransactionResultProvider resultProvider = + new EthSignTransactionResultProvider(chainId, mockSignerProvider, jsonDecoder); + + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + final int id = 1; + request.setId(new JsonRpcRequestId(id)); + request.setParams(List.of(getTxParameters())); + + final Object result = resultProvider.createResponseResult(request); + assertThat(result).isInstanceOf(String.class); + final String signedTx = (String) result; + assertThat(signedTx).hasSize(72); + } + + @Test + public void returnsExpectedSignature() { + Credentials cs = + Credentials.create("0x1618fc3e47aec7e70451256e033b9edb67f4c469258d8e2fbb105552f141ae41"); + String addr = cs.getAddress(); + + final TransactionSigner mockTransactionSigner = mock(TransactionSigner.class); + doReturn(addr).when(mockTransactionSigner).getAddress(); + + doAnswer( + answer -> { + byte[] data = answer.getArgument(0, byte[].class); + final Sign.SignatureData signature = + Sign.signPrefixedMessage(data, cs.getEcKeyPair()); + return new Signature( + new BigInteger(signature.getV()), + new BigInteger(1, signature.getR()), + new BigInteger(1, signature.getS())); + }) + .when(mockTransactionSigner) + .sign(any(byte[].class)); + final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); + doReturn(Optional.of(mockTransactionSigner)).when(mockSignerProvider).getSigner(anyString()); + final EthSignTransactionResultProvider resultProvider = + new EthSignTransactionResultProvider(chainId, mockSignerProvider, jsonDecoder); + + JsonObject params = getTxParameters(); + params.put("from", addr); + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + final int id = 1; + request.setId(new JsonRpcRequestId(id)); + request.setParams(params); + + final Object result = resultProvider.createResponseResult(request); + assertThat(result).isInstanceOf(String.class); + final String encodedTransaction = (String) result; + assertThat(encodedTransaction) + .isEqualTo( + "0xf862468082760094627306090abab3a6e1400e9345bc60c78a8bef57010083015e7ca0c1de8a14a6bb3882fd97d5ebc3ed6db2f15cbdf9cbd9e89027973276c9d5f6d6a068214ca6ca701eaa8e74e819f838478865c267869e362c02018a11a150422efe"); + } + + private static JsonObject getTxParameters() { + final JsonObject jsonObject = new JsonObject(); + jsonObject.put("from", "0xf17f52151ebef6c7334fad080c5704d77216b732"); + jsonObject.put("to", "0x627306090abaB3A6e1400e9345bC60c78a8BEf57"); + jsonObject.put("gasPrice", "0x0"); + jsonObject.put("gas", "0x7600"); + jsonObject.put("nonce", "0x46"); + jsonObject.put("value", "0x1"); + jsonObject.put("data", "0x0"); + return jsonObject; + } + + private static class InvalidParamsProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(final ExtensionContext context) { + return Stream.of( + Arguments.of(Collections.emptyList()), + Arguments.of(Collections.singleton(2)), + Arguments.of(List.of(1, 2, 3)), + Arguments.of(new Object())); + } + } +} From d324477c4bf13de641019eb15c413db170fc7c73 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Wed, 12 Aug 2020 21:16:52 +0200 Subject: [PATCH 20/64] Remove debug provider --- .../tech/pegasys/ethsigner/core/Runner.java | 5 - .../EthSignDataResultProvider.java | 85 --------- .../EthSignTransactionResultProvider.java | 2 + .../EthSignDataResultProviderTest.java | 174 ------------------ 4 files changed, 2 insertions(+), 264 deletions(-) delete mode 100644 ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignDataResultProvider.java delete mode 100644 ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignDataResultProviderTest.java diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java index 119d4f31e..6c0204f43 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java @@ -22,7 +22,6 @@ import tech.pegasys.ethsigner.core.requesthandler.VertxRequestTransmitter; import tech.pegasys.ethsigner.core.requesthandler.VertxRequestTransmitterFactory; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthAccountsResultProvider; -import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignDataResultProvider; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignResultProvider; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignTransactionResultProvider; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.InternalResponseHandler; @@ -178,10 +177,6 @@ private RequestMapper createRequestMapper( new InternalResponseHandler<>( responseFactory, new EthSignTransactionResultProvider(chainId, transactionSignerProvider, jsonDecoder))); - requestMapper.addHandler( - "eth_signData", - new InternalResponseHandler<>( - responseFactory, new EthSignDataResultProvider(transactionSignerProvider))); return requestMapper; } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignDataResultProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignDataResultProvider.java deleted file mode 100644 index 84804324d..000000000 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignDataResultProvider.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2020 ConsenSys AG. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ -package tech.pegasys.ethsigner.core.requesthandler.internalresponse; - -import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.INVALID_PARAMS; -import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT; - -import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequest; -import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; -import tech.pegasys.ethsigner.core.requesthandler.ResultProvider; -import tech.pegasys.ethsigner.core.util.ByteUtils; -import tech.pegasys.signers.secp256k1.api.Signature; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; - -import java.util.List; -import java.util.Optional; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.bytes.Bytes32; -import org.web3j.utils.Numeric; - -public class EthSignDataResultProvider implements ResultProvider { - - private static final Logger LOG = LogManager.getLogger(); - - private final TransactionSignerProvider transactionSignerProvider; - - public EthSignDataResultProvider(final TransactionSignerProvider transactionSignerProvider) { - this.transactionSignerProvider = transactionSignerProvider; - } - - @Override - public String createResponseResult(final JsonRpcRequest request) { - final List params = getParams(request); - if (params == null || params.size() != 2) { - LOG.info( - "eth_signData should have a list of 2 parameters, but has {}", - params == null ? "null" : params.size()); - throw new JsonRpcException(INVALID_PARAMS); - } - final String address = params.get(0); - final Optional transactionSigner = - transactionSignerProvider.getSigner(address); - if (transactionSigner.isEmpty()) { - LOG.info("Address ({}) does not match any available account", address); - throw new JsonRpcException(SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT); - } - final TransactionSigner signer = transactionSigner.get(); - final String originalMessage = params.get(1); - final byte[] message = Numeric.hexStringToByteArray(originalMessage); - final Signature signature = signer.sign(message); - final Bytes outputSignature = - Bytes.concatenate( - Bytes32.leftPad(Bytes.wrap(ByteUtils.bigIntegerToBytes(signature.getR()))), - Bytes32.leftPad(Bytes.wrap(ByteUtils.bigIntegerToBytes(signature.getS()))), - Bytes.wrap(ByteUtils.bigIntegerToBytes(signature.getV()))); - return Numeric.toHexString(outputSignature.toArray()); - } - - private List getParams(final JsonRpcRequest request) { - try { - @SuppressWarnings("unchecked") - final List params = (List) request.getParams(); - return params; - } catch (final ClassCastException e) { - LOG.info( - "eth_signData should have a list of parameters, but received an object: {}", - request.getParams()); - throw new JsonRpcException(INVALID_PARAMS); - } - } -} diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java index 59d3602e9..a4cc196be 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java @@ -58,6 +58,8 @@ public EthSignTransactionResultProvider( @Override public String createResponseResult(final JsonRpcRequest request) { + // Signs a transaction that can be submitted to the network at a later time using with + // eth_sendRawTransaction. LOG.debug("Transforming request {}, {}", request.getId(), request.getMethod()); final Transaction transaction; try { diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignDataResultProviderTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignDataResultProviderTest.java deleted file mode 100644 index a93026611..000000000 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignDataResultProviderTest.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2020 ConsenSys AG. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ -package tech.pegasys.ethsigner.core.jsonrpcproxy; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.INVALID_PARAMS; -import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT; - -import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequest; -import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequestId; -import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; -import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignDataResultProvider; -import tech.pegasys.signers.secp256k1.api.Signature; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; - -import java.math.BigInteger; -import java.nio.charset.Charset; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.stream.Stream; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.ArgumentsProvider; -import org.junit.jupiter.params.provider.ArgumentsSource; -import org.junit.jupiter.params.provider.NullSource; -import org.web3j.crypto.ECDSASignature; -import org.web3j.crypto.ECKeyPair; -import org.web3j.crypto.Sign; -import org.web3j.utils.Numeric; - -public class EthSignDataResultProviderTest { - - @ParameterizedTest - @ArgumentsSource(InvalidParamsProvider.class) - @NullSource - public void ifParamIsInvalidExceptionIsThrownWithInvalidParams(final Object params) { - final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); - final EthSignDataResultProvider resultProvider = - new EthSignDataResultProvider(mockSignerProvider); - - final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signData"); - request.setId(new JsonRpcRequestId(1)); - request.setParams(params); - - final Throwable thrown = catchThrowable(() -> resultProvider.createResponseResult(request)); - assertThat(thrown).isInstanceOf(JsonRpcException.class); - final JsonRpcException rpcException = (JsonRpcException) thrown; - assertThat(rpcException.getJsonRpcError()).isEqualTo(INVALID_PARAMS); - } - - @Test - public void ifAddressIsNotUnlockedExceptionIsThrownWithSigningNotUnlocked() { - final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); - final EthSignDataResultProvider resultProvider = - new EthSignDataResultProvider(mockSignerProvider); - - final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signData"); - request.setId(new JsonRpcRequestId(1)); - request.setParams( - List.of("address", Numeric.toHexString("message".getBytes(Charset.defaultCharset())))); - final Throwable thrown = catchThrowable(() -> resultProvider.createResponseResult(request)); - assertThat(thrown).isInstanceOf(JsonRpcException.class); - final JsonRpcException rpcException = (JsonRpcException) thrown; - assertThat(rpcException.getJsonRpcError()).isEqualTo(SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT); - } - - @Test - public void signatureHasTheExpectedFormat() { - final TransactionSigner mockTransactionSigner = mock(TransactionSigner.class); - final BigInteger v = BigInteger.ONE; - final BigInteger r = BigInteger.TWO; - final BigInteger s = BigInteger.TEN; - doReturn(new Signature(v, r, s)).when(mockTransactionSigner).sign(any(byte[].class)); - final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); - doReturn(Optional.of(mockTransactionSigner)).when(mockSignerProvider).getSigner(anyString()); - final EthSignDataResultProvider resultProvider = - new EthSignDataResultProvider(mockSignerProvider); - - final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signData"); - final int id = 1; - request.setId(new JsonRpcRequestId(id)); - request.setParams( - List.of("address", Numeric.toHexString("message".getBytes(Charset.defaultCharset())))); - - final Object result = resultProvider.createResponseResult(request); - assertThat(result).isInstanceOf(String.class); - final String hexSignature = (String) result; - assertThat(hexSignature).hasSize(132); - - final byte[] signature = Numeric.hexStringToByteArray(hexSignature); - - assertThat(new BigInteger(1, signature, 0, 32)).isEqualTo(r); - assertThat(new BigInteger(1, signature, 32, 32)).isEqualTo(s); - assertThat(new BigInteger(1, signature, 64, 1)).isEqualTo(v); - } - - @Test - public void returnsExpectedSignature() { - final ECKeyPair keyPair = - ECKeyPair.create( - Numeric.hexStringToByteArray( - "0x1618fc3e47aec7e70451256e033b9edb67f4c469258d8e2fbb105552f141ae41")); - final TransactionSigner mockTransactionSigner = mock(TransactionSigner.class); - doAnswer( - answer -> { - byte[] data = answer.getArgument(0, byte[].class); - final Sign.SignatureData signature = Sign.signPrefixedMessage(data, keyPair); - return new Signature( - new BigInteger(signature.getV()), - new BigInteger(1, signature.getR()), - new BigInteger(1, signature.getS())); - }) - .when(mockTransactionSigner) - .sign(any(byte[].class)); - final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); - doReturn(Optional.of(mockTransactionSigner)).when(mockSignerProvider).getSigner(anyString()); - final EthSignDataResultProvider resultProvider = - new EthSignDataResultProvider(mockSignerProvider); - - final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signData"); - final int id = 1; - request.setId(new JsonRpcRequestId(id)); - request.setParams( - List.of( - "address", - Numeric.toHexString( - "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Tubulum fuisse, qua illum, cuius is condemnatus est rogatione, P. Eaedem res maneant alio modo." - .getBytes(Charset.defaultCharset())))); - - final Object result = resultProvider.createResponseResult(request); - assertThat(result).isInstanceOf(String.class); - final String hexSignature = (String) result; - final byte[] signature = Numeric.hexStringToByteArray(hexSignature); - - final ECDSASignature expectedSignature = - keyPair.sign( - Numeric.hexStringToByteArray( - "0xe63325d74baa84af003dfb6a974f41672be881b56aa2c12c093f8259321bd460")); - assertThat(new BigInteger(1, signature, 0, 32)).isEqualTo(expectedSignature.r); - assertThat(new BigInteger(1, signature, 32, 32)).isEqualTo(expectedSignature.s); - } - - private static class InvalidParamsProvider implements ArgumentsProvider { - @Override - public Stream provideArguments(final ExtensionContext context) { - return Stream.of( - Arguments.of(Collections.emptyList()), - Arguments.of(Collections.singleton(2)), - Arguments.of(List.of(1, 2, 3)), - Arguments.of(new Object())); - } - } -} From 6c1f78f027f5426864f1f652efc1a2148f45a84d Mon Sep 17 00:00:00 2001 From: mkrielza Date: Fri, 14 Aug 2020 08:26:06 +0200 Subject: [PATCH 21/64] Switch --- .../subcommands/MultiKeySubCommand.java | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java index 02d78c163..98b66251d 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java @@ -58,31 +58,38 @@ public MultiKeySubCommand() {} private Path directoryPath; @Option( - names = {"-l", "--library"}, - description = "The HSM PKCS11 library used to sign transactions.", - paramLabel = "", + names = {"-c", "--config"}, + description = "Config for signer providers.", + paramLabel = "", required = false) - private Path libraryPath; - - @Option( - names = {"-s", "--slot-label"}, - description = "The HSM slot used to sign transactions.", - paramLabel = "", - required = false) - private String slotLabel; - - @Option( - names = {"-p", "--slot-pin"}, - description = "The crypto user pin of the HSM slot used to sign transactions.", - paramLabel = "", - required = false) - private String slotPin; + private Path configPath; + +// @Option( +// names = {"-l", "--library"}, +// description = "The HSM PKCS11 library used to sign transactions.", +// paramLabel = "", +// required = false) +// private Path libraryPath; +// +// @Option( +// names = {"-s", "--slot-label"}, +// description = "The HSM slot used to sign transactions.", +// paramLabel = "", +// required = false) +// private String slotLabel; +// +// @Option( +// names = {"-p", "--slot-pin"}, +// description = "The crypto user pin of the HSM slot used to sign transactions.", +// paramLabel = "", +// required = false) +// private String slotPin; @Override public TransactionSignerProvider createSignerFactory() throws TransactionSignerInitializationException { return MultiKeyTransactionSignerProvider.create( - directoryPath, libraryPath != null ? libraryPath.toString() : null, slotLabel, slotPin); + directoryPath, configPath); } @Override From 8a277b6ee89a59c1659665406604a95bb05ec10f Mon Sep 17 00:00:00 2001 From: mkrielza Date: Fri, 14 Aug 2020 13:47:14 +0200 Subject: [PATCH 22/64] Upgrades --- CODING-CONVENTIONS.md | 2 +- .../tests/dsl/signer/EthSignerRunner.java | 2 +- .../tests/dsl/signer/SignerConfiguration.java | 10 +++--- .../signer/SignerConfigurationBuilder.java | 6 ++-- .../TransactionSignerParamsSupplier.java | 4 +-- ...yAzureTransactionSignerAcceptanceTest.java | 2 +- ...eBasedTransactionSignerAcceptanceTest.java | 2 +- ...hicorpTransactionSignerAcceptanceTest.java | 2 +- .../pegasys/ethsigner/CommandlineParser.java | 4 +-- .../pegasys/ethsigner/SignerSubCommand.java | 10 +++--- .../ethsigner/NullSignerSubCommand.java | 18 +++++----- .../IllegalSignatureCreationTest.java | 6 ++-- .../jsonrpcproxy/IntegrationTestBase.java | 16 ++++----- .../pegasys/ethsigner/core/EthSigner.java | 12 +++---- .../tech/pegasys/ethsigner/core/Runner.java | 16 ++++----- .../EthSignResultProvider.java | 18 +++++----- .../EthSignTransactionResultProvider.java | 22 ++++++------ .../SendTransactionHandler.java | 22 ++++++------ .../core/signing/TransactionSerializer.java | 6 ++-- .../EthSignResultProviderTest.java | 24 ++++++------- .../EthSignTransactionResultProviderTest.java | 28 +++++++-------- .../SingleTransactionSignerProviderTest.java | 34 +++++++++---------- .../subcommands/AzureSubCommand.java | 28 +++++++-------- .../subcommands/CaviumSubCommand.java | 20 ++++++----- .../subcommands/FileBasedSubCommand.java | 16 ++++----- .../ethsigner/subcommands/HSMSubCommand.java | 24 ++++++------- .../subcommands/HashicorpSubCommand.java | 20 +++++------ .../subcommands/MultiKeySubCommand.java | 14 ++++---- .../ethsigner/subcommands/RawSubCommand.java | 20 +++++------ gradle/versions.gradle | 2 +- 30 files changed, 206 insertions(+), 204 deletions(-) diff --git a/CODING-CONVENTIONS.md b/CODING-CONVENTIONS.md index 2a58509d2..e4fbac498 100644 --- a/CODING-CONVENTIONS.md +++ b/CODING-CONVENTIONS.md @@ -37,7 +37,7 @@ Simple does not mean the fewest lines of code. Simple code is: ## 2.2 Idiomatic Java EthSigner embraces typical Java idioms including using an Object Oriented approach to design. This includes: -* Providing alternate behaviours via polymorphism instead of having conditional logic scattered through the codebase. For example, `TransactionSigner` provides a standard interface to signing operations. +* Providing alternate behaviours via polymorphism instead of having conditional logic scattered through the codebase. For example, `Signer` provides a standard interface to signing operations. * Encapsulating behaviour and data together in classes. * Embracing modern Java features like Optional, Streams and lambdas when they make code simpler and clearer. diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/EthSignerRunner.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/EthSignerRunner.java index 3fc14770a..29a7866de 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/EthSignerRunner.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/EthSignerRunner.java @@ -126,7 +126,7 @@ public void start(final String processName) { params.addAll(createServerTlsArgs()); params.addAll(createDownstreamTlsArgs()); - params.addAll(signerConfig.transactionSignerParamsSupplier().get()); + params.addAll(signerConfig.SignerParamsSupplier().get()); LOG.info("Creating EthSigner process with params {}", params); diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfiguration.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfiguration.java index 3f88d933e..d4a239050 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfiguration.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfiguration.java @@ -29,7 +29,7 @@ public class SignerConfiguration { private final String hostname; private final int httpRpcPort; private final int webSocketPort; - private final TransactionSignerParamsSupplier transactionSignerParamsSupplier; + private final SignerParamsSupplier SignerParamsSupplier; private final Optional serverTlsOptions; private final Optional clientTlsOptions; private final Optional overriddenCaTrustStore; @@ -39,7 +39,7 @@ public SignerConfiguration( final String hostname, final int httpRpcPort, final int webSocketPort, - final TransactionSignerParamsSupplier transactionSignerParamsSupplier, + final SignerParamsSupplier SignerParamsSupplier, final Optional serverTlsOptions, final Optional clientTlsOptions, final Optional overriddenCaTrustStore) { @@ -47,7 +47,7 @@ public SignerConfiguration( this.hostname = hostname; this.httpRpcPort = httpRpcPort; this.webSocketPort = webSocketPort; - this.transactionSignerParamsSupplier = transactionSignerParamsSupplier; + this.SignerParamsSupplier = SignerParamsSupplier; this.serverTlsOptions = serverTlsOptions; this.clientTlsOptions = clientTlsOptions; this.overriddenCaTrustStore = overriddenCaTrustStore; @@ -89,8 +89,8 @@ public Optional getOverriddenCaTrustStore() { return overriddenCaTrustStore; } - public TransactionSignerParamsSupplier transactionSignerParamsSupplier() { - return transactionSignerParamsSupplier; + public SignerParamsSupplier SignerParamsSupplier() { + return SignerParamsSupplier; } public boolean isDynamicPortAllocation() { diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfigurationBuilder.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfigurationBuilder.java index 56eed46c9..760d24e8b 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfigurationBuilder.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfigurationBuilder.java @@ -80,14 +80,14 @@ public SignerConfigurationBuilder withOverriddenCA(final TlsCertificateDefinitio } public SignerConfiguration build() { - final TransactionSignerParamsSupplier transactionSignerParamsSupplier = - new TransactionSignerParamsSupplier(hashicorpNode, keyVaultName, multiKeySignerDirectory); + final SignerParamsSupplier SignerParamsSupplier = + new SignerParamsSupplier(hashicorpNode, keyVaultName, multiKeySignerDirectory); return new SignerConfiguration( CHAIN_ID, LOCALHOST, httpRpcPort, webSocketPort, - transactionSignerParamsSupplier, + SignerParamsSupplier, Optional.ofNullable(serverTlsOptions), Optional.ofNullable(clientTlsOptions), Optional.ofNullable(overriddenCaTrustStore)); diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/TransactionSignerParamsSupplier.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/TransactionSignerParamsSupplier.java index 3ad7256fe..d76866072 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/TransactionSignerParamsSupplier.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/TransactionSignerParamsSupplier.java @@ -31,13 +31,13 @@ import com.google.common.io.Resources; -public class TransactionSignerParamsSupplier { +public class SignerParamsSupplier { private final HashicorpSigningParams hashicorpNode; private final String azureKeyVault; private final Path multiKeySignerDirectory; - public TransactionSignerParamsSupplier( + public SignerParamsSupplier( final HashicorpSigningParams hashicorpNode, final String azureKeyVault, final Path multiKeySignerDirectory) { diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyAzureTransactionSignerAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyAzureTransactionSignerAcceptanceTest.java index 2f6080608..8b8bcf74e 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyAzureTransactionSignerAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyAzureTransactionSignerAcceptanceTest.java @@ -19,7 +19,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -public class MultiKeyAzureTransactionSignerAcceptanceTest +public class MultiKeyAzureSignerAcceptanceTest extends MultiKeyTransactionSigningAcceptanceTestBase { static final String clientId = System.getenv("ETHSIGNER_AZURE_CLIENT_ID"); diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyFileBasedTransactionSignerAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyFileBasedTransactionSignerAcceptanceTest.java index 8e770c92a..77fdcbebc 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyFileBasedTransactionSignerAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyFileBasedTransactionSignerAcceptanceTest.java @@ -24,7 +24,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -public class MultiKeyFileBasedTransactionSignerAcceptanceTest +public class MultiKeyFileBasedSignerAcceptanceTest extends MultiKeyTransactionSigningAcceptanceTestBase { static final String FILENAME = "fe3b557e8fb62b89f4916b721be55ceb828dbd73"; diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyHashicorpTransactionSignerAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyHashicorpTransactionSignerAcceptanceTest.java index a499a8b18..752e856cc 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyHashicorpTransactionSignerAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyHashicorpTransactionSignerAcceptanceTest.java @@ -22,7 +22,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -public class MultiKeyHashicorpTransactionSignerAcceptanceTest +public class MultiKeyHashicorpSignerAcceptanceTest extends MultiKeyTransactionSigningAcceptanceTestBase { static final String FILENAME = "fe3b557e8fb62b89f4916b721be55ceb828dbd73"; diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/CommandlineParser.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/CommandlineParser.java index 849776ea3..98f6341d2 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/CommandlineParser.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/CommandlineParser.java @@ -18,7 +18,7 @@ import tech.pegasys.ethsigner.valueprovider.CascadingDefaultProvider; import tech.pegasys.ethsigner.valueprovider.EnvironmentVariableDefaultProvider; import tech.pegasys.ethsigner.valueprovider.TomlConfigFileDefaultProvider; -import tech.pegasys.signers.secp256k1.common.TransactionSignerInitializationException; +import tech.pegasys.signers.secp256k1.common.SignerInitializationException; import java.io.File; import java.io.PrintWriter; @@ -145,7 +145,7 @@ private int handleExecutionException( final CommandLine commandLine, final CommandLine.ParseResult parseResult) { - if (ex instanceof TransactionSignerInitializationException) { + if (ex instanceof SignerInitializationException) { errorWriter.println(SIGNER_CREATION_ERROR); errorWriter.println("Cause: " + ex.getMessage()); } else if (ex instanceof InitializationException) { diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/SignerSubCommand.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/SignerSubCommand.java index 62cec2eb1..90dd8bb0c 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/SignerSubCommand.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/SignerSubCommand.java @@ -14,8 +14,8 @@ import tech.pegasys.ethsigner.core.EthSigner; import tech.pegasys.ethsigner.core.InitializationException; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; -import tech.pegasys.signers.secp256k1.common.TransactionSignerInitializationException; +import tech.pegasys.signers.secp256k1.api.SignerProvider; +import tech.pegasys.signers.secp256k1.common.SignerInitializationException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -28,8 +28,8 @@ public abstract class SignerSubCommand implements Runnable { @CommandLine.ParentCommand private EthSignerBaseCommand config; - public abstract TransactionSignerProvider createSignerFactory() - throws TransactionSignerInitializationException; + public abstract SignerProvider createSignerFactory() + throws SignerInitializationException; public abstract String getCommandName(); @@ -40,7 +40,7 @@ protected void validateArgs() throws InitializationException { } @Override - public void run() throws TransactionSignerInitializationException { + public void run() throws SignerInitializationException { validateArgs(); diff --git a/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/NullSignerSubCommand.java b/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/NullSignerSubCommand.java index 4156e51b8..22e3b0096 100644 --- a/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/NullSignerSubCommand.java +++ b/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/NullSignerSubCommand.java @@ -12,9 +12,9 @@ */ package tech.pegasys.ethsigner; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; -import tech.pegasys.signers.secp256k1.common.TransactionSignerInitializationException; +import tech.pegasys.signers.secp256k1.api.Signer; +import tech.pegasys.signers.secp256k1.api.SignerProvider; +import tech.pegasys.signers.secp256k1.common.SignerInitializationException; import java.util.Collections; import java.util.Optional; @@ -46,9 +46,9 @@ public NullSignerSubCommand(boolean shouldThrow) { } @Override - public TransactionSignerProvider createSignerFactory() - throws TransactionSignerInitializationException { - return new EmptyTransactionSignerProvider(); + public SignerProvider createSignerFactory() + throws SignerInitializationException { + return new EmptySignerProvider(); } @Override @@ -61,14 +61,14 @@ public void run() { // this is required to do any non-PicoCLI validation of args prior to setup super.validateArgs(); if (shouldThrow) { - throw new TransactionSignerInitializationException(ERROR_MSG); + throw new SignerInitializationException(ERROR_MSG); } } - public static class EmptyTransactionSignerProvider implements TransactionSignerProvider { + public static class EmptySignerProvider implements SignerProvider { @Override - public Optional getSigner(final String address) { + public Optional getSigner(final String address) { return Optional.empty(); } diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IllegalSignatureCreationTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IllegalSignatureCreationTest.java index 3ec2be4a0..60a6d67e9 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IllegalSignatureCreationTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IllegalSignatureCreationTest.java @@ -17,7 +17,7 @@ import tech.pegasys.ethsigner.core.jsonrpc.EthSendTransactionJsonParameters; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.EthTransaction; import tech.pegasys.signers.secp256k1.api.Signature; -import tech.pegasys.signers.secp256k1.filebased.CredentialTransactionSigner; +import tech.pegasys.signers.secp256k1.filebased.CredentialSigner; import org.junit.jupiter.api.Test; import org.web3j.crypto.Credentials; @@ -44,8 +44,8 @@ void ensureSignaturesCreatedHavePositiveValues() { final EthTransaction txn = new EthTransaction(txnParams, null, null); final byte[] serialisedBytes = txn.rlpEncode(chainId); - final CredentialTransactionSigner signer = - new CredentialTransactionSigner( + final CredentialSigner signer = + new CredentialSigner( Credentials.create("ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f")); final Signature signature = signer.sign(serialisedBytes); diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IntegrationTestBase.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IntegrationTestBase.java index 14fcfaf7d..f759a7390 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IntegrationTestBase.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IntegrationTestBase.java @@ -35,9 +35,9 @@ import tech.pegasys.ethsigner.jsonrpcproxy.model.response.EthSignerResponse; import tech.pegasys.ethsigner.jsonrpcproxy.support.MockServer; import tech.pegasys.ethsigner.jsonrpcproxy.support.RestAssuredConverter; -import tech.pegasys.signers.secp256k1.api.SingleTransactionSignerProvider; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; +import tech.pegasys.signers.secp256k1.api.SingleSignerProvider; +import tech.pegasys.signers.secp256k1.api.Signer; +import tech.pegasys.signers.secp256k1.api.SignerProvider; import tech.pegasys.signers.secp256k1.filebased.FileBasedSignerFactory; import java.io.File; @@ -116,8 +116,8 @@ static void setupEthSigner(final long chainId, final String downstreamHttpReques final File passwordFile = createFile("password"); credentials = WalletUtils.loadCredentials("password", keyFile); - final TransactionSignerProvider transactionSignerProvider = - new SingleTransactionSignerProvider(transactionSigner(keyFile, passwordFile)); + final SignerProvider SignerProvider = + new SingleSignerProvider(Signer(keyFile, passwordFile)); final HttpClientOptions httpClientOptions = new HttpClientOptions(); httpClientOptions.setDefaultHost(LOCALHOST); @@ -138,7 +138,7 @@ static void setupEthSigner(final long chainId, final String downstreamHttpReques runner = new Runner( chainId, - transactionSignerProvider, + SignerProvider, httpClientOptions, httpServerOptions, downstreamTimeout, @@ -160,7 +160,7 @@ static void setupEthSigner(final long chainId, final String downstreamHttpReques clientAndServer.getLocalPort()); unlockedAccount = - transactionSignerProvider.availableAddresses().stream().findAny().orElseThrow(); + SignerProvider.availableAddresses().stream().findAny().orElseThrow(); } Web3j jsonRpc() { @@ -312,7 +312,7 @@ void verifyEthNodeReceived( .withHeaders(MockServer.headers(headers))); } - private static TransactionSigner transactionSigner(final File keyFile, final File passwordFile) { + private static Signer Signer(final File keyFile, final File passwordFile) { return FileBasedSignerFactory.createSigner(keyFile.toPath(), passwordFile.toPath()); } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java index a405d43c7..726d23749 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java @@ -18,7 +18,7 @@ import tech.pegasys.ethsigner.core.jsonrpc.JsonDecoder; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.DownstreamPathCalculator; import tech.pegasys.ethsigner.core.util.FileUtil; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; +import tech.pegasys.signers.secp256k1.api.SignerProvider; import java.io.IOException; import java.nio.file.AccessDeniedException; @@ -40,12 +40,12 @@ public final class EthSigner { private static final Logger LOG = LogManager.getLogger(); private final Config config; - private final TransactionSignerProvider transactionSignerProvider; + private final SignerProvider SignerProvider; private final WebClientOptionsFactory webClientOptionsFactory = new WebClientOptionsFactory(); - public EthSigner(final Config config, final TransactionSignerProvider transactionSignerProvider) { + public EthSigner(final Config config, final SignerProvider SignerProvider) { this.config = config; - this.transactionSignerProvider = transactionSignerProvider; + this.SignerProvider = SignerProvider; } public void run() { @@ -76,7 +76,7 @@ public void run() { final Runner runner = new Runner( config.getChainId().id(), - transactionSignerProvider, + SignerProvider, webClientOptionsFactory.createWebClientOptions(config), applyConfigTlsSettingsTo(serverOptions), downstreamHttpRequestTimeout, @@ -97,7 +97,7 @@ public void run() { class Shutdown extends Thread { @Override public void run() { - transactionSignerProvider.shutdown(); + SignerProvider.shutdown(); System.out.println("Shutting down EthSigner"); } } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java index 6c0204f43..0950073e3 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java @@ -29,7 +29,7 @@ import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.DownstreamPathCalculator; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.SendTransactionHandler; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.TransactionFactory; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; +import tech.pegasys.signers.secp256k1.api.SignerProvider; import java.io.File; import java.io.FileOutputStream; @@ -65,7 +65,7 @@ public class Runner { private static final String TEXT = HttpHeaderValues.TEXT_PLAIN.toString() + "; charset=utf-8"; private final long chainId; - private final TransactionSignerProvider transactionSignerProvider; + private final SignerProvider SignerProvider; private final HttpClientOptions clientOptions; private final Duration httpRequestTimeout; private final DownstreamPathCalculator downstreamPathCalculator; @@ -78,7 +78,7 @@ public class Runner { public Runner( final long chainId, - final TransactionSignerProvider transactionSignerProvider, + final SignerProvider SignerProvider, final HttpClientOptions clientOptions, final HttpServerOptions serverOptions, final Duration httpRequestTimeout, @@ -88,7 +88,7 @@ public Runner( final Vertx vertx, final Collection allowedCorsOrigins) { this.chainId = chainId; - this.transactionSignerProvider = transactionSignerProvider; + this.SignerProvider = SignerProvider; this.clientOptions = clientOptions; this.httpRequestTimeout = httpRequestTimeout; this.downstreamPathCalculator = downstreamPathCalculator; @@ -158,7 +158,7 @@ private RequestMapper createRequestMapper( final SendTransactionHandler sendTransactionHandler = new SendTransactionHandler( - chainId, transactionSignerProvider, transactionFactory, transmitterFactory); + chainId, SignerProvider, transactionFactory, transmitterFactory); final RequestMapper requestMapper = new RequestMapper(defaultHandler); requestMapper.addHandler("eth_sendTransaction", sendTransactionHandler); @@ -167,16 +167,16 @@ private RequestMapper createRequestMapper( "eth_accounts", new InternalResponseHandler<>( responseFactory, - new EthAccountsResultProvider(transactionSignerProvider::availableAddresses))); + new EthAccountsResultProvider(SignerProvider::availableAddresses))); requestMapper.addHandler( "eth_sign", new InternalResponseHandler<>( - responseFactory, new EthSignResultProvider(transactionSignerProvider))); + responseFactory, new EthSignResultProvider(SignerProvider))); requestMapper.addHandler( "eth_signTransaction", new InternalResponseHandler<>( responseFactory, - new EthSignTransactionResultProvider(chainId, transactionSignerProvider, jsonDecoder))); + new EthSignTransactionResultProvider(chainId, SignerProvider, jsonDecoder))); return requestMapper; } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignResultProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignResultProvider.java index 714902a81..3acc05aef 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignResultProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignResultProvider.java @@ -20,8 +20,8 @@ import tech.pegasys.ethsigner.core.requesthandler.ResultProvider; import tech.pegasys.ethsigner.core.util.ByteUtils; import tech.pegasys.signers.secp256k1.api.Signature; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; +import tech.pegasys.signers.secp256k1.api.Signer; +import tech.pegasys.signers.secp256k1.api.SignerProvider; import java.nio.charset.StandardCharsets; import java.util.List; @@ -37,10 +37,10 @@ public class EthSignResultProvider implements ResultProvider { private static final Logger LOG = LogManager.getLogger(); - private final TransactionSignerProvider transactionSignerProvider; + private final SignerProvider SignerProvider; - public EthSignResultProvider(final TransactionSignerProvider transactionSignerProvider) { - this.transactionSignerProvider = transactionSignerProvider; + public EthSignResultProvider(final SignerProvider SignerProvider) { + this.SignerProvider = SignerProvider; } @Override @@ -53,13 +53,13 @@ public String createResponseResult(final JsonRpcRequest request) { throw new JsonRpcException(INVALID_PARAMS); } final String address = params.get(0); - final Optional transactionSigner = - transactionSignerProvider.getSigner(address); - if (transactionSigner.isEmpty()) { + final Optional Signer = + SignerProvider.getSigner(address); + if (Signer.isEmpty()) { LOG.info("Address ({}) does not match any available account", address); throw new JsonRpcException(SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT); } - final TransactionSigner signer = transactionSigner.get(); + final Signer signer = Signer.get(); final String originalMessage = params.get(1); final String message = (char) 25 + "Ethereum Signed Message:\n" + originalMessage.length() + originalMessage; diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java index a4cc196be..dd52139c9 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java @@ -25,8 +25,8 @@ import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.Transaction; import tech.pegasys.ethsigner.core.util.HexStringComparator; import tech.pegasys.signers.secp256k1.api.Signature; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; +import tech.pegasys.signers.secp256k1.api.Signer; +import tech.pegasys.signers.secp256k1.api.SignerProvider; import java.util.List; import java.util.Optional; @@ -44,15 +44,15 @@ public class EthSignTransactionResultProvider implements ResultProvider private static final Logger LOG = LogManager.getLogger(); private final long chainId; - private final TransactionSignerProvider transactionSignerProvider; + private final SignerProvider SignerProvider; private final JsonDecoder decoder; public EthSignTransactionResultProvider( final long chainId, - final TransactionSignerProvider transactionSignerProvider, + final SignerProvider SignerProvider, final JsonDecoder decoder) { this.chainId = chainId; - this.transactionSignerProvider = transactionSignerProvider; + this.SignerProvider = SignerProvider; this.decoder = decoder; } @@ -73,25 +73,25 @@ public String createResponseResult(final JsonRpcRequest request) { throw new JsonRpcException(INVALID_PARAMS); } - final Optional transactionSigner = - transactionSignerProvider.getSigner(transaction.sender()); + final Optional Signer = + SignerProvider.getSigner(transaction.sender()); - if (transactionSigner.isEmpty()) { + if (Signer.isEmpty()) { LOG.info("From address ({}) does not match any available account", transaction.sender()); throw new JsonRpcException(SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT); } final HexStringComparator comparator = new HexStringComparator(); - if (comparator.compare(transactionSigner.get().getAddress(), transaction.sender()) != 0) { + if (comparator.compare(Signer.get().getAddress(), transaction.sender()) != 0) { LOG.info( "Ethereum address derived from identifier ({}) is incorrect value ({})", transaction.sender(), - transactionSigner.get().getAddress()); + Signer.get().getAddress()); throw new JsonRpcException(TX_SENDER_NOT_AUTHORIZED); } final byte[] bytesToSign = transaction.rlpEncode(chainId); - final Signature signature = transactionSigner.get().sign(bytesToSign); + final Signature signature = Signer.get().sign(bytesToSign); final Sign.SignatureData web3jSignature = new Sign.SignatureData( diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java index 90da3af49..2d874bf0d 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java @@ -26,8 +26,8 @@ import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.TransactionFactory; import tech.pegasys.ethsigner.core.signing.TransactionSerializer; import tech.pegasys.ethsigner.core.util.HexStringComparator; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; +import tech.pegasys.signers.secp256k1.api.Signer; +import tech.pegasys.signers.secp256k1.api.SignerProvider; import java.util.Optional; @@ -41,7 +41,7 @@ public class SendTransactionHandler implements JsonRpcRequestHandler { private static final Logger LOG = LogManager.getLogger(); private final long chainId; - private final TransactionSignerProvider transactionSignerProvider; + private final SignerProvider SignerProvider; private final TransactionFactory transactionFactory; private final VertxRequestTransmitterFactory vertxTransmitterFactory; @@ -49,11 +49,11 @@ public class SendTransactionHandler implements JsonRpcRequestHandler { public SendTransactionHandler( final long chainId, - final TransactionSignerProvider transactionSignerProvider, + final SignerProvider SignerProvider, final TransactionFactory transactionFactory, final VertxRequestTransmitterFactory vertxTransmitterFactory) { this.chainId = chainId; - this.transactionSignerProvider = transactionSignerProvider; + this.SignerProvider = SignerProvider; this.transactionFactory = transactionFactory; this.vertxTransmitterFactory = vertxTransmitterFactory; } @@ -74,10 +74,10 @@ public void handle(final RoutingContext context, final JsonRpcRequest request) { return; } - final Optional transactionSigner = - transactionSignerProvider.getSigner(transaction.sender()); + final Optional Signer = + SignerProvider.getSigner(transaction.sender()); - if (transactionSigner.isEmpty()) { + if (Signer.isEmpty()) { LOG.info("From address ({}) does not match any available account", transaction.sender()); context.fail( BAD_REQUEST.code(), new JsonRpcException(SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT)); @@ -85,17 +85,17 @@ public void handle(final RoutingContext context, final JsonRpcRequest request) { } final HexStringComparator comparator = new HexStringComparator(); - if (comparator.compare(transactionSigner.get().getAddress(), transaction.sender()) != 0) { + if (comparator.compare(Signer.get().getAddress(), transaction.sender()) != 0) { LOG.info( "Ethereum address derived from identifier ({}) is incorrect value ({})", transaction.sender(), - transactionSigner.get().getAddress()); + Signer.get().getAddress()); context.fail(INTERNAL_SERVER_ERROR.code(), new JsonRpcException(TX_SENDER_NOT_AUTHORIZED)); return; } final TransactionSerializer transactionSerializer = - new TransactionSerializer(transactionSigner.get(), chainId); + new TransactionSerializer(Signer.get(), chainId); sendTransaction(transaction, transactionSerializer, context, request); } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/TransactionSerializer.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/TransactionSerializer.java index fbe548224..5acb0e6df 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/TransactionSerializer.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/TransactionSerializer.java @@ -14,7 +14,7 @@ import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.Transaction; import tech.pegasys.signers.secp256k1.api.Signature; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; +import tech.pegasys.signers.secp256k1.api.Signer; import org.web3j.crypto.Sign.SignatureData; import org.web3j.crypto.TransactionEncoder; @@ -22,10 +22,10 @@ public class TransactionSerializer { - private final TransactionSigner signer; + private final Signer signer; private final long chainId; - public TransactionSerializer(final TransactionSigner signer, final long chainId) { + public TransactionSerializer(final Signer signer, final long chainId) { this.signer = signer; this.chainId = chainId; } diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignResultProviderTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignResultProviderTest.java index c8317fc37..6150b99cd 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignResultProviderTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignResultProviderTest.java @@ -27,8 +27,8 @@ import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignResultProvider; import tech.pegasys.signers.secp256k1.api.Signature; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; +import tech.pegasys.signers.secp256k1.api.Signer; +import tech.pegasys.signers.secp256k1.api.SignerProvider; import java.math.BigInteger; import java.util.Collections; @@ -54,7 +54,7 @@ public class EthSignResultProviderTest { @ArgumentsSource(InvalidParamsProvider.class) @NullSource public void ifParamIsInvalidExceptionIsThrownWithInvalidParams(final Object params) { - final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); + final SignerProvider mockSignerProvider = mock(SignerProvider.class); final EthSignResultProvider resultProvider = new EthSignResultProvider(mockSignerProvider); final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_sign"); @@ -69,7 +69,7 @@ public void ifParamIsInvalidExceptionIsThrownWithInvalidParams(final Object para @Test public void ifAddressIsNotUnlockedExceptionIsThrownWithSigningNotUnlocked() { - final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); + final SignerProvider mockSignerProvider = mock(SignerProvider.class); final EthSignResultProvider resultProvider = new EthSignResultProvider(mockSignerProvider); final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_sign"); @@ -83,13 +83,13 @@ public void ifAddressIsNotUnlockedExceptionIsThrownWithSigningNotUnlocked() { @Test public void signatureHasTheExpectedFormat() { - final TransactionSigner mockTransactionSigner = mock(TransactionSigner.class); + final Signer mockSigner = mock(Signer.class); final BigInteger v = BigInteger.ONE; final BigInteger r = BigInteger.TWO; final BigInteger s = BigInteger.TEN; - doReturn(new Signature(v, r, s)).when(mockTransactionSigner).sign(any(byte[].class)); - final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); - doReturn(Optional.of(mockTransactionSigner)).when(mockSignerProvider).getSigner(anyString()); + doReturn(new Signature(v, r, s)).when(mockSigner).sign(any(byte[].class)); + final SignerProvider mockSignerProvider = mock(SignerProvider.class); + doReturn(Optional.of(mockSigner)).when(mockSignerProvider).getSigner(anyString()); final EthSignResultProvider resultProvider = new EthSignResultProvider(mockSignerProvider); final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_sign"); @@ -115,7 +115,7 @@ public void returnsExpectedSignature() { ECKeyPair.create( Numeric.hexStringToByteArray( "0x1618fc3e47aec7e70451256e033b9edb67f4c469258d8e2fbb105552f141ae41")); - final TransactionSigner mockTransactionSigner = mock(TransactionSigner.class); + final Signer mockSigner = mock(Signer.class); doAnswer( answer -> { byte[] data = answer.getArgument(0, byte[].class); @@ -125,11 +125,11 @@ public void returnsExpectedSignature() { new BigInteger(1, signature.getR()), new BigInteger(1, signature.getS())); }) - .when(mockTransactionSigner) + .when(mockSigner) .sign(any(byte[].class)); - final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); - doReturn(Optional.of(mockTransactionSigner)).when(mockSignerProvider).getSigner(anyString()); + final SignerProvider mockSignerProvider = mock(SignerProvider.class); + doReturn(Optional.of(mockSigner)).when(mockSignerProvider).getSigner(anyString()); final EthSignResultProvider resultProvider = new EthSignResultProvider(mockSignerProvider); final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_sign"); diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionResultProviderTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionResultProviderTest.java index cc019a0fb..ee3e3c466 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionResultProviderTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionResultProviderTest.java @@ -28,8 +28,8 @@ import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignTransactionResultProvider; import tech.pegasys.signers.secp256k1.api.Signature; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; +import tech.pegasys.signers.secp256k1.api.Signer; +import tech.pegasys.signers.secp256k1.api.SignerProvider; import java.math.BigInteger; import java.util.Collections; @@ -70,7 +70,7 @@ static void beforeAll() { @NullSource public void ifParamIsInvalidExceptionIsThrownWithInvalidParams(final Object params) { - final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); + final SignerProvider mockSignerProvider = mock(SignerProvider.class); final EthSignTransactionResultProvider resultProvider = new EthSignTransactionResultProvider(chainId, mockSignerProvider, jsonDecoder); @@ -86,7 +86,7 @@ public void ifParamIsInvalidExceptionIsThrownWithInvalidParams(final Object para @Test public void ifAddressIsNotUnlockedExceptionIsThrownWithSigningNotUnlocked() { - final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); + final SignerProvider mockSignerProvider = mock(SignerProvider.class); final EthSignTransactionResultProvider resultProvider = new EthSignTransactionResultProvider(chainId, mockSignerProvider, jsonDecoder); @@ -101,14 +101,14 @@ public void ifAddressIsNotUnlockedExceptionIsThrownWithSigningNotUnlocked() { @Test public void signatureHasTheExpectedFormat() { - final TransactionSigner mockTransactionSigner = mock(TransactionSigner.class); - doReturn("0xf17f52151ebef6c7334fad080c5704d77216b732").when(mockTransactionSigner).getAddress(); + final Signer mockSigner = mock(Signer.class); + doReturn("0xf17f52151ebef6c7334fad080c5704d77216b732").when(mockSigner).getAddress(); final BigInteger v = BigInteger.ONE; final BigInteger r = BigInteger.TWO; final BigInteger s = BigInteger.TEN; - doReturn(new Signature(v, r, s)).when(mockTransactionSigner).sign(any(byte[].class)); - final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); - doReturn(Optional.of(mockTransactionSigner)).when(mockSignerProvider).getSigner(anyString()); + doReturn(new Signature(v, r, s)).when(mockSigner).sign(any(byte[].class)); + final SignerProvider mockSignerProvider = mock(SignerProvider.class); + doReturn(Optional.of(mockSigner)).when(mockSignerProvider).getSigner(anyString()); final EthSignTransactionResultProvider resultProvider = new EthSignTransactionResultProvider(chainId, mockSignerProvider, jsonDecoder); @@ -129,8 +129,8 @@ public void returnsExpectedSignature() { Credentials.create("0x1618fc3e47aec7e70451256e033b9edb67f4c469258d8e2fbb105552f141ae41"); String addr = cs.getAddress(); - final TransactionSigner mockTransactionSigner = mock(TransactionSigner.class); - doReturn(addr).when(mockTransactionSigner).getAddress(); + final Signer mockSigner = mock(Signer.class); + doReturn(addr).when(mockSigner).getAddress(); doAnswer( answer -> { @@ -142,10 +142,10 @@ public void returnsExpectedSignature() { new BigInteger(1, signature.getR()), new BigInteger(1, signature.getS())); }) - .when(mockTransactionSigner) + .when(mockSigner) .sign(any(byte[].class)); - final TransactionSignerProvider mockSignerProvider = mock(TransactionSignerProvider.class); - doReturn(Optional.of(mockTransactionSigner)).when(mockSignerProvider).getSigner(anyString()); + final SignerProvider mockSignerProvider = mock(SignerProvider.class); + doReturn(Optional.of(mockSigner)).when(mockSignerProvider).getSigner(anyString()); final EthSignTransactionResultProvider resultProvider = new EthSignTransactionResultProvider(chainId, mockSignerProvider, jsonDecoder); diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/signing/SingleTransactionSignerProviderTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/signing/SingleTransactionSignerProviderTest.java index 3829bf3be..0b08bb8ff 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/signing/SingleTransactionSignerProviderTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/signing/SingleTransactionSignerProviderTest.java @@ -16,8 +16,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import tech.pegasys.signers.secp256k1.api.SingleTransactionSignerProvider; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; +import tech.pegasys.signers.secp256k1.api.SingleSignerProvider; +import tech.pegasys.signers.secp256k1.api.Signer; import java.util.Collection; import java.util.Optional; @@ -26,26 +26,26 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -class SingleTransactionSignerProviderTest { +class SingleSignerProviderTest { - private TransactionSigner transactionSigner; - private SingleTransactionSignerProvider signerFactory; + private Signer Signer; + private SingleSignerProvider signerFactory; @BeforeEach void beforeEach() { - transactionSigner = mock(TransactionSigner.class); - signerFactory = new SingleTransactionSignerProvider(transactionSigner); + Signer = mock(Signer.class); + signerFactory = new SingleSignerProvider(Signer); } @Test void whenSignerIsNullFactoryCreationFails() { Assertions.assertThrows( - IllegalArgumentException.class, () -> new SingleTransactionSignerProvider(null)); + IllegalArgumentException.class, () -> new SingleSignerProvider(null)); } @Test void whenSignerAddressIsNullFactoryAvailableAddressesShouldReturnEmptySet() { - when(transactionSigner.getAddress()).thenReturn(null); + when(Signer.getAddress()).thenReturn(null); final Collection addresses = signerFactory.availableAddresses(); assertThat(addresses).isEmpty(); @@ -53,23 +53,23 @@ void whenSignerAddressIsNullFactoryAvailableAddressesShouldReturnEmptySet() { @Test void whenSignerAddressIsNullFactoryGetSignerShouldReturnEmpty() { - when(transactionSigner.getAddress()).thenReturn(null); + when(Signer.getAddress()).thenReturn(null); - final Optional signer = signerFactory.getSigner("0x0"); + final Optional signer = signerFactory.getSigner("0x0"); assertThat(signer).isEmpty(); } @Test void whenGetSignerWithMatchingAccountShouldReturnSigner() { - when(transactionSigner.getAddress()).thenReturn("0x0"); + when(Signer.getAddress()).thenReturn("0x0"); - final Optional signer = signerFactory.getSigner("0x0"); + final Optional signer = signerFactory.getSigner("0x0"); assertThat(signer).isNotEmpty(); } @Test void getSignerAddressIsCaseInsensitive() { - when(transactionSigner.getAddress()).thenReturn("0xa"); + when(Signer.getAddress()).thenReturn("0xa"); assertThat(signerFactory.getSigner("0xa")).isNotEmpty(); assertThat(signerFactory.getSigner("0xA")).isNotEmpty(); @@ -82,15 +82,15 @@ void whenGetSignerWithNullAddressShouldReturnEmpty() { @Test void whenGetSignerWithDifferentSignerAccountShouldReturnEmpty() { - when(transactionSigner.getAddress()).thenReturn("0x0"); + when(Signer.getAddress()).thenReturn("0x0"); - final Optional signer = signerFactory.getSigner("0x1"); + final Optional signer = signerFactory.getSigner("0x1"); assertThat(signer).isEmpty(); } @Test void whenGetAvailableAddressesShouldReturnSignerAddress() { - when(transactionSigner.getAddress()).thenReturn("0x0"); + when(Signer.getAddress()).thenReturn("0x0"); final Collection addresses = signerFactory.availableAddresses(); assertThat(addresses).containsExactly("0x0"); diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/AzureSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/AzureSubCommand.java index 8a4809df3..605d5ff58 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/AzureSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/AzureSubCommand.java @@ -15,14 +15,14 @@ import static tech.pegasys.ethsigner.DefaultCommandValues.MANDATORY_PATH_FORMAT_HELP; import tech.pegasys.ethsigner.SignerSubCommand; -import tech.pegasys.signers.secp256k1.api.SingleTransactionSignerProvider; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; +import tech.pegasys.signers.secp256k1.api.SingleSignerProvider; +import tech.pegasys.signers.secp256k1.api.Signer; +import tech.pegasys.signers.secp256k1.api.SignerProvider; import tech.pegasys.signers.secp256k1.azure.AzureConfig; import tech.pegasys.signers.secp256k1.azure.AzureKeyVaultAuthenticator; -import tech.pegasys.signers.secp256k1.azure.AzureKeyVaultTransactionSignerFactory; +import tech.pegasys.signers.secp256k1.azure.AzureKeyVaultSignerFactory; import tech.pegasys.signers.secp256k1.common.PasswordFileUtil; -import tech.pegasys.signers.secp256k1.common.TransactionSignerInitializationException; +import tech.pegasys.signers.secp256k1.common.SignerInitializationException; import java.io.FileNotFoundException; import java.io.IOException; @@ -77,29 +77,29 @@ public class AzureSubCommand extends SignerSubCommand { private static final String READ_SECRET_FILE_ERROR = "Error when reading the secret from file."; public static final String COMMAND_NAME = "azure-signer"; - private TransactionSigner createSigner() throws TransactionSignerInitializationException { + private Signer createSigner() throws SignerInitializationException { final String clientSecret; try { clientSecret = PasswordFileUtil.readPasswordFromFile(clientSecretPath); } catch (final FileNotFoundException fnfe) { - throw new TransactionSignerInitializationException("File not found: " + clientSecretPath); + throw new SignerInitializationException("File not found: " + clientSecretPath); } catch (final IOException e) { - throw new TransactionSignerInitializationException(READ_SECRET_FILE_ERROR, e); + throw new SignerInitializationException(READ_SECRET_FILE_ERROR, e); } final AzureConfig config = - new AzureConfig(keyVaultName, keyName, keyVersion, clientId, clientSecret); + new AzureConfig(keyVaultName, keyName, keyVersion, clientId, clientSecret, tenantId); - final AzureKeyVaultTransactionSignerFactory factory = - new AzureKeyVaultTransactionSignerFactory(new AzureKeyVaultAuthenticator()); + final AzureKeyVaultSignerFactory factory = + new AzureKeyVaultSignerFactory(new AzureKeyVaultAuthenticator()); return factory.createSigner(config); } @Override - public TransactionSignerProvider createSignerFactory() - throws TransactionSignerInitializationException { - return new SingleTransactionSignerProvider(createSigner()); + public SignerProvider createSignerFactory() + throws SignerInitializationException { + return new SingleSignerProvider(createSigner()); } @Override diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommand.java index 24d647933..65e4edf22 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommand.java @@ -13,12 +13,14 @@ package tech.pegasys.ethsigner.subcommands; import tech.pegasys.ethsigner.SignerSubCommand; +import tech.pegasys.signers.cavium.CaviumConfig; import tech.pegasys.signers.cavium.CaviumKeyStoreProvider; -import tech.pegasys.signers.secp256k1.api.SingleTransactionSignerProvider; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; +import tech.pegasys.signers.hsm.HSMConfig; +import tech.pegasys.signers.secp256k1.api.SingleSignerProvider; +import tech.pegasys.signers.secp256k1.api.Signer; +import tech.pegasys.signers.secp256k1.api.SignerProvider; import tech.pegasys.signers.secp256k1.cavium.CaviumKeyStoreSignerFactory; -import tech.pegasys.signers.secp256k1.common.TransactionSignerInitializationException; +import tech.pegasys.signers.secp256k1.common.SignerInitializationException; import java.nio.file.Path; @@ -65,17 +67,17 @@ public CaviumSubCommand() {} required = true) private String ethAddress; - private TransactionSigner createSigner() throws TransactionSignerInitializationException { + private Signer createSigner() throws SignerInitializationException { final CaviumKeyStoreProvider provider = - new CaviumKeyStoreProvider(libraryPath.toString(), slotPin); + new CaviumKeyStoreProvider(new CaviumConfig(libraryPath != null ? libraryPath.toString() : null, slotPin)); final CaviumKeyStoreSignerFactory factory = new CaviumKeyStoreSignerFactory(provider); return factory.createSigner(ethAddress); } @Override - public TransactionSignerProvider createSignerFactory() - throws TransactionSignerInitializationException { - return new SingleTransactionSignerProvider(createSigner()); + public SignerProvider createSignerFactory() + throws SignerInitializationException { + return new SingleSignerProvider(createSigner()); } @Override diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommand.java index d4af9e0eb..9315cad8a 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommand.java @@ -15,10 +15,10 @@ import static tech.pegasys.ethsigner.DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP; import tech.pegasys.ethsigner.SignerSubCommand; -import tech.pegasys.signers.secp256k1.api.SingleTransactionSignerProvider; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; -import tech.pegasys.signers.secp256k1.common.TransactionSignerInitializationException; +import tech.pegasys.signers.secp256k1.api.SingleSignerProvider; +import tech.pegasys.signers.secp256k1.api.Signer; +import tech.pegasys.signers.secp256k1.api.SignerProvider; +import tech.pegasys.signers.secp256k1.common.SignerInitializationException; import tech.pegasys.signers.secp256k1.filebased.FileBasedSignerFactory; import java.nio.file.Path; @@ -61,14 +61,14 @@ public FileBasedSubCommand() {} arity = "1") private Path keyFilePath; - private TransactionSigner createSigner() throws TransactionSignerInitializationException { + private Signer createSigner() throws SignerInitializationException { return FileBasedSignerFactory.createSigner(keyFilePath, passwordFilePath); } @Override - public TransactionSignerProvider createSignerFactory() - throws TransactionSignerInitializationException { - return new SingleTransactionSignerProvider(createSigner()); + public SignerProvider createSignerFactory() + throws SignerInitializationException { + return new SingleSignerProvider(createSigner()); } @Override diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HSMSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HSMSubCommand.java index 057e9e3f1..20b3445a7 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HSMSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HSMSubCommand.java @@ -13,11 +13,12 @@ package tech.pegasys.ethsigner.subcommands; import tech.pegasys.ethsigner.SignerSubCommand; -import tech.pegasys.signers.secp256k1.api.SingleTransactionSignerProvider; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; -import tech.pegasys.signers.secp256k1.common.TransactionSignerInitializationException; -import tech.pegasys.signers.secp256k1.hsm.HSMTransactionSignerFactory; +import tech.pegasys.signers.hsm.HSMConfig; +import tech.pegasys.signers.secp256k1.api.SingleSignerProvider; +import tech.pegasys.signers.secp256k1.api.Signer; +import tech.pegasys.signers.secp256k1.api.SignerProvider; +import tech.pegasys.signers.secp256k1.common.SignerInitializationException; +import tech.pegasys.signers.secp256k1.hsm.HSMSignerFactory; import java.nio.file.Path; @@ -71,17 +72,16 @@ public HSMSubCommand() {} required = true) private String ethAddress; - private TransactionSigner createSigner() throws TransactionSignerInitializationException { - HSMTransactionSignerFactory factory = - new HSMTransactionSignerFactory( - libraryPath != null ? libraryPath.toString() : null, slotLabel, slotPin); + private Signer createSigner() throws SignerInitializationException { + HSMSignerFactory factory = + new HSMSignerFactory(new HSMConfig(libraryPath != null ? libraryPath.toString() : null, slotLabel, slotPin)); return factory.createSigner(ethAddress); } @Override - public TransactionSignerProvider createSignerFactory() - throws TransactionSignerInitializationException { - return new SingleTransactionSignerProvider(createSigner()); + public SignerProvider createSignerFactory() + throws SignerInitializationException { + return new SingleSignerProvider(createSigner()); } @Override diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommand.java index b776a2f18..7b69072a0 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommand.java @@ -23,10 +23,10 @@ import tech.pegasys.signers.hashicorp.config.HashicorpKeyConfig; import tech.pegasys.signers.hashicorp.config.KeyDefinition; import tech.pegasys.signers.hashicorp.config.TlsOptions; -import tech.pegasys.signers.secp256k1.api.SingleTransactionSignerProvider; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; -import tech.pegasys.signers.secp256k1.common.TransactionSignerInitializationException; +import tech.pegasys.signers.secp256k1.api.SingleSignerProvider; +import tech.pegasys.signers.secp256k1.api.Signer; +import tech.pegasys.signers.secp256k1.api.SignerProvider; +import tech.pegasys.signers.secp256k1.common.SignerInitializationException; import tech.pegasys.signers.secp256k1.hashicorp.HashicorpSignerFactory; import java.io.IOException; @@ -113,7 +113,7 @@ public class HashicorpSubCommand extends SignerSubCommand { arity = "1") private Path tlsKnownServerFile = null; - private TransactionSigner createSigner() throws TransactionSignerInitializationException { + private Signer createSigner() throws SignerInitializationException { final HashicorpKeyConfig keyConfig = new HashicorpKeyConfig( @@ -146,9 +146,9 @@ private TlsOptions generateTlsOptions() { } @Override - public TransactionSignerProvider createSignerFactory() - throws TransactionSignerInitializationException { - return new SingleTransactionSignerProvider(createSigner()); + public SignerProvider createSignerFactory() + throws SignerInitializationException { + return new SingleSignerProvider(createSigner()); } @Override @@ -170,10 +170,10 @@ private String readTokenFromFile(final Path path) { .findFirst() .orElseThrow( () -> - new TransactionSignerInitializationException( + new SignerInitializationException( String.format(AUTH_FILE_ERROR_MSG_FMT, path.toString()))); } catch (final IOException e) { - throw new TransactionSignerInitializationException( + throw new SignerInitializationException( String.format(AUTH_FILE_ERROR_MSG_FMT, path.toString())); } } diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java index 98b66251d..cf4695393 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java @@ -15,9 +15,9 @@ import static tech.pegasys.ethsigner.DefaultCommandValues.MANDATORY_PATH_FORMAT_HELP; import tech.pegasys.ethsigner.SignerSubCommand; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; -import tech.pegasys.signers.secp256k1.common.TransactionSignerInitializationException; -import tech.pegasys.signers.secp256k1.multikey.MultiKeyTransactionSignerProvider; +import tech.pegasys.signers.secp256k1.api.SignerProvider; +import tech.pegasys.signers.secp256k1.common.SignerInitializationException; +import tech.pegasys.signers.secp256k1.multikey.MultiKeySignerProvider; import java.nio.file.Path; @@ -86,10 +86,10 @@ public MultiKeySubCommand() {} // private String slotPin; @Override - public TransactionSignerProvider createSignerFactory() - throws TransactionSignerInitializationException { - return MultiKeyTransactionSignerProvider.create( - directoryPath, configPath); + public SignerProvider createSignerFactory() + throws SignerInitializationException { + return MultiKeySignerProvider.create( + directoryPath, configPath, null); } @Override diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java index 9fa007155..efa1039eb 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java @@ -15,11 +15,11 @@ import static tech.pegasys.ethsigner.DefaultCommandValues.MANDATORY_HOST_FORMAT_HELP; import tech.pegasys.ethsigner.SignerSubCommand; -import tech.pegasys.signers.secp256k1.api.SingleTransactionSignerProvider; -import tech.pegasys.signers.secp256k1.api.TransactionSigner; -import tech.pegasys.signers.secp256k1.api.TransactionSignerProvider; -import tech.pegasys.signers.secp256k1.common.TransactionSignerInitializationException; -import tech.pegasys.signers.secp256k1.filebased.CredentialTransactionSigner; +import tech.pegasys.signers.secp256k1.api.SingleSignerProvider; +import tech.pegasys.signers.secp256k1.api.Signer; +import tech.pegasys.signers.secp256k1.api.SignerProvider; +import tech.pegasys.signers.secp256k1.common.SignerInitializationException; +import tech.pegasys.signers.secp256k1.filebased.CredentialSigner; import com.google.common.base.MoreObjects; import org.web3j.crypto.Credentials; @@ -45,15 +45,15 @@ public class RawSubCommand extends SignerSubCommand { arity = "1") private String privateKey; - private TransactionSigner createSigner() throws TransactionSignerInitializationException { + private Signer createSigner() throws SignerInitializationException { final Credentials credentials = Credentials.create(privateKey); - return new CredentialTransactionSigner(credentials); + return new CredentialSigner(credentials); } @Override - public TransactionSignerProvider createSignerFactory() - throws TransactionSignerInitializationException { - return new SingleTransactionSignerProvider(createSigner()); + public SignerProvider createSignerFactory() + throws SignerInitializationException { + return new SingleSignerProvider(createSigner()); } @Override diff --git a/gradle/versions.gradle b/gradle/versions.gradle index aa0c61869..020725fc8 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -74,7 +74,7 @@ dependencyManagement { dependency 'org.web3j:core:4.5.14' dependency 'org.web3j:crypto:4.5.14' - dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.3-ADHARA-SNAPSHOT') { + dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.4-ADHARA-SNAPSHOT') { entry 'keystorage-hashicorp' entry 'keystorage-hsm' entry 'keystorage-cavium' From f612c1430fab675de306722e751594b2ddf90cf2 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Wed, 19 Aug 2020 17:04:35 +0200 Subject: [PATCH 23/64] Changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3da832a9c..b6cafa98d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.7.1-ADHARA + +### Features Added +- Added "eth_signTransaction" JSON RPC +- Added AWS CloudHSM signer +- Added Generic PKCS11 signer + ## 0.7.0 ### Features Added From 866af3220edceead86f7a7cf1c641001e761d949 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Thu, 20 Aug 2020 11:34:57 +0200 Subject: [PATCH 24/64] Cleanup --- .../subcommands/MultiKeySubCommand.java | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java index 0d123512c..1fed235d3 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java @@ -68,27 +68,6 @@ public MultiKeySubCommand() {} required = false) private Path configPath; - // @Option( - // names = {"-l", "--library"}, - // description = "The HSM PKCS11 library used to sign transactions.", - // paramLabel = "", - // required = false) - // private Path libraryPath; - // - // @Option( - // names = {"-s", "--slot-label"}, - // description = "The HSM slot used to sign transactions.", - // paramLabel = "", - // required = false) - // private String slotLabel; - // - // @Option( - // names = {"-p", "--slot-pin"}, - // description = "The crypto user pin of the HSM slot used to sign transactions.", - // paramLabel = "", - // required = false) - // private String slotPin; - @Override protected void validateArgs() throws InitializationException { checkIfRequiredOptionsAreInitialized(this); From 833f6ef2d6faf1739e8995e9be0c02bcf755607f Mon Sep 17 00:00:00 2001 From: Joshua Fernandes Date: Mon, 7 Sep 2020 08:32:22 +1000 Subject: [PATCH 25/64] update docker image (#326) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 16f5be1c3..dbba50e7c 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,5 @@ -FROM openjdk:11.0.2-jre-slim-stretch +FROM openjdk:11.0.8-jre-slim-buster COPY ethsigner /opt/ethsigner/ WORKDIR /opt/ethsigner From 50d119ef522b5670fc5f8a48ffb0ee84391649d4 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Tue, 8 Sep 2020 19:10:38 +0200 Subject: [PATCH 26/64] Debug logging --- .../internalresponse/EthSignTransactionResultProvider.java | 3 +-- .../requesthandler/sendtransaction/SendTransactionHandler.java | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java index 5fae19d47..395d449fb 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java @@ -56,8 +56,6 @@ public EthSignTransactionResultProvider( @Override public String createResponseResult(final JsonRpcRequest request) { - // Signs a transaction that can be submitted to the network at a later time using with - // eth_sendRawTransaction. LOG.debug("Transforming request {}, {}", request.getId(), request.getMethod()); final Transaction transaction; try { @@ -71,6 +69,7 @@ public String createResponseResult(final JsonRpcRequest request) { throw new JsonRpcException(INVALID_PARAMS); } + LOG.debug("Obtaining signer for {}", transaction.sender()); final Optional Signer = signerProvider.getSigner(transaction.sender()); if (Signer.isEmpty()) { LOG.info("From address ({}) does not match any available account", transaction.sender()); diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java index 41ef32cd7..495112933 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java @@ -71,6 +71,7 @@ public void handle(final RoutingContext context, final JsonRpcRequest request) { return; } + LOG.debug("Obtaining signer for {}", transaction.sender()); final Optional signer = signerProvider.getSigner(transaction.sender()); if (signer.isEmpty()) { From 2eb9aa5b55dea210876f1970a684d04d56090d37 Mon Sep 17 00:00:00 2001 From: Tim Beiko Date: Tue, 8 Sep 2020 10:26:15 -0700 Subject: [PATCH 27/64] Update links, emails and PegaSys references (#325) Signed-off-by: Tim Beiko --- CLA.md | 8 ++++---- CODE-OF-CONDUCT.md | 4 ++-- CODING-CONVENTIONS.md | 2 +- CONTRIBUTING.md | 4 ++-- GOVERNANCE.md | 2 +- README.md | 5 ++++- community/community-membership.md | 24 +++++++++++------------- 7 files changed, 25 insertions(+), 24 deletions(-) diff --git a/CLA.md b/CLA.md index 806961db7..3754207ad 100644 --- a/CLA.md +++ b/CLA.md @@ -1,10 +1,10 @@ Sign the CLA ============= -This page is the step-by-step guide to signing the Consensys AG -Individual Contributor License Agreement. +This page is the step-by-step guide to signing the Consensys Software Inc. +Individual Contributor License Agreement. -1. First and foremost, read [the current version of the CLA]. +1. First and foremost, read [the current version of the CLA]. It is written to be as close to plain English as possible. 2. Make an account on [GitHub] if you don't already have one. @@ -13,7 +13,7 @@ Individual Contributor License Agreement. pre-requisite requiring to you read and sign the CLA. If you have any questions, you can reach us on [Discord]. - + [GitHub]: https://github.com/ [the current version of the CLA]: https://gist.github.com/rojotek/978b48a5e8b68836856a8961d6887992 [Discord]: https://discord.gg/5U9Jwp7 diff --git a/CODE-OF-CONDUCT.md b/CODE-OF-CONDUCT.md index dfd1c60cc..5308b43ca 100644 --- a/CODE-OF-CONDUCT.md +++ b/CODE-OF-CONDUCT.md @@ -55,7 +55,7 @@ further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at [private@pegasys.tech]. All +reported by contacting the project team at [private-quorum@consensys.net]. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. @@ -71,4 +71,4 @@ This Code of Conduct is adapted from the [Contributor Covenant], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [Contributor Covenant]: https://www.contributor-covenant.org -[private@pegasys.tech]: mailto:private@pegasys.tech \ No newline at end of file +[private-quorum@consensys.net]: mailto:private-quorum@consensys.net \ No newline at end of file diff --git a/CODING-CONVENTIONS.md b/CODING-CONVENTIONS.md index 2a58509d2..dfb2e4a57 100644 --- a/CODING-CONVENTIONS.md +++ b/CODING-CONVENTIONS.md @@ -45,7 +45,7 @@ EthSigner embraces typical Java idioms including using an Object Oriented approa - Don't pass lambdas into executors because it makes it harder to identify the threading interactions. The lambda makes the code shorter but not clearer. Instead use a separate class or extract a method. * For good examples, refer to the APIs the JDK itself exposes. ->**Note** If you're not sure what idiomatic Java looks like, start by following the typical patterns and naming used in this or other PegaSys codebases. +>**Note** If you're not sure what idiomatic Java looks like, start by following the typical patterns and naming used in this or other ConsenSys codebases. ## 2.3 You Ain't Gonna Need It (YAGNI) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d10bf36cf..be3b71524 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,7 +27,7 @@ and feel free to propose changes to this document in a pull request. ## Code of Conduct This project and everyone participating in it is governed by the [EthSigner Code of Conduct](CODE-OF-CONDUCT.md). -By participating, you are expected to uphold this code. Please report unacceptable behavior to [private@pegasys.tech]. +By participating, you are expected to uphold this code. Please report unacceptable behavior to [private-quorum@consensys.net]. ## I just have a quick question @@ -242,7 +242,7 @@ These are not strictly enforced during the build, but should be adhered to and c | [`requires-changes`][search-label-requires-changes] | Pull requests which need to be updated based on review comments and then reviewed again. | | [`needs engineering approval`][search-label-needs-engineering-approval] | Pull requests which need to be approved from a technical person, mainly documentation PRs. | -[private@pegasys.tech]: mailto:private@pegasys.tech +[private-quorum@consensys.net]: mailto:private-quorum@consensys.net [Discord]: https://discord.gg/5U9Jwp7 [Github issues]: https://github.com/PegaSysEng/ethsigner/issues [EthSigner documentation]: https://docs.ethsigner.pegasys.tech/ diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 2206efdee..d0693e382 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -1,5 +1,5 @@ # Overview -This project is led by a benevolent dictator (PegaSys) and managed by the community. That is, the +This project is led by a benevolent dictator (ConsenSys) and managed by the community. That is, the community actively contributes to the day-to-day maintenance of the project, but the general strategic line is drawn by the benevolent dictator. In case of disagreement, they have the last word. It is the benevolent dictator’s job to resolve disputes within the community and to ensure that the diff --git a/README.md b/README.md index 089d6263e..6fd25f139 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # EthSigner -A transaction signing application to be used with a web3 provider. All questions, queries and other discussion can be found on [Discord]. +A transaction signing application to be used with a web3 provider. All questions, queries and other discussion can be found on [Discord]. ## Issues @@ -11,6 +11,9 @@ See our [contribution guidelines](CONTRIBUTING.md) for more detail on searching ## Users * [User documentation](https://docs.ethsigner.pegasys.tech/) +## Chat +* [Discord] + ## Developers * [Contribution Guidelines](CONTRIBUTING.md) * [Coding Conventions](CODING-CONVENTIONS.md) diff --git a/community/community-membership.md b/community/community-membership.md index 93607ac86..c4f2f8169 100644 --- a/community/community-membership.md +++ b/community/community-membership.md @@ -11,11 +11,11 @@ this project. | Everyone | none | anybody with a belly button | Member | everyone who contributes - code or otherwise | EthSigner GitHub org member | Approver | approve accepting contributions | write permissions on master -| Project Manager | management of the project | PegaSys -| Project Sponsor | contribute developer resources | PegaSys -| Open Source Circle | OSS support | PegaSys -| Project Evangelist | promote the project | PegaSys -| Benevolent Dictator | decision tie-breaker | PegaSys +| Project Manager | management of the project | ConsenSys +| Project Sponsor | contribute developer resources | ConsenSys +| Open Source Circle | OSS support | ConsenSys +| Project Evangelist | promote the project | ConsenSys +| Benevolent Dictator | decision tie-breaker | ConsenSys ## Everyone Any person from the public is able to access the code. The standard permissions grant the ability to view the code, view open bugs, access the wiki, download binaries, view CI results and comment on pull requests. @@ -46,7 +46,6 @@ issues and PRs assigned to them. - Authoring or reviewing PRs on GitHub - Filing or commenting on issues on GitHub - Contributing to community discussions (e.g. meetings, Slack, email discussion forums, Stack Overflow) -- Subscribed to [ethsigner-dev@pegasys.tech] - Joined [EthSigner Discord] - Have read the [contributor guide] - Signed ICLA, as described in [CLA.md] @@ -88,7 +87,7 @@ Code approvers are members that have signed an ICLA and have been granted additi ## Project Manager The Project Manager role provides the user with control over management aspects of the project. -**Defined by:** PegaSys +**Defined by:** ConsenSys ### Requirements - Includes all of the requirements of a Member user @@ -105,7 +104,7 @@ The Project Manager role provides the user with control over management aspects ## Project Sponsor The Project Sponsor role provides a user with the ability to contribute additional developer resources to the project. Project Sponsors must sign the ICLA. -**Defined by:** PegaSys +**Defined by:** ConsenSys ### Requirements - Signed ICLA, as described in [CLA.md] @@ -117,7 +116,7 @@ The Project Sponsor role provides a user with the ability to contribute addition ## Open Source Circle The Open Source Circle is a group that provides open source software support to projects. -**Defined by:** PegaSys +**Defined by:** ConsenSys ### Requirements - Includes all of the requirements of a Member user @@ -132,7 +131,7 @@ The Open Source Circle is a group that provides open source software support to ## Project Evangelist The Project Evangelist role is for those who wish to promote the project to the outside world, but not actively contribute to it. -**Defined by:** PegaSys +**Defined by:** ConsenSys ### Requirements - Includes all of the requirements of a Member user @@ -158,8 +157,8 @@ The benevolent dictator, or project lead, is self-appointed. However, because th ## Attribution This document is adapted from the following sources: -- Kubernetes community-membership.md, available at [kub community membership]. -- OSSWatch Benevolent Dictator Governance Model, available at [oss watch benevolent dictator]. +- Kubernetes community-membership.md, available at [kub community membership]. +- OSSWatch Benevolent Dictator Governance Model, available at [oss watch benevolent dictator]. [CLA.md]: /CLA.md [oss watch benevolent dictator]: http://oss-watch.ac.uk/resources/benevolentdictatorgovernancemodel @@ -168,6 +167,5 @@ This document is adapted from the following sources: [contributor guide]: /CONTRIBUTING.md [New contributors]: /CONTRIBUTING.md [two-factor authentication]: https://help.github.com/articles/about-two-factor-authentication -[ethsigner-dev@pegasys.tech]: mailto:ethsigner-dev@pegasys.tech [EthSigner Discord]: https://discord.gg/5U9Jwp7 [EthSigner Documentation]: /docs From 03008cca6e06a851478d6d934d279e10b740f65d Mon Sep 17 00:00:00 2001 From: mkrielza Date: Thu, 10 Sep 2020 12:38:41 +0200 Subject: [PATCH 28/64] Upgrade signers library version --- gradle/versions.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/versions.gradle b/gradle/versions.gradle index 6e1c20b52..626f7fe40 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -70,7 +70,7 @@ dependencyManagement { dependency 'org.web3j:core:4.5.14' dependency 'org.web3j:crypto:4.5.14' - dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.4-ADHARA-SNAPSHOT') { + dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.5-ADHARA-SNAPSHOT') { entry 'keystorage-hashicorp' entry 'keystorage-hsm' entry 'keystorage-cavium' From d9c3db97639de0c6007857de7277b9f3fb21f476 Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Fri, 11 Sep 2020 11:02:32 +1000 Subject: [PATCH 29/64] metrics endpoint (#328) --- ethsigner/commandline/build.gradle | 2 + .../ethsigner/EthSignerBaseCommand.java | 80 +++++++++++++++++ .../config/AllowListHostsProperty.java | 86 +++++++++++++++++++ .../convertor/MetricCategoryConverter.java | 39 +++++++++ ethsigner/core/build.gradle | 2 + .../jsonrpcproxy/IntegrationTestBase.java | 5 +- .../pegasys/ethsigner/core/EthSigner.java | 12 ++- .../tech/pegasys/ethsigner/core/Runner.java | 13 ++- .../pegasys/ethsigner/core/config/Config.java | 13 +++ .../core/metrics/EthSignerMetricCategory.java | 52 +++++++++++ .../core/metrics/MetricsEndpoint.java | 83 ++++++++++++++++++ gradle/versions.gradle | 3 + 12 files changed, 385 insertions(+), 5 deletions(-) create mode 100644 ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/config/AllowListHostsProperty.java create mode 100644 ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/convertor/MetricCategoryConverter.java create mode 100644 ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/metrics/EthSignerMetricCategory.java create mode 100644 ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/metrics/MetricsEndpoint.java diff --git a/ethsigner/commandline/build.gradle b/ethsigner/commandline/build.gradle index 13becc29e..0e396446c 100644 --- a/ethsigner/commandline/build.gradle +++ b/ethsigner/commandline/build.gradle @@ -21,6 +21,8 @@ dependencies { implementation 'org.apache.logging.log4j:log4j-api' implementation 'org.apache.logging.log4j:log4j-core' implementation 'org.apache.tuweni:tuweni-toml' + implementation 'org.hyperledger.besu:plugin-api' + implementation 'org.hyperledger.besu.internal:metrics-core' runtimeOnly 'org.apache.logging.log4j:log4j-slf4j-impl' diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/EthSignerBaseCommand.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/EthSignerBaseCommand.java index d1fb9562c..3765afd60 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/EthSignerBaseCommand.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/EthSignerBaseCommand.java @@ -16,31 +16,40 @@ import static tech.pegasys.ethsigner.DefaultCommandValues.LONG_FORMAT_HELP; import static tech.pegasys.ethsigner.DefaultCommandValues.PATH_FORMAT_HELP; import static tech.pegasys.ethsigner.DefaultCommandValues.PORT_FORMAT_HELP; +import static tech.pegasys.ethsigner.core.metrics.EthSignerMetricCategory.DEFAULT_METRIC_CATEGORIES; import static tech.pegasys.ethsigner.util.RequiredOptionsUtil.checkIfRequiredOptionsAreInitialized; import tech.pegasys.ethsigner.annotations.RequiredOption; +import tech.pegasys.ethsigner.config.AllowListHostsProperty; import tech.pegasys.ethsigner.config.ConfigFileOption; import tech.pegasys.ethsigner.config.InvalidCommandLineOptionsException; import tech.pegasys.ethsigner.config.PicoCliTlsServerOptions; import tech.pegasys.ethsigner.config.tls.client.PicoCliClientTlsOptions; +import tech.pegasys.ethsigner.convertor.MetricCategoryConverter; import tech.pegasys.ethsigner.core.CorsAllowedOriginsProperty; import tech.pegasys.ethsigner.core.config.Config; import tech.pegasys.ethsigner.core.config.TlsOptions; import tech.pegasys.ethsigner.core.config.tls.client.ClientTlsOptions; +import tech.pegasys.ethsigner.core.metrics.EthSignerMetricCategory; import tech.pegasys.ethsigner.core.signing.ChainIdProvider; import tech.pegasys.ethsigner.core.signing.ConfigurationChainId; import tech.pegasys.ethsigner.util.PicoCliClientTlsOptionValidator; import tech.pegasys.ethsigner.util.PicoCliTlsServerOptionsValidator; +import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Path; import java.time.Duration; import java.util.Collection; +import java.util.List; import java.util.Optional; +import java.util.Set; import com.google.common.base.MoreObjects; import org.apache.logging.log4j.Level; +import org.hyperledger.besu.metrics.StandardMetricCategory; +import org.hyperledger.besu.plugin.services.metrics.MetricCategory; import picocli.CommandLine.Command; import picocli.CommandLine.HelpCommand; import picocli.CommandLine.Mixin; @@ -172,6 +181,44 @@ public void setDownstreamHttpPath(final String path) { @Mixin private PicoCliClientTlsOptions clientTlsOptions; + @Option( + names = {"--metrics-enabled"}, + description = "Set to start the metrics exporter (default: ${DEFAULT-VALUE})") + private final Boolean metricsEnabled = false; + + @SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings. + @Option( + names = {"--metrics-host"}, + paramLabel = HOST_FORMAT_HELP, + description = "Host for the metrics exporter to listen on (default: ${DEFAULT-VALUE})", + arity = "1") + private String metricsHost = InetAddress.getLoopbackAddress().getHostAddress(); + + @Option( + names = {"--metrics-port"}, + paramLabel = PORT_FORMAT_HELP, + description = "Port for the metrics exporter to listen on (default: ${DEFAULT-VALUE})", + arity = "1") + private final Integer metricsPort = 8546; + + @Option( + names = {"--metrics-category", "--metrics-categories"}, + paramLabel = "", + split = ",", + arity = "1..*", + description = + "Comma separated list of categories to track metrics for (default: ${DEFAULT-VALUE}),", + converter = Web3signerMetricCategoryConverter.class) + private final Set metricCategories = DEFAULT_METRIC_CATEGORIES; + + @Option( + names = {"--metrics-host-allowlist"}, + paramLabel = "[,...]... or * or all", + description = + "Comma separated list of hostnames to allow for metrics access, or * to accept any host (default: ${DEFAULT-VALUE})", + defaultValue = "localhost,127.0.0.1") + private final AllowListHostsProperty metricsHostAllowList = new AllowListHostsProperty(); + @Override public Level getLogLevel() { return logLevel; @@ -234,6 +281,31 @@ public Collection getCorsAllowedOrigins() { return rpcHttpCorsAllowedOrigins; } + @Override + public Boolean isMetricsEnabled() { + return metricsEnabled; + } + + @Override + public Integer getMetricsPort() { + return metricsPort; + } + + @Override + public String getMetricsHost() { + return metricsHost; + } + + @Override + public Set getMetricCategories() { + return metricCategories; + } + + @Override + public List getMetricsHostAllowList() { + return metricsHostAllowList; + } + @Override public void run() { // validation is performed to simulate similar behavior as with ArgGroups. @@ -282,4 +354,12 @@ void validateArgs() { throw new InvalidCommandLineOptionsException(errorMessage.trim()); } } + + public static class Web3signerMetricCategoryConverter extends MetricCategoryConverter { + + public Web3signerMetricCategoryConverter() { + addCategories(EthSignerMetricCategory.class); + addCategories(StandardMetricCategory.class); + } + } } diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/config/AllowListHostsProperty.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/config/AllowListHostsProperty.java new file mode 100644 index 000000000..7e9759427 --- /dev/null +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/config/AllowListHostsProperty.java @@ -0,0 +1,86 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.config; + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nonnull; + +import com.google.common.base.Splitter; +import com.google.common.base.Strings; + +public class AllowListHostsProperty extends AbstractList { + + private final List hostnamesAllowlist = new ArrayList<>(); + + public AllowListHostsProperty() {} + + @Override + @Nonnull + public Iterator iterator() { + if (hostnamesAllowlist.size() == 1 && hostnamesAllowlist.get(0).equals("none")) { + return Collections.emptyIterator(); + } else { + return hostnamesAllowlist.iterator(); + } + } + + @Override + public int size() { + return hostnamesAllowlist.size(); + } + + @Override + public boolean add(final String string) { + return addAll(Collections.singleton(string)); + } + + @Override + public String get(final int index) { + return hostnamesAllowlist.get(index); + } + + @Override + public boolean addAll(final Collection collection) { + final int initialSize = hostnamesAllowlist.size(); + for (final String string : collection) { + if (Strings.isNullOrEmpty(string)) { + throw new IllegalArgumentException("Hostname cannot be empty string or null string."); + } + for (final String s : Splitter.onPattern("\\s*,+\\s*").split(string)) { + if ("all".equals(s)) { + hostnamesAllowlist.add("*"); + } else { + hostnamesAllowlist.add(s); + } + } + } + + if (hostnamesAllowlist.contains("none")) { + if (hostnamesAllowlist.size() > 1) { + throw new IllegalArgumentException("Value 'none' can't be used with other hostnames"); + } + } else if (hostnamesAllowlist.contains("*")) { + if (hostnamesAllowlist.size() > 1) { + throw new IllegalArgumentException( + "Values '*' or 'all' can't be used with other hostnames"); + } + } + + return hostnamesAllowlist.size() != initialSize; + } +} diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/convertor/MetricCategoryConverter.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/convertor/MetricCategoryConverter.java new file mode 100644 index 000000000..623c808ee --- /dev/null +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/convertor/MetricCategoryConverter.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.convertor; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import org.hyperledger.besu.plugin.services.metrics.MetricCategory; +import picocli.CommandLine; + +public class MetricCategoryConverter implements CommandLine.ITypeConverter { + + private final Map metricCategories = new HashMap<>(); + + @Override + public MetricCategory convert(final String value) { + final MetricCategory category = metricCategories.get(value); + if (category == null) { + throw new IllegalArgumentException("Unknown category: " + value); + } + return category; + } + + public & MetricCategory> void addCategories(final Class categoryEnum) { + EnumSet.allOf(categoryEnum) + .forEach(category -> metricCategories.put(category.name(), category)); + } +} diff --git a/ethsigner/core/build.gradle b/ethsigner/core/build.gradle index 81f640393..729b0a951 100644 --- a/ethsigner/core/build.gradle +++ b/ethsigner/core/build.gradle @@ -37,6 +37,8 @@ dependencies { implementation 'io.vertx:vertx-web' implementation 'io.vertx:vertx-web-client' implementation 'org.apache.tuweni:tuweni-net' + implementation 'org.hyperledger.besu.internal:metrics-core' + implementation 'org.hyperledger.besu:plugin-api' runtimeOnly 'org.apache.logging.log4j:log4j-core' runtimeOnly 'org.apache.logging.log4j:log4j-slf4j-impl' diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IntegrationTestBase.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IntegrationTestBase.java index a3e265746..bbae84f8c 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IntegrationTestBase.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IntegrationTestBase.java @@ -15,6 +15,7 @@ import static io.restassured.RestAssured.given; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockserver.integration.ClientAndServer.startClientAndServer; @@ -27,6 +28,7 @@ import tech.pegasys.ethsigner.core.AddressIndexedSignerProvider; import tech.pegasys.ethsigner.core.Runner; import tech.pegasys.ethsigner.core.jsonrpc.JsonDecoder; +import tech.pegasys.ethsigner.core.metrics.MetricsEndpoint; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.DownstreamPathCalculator; import tech.pegasys.ethsigner.jsonrpcproxy.model.request.EthNodeRequest; import tech.pegasys.ethsigner.jsonrpcproxy.model.request.EthRequestFactory; @@ -147,7 +149,8 @@ static void setupEthSigner(final long chainId, final String downstreamHttpReques jsonDecoder, dataPath, vertx, - singletonList("sample.com")); + singletonList("sample.com"), + new MetricsEndpoint(false, 0, "", emptySet(), emptyList())); runner.start(); final Path portsFile = dataPath.resolve(PORTS_FILENAME); diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java index a77f63bf5..4c9f5c585 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java @@ -16,6 +16,7 @@ import tech.pegasys.ethsigner.core.config.Config; import tech.pegasys.ethsigner.core.config.TlsOptions; import tech.pegasys.ethsigner.core.jsonrpc.JsonDecoder; +import tech.pegasys.ethsigner.core.metrics.MetricsEndpoint; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.DownstreamPathCalculator; import tech.pegasys.ethsigner.core.util.FileUtil; import tech.pegasys.signers.secp256k1.api.SignerProvider; @@ -71,6 +72,14 @@ public void run() { .setReuseAddress(true) .setReusePort(true); + final MetricsEndpoint metricsEndpoint = + new MetricsEndpoint( + config.isMetricsEnabled(), + config.getMetricsPort(), + config.getMetricsHost(), + config.getMetricCategories(), + config.getMetricsHostAllowList()); + final Vertx vertx = Vertx.vertx(); try { final Runner runner = @@ -84,7 +93,8 @@ public void run() { jsonDecoder, config.getDataPath(), vertx, - config.getCorsAllowedOrigins()); + config.getCorsAllowedOrigins(), + metricsEndpoint); runner.start(); } catch (final Throwable t) { diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java index c7d21321c..c831be437 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java @@ -19,6 +19,7 @@ import tech.pegasys.ethsigner.core.http.RequestMapper; import tech.pegasys.ethsigner.core.http.UpcheckHandler; import tech.pegasys.ethsigner.core.jsonrpc.JsonDecoder; +import tech.pegasys.ethsigner.core.metrics.MetricsEndpoint; import tech.pegasys.ethsigner.core.requesthandler.VertxRequestTransmitter; import tech.pegasys.ethsigner.core.requesthandler.VertxRequestTransmitterFactory; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthAccountsResultProvider; @@ -34,6 +35,7 @@ import java.nio.file.Path; import java.time.Duration; import java.util.Collection; +import java.util.Optional; import java.util.Properties; import java.util.StringJoiner; import java.util.concurrent.CompletableFuture; @@ -73,6 +75,7 @@ public class Runner { private final Vertx vertx; private final Collection allowedCorsOrigins; private final HttpServerOptions serverOptions; + private final MetricsEndpoint metricsEndpoint; public Runner( final long chainId, @@ -84,7 +87,8 @@ public Runner( final JsonDecoder jsonDecoder, final Path dataPath, final Vertx vertx, - final Collection allowedCorsOrigins) { + final Collection allowedCorsOrigins, + final MetricsEndpoint metricsEndpoint) { this.chainId = chainId; this.signerProvider = signerProvider; this.clientOptions = clientOptions; @@ -95,13 +99,15 @@ public Runner( this.vertx = vertx; this.allowedCorsOrigins = allowedCorsOrigins; this.serverOptions = serverOptions; + this.metricsEndpoint = metricsEndpoint; } public void start() throws ExecutionException, InterruptedException { + metricsEndpoint.start(vertx); final HttpServer httpServer = createServerAndWait(vertx, router()); LOG.info("Server is up, and listening on {}", httpServer.actualPort()); if (dataPath != null) { - writePortsToFile(httpServer); + writePortsToFile(httpServer, metricsEndpoint.getPort()); } } @@ -171,12 +177,13 @@ private RequestMapper createRequestMapper( return requestMapper; } - private void writePortsToFile(final HttpServer server) { + private void writePortsToFile(final HttpServer server, final Optional metricsPort) { final File portsFile = new File(dataPath.toFile(), "ethsigner.ports"); portsFile.deleteOnExit(); final Properties properties = new Properties(); properties.setProperty("http-jsonrpc", String.valueOf(server.actualPort())); + metricsPort.ifPresent(port -> properties.setProperty("metrics-port", String.valueOf(port))); LOG.info( "Writing ethsigner.ports file: {}, with contents: {}", diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/Config.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/Config.java index 17fdc86ff..2a88c91c7 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/Config.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/Config.java @@ -18,9 +18,12 @@ import java.nio.file.Path; import java.time.Duration; import java.util.Collection; +import java.util.List; import java.util.Optional; +import java.util.Set; import org.apache.logging.log4j.Level; +import org.hyperledger.besu.plugin.services.metrics.MetricCategory; public interface Config { @@ -47,4 +50,14 @@ public interface Config { Optional getClientTlsOptions(); Collection getCorsAllowedOrigins(); + + Boolean isMetricsEnabled(); + + Integer getMetricsPort(); + + String getMetricsHost(); + + Set getMetricCategories(); + + List getMetricsHostAllowList(); } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/metrics/EthSignerMetricCategory.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/metrics/EthSignerMetricCategory.java new file mode 100644 index 000000000..039df5d0f --- /dev/null +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/metrics/EthSignerMetricCategory.java @@ -0,0 +1,52 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.core.metrics; + +import java.util.EnumSet; +import java.util.Optional; +import java.util.Set; + +import com.google.common.collect.ImmutableSet; +import org.hyperledger.besu.metrics.StandardMetricCategory; +import org.hyperledger.besu.plugin.services.metrics.MetricCategory; + +public enum EthSignerMetricCategory implements MetricCategory { + HTTP("http"), + SIGNING("signing"); + + private final String name; + + public static final Set DEFAULT_METRIC_CATEGORIES; + + static { + DEFAULT_METRIC_CATEGORIES = + ImmutableSet.builder() + .addAll(EnumSet.allOf(EthSignerMetricCategory.class)) + .addAll(EnumSet.allOf(StandardMetricCategory.class)) + .build(); + } + + EthSignerMetricCategory(final String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public Optional getApplicationPrefix() { + return Optional.empty(); + } +} diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/metrics/MetricsEndpoint.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/metrics/MetricsEndpoint.java new file mode 100644 index 000000000..1708ae46f --- /dev/null +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/metrics/MetricsEndpoint.java @@ -0,0 +1,83 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.core.metrics; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import io.vertx.core.Vertx; +import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; +import org.hyperledger.besu.metrics.prometheus.MetricsService; +import org.hyperledger.besu.metrics.prometheus.PrometheusMetricsSystem; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import org.hyperledger.besu.plugin.services.metrics.MetricCategory; + +public class MetricsEndpoint { + private final MetricsSystem metricsSystem; + private final MetricsConfiguration metricsConfig; + private Optional metricsService = Optional.empty(); + + public MetricsEndpoint( + final Boolean metricsEnabled, + final Integer metricsPort, + final String metricsNetworkInterface, + final Set metricCategories, + final List metricsHostAllowList) { + final MetricsConfiguration metricsConfig = + createMetricsConfiguration( + metricsEnabled, + metricsPort, + metricsNetworkInterface, + metricCategories, + metricsHostAllowList); + this.metricsSystem = PrometheusMetricsSystem.init(metricsConfig); + this.metricsConfig = metricsConfig; + } + + public void start(final Vertx vertx) { + if (metricsConfig.isEnabled()) { + metricsService = Optional.of(MetricsService.create(vertx, metricsConfig, metricsSystem)); + } else { + metricsService = Optional.empty(); + } + metricsService.ifPresent(MetricsService::start); + } + + public void stop() { + metricsService.ifPresent(MetricsService::stop); + } + + public Optional getPort() { + return metricsService.flatMap(MetricsService::getPort); + } + + public MetricsSystem getMetricsSystem() { + return metricsSystem; + } + + private MetricsConfiguration createMetricsConfiguration( + final Boolean metricsEnabled, + final Integer metricsPort, + final String metricsNetworkInterface, + final Set metricCategories, + final List metricsHostAllowList) { + return MetricsConfiguration.builder() + .enabled(metricsEnabled) + .port(metricsPort) + .host(metricsNetworkInterface) + .metricCategories(metricCategories) + .hostsWhitelist(metricsHostAllowList) + .build(); + } +} diff --git a/gradle/versions.gradle b/gradle/versions.gradle index ec1982bed..5718c2a70 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -75,5 +75,8 @@ dependencyManagement { } dependency 'org.zeroturnaround:zt-exec:1.11' + + dependency 'org.hyperledger.besu:plugin-api:1.4.0' + dependency 'org.hyperledger.besu.internal:metrics-core:1.4.0' } } From 7e282b468c643cff35cb226c0d2175612e9f4c92 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Fri, 11 Sep 2020 10:16:17 +0200 Subject: [PATCH 30/64] Run blocking handlers in parallel --- .../core/src/main/java/tech/pegasys/ethsigner/core/Runner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java index a7c27b088..267016e84 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java @@ -133,7 +133,7 @@ private Router router() { .handler(BodyHandler.create()) .handler(ResponseContentTypeHandler.create()) .failureHandler(new JsonRpcErrorHandler(new HttpResponseFactory())) - .blockingHandler(new JsonRpcHandler(responseFactory, requestMapper, jsonDecoder)); + .blockingHandler(new JsonRpcHandler(responseFactory, requestMapper, jsonDecoder),false); // Handler for UpCheck endpoint router From a355c0bbdb3e1df1a2f3d971006da77023a88a39 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Fri, 11 Sep 2020 10:22:55 +0200 Subject: [PATCH 31/64] Spotless fix --- .../core/src/main/java/tech/pegasys/ethsigner/core/Runner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java index 267016e84..d3db25643 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java @@ -133,7 +133,7 @@ private Router router() { .handler(BodyHandler.create()) .handler(ResponseContentTypeHandler.create()) .failureHandler(new JsonRpcErrorHandler(new HttpResponseFactory())) - .blockingHandler(new JsonRpcHandler(responseFactory, requestMapper, jsonDecoder),false); + .blockingHandler(new JsonRpcHandler(responseFactory, requestMapper, jsonDecoder), false); // Handler for UpCheck endpoint router From 418b106568ac987af2794253c62c74c3dbac82bb Mon Sep 17 00:00:00 2001 From: Trent Mohay <37158202+rain-on@users.noreply.github.com> Date: Sat, 12 Sep 2020 08:08:57 +1000 Subject: [PATCH 32/64] Ensure txns are handled in parallel (#331) --- .../core/src/main/java/tech/pegasys/ethsigner/core/Runner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java index c831be437..c00b2914e 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java @@ -138,7 +138,7 @@ private Router router() { .handler(BodyHandler.create()) .handler(ResponseContentTypeHandler.create()) .failureHandler(new JsonRpcErrorHandler(new HttpResponseFactory())) - .blockingHandler(new JsonRpcHandler(responseFactory, requestMapper, jsonDecoder)); + .blockingHandler(new JsonRpcHandler(responseFactory, requestMapper, jsonDecoder), false); // Handler for UpCheck endpoint router From 81337e80b307f7b8250239af6995d88ab12cde02 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Wed, 16 Sep 2020 14:12:17 +0200 Subject: [PATCH 33/64] Validate nonce when not optional --- .../EthSignTransactionResultProvider.java | 24 +++++++---------- .../EthSignTransactionResultProviderTest.java | 26 ++++++++++++++++--- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java index 395d449fb..394623387 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java @@ -23,7 +23,7 @@ import tech.pegasys.ethsigner.core.requesthandler.ResultProvider; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.EthTransaction; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.Transaction; -import tech.pegasys.signers.secp256k1.api.Signature; +import tech.pegasys.ethsigner.core.signing.TransactionSerializer; import tech.pegasys.signers.secp256k1.api.Signer; import java.util.List; @@ -33,9 +33,6 @@ import io.vertx.core.json.JsonObject; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.web3j.crypto.Sign; -import org.web3j.crypto.TransactionEncoder; -import org.web3j.utils.Numeric; public class EthSignTransactionResultProvider implements ResultProvider { @@ -69,6 +66,11 @@ public String createResponseResult(final JsonRpcRequest request) { throw new JsonRpcException(INVALID_PARAMS); } + if (!transaction.isNonceUserSpecified()) { + LOG.debug("Nonce not present in request {}", request.getId()); + throw new JsonRpcException(INVALID_PARAMS); + } + LOG.debug("Obtaining signer for {}", transaction.sender()); final Optional Signer = signerProvider.getSigner(transaction.sender()); if (Signer.isEmpty()) { @@ -76,17 +78,9 @@ public String createResponseResult(final JsonRpcRequest request) { throw new JsonRpcException(SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT); } - final byte[] bytesToSign = transaction.rlpEncode(chainId); - final Signature signature = Signer.get().sign(bytesToSign); - final Sign.SignatureData web3jSignature = - new Sign.SignatureData( - signature.getV().toByteArray(), - signature.getR().toByteArray(), - signature.getS().toByteArray()); - final Sign.SignatureData eip155Signature = - TransactionEncoder.createEip155SignatureData(web3jSignature, chainId); - final byte[] serializedBytes = transaction.rlpEncode(eip155Signature); - return Numeric.toHexString(serializedBytes); + final TransactionSerializer transactionSerializer = + new TransactionSerializer(Signer.get(), chainId); + return transactionSerializer.serialize(transaction); } private Transaction createTransaction(final JsonRpcRequest request) { diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionResultProviderTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionResultProviderTest.java index dfa404662..ffbca4c55 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionResultProviderTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionResultProviderTest.java @@ -72,7 +72,6 @@ static void beforeAll() { @ArgumentsSource(InvalidParamsProvider.class) @NullSource public void ifParamIsInvalidExceptionIsThrownWithInvalidParams(final Object params) { - final AddressIndexedSignerProvider mockSignerProvider = mock(AddressIndexedSignerProvider.class); final EthSignTransactionResultProvider resultProvider = @@ -106,7 +105,7 @@ public void ifAddressIsNotUnlockedExceptionIsThrownWithSigningNotUnlocked() { @Test public void signatureHasTheExpectedFormat() { - Credentials cs = + final Credentials cs = Credentials.create("0x1618fc3e47aec7e70451256e033b9edb67f4c469258d8e2fbb105552f141ae41"); final ECPublicKey key = EthPublicKeyUtils.createPublicKey(cs.getEcKeyPair().getPublicKey()); @@ -133,9 +132,28 @@ public void signatureHasTheExpectedFormat() { assertThat(signedTx).hasSize(72); } + @Test + public void nonceNotProvidedExceptionIsThrownWithInvalidParams() { + final AddressIndexedSignerProvider mockSignerProvider = + mock(AddressIndexedSignerProvider.class); + final EthSignTransactionResultProvider resultProvider = + new EthSignTransactionResultProvider(chainId, mockSignerProvider, jsonDecoder); + + final JsonObject params = getTxParameters(); + params.remove("nonce"); + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + final int id = 1; + request.setId(new JsonRpcRequestId(id)); + request.setParams(params); + final Throwable thrown = catchThrowable(() -> resultProvider.createResponseResult(request)); + assertThat(thrown).isInstanceOf(JsonRpcException.class); + final JsonRpcException rpcException = (JsonRpcException) thrown; + assertThat(rpcException.getJsonRpcError()).isEqualTo(INVALID_PARAMS); + } + @Test public void returnsExpectedSignature() { - Credentials cs = + final Credentials cs = Credentials.create("0x1618fc3e47aec7e70451256e033b9edb67f4c469258d8e2fbb105552f141ae41"); final ECPublicKey key = EthPublicKeyUtils.createPublicKey(cs.getEcKeyPair().getPublicKey()); final String addr = Keys.getAddress(EthPublicKeyUtils.toHexString(key)); @@ -161,7 +179,7 @@ public void returnsExpectedSignature() { final EthSignTransactionResultProvider resultProvider = new EthSignTransactionResultProvider(chainId, mockSignerProvider, jsonDecoder); - JsonObject params = getTxParameters(); + final JsonObject params = getTxParameters(); params.put("from", addr); final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); final int id = 1; From 080cc4e921261b50cb5be5d5b611810f918b6fc3 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Wed, 16 Sep 2020 14:34:27 +0200 Subject: [PATCH 34/64] Upgrade signers library --- gradle/versions.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/versions.gradle b/gradle/versions.gradle index b25336826..1be9eb0c9 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -70,7 +70,7 @@ dependencyManagement { dependency 'org.web3j:core:4.5.14' dependency 'org.web3j:crypto:4.5.14' - dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.5-ADHARA-SNAPSHOT') { + dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.7-ADHARA-SNAPSHOT') { entry 'keystorage-hashicorp' entry 'keystorage-hsm' entry 'keystorage-cavium' From 6d01caa1f2db3dec8ca73c65b7bbac66ce97b046 Mon Sep 17 00:00:00 2001 From: Marc de Klerk Date: Mon, 21 Sep 2020 14:45:32 +0200 Subject: [PATCH 35/64] Moved publish repo --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a00884a39..e30154a4e 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,7 @@ if (!JavaVersion.current().java11Compatible) { " Detected version ${JavaVersion.current()}") } -def dockerRepo = "adharaprojects" +def dockerRepo = "adharatech" def bintrayUser = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') def bintrayKey = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_KEY') From 76dc4e1df3f60ab5b5726567730bc2e760516840 Mon Sep 17 00:00:00 2001 From: Trent Mohay <37158202+rain-on@users.noreply.github.com> Date: Wed, 23 Sep 2020 08:38:18 +1000 Subject: [PATCH 36/64] Remove access-control-allow-origin header from web3provider response (#334) It appears some web3providers (or stubs) insert the CORS response header, regardless of the existence of the ORIGIN header in the request - this in turn breaks CORS validation when EthSIgner is used. To overcome this, Ethsigner now strips the access-control-allow-origin header from the proxied response. --- .../sendtransaction/ForwardedMessageResponder.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/ForwardedMessageResponder.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/ForwardedMessageResponder.java index aa506d115..29e753555 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/ForwardedMessageResponder.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/ForwardedMessageResponder.java @@ -23,6 +23,7 @@ import java.util.concurrent.TimeoutException; import javax.net.ssl.SSLHandshakeException; +import io.netty.handler.codec.http.HttpHeaderNames; import io.vertx.ext.web.RoutingContext; public class ForwardedMessageResponder implements DownstreamResponseHandler { @@ -37,7 +38,12 @@ public ForwardedMessageResponder(final RoutingContext context) { public void handleResponse( final Iterable> headers, final int statusCode, final String body) { context.response().setStatusCode(statusCode); - headers.forEach(entry -> context.response().headers().add(entry.getKey(), entry.getValue())); + headers.forEach( + entry -> { + if (!entry.getKey().equals(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN.toString())) { + context.response().headers().add(entry.getKey(), entry.getValue()); + } + }); context.response().setChunked(false); context.response().end(body); } From 2520d454f75fea80a6b3cbda946cbe448e12e698 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Wed, 30 Sep 2020 11:52:24 +0200 Subject: [PATCH 37/64] Sync with upstream --- gradle/versions.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/versions.gradle b/gradle/versions.gradle index 1be9eb0c9..68492d734 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -70,7 +70,7 @@ dependencyManagement { dependency 'org.web3j:core:4.5.14' dependency 'org.web3j:crypto:4.5.14' - dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.7-ADHARA-SNAPSHOT') { + dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.10-ADHARA-SNAPSHOT') { entry 'keystorage-hashicorp' entry 'keystorage-hsm' entry 'keystorage-cavium' From 419e5597da6761e8d5548246e482e33a55e6804f Mon Sep 17 00:00:00 2001 From: Marelize Kriel <47826265+mkrielza@users.noreply.github.com> Date: Thu, 1 Oct 2020 08:04:13 +0200 Subject: [PATCH 38/64] Add eth_signTransaction endpoint (#336) --- CHANGELOG.md | 5 + .../tech/pegasys/ethsigner/core/Runner.java | 7 +- .../EthSignTransactionResultProvider.java | 116 ++++++++++ .../EthSignTransactionResultProviderTest.java | 219 ++++++++++++++++++ 4 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java create mode 100644 ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionResultProviderTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fc2ea4b8..0cf08c919 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Unreleased + +### Features Added +- Added "eth_signTransaction" JSON RPC + ## 0.7.1 ### Features Added diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java index c00b2914e..99baf807e 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java @@ -24,6 +24,7 @@ import tech.pegasys.ethsigner.core.requesthandler.VertxRequestTransmitterFactory; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthAccountsResultProvider; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignResultProvider; +import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignTransactionResultProvider; import tech.pegasys.ethsigner.core.requesthandler.internalresponse.InternalResponseHandler; import tech.pegasys.ethsigner.core.requesthandler.passthrough.PassThroughHandler; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.DownstreamPathCalculator; @@ -173,7 +174,11 @@ private RequestMapper createRequestMapper( requestMapper.addHandler( "eth_sign", new InternalResponseHandler<>(responseFactory, new EthSignResultProvider(signerProvider))); - + requestMapper.addHandler( + "eth_signTransaction", + new InternalResponseHandler<>( + responseFactory, + new EthSignTransactionResultProvider(chainId, signerProvider, jsonDecoder))); return requestMapper; } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java new file mode 100644 index 000000000..394623387 --- /dev/null +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthSignTransactionResultProvider.java @@ -0,0 +1,116 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.core.requesthandler.internalresponse; + +import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.INVALID_PARAMS; +import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT; + +import tech.pegasys.ethsigner.core.AddressIndexedSignerProvider; +import tech.pegasys.ethsigner.core.jsonrpc.EthSendTransactionJsonParameters; +import tech.pegasys.ethsigner.core.jsonrpc.JsonDecoder; +import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequest; +import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; +import tech.pegasys.ethsigner.core.requesthandler.ResultProvider; +import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.EthTransaction; +import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.Transaction; +import tech.pegasys.ethsigner.core.signing.TransactionSerializer; +import tech.pegasys.signers.secp256k1.api.Signer; + +import java.util.List; +import java.util.Optional; + +import io.vertx.core.json.DecodeException; +import io.vertx.core.json.JsonObject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class EthSignTransactionResultProvider implements ResultProvider { + + private static final Logger LOG = LogManager.getLogger(); + + private final long chainId; + private final AddressIndexedSignerProvider signerProvider; + private final JsonDecoder decoder; + + public EthSignTransactionResultProvider( + final long chainId, + final AddressIndexedSignerProvider signerProvider, + final JsonDecoder decoder) { + this.chainId = chainId; + this.signerProvider = signerProvider; + this.decoder = decoder; + } + + @Override + public String createResponseResult(final JsonRpcRequest request) { + LOG.debug("Transforming request {}, {}", request.getId(), request.getMethod()); + final Transaction transaction; + try { + transaction = createTransaction(request); + + } catch (final NumberFormatException e) { + LOG.debug("Parsing values failed for request: {}", request.getParams(), e); + throw new JsonRpcException(INVALID_PARAMS); + } catch (final IllegalArgumentException | DecodeException e) { + LOG.debug("JSON Deserialization failed for request: {}", request.getParams(), e); + throw new JsonRpcException(INVALID_PARAMS); + } + + if (!transaction.isNonceUserSpecified()) { + LOG.debug("Nonce not present in request {}", request.getId()); + throw new JsonRpcException(INVALID_PARAMS); + } + + LOG.debug("Obtaining signer for {}", transaction.sender()); + final Optional Signer = signerProvider.getSigner(transaction.sender()); + if (Signer.isEmpty()) { + LOG.info("From address ({}) does not match any available account", transaction.sender()); + throw new JsonRpcException(SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT); + } + + final TransactionSerializer transactionSerializer = + new TransactionSerializer(Signer.get(), chainId); + return transactionSerializer.serialize(transaction); + } + + private Transaction createTransaction(final JsonRpcRequest request) { + final EthSendTransactionJsonParameters params = + fromRpcRequestToJsonParam(EthSendTransactionJsonParameters.class, request); + return new EthTransaction(params, null, request.getId()); + } + + public T fromRpcRequestToJsonParam(final Class type, final JsonRpcRequest request) { + final Object object; + final Object params = request.getParams(); + if (params instanceof List) { + @SuppressWarnings("unchecked") + final List paramList = (List) params; + if (paramList.size() != 1) { + throw new IllegalArgumentException( + type.getSimpleName() + + " json Rpc requires one parameter, request contained " + + paramList.size()); + } + object = paramList.get(0); + } else { + object = params; + } + if (object == null) { + throw new IllegalArgumentException( + type.getSimpleName() + + " json Rpc requires a valid parameter, request contained a null object"); + } + final JsonObject receivedParams = JsonObject.mapFrom(object); + return decoder.decodeValue(receivedParams.toBuffer(), type); + } +} diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionResultProviderTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionResultProviderTest.java new file mode 100644 index 000000000..17d9315c0 --- /dev/null +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthSignTransactionResultProviderTest.java @@ -0,0 +1,219 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.core.jsonrpcproxy; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.INVALID_PARAMS; +import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT; + +import tech.pegasys.ethsigner.core.AddressIndexedSignerProvider; +import tech.pegasys.ethsigner.core.jsonrpc.JsonDecoder; +import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequest; +import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequestId; +import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; +import tech.pegasys.ethsigner.core.requesthandler.internalresponse.EthSignTransactionResultProvider; +import tech.pegasys.signers.secp256k1.EthPublicKeyUtils; +import tech.pegasys.signers.secp256k1.api.Signature; +import tech.pegasys.signers.secp256k1.api.Signer; + +import java.math.BigInteger; +import java.security.interfaces.ECPublicKey; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.vertx.core.json.JsonObject; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.junit.jupiter.params.provider.NullSource; +import org.web3j.crypto.Credentials; +import org.web3j.crypto.Keys; +import org.web3j.crypto.Sign; + +public class EthSignTransactionResultProviderTest { + + private static JsonDecoder jsonDecoder; + private static long chainId; + + @BeforeAll + static void beforeAll() { + final ObjectMapper jsonObjectMapper = new ObjectMapper(); + jsonObjectMapper.configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, true); + jsonObjectMapper.configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, true); + jsonDecoder = new JsonDecoder(jsonObjectMapper); + chainId = 44844; + } + + @ParameterizedTest + @ArgumentsSource(InvalidParamsProvider.class) + @NullSource + public void ifParamIsInvalidExceptionIsThrownWithInvalidParams(final Object params) { + final AddressIndexedSignerProvider mockSignerProvider = + mock(AddressIndexedSignerProvider.class); + final EthSignTransactionResultProvider resultProvider = + new EthSignTransactionResultProvider(chainId, mockSignerProvider, jsonDecoder); + + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + request.setId(new JsonRpcRequestId(1)); + request.setParams(params); + + final Throwable thrown = catchThrowable(() -> resultProvider.createResponseResult(request)); + assertThat(thrown).isInstanceOf(JsonRpcException.class); + final JsonRpcException rpcException = (JsonRpcException) thrown; + assertThat(rpcException.getJsonRpcError()).isEqualTo(INVALID_PARAMS); + } + + @Test + public void ifAddressIsNotUnlockedExceptionIsThrownWithSigningNotUnlocked() { + final AddressIndexedSignerProvider mockSignerProvider = + mock(AddressIndexedSignerProvider.class); + final EthSignTransactionResultProvider resultProvider = + new EthSignTransactionResultProvider(chainId, mockSignerProvider, jsonDecoder); + + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + request.setId(new JsonRpcRequestId(1)); + request.setParams(List.of(getTxParameters())); + final Throwable thrown = catchThrowable(() -> resultProvider.createResponseResult(request)); + assertThat(thrown).isInstanceOf(JsonRpcException.class); + final JsonRpcException rpcException = (JsonRpcException) thrown; + assertThat(rpcException.getJsonRpcError()).isEqualTo(SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT); + } + + @Test + public void signatureHasTheExpectedFormat() { + final Credentials cs = + Credentials.create("0x1618fc3e47aec7e70451256e033b9edb67f4c469258d8e2fbb105552f141ae41"); + final ECPublicKey key = EthPublicKeyUtils.createPublicKey(cs.getEcKeyPair().getPublicKey()); + + final Signer mockSigner = mock(Signer.class); + doReturn(key).when(mockSigner).getPublicKey(); + final BigInteger v = BigInteger.ONE; + final BigInteger r = BigInteger.TWO; + final BigInteger s = BigInteger.TEN; + doReturn(new Signature(v, r, s)).when(mockSigner).sign(any(byte[].class)); + final AddressIndexedSignerProvider mockSignerProvider = + mock(AddressIndexedSignerProvider.class); + doReturn(Optional.of(mockSigner)).when(mockSignerProvider).getSigner(anyString()); + final EthSignTransactionResultProvider resultProvider = + new EthSignTransactionResultProvider(chainId, mockSignerProvider, jsonDecoder); + + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + final int id = 1; + request.setId(new JsonRpcRequestId(id)); + request.setParams(List.of(getTxParameters())); + + final Object result = resultProvider.createResponseResult(request); + assertThat(result).isInstanceOf(String.class); + final String signedTx = (String) result; + assertThat(signedTx).hasSize(72); + } + + @Test + public void nonceNotProvidedExceptionIsThrownWithInvalidParams() { + final AddressIndexedSignerProvider mockSignerProvider = + mock(AddressIndexedSignerProvider.class); + final EthSignTransactionResultProvider resultProvider = + new EthSignTransactionResultProvider(chainId, mockSignerProvider, jsonDecoder); + + final JsonObject params = getTxParameters(); + params.remove("nonce"); + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + final int id = 1; + request.setId(new JsonRpcRequestId(id)); + request.setParams(params); + final Throwable thrown = catchThrowable(() -> resultProvider.createResponseResult(request)); + assertThat(thrown).isInstanceOf(JsonRpcException.class); + final JsonRpcException rpcException = (JsonRpcException) thrown; + assertThat(rpcException.getJsonRpcError()).isEqualTo(INVALID_PARAMS); + } + + @Test + public void returnsExpectedSignature() { + final Credentials cs = + Credentials.create("0x1618fc3e47aec7e70451256e033b9edb67f4c469258d8e2fbb105552f141ae41"); + final ECPublicKey key = EthPublicKeyUtils.createPublicKey(cs.getEcKeyPair().getPublicKey()); + final String addr = Keys.getAddress(EthPublicKeyUtils.toHexString(key)); + + final Signer mockSigner = mock(Signer.class); + doReturn(key).when(mockSigner).getPublicKey(); + + doAnswer( + answer -> { + byte[] data = answer.getArgument(0, byte[].class); + final Sign.SignatureData signature = + Sign.signPrefixedMessage(data, cs.getEcKeyPair()); + return new Signature( + new BigInteger(signature.getV()), + new BigInteger(1, signature.getR()), + new BigInteger(1, signature.getS())); + }) + .when(mockSigner) + .sign(any(byte[].class)); + final AddressIndexedSignerProvider mockSignerProvider = + mock(AddressIndexedSignerProvider.class); + doReturn(Optional.of(mockSigner)).when(mockSignerProvider).getSigner(anyString()); + final EthSignTransactionResultProvider resultProvider = + new EthSignTransactionResultProvider(chainId, mockSignerProvider, jsonDecoder); + + final JsonObject params = getTxParameters(); + params.put("from", addr); + final JsonRpcRequest request = new JsonRpcRequest("2.0", "eth_signTransaction"); + final int id = 1; + request.setId(new JsonRpcRequestId(id)); + request.setParams(params); + + final Object result = resultProvider.createResponseResult(request); + assertThat(result).isInstanceOf(String.class); + final String encodedTransaction = (String) result; + assertThat(encodedTransaction) + .isEqualTo( + "0xf862468082760094627306090abab3a6e1400e9345bc60c78a8bef57020083015e7ba0e2b345c1c5af05f518e7fd716459fd41d4af3e355b4afb48d8fddc21eae98c13a043975efec1fcfd03f7af77c4a402510981a088765b180bc84163ecba8f01f46d"); + } + + private static JsonObject getTxParameters() { + final JsonObject jsonObject = new JsonObject(); + jsonObject.put("from", "0xf17f52151ebef6c7334fad080c5704d77216b732"); + jsonObject.put("to", "0x627306090abaB3A6e1400e9345bC60c78a8BEf57"); + jsonObject.put("gasPrice", "0x0"); + jsonObject.put("gas", "0x7600"); + jsonObject.put("nonce", "0x46"); + jsonObject.put("value", "0x2"); + jsonObject.put("data", "0x0"); + return jsonObject; + } + + private static class InvalidParamsProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(final ExtensionContext context) { + return Stream.of( + Arguments.of(Collections.emptyList()), + Arguments.of(Collections.singleton(2)), + Arguments.of(List.of(1, 2, 3)), + Arguments.of(new Object())); + } + } +} From f4c57f9c6a111a06c8f8f485a6fc5acf6bf86b48 Mon Sep 17 00:00:00 2001 From: Joshua Fernandes Date: Wed, 7 Oct 2020 09:44:25 +1000 Subject: [PATCH 39/64] adding in quorumengineering as the new namespace and pegasyseng as the secondary namespace (#338) --- build.gradle | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 072b4ac57..f914c2e22 100644 --- a/build.gradle +++ b/build.gradle @@ -480,7 +480,7 @@ tasks.register("dockerDistUntar") { task distDocker(type: Exec) { dependsOn dockerDistUntar def dockerBuildVersion = project.hasProperty('release.releaseVersion') ? project.property('release.releaseVersion') : "${rootProject.version}" - def imageName = "pegasyseng/" + repositoryName + def imageName = "quorumengineering/" + repositoryName def image = project.hasProperty('release.releaseVersion') ? "${imageName}:" + project.property('release.releaseVersion') : "${imageName}:${project.version}" def dockerBuildDir = "build/docker-" + repositoryName + "/" workingDir "${dockerBuildDir}" @@ -499,7 +499,7 @@ task distDocker(type: Exec) { task testDocker(type: Exec) { dependsOn distDocker def dockerReportsDir = "docker/reports/" - def imageName = "pegasyseng/" + repositoryName + def imageName = "quorumengineering/" + repositoryName def image = project.hasProperty('release.releaseVersion') ? "${imageName}:" + project.property('release.releaseVersion') : "${imageName}:${project.version}" workingDir "docker" @@ -513,9 +513,14 @@ task testDocker(type: Exec) { task dockerUpload(type: Exec) { dependsOn distDocker - def imageName = "pegasyseng/" + repositoryName + def imageName = "quorumengineering/" + repositoryName def image = project.hasProperty('release.releaseVersion') ? "${imageName}:" + project.property('release.releaseVersion') : "${imageName}:${project.version}" def cmd = "docker push '${image}'" + + def psImageName = "pegasyseng/" + repositoryName + def psImage = project.hasProperty('release.releaseVersion') ? "${psImageName}:" + project.property('release.releaseVersion') : "${psImageName}:${project.version}" + cmd += " && docker tag '${image}' '${psImage}' && docker push '${psImage}'" + def additionalTags = [] if (project.hasProperty('branch') && project.property('branch') == 'master') { @@ -528,6 +533,7 @@ task dockerUpload(type: Exec) { } additionalTags.each { tag -> cmd += " && docker tag '${image}' '${imageName}:${tag.trim()}' && docker push '${imageName}:${tag.trim()}'" } + additionalTags.each { tag -> cmd += " && docker tag '${image}' '${psImageName}:${tag.trim()}' && docker push '${psImageName}:${tag.trim()}'" } executable "sh" args "-c", cmd } From 7f2e975801e3d7fba648f3944c96a2c7e418498b Mon Sep 17 00:00:00 2001 From: Joshua Fernandes Date: Thu, 8 Oct 2020 12:31:46 +1000 Subject: [PATCH 40/64] updates for circle (#339) --- .circleci/config.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fc53f36ce..a2f766395 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,10 @@ orbs: executors: executor_med: # 2cpu, 4G ram docker: - - image: circleci/openjdk:11.0.4-jdk-stretch + - image: circleci/openjdk:11.0.8-jdk-buster + auth: + username: $DOCKER_USER + password: $DOCKER_PASSWORD resource_class: medium working_directory: ~/project environment: @@ -16,7 +19,10 @@ executors: executor_large: # 4cpu, 8G ram docker: - - image: circleci/openjdk:11.0.4-jdk-stretch + - image: circleci/openjdk:11.0.8-jdk-buster + auth: + username: $DOCKER_USER + password: $DOCKER_PASSWORD resource_class: large working_directory: ~/project environment: @@ -25,7 +31,7 @@ executors: executor_machine: # 2cpu , 8G ram machine: - image: ubuntu-1604:201903-01 #Ubuntu 16.04, docker 18.09.3, docker-compose 1.23.1 + image: ubuntu-2004:202008-01 #Ubuntu 20.04, docker 19.03, docker-compose 1.27.4 docker_layer_caching: true working_directory: ~/project environment: From 17e0eb2d57d7c31a3bd020a6225d742a47f905cc Mon Sep 17 00:00:00 2001 From: Joshua Fernandes Date: Mon, 12 Oct 2020 10:40:04 +1000 Subject: [PATCH 41/64] adding in docker contexts (#340) --- .circleci/config.yml | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a2f766395..e119eb839 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,8 +9,8 @@ executors: docker: - image: circleci/openjdk:11.0.8-jdk-buster auth: - username: $DOCKER_USER - password: $DOCKER_PASSWORD + username: $DOCKER_USER_RO + password: $DOCKER_PASSWORD_RO resource_class: medium working_directory: ~/project environment: @@ -21,8 +21,8 @@ executors: docker: - image: circleci/openjdk:11.0.8-jdk-buster auth: - username: $DOCKER_USER - password: $DOCKER_PASSWORD + username: $DOCKER_USER_RO + password: $DOCKER_PASSWORD_RO resource_class: large working_directory: ~/project environment: @@ -184,7 +184,7 @@ jobs: - run: name: Publish Docker command: | - docker login --username "${DOCKER_USER}" --password "${DOCKER_PASSWORD}" + docker login --username "${DOCKER_USER_RW}" --password "${DOCKER_PASSWORD_RW}" ./gradlew --no-daemon --parallel "-Pbranch=${CIRCLE_BRANCH}" dockerUpload - notify @@ -199,19 +199,29 @@ workflows: only: - master jobs: - - build + - build: + context: + - dockerhub-quorumengineering-ro - acceptanceTests: requires: - build + context: + - dockerhub-quorumengineering-ro default: jobs: - - build + - build: + context: + - dockerhub-quorumengineering-ro - acceptanceTests: requires: - build + context: + - dockerhub-quorumengineering-ro - buildDocker: requires: - build + context: + - dockerhub-quorumengineering-ro - publish: filters: branches: @@ -221,6 +231,9 @@ workflows: requires: - build - acceptanceTests + context: + - dockerhub-quorumengineering-ro + - quorum-gradle - publishDocker: filters: branches: @@ -231,3 +244,5 @@ workflows: - build - acceptanceTests - buildDocker + context: + - dockerhub-quorumengineering-rw \ No newline at end of file From 28256001435c3568f009478984a9b8db95aed9dc Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Thu, 15 Oct 2020 12:39:33 +1000 Subject: [PATCH 42/64] fixed typo (#341) Signed-off-by: Sally MacFarlane --- .../java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java index 5b2bb3253..d83f9a0ed 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java @@ -30,7 +30,7 @@ @Command( name = RawSubCommand.COMMAND_NAME, description = - "Sign transactions with an unecrypted key specified on cmdline. NOT SUITABLE FOR PRODUCTION.", + "Sign transactions with an unencrypted key specified on cmdline. NOT SUITABLE FOR PRODUCTION.", mixinStandardHelpOptions = true, hidden = true) public class RawSubCommand extends SignerSubCommand { From b1e00beb5ad3974b1b280e67c9d474c9bce630ed Mon Sep 17 00:00:00 2001 From: mkrielza Date: Thu, 15 Oct 2020 09:37:45 +0200 Subject: [PATCH 43/64] Maintenance --- acceptance-tests/build.gradle | 2 +- .../tech/pegasys/ethsigner/tests/AcceptanceTestBase.java | 2 +- .../tech/pegasys/ethsigner/tests/CorsAcceptanceTest.java | 2 +- .../ethsigner/tests/DataPathFeatureFlagAcceptanceTest.java | 2 +- .../ethsigner/tests/MethodNotFoundAcceptanceTest.java | 2 +- .../pegasys/ethsigner/tests/UpCheckAcceptanceTest.java | 2 +- .../tests/account/AccountManagementAcceptanceTest.java | 2 +- .../java/tech/pegasys/ethsigner/tests/dsl/Account.java | 2 +- .../java/tech/pegasys/ethsigner/tests/dsl/Accounts.java | 2 +- .../test/java/tech/pegasys/ethsigner/tests/dsl/Besu.java | 2 +- .../java/tech/pegasys/ethsigner/tests/dsl/Contracts.java | 2 +- .../test/java/tech/pegasys/ethsigner/tests/dsl/Eea.java | 2 +- .../test/java/tech/pegasys/ethsigner/tests/dsl/Eth.java | 2 +- .../test/java/tech/pegasys/ethsigner/tests/dsl/Gas.java | 2 +- .../tech/pegasys/ethsigner/tests/dsl/PrivateContracts.java | 2 +- .../pegasys/ethsigner/tests/dsl/PrivateTransaction.java | 2 +- .../tech/pegasys/ethsigner/tests/dsl/PublicContracts.java | 2 +- .../ethsigner/tests/dsl/RawJsonRpcRequestFactory.java | 2 +- .../pegasys/ethsigner/tests/dsl/RawJsonRpcRequests.java | 2 +- .../tech/pegasys/ethsigner/tests/dsl/Transactions.java | 2 +- .../tech/pegasys/ethsigner/tests/dsl/http/HttpRequest.java | 2 +- .../pegasys/ethsigner/tests/dsl/http/HttpResponse.java | 2 +- .../java/tech/pegasys/ethsigner/tests/dsl/node/Node.java | 2 +- .../ethsigner/tests/dsl/node/besu/BesuNodePorts.java | 2 +- .../ethsigner/tests/dsl/signer/EthSignerProcessRunner.java | 2 +- .../tech/pegasys/ethsigner/tests/dsl/signer/Signer.java | 2 +- .../ethsigner/tests/dsl/signer/SignerConfiguration.java | 2 +- .../tests/dsl/signer/SignerConfigurationBuilder.java | 2 +- .../pegasys/ethsigner/tests/dsl/signer/SignerResponse.java | 2 +- .../tests/dsl/signer/TransactionSignerParamsSupplier.java | 2 +- .../pegasys/ethsigner/tests/dsl/tls/BasicTlsOptions.java | 2 +- .../pegasys/ethsigner/tests/dsl/tls/ClientTlsConfig.java | 2 +- .../ethsigner/tests/dsl/tls/OkHttpClientHelpers.java | 2 +- .../ethsigner/tests/dsl/tls/TlsCertificateDefinition.java | 2 +- .../pegasys/ethsigner/tests/dsl/utils/ExceptionUtils.java | 2 +- .../java/tech/pegasys/ethsigner/tests/dsl/utils/Hex.java | 2 +- .../tech/pegasys/ethsigner/tests/dsl/utils/WaitUtils.java | 2 +- .../AzureBasedTomlLoadingAcceptanceTest.java | 2 +- .../multikeysigner/FileBasedTomlLoadingAcceptanceTest.java | 2 +- .../HashicorpBasedTomlLoadingAcceptanceTest.java | 2 +- .../HashicorpTlsBasedTomlLoadingAcceptanceTest.java | 2 +- .../tests/multikeysigner/MultiKeyAcceptanceTestBase.java | 2 +- .../multikeysigner/MultiKeySigningAcceptanceTest.java | 2 +- .../MultiKeyAzureTransactionSignerAcceptanceTest.java | 2 +- .../MultiKeyFileBasedTransactionSignerAcceptanceTest.java | 2 +- .../MultiKeyHashicorpTransactionSignerAcceptanceTest.java | 2 +- .../MultiKeyTransactionSigningAcceptanceTestBase.java | 2 +- .../ethsigner/tests/proxy/PassThroughAcceptanceTest.java | 2 +- .../tests/signing/PrivateTransactionAcceptanceTest.java | 2 +- .../tests/signing/ReplayProtectionAcceptanceTest.java | 2 +- .../tests/signing/SmartContractAcceptanceTest.java | 2 +- .../tests/signing/ValueTransferAcceptanceTest.java | 2 +- .../signing/ValueTransferWithAzureAcceptanceTest.java | 2 +- .../ethsigner/tests/signing/contract/SimpleStorage.sol | 2 +- .../tests/signing/contract/generated/SimpleStorage.java | 2 +- .../ValueTransferWithHashicorpAcceptanceTest.java | 2 +- .../ValueTransferWithHashicorpOnTlsAcceptanceTest.java | 2 +- .../tests/timeouts/FailedConnectionAcceptanceTest.java | 2 +- .../tests/timeouts/ReadTimeoutAcceptanceTest.java | 2 +- .../ethsigner/tests/tls/ServerSideTlsAcceptanceTest.java | 2 +- build.gradle | 2 +- ethsigner/app/build.gradle | 2 +- .../src/main/java/tech/pegasys/ethsigner/EthSignerApp.java | 2 +- ethsigner/build.gradle | 2 +- .../main/java/tech/pegasys/ethsigner/ApplicationInfo.java | 2 +- .../java/tech/pegasys/ethsigner/CommandlineParser.java | 2 +- .../java/tech/pegasys/ethsigner/EthSignerBaseCommand.java | 2 +- .../main/java/tech/pegasys/ethsigner/PlatformDetector.java | 2 +- .../main/java/tech/pegasys/ethsigner/SignerSubCommand.java | 2 +- .../src/main/java/tech/pegasys/ethsigner/VersionInfo.java | 2 +- .../main/java/tech/pegasys/ethsigner/VersionProvider.java | 2 +- .../java/tech/pegasys/ethsigner/CmdlineHelpers.java | 2 +- .../ethsigner/CommandlineParserClientTlsOptionsTest.java | 2 +- .../java/tech/pegasys/ethsigner/CommandlineParserTest.java | 2 +- .../java/tech/pegasys/ethsigner/NullSignerSubCommand.java | 2 +- ethsigner/core/build.gradle | 2 +- .../pegasys/ethsigner/jsonrpcproxy/DefaultTestBase.java | 2 +- .../ethsigner/jsonrpcproxy/EthAccountsIntegrationTest.java | 2 +- .../jsonrpcproxy/EthSignerParsingIntegrationTest.java | 2 +- .../jsonrpcproxy/FailedConnectionIntegrationTest.java | 2 +- .../ethsigner/jsonrpcproxy/IntegrationTestBase.java | 2 +- .../ethsigner/jsonrpcproxy/ProxyIntegrationTest.java | 2 +- .../SigningEeaSendTransactionIntegrationTest.java | 2 +- .../SigningEthSendTransactionIntegrationTest.java | 2 +- ...igningEthSendTransactionWithChainIdIntegrationTest.java | 2 +- .../tech/pegasys/ethsigner/jsonrpcproxy/TimeoutTest.java | 2 +- .../jsonrpcproxy/model/jsonrpc/EeaSendRawTransaction.java | 2 +- .../jsonrpcproxy/model/jsonrpc/EeaSendTransaction.java | 2 +- .../model/jsonrpc/EthProtocolVersionRequest.java | 2 +- .../jsonrpcproxy/model/jsonrpc/PrivateTransaction.java | 2 +- .../jsonrpcproxy/model/jsonrpc/SendRawTransaction.java | 2 +- .../jsonrpcproxy/model/jsonrpc/SendTransaction.java | 2 +- .../ethsigner/jsonrpcproxy/model/jsonrpc/Transaction.java | 2 +- .../jsonrpcproxy/model/request/EthNodeRequest.java | 2 +- .../jsonrpcproxy/model/request/EthRequestFactory.java | 2 +- .../jsonrpcproxy/model/request/EthSignerRequest.java | 2 +- .../jsonrpcproxy/model/response/EthNodeResponse.java | 2 +- .../jsonrpcproxy/model/response/EthResponseFactory.java | 2 +- .../jsonrpcproxy/model/response/EthSignerResponse.java | 2 +- .../jsonrpcproxy/support/TransactionCountResponder.java | 2 +- .../main/java/tech/pegasys/ethsigner/core/EthSigner.java | 2 +- .../pegasys/ethsigner/core/InitializationException.java | 2 +- .../src/main/java/tech/pegasys/ethsigner/core/Runner.java | 2 +- .../java/tech/pegasys/ethsigner/core/config/Config.java | 2 +- .../tech/pegasys/ethsigner/core/config/TlsOptions.java | 2 +- .../pegasys/ethsigner/core/http/HttpResponseFactory.java | 2 +- .../pegasys/ethsigner/core/http/JsonRpcErrorHandler.java | 2 +- .../tech/pegasys/ethsigner/core/http/JsonRpcHandler.java | 2 +- .../tech/pegasys/ethsigner/core/http/LogErrorHandler.java | 2 +- .../tech/pegasys/ethsigner/core/http/RequestMapper.java | 2 +- .../tech/pegasys/ethsigner/core/http/UpcheckHandler.java | 2 +- .../core/jsonrpc/EeaSendTransactionJsonParameters.java | 2 +- .../core/jsonrpc/EthSendTransactionJsonParameters.java | 2 +- .../tech/pegasys/ethsigner/core/jsonrpc/JsonDecoder.java | 2 +- .../pegasys/ethsigner/core/jsonrpc/JsonRpcRequest.java | 2 +- .../pegasys/ethsigner/core/jsonrpc/JsonRpcRequestId.java | 2 +- .../java/tech/pegasys/ethsigner/core/jsonrpc/RpcUtil.java | 2 +- .../jsonrpc/exception/InvalidJsonRpcRequestException.java | 2 +- .../ethsigner/core/jsonrpc/exception/JsonRpcException.java | 2 +- .../ethsigner/core/jsonrpc/response/JsonRpcError.java | 2 +- .../core/jsonrpc/response/JsonRpcErrorResponse.java | 2 +- .../ethsigner/core/jsonrpc/response/JsonRpcResponse.java | 2 +- .../core/jsonrpc/response/JsonRpcResponseType.java | 2 +- .../core/jsonrpc/response/JsonRpcSuccessResponse.java | 2 +- .../core/requesthandler/JsonRpcRequestHandler.java | 2 +- .../ethsigner/core/requesthandler/ResultProvider.java | 2 +- .../core/requesthandler/VertxRequestTransmitter.java | 2 +- .../requesthandler/VertxRequestTransmitterFactory.java | 2 +- .../internalresponse/EthAccountsResultProvider.java | 2 +- .../internalresponse/InternalResponseHandler.java | 2 +- .../requesthandler/passthrough/PassThroughHandler.java | 2 +- .../core/requesthandler/sendtransaction/NonceProvider.java | 2 +- .../sendtransaction/NonceTooLowRetryMechanism.java | 2 +- .../requesthandler/sendtransaction/RetryMechanism.java | 2 +- .../sendtransaction/RetryingTransactionTransmitter.java | 2 +- .../sendtransaction/SendTransactionHandler.java | 2 +- .../sendtransaction/TransactionTransmitter.java | 2 +- .../transaction/BesuPrivateNonceProvider.java | 2 +- .../transaction/BesuPrivateTransaction.java | 2 +- .../sendtransaction/transaction/EeaPrivateTransaction.java | 2 +- .../sendtransaction/transaction/EthTransaction.java | 2 +- .../sendtransaction/transaction/PrivateTransaction.java | 2 +- .../sendtransaction/transaction/Transaction.java | 2 +- .../sendtransaction/transaction/TransactionFactory.java | 2 +- .../pegasys/ethsigner/core/signing/ChainIdProvider.java | 2 +- .../ethsigner/core/signing/ConfigurationChainId.java | 2 +- .../ethsigner/core/signing/TransactionSerializer.java | 2 +- .../core/jsonrpc/EeaSendTransactionJsonParametersTest.java | 2 +- .../core/jsonrpc/EthSendTransactionJsonParametersTest.java | 2 +- .../pegasys/ethsigner/core/jsonrpc/JsonRpcRequestTest.java | 2 +- .../core/jsonrpc/response/JsonRpcErrorResponseTest.java | 2 +- .../core/jsonrpcproxy/EthAccountsResultProviderTest.java | 2 +- .../sendtransaction/NonceTooLowRetryMechanismTest.java | 2 +- .../transaction/EeaPrivateTransactionTest.java | 2 +- .../sendtransaction/transaction/EthTransactionTest.java | 2 +- .../core/signing/SingleTransactionSignerProviderTest.java | 2 +- .../pegasys/ethsigner/jsonrpcproxy/RequestMapperTest.java | 2 +- .../pegasys/ethsigner/subcommands/AzureSubCommand.java | 2 +- .../pegasys/ethsigner/subcommands/CaviumSubCommand.java | 2 +- .../pegasys/ethsigner/subcommands/FileBasedSubCommand.java | 2 +- .../tech/pegasys/ethsigner/subcommands/HSMSubCommand.java | 2 +- .../pegasys/ethsigner/subcommands/HashicorpSubCommand.java | 2 +- .../pegasys/ethsigner/subcommands/MultiKeySubCommand.java | 7 +++++-- .../tech/pegasys/ethsigner/subcommands/RawSubCommand.java | 2 +- .../ethsigner/subcommands/CaviumSubCommandTest.java | 2 +- .../ethsigner/subcommands/FileBasedSubCommandTest.java | 2 +- .../pegasys/ethsigner/subcommands/HSMSubCommandTest.java | 2 +- .../ethsigner/subcommands/HashicorpSubCommandTest.java | 2 +- .../ethsigner/subcommands/MultiKeySubCommandTest.java | 2 +- gradle/check-licenses.gradle | 2 +- gradle/versions.gradle | 2 +- settings.gradle | 2 +- 172 files changed, 176 insertions(+), 173 deletions(-) diff --git a/acceptance-tests/build.gradle b/acceptance-tests/build.gradle index 35eb395be..ba9fbfd15 100644 --- a/acceptance-tests/build.gradle +++ b/acceptance-tests/build.gradle @@ -1,7 +1,7 @@ import groovy.text.SimpleTemplateEngine /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/AcceptanceTestBase.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/AcceptanceTestBase.java index 476e97223..40e59442f 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/AcceptanceTestBase.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/AcceptanceTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/CorsAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/CorsAcceptanceTest.java index 86598ca4f..7590d7a2b 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/CorsAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/CorsAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/DataPathFeatureFlagAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/DataPathFeatureFlagAcceptanceTest.java index 684451b76..362c73ed3 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/DataPathFeatureFlagAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/DataPathFeatureFlagAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/MethodNotFoundAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/MethodNotFoundAcceptanceTest.java index 280296232..331aaa9f6 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/MethodNotFoundAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/MethodNotFoundAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/UpCheckAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/UpCheckAcceptanceTest.java index e8113ecf3..e7900eb84 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/UpCheckAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/UpCheckAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/account/AccountManagementAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/account/AccountManagementAcceptanceTest.java index 40eda8494..84dfbcee9 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/account/AccountManagementAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/account/AccountManagementAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Account.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Account.java index 366c7302b..b182f3fe2 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Account.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Account.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Accounts.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Accounts.java index 5f5ddec3d..801ce3c8b 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Accounts.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Accounts.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Besu.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Besu.java index 2e4548d9d..cb89047c6 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Besu.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Besu.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Contracts.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Contracts.java index dcfd42e3f..6b9cbfb72 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Contracts.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Contracts.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eea.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eea.java index d8b0ef426..f58c5dbfd 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eea.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eea.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eth.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eth.java index a5030b997..97386f13b 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eth.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eth.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Gas.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Gas.java index 95d78fa1e..621c9a525 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Gas.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Gas.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateContracts.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateContracts.java index c315ec3be..5fb7131cc 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateContracts.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateContracts.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateTransaction.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateTransaction.java index 1017b5a98..7d16bfc81 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateTransaction.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PublicContracts.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PublicContracts.java index 25a71d35d..086455f6b 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PublicContracts.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PublicContracts.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequestFactory.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequestFactory.java index a0aa4b4ee..8ef29194d 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequestFactory.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequestFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequests.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequests.java index b14348880..2b92f5905 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequests.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Transactions.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Transactions.java index f93264f7b..42db63efd 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Transactions.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Transactions.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/http/HttpRequest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/http/HttpRequest.java index a6184566b..b582c8179 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/http/HttpRequest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/http/HttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/http/HttpResponse.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/http/HttpResponse.java index b38faf4ae..3d29624fa 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/http/HttpResponse.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/http/HttpResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/Node.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/Node.java index d75c1d375..950d0ba6e 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/Node.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/Node.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/besu/BesuNodePorts.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/besu/BesuNodePorts.java index e4a01c5f8..1b7a1e7ed 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/besu/BesuNodePorts.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/besu/BesuNodePorts.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/EthSignerProcessRunner.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/EthSignerProcessRunner.java index 8d1b123ae..58fd11fe3 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/EthSignerProcessRunner.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/EthSignerProcessRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/Signer.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/Signer.java index 74e22a12f..5cfc5c883 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/Signer.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/Signer.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfiguration.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfiguration.java index 3f88d933e..c663e6622 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfiguration.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfigurationBuilder.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfigurationBuilder.java index 56eed46c9..6dda7af41 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfigurationBuilder.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfigurationBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerResponse.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerResponse.java index eec8901a5..c6eeccf7b 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerResponse.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/TransactionSignerParamsSupplier.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/TransactionSignerParamsSupplier.java index 489c06d99..837ad3da6 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/TransactionSignerParamsSupplier.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/TransactionSignerParamsSupplier.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/BasicTlsOptions.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/BasicTlsOptions.java index 9b5048545..99de04b1a 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/BasicTlsOptions.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/BasicTlsOptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/ClientTlsConfig.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/ClientTlsConfig.java index c5f34fbdd..906a3e87d 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/ClientTlsConfig.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/ClientTlsConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/OkHttpClientHelpers.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/OkHttpClientHelpers.java index 9c8f4b2fe..3b1af9dc1 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/OkHttpClientHelpers.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/OkHttpClientHelpers.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/TlsCertificateDefinition.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/TlsCertificateDefinition.java index e458c28fc..13c65ab25 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/TlsCertificateDefinition.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/TlsCertificateDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/ExceptionUtils.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/ExceptionUtils.java index 79f9d7226..74fecc112 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/ExceptionUtils.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/ExceptionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/Hex.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/Hex.java index 6e1cf7bf6..fb4fc65c7 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/Hex.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/Hex.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/WaitUtils.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/WaitUtils.java index 04d9f7e84..95b5fcb2e 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/WaitUtils.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/WaitUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/AzureBasedTomlLoadingAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/AzureBasedTomlLoadingAcceptanceTest.java index b78ccd4a1..4b9aaf408 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/AzureBasedTomlLoadingAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/AzureBasedTomlLoadingAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/FileBasedTomlLoadingAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/FileBasedTomlLoadingAcceptanceTest.java index 2aa44803b..5fe431f13 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/FileBasedTomlLoadingAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/FileBasedTomlLoadingAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/HashicorpBasedTomlLoadingAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/HashicorpBasedTomlLoadingAcceptanceTest.java index be124abf2..9fc146a9b 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/HashicorpBasedTomlLoadingAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/HashicorpBasedTomlLoadingAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/HashicorpTlsBasedTomlLoadingAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/HashicorpTlsBasedTomlLoadingAcceptanceTest.java index e034f64f3..457ac9f77 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/HashicorpTlsBasedTomlLoadingAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/HashicorpTlsBasedTomlLoadingAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/MultiKeyAcceptanceTestBase.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/MultiKeyAcceptanceTestBase.java index f627d8074..5dd104a78 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/MultiKeyAcceptanceTestBase.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/MultiKeyAcceptanceTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/MultiKeySigningAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/MultiKeySigningAcceptanceTest.java index d0566b9a4..26e517ab9 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/MultiKeySigningAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/MultiKeySigningAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyAzureTransactionSignerAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyAzureTransactionSignerAcceptanceTest.java index ce5325841..48564308a 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyAzureTransactionSignerAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyAzureTransactionSignerAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyFileBasedTransactionSignerAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyFileBasedTransactionSignerAcceptanceTest.java index 8e770c92a..049c696e9 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyFileBasedTransactionSignerAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyFileBasedTransactionSignerAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyHashicorpTransactionSignerAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyHashicorpTransactionSignerAcceptanceTest.java index a499a8b18..6bd25c599 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyHashicorpTransactionSignerAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyHashicorpTransactionSignerAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyTransactionSigningAcceptanceTestBase.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyTransactionSigningAcceptanceTestBase.java index bb2ae7bbc..b9ae5f2ea 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyTransactionSigningAcceptanceTestBase.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyTransactionSigningAcceptanceTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/proxy/PassThroughAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/proxy/PassThroughAcceptanceTest.java index fe0acf407..f50b05d4c 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/proxy/PassThroughAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/proxy/PassThroughAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/PrivateTransactionAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/PrivateTransactionAcceptanceTest.java index be3148c48..4fac30431 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/PrivateTransactionAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/PrivateTransactionAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ReplayProtectionAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ReplayProtectionAcceptanceTest.java index f4bca6e1f..1a0cf672c 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ReplayProtectionAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ReplayProtectionAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/SmartContractAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/SmartContractAcceptanceTest.java index 4b03ae3d3..7c1ab029f 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/SmartContractAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/SmartContractAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferAcceptanceTest.java index 169c09dfa..3484ab9ff 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferWithAzureAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferWithAzureAcceptanceTest.java index 44e8e515f..3b3012c31 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferWithAzureAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferWithAzureAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/contract/SimpleStorage.sol b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/contract/SimpleStorage.sol index 12a1ffd7c..829812a9d 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/contract/SimpleStorage.sol +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/contract/SimpleStorage.sol @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/contract/generated/SimpleStorage.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/contract/generated/SimpleStorage.java index f48966902..cf94118c9 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/contract/generated/SimpleStorage.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/contract/generated/SimpleStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/hashicorp/ValueTransferWithHashicorpAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/hashicorp/ValueTransferWithHashicorpAcceptanceTest.java index e8df977cc..ce4a6fafb 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/hashicorp/ValueTransferWithHashicorpAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/hashicorp/ValueTransferWithHashicorpAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/hashicorp/ValueTransferWithHashicorpOnTlsAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/hashicorp/ValueTransferWithHashicorpOnTlsAcceptanceTest.java index d6dd03133..9e1b5d3dd 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/hashicorp/ValueTransferWithHashicorpOnTlsAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/hashicorp/ValueTransferWithHashicorpOnTlsAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/timeouts/FailedConnectionAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/timeouts/FailedConnectionAcceptanceTest.java index 5f0259cdb..2ef35a57f 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/timeouts/FailedConnectionAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/timeouts/FailedConnectionAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/timeouts/ReadTimeoutAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/timeouts/ReadTimeoutAcceptanceTest.java index 2576652e2..699d46cc9 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/timeouts/ReadTimeoutAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/timeouts/ReadTimeoutAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/tls/ServerSideTlsAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/tls/ServerSideTlsAcceptanceTest.java index a7d19c761..9cde42b9f 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/tls/ServerSideTlsAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/tls/ServerSideTlsAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/build.gradle b/build.gradle index e30154a4e..88fa7e52c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/app/build.gradle b/ethsigner/app/build.gradle index 836de20c2..094ce85b4 100644 --- a/ethsigner/app/build.gradle +++ b/ethsigner/app/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/app/src/main/java/tech/pegasys/ethsigner/EthSignerApp.java b/ethsigner/app/src/main/java/tech/pegasys/ethsigner/EthSignerApp.java index 8492650b0..6665f0ace 100644 --- a/ethsigner/app/src/main/java/tech/pegasys/ethsigner/EthSignerApp.java +++ b/ethsigner/app/src/main/java/tech/pegasys/ethsigner/EthSignerApp.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/build.gradle b/ethsigner/build.gradle index d4a46e015..5e01de30f 100644 --- a/ethsigner/build.gradle +++ b/ethsigner/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/ApplicationInfo.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/ApplicationInfo.java index b45d8d424..95ca3da54 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/ApplicationInfo.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/ApplicationInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/CommandlineParser.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/CommandlineParser.java index 98f6341d2..488266d1f 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/CommandlineParser.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/CommandlineParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/EthSignerBaseCommand.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/EthSignerBaseCommand.java index 3765afd60..3e37f76ee 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/EthSignerBaseCommand.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/EthSignerBaseCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/PlatformDetector.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/PlatformDetector.java index 23e284adb..7795f9902 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/PlatformDetector.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/PlatformDetector.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/SignerSubCommand.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/SignerSubCommand.java index 0849d7883..1aa334a90 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/SignerSubCommand.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/SignerSubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/VersionInfo.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/VersionInfo.java index 59167ee26..d7b2a8347 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/VersionInfo.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/VersionInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/VersionProvider.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/VersionProvider.java index ac2a2aef0..a28d8e652 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/VersionProvider.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/VersionProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/test-support/java/tech/pegasys/ethsigner/CmdlineHelpers.java b/ethsigner/commandline/src/test-support/java/tech/pegasys/ethsigner/CmdlineHelpers.java index 7ce0ce1cf..e62ba9b3f 100644 --- a/ethsigner/commandline/src/test-support/java/tech/pegasys/ethsigner/CmdlineHelpers.java +++ b/ethsigner/commandline/src/test-support/java/tech/pegasys/ethsigner/CmdlineHelpers.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/CommandlineParserClientTlsOptionsTest.java b/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/CommandlineParserClientTlsOptionsTest.java index 55e17722a..11ae625cf 100644 --- a/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/CommandlineParserClientTlsOptionsTest.java +++ b/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/CommandlineParserClientTlsOptionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/CommandlineParserTest.java b/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/CommandlineParserTest.java index 2a060589b..530f3f97d 100644 --- a/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/CommandlineParserTest.java +++ b/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/CommandlineParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/NullSignerSubCommand.java b/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/NullSignerSubCommand.java index e8cdbbc46..4af6e374d 100644 --- a/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/NullSignerSubCommand.java +++ b/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/NullSignerSubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/build.gradle b/ethsigner/core/build.gradle index aa486b881..ed3e8c867 100644 --- a/ethsigner/core/build.gradle +++ b/ethsigner/core/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/DefaultTestBase.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/DefaultTestBase.java index 3e8a907ec..db0654843 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/DefaultTestBase.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/DefaultTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/EthAccountsIntegrationTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/EthAccountsIntegrationTest.java index ada94373f..fe00da552 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/EthAccountsIntegrationTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/EthAccountsIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/EthSignerParsingIntegrationTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/EthSignerParsingIntegrationTest.java index 1f28d236a..1a2d3166f 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/EthSignerParsingIntegrationTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/EthSignerParsingIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/FailedConnectionIntegrationTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/FailedConnectionIntegrationTest.java index 806bded9b..5a2570eb9 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/FailedConnectionIntegrationTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/FailedConnectionIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IntegrationTestBase.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IntegrationTestBase.java index bbae84f8c..03115d847 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IntegrationTestBase.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IntegrationTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/ProxyIntegrationTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/ProxyIntegrationTest.java index b24be3cdc..f73c51ffb 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/ProxyIntegrationTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/ProxyIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEeaSendTransactionIntegrationTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEeaSendTransactionIntegrationTest.java index 37897cae2..755cd88ff 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEeaSendTransactionIntegrationTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEeaSendTransactionIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEthSendTransactionIntegrationTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEthSendTransactionIntegrationTest.java index 5ca3ba5a4..9df36803e 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEthSendTransactionIntegrationTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEthSendTransactionIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEthSendTransactionWithChainIdIntegrationTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEthSendTransactionWithChainIdIntegrationTest.java index b8c41e218..ea7b5db03 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEthSendTransactionWithChainIdIntegrationTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEthSendTransactionWithChainIdIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/TimeoutTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/TimeoutTest.java index 6f136e8b8..90086e637 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/TimeoutTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/TimeoutTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EeaSendRawTransaction.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EeaSendRawTransaction.java index d5ea9ac9d..ac549a1ff 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EeaSendRawTransaction.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EeaSendRawTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EeaSendTransaction.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EeaSendTransaction.java index d21e6842c..999459067 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EeaSendTransaction.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EeaSendTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EthProtocolVersionRequest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EthProtocolVersionRequest.java index f9a9562e2..653321b2e 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EthProtocolVersionRequest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EthProtocolVersionRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/PrivateTransaction.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/PrivateTransaction.java index 494324b00..837ce74f8 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/PrivateTransaction.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/PrivateTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/SendRawTransaction.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/SendRawTransaction.java index 29108bb8c..8881b7e9c 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/SendRawTransaction.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/SendRawTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/SendTransaction.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/SendTransaction.java index f6bf7e1b1..6af4c0b08 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/SendTransaction.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/SendTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/Transaction.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/Transaction.java index 8a629df51..4efbfde01 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/Transaction.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/Transaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthNodeRequest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthNodeRequest.java index 56d1cb915..a231285bf 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthNodeRequest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthNodeRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthRequestFactory.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthRequestFactory.java index e398d9f07..ed1498cc8 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthRequestFactory.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthRequestFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthSignerRequest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthSignerRequest.java index 7c68fd985..8b81de0fa 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthSignerRequest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthSignerRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthNodeResponse.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthNodeResponse.java index 0e696b0a5..1865a7285 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthNodeResponse.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthNodeResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthResponseFactory.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthResponseFactory.java index 2076f4d92..f4829ecbb 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthResponseFactory.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthResponseFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthSignerResponse.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthSignerResponse.java index fcd7c52c6..8d5bbd270 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthSignerResponse.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthSignerResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/support/TransactionCountResponder.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/support/TransactionCountResponder.java index 2ad59b0b2..c3dd0a3aa 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/support/TransactionCountResponder.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/support/TransactionCountResponder.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java index f2fd4e2f5..4709c2047 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/InitializationException.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/InitializationException.java index 6f1569969..10314c555 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/InitializationException.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/InitializationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java index 99baf807e..61c8d3f0e 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/Config.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/Config.java index 2a88c91c7..fd6db820a 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/Config.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/Config.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/TlsOptions.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/TlsOptions.java index d9c799962..598ca7797 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/TlsOptions.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/TlsOptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/HttpResponseFactory.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/HttpResponseFactory.java index bd312f285..fa6a7109b 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/HttpResponseFactory.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/HttpResponseFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/JsonRpcErrorHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/JsonRpcErrorHandler.java index c4203796b..99b6da080 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/JsonRpcErrorHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/JsonRpcErrorHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/JsonRpcHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/JsonRpcHandler.java index 3fd100ce7..b5b64e07a 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/JsonRpcHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/JsonRpcHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/LogErrorHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/LogErrorHandler.java index a0f794aae..e3d02cbe3 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/LogErrorHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/LogErrorHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/RequestMapper.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/RequestMapper.java index 2657110d3..647fbdacc 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/RequestMapper.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/RequestMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/UpcheckHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/UpcheckHandler.java index c817ccae1..61bef0185 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/UpcheckHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/UpcheckHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EeaSendTransactionJsonParameters.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EeaSendTransactionJsonParameters.java index 8db18b12b..9c6aec78c 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EeaSendTransactionJsonParameters.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EeaSendTransactionJsonParameters.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParameters.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParameters.java index ccbbb659c..49c4f7d85 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParameters.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParameters.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonDecoder.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonDecoder.java index 3d25e175f..905ab83f1 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonDecoder.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequest.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequest.java index ba7c4e0c5..cb3db9d86 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequest.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequestId.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequestId.java index 06db7d738..e09d6c993 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequestId.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequestId.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/RpcUtil.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/RpcUtil.java index 4ee78eee2..d2be13cd4 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/RpcUtil.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/RpcUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/exception/InvalidJsonRpcRequestException.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/exception/InvalidJsonRpcRequestException.java index 9c4a8678e..fd97c3939 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/exception/InvalidJsonRpcRequestException.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/exception/InvalidJsonRpcRequestException.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/exception/JsonRpcException.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/exception/JsonRpcException.java index 855d68699..a7f73fbd6 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/exception/JsonRpcException.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/exception/JsonRpcException.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcError.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcError.java index 63475cce4..30f16d6e9 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcError.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcError.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcErrorResponse.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcErrorResponse.java index 7029c0452..604a56139 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcErrorResponse.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcErrorResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcResponse.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcResponse.java index 5541dcef4..fe6086909 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcResponse.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcResponseType.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcResponseType.java index 12086eaeb..4ec22db67 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcResponseType.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcResponseType.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcSuccessResponse.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcSuccessResponse.java index 0e59dcfcd..ed36938fe 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcSuccessResponse.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcSuccessResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/JsonRpcRequestHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/JsonRpcRequestHandler.java index ad025820e..9ee0aafba 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/JsonRpcRequestHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/JsonRpcRequestHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/ResultProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/ResultProvider.java index a66502e76..22501eee3 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/ResultProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/ResultProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitter.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitter.java index c9fe1f346..4e7e3496b 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitter.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitter.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitterFactory.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitterFactory.java index ace37f2a9..272232819 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitterFactory.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthAccountsResultProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthAccountsResultProvider.java index 4f0f5ef62..8750ebc48 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthAccountsResultProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthAccountsResultProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/InternalResponseHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/InternalResponseHandler.java index 587e08cf1..311c2f435 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/InternalResponseHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/InternalResponseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/passthrough/PassThroughHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/passthrough/PassThroughHandler.java index ed3471f5d..9de24a537 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/passthrough/PassThroughHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/passthrough/PassThroughHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceProvider.java index 1cccef60c..a73f53d3d 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceTooLowRetryMechanism.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceTooLowRetryMechanism.java index 435188d75..434b92b8a 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceTooLowRetryMechanism.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceTooLowRetryMechanism.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/RetryMechanism.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/RetryMechanism.java index 12f4019d1..0e6a7e67b 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/RetryMechanism.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/RetryMechanism.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/RetryingTransactionTransmitter.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/RetryingTransactionTransmitter.java index 69dff6e51..e0a96e916 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/RetryingTransactionTransmitter.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/RetryingTransactionTransmitter.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java index 495112933..d3a700175 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/TransactionTransmitter.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/TransactionTransmitter.java index 6a85b316f..0a6ed2b51 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/TransactionTransmitter.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/TransactionTransmitter.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/BesuPrivateNonceProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/BesuPrivateNonceProvider.java index bc6f08c61..41972b785 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/BesuPrivateNonceProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/BesuPrivateNonceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/BesuPrivateTransaction.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/BesuPrivateTransaction.java index 4d00ee316..4f8983b79 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/BesuPrivateTransaction.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/BesuPrivateTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EeaPrivateTransaction.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EeaPrivateTransaction.java index bd935244b..5706e0675 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EeaPrivateTransaction.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EeaPrivateTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransaction.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransaction.java index 573c934ee..bbc056ba0 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransaction.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/PrivateTransaction.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/PrivateTransaction.java index 3fc843fc9..2cac74f68 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/PrivateTransaction.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/PrivateTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/Transaction.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/Transaction.java index 2629fcb91..ba1d73d4c 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/Transaction.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/Transaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/TransactionFactory.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/TransactionFactory.java index 8ba60e712..bf9c10c98 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/TransactionFactory.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/TransactionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/ChainIdProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/ChainIdProvider.java index f36baea14..84d8fe71b 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/ChainIdProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/ChainIdProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/ConfigurationChainId.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/ConfigurationChainId.java index d7cd07cc8..4b1939556 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/ConfigurationChainId.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/ConfigurationChainId.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/TransactionSerializer.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/TransactionSerializer.java index beac3cdc8..fa9d64e26 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/TransactionSerializer.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/TransactionSerializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/EeaSendTransactionJsonParametersTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/EeaSendTransactionJsonParametersTest.java index 92c72c43a..5aba2e685 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/EeaSendTransactionJsonParametersTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/EeaSendTransactionJsonParametersTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParametersTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParametersTest.java index 05f30e313..440dd6ae7 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParametersTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParametersTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequestTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequestTest.java index 05c6036fa..b88f6c206 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequestTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequestTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcErrorResponseTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcErrorResponseTest.java index f9e0242df..a2d6d9090 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcErrorResponseTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcErrorResponseTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthAccountsResultProviderTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthAccountsResultProviderTest.java index 2e99fc531..75106401c 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthAccountsResultProviderTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthAccountsResultProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceTooLowRetryMechanismTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceTooLowRetryMechanismTest.java index 84ba3708b..44847afc6 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceTooLowRetryMechanismTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceTooLowRetryMechanismTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EeaPrivateTransactionTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EeaPrivateTransactionTest.java index ac9b2424b..126d83462 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EeaPrivateTransactionTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EeaPrivateTransactionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransactionTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransactionTest.java index 8295c6a91..59fb50b5b 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransactionTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransactionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/signing/SingleTransactionSignerProviderTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/signing/SingleTransactionSignerProviderTest.java index f79c6dab1..02b44a7c0 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/signing/SingleTransactionSignerProviderTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/signing/SingleTransactionSignerProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/jsonrpcproxy/RequestMapperTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/jsonrpcproxy/RequestMapperTest.java index 5a7d1f7ab..b7ded65d6 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/jsonrpcproxy/RequestMapperTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/jsonrpcproxy/RequestMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/AzureSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/AzureSubCommand.java index f359c75ae..dca7c8cb5 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/AzureSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/AzureSubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommand.java index 5509d4c37..5a727cdde 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommand.java index d5ba7278a..fa61b7cb8 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HSMSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HSMSubCommand.java index 8de483798..2458435fe 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HSMSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HSMSubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommand.java index b92b9692d..72c221e73 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java index 1fed235d3..36b3f3e1e 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -91,6 +91,9 @@ Path getDirectoryPath() { @Override public String toString() { - return MoreObjects.toStringHelper(this).add("directoryPath", directoryPath).toString(); + return MoreObjects.toStringHelper(this) + .add("directoryPath", directoryPath) + .add("configPath", configPath) + .toString(); } } diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java index 5b2bb3253..49fab249a 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommandTest.java b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommandTest.java index 9604b9f74..04c465951 100644 --- a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommandTest.java +++ b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/CaviumSubCommandTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommandTest.java b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommandTest.java index 8a79f6a5e..6e4c38e97 100644 --- a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommandTest.java +++ b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommandTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/HSMSubCommandTest.java b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/HSMSubCommandTest.java index 090e7eb32..a840bea06 100644 --- a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/HSMSubCommandTest.java +++ b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/HSMSubCommandTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommandTest.java b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommandTest.java index 508249b80..a4311b35b 100644 --- a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommandTest.java +++ b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommandTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommandTest.java b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommandTest.java index 6a028ec5d..e9932a649 100644 --- a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommandTest.java +++ b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommandTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/gradle/check-licenses.gradle b/gradle/check-licenses.gradle index 1b97d7c95..7f27bb966 100644 --- a/gradle/check-licenses.gradle +++ b/gradle/check-licenses.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/gradle/versions.gradle b/gradle/versions.gradle index 68492d734..ce92a0a8d 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/settings.gradle b/settings.gradle index 28516f0b2..92217dc84 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2019 ConsenSys AG. + * Copyright 2020 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at From 0d8690dfcc434329ac3f26e09df6e78ba6c49133 Mon Sep 17 00:00:00 2001 From: Joshua Fernandes Date: Thu, 22 Oct 2020 15:45:41 +1000 Subject: [PATCH 44/64] updating docker namespace (#342) --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index f914c2e22..c0eb2a20c 100644 --- a/build.gradle +++ b/build.gradle @@ -480,7 +480,7 @@ tasks.register("dockerDistUntar") { task distDocker(type: Exec) { dependsOn dockerDistUntar def dockerBuildVersion = project.hasProperty('release.releaseVersion') ? project.property('release.releaseVersion') : "${rootProject.version}" - def imageName = "quorumengineering/" + repositoryName + def imageName = "consensys/quorum-" + repositoryName def image = project.hasProperty('release.releaseVersion') ? "${imageName}:" + project.property('release.releaseVersion') : "${imageName}:${project.version}" def dockerBuildDir = "build/docker-" + repositoryName + "/" workingDir "${dockerBuildDir}" @@ -499,7 +499,7 @@ task distDocker(type: Exec) { task testDocker(type: Exec) { dependsOn distDocker def dockerReportsDir = "docker/reports/" - def imageName = "quorumengineering/" + repositoryName + def imageName = "consensys/quorum-" + repositoryName def image = project.hasProperty('release.releaseVersion') ? "${imageName}:" + project.property('release.releaseVersion') : "${imageName}:${project.version}" workingDir "docker" @@ -513,7 +513,7 @@ task testDocker(type: Exec) { task dockerUpload(type: Exec) { dependsOn distDocker - def imageName = "quorumengineering/" + repositoryName + def imageName = "consensys/quorum-" + repositoryName def image = project.hasProperty('release.releaseVersion') ? "${imageName}:" + project.property('release.releaseVersion') : "${imageName}:${project.version}" def cmd = "docker push '${image}'" From 694af4b4d80e51a02150c2b678d8a786fff292f8 Mon Sep 17 00:00:00 2001 From: Trent Mohay <37158202+rain-on@users.noreply.github.com> Date: Thu, 22 Oct 2020 17:45:33 +1100 Subject: [PATCH 45/64] Update changelog for 20.10.0 (#343) --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cf08c919..ee292434d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,15 @@ # Changelog -## Unreleased +## 20.10.0 ### Features Added - Added "eth_signTransaction" JSON RPC +- Docker namespace updated to reflect "consensys/quorum-ethsigner" +- Strip ACCESS_CONTROL_ALLOW_ORIGIN header from responses received from the web3provider +- Added a Prometheus metrics endpoint, reporting basic application metrics + +### Bugs Fixed +- Transactions were not being handled in parallel, triggering poor performance under load ## 0.7.1 From 29b640d77f3237dc7ed92b79d7f4b27d72db984d Mon Sep 17 00:00:00 2001 From: Trent Mohay <37158202+rain-on@users.noreply.github.com> Date: Thu, 22 Oct 2020 17:53:02 +1100 Subject: [PATCH 46/64] 20.10.0 release (#344) Signed-off-by: Trent Mohay --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index bc83ab5dd..45c291b86 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1g -version=0.7.2-SNAPSHOT +version=20.10.0 besuVersion=1.5.0 besuDistroUrl=https://bintray.com/hyperledger-org/besu-repo/download_file?file_path=besu-${besuVersion}.tar.gz hashicorpVaultVersion=1.4.3 From 4a5587834de1273cf7c9105de36086ca295fb22b Mon Sep 17 00:00:00 2001 From: Trent Mohay <37158202+rain-on@users.noreply.github.com> Date: Fri, 23 Oct 2020 08:10:21 +1100 Subject: [PATCH 47/64] Update version to 20.10.1-SNAPSHOT (#345) Signed-off-by: Trent Mohay --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 45c291b86..d7229c245 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1g -version=20.10.0 +version=20.10.1-SNAPSHOT besuVersion=1.5.0 besuDistroUrl=https://bintray.com/hyperledger-org/besu-repo/download_file?file_path=besu-${besuVersion}.tar.gz hashicorpVaultVersion=1.4.3 From caf7603cf9109d2603cf05f363eb7d55ae04e934 Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Fri, 8 Jan 2021 13:46:55 +1000 Subject: [PATCH 48/64] Improve error message if the http server fails to start (#347) --- .../src/main/java/tech/pegasys/ethsigner/core/EthSigner.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java index 4c9f5c585..07bd1593d 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java @@ -99,7 +99,8 @@ public void run() { runner.start(); } catch (final Throwable t) { vertx.close(); - throw new InitializationException("Failed to create http service.", t); + throw new InitializationException( + "Failed to create http service due to " + t.getMessage(), t); } } From 1732e415aaa256551e2d1a95662cce25e3b6d974 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Fri, 8 Jan 2021 14:08:42 +0200 Subject: [PATCH 49/64] Sync with upstream --- gradle/versions.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/versions.gradle b/gradle/versions.gradle index 8d4dd0e35..d4a4ed951 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -70,7 +70,7 @@ dependencyManagement { dependency 'org.web3j:core:4.5.14' dependency 'org.web3j:crypto:4.5.14' - dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.11-ADHARA-SNAPSHOT') { + dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.15-ADHARA-SNAPSHOT') { entry 'keystorage-hashicorp' entry 'keystorage-hsm' entry 'keystorage-cavium' From 8b9414d3e394b979f7347913c89b8b41206fec49 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Wed, 27 Jan 2021 12:48:34 +1000 Subject: [PATCH 50/64] update links to consensys.net (#348) Signed-off-by: Sally MacFarlane --- CONTRIBUTING.md | 4 ++-- README.md | 2 +- docs/README.md | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index be3b71524..49bd4b69f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -244,8 +244,8 @@ These are not strictly enforced during the build, but should be adhered to and c [private-quorum@consensys.net]: mailto:private-quorum@consensys.net [Discord]: https://discord.gg/5U9Jwp7 -[Github issues]: https://github.com/PegaSysEng/ethsigner/issues -[EthSigner documentation]: https://docs.ethsigner.pegasys.tech/ +[GitHub issues]: https://github.com/ConsenSys/ethsigner/issues +[EthSigner documentation]: https://docs.ethsigner.consensys.net/ [CLA.md]: /CLA.md [Code Reviews]: /community/code-reviews.md [MkDocs]: https://www.mkdocs.org/ diff --git a/README.md b/README.md index 6fd25f139..ff4999ac3 100644 --- a/README.md +++ b/README.md @@ -22,4 +22,4 @@ See our [contribution guidelines](CONTRIBUTING.md) for more detail on searching * [Release Notes](CHANGELOG.md) [Discord]: https://discord.gg/5U9Jwp7 -[Github issues]: https://github.com/PegaSysEng/ethsigner/issues +[GitHub issues]: https://github.com/ConsenSys/ethsigner/issues diff --git a/docs/README.md b/docs/README.md index 882a62411..04354312f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,5 @@ -EthSigner documentation was moved to as separate repository to help manage versions and releases. +EthSigner documentation is maintained as a separate repository to help manage versions and releases. -If you want to contribute to the doc site, make a pull request against https://github.com/PegaSysEng/doc.ethsigner +If you want to contribute to the doc site, make a pull request against https://github.com/ConsenSys/doc.ethsigner -The generated doc website is at https://docs.ethsigner.pegasys.tech/ \ No newline at end of file +The generated doc website is at https://docs.ethsigner.consensys.net/ \ No newline at end of file From d13f083de4c340d8c166152e9fc397738fb9d7e5 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Tue, 16 Feb 2021 11:04:07 +1000 Subject: [PATCH 51/64] added GoQuorum private transaction type (#349) * round trip to get enclave lookup ID from besu is working * call eth_sendRawPrivateTransaction once we have the enclave lookup id * refactor updateNonce() Signed-off-by: Sally MacFarlane --- .../EthSendTransactionJsonParameters.java | 25 ++++ .../ethsigner/core/jsonrpc/RpcUtil.java | 15 ++ .../EnclaveLookupIdProvider.java | 19 +++ .../TransactionTransmitter.java | 14 +- .../transaction/EthTransaction.java | 23 ++- .../GoQuorumPrivateTransaction.java | 140 ++++++++++++++++++ .../transaction/PrivateTransaction.java | 15 +- .../StoreRawEnclaveLookupIdProvider.java | 39 +++++ .../transaction/Transaction.java | 6 +- .../transaction/TransactionFactory.java | 21 ++- .../VertxNonceRequestTransmitter.java | 48 +++--- .../VertxStoreRawRequestTransmitter.java | 128 ++++++++++++++++ .../GoQuorumEthTransactionTest.java | 110 ++++++++++++++ 13 files changed, 551 insertions(+), 52 deletions(-) create mode 100644 ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/EnclaveLookupIdProvider.java create mode 100644 ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/GoQuorumPrivateTransaction.java create mode 100644 ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/StoreRawEnclaveLookupIdProvider.java create mode 100644 ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/VertxStoreRawRequestTransmitter.java create mode 100644 ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/GoQuorumEthTransactionTest.java diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParameters.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParameters.java index ccbbb659c..31a116170 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParameters.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParameters.java @@ -16,12 +16,16 @@ import static tech.pegasys.ethsigner.core.jsonrpc.RpcUtil.validateNotEmpty; import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSetter; +import org.web3j.utils.Base64String; @JsonIgnoreProperties(ignoreUnknown = true) public class EthSendTransactionJsonParameters { @@ -32,6 +36,8 @@ public class EthSendTransactionJsonParameters { private BigInteger value; private String receiver; private String data; + private Base64String privateFrom; + private List privateFor; @JsonCreator public EthSendTransactionJsonParameters(@JsonProperty("from") final String sender) { @@ -69,6 +75,17 @@ public void data(final String data) { this.data = data; } + @JsonSetter("privateFrom") + public void privateFrom(final String privateFrom) { + this.privateFrom = Base64String.wrap(privateFrom); + } + + @JsonSetter("privateFor") + public void privateFor(final String[] privateFor) { + this.privateFor = + Arrays.stream(privateFor).map(Base64String::wrap).collect(Collectors.toList()); + } + public Optional data() { return Optional.ofNullable(data); } @@ -96,4 +113,12 @@ public Optional nonce() { public String sender() { return sender; } + + public Optional privateFrom() { + return Optional.ofNullable(privateFrom); + } + + public Optional> privateFor() { + return Optional.ofNullable(privateFor); + } } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/RpcUtil.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/RpcUtil.java index 4ee78eee2..ea5d713a7 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/RpcUtil.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/RpcUtil.java @@ -14,9 +14,14 @@ import static org.web3j.utils.Numeric.decodeQuantity; +import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError; +import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcErrorResponse; + import java.math.BigInteger; import java.util.List; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.json.DecodeException; import io.vertx.core.json.JsonObject; public class RpcUtil { @@ -54,4 +59,14 @@ static void validateNotEmpty(final String value) { static BigInteger decodeBigInteger(final String value) { return value == null ? null : decodeQuantity(value); } + + public static JsonRpcError determineErrorCode(final String body, final JsonDecoder decoder) { + try { + final JsonRpcErrorResponse response = + decoder.decodeValue(Buffer.buffer(body), JsonRpcErrorResponse.class); + return response.getError(); + } catch (final DecodeException e) { + return JsonRpcError.INTERNAL_ERROR; + } + } } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/EnclaveLookupIdProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/EnclaveLookupIdProvider.java new file mode 100644 index 000000000..245f5d17f --- /dev/null +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/EnclaveLookupIdProvider.java @@ -0,0 +1,19 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.core.requesthandler.sendtransaction; + +@FunctionalInterface +public interface EnclaveLookupIdProvider { + + String getLookupId(final String payload); +} diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/TransactionTransmitter.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/TransactionTransmitter.java index 6a85b316f..f62d78832 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/TransactionTransmitter.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/TransactionTransmitter.java @@ -54,7 +54,7 @@ public TransactionTransmitter( } public void send() { - final Optional request = createSignedTransactionBody(); + final Optional request = createSignedTransactionPayload(); if (request.isEmpty()) { return; @@ -68,12 +68,10 @@ public void send() { } } - private Optional createSignedTransactionBody() { + private Optional createSignedTransactionPayload() { - if (!transaction.isNonceUserSpecified()) { - if (!populateNonce()) { - return Optional.empty(); - } + if (!populateNonce()) { + return Optional.empty(); } final String signedTransactionHexString; @@ -94,12 +92,12 @@ private Optional createSignedTransactionBody() { private boolean populateNonce() { try { - transaction.updateNonce(); + transaction.updateFieldsIfRequired(); return true; } catch (final RuntimeException e) { // It is currently recognised that the underlying nonce provider will wrap a transmission // exception in a Runtime exception. - LOG.warn("Unable to get nonce from web3j provider.", e); + LOG.warn("Unable to get nonce (or enclave lookup id) from web3j provider.", e); this.handleFailure(e.getCause()); } catch (final Throwable thrown) { LOG.debug("Failed to encode/serialize transaction: {}", transaction, thrown); diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransaction.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransaction.java index 573c934ee..0ddcb7406 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransaction.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransaction.java @@ -21,6 +21,7 @@ import java.util.List; import com.google.common.base.MoreObjects; +import org.jetbrains.annotations.NotNull; import org.web3j.crypto.RawTransaction; import org.web3j.crypto.Sign.SignatureData; import org.web3j.crypto.TransactionEncoder; @@ -31,10 +32,10 @@ public class EthTransaction implements Transaction { private static final String JSON_RPC_METHOD = "eth_sendRawTransaction"; - private final EthSendTransactionJsonParameters transactionJsonParameters; - private final NonceProvider nonceProvider; - private final JsonRpcRequestId id; - private BigInteger nonce; + protected final EthSendTransactionJsonParameters transactionJsonParameters; + protected final NonceProvider nonceProvider; + protected final JsonRpcRequestId id; + protected BigInteger nonce; public EthTransaction( final EthSendTransactionJsonParameters transactionJsonParameters, @@ -47,8 +48,16 @@ public EthTransaction( } @Override - public void updateNonce() { - this.nonce = nonceProvider.getNonce(); + public void updateFieldsIfRequired() { + if (!this.isNonceUserSpecified()) { + this.nonce = nonceProvider.getNonce(); + } + } + + @Override + @NotNull + public String getJsonRpcMethodName() { + return JSON_RPC_METHOD; } @Override @@ -90,7 +99,7 @@ public String toString() { .toString(); } - private RawTransaction createTransaction() { + protected RawTransaction createTransaction() { return RawTransaction.createTransaction( nonce, transactionJsonParameters.gasPrice().orElse(DEFAULT_GAS_PRICE), diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/GoQuorumPrivateTransaction.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/GoQuorumPrivateTransaction.java new file mode 100644 index 000000000..cca5b063c --- /dev/null +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/GoQuorumPrivateTransaction.java @@ -0,0 +1,140 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction; + +import static tech.pegasys.ethsigner.core.jsonrpc.RpcUtil.JSON_RPC_VERSION; + +import tech.pegasys.ethsigner.core.jsonrpc.EthSendTransactionJsonParameters; +import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequest; +import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequestId; +import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.EnclaveLookupIdProvider; +import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.NonceProvider; + +import java.util.List; + +import com.google.common.base.MoreObjects; +import io.vertx.core.json.JsonObject; +import org.jetbrains.annotations.NotNull; +import org.web3j.crypto.RawTransaction; +import org.web3j.crypto.Sign.SignatureData; +import org.web3j.crypto.TransactionEncoder; +import org.web3j.rlp.RlpEncoder; +import org.web3j.rlp.RlpList; +import org.web3j.rlp.RlpType; +import org.web3j.utils.Base64String; + +public class GoQuorumPrivateTransaction extends EthTransaction { + + private final List privateFor; + private final EnclaveLookupIdProvider enclaveLookupIdProvider; + private String lookupId = ""; + + public static GoQuorumPrivateTransaction from( + final EthSendTransactionJsonParameters transactionJsonParameters, + final NonceProvider nonceProvider, + final EnclaveLookupIdProvider enclaveLookupIdProvider, + final JsonRpcRequestId id) { + + if (transactionJsonParameters.privateFor().isEmpty()) { + throw new IllegalArgumentException("Transaction does not contain a valid privateFor list."); + } + + return new GoQuorumPrivateTransaction( + transactionJsonParameters, + nonceProvider, + enclaveLookupIdProvider, + id, + transactionJsonParameters.privateFor().get()); + } + + private GoQuorumPrivateTransaction( + final EthSendTransactionJsonParameters transactionJsonParameters, + final NonceProvider nonceProvider, + final EnclaveLookupIdProvider enclaveLookupIdProvider, + final JsonRpcRequestId id, + final List privateFor) { + super(transactionJsonParameters, nonceProvider, id); + this.privateFor = privateFor; + this.enclaveLookupIdProvider = enclaveLookupIdProvider; + } + + @Override + @NotNull + public String getJsonRpcMethodName() { + return "eth_sendRawPrivateTransaction"; + } + + @Override + public void updateFieldsIfRequired() { + + if (!this.isNonceUserSpecified()) { + this.nonce = nonceProvider.getNonce(); + } + + final String data = + this.transactionJsonParameters + .data() + .orElseThrow( + () -> + new IllegalArgumentException("GoQuorum private transaction must contain data")); + this.lookupId = enclaveLookupIdProvider.getLookupId(data); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("transactionJsonParameters", transactionJsonParameters) + .add("nonceProvider", nonceProvider) + .add("id", id) + .add("nonce", nonce) + .add("enclaveLookupIdProvider", enclaveLookupIdProvider) + .add("lookupId", lookupId) + .toString(); + } + + private JsonObject getGoQuorumRawTxJsonParams() { + final JsonObject jsonObject = new JsonObject(); + if (this.transactionJsonParameters.privateFrom().isPresent()) { + jsonObject.put("privateFrom", this.transactionJsonParameters.privateFrom().get().toString()); + } + jsonObject.put("privateFor", Base64String.unwrapList(privateFor)); + return jsonObject; + } + + @Override + public byte[] rlpEncode(final SignatureData signatureData) { + final RawTransaction rawTransaction = createTransaction(); + final List values = TransactionEncoder.asRlpValues(rawTransaction, signatureData); + final RlpList rlpList = new RlpList(values); + return RlpEncoder.encode(rlpList); + } + + @Override + public JsonRpcRequest jsonRpcRequest( + final String signedTransactionHexString, final JsonRpcRequestId id) { + final JsonRpcRequest request = new JsonRpcRequest(JSON_RPC_VERSION, getJsonRpcMethodName()); + request.setParams(new Object[] {signedTransactionHexString, getGoQuorumRawTxJsonParams()}); + request.setId(id); + return request; + } + + @Override + protected RawTransaction createTransaction() { + return RawTransaction.createTransaction( + nonce, + transactionJsonParameters.gasPrice().orElse(DEFAULT_GAS_PRICE), + transactionJsonParameters.gas().orElse(DEFAULT_GAS), + transactionJsonParameters.receiver().orElse(DEFAULT_TO), + lookupId); + } +} diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/PrivateTransaction.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/PrivateTransaction.java index 3fc843fc9..18733abfa 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/PrivateTransaction.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/PrivateTransaction.java @@ -21,6 +21,7 @@ import java.util.List; import com.google.common.base.MoreObjects; +import org.jetbrains.annotations.NotNull; import org.web3j.crypto.Sign.SignatureData; import org.web3j.protocol.eea.crypto.PrivateTransactionEncoder; import org.web3j.protocol.eea.crypto.RawPrivateTransaction; @@ -47,8 +48,10 @@ public abstract class PrivateTransaction implements Transaction { } @Override - public void updateNonce() { - this.nonce = nonceProvider.getNonce(); + public void updateFieldsIfRequired() { + if (!this.isNonceUserSpecified()) { + this.nonce = nonceProvider.getNonce(); + } } @Override @@ -73,7 +76,13 @@ public String sender() { @Override public JsonRpcRequest jsonRpcRequest( final String signedTransactionHexString, final JsonRpcRequestId id) { - return Transaction.jsonRpcRequest(signedTransactionHexString, id, JSON_RPC_METHOD); + return Transaction.jsonRpcRequest(signedTransactionHexString, id, getJsonRpcMethodName()); + } + + @Override + @NotNull + public String getJsonRpcMethodName() { + return JSON_RPC_METHOD; } @Override diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/StoreRawEnclaveLookupIdProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/StoreRawEnclaveLookupIdProvider.java new file mode 100644 index 000000000..e6a774287 --- /dev/null +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/StoreRawEnclaveLookupIdProvider.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction; + +import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequest; +import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.EnclaveLookupIdProvider; + +public class StoreRawEnclaveLookupIdProvider implements EnclaveLookupIdProvider { + + private final VertxStoreRawRequestTransmitter vertxStoreRawRequestTransmitter; + + public StoreRawEnclaveLookupIdProvider( + final VertxStoreRawRequestTransmitter vertxStoreRawRequestTransmitter) { + this.vertxStoreRawRequestTransmitter = vertxStoreRawRequestTransmitter; + } + + @Override + public String getLookupId(final String payload) { + final JsonRpcRequest request = generateRequest(payload); + return vertxStoreRawRequestTransmitter.storeRaw(request); + } + + protected JsonRpcRequest generateRequest(final String payload) { + final JsonRpcRequest request = new JsonRpcRequest("2.0", "goquorum_storeRaw"); + request.setParams(new Object[] {payload}); + + return request; + } +} diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/Transaction.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/Transaction.java index 2629fcb91..fa3a4102e 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/Transaction.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/Transaction.java @@ -21,6 +21,7 @@ import java.math.BigInteger; import java.nio.ByteBuffer; +import org.jetbrains.annotations.NotNull; import org.web3j.crypto.Sign.SignatureData; public interface Transaction { @@ -30,7 +31,7 @@ public interface Transaction { String DEFAULT_DATA = ""; String DEFAULT_TO = ""; - void updateNonce(); + void updateFieldsIfRequired(); byte[] rlpEncode(SignatureData signatureData); @@ -53,6 +54,9 @@ static byte[] longToBytes(final long x) { return buffer.array(); } + @NotNull + String getJsonRpcMethodName(); + static JsonRpcRequest jsonRpcRequest( final String signedTransactionHexString, final JsonRpcRequestId id, final String rpcMethod) { final JsonRpcRequest transaction = new JsonRpcRequest(JSON_RPC_VERSION, rpcMethod); diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/TransactionFactory.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/TransactionFactory.java index 8ba60e712..0dcc4fb40 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/TransactionFactory.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/TransactionFactory.java @@ -43,10 +43,13 @@ public Transaction createTransaction(final RoutingContext context, final JsonRpc final String method = request.getMethod().toLowerCase(); final VertxNonceRequestTransmitter nonceRequestTransmitter = new VertxNonceRequestTransmitter(context.request().headers(), decoder, transmitterFactory); + final VertxStoreRawRequestTransmitter storeRawRequestTransmitter = + new VertxStoreRawRequestTransmitter( + context.request().headers(), decoder, transmitterFactory); switch (method) { case "eth_sendtransaction": - return createEthTransaction(request, nonceRequestTransmitter); + return createEthTransaction(request, nonceRequestTransmitter, storeRawRequestTransmitter); case "eea_sendtransaction": return createEeaTransaction(request, nonceRequestTransmitter); default: @@ -55,13 +58,23 @@ public Transaction createTransaction(final RoutingContext context, final JsonRpc } private Transaction createEthTransaction( - final JsonRpcRequest request, final VertxNonceRequestTransmitter requestTransmitter) { + final JsonRpcRequest request, + final VertxNonceRequestTransmitter nonceRequestTransmitter, + final VertxStoreRawRequestTransmitter storeRawRequestTransmitter) { final EthSendTransactionJsonParameters params = fromRpcRequestToJsonParam(EthSendTransactionJsonParameters.class, request); final NonceProvider ethNonceProvider = - new EthNonceProvider(params.sender(), requestTransmitter); - return new EthTransaction(params, ethNonceProvider, request.getId()); + new EthNonceProvider(params.sender(), nonceRequestTransmitter); + final StoreRawEnclaveLookupIdProvider lookupIdProvider = + new StoreRawEnclaveLookupIdProvider(storeRawRequestTransmitter); + + if (params.privateFor().isPresent()) { + return GoQuorumPrivateTransaction.from( + params, ethNonceProvider, lookupIdProvider, request.getId()); + } else { + return new EthTransaction(params, ethNonceProvider, request.getId()); + } } private Transaction createEeaTransaction( diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/VertxNonceRequestTransmitter.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/VertxNonceRequestTransmitter.java index 41ee7799a..80e86e469 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/VertxNonceRequestTransmitter.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/VertxNonceRequestTransmitter.java @@ -12,13 +12,13 @@ */ package tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction; +import static tech.pegasys.ethsigner.core.jsonrpc.RpcUtil.determineErrorCode; + import tech.pegasys.ethsigner.core.http.HeaderHelpers; import tech.pegasys.ethsigner.core.jsonrpc.JsonDecoder; import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequest; import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequestId; import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; -import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError; -import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcErrorResponse; import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcSuccessResponse; import tech.pegasys.ethsigner.core.requesthandler.DownstreamResponseHandler; import tech.pegasys.ethsigner.core.requesthandler.RequestTransmitter; @@ -50,25 +50,6 @@ public class VertxNonceRequestTransmitter { private static final AtomicInteger nextId = new AtomicInteger(0); - private class ResponseCallback implements DownstreamResponseHandler { - private final CompletableFuture result; - - private ResponseCallback(final CompletableFuture result) { - this.result = result; - } - - @Override - public void handleResponse( - final Iterable> headers, final int statusCode, String body) { - VertxNonceRequestTransmitter.this.handleResponse(body, result); - } - - @Override - public void handleFailure(Throwable t) { - result.completeExceptionally(t); - } - } - public VertxNonceRequestTransmitter( final MultiMap headers, final JsonDecoder decoder, @@ -123,17 +104,26 @@ private void handleResponse(final String body, final CompletableFuture result; + + private ResponseCallback(final CompletableFuture result) { + this.result = result; + } + + @Override + public void handleResponse( + final Iterable> headers, final int statusCode, String body) { + VertxNonceRequestTransmitter.this.handleResponse(body, result); + } + + @Override + public void handleFailure(Throwable t) { + result.completeExceptionally(t); } } } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/VertxStoreRawRequestTransmitter.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/VertxStoreRawRequestTransmitter.java new file mode 100644 index 000000000..a15043e05 --- /dev/null +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/VertxStoreRawRequestTransmitter.java @@ -0,0 +1,128 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction; + +import static tech.pegasys.ethsigner.core.jsonrpc.RpcUtil.determineErrorCode; + +import tech.pegasys.ethsigner.core.http.HeaderHelpers; +import tech.pegasys.ethsigner.core.jsonrpc.JsonDecoder; +import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequest; +import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequestId; +import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; +import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcSuccessResponse; +import tech.pegasys.ethsigner.core.requesthandler.DownstreamResponseHandler; +import tech.pegasys.ethsigner.core.requesthandler.RequestTransmitter; +import tech.pegasys.ethsigner.core.requesthandler.VertxRequestTransmitterFactory; + +import java.util.Map.Entry; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicInteger; + +import io.vertx.core.MultiMap; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.json.DecodeException; +import io.vertx.core.json.Json; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.web3j.exceptions.MessageDecodingException; + +public class VertxStoreRawRequestTransmitter { + + private static final Logger LOG = LogManager.getLogger(); + + private final MultiMap headers; + private final JsonDecoder decoder; + private final VertxRequestTransmitterFactory transmitterFactory; + + private static final AtomicInteger nextId = new AtomicInteger(0); + + public VertxStoreRawRequestTransmitter( + final MultiMap headers, + final JsonDecoder decoder, + final VertxRequestTransmitterFactory transmitterFactory) { + this.headers = headers; + this.transmitterFactory = transmitterFactory; + this.decoder = decoder; + } + + public String storeRaw(final JsonRpcRequest request) { + final CompletableFuture result = storePayloadAndGetLookupId(request, headers); + + try { + final String lookupId = result.get(); + LOG.debug("storeRaw response of {}", lookupId); + return lookupId; + } catch (final InterruptedException | ExecutionException e) { + throw new RuntimeException( + "Failed to retrieve storeRaw result (enclave lookup id):" + e.getMessage(), e.getCause()); + } + } + + private CompletableFuture storePayloadAndGetLookupId( + final JsonRpcRequest requestBody, final MultiMap headers) { + + final CompletableFuture result = new CompletableFuture<>(); + + final RequestTransmitter transmitter = transmitterFactory.create(new ResponseCallback(result)); + + final MultiMap headersToSend = HeaderHelpers.createHeaders(headers); + requestBody.setId(new JsonRpcRequestId(nextId.getAndIncrement())); + transmitter.sendRequest(HttpMethod.POST, headersToSend, "/", Json.encode(requestBody)); + + LOG.info("Transmitted {}", Json.encode(requestBody)); + + return result; + } + + private void handleResponse(final String body, final CompletableFuture result) { + try { + + final JsonRpcSuccessResponse response = + decoder.decodeValue(Buffer.buffer(body), JsonRpcSuccessResponse.class); + final Object suppliedLookupId = response.getResult(); + if (suppliedLookupId instanceof String) { + try { + result.complete((String) suppliedLookupId); + return; + } catch (final MessageDecodingException ex) { + result.completeExceptionally(ex); + return; + } + } + result.completeExceptionally(new RuntimeException("Web3 did not provide a string response.")); + } catch (final DecodeException e) { + result.completeExceptionally(new JsonRpcException(determineErrorCode(body, decoder))); + } + } + + private class ResponseCallback implements DownstreamResponseHandler { + private final CompletableFuture result; + + private ResponseCallback(final CompletableFuture result) { + this.result = result; + } + + @Override + public void handleResponse( + final Iterable> headers, final int statusCode, String body) { + VertxStoreRawRequestTransmitter.this.handleResponse(body, result); + } + + @Override + public void handleFailure(Throwable t) { + result.completeExceptionally(t); + } + } +} diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/GoQuorumEthTransactionTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/GoQuorumEthTransactionTest.java new file mode 100644 index 000000000..1fdbd6641 --- /dev/null +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/GoQuorumEthTransactionTest.java @@ -0,0 +1,110 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction; + +import static org.assertj.core.api.Assertions.assertThat; + +import tech.pegasys.ethsigner.core.jsonrpc.EthSendTransactionJsonParameters; +import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequest; +import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequestId; +import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.EnclaveLookupIdProvider; + +import java.math.BigInteger; +import java.util.List; +import java.util.Optional; + +import io.vertx.core.json.JsonObject; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.web3j.utils.Base64String; + +public class GoQuorumEthTransactionTest { + + private GoQuorumPrivateTransaction ethTransaction; + private EthSendTransactionJsonParameters params; + private EnclaveLookupIdProvider enclaveLookupIdProvider; + + @BeforeEach + public void setup() { + params = new EthSendTransactionJsonParameters("0x7577919ae5df4941180eac211965f275cdce314d"); + params.receiver("0xd46e8dd67c5d32be8058bb8eb970870f07244567"); + params.gas("0x76c0"); + params.gasPrice("0x9184e72a000"); + params.nonce("0x07"); + params.value("0x0"); + params.data( + "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"); + // These extra attributes make it a GoQuorum private transaction via eth_sendTransaction + params.privateFor(new String[] {"GV8m0VZAccYGAAYMBuYQtKEj0XtpXeaw2APcoBmtA2w="}); + + enclaveLookupIdProvider = + (x) -> + "9aefeff5ef9cef1dfdeffccff0afefff6fef0ff9faef9feffaeff3ffeffcf8feefafefeffdefef98ba7aafef"; + } + + @Test + @SuppressWarnings("unchecked") + public void createsJsonRequestWithoutPrivateFrom() { + createGoQuorumPrivateTransaction(Optional.empty()); + + final JsonRpcRequestId id = new JsonRpcRequestId(2); + final String transactionString = + "0xf90114a0e04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f28609184e72a0008276c094d46e8dd67c5d32be8058bb8eb970870f0724456704a9d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f07244567536a0fe72a92aede764ce41d06b163d28700b58e5ee8bb1af91d9d54979ea3bdb3e7ea046ae10c94c322fa44ddceb86677c2cd6cc17dfbd766924f41d10a244c512996dac5a6c617045736c3971444c50792f6538382b2f36797643554556497648383379304e3441367748754b58493dedac4756386d30565a41636359474141594d42755951744b456a3058747058656177324150636f426d744132773d8a72657374726963746564"; + final JsonRpcRequest jsonRpcRequest = ethTransaction.jsonRpcRequest(transactionString, id); + + assertThat(jsonRpcRequest.getMethod()).isEqualTo("eth_sendRawPrivateTransaction"); + assertThat(jsonRpcRequest.getVersion()).isEqualTo("2.0"); + assertThat(jsonRpcRequest.getId()).isEqualTo(id); + final Object[] paramsArray = (Object[]) jsonRpcRequest.getParams(); + assertThat(paramsArray[0]).isEqualTo(transactionString); + + assertThat(paramsArray[1]) + .isEqualTo(getGoQuorumRawTxJsonParams(Optional.empty(), params.privateFor().get())); + } + + @Test + @SuppressWarnings("unchecked") + public void createsJsonRequestWithPrivateFrom() { + createGoQuorumPrivateTransaction(Optional.of("ZlapEsl9qDLPy/e88+/6yvCUEVIvH83y0N4A6wHuKXI=")); + + final JsonRpcRequestId id = new JsonRpcRequestId(2); + final String transactionString = + "0xf90114a0e04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f28609184e72a0008276c094d46e8dd67c5d32be8058bb8eb970870f0724456704a9d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f07244567536a0fe72a92aede764ce41d06b163d28700b58e5ee8bb1af91d9d54979ea3bdb3e7ea046ae10c94c322fa44ddceb86677c2cd6cc17dfbd766924f41d10a244c512996dac5a6c617045736c3971444c50792f6538382b2f36797643554556497648383379304e3441367748754b58493dedac4756386d30565a41636359474141594d42755951744b456a3058747058656177324150636f426d744132773d8a72657374726963746564"; + final JsonRpcRequest jsonRpcRequest = ethTransaction.jsonRpcRequest(transactionString, id); + + assertThat(jsonRpcRequest.getMethod()).isEqualTo("eth_sendRawPrivateTransaction"); + assertThat(jsonRpcRequest.getVersion()).isEqualTo("2.0"); + assertThat(jsonRpcRequest.getId()).isEqualTo(id); + final Object[] paramsArray = (Object[]) jsonRpcRequest.getParams(); + assertThat(paramsArray[0]).isEqualTo(transactionString); + + assertThat(paramsArray[1]) + .isEqualTo(getGoQuorumRawTxJsonParams(params.privateFrom(), params.privateFor().get())); + } + + private void createGoQuorumPrivateTransaction(final Optional privateFrom) { + privateFrom.ifPresent((p) -> params.privateFrom(p)); + ethTransaction = + GoQuorumPrivateTransaction.from( + params, () -> BigInteger.valueOf(7), enclaveLookupIdProvider, new JsonRpcRequestId(1)); + ethTransaction.updateFieldsIfRequired(); + } + + private JsonObject getGoQuorumRawTxJsonParams( + final Optional privateFrom, final List privateFor) { + final JsonObject jsonObject = new JsonObject(); + privateFrom.ifPresent((p) -> jsonObject.put("privateFrom", p.toString())); + jsonObject.put("privateFor", Base64String.unwrapList(privateFor)); + return jsonObject; + } +} From 329af9a92d4a3eb755646eeb4cca3c0ab78af5ce Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Tue, 16 Feb 2021 11:29:10 +1000 Subject: [PATCH 52/64] added changelog for 21.1 (#350) * added changelog for 21.1 Signed-off-by: Sally MacFarlane --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee292434d..17b9ea755 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,15 @@ # Changelog +## 21.1.0 + +### Features Added +- Added support for sending GoQuorum private transactions via "eth_sendTransaction" JSON-RPC +- Improve error message if HTTP server fails to start + ## 20.10.0 ### Features Added -- Added "eth_signTransaction" JSON RPC +- Added "eth_signTransaction" JSON-RPC - Docker namespace updated to reflect "consensys/quorum-ethsigner" - Strip ACCESS_CONTROL_ALLOW_ORIGIN header from responses received from the web3provider - Added a Prometheus metrics endpoint, reporting basic application metrics From 59d943fd3f473ce33cd321a21f4f72d9d85f9fcc Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Fri, 19 Feb 2021 10:38:36 +1000 Subject: [PATCH 53/64] Added GoQuorum private transaction serializer (#352) * added GoQuorum private tx serializer which ignores chainId * fix signature - don't pass chainId of zero but use null signature in that step Signed-off-by: Sally MacFarlane --- .../SendTransactionHandler.java | 14 +++-- .../GoQuorumPrivateTransactionSerializer.java | 51 +++++++++++++++++++ .../core/signing/TransactionSerializer.java | 4 +- 3 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/GoQuorumPrivateTransactionSerializer.java diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java index 41ef32cd7..6122c0d10 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java @@ -21,8 +21,10 @@ import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; import tech.pegasys.ethsigner.core.requesthandler.JsonRpcRequestHandler; import tech.pegasys.ethsigner.core.requesthandler.VertxRequestTransmitterFactory; +import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.GoQuorumPrivateTransaction; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.Transaction; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.TransactionFactory; +import tech.pegasys.ethsigner.core.signing.GoQuorumPrivateTransactionSerializer; import tech.pegasys.ethsigner.core.signing.TransactionSerializer; import tech.pegasys.signers.secp256k1.api.Signer; @@ -80,16 +82,20 @@ public void handle(final RoutingContext context, final JsonRpcRequest request) { return; } - final TransactionSerializer transactionSerializer = - new TransactionSerializer(signer.get(), chainId); - sendTransaction(transaction, transactionSerializer, context, request); + sendTransaction(transaction, context, signer.get(), request); } private void sendTransaction( final Transaction transaction, - final TransactionSerializer transactionSerializer, final RoutingContext routingContext, + final Signer signer, final JsonRpcRequest request) { + + final TransactionSerializer transactionSerializer = + transaction instanceof GoQuorumPrivateTransaction + ? new GoQuorumPrivateTransactionSerializer(signer, chainId) + : new TransactionSerializer(signer, chainId); + final TransactionTransmitter transmitter = createTransactionTransmitter(transaction, transactionSerializer, routingContext, request); transmitter.send(); diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/GoQuorumPrivateTransactionSerializer.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/GoQuorumPrivateTransactionSerializer.java new file mode 100644 index 000000000..e1f9e6309 --- /dev/null +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/GoQuorumPrivateTransactionSerializer.java @@ -0,0 +1,51 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.ethsigner.core.signing; + +import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.Transaction; +import tech.pegasys.signers.secp256k1.api.Signature; +import tech.pegasys.signers.secp256k1.api.Signer; + +import org.web3j.crypto.Sign.SignatureData; +import org.web3j.utils.Numeric; + +public class GoQuorumPrivateTransactionSerializer extends TransactionSerializer { + + public GoQuorumPrivateTransactionSerializer(Signer signer, long chainId) { + super(signer, chainId); + } + + @Override + public String serialize(final Transaction transaction) { + final byte[] bytesToSign = transaction.rlpEncode(null); + final Signature signature = signer.sign(bytesToSign); + + byte[] newV = (getGoQuorumVValue(signature.getV().toByteArray())); + + final SignatureData web3jSignature = + new SignatureData(newV, signature.getR().toByteArray(), signature.getS().toByteArray()); + + final byte[] serializedBytes = transaction.rlpEncode(web3jSignature); + return Numeric.toHexString(serializedBytes); + } + + public static byte[] getGoQuorumVValue(byte[] v) { + // The current v has a value of 27 or 28, + // and we need to change that to 37 or 38 for GoQuorum private tx + if (v[v.length - 1] == (byte) 28) { + return new byte[] {38}; + } else { + return new byte[] {37}; + } + } +} diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/TransactionSerializer.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/TransactionSerializer.java index beac3cdc8..1d0bb7ef2 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/TransactionSerializer.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/TransactionSerializer.java @@ -23,8 +23,8 @@ public class TransactionSerializer { - private final Signer signer; - private final long chainId; + protected final Signer signer; + protected final long chainId; public TransactionSerializer(final Signer signer, final long chainId) { this.signer = signer; From 8409aef71d73624261c7a5dc3c7c8e8539106cb0 Mon Sep 17 00:00:00 2001 From: Usman Saleem Date: Tue, 23 Feb 2021 09:27:06 +1000 Subject: [PATCH 54/64] Updating CI and gradle build to upload to cloudsmith (#353) * Updating CI and gradle build to upload to cloudsmith Signed-off-by: Usman Saleem --- .circleci/config.yml | 10 +++++- build.gradle | 62 +++++++----------------------------- scripts/cloudsmith-upload.sh | 37 +++++++++++++++++++++ 3 files changed, 58 insertions(+), 51 deletions(-) create mode 100755 scripts/cloudsmith-upload.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index e119eb839..4f3f289b7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -166,12 +166,20 @@ jobs: executor: executor_med steps: - prepare + - run: + name: Install Python3 + command: | + sudo apt update + sudo apt install python3 python3-pip python3-venv - attach_workspace: at: ~/project + - run: + name: "Which cloudsmith user is being used?" + command: echo $CLOUDSMITH_USER - run: name: Publish command: | - ./gradlew --no-daemon --parallel bintrayUpload + ./gradlew --no-daemon --parallel cloudSmithUpload - notify publishDocker: diff --git a/build.gradle b/build.gradle index c0eb2a20c..61e7db5a1 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,6 @@ import java.text.SimpleDateFormat plugins { id 'com.diffplug.gradle.spotless' version '3.27.1' - id 'com.jfrog.bintray' version '1.8.4' id 'com.github.ben-manes.versions' version '0.27.0' id 'com.github.hierynomus.license' version '0.15.0' id 'io.spring.dependency-management' version '1.0.9.RELEASE' @@ -36,24 +35,6 @@ if (!JavaVersion.current().java11Compatible) { " Detected version ${JavaVersion.current()}") } -def bintrayUser = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') -def bintrayKey = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_KEY') -def bintrayPackage = bintray.pkg { - repo = 'pegasys-repo' - name = repositoryName - userOrg = 'consensys' - licenses = ['Apache-2.0'] - websiteUrl = 'https://github.com/PegaSysEng/' + repositoryName - issueTrackerUrl = 'https://github.com/PegaSysEng/' + repositoryName + '/issues' - vcsUrl = 'https://github.com/PegaSysEng/' + repositoryName + '.git' - - version { - name = project.version - released = new Date() - } -} - - group = 'tech.pegasys.' + repositoryName defaultTasks 'build', 'checkLicenses', 'javadoc' @@ -120,6 +101,7 @@ allprojects { repositories { jcenter() mavenCentral() + //TODO: Update the cloudsmith maven repo once created - for internal libraries such as Signers maven { url "https://consensys.bintray.com/pegasys-repo" } } @@ -249,7 +231,6 @@ task deploy() {} subprojects { if (file('src/main/java').directory) { - apply plugin: 'com.jfrog.bintray' apply plugin: 'maven-publish' publishing { @@ -283,18 +264,6 @@ subprojects { } } } - - bintray { - user = bintrayUser - key = bintrayKey - - publications = ['mavenJava'] - override = version.endsWith('SNAPSHOT') - - publish = true - - pkg = bintrayPackage - } } tasks.withType(Test) { @@ -639,24 +608,17 @@ release { } } -apply plugin: 'com.jfrog.bintray' - -bintray { - user = bintrayUser - key = bintrayKey - - filesSpec { - from distTar.destinationDirectory - from distZip.destinationDirectory - into '.' +task cloudsmithUpload { + dependsOn([ + distTar, + distZip, + ]) + doLast { + exec { + executable project.file("scripts/cloudsmith-upload.sh") + args rootProject.version, "${buildDir}/distributions" + } } - - publish = true - override = version.endsWith('SNAPSHOT') - - pkg = bintrayPackage } -afterReleaseBuild.dependsOn bintrayUpload -bintrayUpload.mustRunAfter(distTar) -bintrayUpload.mustRunAfter(distZip) +afterReleaseBuild.dependsOn cloudsmithUpload diff --git a/scripts/cloudsmith-upload.sh b/scripts/cloudsmith-upload.sh new file mode 100755 index 000000000..e2895e6a5 --- /dev/null +++ b/scripts/cloudsmith-upload.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set -euo pipefail + +VERSION=${1:?Must specify version} +DIST=${2:?Must specify path to distributions} + +DIST_IDENTIFIER="ethsigner" +CLOUDSMITH_REPO="consensys/ethsigner" +SUMMARY="EthSigner - ${VERSION}" +ZIP_DIST="${DIST}/${DIST_IDENTIFIER}-${VERSION}.zip" +ZIP_NAME="${DIST_IDENTIFIER}.zip" +TAR_DIST="${DIST}/${DIST_IDENTIFIER}-${VERSION}.tar.gz" +TAR_NAME="${DIST_IDENTIFIER}.tar.gz" + +REPUBLISH="" +if [[ $VERSION == *"SNAPSHOT"* ]]; then + REPUBLISH="--republish" +fi + +if [ -z ${CLOUDSMITH_USER+x} ]; then echo "CLOUDSMITH_USER is unset"; else echo "CLOUDSMITH_USER is set to '$CLOUDSMITH_USER'"; fi + +# cloudsmith cli setup +ENV_DIR=./build/tmp/cloudsmith-env +if [[ -d ${ENV_DIR} ]] ; then + source ${ENV_DIR}/bin/activate +else + python3 -m venv ${ENV_DIR} + source ${ENV_DIR}/bin/activate +fi + +python3 -m pip install --upgrade cloudsmith-cli + +# upload +cloudsmith push raw $CLOUDSMITH_REPO $ZIP_DIST $REPUBLISH --name "${ZIP_NAME}" --version "${VERSION}" --summary "${SUMMARY} binary distribution" --description "${SUMMARY} binary distribution in zip format" --content-type 'application/zip' +cloudsmith push raw $CLOUDSMITH_REPO $TAR_DIST $REPUBLISH --name "${TAR_NAME}" --version "${VERSION}" --summary "${SUMMARY} binary distribution" --description "${SUMMARY} binary distribution in tar gzipped format" --content-type 'application/tar+gzip' + + From 2b45667e6ec5946312f3bb7546c711d130f2ed9b Mon Sep 17 00:00:00 2001 From: Usman Saleem Date: Tue, 23 Feb 2021 11:45:10 +1000 Subject: [PATCH 55/64] Update changelog for 21.2.0 (#354) Signed-off-by: Usman Saleem --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17b9ea755..26ff8f2d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 21.2.0 + +### Features Added +- Publish artifacts to [cloudsmith](https://cloudsmith.io/~consensys/repos/ethsigner). + + ## 21.1.0 ### Features Added From c9070bb52fc2035156d43e09d6fd8a58150ffd75 Mon Sep 17 00:00:00 2001 From: Usman Saleem Date: Tue, 23 Feb 2021 13:37:02 +1000 Subject: [PATCH 56/64] Fixing version in CHANGELOG for release 21.1.0 (#355) Signed-off-by: Usman Saleem --- CHANGELOG.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26ff8f2d4..c87d20abc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,9 @@ # Changelog -## 21.2.0 - -### Features Added -- Publish artifacts to [cloudsmith](https://cloudsmith.io/~consensys/repos/ethsigner). - - ## 21.1.0 ### Features Added +- Publish artifacts to [cloudsmith](https://cloudsmith.io/~consensys/repos/ethsigner). - Added support for sending GoQuorum private transactions via "eth_sendTransaction" JSON-RPC - Improve error message if HTTP server fails to start From 6e30f443bf5f4813039eb1e82a3b83ab7a34bf20 Mon Sep 17 00:00:00 2001 From: Usman Saleem Date: Tue, 23 Feb 2021 13:49:41 +1000 Subject: [PATCH 57/64] 21.1.0 release (#356) Signed-off-by: Usman Saleem --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index d7229c245..e6d233dbe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1g -version=20.10.1-SNAPSHOT +version=21.1.0 besuVersion=1.5.0 besuDistroUrl=https://bintray.com/hyperledger-org/besu-repo/download_file?file_path=besu-${besuVersion}.tar.gz hashicorpVaultVersion=1.4.3 From 87644e10f871a3536e7e9d243e1cdc95ceeb08c7 Mon Sep 17 00:00:00 2001 From: Usman Saleem Date: Tue, 23 Feb 2021 13:57:42 +1000 Subject: [PATCH 58/64] Prepare for version 21.1.1-SNAPSHOT (#357) Signed-off-by: Usman Saleem --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e6d233dbe..e3300541b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1g -version=21.1.0 +version=21.1.1-SNAPSHOT besuVersion=1.5.0 besuDistroUrl=https://bintray.com/hyperledger-org/besu-repo/download_file?file_path=besu-${besuVersion}.tar.gz hashicorpVaultVersion=1.4.3 From 0dd7d1ec39edcbf46e048197585fd7a2e134c43b Mon Sep 17 00:00:00 2001 From: Usman Saleem Date: Mon, 8 Mar 2021 16:38:17 +1000 Subject: [PATCH 59/64] Resolve and publish to cloudsmith (#359) * Resolve signers from cloudsmith * Publish EthSigner module jars to cloudsmith as they are used by Besu AT. Signed-off-by: Usman Saleem --- .circleci/config.yml | 7 ++----- build.gradle | 36 +++++++++++++++++++++++++++++++----- gradle/versions.gradle | 2 +- scripts/cloudsmith-upload.sh | 2 +- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4f3f289b7..a7028491e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -173,13 +173,10 @@ jobs: sudo apt install python3 python3-pip python3-venv - attach_workspace: at: ~/project - - run: - name: "Which cloudsmith user is being used?" - command: echo $CLOUDSMITH_USER - run: name: Publish command: | - ./gradlew --no-daemon --parallel cloudSmithUpload + ./gradlew --no-daemon --parallel cloudSmithUpload publish - notify publishDocker: @@ -241,7 +238,7 @@ workflows: - acceptanceTests context: - dockerhub-quorumengineering-ro - - quorum-gradle + - cloudsmith-protocols - publishDocker: filters: branches: diff --git a/build.gradle b/build.gradle index 61e7db5a1..200469e32 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,9 @@ if (!JavaVersion.current().java11Compatible) { " Detected version ${JavaVersion.current()}") } +def cloudsmithUser = project.hasProperty('cloudsmithUser') ? project.property('cloudsmithUser') : System.getenv('CLOUDSMITH_USER') +def cloudsmithKey = project.hasProperty('cloudsmithApiKey') ? project.property('cloudsmithApiKey') : System.getenv('CLOUDSMITH_API_KEY') + group = 'tech.pegasys.' + repositoryName defaultTasks 'build', 'checkLicenses', 'javadoc' @@ -101,8 +104,7 @@ allprojects { repositories { jcenter() mavenCentral() - //TODO: Update the cloudsmith maven repo once created - for internal libraries such as Signers - maven { url "https://consensys.bintray.com/pegasys-repo" } + maven { url "https://artifacts.consensys.net/public/maven/maven/" } } dependencies { errorprone("com.google.errorprone:error_prone_core") } @@ -234,8 +236,19 @@ subprojects { apply plugin: 'maven-publish' publishing { + repositories { + maven { + name = "cloudsmith" + url = "https://api-g.cloudsmith.io/maven/consensys/maven/" + credentials { + username = cloudsmithUser + password = cloudsmithKey + } + } + } publications { mavenJava(MavenPublication) { + artifactId calculateJarName(project) groupId String.format("tech.pegasys.%s.internal", repositoryName) version "${project.version}" from components.java @@ -247,7 +260,7 @@ subprojects { } pom { name = projectName + " - ${project.name}" - url = 'http://github.com/PegaSysEng/' + repositoryName + url = 'http://github.com/ConsenSys/' + repositoryName licenses { license { name = 'The Apache License, Version 2.0' @@ -255,10 +268,10 @@ subprojects { } } scm { - def hostPath = 'github.com/PegasysEng/' + repositoryName + '.git' + def hostPath = 'github.com/ConsenSys/' + repositoryName + '.git' connection = 'scm:git:git://' + hostPath developerConnection = 'scm:git:ssh://' + hostPath - url = 'https://github.com/PegaSysEng/' + repositoryName + url = 'https://github.com/ConsenSys/' + repositoryName } } } @@ -622,3 +635,16 @@ task cloudsmithUpload { } afterReleaseBuild.dependsOn cloudsmithUpload + +def calculateJarName(Project project) { + def jarName = project.name + def parent = project.parent + + while (parent != null) { + if (parent != rootProject) { + jarName = parent.name + '-' + jarName + } + parent = parent.parent + } + return jarName +} diff --git a/gradle/versions.gradle b/gradle/versions.gradle index 5718c2a70..d211081e0 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -68,7 +68,7 @@ dependencyManagement { dependency 'org.web3j:core:4.5.14' dependency 'org.web3j:crypto:4.5.14' - dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.4') { + dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.16') { entry 'keystorage-hashicorp' entry 'signing-secp256k1-api' entry 'signing-secp256k1-impl' diff --git a/scripts/cloudsmith-upload.sh b/scripts/cloudsmith-upload.sh index e2895e6a5..7e731f75d 100755 --- a/scripts/cloudsmith-upload.sh +++ b/scripts/cloudsmith-upload.sh @@ -17,7 +17,7 @@ if [[ $VERSION == *"SNAPSHOT"* ]]; then REPUBLISH="--republish" fi -if [ -z ${CLOUDSMITH_USER+x} ]; then echo "CLOUDSMITH_USER is unset"; else echo "CLOUDSMITH_USER is set to '$CLOUDSMITH_USER'"; fi +if [ -z ${CLOUDSMITH_USER+x} ]; then echo "CLOUDSMITH_USER is unset."; else echo "CLOUDSMITH_USER is set."; fi # cloudsmith cli setup ENV_DIR=./build/tmp/cloudsmith-env From 53e6ec50afcecc0d32f2797f52896c8c64173050 Mon Sep 17 00:00:00 2001 From: Usman Saleem Date: Tue, 9 Mar 2021 15:26:00 +1000 Subject: [PATCH 60/64] Upgrade besu metrics library (#360) -- Upgrade besu metrics library -- update license aliases in check-licenses --- CHANGELOG.md | 6 ++++++ build.gradle | 1 + .../ethsigner/core/metrics/MetricsEndpoint.java | 10 +++++----- gradle/check-licenses.gradle | 3 +++ gradle/versions.gradle | 4 ++-- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c87d20abc..4866285d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 21.3.0 + +### Features Added +- Upgraded besu-metrics library +- Publish ethsigner module jars to cloudsmith maven repo + ## 21.1.0 ### Features Added diff --git a/build.gradle b/build.gradle index 200469e32..6d13abbed 100644 --- a/build.gradle +++ b/build.gradle @@ -104,6 +104,7 @@ allprojects { repositories { jcenter() mavenCentral() + maven { url "https://hyperledger.jfrog.io/artifactory/besu-maven" } maven { url "https://artifacts.consensys.net/public/maven/maven/" } } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/metrics/MetricsEndpoint.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/metrics/MetricsEndpoint.java index 1708ae46f..ec681f11d 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/metrics/MetricsEndpoint.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/metrics/MetricsEndpoint.java @@ -17,9 +17,9 @@ import java.util.Set; import io.vertx.core.Vertx; +import org.hyperledger.besu.metrics.MetricsService; +import org.hyperledger.besu.metrics.MetricsSystemFactory; import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; -import org.hyperledger.besu.metrics.prometheus.MetricsService; -import org.hyperledger.besu.metrics.prometheus.PrometheusMetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.metrics.MetricCategory; @@ -41,13 +41,13 @@ public MetricsEndpoint( metricsNetworkInterface, metricCategories, metricsHostAllowList); - this.metricsSystem = PrometheusMetricsSystem.init(metricsConfig); + this.metricsSystem = MetricsSystemFactory.create(metricsConfig); this.metricsConfig = metricsConfig; } public void start(final Vertx vertx) { if (metricsConfig.isEnabled()) { - metricsService = Optional.of(MetricsService.create(vertx, metricsConfig, metricsSystem)); + metricsService = MetricsService.create(vertx, metricsConfig, metricsSystem); } else { metricsService = Optional.empty(); } @@ -77,7 +77,7 @@ private MetricsConfiguration createMetricsConfiguration( .port(metricsPort) .host(metricsNetworkInterface) .metricCategories(metricCategories) - .hostsWhitelist(metricsHostAllowList) + .hostsAllowlist(metricsHostAllowList) .build(); } } diff --git a/gradle/check-licenses.gradle b/gradle/check-licenses.gradle index a49d76184..6da0fcc9c 100644 --- a/gradle/check-licenses.gradle +++ b/gradle/check-licenses.gradle @@ -100,6 +100,8 @@ downloadLicenses { (bsd3Clause): [ 'BSD 3-Clause', 'BSD 3-Clause "New" or "Revised" License (BSD-3-Clause)', + '3-Clause BSD License', + 'The BSD 3-Clause License', license('BSD 3-clause', 'http://opensource.org/licenses/BSD-3-Clause'), license('BSD 3-Clause', 'http://www.scala-lang.org/license.html') ], @@ -157,6 +159,7 @@ downloadLicenses { (group('org.glassfish.jersey.connectors')): apache, (group('com.github.jnr')): epl_2, (group('javax.mail')): cddl, + (group('javax.xml.bind')): cddl1_1, (group('net.jcip')): apache, (group('net.java.dev.jna')): apache, (group('com.sun.mail')): cddl diff --git a/gradle/versions.gradle b/gradle/versions.gradle index d211081e0..d04d06a75 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -76,7 +76,7 @@ dependencyManagement { dependency 'org.zeroturnaround:zt-exec:1.11' - dependency 'org.hyperledger.besu:plugin-api:1.4.0' - dependency 'org.hyperledger.besu.internal:metrics-core:1.4.0' + dependency 'org.hyperledger.besu:plugin-api:21.1.1' + dependency 'org.hyperledger.besu.internal:metrics-core:21.1.1' } } From 2c78c8482ea2f865a54b740a66c1dd58f96efb25 Mon Sep 17 00:00:00 2001 From: Usman Saleem Date: Thu, 11 Mar 2021 11:33:01 +1000 Subject: [PATCH 61/64] Support git tag based release (#363) - version calculation is automated based on git commits and tags --- build.gradle | 111 ++++++++++++++--------------------- gradle.properties | 3 +- scripts/cloudsmith-upload.sh | 2 +- 3 files changed, 46 insertions(+), 70 deletions(-) diff --git a/build.gradle b/build.gradle index 6d13abbed..6714d918a 100644 --- a/build.gradle +++ b/build.gradle @@ -11,6 +11,8 @@ * specific language governing permissions and limitations under the License. */ + +import groovy.transform.Memoized import net.ltgt.gradle.errorprone.CheckSeverity import java.text.SimpleDateFormat @@ -22,7 +24,7 @@ plugins { id 'io.spring.dependency-management' version '1.0.9.RELEASE' id 'me.champeau.gradle.jmh' version '0.5.0' apply false id 'net.ltgt.errorprone' version '1.1.1' - id 'net.researchgate.release' version '2.8.1' + id 'org.ajoberstar.grgit' version '4.1.0' } String projectName = "EthSigner" @@ -35,6 +37,10 @@ if (!JavaVersion.current().java11Compatible) { " Detected version ${JavaVersion.current()}") } +rootProject.version = calculatePublishVersion() +def specificVersion = calculateVersion() +def isDevelopBuild = rootProject.version.contains('develop') + def cloudsmithUser = project.hasProperty('cloudsmithUser') ? project.property('cloudsmithUser') : System.getenv('CLOUDSMITH_USER') def cloudsmithKey = project.hasProperty('cloudsmithApiKey') ? project.property('cloudsmithApiKey') : System.getenv('CLOUDSMITH_API_KEY') @@ -510,7 +516,7 @@ task dockerUpload(type: Exec) { additionalTags.add('develop') } - if (!(version ==~ /.*-SNAPSHOT/)) { + if (!isDevelopBuild) { additionalTags.add('latest') additionalTags.add(version.split(/\./)[0..1].join('.')) } @@ -540,17 +546,6 @@ task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) { configurations { annotationProcessor } -// Prevent errorprone-checks being dependent upon errorprone-checks! -// However, ensure all subprojects comply with the custom rules. -/* - configure(subprojects.findAll {it.name != 'errorprone-checks'}) { - dependencies { annotationProcessor project(":errorprone-checks") } - tasks.withType(JavaCompile) { - options.annotationProcessorPath = configurations.annotationProcessor - } - } - */ - // http://label-schema.org/rc1/ // using the RFC3339 format "2016-04-12T23:20:50.52Z" def buildTime() { @@ -559,67 +554,51 @@ def buildTime() { return df.format(new Date()) } -// Takes the version, and if -SNAPSHOT is part of it replaces SNAPSHOT -// with the git commit version. -def calculateVersion() { - String version = rootProject.version - if (version.endsWith("-SNAPSHOT")) { - version = version.replace("-SNAPSHOT", "-dev-" + getCheckedOutGitCommitHash()) +// Calculate the version that this build would be published under (if it is published) +// If this exact commit is tagged, use the tag +// If this is on a release-* branch, use the most recent tag appended with +develop (e.g. 0.1.1-RC1+develop) +// Otherwise, use develop +def calculatePublishVersion() { + if (!grgit) { + return 'UNKNOWN' } - return version + def specificVersion = calculateVersion() + def isReleaseBranch = grgit.branch.current().name.startsWith('release-') + if (specificVersion.contains('+')) { + return isReleaseBranch ? "${specificVersion.substring(0, specificVersion.indexOf('+'))}+develop" : "develop" + } + return specificVersion } -def getCheckedOutGitCommitHash() { - def gitFolder = "$projectDir/.git/" - if (!file(gitFolder).isDirectory()) { - // We are in a submodule. The file's contents are `gitdir: \n`. - // Read the file, cut off the front, and trim the whitespace. - gitFolder = file(gitFolder).text.substring(8).trim() + "/" +// Calculate the version that `product --version` will report (among other places) +// If this exact commit is tagged, use the tag +// Otherwise use git describe --tags and replace the - after the tag with a + +@Memoized +def calculateVersion() { + if (!grgit) { + return 'UNKNOWN' } - def takeFromHash = 8 - /* - * '.git/HEAD' contains either - * in case of detached head: the currently checked out commit hash - * otherwise: a reference to a file containing the current commit hash - */ - def head = new File(gitFolder + "HEAD").text.split(":") // .git/HEAD - def isCommit = head.length == 1 // e5a7c79edabbf7dd39888442df081b1c9d8e88fd - - if (isCommit) return head[0].trim().take(takeFromHash) // e5a7c79edabb - - def refHead = new File(gitFolder + head[1].trim()) // .git/refs/heads/master - refHead.text.trim().take takeFromHash + String version = grgit.describe(tags: true) + if (version == null) { + return "UNKNOWN+g${grgit.head().abbreviatedId}" + } + def versionPattern = ~/^(?.*)-(?[0-9]+-g[a-z0-9]+)$/ + def matcher = version =~ versionPattern + if (matcher.find()) { + return "${matcher.group("lastVersion")}+${matcher.group("devVersion")}" + } + return version } -apply plugin: 'net.researchgate.release' - -task releaseIntegrationTest(type: Test) { - for (TaskContainer taskList : subprojects.tasks) { - def subProjectIntegrationTask = taskList.findByName('integrationTest') - - if (subProjectIntegrationTask != null) { - dependsOn subProjectIntegrationTask - } +task printVersion() { + doFirst { + print "Specific version: ${specificVersion} Publish version: ${project.version}" } } -task releaseAcceptanceTest(type: Test, dependsOn: ':acceptance-tests:acceptanceTest') {} - -release { - preTagCommitMessage = '[Gradle Release Plugin] - pre tag commit: ' - tagCommitMessage = '[Gradle Release Plugin] - creating tag: ' - newVersionCommitMessage = '[Gradle Release Plugin] - new version commit: ' - buildTasks = [ - 'build', - 'releaseIntegrationTest', - 'releaseAcceptanceTest', - 'checkLicenses', - 'javadoc' - ] - - git { - requireBranch = project.hasProperty('release.branch') ? project.property('release.branch') : 'master' - } +def getCheckedOutGitCommitHash() { + def takeFromHash = 8 + grgit ? grgit.head().id.take(takeFromHash) : 'UNKNOWN' } task cloudsmithUpload { @@ -635,8 +614,6 @@ task cloudsmithUpload { } } -afterReleaseBuild.dependsOn cloudsmithUpload - def calculateJarName(Project project) { def jarName = project.name def parent = project.parent diff --git a/gradle.properties b/gradle.properties index e3300541b..254b25659 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,5 @@ org.gradle.jvmargs=-Xmx1g -version=21.1.1-SNAPSHOT besuVersion=1.5.0 -besuDistroUrl=https://bintray.com/hyperledger-org/besu-repo/download_file?file_path=besu-${besuVersion}.tar.gz +besuDistroUrl=https://hyperledger.jfrog.io/artifactory/besu-binaries/besu/${besuVersion}/besu-${besuVersion}.tar.gz hashicorpVaultVersion=1.4.3 hashicorpVaultUrl=https://releases.hashicorp.com/vault diff --git a/scripts/cloudsmith-upload.sh b/scripts/cloudsmith-upload.sh index 7e731f75d..43160c921 100755 --- a/scripts/cloudsmith-upload.sh +++ b/scripts/cloudsmith-upload.sh @@ -13,7 +13,7 @@ TAR_DIST="${DIST}/${DIST_IDENTIFIER}-${VERSION}.tar.gz" TAR_NAME="${DIST_IDENTIFIER}.tar.gz" REPUBLISH="" -if [[ $VERSION == *"SNAPSHOT"* ]]; then +if [[ $VERSION == *"develop"* ]]; then REPUBLISH="--republish" fi From 866cf3ab970eeeeaa3fe621d9cdf6e59e2c3319c Mon Sep 17 00:00:00 2001 From: Usman Saleem Date: Fri, 12 Mar 2021 15:28:40 +1000 Subject: [PATCH 62/64] Updating latest Besu release to use with AT (#361) * webclientoptions tryUseCompression property to true * AT DSL Handle Json Error * Enable support for compression for EthSigner clients * add debug headers output * set 100 continue automatically --- CHANGELOG.md | 2 ++ .../tests/MethodNotFoundAcceptanceTest.java | 4 ++-- .../ethsigner/tests/dsl/Contracts.java | 8 +++---- .../tech/pegasys/ethsigner/tests/dsl/Eea.java | 17 ++++---------- .../tech/pegasys/ethsigner/tests/dsl/Eth.java | 10 ++++++++ .../ethsigner/tests/dsl/PrivateContracts.java | 23 ++++++++++++++++++- .../ethsigner/tests/dsl/PublicContracts.java | 9 ++++++++ .../tests/dsl/RawJsonRpcRequests.java | 6 ++++- .../ethsigner/tests/dsl/Transactions.java | 6 +---- .../tests/dsl/node/besu/BesuNodeFactory.java | 1 + .../tests/dsl/signer/SignerResponse.java | 15 ++++++++++++ .../ReplayProtectionAcceptanceTest.java | 6 ++--- .../signing/ValueTransferAcceptanceTest.java | 3 ++- .../pegasys/ethsigner/core/EthSigner.java | 4 +++- .../core/WebClientOptionsFactory.java | 3 ++- .../VertxRequestTransmitter.java | 13 ++++++++++- gradle.properties | 2 +- 17 files changed, 99 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4866285d6..345b9ad33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ ### Features Added - Upgraded besu-metrics library - Publish ethsigner module jars to cloudsmith maven repo +- Update Besu latest version for Acceptance Tests +- Add gzip compression support ## 21.1.0 diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/MethodNotFoundAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/MethodNotFoundAcceptanceTest.java index 280296232..cd3d0aca1 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/MethodNotFoundAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/MethodNotFoundAcceptanceTest.java @@ -28,7 +28,7 @@ public void sendDisabledApiReturnsBadRequest() { final SignerResponse response = ethSigner().rawJsonRpcRequests().exceptionalRequest("ibft_getPendingVotes"); - assertThat(response.status()).isEqualTo(HttpResponseStatus.BAD_REQUEST); + assertThat(response.status()).isEqualTo(HttpResponseStatus.OK); assertThat(response.jsonRpc().getError()).isEqualTo(JsonRpcError.METHOD_NOT_ENABLED); } @@ -37,7 +37,7 @@ public void unknownJsonRpcMethodReturnsBadRequest() { final SignerResponse response = ethSigner().rawJsonRpcRequests().exceptionalRequest("invalidJsonRpcMethod"); - assertThat(response.status()).isEqualTo(HttpResponseStatus.BAD_REQUEST); + assertThat(response.status()).isEqualTo(HttpResponseStatus.OK); assertThat(response.jsonRpc().getError()).isEqualTo(JsonRpcError.METHOD_NOT_FOUND); } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Contracts.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Contracts.java index dcfd42e3f..1a5c75b40 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Contracts.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Contracts.java @@ -13,7 +13,6 @@ package tech.pegasys.ethsigner.tests.dsl; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.fail; import static tech.pegasys.ethsigner.tests.WaitUtils.waitFor; import static tech.pegasys.ethsigner.tests.dsl.utils.ExceptionUtils.failOnIOException; @@ -39,6 +38,9 @@ public abstract class Contracts { public abstract String sendTransaction(T smartContract) throws IOException; + public abstract SignerResponse sendTransactionExpectsError(T smartContract) + throws IOException; + public abstract Optional getTransactionReceipt(final String hash) throws IOException; @@ -48,13 +50,11 @@ public String submit(final T smartContract) { public SignerResponse submitExceptional(final T smartContract) { try { - submit(smartContract); - fail("Expecting exceptional response "); + return failOnIOException(() -> sendTransactionExpectsError(smartContract)); } catch (final ClientConnectionException e) { LOG.info("ClientConnectionException with message: " + e.getMessage()); return SignerResponse.fromError(e); } - return null; } public void awaitBlockContaining(final String hash) { diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eea.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eea.java index d8b0ef426..8b5d618c7 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eea.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eea.java @@ -13,7 +13,6 @@ package tech.pegasys.ethsigner.tests.dsl; import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; @@ -27,16 +26,10 @@ public Eea(final RawJsonRpcRequestFactory requestFactory) { this.requestFactory = requestFactory; } - public String sendTransaction(final PrivateTransaction transaction) throws IOException { - final EthSendTransaction response = - requestFactory - .createRequest( - "eea_sendTransaction", singletonList(transaction), EthSendTransaction.class) - .send(); - - assertThat(response.getTransactionHash()).isNotEmpty(); - assertThat(response.getError()).isNull(); - - return response.getTransactionHash(); + public EthSendTransaction sendTransaction(final PrivateTransaction transaction) + throws IOException { + return requestFactory + .createRequest("eea_sendTransaction", singletonList(transaction), EthSendTransaction.class) + .send(); } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eth.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eth.java index a5030b997..198c96081 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eth.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eth.java @@ -14,6 +14,9 @@ import static org.assertj.core.api.Assertions.assertThat; +import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcErrorResponse; +import tech.pegasys.ethsigner.tests.dsl.signer.SignerResponse; + import java.io.IOException; import java.math.BigInteger; import java.util.List; @@ -42,6 +45,13 @@ public String sendTransaction(final Transaction transaction) throws IOException return response.getTransactionHash(); } + public SignerResponse sendTransactionExpectsError( + final Transaction transaction) throws IOException { + final EthSendTransaction response = jsonRpc.ethSendTransaction(transaction).send(); + assertThat(response.hasError()).isTrue(); + return SignerResponse.fromWeb3jErrorResponse(response); + } + public List getAccounts() throws IOException { return jsonRpc.ethAccounts().send().getAccounts(); } diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateContracts.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateContracts.java index c315ec3be..b4713180f 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateContracts.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateContracts.java @@ -12,9 +12,15 @@ */ package tech.pegasys.ethsigner.tests.dsl; +import static org.assertj.core.api.Assertions.assertThat; + +import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcErrorResponse; +import tech.pegasys.ethsigner.tests.dsl.signer.SignerResponse; + import java.io.IOException; import java.util.Optional; +import org.web3j.protocol.core.methods.response.EthSendTransaction; import org.web3j.protocol.core.methods.response.TransactionReceipt; public class PrivateContracts extends Contracts { @@ -29,7 +35,22 @@ public PrivateContracts(final Besu besu, final Eea eea) { @Override public String sendTransaction(final PrivateTransaction smartContract) throws IOException { - return eea.sendTransaction(smartContract); + final EthSendTransaction response = eea.sendTransaction(smartContract); + + assertThat(response.getTransactionHash()).isNotEmpty(); + assertThat(response.getError()).isNull(); + + return response.getTransactionHash(); + } + + @Override + public SignerResponse sendTransactionExpectsError( + final PrivateTransaction smartContract) throws IOException { + final EthSendTransaction response = eea.sendTransaction(smartContract); + + assertThat(response.hasError()).isTrue(); + + return SignerResponse.fromWeb3jErrorResponse(response); } @Override diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PublicContracts.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PublicContracts.java index 25a71d35d..9893fca2f 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PublicContracts.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PublicContracts.java @@ -15,6 +15,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static tech.pegasys.ethsigner.tests.dsl.utils.ExceptionUtils.failOnIOException; +import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcErrorResponse; +import tech.pegasys.ethsigner.tests.dsl.signer.SignerResponse; + import java.io.IOException; import java.util.Optional; @@ -34,6 +37,12 @@ public String sendTransaction(final Transaction smartContract) throws IOExceptio return eth.sendTransaction(smartContract); } + @Override + public SignerResponse sendTransactionExpectsError( + final Transaction smartContract) throws IOException { + return eth.sendTransactionExpectsError(smartContract); + } + @Override public Optional getTransactionReceipt(final String hash) throws IOException { return eth.getTransactionReceipt(hash); diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequests.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequests.java index b14348880..cab48ab9e 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequests.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequests.java @@ -54,7 +54,11 @@ public SignerResponse exceptionalRequest( final Request request = requestFactory.createRequest(method); try { - failOnIOException(request::send); + final SignerResponse response = + SignerResponse.fromWeb3jErrorResponse(failOnIOException(request::send)); + if (response != null) { + return response; + } fail("Expecting exceptional response "); return null; } catch (final ClientConnectionException e) { diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Transactions.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Transactions.java index f93264f7b..3f592e2c0 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Transactions.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Transactions.java @@ -13,7 +13,6 @@ package tech.pegasys.ethsigner.tests.dsl; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; import static tech.pegasys.ethsigner.tests.WaitUtils.waitFor; import static tech.pegasys.ethsigner.tests.dsl.utils.ExceptionUtils.failOnIOException; @@ -44,14 +43,11 @@ public String submit(final Transaction transaction) { public SignerResponse submitExceptional(final Transaction transaction) { try { - failOnIOException(() -> eth.sendTransaction(transaction)); - fail("Expecting exceptional response "); + return failOnIOException(() -> eth.sendTransactionExpectsError(transaction)); } catch (final ClientConnectionException e) { LOG.info("ClientConnectionException with message: " + e.getMessage()); return SignerResponse.fromError(e); } - - return null; } public void awaitBlockContaining(final String hash) { diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/besu/BesuNodeFactory.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/besu/BesuNodeFactory.java index 85dbae62b..598c706f9 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/besu/BesuNodeFactory.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/besu/BesuNodeFactory.java @@ -66,6 +66,7 @@ public static BesuNode create(final BesuNodeConfig config) { params.add(config.getHostName()); params.add("--rpc-http-apis"); params.add("ETH,NET,WEB3,EEA"); + params.add("--min-gas-price=0"); params.add("--privacy-enabled"); params.add("--privacy-public-key-file"); params.add(privacyPublicKeyFilePath()); diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerResponse.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerResponse.java index eec8901a5..df5814a6d 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerResponse.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerResponse.java @@ -12,11 +12,13 @@ */ package tech.pegasys.ethsigner.tests.dsl.signer; +import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError; import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcErrorResponse; import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.vertx.core.json.Json; +import org.web3j.protocol.core.Response; import org.web3j.protocol.exceptions.ClientConnectionException; public class SignerResponse { @@ -54,4 +56,17 @@ public static SignerResponse fromError(final ClientConnect throw new RuntimeException("Unable to parse web3j exception message", e); } } + + public static SignerResponse fromWeb3jErrorResponse( + final Response response) { + if (response != null && response.hasError()) { + final Response.Error error = response.getError(); + final JsonRpcError jsonRpcError = JsonRpcError.fromJson(error.getCode(), error.getMessage()); + final JsonRpcErrorResponse jsonRpcErrorResponse = + new JsonRpcErrorResponse(response.getId(), jsonRpcError); + return new SignerResponse<>(jsonRpcErrorResponse, HttpResponseStatus.OK); + } + + return null; + } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ReplayProtectionAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ReplayProtectionAcceptanceTest.java index f4bca6e1f..d902664bc 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ReplayProtectionAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ReplayProtectionAcceptanceTest.java @@ -12,7 +12,7 @@ */ package tech.pegasys.ethsigner.tests.signing; -import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; +import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static org.assertj.core.api.Assertions.assertThat; import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED; import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.WRONG_CHAIN_ID; @@ -95,7 +95,7 @@ public void wrongChainId() { RECIPIENT, TRANSFER_AMOUNT_WEI)); - assertThat(signerResponse.status()).isEqualTo(BAD_REQUEST); + assertThat(signerResponse.status()).isEqualTo(OK); assertThat(signerResponse.jsonRpc().getError()).isEqualTo(WRONG_CHAIN_ID); } @@ -115,7 +115,7 @@ public void unnecessaryChainId() { RECIPIENT, TRANSFER_AMOUNT_WEI)); - assertThat(signerResponse.status()).isEqualTo(BAD_REQUEST); + assertThat(signerResponse.status()).isEqualTo(OK); assertThat(signerResponse.jsonRpc().getError()) .isEqualTo(REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED); } diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferAcceptanceTest.java index 169c09dfa..9a4a2fc04 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferAcceptanceTest.java @@ -13,6 +13,7 @@ package tech.pegasys.ethsigner.tests.signing; import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; +import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static org.assertj.core.api.Assertions.assertThat; import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT; import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.TRANSACTION_UPFRONT_COST_EXCEEDS_BALANCE; @@ -75,7 +76,7 @@ public void valueTransferFromAccountWithInsufficientFunds() { final SignerResponse signerResponse = ethSigner().transactions().submitExceptional(transaction); - assertThat(signerResponse.status()).isEqualTo(BAD_REQUEST); + assertThat(signerResponse.status()).isEqualTo(OK); assertThat(signerResponse.jsonRpc().getError()) .isEqualTo(TRANSACTION_UPFRONT_COST_EXCEEDS_BALANCE); diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java index 07bd1593d..8aec80335 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java @@ -70,7 +70,9 @@ public void run() { .setPort(config.getHttpListenPort()) .setHost(config.getHttpListenHost()) .setReuseAddress(true) - .setReusePort(true); + .setReusePort(true) + .setHandle100ContinueAutomatically(true) + .setCompressionSupported(true); final MetricsEndpoint metricsEndpoint = new MetricsEndpoint( diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/WebClientOptionsFactory.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/WebClientOptionsFactory.java index aad5ea623..2417d7416 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/WebClientOptionsFactory.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/WebClientOptionsFactory.java @@ -31,7 +31,8 @@ public WebClientOptions createWebClientOptions(final Config config) { final WebClientOptions clientOptions = new WebClientOptions() .setDefaultPort(config.getDownstreamHttpPort()) - .setDefaultHost(config.getDownstreamHttpHost()); + .setDefaultHost(config.getDownstreamHttpHost()) + .setTryUseCompression(true); applyTlsOptions(clientOptions, config); return clientOptions; diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitter.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitter.java index c9fe1f346..cb2949a9e 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitter.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitter.java @@ -17,7 +17,10 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Map.Entry; +import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import io.vertx.core.Vertx; import io.vertx.core.http.HttpClient; @@ -57,7 +60,15 @@ public void sendRequest( final Iterable> headers, final String path, final String body) { - LOG.debug("Sending request {} to {}", body, path); + LOG.debug( + "Sending headers {} and request {} to {} ", + () -> + StreamSupport.stream(headers.spliterator(), false) + .map(Objects::toString) + .collect(Collectors.joining(", ")), + () -> body, + () -> path); + final String fullPath = downstreamPathCalculator.calculateDownstreamPath(path); final HttpClientRequest request = downStreamConnection.request(method, fullPath, this::handleResponse); diff --git a/gradle.properties b/gradle.properties index 254b25659..17321884e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1g -besuVersion=1.5.0 +besuVersion=21.1.1 besuDistroUrl=https://hyperledger.jfrog.io/artifactory/besu-binaries/besu/${besuVersion}/besu-${besuVersion}.tar.gz hashicorpVaultVersion=1.4.3 hashicorpVaultUrl=https://releases.hashicorp.com/vault From 3645eba3e64fb9513b8d6d16996ece50ac18cca5 Mon Sep 17 00:00:00 2001 From: Usman Saleem Date: Sat, 13 Mar 2021 10:28:02 +1000 Subject: [PATCH 63/64] Enable tag build in circleci config (#364) --- .circleci/config.yml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a7028491e..6bfcb1ee9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -216,23 +216,34 @@ workflows: jobs: - build: context: - - dockerhub-quorumengineering-ro + - dockerhub-quorumengineering-ro + filters: + tags: &filters-release-tags + only: /^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?/ - acceptanceTests: requires: - build context: - - dockerhub-quorumengineering-ro + - dockerhub-quorumengineering-ro + filters: + tags: + <<: *filters-release-tags - buildDocker: requires: - build context: - - dockerhub-quorumengineering-ro + - dockerhub-quorumengineering-ro + filters: + tags: + <<: *filters-release-tags - publish: filters: branches: only: - master - /^release-.*/ + tags: + <<: *filters-release-tags requires: - build - acceptanceTests @@ -245,6 +256,8 @@ workflows: only: - master - /^release-.*/ + tags: + <<: *filters-release-tags requires: - build - acceptanceTests From 42e02420e3cd446d4bb5326060d88ae283b6a565 Mon Sep 17 00:00:00 2001 From: mkrielza Date: Mon, 15 Mar 2021 10:20:22 +0200 Subject: [PATCH 64/64] Minimal diffs --- CHANGELOG.md | 17 ++++++- CONTRIBUTING.md | 4 +- README.md | 2 +- .../ethsigner/tests/AcceptanceTestBase.java | 2 +- .../ethsigner/tests/CorsAcceptanceTest.java | 2 +- .../DataPathFeatureFlagAcceptanceTest.java | 2 +- .../tests/MethodNotFoundAcceptanceTest.java | 6 +-- .../tests/UpCheckAcceptanceTest.java | 2 +- .../AccountManagementAcceptanceTest.java | 2 +- .../pegasys/ethsigner/tests/dsl/Account.java | 2 +- .../pegasys/ethsigner/tests/dsl/Accounts.java | 2 +- .../pegasys/ethsigner/tests/dsl/Besu.java | 2 +- .../ethsigner/tests/dsl/Contracts.java | 10 ++-- .../tech/pegasys/ethsigner/tests/dsl/Eea.java | 19 +++---- .../tech/pegasys/ethsigner/tests/dsl/Eth.java | 12 ++++- .../tech/pegasys/ethsigner/tests/dsl/Gas.java | 2 +- .../ethsigner/tests/dsl/PrivateContracts.java | 25 ++++++++- .../tests/dsl/PrivateTransaction.java | 2 +- .../ethsigner/tests/dsl/PublicContracts.java | 11 +++- .../tests/dsl/RawJsonRpcRequestFactory.java | 2 +- .../tests/dsl/RawJsonRpcRequests.java | 8 ++- .../ethsigner/tests/dsl/Transactions.java | 8 +-- .../ethsigner/tests/dsl/http/HttpRequest.java | 2 +- .../tests/dsl/http/HttpResponse.java | 2 +- .../ethsigner/tests/dsl/node/Node.java | 2 +- .../tests/dsl/node/besu/BesuNodeFactory.java | 1 + .../tests/dsl/node/besu/BesuNodePorts.java | 2 +- .../dsl/signer/EthSignerProcessRunner.java | 2 +- .../ethsigner/tests/dsl/signer/Signer.java | 2 +- .../tests/dsl/signer/SignerConfiguration.java | 2 +- .../signer/SignerConfigurationBuilder.java | 2 +- .../tests/dsl/signer/SignerResponse.java | 17 ++++++- .../TransactionSignerParamsSupplier.java | 2 +- .../tests/dsl/tls/BasicTlsOptions.java | 2 +- .../tests/dsl/tls/ClientTlsConfig.java | 2 +- .../tests/dsl/tls/OkHttpClientHelpers.java | 2 +- .../dsl/tls/TlsCertificateDefinition.java | 2 +- .../tests/dsl/utils/ExceptionUtils.java | 2 +- .../ethsigner/tests/dsl/utils/Hex.java | 2 +- .../ethsigner/tests/dsl/utils/WaitUtils.java | 2 +- .../AzureBasedTomlLoadingAcceptanceTest.java | 2 +- .../FileBasedTomlLoadingAcceptanceTest.java | 2 +- ...shicorpBasedTomlLoadingAcceptanceTest.java | 2 +- ...corpTlsBasedTomlLoadingAcceptanceTest.java | 2 +- .../MultiKeyAcceptanceTestBase.java | 2 +- .../MultiKeySigningAcceptanceTest.java | 2 +- ...yAzureTransactionSignerAcceptanceTest.java | 2 +- ...eBasedTransactionSignerAcceptanceTest.java | 2 +- ...hicorpTransactionSignerAcceptanceTest.java | 2 +- ...yTransactionSigningAcceptanceTestBase.java | 2 +- .../proxy/PassThroughAcceptanceTest.java | 2 +- .../PrivateTransactionAcceptanceTest.java | 2 +- .../ReplayProtectionAcceptanceTest.java | 4 +- .../signing/SmartContractAcceptanceTest.java | 2 +- .../signing/ValueTransferAcceptanceTest.java | 5 +- .../ValueTransferWithAzureAcceptanceTest.java | 2 +- .../tests/signing/contract/SimpleStorage.sol | 2 +- .../contract/generated/SimpleStorage.java | 2 +- ...ueTransferWithHashicorpAcceptanceTest.java | 2 +- ...nsferWithHashicorpOnTlsAcceptanceTest.java | 2 +- .../FailedConnectionAcceptanceTest.java | 2 +- .../timeouts/ReadTimeoutAcceptanceTest.java | 2 +- .../tls/ServerSideTlsAcceptanceTest.java | 2 +- build.gradle | 7 +-- docs/README.md | 6 +-- ethsigner/app/build.gradle | 2 +- .../tech/pegasys/ethsigner/EthSignerApp.java | 2 +- ethsigner/build.gradle | 2 +- .../pegasys/ethsigner/ApplicationInfo.java | 2 +- .../pegasys/ethsigner/CommandlineParser.java | 2 +- .../ethsigner/EthSignerBaseCommand.java | 2 +- .../pegasys/ethsigner/PlatformDetector.java | 2 +- .../pegasys/ethsigner/SignerSubCommand.java | 2 +- .../tech/pegasys/ethsigner/VersionInfo.java | 2 +- .../pegasys/ethsigner/VersionProvider.java | 2 +- .../pegasys/ethsigner/CmdlineHelpers.java | 2 +- ...CommandlineParserClientTlsOptionsTest.java | 2 +- .../ethsigner/CommandlineParserTest.java | 2 +- .../ethsigner/NullSignerSubCommand.java | 2 +- ethsigner/core/build.gradle | 2 +- .../jsonrpcproxy/DefaultTestBase.java | 2 +- .../EthAccountsIntegrationTest.java | 2 +- .../EthSignerParsingIntegrationTest.java | 2 +- .../FailedConnectionIntegrationTest.java | 2 +- .../jsonrpcproxy/IntegrationTestBase.java | 2 +- .../jsonrpcproxy/ProxyIntegrationTest.java | 2 +- ...ningEeaSendTransactionIntegrationTest.java | 2 +- ...ningEthSendTransactionIntegrationTest.java | 2 +- ...TransactionWithChainIdIntegrationTest.java | 2 +- .../ethsigner/jsonrpcproxy/TimeoutTest.java | 2 +- .../model/jsonrpc/EeaSendRawTransaction.java | 2 +- .../model/jsonrpc/EeaSendTransaction.java | 2 +- .../jsonrpc/EthProtocolVersionRequest.java | 2 +- .../model/jsonrpc/PrivateTransaction.java | 2 +- .../model/jsonrpc/SendRawTransaction.java | 2 +- .../model/jsonrpc/SendTransaction.java | 2 +- .../model/jsonrpc/Transaction.java | 2 +- .../model/request/EthNodeRequest.java | 2 +- .../model/request/EthRequestFactory.java | 2 +- .../model/request/EthSignerRequest.java | 2 +- .../model/response/EthNodeResponse.java | 2 +- .../model/response/EthResponseFactory.java | 2 +- .../model/response/EthSignerResponse.java | 2 +- .../support/TransactionCountResponder.java | 2 +- .../pegasys/ethsigner/core/EthSigner.java | 6 ++- .../core/InitializationException.java | 2 +- .../tech/pegasys/ethsigner/core/Runner.java | 2 +- .../core/WebClientOptionsFactory.java | 3 +- .../pegasys/ethsigner/core/config/Config.java | 2 +- .../ethsigner/core/config/TlsOptions.java | 2 +- .../core/http/HttpResponseFactory.java | 2 +- .../core/http/JsonRpcErrorHandler.java | 2 +- .../ethsigner/core/http/JsonRpcHandler.java | 2 +- .../ethsigner/core/http/LogErrorHandler.java | 2 +- .../ethsigner/core/http/RequestMapper.java | 2 +- .../ethsigner/core/http/UpcheckHandler.java | 2 +- .../EeaSendTransactionJsonParameters.java | 2 +- .../EthSendTransactionJsonParameters.java | 27 +++++++++- .../ethsigner/core/jsonrpc/JsonDecoder.java | 2 +- .../core/jsonrpc/JsonRpcRequest.java | 2 +- .../core/jsonrpc/JsonRpcRequestId.java | 2 +- .../ethsigner/core/jsonrpc/RpcUtil.java | 17 ++++++- .../InvalidJsonRpcRequestException.java | 2 +- .../jsonrpc/exception/JsonRpcException.java | 2 +- .../core/jsonrpc/response/JsonRpcError.java | 2 +- .../response/JsonRpcErrorResponse.java | 2 +- .../jsonrpc/response/JsonRpcResponse.java | 2 +- .../jsonrpc/response/JsonRpcResponseType.java | 2 +- .../response/JsonRpcSuccessResponse.java | 2 +- .../core/metrics/MetricsEndpoint.java | 10 ++-- .../requesthandler/JsonRpcRequestHandler.java | 2 +- .../core/requesthandler/ResultProvider.java | 2 +- .../VertxRequestTransmitter.java | 15 +++++- .../VertxRequestTransmitterFactory.java | 2 +- .../EthAccountsResultProvider.java | 2 +- .../InternalResponseHandler.java | 2 +- .../passthrough/PassThroughHandler.java | 2 +- .../sendtransaction/NonceProvider.java | 2 +- .../NonceTooLowRetryMechanism.java | 2 +- .../sendtransaction/RetryMechanism.java | 2 +- .../RetryingTransactionTransmitter.java | 2 +- .../SendTransactionHandler.java | 17 ++++--- .../TransactionTransmitter.java | 12 ++--- .../transaction/BesuPrivateNonceProvider.java | 2 +- .../transaction/BesuPrivateTransaction.java | 2 +- .../transaction/EeaPrivateTransaction.java | 2 +- .../transaction/EthTransaction.java | 25 ++++++--- .../transaction/PrivateTransaction.java | 17 +++++-- .../transaction/Transaction.java | 8 ++- .../transaction/TransactionFactory.java | 23 +++++++-- .../VertxNonceRequestTransmitter.java | 48 +++++++----------- .../core/signing/ChainIdProvider.java | 2 +- .../core/signing/ConfigurationChainId.java | 2 +- .../core/signing/TransactionSerializer.java | 6 +-- .../EeaSendTransactionJsonParametersTest.java | 2 +- .../EthSendTransactionJsonParametersTest.java | 2 +- .../core/jsonrpc/JsonRpcRequestTest.java | 2 +- .../response/JsonRpcErrorResponseTest.java | 2 +- .../EthAccountsResultProviderTest.java | 2 +- .../NonceTooLowRetryMechanismTest.java | 2 +- .../EeaPrivateTransactionTest.java | 2 +- .../transaction/EthTransactionTest.java | 2 +- .../SingleTransactionSignerProviderTest.java | 2 +- .../jsonrpcproxy/RequestMapperTest.java | 2 +- .../subcommands/AzureSubCommand.java | 2 +- .../subcommands/FileBasedSubCommand.java | 2 +- .../subcommands/HashicorpSubCommand.java | 2 +- .../subcommands/MultiKeySubCommand.java | 2 +- .../ethsigner/subcommands/RawSubCommand.java | 2 +- .../subcommands/FileBasedSubCommandTest.java | 2 +- .../subcommands/HashicorpSubCommandTest.java | 2 +- .../subcommands/MultiKeySubCommandTest.java | 2 +- gradle.properties | 6 +-- gradle/check-licenses.gradle | 5 ++ gradle/versions.gradle | 6 +-- gradle/wrapper/gradle-wrapper.jar | Bin 58702 -> 58910 bytes 176 files changed, 424 insertions(+), 271 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fdf93b34..32b09358d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,24 @@ # Changelog +## 21.3.0 + +### Features Added +- Upgraded besu-metrics library +- Publish ethsigner module jars to cloudsmith maven repo +- Update Besu latest version for Acceptance Tests +- Add gzip compression support + +## 21.1.0 + +### Features Added +- Publish artifacts to [cloudsmith](https://cloudsmith.io/~consensys/repos/ethsigner). +- Added support for sending GoQuorum private transactions via "eth_sendTransaction" JSON-RPC +- Improve error message if HTTP server fails to start + ## 20.10.0 ### Features Added -- Added "eth_signTransaction" JSON RPC +- Added "eth_signTransaction" JSON-RPC - Docker namespace updated to reflect "consensys/quorum-ethsigner" - Strip ACCESS_CONTROL_ALLOW_ORIGIN header from responses received from the web3provider - Added a Prometheus metrics endpoint, reporting basic application metrics diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index be3b71524..49bd4b69f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -244,8 +244,8 @@ These are not strictly enforced during the build, but should be adhered to and c [private-quorum@consensys.net]: mailto:private-quorum@consensys.net [Discord]: https://discord.gg/5U9Jwp7 -[Github issues]: https://github.com/PegaSysEng/ethsigner/issues -[EthSigner documentation]: https://docs.ethsigner.pegasys.tech/ +[GitHub issues]: https://github.com/ConsenSys/ethsigner/issues +[EthSigner documentation]: https://docs.ethsigner.consensys.net/ [CLA.md]: /CLA.md [Code Reviews]: /community/code-reviews.md [MkDocs]: https://www.mkdocs.org/ diff --git a/README.md b/README.md index 6fd25f139..ff4999ac3 100644 --- a/README.md +++ b/README.md @@ -22,4 +22,4 @@ See our [contribution guidelines](CONTRIBUTING.md) for more detail on searching * [Release Notes](CHANGELOG.md) [Discord]: https://discord.gg/5U9Jwp7 -[Github issues]: https://github.com/PegaSysEng/ethsigner/issues +[GitHub issues]: https://github.com/ConsenSys/ethsigner/issues diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/AcceptanceTestBase.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/AcceptanceTestBase.java index 40e59442f..476e97223 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/AcceptanceTestBase.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/AcceptanceTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/CorsAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/CorsAcceptanceTest.java index 7590d7a2b..86598ca4f 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/CorsAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/CorsAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/DataPathFeatureFlagAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/DataPathFeatureFlagAcceptanceTest.java index 362c73ed3..684451b76 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/DataPathFeatureFlagAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/DataPathFeatureFlagAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/MethodNotFoundAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/MethodNotFoundAcceptanceTest.java index 331aaa9f6..cd3d0aca1 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/MethodNotFoundAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/MethodNotFoundAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -28,7 +28,7 @@ public void sendDisabledApiReturnsBadRequest() { final SignerResponse response = ethSigner().rawJsonRpcRequests().exceptionalRequest("ibft_getPendingVotes"); - assertThat(response.status()).isEqualTo(HttpResponseStatus.BAD_REQUEST); + assertThat(response.status()).isEqualTo(HttpResponseStatus.OK); assertThat(response.jsonRpc().getError()).isEqualTo(JsonRpcError.METHOD_NOT_ENABLED); } @@ -37,7 +37,7 @@ public void unknownJsonRpcMethodReturnsBadRequest() { final SignerResponse response = ethSigner().rawJsonRpcRequests().exceptionalRequest("invalidJsonRpcMethod"); - assertThat(response.status()).isEqualTo(HttpResponseStatus.BAD_REQUEST); + assertThat(response.status()).isEqualTo(HttpResponseStatus.OK); assertThat(response.jsonRpc().getError()).isEqualTo(JsonRpcError.METHOD_NOT_FOUND); } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/UpCheckAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/UpCheckAcceptanceTest.java index e7900eb84..e8113ecf3 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/UpCheckAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/UpCheckAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/account/AccountManagementAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/account/AccountManagementAcceptanceTest.java index 84dfbcee9..40eda8494 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/account/AccountManagementAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/account/AccountManagementAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Account.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Account.java index b182f3fe2..366c7302b 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Account.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Account.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Accounts.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Accounts.java index 801ce3c8b..5f5ddec3d 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Accounts.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Accounts.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Besu.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Besu.java index cb89047c6..2e4548d9d 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Besu.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Besu.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Contracts.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Contracts.java index 6b9cbfb72..1a5c75b40 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Contracts.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Contracts.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -13,7 +13,6 @@ package tech.pegasys.ethsigner.tests.dsl; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.fail; import static tech.pegasys.ethsigner.tests.WaitUtils.waitFor; import static tech.pegasys.ethsigner.tests.dsl.utils.ExceptionUtils.failOnIOException; @@ -39,6 +38,9 @@ public abstract class Contracts { public abstract String sendTransaction(T smartContract) throws IOException; + public abstract SignerResponse sendTransactionExpectsError(T smartContract) + throws IOException; + public abstract Optional getTransactionReceipt(final String hash) throws IOException; @@ -48,13 +50,11 @@ public String submit(final T smartContract) { public SignerResponse submitExceptional(final T smartContract) { try { - submit(smartContract); - fail("Expecting exceptional response "); + return failOnIOException(() -> sendTransactionExpectsError(smartContract)); } catch (final ClientConnectionException e) { LOG.info("ClientConnectionException with message: " + e.getMessage()); return SignerResponse.fromError(e); } - return null; } public void awaitBlockContaining(final String hash) { diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eea.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eea.java index f58c5dbfd..8b5d618c7 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eea.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eea.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -13,7 +13,6 @@ package tech.pegasys.ethsigner.tests.dsl; import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; @@ -27,16 +26,10 @@ public Eea(final RawJsonRpcRequestFactory requestFactory) { this.requestFactory = requestFactory; } - public String sendTransaction(final PrivateTransaction transaction) throws IOException { - final EthSendTransaction response = - requestFactory - .createRequest( - "eea_sendTransaction", singletonList(transaction), EthSendTransaction.class) - .send(); - - assertThat(response.getTransactionHash()).isNotEmpty(); - assertThat(response.getError()).isNull(); - - return response.getTransactionHash(); + public EthSendTransaction sendTransaction(final PrivateTransaction transaction) + throws IOException { + return requestFactory + .createRequest("eea_sendTransaction", singletonList(transaction), EthSendTransaction.class) + .send(); } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eth.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eth.java index 97386f13b..198c96081 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eth.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Eth.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -14,6 +14,9 @@ import static org.assertj.core.api.Assertions.assertThat; +import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcErrorResponse; +import tech.pegasys.ethsigner.tests.dsl.signer.SignerResponse; + import java.io.IOException; import java.math.BigInteger; import java.util.List; @@ -42,6 +45,13 @@ public String sendTransaction(final Transaction transaction) throws IOException return response.getTransactionHash(); } + public SignerResponse sendTransactionExpectsError( + final Transaction transaction) throws IOException { + final EthSendTransaction response = jsonRpc.ethSendTransaction(transaction).send(); + assertThat(response.hasError()).isTrue(); + return SignerResponse.fromWeb3jErrorResponse(response); + } + public List getAccounts() throws IOException { return jsonRpc.ethAccounts().send().getAccounts(); } diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Gas.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Gas.java index 621c9a525..95d78fa1e 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Gas.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Gas.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateContracts.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateContracts.java index 5fb7131cc..b4713180f 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateContracts.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateContracts.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -12,9 +12,15 @@ */ package tech.pegasys.ethsigner.tests.dsl; +import static org.assertj.core.api.Assertions.assertThat; + +import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcErrorResponse; +import tech.pegasys.ethsigner.tests.dsl.signer.SignerResponse; + import java.io.IOException; import java.util.Optional; +import org.web3j.protocol.core.methods.response.EthSendTransaction; import org.web3j.protocol.core.methods.response.TransactionReceipt; public class PrivateContracts extends Contracts { @@ -29,7 +35,22 @@ public PrivateContracts(final Besu besu, final Eea eea) { @Override public String sendTransaction(final PrivateTransaction smartContract) throws IOException { - return eea.sendTransaction(smartContract); + final EthSendTransaction response = eea.sendTransaction(smartContract); + + assertThat(response.getTransactionHash()).isNotEmpty(); + assertThat(response.getError()).isNull(); + + return response.getTransactionHash(); + } + + @Override + public SignerResponse sendTransactionExpectsError( + final PrivateTransaction smartContract) throws IOException { + final EthSendTransaction response = eea.sendTransaction(smartContract); + + assertThat(response.hasError()).isTrue(); + + return SignerResponse.fromWeb3jErrorResponse(response); } @Override diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateTransaction.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateTransaction.java index 7d16bfc81..1017b5a98 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateTransaction.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PrivateTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PublicContracts.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PublicContracts.java index 086455f6b..9893fca2f 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PublicContracts.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/PublicContracts.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -15,6 +15,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static tech.pegasys.ethsigner.tests.dsl.utils.ExceptionUtils.failOnIOException; +import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcErrorResponse; +import tech.pegasys.ethsigner.tests.dsl.signer.SignerResponse; + import java.io.IOException; import java.util.Optional; @@ -34,6 +37,12 @@ public String sendTransaction(final Transaction smartContract) throws IOExceptio return eth.sendTransaction(smartContract); } + @Override + public SignerResponse sendTransactionExpectsError( + final Transaction smartContract) throws IOException { + return eth.sendTransactionExpectsError(smartContract); + } + @Override public Optional getTransactionReceipt(final String hash) throws IOException { return eth.getTransactionReceipt(hash); diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequestFactory.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequestFactory.java index 8ef29194d..a0aa4b4ee 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequestFactory.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequestFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequests.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequests.java index 2b92f5905..cab48ab9e 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequests.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/RawJsonRpcRequests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -54,7 +54,11 @@ public SignerResponse exceptionalRequest( final Request request = requestFactory.createRequest(method); try { - failOnIOException(request::send); + final SignerResponse response = + SignerResponse.fromWeb3jErrorResponse(failOnIOException(request::send)); + if (response != null) { + return response; + } fail("Expecting exceptional response "); return null; } catch (final ClientConnectionException e) { diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Transactions.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Transactions.java index 42db63efd..3f592e2c0 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Transactions.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/Transactions.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -13,7 +13,6 @@ package tech.pegasys.ethsigner.tests.dsl; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; import static tech.pegasys.ethsigner.tests.WaitUtils.waitFor; import static tech.pegasys.ethsigner.tests.dsl.utils.ExceptionUtils.failOnIOException; @@ -44,14 +43,11 @@ public String submit(final Transaction transaction) { public SignerResponse submitExceptional(final Transaction transaction) { try { - failOnIOException(() -> eth.sendTransaction(transaction)); - fail("Expecting exceptional response "); + return failOnIOException(() -> eth.sendTransactionExpectsError(transaction)); } catch (final ClientConnectionException e) { LOG.info("ClientConnectionException with message: " + e.getMessage()); return SignerResponse.fromError(e); } - - return null; } public void awaitBlockContaining(final String hash) { diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/http/HttpRequest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/http/HttpRequest.java index b582c8179..a6184566b 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/http/HttpRequest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/http/HttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/http/HttpResponse.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/http/HttpResponse.java index 3d29624fa..b38faf4ae 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/http/HttpResponse.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/http/HttpResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/Node.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/Node.java index 950d0ba6e..d75c1d375 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/Node.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/Node.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/besu/BesuNodeFactory.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/besu/BesuNodeFactory.java index 85dbae62b..598c706f9 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/besu/BesuNodeFactory.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/besu/BesuNodeFactory.java @@ -66,6 +66,7 @@ public static BesuNode create(final BesuNodeConfig config) { params.add(config.getHostName()); params.add("--rpc-http-apis"); params.add("ETH,NET,WEB3,EEA"); + params.add("--min-gas-price=0"); params.add("--privacy-enabled"); params.add("--privacy-public-key-file"); params.add(privacyPublicKeyFilePath()); diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/besu/BesuNodePorts.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/besu/BesuNodePorts.java index 1b7a1e7ed..e4a01c5f8 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/besu/BesuNodePorts.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/node/besu/BesuNodePorts.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/EthSignerProcessRunner.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/EthSignerProcessRunner.java index 58fd11fe3..8d1b123ae 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/EthSignerProcessRunner.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/EthSignerProcessRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/Signer.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/Signer.java index 5cfc5c883..74e22a12f 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/Signer.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/Signer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfiguration.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfiguration.java index c663e6622..3f88d933e 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfiguration.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfigurationBuilder.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfigurationBuilder.java index 6dda7af41..56eed46c9 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfigurationBuilder.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerConfigurationBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerResponse.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerResponse.java index c6eeccf7b..df5814a6d 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerResponse.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/SignerResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -12,11 +12,13 @@ */ package tech.pegasys.ethsigner.tests.dsl.signer; +import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError; import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcErrorResponse; import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.vertx.core.json.Json; +import org.web3j.protocol.core.Response; import org.web3j.protocol.exceptions.ClientConnectionException; public class SignerResponse { @@ -54,4 +56,17 @@ public static SignerResponse fromError(final ClientConnect throw new RuntimeException("Unable to parse web3j exception message", e); } } + + public static SignerResponse fromWeb3jErrorResponse( + final Response response) { + if (response != null && response.hasError()) { + final Response.Error error = response.getError(); + final JsonRpcError jsonRpcError = JsonRpcError.fromJson(error.getCode(), error.getMessage()); + final JsonRpcErrorResponse jsonRpcErrorResponse = + new JsonRpcErrorResponse(response.getId(), jsonRpcError); + return new SignerResponse<>(jsonRpcErrorResponse, HttpResponseStatus.OK); + } + + return null; + } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/TransactionSignerParamsSupplier.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/TransactionSignerParamsSupplier.java index 837ad3da6..489c06d99 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/TransactionSignerParamsSupplier.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/TransactionSignerParamsSupplier.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/BasicTlsOptions.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/BasicTlsOptions.java index 99de04b1a..9b5048545 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/BasicTlsOptions.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/BasicTlsOptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/ClientTlsConfig.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/ClientTlsConfig.java index 906a3e87d..c5f34fbdd 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/ClientTlsConfig.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/ClientTlsConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/OkHttpClientHelpers.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/OkHttpClientHelpers.java index 3b1af9dc1..9c8f4b2fe 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/OkHttpClientHelpers.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/OkHttpClientHelpers.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/TlsCertificateDefinition.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/TlsCertificateDefinition.java index 13c65ab25..e458c28fc 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/TlsCertificateDefinition.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/tls/TlsCertificateDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/ExceptionUtils.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/ExceptionUtils.java index 74fecc112..79f9d7226 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/ExceptionUtils.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/ExceptionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/Hex.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/Hex.java index fb4fc65c7..6e1cf7bf6 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/Hex.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/Hex.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/WaitUtils.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/WaitUtils.java index 95b5fcb2e..04d9f7e84 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/WaitUtils.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/utils/WaitUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/AzureBasedTomlLoadingAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/AzureBasedTomlLoadingAcceptanceTest.java index 4b9aaf408..b78ccd4a1 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/AzureBasedTomlLoadingAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/AzureBasedTomlLoadingAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/FileBasedTomlLoadingAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/FileBasedTomlLoadingAcceptanceTest.java index 5fe431f13..2aa44803b 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/FileBasedTomlLoadingAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/FileBasedTomlLoadingAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/HashicorpBasedTomlLoadingAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/HashicorpBasedTomlLoadingAcceptanceTest.java index 9fc146a9b..be124abf2 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/HashicorpBasedTomlLoadingAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/HashicorpBasedTomlLoadingAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/HashicorpTlsBasedTomlLoadingAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/HashicorpTlsBasedTomlLoadingAcceptanceTest.java index 457ac9f77..e034f64f3 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/HashicorpTlsBasedTomlLoadingAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/HashicorpTlsBasedTomlLoadingAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/MultiKeyAcceptanceTestBase.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/MultiKeyAcceptanceTestBase.java index 5dd104a78..f627d8074 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/MultiKeyAcceptanceTestBase.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/MultiKeyAcceptanceTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/MultiKeySigningAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/MultiKeySigningAcceptanceTest.java index 26e517ab9..d0566b9a4 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/MultiKeySigningAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/MultiKeySigningAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyAzureTransactionSignerAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyAzureTransactionSignerAcceptanceTest.java index 48564308a..ce5325841 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyAzureTransactionSignerAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyAzureTransactionSignerAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyFileBasedTransactionSignerAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyFileBasedTransactionSignerAcceptanceTest.java index 049c696e9..8e770c92a 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyFileBasedTransactionSignerAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyFileBasedTransactionSignerAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyHashicorpTransactionSignerAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyHashicorpTransactionSignerAcceptanceTest.java index 6bd25c599..a499a8b18 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyHashicorpTransactionSignerAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyHashicorpTransactionSignerAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyTransactionSigningAcceptanceTestBase.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyTransactionSigningAcceptanceTestBase.java index b9ae5f2ea..bb2ae7bbc 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyTransactionSigningAcceptanceTestBase.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/multikeysigner/transactionsigning/MultiKeyTransactionSigningAcceptanceTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/proxy/PassThroughAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/proxy/PassThroughAcceptanceTest.java index f50b05d4c..fe0acf407 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/proxy/PassThroughAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/proxy/PassThroughAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/PrivateTransactionAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/PrivateTransactionAcceptanceTest.java index 4fac30431..be3148c48 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/PrivateTransactionAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/PrivateTransactionAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ReplayProtectionAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ReplayProtectionAcceptanceTest.java index 1a0cf672c..dbc3c838e 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ReplayProtectionAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ReplayProtectionAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -12,7 +12,7 @@ */ package tech.pegasys.ethsigner.tests.signing; -import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; +import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static org.assertj.core.api.Assertions.assertThat; import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED; import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.WRONG_CHAIN_ID; diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/SmartContractAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/SmartContractAcceptanceTest.java index 7c1ab029f..4b03ae3d3 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/SmartContractAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/SmartContractAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferAcceptanceTest.java index 3484ab9ff..9a4a2fc04 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -13,6 +13,7 @@ package tech.pegasys.ethsigner.tests.signing; import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; +import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static org.assertj.core.api.Assertions.assertThat; import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT; import static tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError.TRANSACTION_UPFRONT_COST_EXCEEDS_BALANCE; @@ -75,7 +76,7 @@ public void valueTransferFromAccountWithInsufficientFunds() { final SignerResponse signerResponse = ethSigner().transactions().submitExceptional(transaction); - assertThat(signerResponse.status()).isEqualTo(BAD_REQUEST); + assertThat(signerResponse.status()).isEqualTo(OK); assertThat(signerResponse.jsonRpc().getError()) .isEqualTo(TRANSACTION_UPFRONT_COST_EXCEEDS_BALANCE); diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferWithAzureAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferWithAzureAcceptanceTest.java index 3b3012c31..44e8e515f 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferWithAzureAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/ValueTransferWithAzureAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/contract/SimpleStorage.sol b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/contract/SimpleStorage.sol index 829812a9d..12a1ffd7c 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/contract/SimpleStorage.sol +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/contract/SimpleStorage.sol @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/contract/generated/SimpleStorage.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/contract/generated/SimpleStorage.java index cf94118c9..f48966902 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/contract/generated/SimpleStorage.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/contract/generated/SimpleStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/hashicorp/ValueTransferWithHashicorpAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/hashicorp/ValueTransferWithHashicorpAcceptanceTest.java index ce4a6fafb..e8df977cc 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/hashicorp/ValueTransferWithHashicorpAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/hashicorp/ValueTransferWithHashicorpAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/hashicorp/ValueTransferWithHashicorpOnTlsAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/hashicorp/ValueTransferWithHashicorpOnTlsAcceptanceTest.java index 9e1b5d3dd..d6dd03133 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/hashicorp/ValueTransferWithHashicorpOnTlsAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/signing/hashicorp/ValueTransferWithHashicorpOnTlsAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/timeouts/FailedConnectionAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/timeouts/FailedConnectionAcceptanceTest.java index 2ef35a57f..5f0259cdb 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/timeouts/FailedConnectionAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/timeouts/FailedConnectionAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/timeouts/ReadTimeoutAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/timeouts/ReadTimeoutAcceptanceTest.java index 699d46cc9..2576652e2 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/timeouts/ReadTimeoutAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/timeouts/ReadTimeoutAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/tls/ServerSideTlsAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/tls/ServerSideTlsAcceptanceTest.java index 9cde42b9f..a7d19c761 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/tls/ServerSideTlsAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/tls/ServerSideTlsAcceptanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/build.gradle b/build.gradle index 88fa7e52c..0fd2aa38b 100644 --- a/build.gradle +++ b/build.gradle @@ -24,6 +24,7 @@ plugins { id 'me.champeau.gradle.jmh' version '0.5.0' apply false id 'net.ltgt.errorprone' version '1.1.1' id 'net.researchgate.release' version '2.8.1' + id 'org.ajoberstar.grgit' version '4.1.0' } String projectName = "EthSigner" @@ -277,7 +278,7 @@ subprojects { } pom { name = projectName + " - ${project.name}" - url = 'http://github.com/PegaSysEng/' + repositoryName + url = 'http://github.com/ConsenSys/' + repositoryName licenses { license { name = 'The Apache License, Version 2.0' @@ -285,10 +286,10 @@ subprojects { } } scm { - def hostPath = 'github.com/PegasysEng/' + repositoryName + '.git' + def hostPath = 'github.com/ConsenSys/' + repositoryName + '.git' connection = 'scm:git:git://' + hostPath developerConnection = 'scm:git:ssh://' + hostPath - url = 'https://github.com/PegaSysEng/' + repositoryName + url = 'https://github.com/ConsenSys/' + repositoryName } } } diff --git a/docs/README.md b/docs/README.md index 882a62411..04354312f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,5 @@ -EthSigner documentation was moved to as separate repository to help manage versions and releases. +EthSigner documentation is maintained as a separate repository to help manage versions and releases. -If you want to contribute to the doc site, make a pull request against https://github.com/PegaSysEng/doc.ethsigner +If you want to contribute to the doc site, make a pull request against https://github.com/ConsenSys/doc.ethsigner -The generated doc website is at https://docs.ethsigner.pegasys.tech/ \ No newline at end of file +The generated doc website is at https://docs.ethsigner.consensys.net/ \ No newline at end of file diff --git a/ethsigner/app/build.gradle b/ethsigner/app/build.gradle index 094ce85b4..836de20c2 100644 --- a/ethsigner/app/build.gradle +++ b/ethsigner/app/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/app/src/main/java/tech/pegasys/ethsigner/EthSignerApp.java b/ethsigner/app/src/main/java/tech/pegasys/ethsigner/EthSignerApp.java index 6665f0ace..8492650b0 100644 --- a/ethsigner/app/src/main/java/tech/pegasys/ethsigner/EthSignerApp.java +++ b/ethsigner/app/src/main/java/tech/pegasys/ethsigner/EthSignerApp.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/build.gradle b/ethsigner/build.gradle index 5e01de30f..d4a46e015 100644 --- a/ethsigner/build.gradle +++ b/ethsigner/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/ApplicationInfo.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/ApplicationInfo.java index 95ca3da54..b45d8d424 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/ApplicationInfo.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/ApplicationInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/CommandlineParser.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/CommandlineParser.java index 488266d1f..98f6341d2 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/CommandlineParser.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/CommandlineParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/EthSignerBaseCommand.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/EthSignerBaseCommand.java index 3e37f76ee..3765afd60 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/EthSignerBaseCommand.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/EthSignerBaseCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/PlatformDetector.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/PlatformDetector.java index 7795f9902..23e284adb 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/PlatformDetector.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/PlatformDetector.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/SignerSubCommand.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/SignerSubCommand.java index 1aa334a90..0849d7883 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/SignerSubCommand.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/SignerSubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/VersionInfo.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/VersionInfo.java index d7b2a8347..59167ee26 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/VersionInfo.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/VersionInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/VersionProvider.java b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/VersionProvider.java index a28d8e652..ac2a2aef0 100644 --- a/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/VersionProvider.java +++ b/ethsigner/commandline/src/main/java/tech/pegasys/ethsigner/VersionProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/test-support/java/tech/pegasys/ethsigner/CmdlineHelpers.java b/ethsigner/commandline/src/test-support/java/tech/pegasys/ethsigner/CmdlineHelpers.java index e62ba9b3f..7ce0ce1cf 100644 --- a/ethsigner/commandline/src/test-support/java/tech/pegasys/ethsigner/CmdlineHelpers.java +++ b/ethsigner/commandline/src/test-support/java/tech/pegasys/ethsigner/CmdlineHelpers.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/CommandlineParserClientTlsOptionsTest.java b/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/CommandlineParserClientTlsOptionsTest.java index 11ae625cf..55e17722a 100644 --- a/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/CommandlineParserClientTlsOptionsTest.java +++ b/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/CommandlineParserClientTlsOptionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/CommandlineParserTest.java b/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/CommandlineParserTest.java index 530f3f97d..2a060589b 100644 --- a/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/CommandlineParserTest.java +++ b/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/CommandlineParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/NullSignerSubCommand.java b/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/NullSignerSubCommand.java index 4af6e374d..e8cdbbc46 100644 --- a/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/NullSignerSubCommand.java +++ b/ethsigner/commandline/src/test/java/tech/pegasys/ethsigner/NullSignerSubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/build.gradle b/ethsigner/core/build.gradle index ed3e8c867..aa486b881 100644 --- a/ethsigner/core/build.gradle +++ b/ethsigner/core/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/DefaultTestBase.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/DefaultTestBase.java index db0654843..3e8a907ec 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/DefaultTestBase.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/DefaultTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/EthAccountsIntegrationTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/EthAccountsIntegrationTest.java index fe00da552..ada94373f 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/EthAccountsIntegrationTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/EthAccountsIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/EthSignerParsingIntegrationTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/EthSignerParsingIntegrationTest.java index 1a2d3166f..1f28d236a 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/EthSignerParsingIntegrationTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/EthSignerParsingIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/FailedConnectionIntegrationTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/FailedConnectionIntegrationTest.java index 5a2570eb9..806bded9b 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/FailedConnectionIntegrationTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/FailedConnectionIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IntegrationTestBase.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IntegrationTestBase.java index 03115d847..bbae84f8c 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IntegrationTestBase.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/IntegrationTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/ProxyIntegrationTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/ProxyIntegrationTest.java index f73c51ffb..b24be3cdc 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/ProxyIntegrationTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/ProxyIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEeaSendTransactionIntegrationTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEeaSendTransactionIntegrationTest.java index 755cd88ff..37897cae2 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEeaSendTransactionIntegrationTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEeaSendTransactionIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEthSendTransactionIntegrationTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEthSendTransactionIntegrationTest.java index 9df36803e..5ca3ba5a4 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEthSendTransactionIntegrationTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEthSendTransactionIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEthSendTransactionWithChainIdIntegrationTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEthSendTransactionWithChainIdIntegrationTest.java index ea7b5db03..b8c41e218 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEthSendTransactionWithChainIdIntegrationTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/SigningEthSendTransactionWithChainIdIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/TimeoutTest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/TimeoutTest.java index 90086e637..6f136e8b8 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/TimeoutTest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/TimeoutTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EeaSendRawTransaction.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EeaSendRawTransaction.java index ac549a1ff..d5ea9ac9d 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EeaSendRawTransaction.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EeaSendRawTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EeaSendTransaction.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EeaSendTransaction.java index 999459067..d21e6842c 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EeaSendTransaction.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EeaSendTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EthProtocolVersionRequest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EthProtocolVersionRequest.java index 653321b2e..f9a9562e2 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EthProtocolVersionRequest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/EthProtocolVersionRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/PrivateTransaction.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/PrivateTransaction.java index 837ce74f8..494324b00 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/PrivateTransaction.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/PrivateTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/SendRawTransaction.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/SendRawTransaction.java index 8881b7e9c..29108bb8c 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/SendRawTransaction.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/SendRawTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/SendTransaction.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/SendTransaction.java index 6af4c0b08..f6bf7e1b1 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/SendTransaction.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/SendTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/Transaction.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/Transaction.java index 4efbfde01..8a629df51 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/Transaction.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/jsonrpc/Transaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthNodeRequest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthNodeRequest.java index a231285bf..56d1cb915 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthNodeRequest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthNodeRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthRequestFactory.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthRequestFactory.java index ed1498cc8..e398d9f07 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthRequestFactory.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthRequestFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthSignerRequest.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthSignerRequest.java index 8b81de0fa..7c68fd985 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthSignerRequest.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/request/EthSignerRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthNodeResponse.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthNodeResponse.java index 1865a7285..0e696b0a5 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthNodeResponse.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthNodeResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthResponseFactory.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthResponseFactory.java index f4829ecbb..2076f4d92 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthResponseFactory.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthResponseFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthSignerResponse.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthSignerResponse.java index 8d5bbd270..fcd7c52c6 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthSignerResponse.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/model/response/EthSignerResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/support/TransactionCountResponder.java b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/support/TransactionCountResponder.java index c3dd0a3aa..2ad59b0b2 100644 --- a/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/support/TransactionCountResponder.java +++ b/ethsigner/core/src/integration-test/java/tech/pegasys/ethsigner/jsonrpcproxy/support/TransactionCountResponder.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java index 869fe1fe2..27422f9d8 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/EthSigner.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -70,7 +70,9 @@ public void run() { .setPort(config.getHttpListenPort()) .setHost(config.getHttpListenHost()) .setReuseAddress(true) - .setReusePort(true); + .setReusePort(true) + .setHandle100ContinueAutomatically(true) + .setCompressionSupported(true); final MetricsEndpoint metricsEndpoint = new MetricsEndpoint( diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/InitializationException.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/InitializationException.java index 10314c555..6f1569969 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/InitializationException.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/InitializationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java index 61c8d3f0e..99baf807e 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/Runner.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/WebClientOptionsFactory.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/WebClientOptionsFactory.java index aad5ea623..2417d7416 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/WebClientOptionsFactory.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/WebClientOptionsFactory.java @@ -31,7 +31,8 @@ public WebClientOptions createWebClientOptions(final Config config) { final WebClientOptions clientOptions = new WebClientOptions() .setDefaultPort(config.getDownstreamHttpPort()) - .setDefaultHost(config.getDownstreamHttpHost()); + .setDefaultHost(config.getDownstreamHttpHost()) + .setTryUseCompression(true); applyTlsOptions(clientOptions, config); return clientOptions; diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/Config.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/Config.java index fd6db820a..2a88c91c7 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/Config.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/Config.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/TlsOptions.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/TlsOptions.java index 598ca7797..d9c799962 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/TlsOptions.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/config/TlsOptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/HttpResponseFactory.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/HttpResponseFactory.java index fa6a7109b..bd312f285 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/HttpResponseFactory.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/HttpResponseFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/JsonRpcErrorHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/JsonRpcErrorHandler.java index 99b6da080..c4203796b 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/JsonRpcErrorHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/JsonRpcErrorHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/JsonRpcHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/JsonRpcHandler.java index b5b64e07a..3fd100ce7 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/JsonRpcHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/JsonRpcHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/LogErrorHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/LogErrorHandler.java index e3d02cbe3..a0f794aae 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/LogErrorHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/LogErrorHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/RequestMapper.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/RequestMapper.java index 647fbdacc..2657110d3 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/RequestMapper.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/RequestMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/UpcheckHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/UpcheckHandler.java index 61bef0185..c817ccae1 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/UpcheckHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/http/UpcheckHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EeaSendTransactionJsonParameters.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EeaSendTransactionJsonParameters.java index 9c6aec78c..8db18b12b 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EeaSendTransactionJsonParameters.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EeaSendTransactionJsonParameters.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParameters.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParameters.java index 49c4f7d85..31a116170 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParameters.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParameters.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -16,12 +16,16 @@ import static tech.pegasys.ethsigner.core.jsonrpc.RpcUtil.validateNotEmpty; import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSetter; +import org.web3j.utils.Base64String; @JsonIgnoreProperties(ignoreUnknown = true) public class EthSendTransactionJsonParameters { @@ -32,6 +36,8 @@ public class EthSendTransactionJsonParameters { private BigInteger value; private String receiver; private String data; + private Base64String privateFrom; + private List privateFor; @JsonCreator public EthSendTransactionJsonParameters(@JsonProperty("from") final String sender) { @@ -69,6 +75,17 @@ public void data(final String data) { this.data = data; } + @JsonSetter("privateFrom") + public void privateFrom(final String privateFrom) { + this.privateFrom = Base64String.wrap(privateFrom); + } + + @JsonSetter("privateFor") + public void privateFor(final String[] privateFor) { + this.privateFor = + Arrays.stream(privateFor).map(Base64String::wrap).collect(Collectors.toList()); + } + public Optional data() { return Optional.ofNullable(data); } @@ -96,4 +113,12 @@ public Optional nonce() { public String sender() { return sender; } + + public Optional privateFrom() { + return Optional.ofNullable(privateFrom); + } + + public Optional> privateFor() { + return Optional.ofNullable(privateFor); + } } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonDecoder.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonDecoder.java index 905ab83f1..3d25e175f 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonDecoder.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequest.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequest.java index cb3db9d86..ba7c4e0c5 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequest.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequestId.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequestId.java index e09d6c993..06db7d738 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequestId.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequestId.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/RpcUtil.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/RpcUtil.java index d2be13cd4..ea5d713a7 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/RpcUtil.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/RpcUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -14,9 +14,14 @@ import static org.web3j.utils.Numeric.decodeQuantity; +import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError; +import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcErrorResponse; + import java.math.BigInteger; import java.util.List; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.json.DecodeException; import io.vertx.core.json.JsonObject; public class RpcUtil { @@ -54,4 +59,14 @@ static void validateNotEmpty(final String value) { static BigInteger decodeBigInteger(final String value) { return value == null ? null : decodeQuantity(value); } + + public static JsonRpcError determineErrorCode(final String body, final JsonDecoder decoder) { + try { + final JsonRpcErrorResponse response = + decoder.decodeValue(Buffer.buffer(body), JsonRpcErrorResponse.class); + return response.getError(); + } catch (final DecodeException e) { + return JsonRpcError.INTERNAL_ERROR; + } + } } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/exception/InvalidJsonRpcRequestException.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/exception/InvalidJsonRpcRequestException.java index fd97c3939..9c4a8678e 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/exception/InvalidJsonRpcRequestException.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/exception/InvalidJsonRpcRequestException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/exception/JsonRpcException.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/exception/JsonRpcException.java index a7f73fbd6..855d68699 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/exception/JsonRpcException.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/exception/JsonRpcException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcError.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcError.java index 30f16d6e9..63475cce4 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcError.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcError.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcErrorResponse.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcErrorResponse.java index 604a56139..7029c0452 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcErrorResponse.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcErrorResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcResponse.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcResponse.java index fe6086909..5541dcef4 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcResponse.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcResponseType.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcResponseType.java index 4ec22db67..12086eaeb 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcResponseType.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcResponseType.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcSuccessResponse.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcSuccessResponse.java index ed36938fe..0e59dcfcd 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcSuccessResponse.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcSuccessResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/metrics/MetricsEndpoint.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/metrics/MetricsEndpoint.java index 1708ae46f..ec681f11d 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/metrics/MetricsEndpoint.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/metrics/MetricsEndpoint.java @@ -17,9 +17,9 @@ import java.util.Set; import io.vertx.core.Vertx; +import org.hyperledger.besu.metrics.MetricsService; +import org.hyperledger.besu.metrics.MetricsSystemFactory; import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; -import org.hyperledger.besu.metrics.prometheus.MetricsService; -import org.hyperledger.besu.metrics.prometheus.PrometheusMetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.metrics.MetricCategory; @@ -41,13 +41,13 @@ public MetricsEndpoint( metricsNetworkInterface, metricCategories, metricsHostAllowList); - this.metricsSystem = PrometheusMetricsSystem.init(metricsConfig); + this.metricsSystem = MetricsSystemFactory.create(metricsConfig); this.metricsConfig = metricsConfig; } public void start(final Vertx vertx) { if (metricsConfig.isEnabled()) { - metricsService = Optional.of(MetricsService.create(vertx, metricsConfig, metricsSystem)); + metricsService = MetricsService.create(vertx, metricsConfig, metricsSystem); } else { metricsService = Optional.empty(); } @@ -77,7 +77,7 @@ private MetricsConfiguration createMetricsConfiguration( .port(metricsPort) .host(metricsNetworkInterface) .metricCategories(metricCategories) - .hostsWhitelist(metricsHostAllowList) + .hostsAllowlist(metricsHostAllowList) .build(); } } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/JsonRpcRequestHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/JsonRpcRequestHandler.java index 9ee0aafba..ad025820e 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/JsonRpcRequestHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/JsonRpcRequestHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/ResultProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/ResultProvider.java index 22501eee3..a66502e76 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/ResultProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/ResultProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitter.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitter.java index 4e7e3496b..cb2949a9e 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitter.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitter.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -17,7 +17,10 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Map.Entry; +import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import io.vertx.core.Vertx; import io.vertx.core.http.HttpClient; @@ -57,7 +60,15 @@ public void sendRequest( final Iterable> headers, final String path, final String body) { - LOG.debug("Sending request {} to {}", body, path); + LOG.debug( + "Sending headers {} and request {} to {} ", + () -> + StreamSupport.stream(headers.spliterator(), false) + .map(Objects::toString) + .collect(Collectors.joining(", ")), + () -> body, + () -> path); + final String fullPath = downstreamPathCalculator.calculateDownstreamPath(path); final HttpClientRequest request = downStreamConnection.request(method, fullPath, this::handleResponse); diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitterFactory.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitterFactory.java index 272232819..ace37f2a9 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitterFactory.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/VertxRequestTransmitterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthAccountsResultProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthAccountsResultProvider.java index 8750ebc48..4f0f5ef62 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthAccountsResultProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/EthAccountsResultProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/InternalResponseHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/InternalResponseHandler.java index 311c2f435..587e08cf1 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/InternalResponseHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/internalresponse/InternalResponseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/passthrough/PassThroughHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/passthrough/PassThroughHandler.java index 9de24a537..ed3471f5d 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/passthrough/PassThroughHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/passthrough/PassThroughHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceProvider.java index a73f53d3d..1cccef60c 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceTooLowRetryMechanism.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceTooLowRetryMechanism.java index 434b92b8a..435188d75 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceTooLowRetryMechanism.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceTooLowRetryMechanism.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/RetryMechanism.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/RetryMechanism.java index 0e6a7e67b..12f4019d1 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/RetryMechanism.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/RetryMechanism.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/RetryingTransactionTransmitter.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/RetryingTransactionTransmitter.java index e0a96e916..69dff6e51 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/RetryingTransactionTransmitter.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/RetryingTransactionTransmitter.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java index d3a700175..6122c0d10 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/SendTransactionHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -21,8 +21,10 @@ import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; import tech.pegasys.ethsigner.core.requesthandler.JsonRpcRequestHandler; import tech.pegasys.ethsigner.core.requesthandler.VertxRequestTransmitterFactory; +import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.GoQuorumPrivateTransaction; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.Transaction; import tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction.TransactionFactory; +import tech.pegasys.ethsigner.core.signing.GoQuorumPrivateTransactionSerializer; import tech.pegasys.ethsigner.core.signing.TransactionSerializer; import tech.pegasys.signers.secp256k1.api.Signer; @@ -71,7 +73,6 @@ public void handle(final RoutingContext context, final JsonRpcRequest request) { return; } - LOG.debug("Obtaining signer for {}", transaction.sender()); final Optional signer = signerProvider.getSigner(transaction.sender()); if (signer.isEmpty()) { @@ -81,16 +82,20 @@ public void handle(final RoutingContext context, final JsonRpcRequest request) { return; } - final TransactionSerializer transactionSerializer = - new TransactionSerializer(signer.get(), chainId); - sendTransaction(transaction, transactionSerializer, context, request); + sendTransaction(transaction, context, signer.get(), request); } private void sendTransaction( final Transaction transaction, - final TransactionSerializer transactionSerializer, final RoutingContext routingContext, + final Signer signer, final JsonRpcRequest request) { + + final TransactionSerializer transactionSerializer = + transaction instanceof GoQuorumPrivateTransaction + ? new GoQuorumPrivateTransactionSerializer(signer, chainId) + : new TransactionSerializer(signer, chainId); + final TransactionTransmitter transmitter = createTransactionTransmitter(transaction, transactionSerializer, routingContext, request); transmitter.send(); diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/TransactionTransmitter.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/TransactionTransmitter.java index 0a6ed2b51..572853bb7 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/TransactionTransmitter.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/TransactionTransmitter.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -54,7 +54,7 @@ public TransactionTransmitter( } public void send() { - final Optional request = createSignedTransactionBody(); + final Optional request = createSignedTransactionPayload(); if (request.isEmpty()) { return; @@ -68,12 +68,10 @@ public void send() { } } - private Optional createSignedTransactionBody() { + private Optional createSignedTransactionPayload() { - if (!transaction.isNonceUserSpecified()) { - if (!populateNonce()) { - return Optional.empty(); - } + if (!populateNonce()) { + return Optional.empty(); } final String signedTransactionHexString; diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/BesuPrivateNonceProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/BesuPrivateNonceProvider.java index 41972b785..bc6f08c61 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/BesuPrivateNonceProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/BesuPrivateNonceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/BesuPrivateTransaction.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/BesuPrivateTransaction.java index 4f8983b79..4d00ee316 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/BesuPrivateTransaction.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/BesuPrivateTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EeaPrivateTransaction.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EeaPrivateTransaction.java index 5706e0675..bd935244b 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EeaPrivateTransaction.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EeaPrivateTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransaction.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransaction.java index bbc056ba0..0ddcb7406 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransaction.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -21,6 +21,7 @@ import java.util.List; import com.google.common.base.MoreObjects; +import org.jetbrains.annotations.NotNull; import org.web3j.crypto.RawTransaction; import org.web3j.crypto.Sign.SignatureData; import org.web3j.crypto.TransactionEncoder; @@ -31,10 +32,10 @@ public class EthTransaction implements Transaction { private static final String JSON_RPC_METHOD = "eth_sendRawTransaction"; - private final EthSendTransactionJsonParameters transactionJsonParameters; - private final NonceProvider nonceProvider; - private final JsonRpcRequestId id; - private BigInteger nonce; + protected final EthSendTransactionJsonParameters transactionJsonParameters; + protected final NonceProvider nonceProvider; + protected final JsonRpcRequestId id; + protected BigInteger nonce; public EthTransaction( final EthSendTransactionJsonParameters transactionJsonParameters, @@ -47,8 +48,16 @@ public EthTransaction( } @Override - public void updateNonce() { - this.nonce = nonceProvider.getNonce(); + public void updateFieldsIfRequired() { + if (!this.isNonceUserSpecified()) { + this.nonce = nonceProvider.getNonce(); + } + } + + @Override + @NotNull + public String getJsonRpcMethodName() { + return JSON_RPC_METHOD; } @Override @@ -90,7 +99,7 @@ public String toString() { .toString(); } - private RawTransaction createTransaction() { + protected RawTransaction createTransaction() { return RawTransaction.createTransaction( nonce, transactionJsonParameters.gasPrice().orElse(DEFAULT_GAS_PRICE), diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/PrivateTransaction.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/PrivateTransaction.java index 2cac74f68..18733abfa 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/PrivateTransaction.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/PrivateTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -21,6 +21,7 @@ import java.util.List; import com.google.common.base.MoreObjects; +import org.jetbrains.annotations.NotNull; import org.web3j.crypto.Sign.SignatureData; import org.web3j.protocol.eea.crypto.PrivateTransactionEncoder; import org.web3j.protocol.eea.crypto.RawPrivateTransaction; @@ -47,8 +48,10 @@ public abstract class PrivateTransaction implements Transaction { } @Override - public void updateNonce() { - this.nonce = nonceProvider.getNonce(); + public void updateFieldsIfRequired() { + if (!this.isNonceUserSpecified()) { + this.nonce = nonceProvider.getNonce(); + } } @Override @@ -73,7 +76,13 @@ public String sender() { @Override public JsonRpcRequest jsonRpcRequest( final String signedTransactionHexString, final JsonRpcRequestId id) { - return Transaction.jsonRpcRequest(signedTransactionHexString, id, JSON_RPC_METHOD); + return Transaction.jsonRpcRequest(signedTransactionHexString, id, getJsonRpcMethodName()); + } + + @Override + @NotNull + public String getJsonRpcMethodName() { + return JSON_RPC_METHOD; } @Override diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/Transaction.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/Transaction.java index ba1d73d4c..fa3a4102e 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/Transaction.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/Transaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -21,6 +21,7 @@ import java.math.BigInteger; import java.nio.ByteBuffer; +import org.jetbrains.annotations.NotNull; import org.web3j.crypto.Sign.SignatureData; public interface Transaction { @@ -30,7 +31,7 @@ public interface Transaction { String DEFAULT_DATA = ""; String DEFAULT_TO = ""; - void updateNonce(); + void updateFieldsIfRequired(); byte[] rlpEncode(SignatureData signatureData); @@ -53,6 +54,9 @@ static byte[] longToBytes(final long x) { return buffer.array(); } + @NotNull + String getJsonRpcMethodName(); + static JsonRpcRequest jsonRpcRequest( final String signedTransactionHexString, final JsonRpcRequestId id, final String rpcMethod) { final JsonRpcRequest transaction = new JsonRpcRequest(JSON_RPC_VERSION, rpcMethod); diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/TransactionFactory.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/TransactionFactory.java index bf9c10c98..0dcc4fb40 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/TransactionFactory.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/TransactionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -43,10 +43,13 @@ public Transaction createTransaction(final RoutingContext context, final JsonRpc final String method = request.getMethod().toLowerCase(); final VertxNonceRequestTransmitter nonceRequestTransmitter = new VertxNonceRequestTransmitter(context.request().headers(), decoder, transmitterFactory); + final VertxStoreRawRequestTransmitter storeRawRequestTransmitter = + new VertxStoreRawRequestTransmitter( + context.request().headers(), decoder, transmitterFactory); switch (method) { case "eth_sendtransaction": - return createEthTransaction(request, nonceRequestTransmitter); + return createEthTransaction(request, nonceRequestTransmitter, storeRawRequestTransmitter); case "eea_sendtransaction": return createEeaTransaction(request, nonceRequestTransmitter); default: @@ -55,13 +58,23 @@ public Transaction createTransaction(final RoutingContext context, final JsonRpc } private Transaction createEthTransaction( - final JsonRpcRequest request, final VertxNonceRequestTransmitter requestTransmitter) { + final JsonRpcRequest request, + final VertxNonceRequestTransmitter nonceRequestTransmitter, + final VertxStoreRawRequestTransmitter storeRawRequestTransmitter) { final EthSendTransactionJsonParameters params = fromRpcRequestToJsonParam(EthSendTransactionJsonParameters.class, request); final NonceProvider ethNonceProvider = - new EthNonceProvider(params.sender(), requestTransmitter); - return new EthTransaction(params, ethNonceProvider, request.getId()); + new EthNonceProvider(params.sender(), nonceRequestTransmitter); + final StoreRawEnclaveLookupIdProvider lookupIdProvider = + new StoreRawEnclaveLookupIdProvider(storeRawRequestTransmitter); + + if (params.privateFor().isPresent()) { + return GoQuorumPrivateTransaction.from( + params, ethNonceProvider, lookupIdProvider, request.getId()); + } else { + return new EthTransaction(params, ethNonceProvider, request.getId()); + } } private Transaction createEeaTransaction( diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/VertxNonceRequestTransmitter.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/VertxNonceRequestTransmitter.java index 41ee7799a..80e86e469 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/VertxNonceRequestTransmitter.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/VertxNonceRequestTransmitter.java @@ -12,13 +12,13 @@ */ package tech.pegasys.ethsigner.core.requesthandler.sendtransaction.transaction; +import static tech.pegasys.ethsigner.core.jsonrpc.RpcUtil.determineErrorCode; + import tech.pegasys.ethsigner.core.http.HeaderHelpers; import tech.pegasys.ethsigner.core.jsonrpc.JsonDecoder; import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequest; import tech.pegasys.ethsigner.core.jsonrpc.JsonRpcRequestId; import tech.pegasys.ethsigner.core.jsonrpc.exception.JsonRpcException; -import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcError; -import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcErrorResponse; import tech.pegasys.ethsigner.core.jsonrpc.response.JsonRpcSuccessResponse; import tech.pegasys.ethsigner.core.requesthandler.DownstreamResponseHandler; import tech.pegasys.ethsigner.core.requesthandler.RequestTransmitter; @@ -50,25 +50,6 @@ public class VertxNonceRequestTransmitter { private static final AtomicInteger nextId = new AtomicInteger(0); - private class ResponseCallback implements DownstreamResponseHandler { - private final CompletableFuture result; - - private ResponseCallback(final CompletableFuture result) { - this.result = result; - } - - @Override - public void handleResponse( - final Iterable> headers, final int statusCode, String body) { - VertxNonceRequestTransmitter.this.handleResponse(body, result); - } - - @Override - public void handleFailure(Throwable t) { - result.completeExceptionally(t); - } - } - public VertxNonceRequestTransmitter( final MultiMap headers, final JsonDecoder decoder, @@ -123,17 +104,26 @@ private void handleResponse(final String body, final CompletableFuture result; + + private ResponseCallback(final CompletableFuture result) { + this.result = result; + } + + @Override + public void handleResponse( + final Iterable> headers, final int statusCode, String body) { + VertxNonceRequestTransmitter.this.handleResponse(body, result); + } + + @Override + public void handleFailure(Throwable t) { + result.completeExceptionally(t); } } } diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/ChainIdProvider.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/ChainIdProvider.java index 84d8fe71b..f36baea14 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/ChainIdProvider.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/ChainIdProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/ConfigurationChainId.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/ConfigurationChainId.java index 4b1939556..d7cd07cc8 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/ConfigurationChainId.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/ConfigurationChainId.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/TransactionSerializer.java b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/TransactionSerializer.java index fa9d64e26..1d0bb7ef2 100644 --- a/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/TransactionSerializer.java +++ b/ethsigner/core/src/main/java/tech/pegasys/ethsigner/core/signing/TransactionSerializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -23,8 +23,8 @@ public class TransactionSerializer { - private final Signer signer; - private final long chainId; + protected final Signer signer; + protected final long chainId; public TransactionSerializer(final Signer signer, final long chainId) { this.signer = signer; diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/EeaSendTransactionJsonParametersTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/EeaSendTransactionJsonParametersTest.java index 5aba2e685..92c72c43a 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/EeaSendTransactionJsonParametersTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/EeaSendTransactionJsonParametersTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParametersTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParametersTest.java index 440dd6ae7..05f30e313 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParametersTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/EthSendTransactionJsonParametersTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequestTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequestTest.java index b88f6c206..05c6036fa 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequestTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/JsonRpcRequestTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcErrorResponseTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcErrorResponseTest.java index a2d6d9090..f9e0242df 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcErrorResponseTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpc/response/JsonRpcErrorResponseTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthAccountsResultProviderTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthAccountsResultProviderTest.java index 75106401c..2e99fc531 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthAccountsResultProviderTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/jsonrpcproxy/EthAccountsResultProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceTooLowRetryMechanismTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceTooLowRetryMechanismTest.java index 44847afc6..84ba3708b 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceTooLowRetryMechanismTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/NonceTooLowRetryMechanismTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EeaPrivateTransactionTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EeaPrivateTransactionTest.java index 126d83462..ac9b2424b 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EeaPrivateTransactionTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EeaPrivateTransactionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransactionTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransactionTest.java index 59fb50b5b..8295c6a91 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransactionTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/requesthandler/sendtransaction/transaction/EthTransactionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/signing/SingleTransactionSignerProviderTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/signing/SingleTransactionSignerProviderTest.java index 02b44a7c0..f79c6dab1 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/signing/SingleTransactionSignerProviderTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/core/signing/SingleTransactionSignerProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/jsonrpcproxy/RequestMapperTest.java b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/jsonrpcproxy/RequestMapperTest.java index b7ded65d6..5a7d1f7ab 100644 --- a/ethsigner/core/src/test/java/tech/pegasys/ethsigner/jsonrpcproxy/RequestMapperTest.java +++ b/ethsigner/core/src/test/java/tech/pegasys/ethsigner/jsonrpcproxy/RequestMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/AzureSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/AzureSubCommand.java index dca7c8cb5..f359c75ae 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/AzureSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/AzureSubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommand.java index fa61b7cb8..d5ba7278a 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommand.java index 72c221e73..b92b9692d 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java index 36b3f3e1e..12c785258 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java index fa3fe2bd4..d83f9a0ed 100644 --- a/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java +++ b/ethsigner/subcommands/src/main/java/tech/pegasys/ethsigner/subcommands/RawSubCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2018 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommandTest.java b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommandTest.java index 6e4c38e97..8a79f6a5e 100644 --- a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommandTest.java +++ b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/FileBasedSubCommandTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommandTest.java b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommandTest.java index a4311b35b..508249b80 100644 --- a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommandTest.java +++ b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/HashicorpSubCommandTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommandTest.java b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommandTest.java index e9932a649..6a028ec5d 100644 --- a/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommandTest.java +++ b/ethsigner/subcommands/src/test/java/tech/pegasys/ethsigner/subcommands/MultiKeySubCommandTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 ConsenSys AG. + * Copyright 2019 ConsenSys AG. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/gradle.properties b/gradle.properties index 7fcca347d..25066d924 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs=-Xmx1g -version=20.10.1-ADHARA-SNAPSHOT -besuVersion=1.5.0 -besuDistroUrl=https://bintray.com/hyperledger-org/besu-repo/download_file?file_path=besu-${besuVersion}.tar.gz +version=21.3.0-ADHARA-SNAPSHOT +besuVersion=21.1.1 +besuDistroUrl=https://hyperledger.jfrog.io/artifactory/besu-binaries/besu/${besuVersion}/besu-${besuVersion}.tar.gz hashicorpVaultVersion=1.4.3 hashicorpVaultUrl=https://releases.hashicorp.com/vault diff --git a/gradle/check-licenses.gradle b/gradle/check-licenses.gradle index 7f27bb966..e84ee95e7 100644 --- a/gradle/check-licenses.gradle +++ b/gradle/check-licenses.gradle @@ -101,6 +101,8 @@ downloadLicenses { (bsd3Clause): [ 'BSD 3-Clause', 'BSD 3-Clause "New" or "Revised" License (BSD-3-Clause)', + '3-Clause BSD License', + 'The BSD 3-Clause License', license('BSD 3-clause', 'http://opensource.org/licenses/BSD-3-Clause'), license('BSD 3-Clause', 'http://www.scala-lang.org/license.html') ], @@ -158,9 +160,12 @@ downloadLicenses { (group('org.glassfish.jersey.connectors')): apache, (group('com.github.jnr')): epl_2, (group('javax.mail')): cddl, + (group('javax.xml.bind')): cddl1_1, (group('net.jcip')): apache, (group('net.java.dev.jna')): apache, (group('com.sun.mail')): cddl, + (group('stax')): apache, + (group('xpp3')): apache, ('cloudhsm-3.0.1.jar'): apache ] } diff --git a/gradle/versions.gradle b/gradle/versions.gradle index d4a4ed951..abc7b2d43 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -70,7 +70,7 @@ dependencyManagement { dependency 'org.web3j:core:4.5.14' dependency 'org.web3j:crypto:4.5.14' - dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.15-ADHARA-SNAPSHOT') { + dependencySet(group: 'tech.pegasys.signers.internal', version: '1.0.16-ADHARA-SNAPSHOT') { entry 'keystorage-hashicorp' entry 'keystorage-hsm' entry 'keystorage-cavium' @@ -80,7 +80,7 @@ dependencyManagement { dependency 'org.zeroturnaround:zt-exec:1.11' - dependency 'org.hyperledger.besu:plugin-api:1.4.0' - dependency 'org.hyperledger.besu.internal:metrics-core:1.4.0' + dependency 'org.hyperledger.besu:plugin-api:21.1.1' + dependency 'org.hyperledger.besu.internal:metrics-core:21.1.1' } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index cc4fdc293d0e50b0ad9b65c16e7ddd1db2f6025b..62d4c053550b91381bbd28b1afc82d634bf73a8a 100644 GIT binary patch delta 18673 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 Bgx delta 18406 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 RC3`**IJaad=rFcB?7uiSG#~%~