mirror of https://github.com/hyperledger/besu
[PRIV] Complete Private Transaction Processor (#938)
* Remove entries() in KeyValueStorage * Implement Private Transaction Processor * Implement PrivateTransactionReceipt * Refactor privacy parameter injection * Disable ether for private transaction * Implement AcceptanceTest * Fix PrivateWorldState injection * Add unimplemented private transaction type - https://entethalliance.github.io/client-spec/spec.html#sec-extensions-json-rpc * Change Private Transaction RLP * Change Private Transaction RLP for acceptanceTest * Fix copyright year * Fix build Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>pull/2/head
parent
6176a63049
commit
582454581a
@ -0,0 +1,48 @@ |
|||||||
|
/* |
||||||
|
* 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.tests.acceptance.dsl.transaction; |
||||||
|
|
||||||
|
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.ResponseTypes.PrivateTransactionReceiptResponse; |
||||||
|
|
||||||
|
import java.util.Collections; |
||||||
|
|
||||||
|
import org.assertj.core.util.Lists; |
||||||
|
import org.web3j.protocol.Web3jService; |
||||||
|
import org.web3j.protocol.core.Request; |
||||||
|
|
||||||
|
public class EeaJsonRpcRequestFactory { |
||||||
|
|
||||||
|
private final Web3jService web3jService; |
||||||
|
|
||||||
|
public EeaJsonRpcRequestFactory(final Web3jService web3jService) { |
||||||
|
this.web3jService = web3jService; |
||||||
|
} |
||||||
|
|
||||||
|
public Request<?, org.web3j.protocol.core.methods.response.EthSendTransaction> |
||||||
|
eeaSendRawTransaction(final String signedTransactionData) { |
||||||
|
return new Request<>( |
||||||
|
"eea_sendRawTransaction", |
||||||
|
Collections.singletonList(signedTransactionData), |
||||||
|
web3jService, |
||||||
|
org.web3j.protocol.core.methods.response.EthSendTransaction.class); |
||||||
|
} |
||||||
|
|
||||||
|
public Request<?, PrivateTransactionReceiptResponse> eeaGetTransactionReceipt( |
||||||
|
final String txHash, final String publicKey) { |
||||||
|
return new Request<>( |
||||||
|
"eea_getTransactionReceipt", |
||||||
|
Lists.newArrayList(txHash, publicKey), |
||||||
|
web3jService, |
||||||
|
PrivateTransactionReceiptResponse.class); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
/* |
||||||
|
* 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.tests.acceptance.dsl.transaction.eea; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.JsonRequestFactories; |
||||||
|
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.ResponseTypes.PrivateTransactionReceipt; |
||||||
|
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.ResponseTypes.PrivateTransactionReceiptResponse; |
||||||
|
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
public class EeaGetTransactionReceiptTransaction implements Transaction<PrivateTransactionReceipt> { |
||||||
|
|
||||||
|
private final String txHash; |
||||||
|
private final String publicKey; |
||||||
|
|
||||||
|
public EeaGetTransactionReceiptTransaction(final String txHash, final String publicKey) { |
||||||
|
this.txHash = txHash; |
||||||
|
this.publicKey = publicKey; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public PrivateTransactionReceipt execute(final JsonRequestFactories node) { |
||||||
|
try { |
||||||
|
final PrivateTransactionReceiptResponse result = |
||||||
|
node.eea().eeaGetTransactionReceipt(txHash, publicKey).send(); |
||||||
|
assertThat(result).isNotNull(); |
||||||
|
assertThat(result.hasError()).isFalse(); |
||||||
|
return result.getResult(); |
||||||
|
} catch (IOException e) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
/* |
||||||
|
* 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.pantheon.tests.acceptance.dsl.transaction.eea; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.JsonRequestFactories; |
||||||
|
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
import org.web3j.protocol.core.methods.response.EthSendTransaction; |
||||||
|
|
||||||
|
public class EeaSendRawTransactionTransaction implements Transaction<String> { |
||||||
|
|
||||||
|
private final String transactionData; |
||||||
|
|
||||||
|
public EeaSendRawTransactionTransaction(final String transactionData) { |
||||||
|
this.transactionData = transactionData; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String execute(final JsonRequestFactories node) { |
||||||
|
try { |
||||||
|
EthSendTransaction response = node.eea().eeaSendRawTransaction(transactionData).send(); |
||||||
|
assertThat(response.getTransactionHash()).isNotNull(); |
||||||
|
return response.getTransactionHash(); |
||||||
|
} catch (final IOException e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,183 @@ |
|||||||
|
/* |
||||||
|
* 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.tests.web3j.privacy; |
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8; |
||||||
|
import static org.junit.Assert.assertEquals; |
||||||
|
import static tech.pegasys.pantheon.tests.acceptance.dsl.WaitUtils.waitFor; |
||||||
|
|
||||||
|
import tech.pegasys.orion.testutil.OrionTestHarness; |
||||||
|
import tech.pegasys.pantheon.crypto.SECP256K1; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Address; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Wei; |
||||||
|
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction; |
||||||
|
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput; |
||||||
|
import tech.pegasys.pantheon.tests.acceptance.dsl.AcceptanceTestBase; |
||||||
|
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode; |
||||||
|
import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.ResponseTypes.PrivateTransactionReceipt; |
||||||
|
import tech.pegasys.pantheon.util.bytes.BytesValue; |
||||||
|
|
||||||
|
import java.math.BigInteger; |
||||||
|
|
||||||
|
import com.google.common.collect.Lists; |
||||||
|
import org.junit.After; |
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.ClassRule; |
||||||
|
import org.junit.Test; |
||||||
|
import org.junit.rules.TemporaryFolder; |
||||||
|
import org.web3j.crypto.Credentials; |
||||||
|
import org.web3j.protocol.core.methods.response.TransactionReceipt; |
||||||
|
|
||||||
|
public class DeployPrivateSmartContractAcceptanceTest extends AcceptanceTestBase { |
||||||
|
|
||||||
|
@ClassRule public static final TemporaryFolder folder = new TemporaryFolder(); |
||||||
|
|
||||||
|
// Contract address is generated from sender address and transaction nonce
|
||||||
|
private static final Address CONTRACT_ADDRESS = |
||||||
|
Address.fromHexString("0x42699a7612a82f1d9c36148af9c77354759b210b"); |
||||||
|
|
||||||
|
private static final Address SENDER = |
||||||
|
Address.fromHexString( |
||||||
|
Credentials.create("8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63") |
||||||
|
.getAddress()); |
||||||
|
|
||||||
|
private static final PrivateTransaction DEPLOY_CONTRACT = |
||||||
|
PrivateTransaction.builder() |
||||||
|
.nonce(0) |
||||||
|
.gasPrice(Wei.of(1000)) |
||||||
|
.gasLimit(3000000) |
||||||
|
.to(null) |
||||||
|
.value(Wei.ZERO) |
||||||
|
.payload( |
||||||
|
BytesValue.fromHexString( |
||||||
|
"0x608060405234801561001057600080fd5b5060d08061001f60003960" |
||||||
|
+ "00f3fe60806040526004361060485763ffffffff7c01000000" |
||||||
|
+ "00000000000000000000000000000000000000000000000000" |
||||||
|
+ "60003504166360fe47b18114604d5780636d4ce63c14607557" |
||||||
|
+ "5b600080fd5b348015605857600080fd5b5060736004803603" |
||||||
|
+ "6020811015606d57600080fd5b50356099565b005b34801560" |
||||||
|
+ "8057600080fd5b506087609e565b6040805191825251908190" |
||||||
|
+ "0360200190f35b600055565b6000549056fea165627a7a7230" |
||||||
|
+ "5820cb1d0935d14b589300b12fcd0ab849a7e9019c81da24d6" |
||||||
|
+ "daa4f6b2f003d1b0180029")) |
||||||
|
.sender(SENDER) |
||||||
|
.chainId(2018) |
||||||
|
.privateFrom( |
||||||
|
BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8))) |
||||||
|
.privateFor( |
||||||
|
Lists.newArrayList( |
||||||
|
BytesValue.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8)))) |
||||||
|
.restriction(BytesValue.wrap("unrestricted".getBytes(UTF_8))) |
||||||
|
.signAndBuild( |
||||||
|
SECP256K1.KeyPair.create( |
||||||
|
SECP256K1.PrivateKey.create( |
||||||
|
new BigInteger( |
||||||
|
"8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", |
||||||
|
16)))); |
||||||
|
|
||||||
|
private static final PrivateTransaction FUNCTION_CALL = |
||||||
|
PrivateTransaction.builder() |
||||||
|
.nonce(1) |
||||||
|
.gasPrice(Wei.of(1000)) |
||||||
|
.gasLimit(3000000) |
||||||
|
.to(CONTRACT_ADDRESS) |
||||||
|
.value(Wei.ZERO) |
||||||
|
.payload( |
||||||
|
BytesValue.fromHexString( |
||||||
|
"0xcccdda2cf2895862749f1c69aa9f55cf481ea82500e4eabb4e2578b36636979b" |
||||||
|
+ "0000000000000000000000000000000000000000000000000000000000000000")) |
||||||
|
.sender(SENDER) |
||||||
|
.chainId(2018) |
||||||
|
.privateFrom( |
||||||
|
BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8))) |
||||||
|
.privateFor( |
||||||
|
Lists.newArrayList( |
||||||
|
BytesValue.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8)))) |
||||||
|
.restriction(BytesValue.wrap("unrestricted".getBytes(UTF_8))) |
||||||
|
.signAndBuild( |
||||||
|
SECP256K1.KeyPair.create( |
||||||
|
SECP256K1.PrivateKey.create( |
||||||
|
new BigInteger( |
||||||
|
"8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", |
||||||
|
16)))); |
||||||
|
|
||||||
|
private PantheonNode minerNode; |
||||||
|
|
||||||
|
private OrionTestHarness testHarness; |
||||||
|
|
||||||
|
@Before |
||||||
|
public void setUp() throws Exception { |
||||||
|
testHarness = OrionTestHarness.create(folder.newFolder().toPath()); |
||||||
|
|
||||||
|
final PrivacyParameters privacyParameters = new PrivacyParameters(); |
||||||
|
privacyParameters.setUrl(testHarness.clientUrl()); |
||||||
|
privacyParameters.setPrivacyAddress(Address.PRIVACY); |
||||||
|
privacyParameters.setPublicKeyUsingFile(testHarness.getConfig().publicKeys().get(0).toFile()); |
||||||
|
privacyParameters.enablePrivateDB(folder.newFolder("private").toPath()); |
||||||
|
|
||||||
|
minerNode = pantheon.createPrivateTransactionEnabledMinerNode("miner-node", privacyParameters); |
||||||
|
cluster.start(minerNode); |
||||||
|
} |
||||||
|
|
||||||
|
@After |
||||||
|
public void tearDownOnce() { |
||||||
|
testHarness.getOrion().stop(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void deployingMustGiveValidReceipt() { |
||||||
|
|
||||||
|
final String signedRawDeployTransaction = toRlp(DEPLOY_CONTRACT); |
||||||
|
|
||||||
|
final String transactionHash = |
||||||
|
minerNode.execute(transactions.createPrivateRawTransaction(signedRawDeployTransaction)); |
||||||
|
|
||||||
|
minerNode.waitUntil(wait.chainHeadHasProgressed(minerNode, 2)); |
||||||
|
|
||||||
|
waitFor(() -> minerNode.verify(eth.expectSuccessfulTransactionReceipt(transactionHash))); |
||||||
|
|
||||||
|
TransactionReceipt txReceipt = |
||||||
|
minerNode.execute(transactions.getTransactionReceipt(transactionHash)).get(); |
||||||
|
|
||||||
|
assertEquals(Address.DEFAULT_PRIVACY.toString(), txReceipt.getTo()); |
||||||
|
|
||||||
|
PrivateTransactionReceipt privateTxReceipt = |
||||||
|
minerNode.execute( |
||||||
|
transactions.getPrivateTransactionReceipt( |
||||||
|
transactionHash, "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=")); |
||||||
|
|
||||||
|
assertEquals(CONTRACT_ADDRESS.toString(), privateTxReceipt.getContractAddress()); |
||||||
|
|
||||||
|
final String signedRawFunctionTransaction = toRlp(FUNCTION_CALL); |
||||||
|
|
||||||
|
final String transactionHash1 = |
||||||
|
minerNode.execute(transactions.createPrivateRawTransaction(signedRawFunctionTransaction)); |
||||||
|
|
||||||
|
minerNode.waitUntil(wait.chainHeadHasProgressed(minerNode, 2)); |
||||||
|
|
||||||
|
waitFor(() -> minerNode.verify(eth.expectSuccessfulTransactionReceipt(transactionHash1))); |
||||||
|
|
||||||
|
TransactionReceipt txReceipt2 = |
||||||
|
minerNode.execute(transactions.getTransactionReceipt(transactionHash)).get(); |
||||||
|
|
||||||
|
// TODO: fire function call from minerNode and from a non-privy node
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private String toRlp(final PrivateTransaction transaction) { |
||||||
|
BytesValueRLPOutput bvrlpo = new BytesValueRLPOutput(); |
||||||
|
transaction.writeTo(bvrlpo); |
||||||
|
return bvrlpo.encoded().toString(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,93 @@ |
|||||||
|
/* |
||||||
|
* 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.methods.privacy; |
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8; |
||||||
|
|
||||||
|
import tech.pegasys.pantheon.enclave.Enclave; |
||||||
|
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.Hash; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Transaction; |
||||||
|
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest; |
||||||
|
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.queries.BlockchainQueries; |
||||||
|
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.PrivateTransactionReceiptResult; |
||||||
|
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction; |
||||||
|
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPInput; |
||||||
|
import tech.pegasys.pantheon.util.bytes.BytesValue; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Base64; |
||||||
|
import java.util.Optional; |
||||||
|
|
||||||
|
public class EeaGetTransactionReceipt implements JsonRpcMethod { |
||||||
|
|
||||||
|
private final BlockchainQueries blockchain; |
||||||
|
private final Enclave enclave; |
||||||
|
private final JsonRpcParameter parameters; |
||||||
|
|
||||||
|
public EeaGetTransactionReceipt( |
||||||
|
final BlockchainQueries blockchain, |
||||||
|
final Enclave enclave, |
||||||
|
final JsonRpcParameter parameters) { |
||||||
|
this.blockchain = blockchain; |
||||||
|
this.enclave = enclave; |
||||||
|
this.parameters = parameters; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() { |
||||||
|
return "eea_getTransactionReceipt"; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonRpcResponse response(final JsonRpcRequest request) { |
||||||
|
final Hash hash = parameters.required(request.getParams(), 0, Hash.class); |
||||||
|
final String publicKey = parameters.required(request.getParams(), 1, String.class); |
||||||
|
final Optional<Transaction> transactionCompleteResult = |
||||||
|
blockchain.getBlockchain().getTransactionByHash(hash); |
||||||
|
final PrivateTransactionReceiptResult result = |
||||||
|
transactionCompleteResult |
||||||
|
.map( |
||||||
|
t -> { |
||||||
|
final ReceiveRequest enclaveRequest = |
||||||
|
new ReceiveRequest( |
||||||
|
new String(t.getPayload().extractArray(), UTF_8), publicKey); |
||||||
|
try { |
||||||
|
ReceiveResponse enclaveResponse = enclave.receive(enclaveRequest); |
||||||
|
final BytesValueRLPInput bytesValueRLPInput = |
||||||
|
new BytesValueRLPInput( |
||||||
|
BytesValue.wrap( |
||||||
|
Base64.getDecoder().decode(enclaveResponse.getPayload())), |
||||||
|
false); |
||||||
|
PrivateTransaction pt = PrivateTransaction.readFrom(bytesValueRLPInput); |
||||||
|
final String contractAddress = |
||||||
|
Address.contractAddress(pt.getSender(), pt.getNonce()).toString(); |
||||||
|
// TODO(PRIV): Return internal transaction emitted events and return values
|
||||||
|
return new PrivateTransactionReceiptResult( |
||||||
|
contractAddress, |
||||||
|
pt.getSender().toString(), |
||||||
|
pt.getTo().map(Address::toString).orElse(null)); |
||||||
|
} catch (IOException e) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
}) |
||||||
|
.orElse(null); |
||||||
|
return new JsonRpcSuccessResponse(request.getId(), result); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,77 @@ |
|||||||
|
/* |
||||||
|
* 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.core.Hash; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Log; |
||||||
|
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.TransactionReceiptLogResult; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonGetter; |
||||||
|
import com.fasterxml.jackson.annotation.JsonPropertyOrder; |
||||||
|
|
||||||
|
@JsonPropertyOrder({ |
||||||
|
"contractAddress", |
||||||
|
"from", |
||||||
|
"to", |
||||||
|
}) |
||||||
|
public class PrivateTransactionReceiptResult { |
||||||
|
|
||||||
|
private final String contractAddress; |
||||||
|
private final String from; |
||||||
|
private final String to; |
||||||
|
|
||||||
|
public PrivateTransactionReceiptResult( |
||||||
|
final String contractAddress, final String from, final String to) { |
||||||
|
|
||||||
|
this.contractAddress = contractAddress; |
||||||
|
this.from = from; |
||||||
|
// TODO: handle logs as in TransactionReceiptResult
|
||||||
|
this.to = to; |
||||||
|
} |
||||||
|
|
||||||
|
@JsonGetter(value = "contractAddress") |
||||||
|
public String getContractAddress() { |
||||||
|
return contractAddress; |
||||||
|
} |
||||||
|
|
||||||
|
@JsonGetter(value = "from") |
||||||
|
public String getFrom() { |
||||||
|
return from; |
||||||
|
} |
||||||
|
|
||||||
|
@JsonGetter(value = "to") |
||||||
|
public String getTo() { |
||||||
|
return to; |
||||||
|
} |
||||||
|
|
||||||
|
private List<TransactionReceiptLogResult> logReceipts( |
||||||
|
final List<Log> logs, |
||||||
|
final long blockNumber, |
||||||
|
final Hash transactionHash, |
||||||
|
final Hash blockHash, |
||||||
|
final int transactionIndex) { |
||||||
|
final List<TransactionReceiptLogResult> logResults = new ArrayList<>(logs.size()); |
||||||
|
|
||||||
|
for (int i = 0; i < logs.size(); i++) { |
||||||
|
final Log log = logs.get(i); |
||||||
|
logResults.add( |
||||||
|
new TransactionReceiptLogResult( |
||||||
|
log, blockNumber, transactionHash, blockHash, transactionIndex, i)); |
||||||
|
} |
||||||
|
|
||||||
|
return logResults; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,132 @@ |
|||||||
|
/* |
||||||
|
* 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.methods.privacy; |
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8; |
||||||
|
import static org.junit.Assert.assertEquals; |
||||||
|
import static org.mockito.ArgumentMatchers.any; |
||||||
|
import static org.mockito.Mockito.mock; |
||||||
|
import static org.mockito.Mockito.when; |
||||||
|
|
||||||
|
import tech.pegasys.pantheon.crypto.SECP256K1; |
||||||
|
import tech.pegasys.pantheon.enclave.Enclave; |
||||||
|
import tech.pegasys.pantheon.enclave.types.ReceiveRequest; |
||||||
|
import tech.pegasys.pantheon.enclave.types.ReceiveResponse; |
||||||
|
import tech.pegasys.pantheon.ethereum.chain.Blockchain; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Address; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Hash; |
||||||
|
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.queries.BlockchainQueries; |
||||||
|
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse; |
||||||
|
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.privacy.PrivateTransactionReceiptResult; |
||||||
|
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction; |
||||||
|
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput; |
||||||
|
import tech.pegasys.pantheon.util.bytes.BytesValue; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.math.BigInteger; |
||||||
|
import java.util.Base64; |
||||||
|
import java.util.Optional; |
||||||
|
|
||||||
|
import com.google.common.collect.Lists; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
public class EeaGetTransactionReceiptTest { |
||||||
|
|
||||||
|
private final Address sender = |
||||||
|
Address.fromHexString("0x0000000000000000000000000000000000000003"); |
||||||
|
private final PrivateTransaction privateTransaction = |
||||||
|
PrivateTransaction.builder() |
||||||
|
.nonce(0) |
||||||
|
.gasPrice(Wei.of(1000)) |
||||||
|
.gasLimit(3000000) |
||||||
|
.to(null) |
||||||
|
.value(Wei.ZERO) |
||||||
|
.payload( |
||||||
|
BytesValue.fromHexString( |
||||||
|
"0x608060405234801561001057600080fd5b5060d08061001f60003960" |
||||||
|
+ "00f3fe60806040526004361060485763ffffffff7c01000000" |
||||||
|
+ "00000000000000000000000000000000000000000000000000" |
||||||
|
+ "60003504166360fe47b18114604d5780636d4ce63c14607557" |
||||||
|
+ "5b600080fd5b348015605857600080fd5b5060736004803603" |
||||||
|
+ "6020811015606d57600080fd5b50356099565b005b34801560" |
||||||
|
+ "8057600080fd5b506087609e565b6040805191825251908190" |
||||||
|
+ "0360200190f35b600055565b6000549056fea165627a7a7230" |
||||||
|
+ "5820cb1d0935d14b589300b12fcd0ab849a7e9019c81da24d6" |
||||||
|
+ "daa4f6b2f003d1b0180029")) |
||||||
|
.sender(sender) |
||||||
|
.chainId(2018) |
||||||
|
.privateFrom( |
||||||
|
BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8))) |
||||||
|
.privateFor( |
||||||
|
Lists.newArrayList( |
||||||
|
BytesValue.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8)))) |
||||||
|
.restriction(BytesValue.wrap("restricted".getBytes(UTF_8))) |
||||||
|
.signAndBuild( |
||||||
|
SECP256K1.KeyPair.create( |
||||||
|
SECP256K1.PrivateKey.create( |
||||||
|
new BigInteger( |
||||||
|
"8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", |
||||||
|
16)))); |
||||||
|
private final Transaction transaction = |
||||||
|
new Transaction( |
||||||
|
privateTransaction.getNonce(), |
||||||
|
privateTransaction.getGasPrice(), |
||||||
|
privateTransaction.getGasLimit(), |
||||||
|
Optional.of(Address.DEFAULT_PRIVACY), |
||||||
|
privateTransaction.getValue(), |
||||||
|
privateTransaction.getSignature(), |
||||||
|
BytesValue.wrap("EnclaveKey".getBytes(UTF_8)), |
||||||
|
privateTransaction.getSender(), |
||||||
|
privateTransaction.getChainId().getAsInt()); |
||||||
|
|
||||||
|
private final Hash hash = |
||||||
|
Hash.fromHexString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); |
||||||
|
private final Hash blockHash = |
||||||
|
Hash.fromHexString("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); |
||||||
|
|
||||||
|
private final JsonRpcParameter parameters = new JsonRpcParameter(); |
||||||
|
|
||||||
|
private final BlockchainQueries blockchainQueries = mock(BlockchainQueries.class); |
||||||
|
private final Blockchain blockchain = mock(Blockchain.class); |
||||||
|
private final Enclave enclave = mock(Enclave.class); |
||||||
|
private final EeaGetTransactionReceipt eeaGetTransactionReceipt = |
||||||
|
new EeaGetTransactionReceipt(blockchainQueries, enclave, parameters); |
||||||
|
Object[] params = new Object[] {transaction.hash(), "EnclavePublicKey"}; |
||||||
|
private final JsonRpcRequest request = |
||||||
|
new JsonRpcRequest("1", "eea_getTransactionReceipt", params); |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createsPrivateTransactionReceipt() throws IOException { |
||||||
|
when(blockchainQueries.getBlockchain()).thenReturn(blockchain); |
||||||
|
when(blockchain.getTransactionByHash(transaction.hash())).thenReturn(Optional.of(transaction)); |
||||||
|
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) eeaGetTransactionReceipt.response(request); |
||||||
|
final PrivateTransactionReceiptResult result = |
||||||
|
(PrivateTransactionReceiptResult) response.getResult(); |
||||||
|
|
||||||
|
assertEquals("0x42699a7612a82f1d9c36148af9c77354759b210b", result.getContractAddress()); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue