From 26c7e059480caf57e9af4413cadc3de2d6adbebe Mon Sep 17 00:00:00 2001 From: Ivaylo Kirilov Date: Tue, 16 Jul 2019 01:10:35 +0100 Subject: [PATCH] [PAN-2910] Eea get private transaction fix (#1707) Signed-off-by: Adrian Sutton --- ...acyPrecompiledContractIntegrationTest.java | 4 +- .../privacy/PrivacyPrecompiledContract.java | 3 +- .../ethereum/privacy/PrivateTransaction.java | 112 ++++++++++------- .../privacy/PrivateTransactionHandler.java | 19 ++- .../PrivateTransactionHandlerTest.java | 22 ++-- .../privacy/PrivateTransactionTest.java | 117 ++++++++---------- .../privacy/EeaGetPrivateTransaction.java | 12 +- .../privacy/EeaGetTransactionReceipt.java | 3 +- .../privacy/EeaSendRawTransaction.java | 5 +- .../PrivateTransactionGroupResult.java | 53 ++++++++ .../PrivateTransactionLegacyResult.java | 59 +++++++++ .../privacy/PrivateTransactionResult.java | 117 ++++++++++++++++++ .../privacy/EeaGetPrivateTransactionTest.java | 77 ++++++++---- .../privacy/EeaSendRawTransactionTest.java | 64 ++++++++-- 14 files changed, 493 insertions(+), 174 deletions(-) create mode 100644 ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/privacy/PrivateTransactionGroupResult.java create mode 100644 ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/privacy/PrivateTransactionLegacyResult.java create mode 100644 ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/privacy/PrivateTransactionResult.java diff --git a/ethereum/core/src/integration-test/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java b/ethereum/core/src/integration-test/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java index 59a44445d5..a1fadc76a2 100644 --- a/ethereum/core/src/integration-test/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java +++ b/ethereum/core/src/integration-test/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java @@ -43,6 +43,7 @@ import tech.pegasys.pantheon.ethereum.vm.OperationTracer; import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive; import tech.pegasys.pantheon.util.bytes.Bytes32; import tech.pegasys.pantheon.util.bytes.BytesValue; +import tech.pegasys.pantheon.util.bytes.BytesValues; import java.io.IOException; import java.util.Base64; @@ -168,8 +169,7 @@ public class PrivacyPrecompiledContractIntegrationTest { privacyPrecompiledContract.setPrivateTransactionProcessor(mockPrivateTxProcessor()); BytesValue actual = - privacyPrecompiledContract.compute( - BytesValue.wrap(sr.getKey().getBytes(UTF_8)), messageFrame); + privacyPrecompiledContract.compute(BytesValues.fromBase64(sr.getKey()), messageFrame); assertThat(actual).isEqualTo(BytesValue.fromHexString(DEFAULT_OUTPUT)); } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java index b3f9cd7f6a..6982437884 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java @@ -12,7 +12,6 @@ */ package tech.pegasys.pantheon.ethereum.mainnet.precompiles.privacy; -import static java.nio.charset.StandardCharsets.UTF_8; import static tech.pegasys.pantheon.crypto.Hash.keccak256; import tech.pegasys.pantheon.enclave.Enclave; @@ -93,7 +92,7 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract { @Override public BytesValue compute(final BytesValue input, final MessageFrame messageFrame) { - final String key = new String(input.extractArray(), UTF_8); + final String key = BytesValues.asBase64String(input); final ReceiveRequest receiveRequest = new ReceiveRequest(key, enclavePublicKey); ReceiveResponse receiveResponse; diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransaction.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransaction.java index 79c0a915c9..0dd2741967 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransaction.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransaction.java @@ -66,12 +66,12 @@ public class PrivateTransaction { private final Optional chainId; - private final Optional privacyGroupId; - - private final Optional privateFrom; + private final BytesValue privateFrom; private final Optional> privateFor; + private final Optional privacyGroupId; + private final Restriction restriction; // Caches a "hash" of a portion of the transaction used for sender recovery. @@ -91,6 +91,12 @@ public class PrivateTransaction { } public static PrivateTransaction readFrom(final RLPInput input) throws RLPException { + return readFrom(input, null); + } + + @SuppressWarnings({"unchecked"}) + public static PrivateTransaction readFrom(final RLPInput input, final BytesValue enclavePublicKey) + throws RLPException { input.enterList(); final Builder builder = @@ -117,34 +123,49 @@ public class PrivateTransaction { final BigInteger r = BytesValues.asUnsignedBigInteger(input.readUInt256Scalar().getBytes()); final BigInteger s = BytesValues.asUnsignedBigInteger(input.readUInt256Scalar().getBytes()); final SECP256K1.Signature signature = SECP256K1.Signature.create(r, s, recId); - final BytesValue tempValue = input.readBytesValue(); - Optional> privateFor = Optional.empty(); - if (input.nextIsList()) { - privateFor = Optional.of(input.readList(RLPInput::readBytesValue)); - } - final Restriction restriction = convertToEnum(input.readBytesValue()); + final RLPInput item1 = input.readAsRlp(); // privateFrom or privateFor/PrivacyGroupId + final RLPInput item2 = input.readAsRlp(); // privateFor/PrivacyGroupId or restriction + + final BytesValue privateFrom; + final Object privateForOrPrivacyGroupId; + final Restriction restriction; + + if (input.isEndOfCurrentList()) { + privateFrom = enclavePublicKey; + privateForOrPrivacyGroupId = resolvePrivateForOrPrivacyGroupId(item1); + restriction = convertToEnum(item2.readBytesValue()); + } else { + privateFrom = item1.readBytesValue(); + privateForOrPrivacyGroupId = resolvePrivateForOrPrivacyGroupId(item2); + restriction = convertToEnum(input.readBytesValue()); + } input.leaveList(); chainId.ifPresent(builder::chainId); - if (privateFor.isPresent()) { + if (privateForOrPrivacyGroupId instanceof List) { return builder .signature(signature) - .privateFrom(tempValue) - .privateFor(privateFor.get()) + .privateFrom(privateFrom) + .privateFor((List) privateForOrPrivacyGroupId) .restriction(restriction) .build(); } else { return builder .signature(signature) - .privacyGroupId(tempValue) + .privateFrom(privateFrom) + .privacyGroupId((BytesValue) privateForOrPrivacyGroupId) .restriction(restriction) .build(); } } + private static Object resolvePrivateForOrPrivacyGroupId(final RLPInput item) { + return item.nextIsList() ? item.readList(RLPInput::readBytesValue) : item.readBytesValue(); + } + private static Restriction convertToEnum(final BytesValue readBytesValue) { if (readBytesValue.equals(Restriction.RESTRICTED.getBytes())) { return Restriction.RESTRICTED; @@ -186,9 +207,9 @@ public class PrivateTransaction { final BytesValue payload, final Address sender, final Optional chainId, - final Optional privacyGroupId, - final Optional privateFrom, + final BytesValue privateFrom, final Optional> privateFor, + final Optional privacyGroupId, final Restriction restriction) { this.nonce = nonce; this.gasPrice = gasPrice; @@ -199,9 +220,9 @@ public class PrivateTransaction { this.payload = payload; this.sender = sender; this.chainId = chainId; - this.privacyGroupId = privacyGroupId; this.privateFrom = privateFrom; this.privateFor = privateFor; + this.privacyGroupId = privacyGroupId; this.restriction = restriction; } @@ -283,21 +304,12 @@ public class PrivateTransaction { return chainId; } - /** - * Returns the enclave privacy group id. - * - * @return the enclave privacy group id. - */ - public Optional getPrivacyGroupId() { - return privacyGroupId; - } - /** * Returns the enclave public key of the sender. * * @return the enclave public key of the sender. */ - public Optional getPrivateFrom() { + public BytesValue getPrivateFrom() { return privateFrom; } @@ -310,6 +322,15 @@ public class PrivateTransaction { return privateFor; } + /** + * Returns the enclave privacy group id. + * + * @return the enclave privacy group id. + */ + public Optional getPrivacyGroupId() { + return privacyGroupId; + } + /** * Returns the restriction of this private transaction. * @@ -348,9 +369,9 @@ public class PrivateTransaction { value, payload, chainId, - privacyGroupId, privateFrom, privateFor, + privacyGroupId, restriction.getBytes()); } return hashNoSignature; @@ -371,10 +392,10 @@ public class PrivateTransaction { out.writeUInt256Scalar(getValue()); out.writeBytesValue(getPayload()); writeSignature(out); - getPrivacyGroupId().ifPresent(out::writeBytesValue); - getPrivateFrom().ifPresent(out::writeBytesValue); + out.writeBytesValue(getPrivateFrom()); getPrivateFor() .ifPresent(privateFor -> out.writeList(privateFor, (bv, rlpO) -> rlpO.writeBytesValue(bv))); + getPrivacyGroupId().ifPresent(out::writeBytesValue); out.writeBytesValue(getRestriction().getBytes()); out.endList(); @@ -457,9 +478,9 @@ public class PrivateTransaction { final Wei value, final BytesValue payload, final Optional chainId, - final Optional privacyGroupId, - final Optional privateFrom, + final BytesValue privateFrom, final Optional> privateFor, + final Optional privacyGroupId, final BytesValue restriction) { return keccak256( RLP.encode( @@ -476,9 +497,9 @@ public class PrivateTransaction { out.writeUInt256Scalar(UInt256.ZERO); out.writeUInt256Scalar(UInt256.ZERO); } - privacyGroupId.ifPresent(out::writeBytesValue); - privateFrom.ifPresent(out::writeBytesValue); + out.writeBytesValue(privateFrom); privateFor.ifPresent(pF -> out.writeList(pF, (bv, rlpO) -> rlpO.writeBytesValue(bv))); + privacyGroupId.ifPresent(out::writeBytesValue); out.writeBytesValue(restriction); out.endList(); })); @@ -530,15 +551,14 @@ public class PrivateTransaction { sb.append("value=").append(getValue()).append(", "); sb.append("sig=").append(getSignature()).append(", "); if (chainId.isPresent()) sb.append("chainId=").append(getChainId().get()).append(", "); - sb.append("payload=").append(getPayload()); - if (getPrivacyGroupId().isPresent()) - sb.append("privacyGroupId=").append(getPrivacyGroupId().get()).append(", "); - if (getPrivateFrom().isPresent()) - sb.append("privateFrom=").append(getPrivateFrom().get()).append(", "); + sb.append("payload=").append(getPayload()).append(", "); + sb.append("privateFrom=").append(getPrivateFrom()).append(", "); if (getPrivateFor().isPresent()) sb.append("privateFor=") .append(Arrays.toString(getPrivateFor().get().toArray())) .append(", "); + if (getPrivacyGroupId().isPresent()) + sb.append("privacyGroupId=").append(getPrivacyGroupId().get()).append(", "); sb.append("restriction=").append(getRestriction()); return sb.append("}").toString(); } @@ -570,12 +590,12 @@ public class PrivateTransaction { protected Optional chainId = Optional.empty(); - protected Optional privacyGroupId = Optional.empty(); - - protected Optional privateFrom = Optional.empty(); + protected BytesValue privateFrom; protected Optional> privateFor = Optional.empty(); + protected Optional privacyGroupId = Optional.empty(); + protected Restriction restriction; public Builder chainId(final BigInteger chainId) { @@ -629,7 +649,7 @@ public class PrivateTransaction { } public Builder privateFrom(final BytesValue privateFrom) { - this.privateFrom = Optional.of(privateFrom); + this.privateFrom = privateFrom; return this; } @@ -644,9 +664,9 @@ public class PrivateTransaction { } public PrivateTransaction build() { - if (privacyGroupId.isPresent() && (privateFrom.isPresent() || privateFor.isPresent())) { + if (privacyGroupId.isPresent() && privateFor.isPresent()) { throw new IllegalArgumentException( - "Private transaction should contain either privacyGroup by itself or privateFrom and privateFor together, but not both"); + "Private transaction should contain either privacyGroup or privateFor, but not both"); } return new PrivateTransaction( nonce, @@ -658,9 +678,9 @@ public class PrivateTransaction { payload, sender, chainId, - privacyGroupId, privateFrom, privateFor, + privacyGroupId, restriction); } @@ -682,9 +702,9 @@ public class PrivateTransaction { value, payload, chainId, - privacyGroupId, privateFrom, privateFor, + privacyGroupId, restriction.getBytes()); return SECP256K1.sign(hash, keys); } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandler.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandler.java index 119c6c4a09..ab06d46b52 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandler.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandler.java @@ -32,13 +32,11 @@ import tech.pegasys.pantheon.ethereum.mainnet.TransactionValidator.TransactionIn import tech.pegasys.pantheon.ethereum.mainnet.ValidationResult; import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput; import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive; -import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.bytes.BytesValues; import java.util.List; import java.util.stream.Collectors; -import com.google.common.base.Charsets; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -100,11 +98,10 @@ public class PrivateTransactionHandler { return BytesValues.asBase64String(privateTransaction.getPrivacyGroupId().get()); } final ReceiveRequest receiveRequest = - new ReceiveRequest( - key, BytesValues.asBase64String(privateTransaction.getPrivateFrom().get())); + new ReceiveRequest(key, BytesValues.asBase64String(privateTransaction.getPrivateFrom())); LOG.debug( "Getting privacy group for {}", - BytesValues.asBase64String(privateTransaction.getPrivateFrom().get())); + BytesValues.asBase64String(privateTransaction.getPrivateFrom())); final ReceiveResponse receiveResponse; try { receiveResponse = enclave.receive(receiveRequest); @@ -126,7 +123,7 @@ public class PrivateTransactionHandler { .gasLimit(privateTransaction.getGasLimit()) .to(privacyPrecompileAddress) .value(privateTransaction.getValue()) - .payload(BytesValue.wrap(transactionEnclaveKey.getBytes(Charsets.UTF_8))) + .payload(BytesValues.fromBase64(transactionEnclaveKey)) .sender(signerAddress) .signAndBuild(nodeKeyPair); } @@ -173,13 +170,11 @@ public class PrivateTransactionHandler { // FIXME: orion should accept empty privateFor if (privateFor.isEmpty()) { - privateFor.add(BytesValues.asBase64String(privateTransaction.getPrivateFrom().get())); + privateFor.add(BytesValues.asBase64String(privateTransaction.getPrivateFrom())); } return new SendRequestLegacy( - payload, - BytesValues.asBase64String(privateTransaction.getPrivateFrom().get()), - privateFor); + payload, BytesValues.asBase64String(privateTransaction.getPrivateFrom()), privateFor); } } @@ -210,4 +205,8 @@ public class PrivateTransactionHandler { public Address getSignerAddress() { return signerAddress; } + + public String getEnclaveKey() { + return enclavePublicKey; + } } diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandlerTest.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandlerTest.java index 260d6654af..7dddee0684 100644 --- a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandlerTest.java +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandlerTest.java @@ -12,7 +12,6 @@ */ package tech.pegasys.pantheon.ethereum.privacy; -import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -38,12 +37,12 @@ import tech.pegasys.pantheon.ethereum.mainnet.TransactionValidator.TransactionIn import tech.pegasys.pantheon.ethereum.mainnet.ValidationResult; import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive; import tech.pegasys.pantheon.util.bytes.BytesValue; +import tech.pegasys.pantheon.util.bytes.BytesValues; import java.io.IOException; import java.math.BigInteger; import java.util.Optional; -import com.google.common.base.Charsets; import com.google.common.collect.Lists; import org.junit.Before; import org.junit.Test; @@ -53,15 +52,15 @@ import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class PrivateTransactionHandlerTest { - private static final String TRANSACTION_KEY = "My Transaction Key"; + private static final String TRANSACTION_KEY = "93Ky7lXwFkMc7+ckoFgUMku5bpr9tz4zhmWmk9RlNng="; private static final KeyPair KEY_PAIR = KeyPair.create( SECP256K1.PrivateKey.create( new BigInteger( "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", 16))); - PrivateTransactionHandler privateTransactionHandler; - PrivateTransactionHandler brokenPrivateTransactionHandler; + private PrivateTransactionHandler privateTransactionHandler; + private PrivateTransactionHandler brokenPrivateTransactionHandler; private static final Transaction PUBLIC_TRANSACTION = Transaction.builder() @@ -70,7 +69,7 @@ public class PrivateTransactionHandlerTest { .gasLimit(3000000) .to(Address.fromHexString("0x627306090abab3a6e1400e9345bc60c78a8bef57")) .value(Wei.ZERO) - .payload(BytesValue.wrap(TRANSACTION_KEY.getBytes(Charsets.UTF_8))) + .payload(BytesValues.fromBase64(TRANSACTION_KEY)) .sender(Address.fromHexString("0xfe3b557e8fb62b89f4916b721be55ceb828dbd73")) .chainId(BigInteger.valueOf(2018)) .signAndBuild(KEY_PAIR); @@ -202,20 +201,19 @@ public class PrivateTransactionHandlerTest { private static PrivateTransaction buildLegacyPrivateTransaction(final long nonce) { return buildPrivateTransaction(nonce) - .privateFrom( - BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8))) + .privateFrom(BytesValues.fromBase64("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=")) .privateFor( Lists.newArrayList( - BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8)), - BytesValue.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8)))) + BytesValues.fromBase64("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="), + BytesValues.fromBase64("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="))) .signAndBuild(KEY_PAIR); } private static PrivateTransaction buildPantheonPrivateTransaction(final long nonce) { return buildPrivateTransaction(nonce) - .privacyGroupId( - BytesValue.wrap("DyAOiF/ynpc+JXa2YAGB0bCitSlOMNm+ShmB/7M6C4w=".getBytes(UTF_8))) + .privateFrom(BytesValues.fromBase64("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=")) + .privacyGroupId(BytesValues.fromBase64("DyAOiF/ynpc+JXa2YAGB0bCitSlOMNm+ShmB/7M6C4w=")) .signAndBuild(KEY_PAIR); } diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionTest.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionTest.java index a7eb06e175..4505bd21b2 100644 --- a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionTest.java +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionTest.java @@ -12,7 +12,6 @@ */ package tech.pegasys.pantheon.ethereum.privacy; -import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import tech.pegasys.pantheon.crypto.SECP256K1; @@ -22,6 +21,7 @@ import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPInput; import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput; import tech.pegasys.pantheon.ethereum.rlp.RLPException; import tech.pegasys.pantheon.util.bytes.BytesValue; +import tech.pegasys.pantheon.util.bytes.BytesValues; import java.math.BigInteger; import java.util.Optional; @@ -39,58 +39,53 @@ public class PrivateTransactionTest { + "601b4ab949f53faa07bd2c804"; private static final String VALID_PRIVATE_TRANSACTION_RLP = - "0xf90113800182520894095e7baea6a6c7c4c2dfeb977efac326af552d87" - + "a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - + "ffff801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d" - + "495a36649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab94" - + "9f53faa07bd2c804ac41316156744d784c4355486d425648586f5a7a7a4267" - + "5062572f776a3561784470573958386c393153476f3df85aac41316156744d" - + "784c4355486d425648586f5a7a7a42675062572f776a356178447057395838" - + "6c393153476f3dac4b6f32625671442b6e4e6c4e594c35454537793349644f" - + "6e766966746a69697a706a52742b4854754642733d8a726573747269637465" - + "64"; + "0xf8ef800182520894095e7baea6a6c7c4c2dfeb977efac326af552d87a0f" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + + "801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a3" + + "6649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53fa" + + "a07bd2c804a0035695b4cc4b0941e60551d7a19cf30603db5bfc23e5ac43a56" + + "f57f25f75486af842a0035695b4cc4b0941e60551d7a19cf30603db5bfc23e5" + + "ac43a56f57f25f75486aa02a8d9b56a0fe9cd94d60be4413bcb721d3a7be27e" + + "d8e28b3a6346df874ee141b8a72657374726963746564"; private static final String VALID_PRIVATE_TRANSACTION_RLP_PRIVACY_GROUP = - "0xf8b7800182520894095e7baea6a6c7c4c2dfeb977efac326af552d87a0f" + "0xf8cc800182520894095e7baea6a6c7c4c2dfeb977efac326af552d87a0f" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a3" + "6649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53fa" - + "a07bd2c804ac4479414f69462f796e70632b4a5861325941474230624369745" - + "36c4f4d4e6d2b53686d422f374d364334773d8a72657374726963746564"; + + "a07bd2c804a0035695b4cc4b0941e60551d7a19cf30603db5bfc23e5ac43a56" + + "f57f25f75486aa00f200e885ff29e973e2576b6600181d1b0a2b5294e30d9be" + + "4a1981ffb33a0b8c8a72657374726963746564"; private static final String VALID_SIGNED_PRIVATE_TRANSACTION_RLP = - "0xf901a4808203e8832dc6c08080b8ef60806040523480156100105760008" - + "0fd5b5060d08061001f6000396000f3fe60806040526004361060485763f" - + "fffffff7c010000000000000000000000000000000000000000000000000" - + "000000060003504166360fe47b18114604d5780636d4ce63c146075575b6" - + "00080fd5b348015605857600080fd5b50607360048036036020811015606" - + "d57600080fd5b50356099565b005b348015608057600080fd5b506087609" - + "e565b60408051918252519081900360200190f35b600055565b600054905" - + "6fea165627a7a72305820cb1d0935d14b589300b12fcd0ab849a7e9019c8" - + "1da24d6daa4f6b2f003d1b01800292ca0a6dc7319bd355ce9d8e0928d29d" - + "9b8110bcba9168fad68498e49526420fe65dea06c4c12c2ae518c5130353" - + "eb6c2893b1c36b7fd1497c156b1e158b716f482601fac41316156744d784" - + "c4355486d425648586f5a7a7a42675062572f776a3561784470573958386" - + "c393153476f3dedac4b6f32625671442b6e4e6c4e594c354545377933496" - + "44f6e766966746a69697a706a52742b4854754642733d8a7265737472696" - + "3746564"; + "0xf9018c808203e8832dc6c08080b8ef60806040523480156100105760008" + + "0fd5b5060d08061001f6000396000f3fe60806040526004361060485763ffff" + + "ffff7c010000000000000000000000000000000000000000000000000000000" + + "060003504166360fe47b18114604d5780636d4ce63c146075575b600080fd5b" + + "348015605857600080fd5b50607360048036036020811015606d57600080fd5" + + "b50356099565b005b348015608057600080fd5b506087609e565b6040805191" + + "8252519081900360200190f35b600055565b6000549056fea165627a7a72305" + + "820cb1d0935d14b589300b12fcd0ab849a7e9019c81da24d6daa4f6b2f003d1" + + "b01800292ca0bab7ae7f68afad5ade9e26177e044079bbcfd7857cf911b7a4c" + + "97027dd92e41ba06e92e391ced52de53725e3054af3908a9f2f36374d727ebc" + + "616a96a1ed2ca548a0035695b4cc4b0941e60551d7a19cf30603db5bfc23e5a" + + "c43a56f57f25f75486ae1a02a8d9b56a0fe9cd94d60be4413bcb721d3a7be27" + + "ed8e28b3a6346df874ee141b8a72657374726963746564"; private static final String VALID_SIGNED_PRIVATE_TRANSACTION_LARGE_CHAINID_RLP = - "0xf901a9808203e8832dc6c08080b8ef608060405234801561001057600080" - + "fd5b5060d08061001f6000396000f3fe60806040526004361060485763ff" - + "ffffff7c0100000000000000000000000000000000000000000000000000" - + "00000060003504166360fe47b18114604d5780636d4ce63c146075575b60" - + "0080fd5b348015605857600080fd5b50607360048036036020811015606d" - + "57600080fd5b50356099565b005b348015608057600080fd5b506087609e" - + "565b60408051918252519081900360200190f35b600055565b6000549056" - + "fea165627a7a72305820cb1d0935d14b589300b12fcd0ab849a7e9019c81" - + "da24d6daa4f6b2f003d1b0180029850100000022a0ebccb6952d7ad4eb5c" - + "1d4da2f67a833f66c1b9127e0c592224dd24210104a095a07d35a1bbc54f" - + "fa5b2dc9f315b545238575c8960108076036c6ffcafedddf4d22ac413161" - + "56744d784c4355486d425648586f5a7a7a42675062572f776a3561784470" - + "573958386c393153476f3dedac4b6f32625671442b6e4e6c4e594c354545" - + "37793349644f6e766966746a69697a706a52742b4854754642733d8a7265" - + "7374726963746564"; + "0xf90191808203e8832dc6c08080b8ef60806040523480156100105760008" + + "0fd5b5060d08061001f6000396000f3fe60806040526004361060485763ffff" + + "ffff7c010000000000000000000000000000000000000000000000000000000" + + "060003504166360fe47b18114604d5780636d4ce63c146075575b600080fd5b" + + "348015605857600080fd5b50607360048036036020811015606d57600080fd5" + + "b50356099565b005b348015608057600080fd5b506087609e565b6040805191" + + "8252519081900360200190f35b600055565b6000549056fea165627a7a72305" + + "820cb1d0935d14b589300b12fcd0ab849a7e9019c81da24d6daa4f6b2f003d1" + + "b0180029850100000021a0bfa05529e85cdaadda498b6ab4ded157a70356ad7" + + "6df24c18fa12b9d49be6ef4a03a54b8bc0c4e41e08d4cf0e94da98c5e02ef57" + + "524cf2b53deecd56e3ba184927a0035695b4cc4b0941e60551d7a19cf30603d" + + "b5bfc23e5ac43a56f57f25f75486ae1a02a8d9b56a0fe9cd94d60be4413bcb7" + + "21d3a7be27ed8e28b3a6346df874ee141b8a72657374726963746564"; private static final PrivateTransaction VALID_PRIVATE_TRANSACTION = new PrivateTransaction( @@ -111,13 +106,12 @@ public class PrivateTransactionTest { BytesValue.fromHexString("0x"), Address.wrap(BytesValue.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")), Optional.empty(), - Optional.empty(), - Optional.of( - BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8))), + BytesValues.fromBase64("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="), Optional.of( Lists.newArrayList( - BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8)), - BytesValue.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8)))), + BytesValues.fromBase64("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="), + BytesValues.fromBase64("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="))), + Optional.empty(), Restriction.RESTRICTED); private static final PrivateTransaction VALID_PRIVATE_TRANSACTION_PRIVACY_GROUP = @@ -139,10 +133,9 @@ public class PrivateTransactionTest { BytesValue.fromHexString("0x"), Address.wrap(BytesValue.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")), Optional.empty(), - Optional.of( - BytesValue.wrap("DyAOiF/ynpc+JXa2YAGB0bCitSlOMNm+ShmB/7M6C4w=".getBytes(UTF_8))), - Optional.empty(), + BytesValues.fromBase64("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="), Optional.empty(), + Optional.of(BytesValues.fromBase64("DyAOiF/ynpc+JXa2YAGB0bCitSlOMNm+ShmB/7M6C4w=")), Restriction.RESTRICTED); private static final PrivateTransaction VALID_SIGNED_PRIVATE_TRANSACTION = @@ -167,11 +160,10 @@ public class PrivateTransactionTest { .sender( Address.wrap(BytesValue.fromHexString("0x1c9a6e1ee3b7ac6028e786d9519ae3d24ee31e79"))) .chainId(BigInteger.valueOf(4)) - .privateFrom( - BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8))) + .privateFrom(BytesValues.fromBase64("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=")) .privateFor( Lists.newArrayList( - BytesValue.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8)))) + BytesValues.fromBase64("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="))) .restriction(Restriction.RESTRICTED) .signAndBuild( SECP256K1.KeyPair.create( @@ -202,11 +194,10 @@ public class PrivateTransactionTest { .sender( Address.wrap(BytesValue.fromHexString("0x1c9a6e1ee3b7ac6028e786d9519ae3d24ee31e79"))) .chainId(BigInteger.valueOf(2147483647)) - .privateFrom( - BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8))) + .privateFrom(BytesValues.fromBase64("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=")) .privateFor( Lists.newArrayList( - BytesValue.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8)))) + BytesValues.fromBase64("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="))) .restriction(Restriction.RESTRICTED) .signAndBuild( SECP256K1.KeyPair.create( @@ -300,14 +291,12 @@ public class PrivateTransactionTest { .payload(BytesValue.fromHexString("0x")) .sender(Address.fromHexString("0xfe3b557e8fb62b89f4916b721be55ceb828dbd73")) .chainId(BigInteger.valueOf(2018)) - .privacyGroupId( - BytesValue.wrap("DyAOiF/ynpc+JXa2YAGB0bCitSlOMNm+ShmB/7M6C4w=".getBytes(UTF_8))) - .privateFrom( - BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8))) + .privacyGroupId(BytesValues.fromBase64("DyAOiF/ynpc+JXa2YAGB0bCitSlOMNm+ShmB/7M6C4w=")) + .privateFrom(BytesValues.fromBase64("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=")) .privateFor( Lists.newArrayList( - BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8)), - BytesValue.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8)))) + BytesValues.fromBase64("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="), + BytesValues.fromBase64("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="))) .restriction(Restriction.RESTRICTED) .build(); } diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetPrivateTransaction.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetPrivateTransaction.java index c2bb1a9e49..4ade6dcafd 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetPrivateTransaction.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetPrivateTransaction.java @@ -24,6 +24,8 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.privacy.PrivateTransactionGroupResult; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.privacy.PrivateTransactionLegacyResult; import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction; import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPInput; import tech.pegasys.pantheon.util.bytes.BytesValues; @@ -65,9 +67,15 @@ public class EeaGetPrivateTransaction implements JsonRpcMethod { new BytesValueRLPInput(BytesValues.fromBase64(receiveResponse.getPayload()), false); final PrivateTransaction privateTransaction = PrivateTransaction.readFrom(bytesValueRLPInput); - return new JsonRpcSuccessResponse(request.getId(), privateTransaction); + if (privateTransaction.getPrivacyGroupId().isPresent()) { + return new JsonRpcSuccessResponse( + request.getId(), new PrivateTransactionGroupResult(privateTransaction)); + } else { + return new JsonRpcSuccessResponse( + request.getId(), new PrivateTransactionLegacyResult(privateTransaction)); + } } catch (Exception e) { - LOG.error("Failed to fetch transaction from Enclave with error " + e.getMessage()); + LOG.error("Failed to fetch private transaction with error " + e.getMessage()); return new JsonRpcSuccessResponse(request.getId(), null); } } diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetTransactionReceipt.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetTransactionReceipt.java index 375cfb19ad..2c3eda6b03 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetTransactionReceipt.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetTransactionReceipt.java @@ -12,7 +12,6 @@ */ package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.privacy; -import static java.nio.charset.StandardCharsets.UTF_8; import static org.apache.logging.log4j.LogManager.getLogger; import tech.pegasys.pantheon.enclave.Enclave; @@ -160,7 +159,7 @@ public class EeaGetTransactionReceipt implements JsonRpcMethod { final Transaction transaction, final String publicKey) throws Exception { LOG.trace("Fetching transaction information from Enclave"); final ReceiveRequest enclaveRequest = - new ReceiveRequest(new String(transaction.getPayload().extractArray(), UTF_8), publicKey); + new ReceiveRequest(BytesValues.asBase64String(transaction.getPayload()), publicKey); ReceiveResponse enclaveResponse = enclave.receive(enclaveRequest); LOG.trace("Received transaction information from Enclave"); return enclaveResponse; diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaSendRawTransaction.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaSendRawTransaction.java index 997f05085e..2a2b875a99 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaSendRawTransaction.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaSendRawTransaction.java @@ -35,6 +35,7 @@ import tech.pegasys.pantheon.ethereum.privacy.Restriction; import tech.pegasys.pantheon.ethereum.rlp.RLP; import tech.pegasys.pantheon.ethereum.rlp.RLPException; import tech.pegasys.pantheon.util.bytes.BytesValue; +import tech.pegasys.pantheon.util.bytes.BytesValues; import java.util.OptionalLong; @@ -133,7 +134,9 @@ public class EeaSendRawTransaction implements JsonRpcMethod { private PrivateTransaction decodeRawTransaction(final String hash) throws InvalidJsonRpcRequestException { try { - return PrivateTransaction.readFrom(RLP.input(BytesValue.fromHexString(hash))); + return PrivateTransaction.readFrom( + RLP.input(BytesValue.fromHexString(hash)), + BytesValues.fromBase64(privateTransactionHandler.getEnclaveKey())); } catch (final IllegalArgumentException | RLPException e) { LOG.debug(e); throw new InvalidJsonRpcRequestException("Invalid raw private transaction hex", e); diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/privacy/PrivateTransactionGroupResult.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/privacy/PrivateTransactionGroupResult.java new file mode 100644 index 0000000000..8a725077b7 --- /dev/null +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/privacy/PrivateTransactionGroupResult.java @@ -0,0 +1,53 @@ +/* + * 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.pantheon.ethereum.jsonrpc.internal.results.privacy; + +import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction; +import tech.pegasys.pantheon.util.bytes.BytesValues; + +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +@JsonPropertyOrder({ + "from", + "gas", + "gasPrice", + "hash", + "input", + "nonce", + "to", + "value", + "v", + "r", + "s", + "privateFrom", + "privacyGroupId", + "restriction" +}) +/* + The original deserialised private transaction sent via eea_sendRawTransaction + This class is used if the original request was sent with privateFrom and privateFor +*/ +public class PrivateTransactionGroupResult extends PrivateTransactionResult { + private final String privacyGroupId; + + public PrivateTransactionGroupResult(final PrivateTransaction tx) { + super(tx); + this.privacyGroupId = BytesValues.asBase64String(tx.getPrivacyGroupId().get()); + } + + @JsonGetter(value = "privateFor") + public String getPrivacyGroupId() { + return privacyGroupId; + } +} diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/privacy/PrivateTransactionLegacyResult.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/privacy/PrivateTransactionLegacyResult.java new file mode 100644 index 0000000000..fadc0b9ff7 --- /dev/null +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/privacy/PrivateTransactionLegacyResult.java @@ -0,0 +1,59 @@ +/* + * 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.pantheon.ethereum.jsonrpc.internal.results.privacy; + +import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction; +import tech.pegasys.pantheon.util.bytes.BytesValues; + +import java.util.List; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +@JsonPropertyOrder({ + "from", + "gas", + "gasPrice", + "hash", + "input", + "nonce", + "to", + "value", + "v", + "r", + "s", + "privateFrom", + "privateFor", + "restriction" +}) +/* + The original deserialised private transaction sent via eea_sendRawTransaction + This class is used if the original request was sent with privateFrom and privateFor +*/ +public class PrivateTransactionLegacyResult extends PrivateTransactionResult { + private final List privateFor; + + public PrivateTransactionLegacyResult(final PrivateTransaction tx) { + super(tx); + this.privateFor = + tx.getPrivateFor().get().stream() + .map(BytesValues::asBase64String) + .collect(Collectors.toList()); + } + + @JsonGetter(value = "privateFor") + public List getPrivateFor() { + return privateFor; + } +} diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/privacy/PrivateTransactionResult.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/privacy/PrivateTransactionResult.java new file mode 100644 index 0000000000..f49fce9885 --- /dev/null +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/results/privacy/PrivateTransactionResult.java @@ -0,0 +1,117 @@ +/* + * 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.pantheon.ethereum.jsonrpc.internal.results.privacy; + +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.Quantity; +import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction; +import tech.pegasys.pantheon.util.bytes.BytesValue; +import tech.pegasys.pantheon.util.bytes.BytesValues; + +import com.fasterxml.jackson.annotation.JsonGetter; + +public abstract class PrivateTransactionResult { + private final String from; + private final String gas; + private final String gasPrice; + private final String hash; + private final String input; + private final String nonce; + private final String to; + private final String value; + private final String v; + private final String r; + private final String s; + private final String privateFrom; + private final String restriction; + + public PrivateTransactionResult(final PrivateTransaction tx) { + this.from = tx.getSender().toString(); + this.gas = Quantity.create(tx.getGasLimit()); + this.gasPrice = Quantity.create(tx.getGasPrice()); + this.hash = tx.hash().toString(); + this.input = tx.getPayload().toString(); + this.nonce = Quantity.create(tx.getNonce()); + this.to = tx.getTo().map(BytesValue::toString).orElse(null); + this.value = Quantity.create(tx.getValue()); + this.v = Quantity.create(tx.getV()); + this.r = Quantity.create(tx.getR()); + this.s = Quantity.create(tx.getS()); + this.privateFrom = BytesValues.asBase64String(tx.getPrivateFrom()); + this.restriction = BytesValues.asString(tx.getRestriction().getBytes()); + } + + @JsonGetter(value = "from") + public String getFrom() { + return from; + } + + @JsonGetter(value = "gas") + public String getGas() { + return gas; + } + + @JsonGetter(value = "gasPrice") + public String getGasPrice() { + return gasPrice; + } + + @JsonGetter(value = "hash") + public String getHash() { + return hash; + } + + @JsonGetter(value = "input") + public String getInput() { + return input; + } + + @JsonGetter(value = "nonce") + public String getNonce() { + return nonce; + } + + @JsonGetter(value = "to") + public String getTo() { + return to; + } + + @JsonGetter(value = "value") + public String getValue() { + return value; + } + + @JsonGetter(value = "v") + public String getV() { + return v; + } + + @JsonGetter(value = "r") + public String getR() { + return r; + } + + @JsonGetter(value = "s") + public String getS() { + return s; + } + + @JsonGetter(value = "privateFrom") + public String getPrivateFrom() { + return privateFrom; + } + + @JsonGetter(value = "restriction") + public String getRestriction() { + return restriction; + } +} diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetPrivateTransactionTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetPrivateTransactionTest.java index b9d85fe090..d823890797 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetPrivateTransactionTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetPrivateTransactionTest.java @@ -13,7 +13,7 @@ package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.privacy; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -24,15 +24,18 @@ import tech.pegasys.pantheon.enclave.types.ReceiveRequest; import tech.pegasys.pantheon.enclave.types.ReceiveResponse; import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.core.PrivacyParameters; -import tech.pegasys.pantheon.ethereum.core.Transaction; import tech.pegasys.pantheon.ethereum.core.Wei; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.parameters.JsonRpcParameter; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.privacy.PrivateTransactionGroupResult; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.privacy.PrivateTransactionLegacyResult; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.privacy.PrivateTransactionResult; import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction; import tech.pegasys.pantheon.ethereum.privacy.Restriction; import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput; import tech.pegasys.pantheon.util.bytes.BytesValue; +import tech.pegasys.pantheon.util.bytes.BytesValues; import java.math.BigInteger; import java.util.Base64; @@ -54,7 +57,7 @@ public class EeaGetPrivateTransactionTest { new BigInteger( "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", 16))); - private final PrivateTransaction privateTransaction = + private final PrivateTransaction.Builder privateTransactionBuilder = PrivateTransaction.builder() .nonce(0) .gasPrice(Wei.of(1000)) @@ -75,25 +78,10 @@ public class EeaGetPrivateTransactionTest { + "daa4f6b2f003d1b0180029")) .sender(sender) .chainId(BigInteger.valueOf(2018)) - .privateFrom( - BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8))) - .privateFor( - Lists.newArrayList( - BytesValue.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8)))) - .restriction(Restriction.RESTRICTED) - .signAndBuild(KEY_PAIR); - - private final Transaction transaction = - Transaction.builder() - .nonce(0) - .gasPrice(Wei.of(1000)) - .gasLimit(3000000) - .to(Address.fromHexString("0x627306090abab3a6e1400e9345bc60c78a8bef57")) - .value(Wei.ZERO) - .payload(BytesValue.wrap("EnclaveKey".getBytes(UTF_8))) - .sender(Address.fromHexString("0xfe3b557e8fb62b89f4916b721be55ceb828dbd73")) - .chainId(BigInteger.valueOf(2018)) - .signAndBuild(KEY_PAIR); + .privateFrom(BytesValues.fromBase64("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=")) + .restriction(Restriction.RESTRICTED); + + private final String enclaveKey = "93Ky7lXwFkMc7+ckoFgUMku5bpr9tz4zhmWmk9RlNng="; private final JsonRpcParameter parameters = new JsonRpcParameter(); private final Enclave enclave = mock(Enclave.class); @@ -101,10 +89,47 @@ public class EeaGetPrivateTransactionTest { private final PrivacyParameters privacyParameters = mock(PrivacyParameters.class); @Test - public void returnsPrivateTransaction() throws Exception { + public void returnsPrivateTransactionLegacy() throws Exception { + final PrivateTransaction privateTransaction = + privateTransactionBuilder + .privateFor( + Lists.newArrayList( + BytesValues.fromBase64("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="))) + .signAndBuild(KEY_PAIR); + final PrivateTransactionLegacyResult privateTransactionLegacyResult = + new PrivateTransactionLegacyResult(privateTransaction); + + final EeaGetPrivateTransaction eeaGetPrivateTransaction = + new EeaGetPrivateTransaction(enclave, parameters, privacyParameters); + final Object[] params = new Object[] {enclaveKey}; + final JsonRpcRequest request = new JsonRpcRequest("1", "eea_getPrivateTransaction", params); + + final BytesValueRLPOutput bvrlp = new BytesValueRLPOutput(); + privateTransaction.writeTo(bvrlp); + when(enclave.receive(any(ReceiveRequest.class))) + .thenReturn( + new ReceiveResponse( + Base64.getEncoder().encodeToString(bvrlp.encoded().extractArray()).getBytes(UTF_8), + "")); + final JsonRpcSuccessResponse response = + (JsonRpcSuccessResponse) eeaGetPrivateTransaction.response(request); + final PrivateTransactionResult result = (PrivateTransactionResult) response.getResult(); + + assertThat(result).isEqualToComparingFieldByField(privateTransactionLegacyResult); + } + + @Test + public void returnsPrivateTransactionGroup() throws Exception { + final PrivateTransaction privateTransaction = + privateTransactionBuilder + .privacyGroupId(BytesValues.fromBase64("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=")) + .signAndBuild(KEY_PAIR); + final PrivateTransactionGroupResult privateTransactionGroupResult = + new PrivateTransactionGroupResult(privateTransaction); + final EeaGetPrivateTransaction eeaGetPrivateTransaction = new EeaGetPrivateTransaction(enclave, parameters, privacyParameters); - final Object[] params = new Object[] {transaction.getPayload().toString()}; + final Object[] params = new Object[] {enclaveKey}; final JsonRpcRequest request = new JsonRpcRequest("1", "eea_getPrivateTransaction", params); final BytesValueRLPOutput bvrlp = new BytesValueRLPOutput(); @@ -116,8 +141,8 @@ public class EeaGetPrivateTransactionTest { "")); final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) eeaGetPrivateTransaction.response(request); - final PrivateTransaction result = (PrivateTransaction) response.getResult(); + final PrivateTransactionResult result = (PrivateTransactionResult) response.getResult(); - assertEquals(privateTransaction, result); + assertThat(result).isEqualToComparingFieldByField(privateTransactionGroupResult); } } diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaSendRawTransactionTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaSendRawTransactionTest.java index 542f9296ec..21f6ab9242 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaSendRawTransactionTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaSendRawTransactionTest.java @@ -72,12 +72,21 @@ public class EeaSendRawTransactionTest { + "657374726963746564"; private static final String VALID_PRIVATE_TRANSACTION_RLP_PRIVACY_GROUP = - "0xf897800182520894095e7baea6a6c7c4c2dfeb977efac326af552d878" - + "0801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff75" - + "9b307d495a36649353a01fffd310ac743f371de3b9f7f9cb56c0b" - + "28ad43601b4ab949f53faa07bd2c804ac4479414f69462f796e70" - + "632b4a586132594147423062436974536c4f4d4e6d2b53686d422" - + "f374d364334773d8a72657374726963746564"; + "0xf8ac800182520894095e7baea6a6c7c4c2dfeb977efac326af552d87" + + "80801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff" + + "759b307d495a36649353a01fffd310ac743f371de3b9f7f9cb56" + + "c0b28ad43601b4ab949f53faa07bd2c804a0035695b4cc4b0941" + + "e60551d7a19cf30603db5bfc23e5ac43a56f57f25f75486aa00f" + + "200e885ff29e973e2576b6600181d1b0a2b5294e30d9be4a1981" + + "ffb33a0b8c8a72657374726963746564"; + + private static final String VALID_PRIVATE_TRANSACTION_RLP_PRIVACY_GROUP_NO_PRIVATE_FROM = + "0xf88b800182520894095e7baea6a6c7c4c2dfeb977efac326af55" + + "2d8780801ba048b55bfa915ac795c431978d8a6a992b628d55" + + "7da5ff759b307d495a36649353a01fffd310ac743f371de3b9" + + "f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804a00f200e" + + "885ff29e973e2576b6600181d1b0a2b5294e30d9be4a1981ff" + + "b33a0b8c8a72657374726963746564"; private static final Transaction PUBLIC_TRANSACTION = new Transaction( @@ -115,6 +124,8 @@ public class EeaSendRawTransactionTest { @Before public void before() { when(transactionPool.getPendingTransactions()).thenReturn(pendingTransactions); + when(privateTxHandler.getEnclaveKey()) + .thenReturn("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="); method = new EeaSendRawTransaction(blockchainQueries, privateTxHandler, transactionPool, parameter); @@ -228,7 +239,7 @@ public class EeaSendRawTransactionTest { } @Test - public void validTransactionPrivayGroupIsSentToTransactionPool() throws Exception { + public void validTransactionPrivacyGroupIsSentToTransactionPool() throws Exception { when(parameter.required(any(Object[].class), anyInt(), any())) .thenReturn(VALID_PRIVATE_TRANSACTION_RLP_PRIVACY_GROUP); when(privateTxHandler.sendToOrion(any(PrivateTransaction.class))).thenReturn(MOCK_ORION_KEY); @@ -266,6 +277,45 @@ public class EeaSendRawTransactionTest { verify(transactionPool).addLocalTransaction(any(Transaction.class)); } + @Test + public void validTransactionPrivacyGroupNoPrivateFromIsSentToTransactionPool() throws Exception { + when(parameter.required(any(Object[].class), anyInt(), any())) + .thenReturn(VALID_PRIVATE_TRANSACTION_RLP_PRIVACY_GROUP_NO_PRIVATE_FROM); + when(privateTxHandler.sendToOrion(any(PrivateTransaction.class))).thenReturn(MOCK_ORION_KEY); + when(privateTxHandler.getPrivacyGroup(any(String.class), any(PrivateTransaction.class))) + .thenReturn(MOCK_PRIVACY_GROUP); + when(privateTxHandler.validatePrivateTransaction( + any(PrivateTransaction.class), any(String.class))) + .thenReturn(ValidationResult.valid()); + when(privateTxHandler.createPrivacyMarkerTransaction( + any(String.class), any(PrivateTransaction.class), any(Long.class))) + .thenReturn(PUBLIC_TRANSACTION); + when(transactionPool.addLocalTransaction(any(Transaction.class))) + .thenReturn(ValidationResult.valid()); + + final JsonRpcRequest request = + new JsonRpcRequest( + "2.0", + "eea_sendRawTransaction", + new String[] {VALID_PRIVATE_TRANSACTION_RLP_PRIVACY_GROUP_NO_PRIVATE_FROM}); + + final JsonRpcResponse expectedResponse = + new JsonRpcSuccessResponse( + request.getId(), "0x221e930a2c18d91fca4d509eaa3512f3e01fef266f660e32473de67474b36c15"); + + final JsonRpcResponse actualResponse = method.response(request); + + assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); + verify(privateTxHandler).sendToOrion(any(PrivateTransaction.class)); + verify(privateTxHandler).getPrivacyGroup(any(String.class), any(PrivateTransaction.class)); + verify(privateTxHandler) + .validatePrivateTransaction(any(PrivateTransaction.class), any(String.class)); + verify(privateTxHandler) + .createPrivacyMarkerTransaction( + any(String.class), any(PrivateTransaction.class), any(Long.class)); + verify(transactionPool).addLocalTransaction(any(Transaction.class)); + } + @Test public void invalidTransactionIsSentToTransactionPool() throws Exception { when(parameter.required(any(Object[].class), anyInt(), any()))