mirror of https://github.com/hyperledger/besu
[23.4] Remove GoQuorum privacy (#5303)
* removed separate decoding logic * run non-mainnet ATs to make sure * remove goQuorum flag from everywhere * remove GOQUORUM api group * remove GoQuorum enclave, privacy params, RPCs and other related config * removed Goquorum related error codes, rpcMethod codes, privateBloom * removed v from transaction * removed private GoQuorum storage provider --------- Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>pull/5344/head
parent
42f52e72be
commit
067a263374
@ -1,58 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.config; |
||||
|
||||
/** |
||||
* Flag to determine whether we are processing in GoQuorum mode. Note that this mode is incompatible |
||||
* with MainNet. |
||||
*/ |
||||
public class GoQuorumOptions { |
||||
private static final boolean GOQUORUM_COMPATIBILITY_MODE_DEFAULT_VALUE = false; |
||||
|
||||
private static Boolean goQuorumCompatibilityMode; |
||||
|
||||
/** |
||||
* Sets go quorum compatibility mode. |
||||
* |
||||
* @param goQuorumCompatibilityMode the go quorum compatibility mode |
||||
*/ |
||||
public static void setGoQuorumCompatibilityMode(final boolean goQuorumCompatibilityMode) { |
||||
if (GoQuorumOptions.goQuorumCompatibilityMode == null) { |
||||
GoQuorumOptions.goQuorumCompatibilityMode = goQuorumCompatibilityMode; |
||||
} else { |
||||
throw new IllegalStateException( |
||||
"goQuorumCompatibilityMode can not be changed after having been assigned"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Gets go quorum compatibility mode. |
||||
* |
||||
* @return the go quorum compatibility mode |
||||
*/ |
||||
public static boolean getGoQuorumCompatibilityMode() { |
||||
if (goQuorumCompatibilityMode == null) { |
||||
// If the quorum mode has never been set, we default it
|
||||
// here. This allows running individual unit tests that
|
||||
// query the quorum mode without having to include a
|
||||
// setGoQuorumCompatibilityMode call in their setup
|
||||
// procedure. For production use, this case is not
|
||||
// triggered as we set the quorum mode very early during
|
||||
// startup.
|
||||
goQuorumCompatibilityMode = GOQUORUM_COMPATIBILITY_MODE_DEFAULT_VALUE; |
||||
} |
||||
return goQuorumCompatibilityMode; |
||||
} |
||||
} |
@ -1,73 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.consensus.ibft; |
||||
|
||||
import org.hyperledger.besu.consensus.common.BlockInterface; |
||||
import org.hyperledger.besu.consensus.common.EpochManager; |
||||
import org.hyperledger.besu.consensus.common.PoaContext; |
||||
import org.hyperledger.besu.consensus.common.validator.ValidatorProvider; |
||||
import org.hyperledger.besu.ethereum.ConsensusContext; |
||||
|
||||
/** Holds the BFT specific mutable state. */ |
||||
public class IbftLegacyContext implements PoaContext { |
||||
|
||||
private final ValidatorProvider validatorProvider; |
||||
private final EpochManager epochManager; |
||||
private final BlockInterface blockInterface; |
||||
|
||||
/** |
||||
* Instantiates a new Ibft legacy context. |
||||
* |
||||
* @param validatorProvider the validator provider |
||||
* @param epochManager the epoch manager |
||||
* @param blockInterface the block interface
|
||||
*/ |
||||
public IbftLegacyContext( |
||||
final ValidatorProvider validatorProvider, |
||||
final EpochManager epochManager, |
||||
final BlockInterface blockInterface) { |
||||
this.validatorProvider = validatorProvider; |
||||
this.epochManager = epochManager; |
||||
this.blockInterface = blockInterface; |
||||
} |
||||
|
||||
/** |
||||
* Gets validator provider. |
||||
* |
||||
* @return the validator provider |
||||
*/ |
||||
public ValidatorProvider getValidatorProvider() { |
||||
return validatorProvider; |
||||
} |
||||
|
||||
/** |
||||
* Gets epoch manager. |
||||
* |
||||
* @return the epoch manager |
||||
*/ |
||||
public EpochManager getEpochManager() { |
||||
return epochManager; |
||||
} |
||||
|
||||
@Override |
||||
public BlockInterface getBlockInterface() { |
||||
return blockInterface; |
||||
} |
||||
|
||||
@Override |
||||
public <C extends ConsensusContext> C as(final Class<C> klass) { |
||||
return klass.cast(this); |
||||
} |
||||
} |
@ -1,126 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.enclave; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.ArgumentMatchers.anyBoolean; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import org.hyperledger.besu.enclave.types.GoQuorumReceiveResponse; |
||||
import org.hyperledger.besu.enclave.types.SendResponse; |
||||
import org.hyperledger.besu.enclave.types.StoreRawResponse; |
||||
|
||||
import java.net.URI; |
||||
import java.net.URISyntaxException; |
||||
import java.util.Arrays; |
||||
import java.util.Base64; |
||||
import java.util.List; |
||||
|
||||
import io.vertx.core.Vertx; |
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.mockito.ArgumentMatchers; |
||||
|
||||
public class GoQuorumEnclaveTest { |
||||
|
||||
private static final byte[] PAYLOAD = Base64.getDecoder().decode("EAAAAAAA"); |
||||
private static final String MOCK_KEY = "iOCzoGo5kwtZU0J41Z9xnGXHN6ZNukIa9MspvHtu3Jk="; |
||||
private static final String KEY = |
||||
"tQEmN0d/xXJZs5OMgl8QVBIyYxu1XubAKehsSYbcOjbxai+QJQpEOs6ghrYAZizLtnM4EJdMyVeVrxO3cA9JJA=="; |
||||
private static GoQuorumEnclave enclave; |
||||
|
||||
private RequestTransmitter vertxTransmitter; |
||||
|
||||
@BeforeEach |
||||
public void setUp() { |
||||
enclave = createGoQuorumEnclaveWithMockRequestTransmitter(); |
||||
} |
||||
|
||||
@Test |
||||
public void upCheck() { |
||||
when(vertxTransmitter.get( |
||||
any(), any(), ArgumentMatchers.contains("/upcheck"), any(), anyBoolean())) |
||||
.thenReturn("I'm up!"); |
||||
|
||||
assertThat(enclave.upCheck()).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void receiveThrowsWhenPayloadDoesNotExist() { |
||||
when(vertxTransmitter.get( |
||||
any(), any(), ArgumentMatchers.contains("/transaction"), any(), anyBoolean())) |
||||
.thenThrow( |
||||
new EnclaveClientException(404, "Message with hash " + MOCK_KEY + " was not found")); |
||||
|
||||
assertThatThrownBy(() -> enclave.receive(MOCK_KEY)) |
||||
.isInstanceOf(EnclaveClientException.class) |
||||
.hasMessageContaining("Message with hash " + MOCK_KEY + " was not found"); |
||||
} |
||||
|
||||
@Test |
||||
public void sendAndReceive() { |
||||
when(vertxTransmitter.post(any(), any(), any(), any())).thenReturn(new SendResponse(KEY)); |
||||
when(vertxTransmitter.get( |
||||
any(), any(), ArgumentMatchers.contains("/transaction"), any(), anyBoolean())) |
||||
.thenReturn(new GoQuorumReceiveResponse(PAYLOAD, 0, null, null)); |
||||
|
||||
final List<String> publicKeys = Arrays.asList("/+UuD63zItL1EbjxkKUljMgG8Z1w0AJ8pNOR4iq2yQc="); |
||||
|
||||
final SendResponse sr = enclave.send(PAYLOAD, publicKeys.get(0), publicKeys); |
||||
assertThat(sr.getKey()).isEqualTo(KEY); |
||||
|
||||
final GoQuorumReceiveResponse rr = enclave.receive(sr.getKey()); |
||||
assertThat(rr).isNotNull(); |
||||
assertThat(rr.getPayload()).isEqualTo(PAYLOAD); |
||||
} |
||||
|
||||
@Test |
||||
public void sendSignedTransaction() { |
||||
when(vertxTransmitter.post(any(), any(), ArgumentMatchers.contains("/sendsignedtx"), any())) |
||||
.thenReturn(new SendResponse(KEY)); |
||||
|
||||
final List<String> publicKeys = Arrays.asList("/+UuD63zItL1EbjxkKUljMgG8Z1w0AJ8pNOR4iq2yQc="); |
||||
|
||||
final SendResponse sr = enclave.sendSignedTransaction(PAYLOAD, publicKeys); |
||||
assertThat(sr.getKey()).isEqualTo(KEY); |
||||
} |
||||
|
||||
@Test |
||||
public void storeRawTransaction() { |
||||
when(vertxTransmitter.post(any(), any(), ArgumentMatchers.contains("/storeraw"), any())) |
||||
.thenReturn(new StoreRawResponse(KEY)); |
||||
|
||||
final StoreRawResponse sr = |
||||
enclave.storeRaw( |
||||
"tQEmN0d/xXJZs5OMgl8QVBIyYxu1XubAKehsSYbcOjbxai+QJQpEOs6ghrYAZizLtnM4EJdMyVeVrxO3cA9JJA=="); |
||||
assertThat(sr.getKey()).isEqualTo(KEY); |
||||
} |
||||
|
||||
@Test |
||||
public void upcheckReturnsFalseIfNoResponseReceived() throws URISyntaxException { |
||||
final Vertx vertx = Vertx.vertx(); |
||||
final EnclaveFactory factory = new EnclaveFactory(vertx); |
||||
assertThat(factory.createGoQuorumEnclave(new URI("http://8.8.8.8:65535")).upCheck()).isFalse(); |
||||
} |
||||
|
||||
private GoQuorumEnclave createGoQuorumEnclaveWithMockRequestTransmitter() { |
||||
vertxTransmitter = mock(RequestTransmitter.class); |
||||
|
||||
return new GoQuorumEnclave(vertxTransmitter); |
||||
} |
||||
} |
@ -1,194 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.enclave; |
||||
|
||||
import org.hyperledger.besu.enclave.RequestTransmitter.ResponseBodyHandler; |
||||
import org.hyperledger.besu.enclave.types.GoQuorumReceiveResponse; |
||||
import org.hyperledger.besu.enclave.types.GoQuorumSendRequest; |
||||
import org.hyperledger.besu.enclave.types.GoQuorumSendSignedRequest; |
||||
import org.hyperledger.besu.enclave.types.GoQuorumStoreRawRequest; |
||||
import org.hyperledger.besu.enclave.types.SendResponse; |
||||
import org.hyperledger.besu.enclave.types.StoreRawResponse; |
||||
|
||||
import java.io.IOException; |
||||
import java.net.URLEncoder; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.List; |
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException; |
||||
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
|
||||
/** The GoQuorum enclave. */ |
||||
public class GoQuorumEnclave { |
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper(); |
||||
private static final String JSON = "application/json"; |
||||
|
||||
private final RequestTransmitter requestTransmitter; |
||||
|
||||
/** |
||||
* Instantiates a new GoQuorum enclave. |
||||
* |
||||
* @param requestTransmitter the request transmitter |
||||
*/ |
||||
public GoQuorumEnclave(final RequestTransmitter requestTransmitter) { |
||||
this.requestTransmitter = requestTransmitter; |
||||
} |
||||
|
||||
/** |
||||
* Up check. |
||||
* |
||||
* @return the boolean |
||||
*/ |
||||
public boolean upCheck() { |
||||
try { |
||||
final String upcheckResponse = |
||||
requestTransmitter.get(null, null, "/upcheck", this::handleRawResponse, false); |
||||
return upcheckResponse.equals("I'm up!"); |
||||
} catch (final Exception e) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Send payload. |
||||
* |
||||
* @param payload the payload |
||||
* @param privateFrom the private from |
||||
* @param privateFor the private for |
||||
* @return the send response |
||||
*/ |
||||
public SendResponse send( |
||||
final byte[] payload, final String privateFrom, final List<String> privateFor) { |
||||
final GoQuorumSendRequest request = new GoQuorumSendRequest(payload, privateFrom, privateFor); |
||||
return post( |
||||
JSON, |
||||
request, |
||||
"/send", |
||||
(statusCode, body) -> handleJsonResponse(statusCode, body, SendResponse.class, 201)); |
||||
} |
||||
|
||||
/** |
||||
* Send signed transaction. |
||||
* |
||||
* @param txLookupId the tx lookup id |
||||
* @param privateFor the private for |
||||
* @return the send response |
||||
*/ |
||||
public SendResponse sendSignedTransaction( |
||||
final byte[] txLookupId, final List<String> privateFor) { |
||||
final GoQuorumSendSignedRequest request = new GoQuorumSendSignedRequest(txLookupId, privateFor); |
||||
return post( |
||||
JSON, |
||||
request, |
||||
"/sendsignedtx", |
||||
(statusCode, body) -> handleJsonResponse(statusCode, body, SendResponse.class, 201)); |
||||
} |
||||
|
||||
/** |
||||
* Store raw payload. |
||||
* |
||||
* @param payload the payload |
||||
* @return the store raw response |
||||
*/ |
||||
public StoreRawResponse storeRaw(final String payload) { |
||||
final GoQuorumStoreRawRequest request = new GoQuorumStoreRawRequest(payload); |
||||
return post( |
||||
JSON, |
||||
request, |
||||
"/storeraw", |
||||
(statusCode, body) -> handleJsonResponse(statusCode, body, StoreRawResponse.class, 200)); |
||||
} |
||||
|
||||
/** |
||||
* Receive GoQuorum response. |
||||
* |
||||
* @param payloadKey the payload key |
||||
* @return the go quorum receive response |
||||
*/ |
||||
public GoQuorumReceiveResponse receive(final String payloadKey) { |
||||
return get( |
||||
JSON, |
||||
"/transaction/" + URLEncoder.encode(payloadKey, StandardCharsets.UTF_8), |
||||
(statusCode, body) -> |
||||
handleJsonResponse(statusCode, body, GoQuorumReceiveResponse.class, 200)); |
||||
} |
||||
|
||||
private <T> T post( |
||||
final String mediaType, |
||||
final Object content, |
||||
final String endpoint, |
||||
final ResponseBodyHandler<T> responseBodyHandler) { |
||||
final String bodyText; |
||||
try { |
||||
bodyText = objectMapper.writeValueAsString(content); |
||||
} catch (final JsonProcessingException e) { |
||||
throw new EnclaveClientException(400, "Unable to serialize request."); |
||||
} |
||||
return requestTransmitter.post(mediaType, bodyText, endpoint, responseBodyHandler); |
||||
} |
||||
|
||||
private <T> T get( |
||||
final String mediaType, |
||||
final String endpoint, |
||||
final ResponseBodyHandler<T> responseBodyHandler) { |
||||
final T t = requestTransmitter.get(mediaType, null, endpoint, responseBodyHandler, true); |
||||
return t; |
||||
} |
||||
|
||||
private <T> T handleJsonResponse( |
||||
final int statusCode, |
||||
final byte[] body, |
||||
final Class<T> responseType, |
||||
final int expectedStatusCode) { |
||||
|
||||
if (isSuccess(statusCode, expectedStatusCode)) { |
||||
return parseResponse(statusCode, body, responseType); |
||||
} else if (clientError(statusCode)) { |
||||
final String utf8EncodedBody = new String(body, StandardCharsets.UTF_8); |
||||
throw new EnclaveClientException(statusCode, utf8EncodedBody); |
||||
} else { |
||||
final String utf8EncodedBody = new String(body, StandardCharsets.UTF_8); |
||||
throw new EnclaveServerException(statusCode, utf8EncodedBody); |
||||
} |
||||
} |
||||
|
||||
private <T> T parseResponse( |
||||
final int statusCode, final byte[] body, final Class<T> responseType) { |
||||
try { |
||||
return objectMapper.readValue(body, responseType); |
||||
} catch (final IOException e) { |
||||
final String utf8EncodedBody = new String(body, StandardCharsets.UTF_8); |
||||
throw new EnclaveClientException(statusCode, utf8EncodedBody); |
||||
} |
||||
} |
||||
|
||||
private boolean clientError(final int statusCode) { |
||||
return statusCode >= 400 && statusCode < 500; |
||||
} |
||||
|
||||
private boolean isSuccess(final int statusCode, final int expectedStatusCode) { |
||||
return statusCode == expectedStatusCode; |
||||
} |
||||
|
||||
private String handleRawResponse(final int statusCode, final byte[] body) { |
||||
final String bodyText = new String(body, StandardCharsets.UTF_8); |
||||
if (isSuccess(statusCode, 200)) { |
||||
return bodyText; |
||||
} |
||||
throw new EnclaveClientException( |
||||
statusCode, String.format("Request failed with %d; body={%s}", statusCode, bodyText)); |
||||
} |
||||
} |
@ -1,84 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.enclave.types; |
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator; |
||||
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
||||
/** The GoQuorum receive response. */ |
||||
public class GoQuorumReceiveResponse { |
||||
|
||||
private final byte[] payload; |
||||
private final int privacyFlag; |
||||
private final String affectedContractTransactions[]; |
||||
private final String execHash; |
||||
|
||||
/** |
||||
* Instantiates a new GoQuorum receive response. |
||||
* |
||||
* @param payload the payload |
||||
* @param privacyFlag the privacy flag |
||||
* @param affectedContractTransactions the affected contract transactions |
||||
* @param execHash the exec hash |
||||
*/ |
||||
@JsonCreator |
||||
public GoQuorumReceiveResponse( |
||||
@JsonProperty(value = "payload") final byte[] payload, |
||||
@JsonProperty(value = "privacyFlag") final int privacyFlag, |
||||
@JsonProperty(value = "affectedContractTransactions") |
||||
final String affectedContractTransactions[], |
||||
@JsonProperty(value = "execHash") final String execHash) { |
||||
this.payload = payload; |
||||
this.privacyFlag = privacyFlag; |
||||
this.affectedContractTransactions = affectedContractTransactions; |
||||
this.execHash = execHash; |
||||
} |
||||
|
||||
/** |
||||
* Get payload. |
||||
* |
||||
* @return the byte [ ] |
||||
*/ |
||||
public byte[] getPayload() { |
||||
return payload; |
||||
} |
||||
|
||||
/** |
||||
* Gets privacy flag. |
||||
* |
||||
* @return the privacy flag |
||||
*/ |
||||
public int getPrivacyFlag() { |
||||
return privacyFlag; |
||||
} |
||||
|
||||
/** |
||||
* Get affected contract transactions. |
||||
* |
||||
* @return the string [ ] |
||||
*/ |
||||
public String[] getAffectedContractTransactions() { |
||||
return affectedContractTransactions; |
||||
} |
||||
|
||||
/** |
||||
* Gets exec hash. |
||||
* |
||||
* @return the exec hash |
||||
*/ |
||||
public String getExecHash() { |
||||
return execHash; |
||||
} |
||||
} |
@ -1,71 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.enclave.types; |
||||
|
||||
import java.util.List; |
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder; |
||||
|
||||
/** The GoQuorum send request. */ |
||||
@JsonPropertyOrder({"payload", "from", "to"}) |
||||
public class GoQuorumSendRequest { |
||||
private final byte[] payload; |
||||
private final String from; |
||||
private final List<String> to; |
||||
|
||||
/** |
||||
* Instantiates a new Go quorum send request. |
||||
* |
||||
* @param payload the payload |
||||
* @param from the from |
||||
* @param to the to |
||||
*/ |
||||
public GoQuorumSendRequest( |
||||
@JsonProperty(value = "payload") final byte[] payload, |
||||
@JsonProperty(value = "from") final String from, |
||||
@JsonProperty(value = "to") final List<String> to) { |
||||
this.payload = payload; |
||||
this.from = from; |
||||
this.to = to; |
||||
} |
||||
|
||||
/** |
||||
* Get payload byte [ ]. |
||||
* |
||||
* @return the byte [ ] |
||||
*/ |
||||
public byte[] getPayload() { |
||||
return payload; |
||||
} |
||||
|
||||
/** |
||||
* Gets from. |
||||
* |
||||
* @return the from |
||||
*/ |
||||
public String getFrom() { |
||||
return from; |
||||
} |
||||
|
||||
/** |
||||
* Gets to. |
||||
* |
||||
* @return the to |
||||
*/ |
||||
public List<String> getTo() { |
||||
return to; |
||||
} |
||||
} |
@ -1,58 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.enclave.types; |
||||
|
||||
import java.util.List; |
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder; |
||||
|
||||
/** The GoQuorum send signed request. */ |
||||
@JsonPropertyOrder({"hash", "to"}) |
||||
public class GoQuorumSendSignedRequest { |
||||
private final byte[] hash; |
||||
private final List<String> to; |
||||
|
||||
/** |
||||
* Instantiates a new GoQuorum send signed request. |
||||
* |
||||
* @param hash the hash |
||||
* @param privateFor the private for |
||||
*/ |
||||
public GoQuorumSendSignedRequest( |
||||
@JsonProperty(value = "hash") final byte[] hash, |
||||
@JsonProperty(value = "to") final List<String> privateFor) { |
||||
this.hash = hash; |
||||
this.to = privateFor; |
||||
} |
||||
|
||||
/** |
||||
* Get hash byte [ ]. |
||||
* |
||||
* @return the byte [ ] |
||||
*/ |
||||
public byte[] getHash() { |
||||
return hash; |
||||
} |
||||
|
||||
/** |
||||
* Gets to. |
||||
* |
||||
* @return the to |
||||
*/ |
||||
public List<String> getTo() { |
||||
return to; |
||||
} |
||||
} |
@ -1,40 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.enclave.types; |
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
||||
/** The type Go quorum store raw request. */ |
||||
public class GoQuorumStoreRawRequest { |
||||
private final String payload; |
||||
|
||||
/** |
||||
* Instantiates a new Go quorum store raw request. |
||||
* |
||||
* @param payload the payload |
||||
*/ |
||||
public GoQuorumStoreRawRequest(@JsonProperty(value = "payload") final String payload) { |
||||
this.payload = payload; |
||||
} |
||||
|
||||
/** |
||||
* Gets payload. |
||||
* |
||||
* @return the payload |
||||
*/ |
||||
public String getPayload() { |
||||
return payload; |
||||
} |
||||
} |
@ -1,79 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv; |
||||
|
||||
import org.hyperledger.besu.enclave.EnclaveClientException; |
||||
import org.hyperledger.besu.enclave.GoQuorumEnclave; |
||||
import org.hyperledger.besu.enclave.types.GoQuorumReceiveResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
public class GoQuorumEthGetQuorumPayload implements JsonRpcMethod { |
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GoQuorumEthGetQuorumPayload.class); |
||||
|
||||
private final GoQuorumEnclave enclave; |
||||
|
||||
public GoQuorumEthGetQuorumPayload(final GoQuorumEnclave enclave) { |
||||
this.enclave = enclave; |
||||
} |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return RpcMethod.GOQUORUM_ETH_GET_QUORUM_PAYLOAD.getMethodName(); |
||||
} |
||||
|
||||
@Override |
||||
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { |
||||
final String key = requestContext.getRequiredParameter(0, String.class); |
||||
final Bytes bytes; |
||||
try { |
||||
bytes = Bytes.fromHexString(key); |
||||
} catch (final IllegalArgumentException e) { |
||||
LOG.debug("Enclave key contains invalid hex character {}", key); |
||||
return new JsonRpcErrorResponse( |
||||
requestContext.getRequest().getId(), JsonRpcError.INVALID_PARAMS); |
||||
} |
||||
if (bytes.size() != 64) { |
||||
LOG.debug("Enclave key expected length 64, but length is {}", bytes.size()); |
||||
return new JsonRpcErrorResponse( |
||||
requestContext.getRequest().getId(), JsonRpcError.INVALID_PARAMS); |
||||
} |
||||
|
||||
try { |
||||
final GoQuorumReceiveResponse receive = enclave.receive(bytes.toBase64String()); |
||||
return new JsonRpcSuccessResponse( |
||||
requestContext.getRequest().getId(), Bytes.wrap(receive.getPayload()).toHexString()); |
||||
} catch (final EnclaveClientException ex) { |
||||
if (ex.getStatusCode() == 404) { |
||||
return new JsonRpcSuccessResponse( |
||||
requestContext.getRequest().getId(), Bytes.EMPTY.toHexString()); |
||||
} else { |
||||
LOG.debug("Error retrieving enclave payload: ", ex); |
||||
return new JsonRpcErrorResponse( |
||||
requestContext.getRequest().getId(), JsonRpcError.INTERNAL_ERROR); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,161 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv; |
||||
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcEnclaveErrorConverter.convertEnclaveInvalidReason; |
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcErrorConverter.convertTransactionInvalidReason; |
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.DECODE_ERROR; |
||||
|
||||
import org.hyperledger.besu.config.GoQuorumOptions; |
||||
import org.hyperledger.besu.enclave.GoQuorumEnclave; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.PrivacyIdProvider; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.eea.JsonRpcErrorResponseException; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; |
||||
import org.hyperledger.besu.ethereum.core.Transaction; |
||||
import org.hyperledger.besu.ethereum.core.encoding.TransactionDecoder; |
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; |
||||
import org.hyperledger.besu.ethereum.privacy.GoQuorumSendRawTxArgs; |
||||
import org.hyperledger.besu.ethereum.rlp.RLP; |
||||
import org.hyperledger.besu.ethereum.rlp.RLPException; |
||||
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; |
||||
|
||||
import java.math.BigInteger; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
public class GoQuorumSendRawPrivateTransaction implements JsonRpcMethod { |
||||
|
||||
private static final Logger LOG = |
||||
LoggerFactory.getLogger(GoQuorumSendRawPrivateTransaction.class); |
||||
final TransactionPool transactionPool; |
||||
private final PrivacyIdProvider privacyIdProvider; |
||||
private final GoQuorumEnclave enclave; |
||||
private final boolean goQuorumCompatibilityMode; |
||||
|
||||
public GoQuorumSendRawPrivateTransaction( |
||||
final GoQuorumEnclave enclave, |
||||
final TransactionPool transactionPool, |
||||
final PrivacyIdProvider privacyIdProvider) { |
||||
this( |
||||
enclave, |
||||
transactionPool, |
||||
privacyIdProvider, |
||||
GoQuorumOptions.getGoQuorumCompatibilityMode()); |
||||
} |
||||
|
||||
public GoQuorumSendRawPrivateTransaction( |
||||
final GoQuorumEnclave enclave, |
||||
final TransactionPool transactionPool, |
||||
final PrivacyIdProvider privacyIdProvider, |
||||
final boolean goQuorumCompatibilityMode) { |
||||
this.enclave = enclave; |
||||
this.transactionPool = transactionPool; |
||||
this.privacyIdProvider = privacyIdProvider; |
||||
this.goQuorumCompatibilityMode = goQuorumCompatibilityMode; |
||||
} |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return RpcMethod.ETH_SEND_RAW_PRIVATE_TRANSACTION.getMethodName(); |
||||
} |
||||
|
||||
@Override |
||||
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { |
||||
final Object id = requestContext.getRequest().getId(); |
||||
final String rawPrivateTransaction = requestContext.getRequiredParameter(0, String.class); |
||||
|
||||
final GoQuorumSendRawTxArgs rawTxArgs = |
||||
requestContext.getRequiredParameter(1, GoQuorumSendRawTxArgs.class); |
||||
|
||||
try { |
||||
final Transaction transaction = |
||||
TransactionDecoder.decodeForWire( |
||||
RLP.input(Bytes.fromHexString(rawPrivateTransaction)), goQuorumCompatibilityMode); |
||||
|
||||
checkAndHandlePrivateTransaction(transaction, rawTxArgs, requestContext); |
||||
|
||||
return transactionPool |
||||
.addLocalTransaction(transaction) |
||||
.either( |
||||
() -> new JsonRpcSuccessResponse(id, transaction.getHash().toString()), |
||||
errorReason -> getJsonRpcErrorResponse(id, errorReason)); |
||||
|
||||
} catch (final JsonRpcErrorResponseException e) { |
||||
return new JsonRpcErrorResponse(id, e.getJsonRpcError()); |
||||
} catch (final IllegalArgumentException | RLPException e) { |
||||
LOG.error("Unable to decode private transaction for send", e); |
||||
return new JsonRpcErrorResponse(id, DECODE_ERROR); |
||||
} catch (final Exception e) { |
||||
LOG.error("Unexpected error", e); |
||||
return new JsonRpcErrorResponse(id, convertEnclaveInvalidReason(e.getMessage())); |
||||
} |
||||
} |
||||
|
||||
private void checkAndHandlePrivateTransaction( |
||||
final Transaction transaction, |
||||
final GoQuorumSendRawTxArgs rawTxArgs, |
||||
final JsonRpcRequestContext requestContext) { |
||||
// rawTxArgs cannot be null as the call to getRequiredParameter would have failed if it was not
|
||||
// available
|
||||
|
||||
if (rawTxArgs.getPrivateFor() == null) { |
||||
LOG.error(JsonRpcError.GOQUORUM_NO_PRIVATE_FOR.getMessage()); |
||||
throw new JsonRpcErrorResponseException(JsonRpcError.GOQUORUM_NO_PRIVATE_FOR); |
||||
} |
||||
|
||||
if (rawTxArgs.getPrivacyFlag() != 0) { |
||||
LOG.error(JsonRpcError.GOQUORUM_ONLY_STANDARD_MODE_SUPPORTED.getMessage()); |
||||
throw new JsonRpcErrorResponseException(JsonRpcError.GOQUORUM_ONLY_STANDARD_MODE_SUPPORTED); |
||||
} |
||||
|
||||
if (rawTxArgs.getPrivateFrom() != null) { |
||||
final String privateFrom = rawTxArgs.getPrivateFrom(); |
||||
final String privacyUserId = privacyIdProvider.getPrivacyUserId(requestContext.getUser()); |
||||
if (!privateFrom.equals(privacyUserId)) { |
||||
LOG.error(JsonRpcError.PRIVATE_FROM_DOES_NOT_MATCH_ENCLAVE_PUBLIC_KEY.getMessage()); |
||||
throw new JsonRpcErrorResponseException( |
||||
JsonRpcError.PRIVATE_FROM_DOES_NOT_MATCH_ENCLAVE_PUBLIC_KEY); |
||||
} |
||||
} |
||||
|
||||
if (!transaction.getV().equals(BigInteger.valueOf(37)) |
||||
&& !transaction.getV().equals(BigInteger.valueOf(38))) { |
||||
LOG.error(JsonRpcError.GOQUORUM_V_VALUE.getMessage()); |
||||
throw new JsonRpcErrorResponseException(JsonRpcError.GOQUORUM_V_VALUE); |
||||
} |
||||
|
||||
final Bytes txId = transaction.getPayload(); |
||||
if (txId == null || txId.isEmpty()) { |
||||
throw new JsonRpcErrorResponseException(JsonRpcError.GOQUORUM_LOOKUP_ID_NOT_AVAILABLE); |
||||
} |
||||
enclave.sendSignedTransaction(txId.toArray(), rawTxArgs.getPrivateFor()); |
||||
} |
||||
|
||||
JsonRpcErrorResponse getJsonRpcErrorResponse( |
||||
final Object id, final TransactionInvalidReason errorReason) { |
||||
if (errorReason.equals(TransactionInvalidReason.INTRINSIC_GAS_EXCEEDS_GAS_LIMIT)) { |
||||
return new JsonRpcErrorResponse(id, JsonRpcError.PMT_FAILED_INTRINSIC_GAS_EXCEEDS_LIMIT); |
||||
} |
||||
return new JsonRpcErrorResponse(id, convertTransactionInvalidReason(errorReason)); |
||||
} |
||||
} |
@ -1,75 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv; |
||||
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcEnclaveErrorConverter.convertEnclaveInvalidReason; |
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.DECODE_ERROR; |
||||
|
||||
import org.hyperledger.besu.enclave.GoQuorumEnclave; |
||||
import org.hyperledger.besu.enclave.types.StoreRawResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; |
||||
import org.hyperledger.besu.ethereum.rlp.RLPException; |
||||
|
||||
import java.util.Base64; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
public class GoQuorumStoreRawPrivateTransaction implements JsonRpcMethod { |
||||
|
||||
private static final Logger LOG = |
||||
LoggerFactory.getLogger(GoQuorumStoreRawPrivateTransaction.class); |
||||
private final GoQuorumEnclave enclave; |
||||
|
||||
public GoQuorumStoreRawPrivateTransaction(final GoQuorumEnclave enclave) { |
||||
this.enclave = enclave; |
||||
} |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return RpcMethod.GOQUORUM_STORE_RAW.getMethodName(); |
||||
} |
||||
|
||||
@Override |
||||
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { |
||||
final Object id = requestContext.getRequest().getId(); |
||||
final String payload = requestContext.getRequiredParameter(0, String.class); |
||||
|
||||
try { |
||||
LOG.debug("sending payload to GoQuorum enclave" + payload); |
||||
final StoreRawResponse storeRawResponse = |
||||
enclave.storeRaw( |
||||
Base64.getEncoder().encodeToString(Bytes.fromHexString(payload).toArray())); |
||||
final String enclaveLookupId = storeRawResponse.getKey(); |
||||
LOG.debug("retrieved lookupId from GoQuorum enclave " + enclaveLookupId); |
||||
return new JsonRpcSuccessResponse(id, hexEncodeEnclaveKey(enclaveLookupId)); |
||||
} catch (final IllegalArgumentException | RLPException e) { |
||||
LOG.error("Unable to decode private transaction for store", e); |
||||
return new JsonRpcErrorResponse(id, DECODE_ERROR); |
||||
} catch (final Exception e) { |
||||
return new JsonRpcErrorResponse(id, convertEnclaveInvalidReason(e.getMessage())); |
||||
} |
||||
} |
||||
|
||||
private String hexEncodeEnclaveKey(final String enclaveKey) { |
||||
return Bytes.wrap(Base64.getDecoder().decode(enclaveKey)).toHexString(); |
||||
} |
||||
} |
@ -1,71 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.methods; |
||||
|
||||
import org.hyperledger.besu.enclave.GoQuorumEnclave; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.PrivacyIdProvider; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.GoQuorumEthGetQuorumPayload; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.GoQuorumSendRawPrivateTransaction; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.GoQuorumStoreRawPrivateTransaction; |
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; |
||||
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters; |
||||
import org.hyperledger.besu.ethereum.core.PrivacyParameters; |
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; |
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; |
||||
import org.hyperledger.besu.ethereum.privacy.PrivacyController; |
||||
import org.hyperledger.besu.plugin.services.privacy.PrivateMarkerTransactionFactory; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Map; |
||||
import java.util.Optional; |
||||
|
||||
public class GoQuorumJsonRpcPrivacyMethods extends PrivacyApiGroupJsonRpcMethods { |
||||
|
||||
private final Optional<GoQuorumPrivacyParameters> goQuorumParameters; |
||||
|
||||
public GoQuorumJsonRpcPrivacyMethods( |
||||
final BlockchainQueries blockchainQueries, |
||||
final ProtocolSchedule protocolSchedule, |
||||
final TransactionPool transactionPool, |
||||
final PrivacyParameters privacyParameters) { |
||||
super(blockchainQueries, protocolSchedule, transactionPool, privacyParameters); |
||||
this.goQuorumParameters = privacyParameters.getGoQuorumPrivacyParameters(); |
||||
} |
||||
|
||||
@Override |
||||
protected Map<String, JsonRpcMethod> create( |
||||
final PrivacyController privacyController, |
||||
final PrivacyIdProvider enclavePublicKeyProvider, |
||||
final PrivateMarkerTransactionFactory privateMarkerTransactionFactory) { |
||||
|
||||
if (goQuorumParameters.isPresent()) { |
||||
final GoQuorumEnclave enclave = goQuorumParameters.get().enclave(); |
||||
return mapOf( |
||||
new GoQuorumSendRawPrivateTransaction( |
||||
enclave, getTransactionPool(), enclavePublicKeyProvider), |
||||
new GoQuorumStoreRawPrivateTransaction(enclave), |
||||
new GoQuorumEthGetQuorumPayload(enclave)); |
||||
} else { |
||||
return Collections.emptyMap(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected String getApiGroup() { |
||||
return RpcApis.GOQUORUM.name(); |
||||
} |
||||
} |
@ -1,150 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.goquorum; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import org.hyperledger.besu.enclave.EnclaveClientException; |
||||
import org.hyperledger.besu.enclave.GoQuorumEnclave; |
||||
import org.hyperledger.besu.enclave.types.GoQuorumReceiveResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.GoQuorumEthGetQuorumPayload; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.Mock; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
|
||||
@RunWith(MockitoJUnitRunner.class) |
||||
@SuppressWarnings("DirectInvocationOnMock") |
||||
public class GoQuorumEthGetQuorumPayloadTest { |
||||
|
||||
@Mock GoQuorumEthGetQuorumPayload method; |
||||
@Mock GoQuorumEnclave enclave; |
||||
|
||||
@Before |
||||
public void before() { |
||||
method = new GoQuorumEthGetQuorumPayload(enclave); |
||||
} |
||||
|
||||
@Test |
||||
public void correctRequest() { |
||||
final String hexString = Bytes.wrap(new byte[64]).toHexString(); |
||||
final JsonRpcRequestContext request = |
||||
new JsonRpcRequestContext( |
||||
new JsonRpcRequest("2.0", "eth_getQuorumPayload", new String[] {hexString})); |
||||
|
||||
when(enclave.receive(any())) |
||||
.thenReturn(new GoQuorumReceiveResponse(new byte[10], 0, null, null)); |
||||
|
||||
final JsonRpcResponse response = method.response(request); |
||||
assertThat(response).isInstanceOf(JsonRpcSuccessResponse.class); |
||||
assertThat(((JsonRpcSuccessResponse) response).getResult().toString()) |
||||
.isEqualTo("0x00000000000000000000"); |
||||
} |
||||
|
||||
@Test |
||||
public void requestIsMissingParameter() { |
||||
final JsonRpcRequestContext request = |
||||
new JsonRpcRequestContext( |
||||
new JsonRpcRequest("2.0", "eth_getQuorumPayload", new String[] {})); |
||||
|
||||
assertThatThrownBy(() -> method.response(request)) |
||||
.isInstanceOf(InvalidJsonRpcParameters.class) |
||||
.hasMessage("Missing required json rpc parameter at index 0"); |
||||
} |
||||
|
||||
@Test |
||||
public void requestHasNullObjectParameter() { |
||||
final JsonRpcRequestContext request = |
||||
new JsonRpcRequestContext(new JsonRpcRequest("2.0", "eth_getQuorumPayload", null)); |
||||
|
||||
assertThatThrownBy(() -> method.response(request)) |
||||
.isInstanceOf(InvalidJsonRpcParameters.class) |
||||
.hasMessage("Missing required json rpc parameter at index 0"); |
||||
} |
||||
|
||||
@Test |
||||
public void requestHasNullArrayParameter() { |
||||
final JsonRpcRequestContext request = |
||||
new JsonRpcRequestContext( |
||||
new JsonRpcRequest("2.0", "eth_getQuorumPayload", new String[] {null})); |
||||
|
||||
assertThatThrownBy(() -> method.response(request)) |
||||
.isInstanceOf(InvalidJsonRpcParameters.class) |
||||
.hasMessage("Missing required json rpc parameter at index 0"); |
||||
} |
||||
|
||||
@Test |
||||
public void requestHasShortHex() { |
||||
final String hexString = Bytes.wrap(new byte[63]).toHexString(); |
||||
final JsonRpcRequestContext request = |
||||
new JsonRpcRequestContext( |
||||
new JsonRpcRequest("2.0", "eth_getQuorumPayload", new String[] {hexString})); |
||||
|
||||
final JsonRpcResponse response = method.response(request); |
||||
assertThat(response).isInstanceOf(JsonRpcErrorResponse.class); |
||||
assertThat(response.toString()).contains("INVALID_PARAMS"); |
||||
} |
||||
|
||||
@Test |
||||
public void requestHasLongHex() { |
||||
final String hexString = Bytes.wrap(new byte[65]).toHexString(); |
||||
final JsonRpcRequestContext request = |
||||
new JsonRpcRequestContext( |
||||
new JsonRpcRequest("2.0", "eth_getQuorumPayload", new String[] {hexString})); |
||||
|
||||
final JsonRpcResponse response = method.response(request); |
||||
assertThat(response).isInstanceOf(JsonRpcErrorResponse.class); |
||||
assertThat(response.toString()).contains("INVALID_PARAMS"); |
||||
} |
||||
|
||||
@Test |
||||
public void requestNonHexString() { |
||||
final String hexString = Bytes.wrap(new byte[63]).toHexString() + "f"; |
||||
final JsonRpcRequestContext request = |
||||
new JsonRpcRequestContext( |
||||
new JsonRpcRequest("2.0", "eth_getQuorumPayload", new String[] {hexString})); |
||||
|
||||
final JsonRpcResponse response = method.response(request); |
||||
assertThat(response).isInstanceOf(JsonRpcErrorResponse.class); |
||||
assertThat(response.toString()).contains("INVALID_PARAMS"); |
||||
} |
||||
|
||||
@Test |
||||
public void enclave404ReturnsEmptyBytesString() { |
||||
final String hexString = Bytes.wrap(new byte[64]).toHexString(); |
||||
final JsonRpcRequestContext request = |
||||
new JsonRpcRequestContext( |
||||
new JsonRpcRequest("2.0", "eth_getQuorumPayload", new String[] {hexString})); |
||||
|
||||
when(enclave.receive(any())).thenThrow(new EnclaveClientException(404, null)); |
||||
|
||||
final JsonRpcResponse response = method.response(request); |
||||
assertThat(response).isInstanceOf(JsonRpcSuccessResponse.class); |
||||
assertThat(((JsonRpcSuccessResponse) response).getResult()) |
||||
.isEqualTo(Bytes.EMPTY.toHexString()); |
||||
} |
||||
} |
@ -1,298 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.goquorum; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.Mockito.verify; |
||||
import static org.mockito.Mockito.verifyNoInteractions; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import org.hyperledger.besu.crypto.KeyPair; |
||||
import org.hyperledger.besu.crypto.SignatureAlgorithm; |
||||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; |
||||
import org.hyperledger.besu.datatypes.Address; |
||||
import org.hyperledger.besu.datatypes.Wei; |
||||
import org.hyperledger.besu.enclave.GoQuorumEnclave; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.PrivacyIdProvider; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.GoQuorumSendRawPrivateTransaction; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; |
||||
import org.hyperledger.besu.ethereum.core.Transaction; |
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; |
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult; |
||||
import org.hyperledger.besu.ethereum.privacy.GoQuorumSendRawTxArgs; |
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; |
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; |
||||
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; |
||||
import org.hyperledger.besu.plugin.data.Restriction; |
||||
|
||||
import java.math.BigInteger; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
|
||||
import com.google.common.base.Supplier; |
||||
import com.google.common.base.Suppliers; |
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.Mock; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
|
||||
@RunWith(MockitoJUnitRunner.class) |
||||
@SuppressWarnings("DirectInvocationOnMock") |
||||
public class GoQuorumSendRawPrivateTransactionTest { |
||||
|
||||
static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM = |
||||
Suppliers.memoize(SignatureAlgorithmFactory::getInstance); |
||||
static final String INVALID_TRANSACTION_RLP = invalidGQPrivateTransactionRlp(); |
||||
|
||||
static final String ENCLAVE_PUBLIC_KEY = "S28yYlZxRCtuTmxOWUw1RUU3eTNJZE9udmlmdGppaXo="; |
||||
final PrivacyIdProvider privacyIdProvider = (user) -> ENCLAVE_PUBLIC_KEY; |
||||
|
||||
@Mock GoQuorumSendRawPrivateTransaction method; |
||||
@Mock TransactionPool transactionPool; |
||||
@Mock GoQuorumEnclave enclave; |
||||
private static final String RAW_TRANSACTION_STRING = "someString"; |
||||
private static final String RAW_TRANSACTION = createValidTransactionRLP(); |
||||
private static final GoQuorumSendRawTxArgs GO_QUORUM_SEND_RAW_TX_ARGS = |
||||
new GoQuorumSendRawTxArgs( |
||||
ENCLAVE_PUBLIC_KEY, Collections.singletonList(ENCLAVE_PUBLIC_KEY), 0); |
||||
|
||||
@Before |
||||
public void before() { |
||||
boolean goQuorumCompatibilityMode = true; |
||||
method = |
||||
new GoQuorumSendRawPrivateTransaction( |
||||
enclave, transactionPool, privacyIdProvider, goQuorumCompatibilityMode); |
||||
} |
||||
|
||||
@Test |
||||
public void requestIsMissingParameter() { |
||||
final JsonRpcRequestContext request = |
||||
new JsonRpcRequestContext( |
||||
new JsonRpcRequest("2.0", "eth_sendRawPrivateTransaction", new String[] {})); |
||||
|
||||
assertThatThrownBy(() -> method.response(request)) |
||||
.isInstanceOf(InvalidJsonRpcParameters.class) |
||||
.hasMessage("Missing required json rpc parameter at index 0"); |
||||
|
||||
final JsonRpcRequestContext request2 = |
||||
new JsonRpcRequestContext( |
||||
new JsonRpcRequest( |
||||
"2.0", "eth_sendRawPrivateTransaction", new String[] {RAW_TRANSACTION_STRING})); |
||||
|
||||
assertThatThrownBy(() -> method.response(request2)) |
||||
.isInstanceOf(InvalidJsonRpcParameters.class) |
||||
.hasMessage("Missing required json rpc parameter at index 1"); |
||||
} |
||||
|
||||
@Test |
||||
public void requestHasNullObjectParameter() { |
||||
final JsonRpcRequestContext request = |
||||
new JsonRpcRequestContext(new JsonRpcRequest("2.0", "eth_sendRawPrivateTransaction", null)); |
||||
|
||||
assertThatThrownBy(() -> method.response(request)) |
||||
.isInstanceOf(InvalidJsonRpcParameters.class) |
||||
.hasMessage("Missing required json rpc parameter at index 0"); |
||||
} |
||||
|
||||
@Test |
||||
public void requestHasNullArrayParameter() { |
||||
final JsonRpcRequestContext request = |
||||
new JsonRpcRequestContext( |
||||
new JsonRpcRequest("2.0", "\"eth_sendRawPrivateTransaction\"", new String[] {null})); |
||||
|
||||
assertThatThrownBy(() -> method.response(request)) |
||||
.isInstanceOf(InvalidJsonRpcParameters.class) |
||||
.hasMessage("Missing required json rpc parameter at index 0"); |
||||
} |
||||
|
||||
@Test |
||||
public void validTransactionIsSentToTransactionPool() { |
||||
when(enclave.sendSignedTransaction(any(), any())).thenReturn(null); |
||||
|
||||
when(transactionPool.addLocalTransaction(any(Transaction.class))) |
||||
.thenReturn(ValidationResult.valid()); |
||||
final JsonRpcRequestContext request = |
||||
new JsonRpcRequestContext( |
||||
new JsonRpcRequest( |
||||
"2.0", |
||||
"\"eth_sendRawPrivateTransaction\"", |
||||
new Object[] {RAW_TRANSACTION, GO_QUORUM_SEND_RAW_TX_ARGS})); |
||||
|
||||
method.response(request); |
||||
|
||||
verify(transactionPool).addLocalTransaction(any(Transaction.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void invalidTransactionIsNotAddedToTransactionPool() { |
||||
final JsonRpcRequestContext request = |
||||
new JsonRpcRequestContext( |
||||
new JsonRpcRequest( |
||||
"2.0", |
||||
"\"eth_sendRawPrivateTransaction\"", |
||||
new Object[] {INVALID_TRANSACTION_RLP, GO_QUORUM_SEND_RAW_TX_ARGS})); |
||||
|
||||
final JsonRpcResponse expectedResponse = |
||||
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.DECODE_ERROR); |
||||
|
||||
final JsonRpcResponse actualResponse = method.response(request); |
||||
|
||||
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); |
||||
verifyNoInteractions(transactionPool); |
||||
} |
||||
|
||||
@Test |
||||
public void transactionWithNonceBelowAccountNonceIsRejected() { |
||||
verifyErrorForInvalidTransaction( |
||||
TransactionInvalidReason.NONCE_TOO_LOW, JsonRpcError.NONCE_TOO_LOW); |
||||
} |
||||
|
||||
@Test |
||||
public void transactionWithNonceAboveAccountNonceIsRejected() { |
||||
verifyErrorForInvalidTransaction( |
||||
TransactionInvalidReason.NONCE_TOO_HIGH, JsonRpcError.NONCE_TOO_HIGH); |
||||
} |
||||
|
||||
@Test |
||||
public void transactionWithInvalidSignatureIsRejected() { |
||||
verifyErrorForInvalidTransaction( |
||||
TransactionInvalidReason.INVALID_SIGNATURE, JsonRpcError.INVALID_TRANSACTION_SIGNATURE); |
||||
} |
||||
|
||||
@Test |
||||
public void transactionWithIntrinsicGasExceedingGasLimitIsRejected() { |
||||
verifyErrorForInvalidTransaction( |
||||
TransactionInvalidReason.INTRINSIC_GAS_EXCEEDS_GAS_LIMIT, |
||||
JsonRpcError.PMT_FAILED_INTRINSIC_GAS_EXCEEDS_LIMIT); |
||||
} |
||||
|
||||
@Test |
||||
public void transactionWithUpfrontGasExceedingAccountBalanceIsRejected() { |
||||
verifyErrorForInvalidTransaction( |
||||
TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE, |
||||
JsonRpcError.TRANSACTION_UPFRONT_COST_EXCEEDS_BALANCE); |
||||
} |
||||
|
||||
@Test |
||||
public void transactionWithGasLimitExceedingBlockGasLimitIsRejected() { |
||||
verifyErrorForInvalidTransaction( |
||||
TransactionInvalidReason.EXCEEDS_BLOCK_GAS_LIMIT, JsonRpcError.EXCEEDS_BLOCK_GAS_LIMIT); |
||||
} |
||||
|
||||
@Test |
||||
public void transactionWithNotWhitelistedSenderAccountIsRejected() { |
||||
verifyErrorForInvalidTransaction( |
||||
TransactionInvalidReason.TX_SENDER_NOT_AUTHORIZED, JsonRpcError.TX_SENDER_NOT_AUTHORIZED); |
||||
} |
||||
|
||||
private void verifyErrorForInvalidTransaction( |
||||
final TransactionInvalidReason transactionInvalidReason, final JsonRpcError expectedError) { |
||||
|
||||
when(transactionPool.addLocalTransaction(any(Transaction.class))) |
||||
.thenReturn(ValidationResult.invalid(transactionInvalidReason)); |
||||
final JsonRpcRequestContext request = |
||||
new JsonRpcRequestContext( |
||||
new JsonRpcRequest( |
||||
"2.0", |
||||
"\"eth_sendRawPrivateTransaction\"", |
||||
new Object[] {RAW_TRANSACTION, GO_QUORUM_SEND_RAW_TX_ARGS})); |
||||
|
||||
final JsonRpcResponse expectedResponse = |
||||
new JsonRpcErrorResponse(request.getRequest().getId(), expectedError); |
||||
|
||||
final JsonRpcResponse actualResponse = method.response(request); |
||||
|
||||
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); |
||||
|
||||
verify(transactionPool).addLocalTransaction(any(Transaction.class)); |
||||
} |
||||
|
||||
private static String invalidGQPrivateTransactionRlp() { |
||||
final PrivateTransaction.Builder privateTransactionBuilder = |
||||
PrivateTransaction.builder() |
||||
.nonce(0) |
||||
.gasPrice(Wei.of(1)) |
||||
.gasLimit(21000) |
||||
.value(Wei.ZERO) |
||||
.payload(Bytes.EMPTY) |
||||
.to(Address.fromHexString("0x095e7baea6a6c7c4c2dfeb977efac326af552d87")) |
||||
.chainId(BigInteger.ONE) |
||||
.privateFrom(Bytes.fromBase64String("S28yYlZxRCtuTmxOWUw1RUU3eTNJZE9udmlmdGppaXp=")) |
||||
.privateFor( |
||||
List.of( |
||||
Bytes.fromBase64String("S28yYlZxRCtuTmxOWUw1RUU3eTNJZE9udmlmdGppaXp="), |
||||
Bytes.fromBase64String("QTFhVnRNeExDVUhtQlZIWG9aenpCZ1BiVy93ajVheER="))) |
||||
.restriction(Restriction.RESTRICTED); |
||||
return rlpEncodeTransaction(privateTransactionBuilder); |
||||
} |
||||
|
||||
private static String rlpEncodeTransaction( |
||||
final PrivateTransaction.Builder privateTransactionBuilder) { |
||||
final KeyPair keyPair = |
||||
SIGNATURE_ALGORITHM |
||||
.get() |
||||
.createKeyPair( |
||||
SIGNATURE_ALGORITHM |
||||
.get() |
||||
.createPrivateKey( |
||||
new BigInteger( |
||||
"8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", |
||||
16))); |
||||
|
||||
final PrivateTransaction privateTransaction = privateTransactionBuilder.signAndBuild(keyPair); |
||||
final BytesValueRLPOutput bvrlp = new BytesValueRLPOutput(); |
||||
privateTransaction.writeTo(bvrlp); |
||||
return bvrlp.encoded().toHexString(); |
||||
} |
||||
|
||||
private static final String createValidTransactionRLP() { |
||||
final BytesValueRLPOutput bvrlp = new BytesValueRLPOutput(); |
||||
final Transaction publicTransaction = |
||||
new Transaction( |
||||
0L, |
||||
Wei.of(1), |
||||
21000L, |
||||
Optional.of( |
||||
Address.wrap(Bytes.fromHexString("0x095e7baea6a6c7c4c2dfeb977efac326af552d87"))), |
||||
Wei.ZERO, |
||||
SIGNATURE_ALGORITHM |
||||
.get() |
||||
.createSignature( |
||||
new BigInteger( |
||||
"32886959230931919120748662916110619501838190146643992583529828535682419954515"), |
||||
new BigInteger( |
||||
"14473701025599600909210599917245952381483216609124029382871721729679842002948"), |
||||
Byte.parseByte("0")), |
||||
Bytes.fromHexString("0x01"), // this is the enclave key for the private payload
|
||||
Address.wrap( |
||||
Bytes.fromHexString( |
||||
"0x8411b12666f68ef74cace3615c9d5a377729d03f")), // sender public address
|
||||
Optional.empty(), |
||||
Optional.of(BigInteger.valueOf(37)), |
||||
Optional.empty()); |
||||
publicTransaction.writeTo(bvrlp); |
||||
return bvrlp.encoded().toHexString(); |
||||
} |
||||
} |
@ -1,132 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
* |
||||
*/ |
||||
|
||||
package org.hyperledger.besu.ethereum.api.query; |
||||
|
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.ArgumentMatchers.anyLong; |
||||
import static org.mockito.Mockito.times; |
||||
import static org.mockito.Mockito.verify; |
||||
import static org.mockito.Mockito.verifyNoMoreInteractions; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import org.hyperledger.besu.datatypes.Address; |
||||
import org.hyperledger.besu.datatypes.Hash; |
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain; |
||||
import org.hyperledger.besu.ethereum.core.BlockBody; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||
import org.hyperledger.besu.ethereum.core.Difficulty; |
||||
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; |
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; |
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; |
||||
import org.hyperledger.besu.evm.log.Log; |
||||
import org.hyperledger.besu.evm.log.LogsBloomFilter; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.junit.Before; |
||||
import org.junit.BeforeClass; |
||||
import org.junit.ClassRule; |
||||
import org.junit.Test; |
||||
import org.junit.rules.TemporaryFolder; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.Mock; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
|
||||
@RunWith(MockitoJUnitRunner.class) |
||||
public class GoQuorumPrivateTxBloomBlockchainQueriesTest { |
||||
|
||||
@ClassRule public static TemporaryFolder cacheDir = new TemporaryFolder(); |
||||
|
||||
private static LogsQuery logsQuery; |
||||
private Hash testHash; |
||||
private static LogsBloomFilter testLogsBloomFilter; |
||||
|
||||
@Mock MutableBlockchain blockchain; |
||||
@Mock WorldStateArchive worldStateArchive; |
||||
@Mock EthScheduler scheduler; |
||||
private BlockchainQueries blockchainQueries; |
||||
|
||||
@BeforeClass |
||||
public static void setupClass() { |
||||
final Address testAddress = Address.fromHexString("0x123456"); |
||||
final Bytes testMessage = Bytes.fromHexString("0x9876"); |
||||
final Log testLog = new Log(testAddress, testMessage, List.of()); |
||||
testLogsBloomFilter = LogsBloomFilter.builder().insertLog(testLog).build(); |
||||
logsQuery = new LogsQuery(List.of(testAddress), List.of()); |
||||
} |
||||
|
||||
@Before |
||||
public void setup() { |
||||
final BlockHeader fakeHeader = |
||||
new BlockHeader( |
||||
Hash.EMPTY, |
||||
Hash.EMPTY, |
||||
Address.ZERO, |
||||
Hash.EMPTY, |
||||
Hash.EMPTY, |
||||
Hash.EMPTY, |
||||
LogsBloomFilter.empty(), |
||||
Difficulty.ZERO, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
Bytes.EMPTY, |
||||
null, |
||||
Hash.EMPTY, |
||||
0, |
||||
null, |
||||
null, |
||||
null, |
||||
new MainnetBlockHeaderFunctions(), |
||||
Optional.of(testLogsBloomFilter)); |
||||
testHash = fakeHeader.getHash(); |
||||
final BlockBody fakeBody = new BlockBody(Collections.emptyList(), Collections.emptyList()); |
||||
when(blockchain.getBlockHeader(any())).thenReturn(Optional.of(fakeHeader)); |
||||
when(blockchain.getBlockHeader(anyLong())).thenReturn(Optional.of(fakeHeader)); |
||||
when(blockchain.getTxReceipts(any())).thenReturn(Optional.of(Collections.emptyList())); |
||||
when(blockchain.getBlockBody(any())).thenReturn(Optional.of(fakeBody)); |
||||
blockchainQueries = |
||||
new BlockchainQueries( |
||||
blockchain, |
||||
worldStateArchive, |
||||
Optional.of(cacheDir.getRoot().toPath()), |
||||
Optional.of(scheduler)); |
||||
} |
||||
|
||||
/** |
||||
* Tests whether block headers containing private blooms match. The resulting list of |
||||
* LogWithMetadata would be empty, because the mocked blockchain does not return any receipts, but |
||||
* we do check that the methods on the blockchain are actually called that would be called if the |
||||
* block header matches the bloom filter. |
||||
*/ |
||||
@Test |
||||
public void testPrivateBloomsWork() { |
||||
blockchainQueries.matchingLogs(0, 2, logsQuery, () -> true); |
||||
|
||||
verify(blockchain, times(3)).getBlockHeader(anyLong()); |
||||
verify(blockchain, times(3)).getBlockHeader(testHash); |
||||
verify(blockchain, times(3)).getTxReceipts(testHash); |
||||
verify(blockchain, times(3)).getBlockBody(testHash); |
||||
verify(blockchain, times(3)).blockIsOnCanonicalChain(testHash); |
||||
|
||||
verifyNoMoreInteractions(blockchain); |
||||
} |
||||
} |
@ -1,178 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
* |
||||
*/ |
||||
|
||||
package org.hyperledger.besu.ethereum.api.query.cache; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.hyperledger.besu.ethereum.api.query.cache.TransactionLogBloomCacher.BLOOM_BITS_LENGTH; |
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.ArgumentMatchers.anyLong; |
||||
import static org.mockito.Mockito.times; |
||||
import static org.mockito.Mockito.verify; |
||||
import static org.mockito.Mockito.verifyNoMoreInteractions; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import org.hyperledger.besu.datatypes.Address; |
||||
import org.hyperledger.besu.datatypes.Hash; |
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; |
||||
import org.hyperledger.besu.ethereum.api.query.LogsQuery; |
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain; |
||||
import org.hyperledger.besu.ethereum.core.BlockBody; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||
import org.hyperledger.besu.ethereum.core.Difficulty; |
||||
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; |
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; |
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; |
||||
import org.hyperledger.besu.evm.log.Log; |
||||
import org.hyperledger.besu.evm.log.LogsBloomFilter; |
||||
|
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.io.RandomAccessFile; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.junit.Before; |
||||
import org.junit.BeforeClass; |
||||
import org.junit.Rule; |
||||
import org.junit.Test; |
||||
import org.junit.rules.TemporaryFolder; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.Mock; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
|
||||
@RunWith(MockitoJUnitRunner.class) |
||||
@SuppressWarnings({"unused", "DirectInvocationOnMock"}) |
||||
public class GoQuorumPrivateTransactionLogBloomCacherTest { |
||||
|
||||
private static final long NUMBER_3 = 3L; |
||||
private static LogsQuery logsQuery; |
||||
@Rule public TemporaryFolder cacheDir = new TemporaryFolder(); |
||||
|
||||
private Hash testBlockHeaderHash; |
||||
private static LogsBloomFilter testLogsBloomFilter; |
||||
|
||||
@Mock MutableBlockchain blockchain; |
||||
@Mock EthScheduler scheduler; |
||||
@Mock WorldStateArchive worldStateArchive; |
||||
|
||||
private TransactionLogBloomCacher transactionLogBloomCacher; |
||||
private BlockchainQueries blockchainQueries; |
||||
private static Address testAddress; |
||||
private static Bytes testMessage; |
||||
private BlockHeader fakeHeader; |
||||
|
||||
@BeforeClass |
||||
public static void setupClass() { |
||||
testAddress = Address.fromHexString("0x123456"); |
||||
testMessage = Bytes.fromHexString("0x9876"); |
||||
final Log testLog = new Log(testAddress, testMessage, List.of()); |
||||
testLogsBloomFilter = LogsBloomFilter.builder().insertLog(testLog).build(); |
||||
logsQuery = new LogsQuery(List.of(testAddress), List.of()); |
||||
} |
||||
|
||||
@SuppressWarnings({"unchecked", "ReturnValueIgnored"}) |
||||
@Before |
||||
public void setup() throws IOException { |
||||
fakeHeader = createBlock(NUMBER_3); |
||||
|
||||
testBlockHeaderHash = fakeHeader.getHash(); |
||||
|
||||
transactionLogBloomCacher = |
||||
new TransactionLogBloomCacher(blockchain, cacheDir.getRoot().toPath(), scheduler); |
||||
|
||||
blockchainQueries = |
||||
new BlockchainQueries( |
||||
blockchain, |
||||
worldStateArchive, |
||||
Optional.of(cacheDir.getRoot().toPath()), |
||||
Optional.of(scheduler)); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldUpdateCacheWhenBlockAdded() throws IOException { |
||||
|
||||
final BlockBody fakeBody = new BlockBody(Collections.emptyList(), Collections.emptyList()); |
||||
when(blockchain.getBlockHeader(testBlockHeaderHash)).thenReturn(Optional.of(fakeHeader)); |
||||
when(blockchain.getBlockHashByNumber(anyLong())).thenReturn(Optional.of(testBlockHeaderHash)); |
||||
when(blockchain.getTxReceipts(any())).thenReturn(Optional.of(Collections.emptyList())); |
||||
when(blockchain.getBlockBody(any())).thenReturn(Optional.of(fakeBody)); |
||||
|
||||
final File logBloom = cacheDir.newFile("logBloom-0.cache"); |
||||
|
||||
createLogBloomCache(logBloom); |
||||
|
||||
createBlock(3L); |
||||
|
||||
assertThat(logBloom.length()).isEqualTo(BLOOM_BITS_LENGTH * 3); |
||||
|
||||
when(blockchain.getBlockHeader(anyLong())).thenReturn(Optional.of(fakeHeader)); |
||||
transactionLogBloomCacher.cacheLogsBloomForBlockHeader( |
||||
blockchain.getBlockHeader(NUMBER_3).get(), Optional.empty(), Optional.of(logBloom)); |
||||
|
||||
assertThat(logBloom.length()).isEqualTo(BLOOM_BITS_LENGTH * 4); |
||||
assertThat(cacheDir.getRoot().list().length).isEqualTo(1); |
||||
|
||||
blockchainQueries.matchingLogs(NUMBER_3, 3, logsQuery, () -> true); |
||||
|
||||
verify(blockchain, times(1)).getBlockHashByNumber(NUMBER_3); |
||||
verify(blockchain, times(1)).getBlockHeader(NUMBER_3); |
||||
verify(blockchain, times(1)).getBlockHeader(testBlockHeaderHash); |
||||
verify(blockchain, times(1)).getTxReceipts(testBlockHeaderHash); |
||||
verify(blockchain, times(1)).getBlockBody(testBlockHeaderHash); |
||||
verify(blockchain, times(1)).blockIsOnCanonicalChain(testBlockHeaderHash); |
||||
|
||||
verifyNoMoreInteractions(blockchain); |
||||
} |
||||
|
||||
private void createLogBloomCache(final File logBloom) throws IOException { |
||||
try (final RandomAccessFile randomAccessFile = new RandomAccessFile(logBloom, "rws")) { |
||||
randomAccessFile.write(testLogsBloomFilter.toArray()); |
||||
randomAccessFile.write(testLogsBloomFilter.toArray()); |
||||
randomAccessFile.write(testLogsBloomFilter.toArray()); |
||||
} |
||||
} |
||||
|
||||
private BlockHeader createBlock(final long number) { |
||||
final Log testLog = new Log(testAddress, testMessage, List.of()); |
||||
final BlockHeader fakeHeader = |
||||
new BlockHeader( |
||||
Hash.EMPTY, |
||||
Hash.EMPTY, |
||||
Address.ZERO, |
||||
Hash.EMPTY, |
||||
Hash.EMPTY, |
||||
Hash.EMPTY, |
||||
LogsBloomFilter.empty(), |
||||
Difficulty.ZERO, |
||||
number, |
||||
0, |
||||
0, |
||||
0, |
||||
Bytes.EMPTY, |
||||
null, |
||||
Hash.EMPTY, |
||||
0, |
||||
null, |
||||
null, |
||||
null, |
||||
new MainnetBlockHeaderFunctions(), |
||||
Optional.of(testLogsBloomFilter)); |
||||
return fakeHeader; |
||||
} |
||||
} |
@ -1,56 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.core; |
||||
|
||||
import org.hyperledger.besu.enclave.GoQuorumEnclave; |
||||
import org.hyperledger.besu.ethereum.goquorum.GoQuorumPrivateStorage; |
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; |
||||
|
||||
public class GoQuorumPrivacyParameters { |
||||
|
||||
private final GoQuorumEnclave enclave; |
||||
|
||||
private final String enclaveKey; |
||||
|
||||
private final GoQuorumPrivateStorage goQuorumPrivateStorage; |
||||
private final WorldStateArchive privateWorldStateArchive; |
||||
|
||||
public GoQuorumPrivacyParameters( |
||||
final GoQuorumEnclave enclave, |
||||
final String enclaveKey, |
||||
final GoQuorumPrivateStorage goQuorumPrivateStorage, |
||||
final WorldStateArchive privateWorldStateArchive) { |
||||
this.enclave = enclave; |
||||
this.enclaveKey = enclaveKey; |
||||
this.goQuorumPrivateStorage = goQuorumPrivateStorage; |
||||
this.privateWorldStateArchive = privateWorldStateArchive; |
||||
} |
||||
|
||||
public GoQuorumEnclave enclave() { |
||||
return enclave; |
||||
} |
||||
|
||||
public String enclaveKey() { |
||||
return enclaveKey; |
||||
} |
||||
|
||||
public GoQuorumPrivateStorage privateStorage() { |
||||
return goQuorumPrivateStorage; |
||||
} |
||||
|
||||
public WorldStateArchive worldStateArchive() { |
||||
return privateWorldStateArchive; |
||||
} |
||||
} |
@ -1,44 +0,0 @@ |
||||
/* |
||||
* Copyright Hyperledger Besu Contributors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
* |
||||
*/ |
||||
|
||||
package org.hyperledger.besu.ethereum.goquorum; |
||||
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingOutputs; |
||||
import org.hyperledger.besu.ethereum.BlockProcessingResult; |
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
|
||||
public class GoQuorumBlockProcessingResult extends BlockProcessingResult { |
||||
|
||||
public final Optional<BlockProcessingOutputs> privateYield; |
||||
|
||||
public GoQuorumBlockProcessingResult( |
||||
final BlockProcessingOutputs mainnetYield, final BlockProcessingOutputs privateYield) { |
||||
super(Optional.of(mainnetYield)); |
||||
this.privateYield = Optional.ofNullable(privateYield); |
||||
} |
||||
|
||||
public List<TransactionReceipt> getPrivateReceipts() { |
||||
if (privateYield.isEmpty()) { |
||||
return new ArrayList<>(); |
||||
} else { |
||||
return privateYield.get().getReceipts(); |
||||
} |
||||
} |
||||
} |
@ -1,303 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.goquorum; |
||||
|
||||
import org.hyperledger.besu.datatypes.Address; |
||||
import org.hyperledger.besu.datatypes.Wei; |
||||
import org.hyperledger.besu.enclave.EnclaveClientException; |
||||
import org.hyperledger.besu.enclave.GoQuorumEnclave; |
||||
import org.hyperledger.besu.enclave.types.GoQuorumReceiveResponse; |
||||
import org.hyperledger.besu.ethereum.BlockProcessingOutputs; |
||||
import org.hyperledger.besu.ethereum.BlockProcessingResult; |
||||
import org.hyperledger.besu.ethereum.chain.Blockchain; |
||||
import org.hyperledger.besu.ethereum.core.Block; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters; |
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState; |
||||
import org.hyperledger.besu.ethereum.core.Transaction; |
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt; |
||||
import org.hyperledger.besu.ethereum.mainnet.HeaderBasedProtocolSchedule; |
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockProcessor; |
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; |
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionValidator; |
||||
import org.hyperledger.besu.ethereum.mainnet.MiningBeneficiaryCalculator; |
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; |
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult; |
||||
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; |
||||
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; |
||||
import org.hyperledger.besu.ethereum.vm.BlockHashLookup; |
||||
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; |
||||
import org.hyperledger.besu.ethereum.worldstate.GoQuorumMutablePrivateWorldStateUpdater; |
||||
import org.hyperledger.besu.evm.account.EvmAccount; |
||||
import org.hyperledger.besu.evm.log.LogsBloomFilter; |
||||
import org.hyperledger.besu.evm.tracing.OperationTracer; |
||||
import org.hyperledger.besu.evm.worldstate.WorldUpdater; |
||||
|
||||
import java.text.MessageFormat; |
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.Objects; |
||||
import java.util.Optional; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
public class GoQuorumBlockProcessor extends MainnetBlockProcessor { |
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GoQuorumBlockProcessor.class); |
||||
|
||||
private final GoQuorumEnclave goQuorumEnclave; |
||||
private final GoQuorumPrivateStorage goQuorumPrivateStorage; |
||||
|
||||
public GoQuorumBlockProcessor( |
||||
final MainnetTransactionProcessor transactionProcessor, |
||||
final TransactionReceiptFactory transactionReceiptFactory, |
||||
final Wei blockReward, |
||||
final MiningBeneficiaryCalculator miningBeneficiaryCalculator, |
||||
final boolean skipZeroBlockRewards, |
||||
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters, |
||||
final HeaderBasedProtocolSchedule protocolSchedule) { |
||||
super( |
||||
transactionProcessor, |
||||
transactionReceiptFactory, |
||||
blockReward, |
||||
miningBeneficiaryCalculator, |
||||
skipZeroBlockRewards, |
||||
Optional.empty(), |
||||
protocolSchedule); |
||||
|
||||
this.goQuorumEnclave = goQuorumPrivacyParameters.orElseThrow().enclave(); |
||||
this.goQuorumPrivateStorage = goQuorumPrivacyParameters.orElseThrow().privateStorage(); |
||||
} |
||||
|
||||
@Override |
||||
public BlockProcessingResult processBlock( |
||||
final Blockchain blockchain, |
||||
final MutableWorldState publicWorldState, |
||||
final MutableWorldState privateWorldState, |
||||
final Block block) { |
||||
final BlockHeader blockHeader = block.getHeader(); |
||||
final List<Transaction> transactions = block.getBody().getTransactions(); |
||||
final List<BlockHeader> ommers = block.getBody().getOmmers(); |
||||
|
||||
final List<TransactionReceipt> publicTxReceipts = new ArrayList<>(); |
||||
final List<TransactionReceipt> privateTxReceipts = new ArrayList<>(); |
||||
long currentGasUsed = 0; |
||||
|
||||
final GoQuorumPrivateStorage.Updater privateStorageUpdater = goQuorumPrivateStorage.updater(); |
||||
|
||||
for (final Transaction transaction : transactions) { |
||||
if (!hasAvailableBlockBudget(blockHeader, transaction, currentGasUsed)) { |
||||
return new BlockProcessingResult(Optional.empty(), "insufficient gas"); |
||||
} |
||||
|
||||
final WorldUpdater publicWorldStateUpdater = publicWorldState.updater(); |
||||
final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(blockHeader, blockchain); |
||||
final Address miningBeneficiary = |
||||
miningBeneficiaryCalculator.calculateBeneficiary(blockHeader); |
||||
|
||||
WorldUpdater effectiveWorldUpdater = null; |
||||
Transaction effectiveTransaction; |
||||
|
||||
final boolean isGoQuorumPrivateTransaction = |
||||
transaction.isGoQuorumPrivateTransaction( |
||||
transactionProcessor.getTransactionValidator().getGoQuorumCompatibilityMode()); |
||||
|
||||
if (isGoQuorumPrivateTransaction) { // private transaction
|
||||
try { |
||||
effectiveTransaction = retrievePrivateTransactionFromEnclave(transaction); |
||||
|
||||
effectiveWorldUpdater = |
||||
new GoQuorumMutablePrivateWorldStateUpdater( |
||||
publicWorldStateUpdater, privateWorldState.updater()); |
||||
|
||||
} catch (final EnclaveClientException e) { // private transaction but not party to it
|
||||
// We do not have to execute anything, but we still need to validate the transaction
|
||||
effectiveTransaction = null; |
||||
final ValidationResult<TransactionInvalidReason> validationResult = |
||||
validateTransaction(blockHeader, transaction, publicWorldStateUpdater); |
||||
if (!validationResult.isValid()) { |
||||
return new BlockProcessingResult(Optional.empty(), e); |
||||
} |
||||
} |
||||
} else { // public Transaction
|
||||
effectiveWorldUpdater = publicWorldState.updater(); |
||||
|
||||
effectiveTransaction = transaction; |
||||
} |
||||
|
||||
if (effectiveTransaction != null) { // public tx, or private tx that we are party to
|
||||
final TransactionProcessingResult result = |
||||
transactionProcessor.processTransaction( |
||||
blockchain, |
||||
effectiveWorldUpdater, |
||||
blockHeader, |
||||
effectiveTransaction, |
||||
miningBeneficiary, |
||||
OperationTracer.NO_TRACING, |
||||
blockHashLookup, |
||||
true, |
||||
TransactionValidationParams.processingBlock(), |
||||
null, |
||||
Wei.ZERO); |
||||
|
||||
if (result.isInvalid()) { |
||||
String errorMessage = |
||||
MessageFormat.format( |
||||
"Block processing error: transaction invalid '{}'. Block {} Transaction {}", |
||||
result.getValidationResult().getErrorMessage(), |
||||
blockHeader.getHash().toHexString(), |
||||
transaction.getHash().toHexString()); |
||||
LOG.info(errorMessage); |
||||
return new BlockProcessingResult(Optional.empty(), errorMessage); |
||||
} |
||||
|
||||
if (isGoQuorumPrivateTransaction) { // private transaction we are party to
|
||||
publicTxReceipts.add( |
||||
transactionReceiptFactory.create( |
||||
transaction.getType(), |
||||
publicResultForWhenWeHaveAPrivateTransaction(transaction), |
||||
publicWorldState, |
||||
currentGasUsed)); |
||||
privateTxReceipts.add( |
||||
transactionReceiptFactory.create( |
||||
transaction.getType(), result, privateWorldState, currentGasUsed)); |
||||
publicWorldStateUpdater |
||||
.getOrCreate(effectiveTransaction.getSender()) |
||||
.getMutable() |
||||
.incrementNonce(); |
||||
effectiveWorldUpdater.commit(); |
||||
} else { // public transaction
|
||||
final long gasUsed = transaction.getGasLimit() - result.getGasRemaining(); |
||||
currentGasUsed += gasUsed; |
||||
|
||||
publicTxReceipts.add( |
||||
transactionReceiptFactory.create( |
||||
transaction.getType(), result, publicWorldState, currentGasUsed)); |
||||
privateTxReceipts.add(null); |
||||
effectiveWorldUpdater.commit(); |
||||
} |
||||
} else { // private transaction we are not party to
|
||||
publicTxReceipts.add( |
||||
transactionReceiptFactory.create( |
||||
transaction.getType(), |
||||
publicResultForWhenWeHaveAPrivateTransaction(transaction), |
||||
publicWorldState, |
||||
currentGasUsed)); |
||||
privateTxReceipts.add(null); |
||||
publicWorldStateUpdater.getOrCreate(transaction.getSender()).getMutable().incrementNonce(); |
||||
} |
||||
|
||||
publicWorldStateUpdater.commit(); |
||||
} |
||||
|
||||
if (!rewardCoinbase(publicWorldState, blockHeader, ommers, skipZeroBlockRewards)) { |
||||
// no need to log, rewardCoinbase logs the error.
|
||||
return new BlockProcessingResult(Optional.empty(), "ommer too old"); |
||||
} |
||||
|
||||
// create the bloom for the private transactions in the block and store it
|
||||
final LogsBloomFilter.Builder privateBloomBuilder = LogsBloomFilter.builder(); |
||||
privateTxReceipts.stream() |
||||
.filter(Objects::nonNull) |
||||
.forEach(pr -> privateBloomBuilder.insertFilter(pr.getBloomFilter())); |
||||
blockHeader.setPrivateLogsBloom(privateBloomBuilder.build()); |
||||
|
||||
publicWorldState.persist(blockHeader); |
||||
privateWorldState.persist(null); |
||||
|
||||
privateStorageUpdater.putPrivateStateRootHashMapping( |
||||
publicWorldState.rootHash(), privateWorldState.rootHash()); |
||||
privateStorageUpdater.commit(); |
||||
|
||||
BlockProcessingOutputs mainnetYield = |
||||
new BlockProcessingOutputs(publicWorldState, publicTxReceipts); |
||||
BlockProcessingOutputs privateYield = |
||||
new BlockProcessingOutputs(privateWorldState, privateTxReceipts); |
||||
return new GoQuorumBlockProcessingResult(mainnetYield, privateYield); |
||||
} |
||||
|
||||
private ValidationResult<TransactionInvalidReason> validateTransaction( |
||||
final BlockHeader blockHeader, |
||||
final Transaction transaction, |
||||
final WorldUpdater publicWorldStateUpdater) { |
||||
final MainnetTransactionValidator transactionValidator = |
||||
transactionProcessor.getTransactionValidator(); |
||||
final TransactionValidationParams transactionValidationParams = |
||||
TransactionValidationParams.processingBlock(); |
||||
ValidationResult<TransactionInvalidReason> validationResult = |
||||
transactionValidator.validate( |
||||
transaction, blockHeader.getBaseFee(), transactionValidationParams); |
||||
if (!validationResult.isValid()) { |
||||
LOG.warn( |
||||
"Invalid transaction: {}. Block {} Transaction {}", |
||||
validationResult.getErrorMessage(), |
||||
blockHeader.getHash().toHexString(), |
||||
transaction.getHash().toHexString()); |
||||
return validationResult; |
||||
} |
||||
|
||||
final Address senderAddress = transaction.getSender(); |
||||
|
||||
final EvmAccount sender = publicWorldStateUpdater.getOrCreate(senderAddress); |
||||
validationResult = |
||||
transactionValidator.validateForSender(transaction, sender, transactionValidationParams); |
||||
if (!validationResult.isValid()) { |
||||
LOG.warn( |
||||
"Invalid transaction: {}. Block {} Transaction {}", |
||||
validationResult.getErrorMessage(), |
||||
blockHeader.getHash().toHexString(), |
||||
transaction.getHash().toHexString()); |
||||
return validationResult; |
||||
} |
||||
return ValidationResult.valid(); |
||||
} |
||||
|
||||
private TransactionProcessingResult publicResultForWhenWeHaveAPrivateTransaction( |
||||
final Transaction transaction) { |
||||
return TransactionProcessingResult.successful( |
||||
Collections.emptyList(), |
||||
0, |
||||
transaction.getGasLimit(), |
||||
Bytes.EMPTY, |
||||
ValidationResult.valid()); |
||||
} |
||||
|
||||
private Transaction retrievePrivateTransactionFromEnclave(final Transaction transaction) { |
||||
final GoQuorumReceiveResponse receive = |
||||
goQuorumEnclave.receive(transaction.getPayload().toBase64String()); |
||||
|
||||
final Bytes privatePayload = Bytes.wrap(receive.getPayload()); |
||||
|
||||
return new Transaction( |
||||
transaction.getNonce(), |
||||
transaction.getGasPrice().get(), |
||||
transaction.getGasLimit(), |
||||
transaction.getTo(), |
||||
transaction.getValue(), |
||||
transaction.getSignature(), |
||||
privatePayload, |
||||
transaction |
||||
.getSender(), // at this point we are checking the signature of the public transaction
|
||||
// and we are setting the sender for the private transaction, so the
|
||||
// signature of the private transaction will not (and should not) be
|
||||
// checked again.
|
||||
transaction.getChainId(), |
||||
Optional.of(transaction.getV()), |
||||
Optional.empty()); |
||||
} |
||||
} |
@ -1,61 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.goquorum; |
||||
|
||||
import static org.hyperledger.besu.ethereum.goquorum.GoQuorumPrivateStateUtil.getPrivateWorldState; |
||||
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingResult; |
||||
import org.hyperledger.besu.ethereum.MainnetBlockValidator; |
||||
import org.hyperledger.besu.ethereum.ProtocolContext; |
||||
import org.hyperledger.besu.ethereum.chain.BadBlockManager; |
||||
import org.hyperledger.besu.ethereum.core.Block; |
||||
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters; |
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState; |
||||
import org.hyperledger.besu.ethereum.mainnet.BlockBodyValidator; |
||||
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator; |
||||
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor; |
||||
|
||||
import java.util.Optional; |
||||
|
||||
public class GoQuorumBlockValidator extends MainnetBlockValidator { |
||||
|
||||
private final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters; |
||||
|
||||
public GoQuorumBlockValidator( |
||||
final BlockHeaderValidator blockHeaderValidator, |
||||
final BlockBodyValidator blockBodyValidator, |
||||
final BlockProcessor blockProcessor, |
||||
final BadBlockManager badBlockManager, |
||||
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters) { |
||||
super(blockHeaderValidator, blockBodyValidator, blockProcessor, badBlockManager); |
||||
|
||||
this.goQuorumPrivacyParameters = goQuorumPrivacyParameters; |
||||
|
||||
if (!(blockProcessor instanceof GoQuorumBlockProcessor)) { |
||||
throw new IllegalStateException( |
||||
"GoQuorumBlockValidator requires an instance of GoQuorumBlockProcessor"); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected BlockProcessingResult processBlock( |
||||
final ProtocolContext context, final MutableWorldState worldState, final Block block) { |
||||
final MutableWorldState privateWorldState = |
||||
getPrivateWorldState(goQuorumPrivacyParameters, worldState.rootHash(), block.getHash()); |
||||
|
||||
return ((GoQuorumBlockProcessor) blockProcessor) |
||||
.processBlock(context.getBlockchain(), worldState, privateWorldState, block); |
||||
} |
||||
} |
@ -1,81 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.goquorum; |
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8; |
||||
|
||||
import org.hyperledger.besu.datatypes.Hash; |
||||
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; |
||||
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction; |
||||
|
||||
import java.util.Optional; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.apache.tuweni.bytes.Bytes32; |
||||
|
||||
public class GoQuorumPrivateKeyValueStorage implements GoQuorumPrivateStorage { |
||||
|
||||
private static final Bytes PRIVATE_STATEROOT_SUFFIX = Bytes.of("PRIVSTATEROOT".getBytes(UTF_8)); |
||||
|
||||
private final KeyValueStorage keyValueStorage; |
||||
|
||||
public GoQuorumPrivateKeyValueStorage(final KeyValueStorage keyValueStorage) { |
||||
this.keyValueStorage = keyValueStorage; |
||||
} |
||||
|
||||
@Override |
||||
public Optional<Hash> getPrivateStateRootHash(final Hash publicStateRootHash) { |
||||
return get(publicStateRootHash, PRIVATE_STATEROOT_SUFFIX).map(Bytes32::wrap).map(Hash::wrap); |
||||
} |
||||
|
||||
private Optional<Bytes> get(final Bytes key, final Bytes keySuffix) { |
||||
return keyValueStorage.get(Bytes.concatenate(key, keySuffix).toArrayUnsafe()).map(Bytes::wrap); |
||||
} |
||||
|
||||
@Override |
||||
public GoQuorumPrivateStorage.Updater updater() { |
||||
return new Updater(keyValueStorage.startTransaction()); |
||||
} |
||||
|
||||
public static class Updater implements GoQuorumPrivateStorage.Updater { |
||||
|
||||
private final KeyValueStorageTransaction transaction; |
||||
|
||||
private Updater(final KeyValueStorageTransaction transaction) { |
||||
this.transaction = transaction; |
||||
} |
||||
|
||||
@Override |
||||
public GoQuorumPrivateStorage.Updater putPrivateStateRootHashMapping( |
||||
final Hash publicStateRootHash, final Hash privateStateRootHash) { |
||||
set(publicStateRootHash, PRIVATE_STATEROOT_SUFFIX, privateStateRootHash); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public void commit() { |
||||
transaction.commit(); |
||||
} |
||||
|
||||
@Override |
||||
public void rollback() { |
||||
transaction.rollback(); |
||||
} |
||||
|
||||
private void set(final Bytes key, final Bytes keySuffix, final Bytes value) { |
||||
transaction.put(Bytes.concatenate(key, keySuffix).toArrayUnsafe(), value.toArrayUnsafe()); |
||||
} |
||||
} |
||||
} |
@ -1,69 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
package org.hyperledger.besu.ethereum.goquorum; |
||||
|
||||
import org.hyperledger.besu.datatypes.Hash; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters; |
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState; |
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; |
||||
|
||||
import java.util.Optional; |
||||
|
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
public class GoQuorumPrivateStateUtil { |
||||
private static final Logger LOG = LoggerFactory.getLogger(GoQuorumPrivateStateUtil.class); |
||||
|
||||
public static MutableWorldState getPrivateWorldStateAtBlock( |
||||
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters, |
||||
final BlockHeader header) { |
||||
return getPrivateWorldState(goQuorumPrivacyParameters, header.getStateRoot(), header.getHash()); |
||||
} |
||||
|
||||
public static MutableWorldState getPrivateWorldState( |
||||
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters, |
||||
final Hash worldStateRootHash, |
||||
final Hash publicBlockHash) { |
||||
final GoQuorumPrivateStorage goQuorumPrivateStorage = |
||||
goQuorumPrivacyParameters.orElseThrow().privateStorage(); |
||||
final WorldStateArchive goQuorumWorldStateArchive = |
||||
goQuorumPrivacyParameters.orElseThrow().worldStateArchive(); |
||||
|
||||
final Hash privateStateRootHash = |
||||
goQuorumPrivateStorage |
||||
.getPrivateStateRootHash(worldStateRootHash) |
||||
.orElse(Hash.EMPTY_TRIE_HASH); |
||||
|
||||
final Optional<MutableWorldState> maybePrivateWorldState = |
||||
goQuorumWorldStateArchive.getMutable(privateStateRootHash, publicBlockHash); |
||||
if (maybePrivateWorldState.isEmpty()) { |
||||
LOG.debug( |
||||
"Private world state not available for public world state root hash {}, public block hash {}", |
||||
worldStateRootHash, |
||||
publicBlockHash); |
||||
|
||||
/* |
||||
This should never happen because privateStateRootResolver will either return a matching |
||||
private world state root hash, or the hash for an empty world state (first private tx ever). |
||||
*/ |
||||
throw new IllegalStateException( |
||||
"Private world state not available for public world state root hash " + publicBlockHash); |
||||
} |
||||
return maybePrivateWorldState.get(); |
||||
} |
||||
} |
@ -1,36 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.goquorum; |
||||
|
||||
import org.hyperledger.besu.datatypes.Hash; |
||||
|
||||
import java.util.Optional; |
||||
|
||||
public interface GoQuorumPrivateStorage { |
||||
|
||||
Optional<Hash> getPrivateStateRootHash(final Hash publicStateRootHash); |
||||
|
||||
Updater updater(); |
||||
|
||||
interface Updater { |
||||
|
||||
Updater putPrivateStateRootHashMapping( |
||||
final Hash publicStateRootHash, final Hash privateStateRootHash); |
||||
|
||||
void commit(); |
||||
|
||||
void rollback(); |
||||
} |
||||
} |
@ -1,52 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.privacy; |
||||
|
||||
import java.util.List; |
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator; |
||||
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
||||
public class GoQuorumSendRawTxArgs { |
||||
|
||||
private final String privateFrom; |
||||
|
||||
private final List<String> privateFor; |
||||
|
||||
private final int privacyFlag; |
||||
|
||||
@JsonCreator |
||||
public GoQuorumSendRawTxArgs( |
||||
@JsonProperty("privateFrom") final String privateFrom, |
||||
@JsonProperty("privateFor") final List<String> privateFor, |
||||
@JsonProperty("privacyFlag") final int privacyFlag) { |
||||
|
||||
this.privateFrom = privateFrom; |
||||
this.privateFor = privateFor; |
||||
this.privacyFlag = privacyFlag; |
||||
} |
||||
|
||||
public String getPrivateFrom() { |
||||
return privateFrom; |
||||
} |
||||
|
||||
public List<String> getPrivateFor() { |
||||
return privateFor; |
||||
} |
||||
|
||||
public int getPrivacyFlag() { |
||||
return privacyFlag; |
||||
} |
||||
} |
@ -1,72 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.storage.keyvalue; |
||||
|
||||
import org.hyperledger.besu.datatypes.Hash; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions; |
||||
import org.hyperledger.besu.ethereum.rlp.RLP; |
||||
import org.hyperledger.besu.evm.log.LogsBloomFilter; |
||||
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; |
||||
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction; |
||||
|
||||
import java.util.Optional; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
|
||||
public class GoQuorumKeyValueStoragePrefixedKeyBlockchainStorage |
||||
extends KeyValueStoragePrefixedKeyBlockchainStorage { |
||||
|
||||
private static final Bytes BLOCK_HEADER_PRIVATE_BLOOM_PREFIX = Bytes.of(8); |
||||
|
||||
public GoQuorumKeyValueStoragePrefixedKeyBlockchainStorage( |
||||
final KeyValueStorage storage, final BlockHeaderFunctions blockHeaderFunctions) { |
||||
super(storage, blockHeaderFunctions); |
||||
} |
||||
|
||||
@Override |
||||
public Optional<BlockHeader> getBlockHeader(final Hash blockHash) { |
||||
final Optional<BlockHeader> blockHeaderOptional = |
||||
get(BLOCK_HEADER_PREFIX, blockHash) |
||||
.map(b -> BlockHeader.readFrom(RLP.input(b), blockHeaderFunctions)); |
||||
if (blockHeaderOptional.isPresent()) { |
||||
final Optional<Bytes> privateBloomBytesOptional = |
||||
get(BLOCK_HEADER_PRIVATE_BLOOM_PREFIX, blockHash); |
||||
privateBloomBytesOptional.ifPresent( |
||||
pBB -> blockHeaderOptional.get().setPrivateLogsBloom(new LogsBloomFilter(pBB))); |
||||
} |
||||
return blockHeaderOptional; |
||||
} |
||||
|
||||
@Override |
||||
public Updater updater() { |
||||
return new Updater(storage.startTransaction()); |
||||
} |
||||
|
||||
public static class Updater extends KeyValueStoragePrefixedKeyBlockchainStorage.Updater { |
||||
|
||||
private Updater(final KeyValueStorageTransaction transaction) { |
||||
super(transaction); |
||||
} |
||||
|
||||
@Override |
||||
public void putBlockHeader(final Hash blockHash, final BlockHeader blockHeader) { |
||||
set(BLOCK_HEADER_PREFIX, blockHash, RLP.encode(blockHeader::writeTo)); |
||||
blockHeader |
||||
.getPrivateLogsBloom() |
||||
.ifPresent(privateLoom -> set(BLOCK_HEADER_PRIVATE_BLOOM_PREFIX, blockHash, privateLoom)); |
||||
} |
||||
} |
||||
} |
@ -1,57 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.storage.keyvalue; |
||||
|
||||
import org.hyperledger.besu.ethereum.chain.BlockchainStorage; |
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; |
||||
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions; |
||||
import org.hyperledger.besu.metrics.ObservableMetricsSystem; |
||||
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; |
||||
import org.hyperledger.besu.plugin.services.storage.SegmentIdentifier; |
||||
|
||||
import java.util.function.Function; |
||||
|
||||
public class GoQuorumKeyValueStorageProvider extends KeyValueStorageProvider { |
||||
|
||||
public GoQuorumKeyValueStorageProvider( |
||||
final Function<SegmentIdentifier, KeyValueStorage> storageCreator, |
||||
final KeyValueStorage worldStatePreimageStorage, |
||||
final boolean segmentIsolationSupported, |
||||
final ObservableMetricsSystem metricsSystem) { |
||||
super(storageCreator, worldStatePreimageStorage, segmentIsolationSupported, metricsSystem); |
||||
} |
||||
|
||||
public GoQuorumKeyValueStorageProvider( |
||||
final Function<SegmentIdentifier, KeyValueStorage> storageCreator, |
||||
final KeyValueStorage worldStatePreimageStorage, |
||||
final KeyValueStorage privateWorldStatePreimageStorage, |
||||
final boolean segmentIsolationSupported, |
||||
final ObservableMetricsSystem metricsSystem) { |
||||
super( |
||||
storageCreator, |
||||
worldStatePreimageStorage, |
||||
privateWorldStatePreimageStorage, |
||||
segmentIsolationSupported, |
||||
SNAPSHOT_ISOLATION_UNSUPPORTED, |
||||
metricsSystem); |
||||
} |
||||
|
||||
@Override |
||||
public BlockchainStorage createBlockchainStorage(final ProtocolSchedule protocolSchedule) { |
||||
return new GoQuorumKeyValueStoragePrefixedKeyBlockchainStorage( |
||||
getStorageBySegmentIdentifier(KeyValueSegmentIdentifier.BLOCKCHAIN), |
||||
ScheduleBasedBlockHeaderFunctions.create(protocolSchedule)); |
||||
} |
||||
} |
@ -1,34 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
package org.hyperledger.besu.ethereum.transaction; |
||||
|
||||
import static org.hyperledger.besu.ethereum.core.Transaction.GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MAX; |
||||
import static org.hyperledger.besu.ethereum.core.Transaction.GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MIN; |
||||
|
||||
import java.math.BigInteger; |
||||
|
||||
public class GoQuorumPrivateTransactionDetector { |
||||
/** |
||||
* Returns whether or not <i>v</i> indicates a GoQuorum private transaction. |
||||
* |
||||
* @param v the v value of a transaction |
||||
* @return true if GoQuorum private transaction, false otherwise |
||||
*/ |
||||
public static final boolean isGoQuorumPrivateTransactionV(final BigInteger v) { |
||||
return ((v.compareTo(GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MAX) <= 0) |
||||
&& (v.compareTo(GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MIN)) >= 0); |
||||
} |
||||
} |
@ -1,53 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.worldstate; |
||||
|
||||
import org.hyperledger.besu.datatypes.Address; |
||||
import org.hyperledger.besu.evm.account.EvmAccount; |
||||
import org.hyperledger.besu.evm.worldstate.WorldUpdater; |
||||
|
||||
// This class uses a public WorldUpdater and a private WorldUpdater to
|
||||
// provide a MutableWorldStateUpdater that can read and write from
|
||||
// BOTH the private world state and the public world state.
|
||||
//
|
||||
// Note that only writes to private states are committed to the
|
||||
// underlying storage (via
|
||||
// DefaultMutablePrivateWorldStateUpdater::commit) whereas public
|
||||
// state changes are discarded.
|
||||
//
|
||||
// World state obtained by this class must not be persisted: it allows
|
||||
// illegal write access from private to public contract data that
|
||||
// would result in world state divergence between nodes.
|
||||
public class GoQuorumMutablePrivateAndPublicWorldStateUpdater |
||||
extends GoQuorumMutablePrivateWorldStateUpdater { |
||||
|
||||
public GoQuorumMutablePrivateAndPublicWorldStateUpdater( |
||||
final WorldUpdater publicWorldUpdater, final WorldUpdater privateWorldUpdater) { |
||||
super(publicWorldUpdater, privateWorldUpdater); |
||||
} |
||||
|
||||
@Override |
||||
public EvmAccount getAccount(final Address address) { |
||||
final EvmAccount privateAccount = privateWorldUpdater.getAccount(address); |
||||
if (privateAccount != null && !privateAccount.isEmpty()) { |
||||
return privateAccount; |
||||
} |
||||
final EvmAccount publicAccount = publicWorldUpdater.getAccount(address); |
||||
if (publicAccount != null && !publicAccount.isEmpty()) { |
||||
return publicAccount; |
||||
} |
||||
return privateAccount; |
||||
} |
||||
} |
@ -1,48 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.worldstate; |
||||
|
||||
import org.hyperledger.besu.datatypes.Address; |
||||
import org.hyperledger.besu.evm.account.EvmAccount; |
||||
import org.hyperledger.besu.evm.frame.MessageFrame; |
||||
import org.hyperledger.besu.evm.worldstate.UpdateTrackingAccount; |
||||
import org.hyperledger.besu.evm.worldstate.WorldUpdater; |
||||
|
||||
// This class uses a public WorldUpdater and a private WorldUpdater to provide a
|
||||
// MutableWorldStateUpdater that can read and write from the private world state and can read from
|
||||
// the public world state, but cannot write to it.
|
||||
public class GoQuorumMutablePrivateWorldStateUpdater |
||||
extends DefaultMutablePrivateWorldStateUpdater { |
||||
|
||||
public GoQuorumMutablePrivateWorldStateUpdater( |
||||
final WorldUpdater publicWorldUpdater, final WorldUpdater privateWorldUpdater) { |
||||
super(publicWorldUpdater, privateWorldUpdater); |
||||
} |
||||
|
||||
@Override |
||||
public EvmAccount getOrCreateSenderAccount(final Address address) { |
||||
return new UpdateTrackingAccount<>(publicWorldUpdater.getOrCreate(address)); |
||||
} |
||||
|
||||
@Override |
||||
public EvmAccount getSenderAccount(final MessageFrame frame) { |
||||
final Address senderAddress = frame.getSenderAddress(); |
||||
if (senderAddress.equals(frame.getOriginatorAddress())) { |
||||
return new UpdateTrackingAccount<>(publicWorldUpdater.getOrCreate(senderAddress)); |
||||
} else { |
||||
return getAccount(senderAddress); |
||||
} |
||||
} |
||||
} |
@ -1,89 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.core; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
import org.hyperledger.besu.datatypes.Wei; |
||||
import org.hyperledger.besu.ethereum.core.encoding.TransactionDecoder; |
||||
import org.hyperledger.besu.ethereum.rlp.RLP; |
||||
import org.hyperledger.besu.ethereum.rlp.RLPInput; |
||||
|
||||
import java.math.BigInteger; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.junit.Test; |
||||
|
||||
public class TransactionGoQuorumTest { |
||||
|
||||
private static final RLPInput ETHEREUM_PUBLIC_TX_RLP = |
||||
toRLP( |
||||
"0xf901fc8032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b561ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884"); |
||||
private static final RLPInput GOQUORUM_PRIVATE_TX_RLP_V37 = |
||||
toRLP( |
||||
"0xf88d0b808347b7608080b840290a80a37d198ff06abe189b638ff53ac8a8dc51a0aff07609d2aa75342783ae493b3e3c6b564c0eebe49284b05a0726fb33087b9e0231d349ea0c7b5661c8c525a07144db7045a395e608cda6ab051c86cc4fb42e319960b82087f3b26f0cbc3c2da00223ac129b22aec7a6c2ace3c3ef39c5eaaa54070fd82d8ee2140b0e70b1dca9"); |
||||
private static final RLPInput GOQUORUM_PRIVATE_TX_RLP_V38 = |
||||
toRLP( |
||||
"0xf88d0b808347b7608080b840290a80a37d198ff06abe189b638ff53ac8a8dc51a0aff07609d2aa75342783ae493b3e3c6b564c0eebe49284b05a0726fb33087b9e0231d349ea0c7b5661c8c526a07144db7045a395e608cda6ab051c86cc4fb42e319960b82087f3b26f0cbc3c2da00223ac129b22aec7a6c2ace3c3ef39c5eaaa54070fd82d8ee2140b0e70b1dca9"); |
||||
|
||||
private static final boolean goQuorumCompatibilityMode = true; |
||||
|
||||
@Test |
||||
public void givenPublicTransaction_assertThatIsGoQuorumFlagIsFalse() { |
||||
final Transaction transaction = |
||||
TransactionDecoder.decodeForWire(ETHEREUM_PUBLIC_TX_RLP, goQuorumCompatibilityMode); |
||||
|
||||
assertThat(transaction.isGoQuorumPrivateTransaction(goQuorumCompatibilityMode)).isFalse(); |
||||
assertThat(transaction.hasCostParams()).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void givenGoQuorumTransactionV37_assertThatIsGoQuorumFlagIsTrue() { |
||||
final Transaction transaction = |
||||
TransactionDecoder.decodeForWire(GOQUORUM_PRIVATE_TX_RLP_V37, goQuorumCompatibilityMode); |
||||
|
||||
assertThat(transaction.getV()).isEqualTo(37); |
||||
assertThat(transaction.isGoQuorumPrivateTransaction(goQuorumCompatibilityMode)).isTrue(); |
||||
assertThat(transaction.hasCostParams()).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void givenGoQuorumTransactionV38_assertThatIsGoQuorumFlagIsTrue() { |
||||
final Transaction transaction = |
||||
TransactionDecoder.decodeForWire(GOQUORUM_PRIVATE_TX_RLP_V38, goQuorumCompatibilityMode); |
||||
|
||||
assertThat(transaction.getV()).isEqualTo(38); |
||||
assertThat(transaction.isGoQuorumPrivateTransaction(goQuorumCompatibilityMode)).isTrue(); |
||||
assertThat(transaction.hasCostParams()).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void givenTransactionWithChainId_assertThatIsGoQuorumFlagIsFalse() { |
||||
final Transaction transaction = |
||||
Transaction.builder().chainId(BigInteger.valueOf(0)).gasPrice(Wei.ZERO).build(); |
||||
assertThat(transaction.isGoQuorumPrivateTransaction(goQuorumCompatibilityMode)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void givenTransactionWithoutChainIdAndV37_assertThatIsGoQuorumFlagIsTrue() { |
||||
final Transaction transaction = |
||||
Transaction.builder().v(BigInteger.valueOf(37)).gasPrice(Wei.ZERO).build(); |
||||
assertThat(transaction.isGoQuorumPrivateTransaction(goQuorumCompatibilityMode)).isTrue(); |
||||
} |
||||
|
||||
private static RLPInput toRLP(final String bytes) { |
||||
return RLP.input(Bytes.fromHexString(bytes)); |
||||
} |
||||
} |
@ -1,165 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.goquorum; |
||||
|
||||
import static java.util.Collections.emptyList; |
||||
import static java.util.Collections.emptyMap; |
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import org.hyperledger.besu.datatypes.Hash; |
||||
import org.hyperledger.besu.datatypes.Wei; |
||||
import org.hyperledger.besu.enclave.EnclaveServerException; |
||||
import org.hyperledger.besu.enclave.GoQuorumEnclave; |
||||
import org.hyperledger.besu.ethereum.chain.Blockchain; |
||||
import org.hyperledger.besu.ethereum.core.Block; |
||||
import org.hyperledger.besu.ethereum.core.BlockBody; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; |
||||
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters; |
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState; |
||||
import org.hyperledger.besu.ethereum.core.Transaction; |
||||
import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor; |
||||
import org.hyperledger.besu.ethereum.mainnet.HeaderBasedProtocolSchedule; |
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; |
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionValidator; |
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; |
||||
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestBlockchain; |
||||
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestWorldState; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Optional; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.Mock; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
|
||||
@RunWith(MockitoJUnitRunner.class) |
||||
public class GoQuorumBlockProcessorTest { |
||||
|
||||
@Mock private AbstractBlockProcessor.TransactionReceiptFactory transactionReceiptFactory; |
||||
@Mock private GoQuorumEnclave goQuorumEnclave; |
||||
@Mock private GoQuorumPrivateStorage goQuorumPrivateStorage; |
||||
@Mock private MainnetTransactionProcessor transactionProcessor; |
||||
@Mock private MainnetTransactionValidator transactionValidator; |
||||
@Mock private HeaderBasedProtocolSchedule protocolSchedule; |
||||
@Mock private ProtocolSpec protocolSpec; |
||||
|
||||
private GoQuorumPrivacyParameters goQuorumPrivacyParameters; |
||||
|
||||
@Before |
||||
public void setup() { |
||||
when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec); |
||||
goQuorumPrivacyParameters = |
||||
new GoQuorumPrivacyParameters(goQuorumEnclave, "123", goQuorumPrivateStorage, null); |
||||
} |
||||
|
||||
@Test |
||||
public void noAccountCreatedWhenBlockRewardIsZeroAndSkipped() { |
||||
final Blockchain blockchain = new ReferenceTestBlockchain(); |
||||
final GoQuorumBlockProcessor blockProcessor = |
||||
new GoQuorumBlockProcessor( |
||||
transactionProcessor, |
||||
transactionReceiptFactory, |
||||
Wei.ZERO, |
||||
BlockHeader::getCoinbase, |
||||
true, |
||||
Optional.of(goQuorumPrivacyParameters), |
||||
protocolSchedule); |
||||
|
||||
final MutableWorldState worldState = ReferenceTestWorldState.create(emptyMap()); |
||||
final Hash initialHash = worldState.rootHash(); |
||||
|
||||
final BlockHeader emptyBlockHeader = |
||||
new BlockHeaderTestFixture() |
||||
.transactionsRoot(Hash.EMPTY_LIST_HASH) |
||||
.ommersHash(Hash.EMPTY_LIST_HASH) |
||||
.buildHeader(); |
||||
blockProcessor.processBlock(blockchain, worldState, emptyBlockHeader, emptyList(), emptyList()); |
||||
|
||||
// An empty block with 0 reward should not change the world state
|
||||
assertThat(worldState.rootHash()).isEqualTo(initialHash); |
||||
} |
||||
|
||||
@Test |
||||
public void accountCreatedWhenBlockRewardIsZeroAndNotSkipped() { |
||||
final Blockchain blockchain = new ReferenceTestBlockchain(); |
||||
final GoQuorumBlockProcessor blockProcessor = |
||||
new GoQuorumBlockProcessor( |
||||
transactionProcessor, |
||||
transactionReceiptFactory, |
||||
Wei.ZERO, |
||||
BlockHeader::getCoinbase, |
||||
false, |
||||
Optional.of(goQuorumPrivacyParameters), |
||||
protocolSchedule); |
||||
|
||||
final MutableWorldState worldState = ReferenceTestWorldState.create(emptyMap()); |
||||
final Hash initialHash = worldState.rootHash(); |
||||
|
||||
final BlockHeader emptyBlockHeader = |
||||
new BlockHeaderTestFixture() |
||||
.transactionsRoot(Hash.EMPTY_LIST_HASH) |
||||
.ommersHash(Hash.EMPTY_LIST_HASH) |
||||
.buildHeader(); |
||||
blockProcessor.processBlock(blockchain, worldState, emptyBlockHeader, emptyList(), emptyList()); |
||||
|
||||
// An empty block with 0 reward should change the world state prior to EIP158
|
||||
assertThat(worldState.rootHash()).isNotEqualTo(initialHash); |
||||
} |
||||
|
||||
@Test |
||||
public void enclaveNotAvailable() { |
||||
final Blockchain blockchain = new ReferenceTestBlockchain(); |
||||
final GoQuorumBlockProcessor blockProcessor = |
||||
new GoQuorumBlockProcessor( |
||||
transactionProcessor, |
||||
transactionReceiptFactory, |
||||
Wei.ZERO, |
||||
BlockHeader::getCoinbase, |
||||
false, |
||||
Optional.of(goQuorumPrivacyParameters), |
||||
protocolSchedule); |
||||
|
||||
final MutableWorldState worldState = ReferenceTestWorldState.create(emptyMap()); |
||||
|
||||
final Block block = mock(Block.class); |
||||
final BlockHeader blockHeader = mock(BlockHeader.class); |
||||
final BlockBody blockBody = mock(BlockBody.class); |
||||
final Transaction transaction = mock(Transaction.class); |
||||
when(transaction.getGasLimit()).thenReturn(1000L); |
||||
when(transaction.isGoQuorumPrivateTransaction(true)).thenReturn(true); |
||||
when(transaction.getPayload()).thenReturn(Bytes.wrap(new byte[] {(byte) 1})); |
||||
when(block.getBody()).thenReturn(blockBody); |
||||
when(blockBody.getTransactions()).thenReturn(Collections.singletonList(transaction)); |
||||
when(blockBody.getOmmers()).thenReturn(Collections.emptyList()); |
||||
when(blockHeader.getNumber()).thenReturn(20000L); |
||||
when(blockHeader.getGasLimit()).thenReturn(20000L); |
||||
when(block.getHeader()).thenReturn(blockHeader); |
||||
when(goQuorumEnclave.receive(any())).thenThrow(new EnclaveServerException(1, "a")); |
||||
when(transactionProcessor.getTransactionValidator()).thenReturn(transactionValidator); |
||||
when(transactionValidator.getGoQuorumCompatibilityMode()).thenReturn(true); |
||||
|
||||
assertThatThrownBy(() -> blockProcessor.processBlock(blockchain, worldState, worldState, block)) |
||||
.isExactlyInstanceOf(EnclaveServerException.class) |
||||
.hasMessageContaining("a"); |
||||
} |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue