mirror of https://github.com/hyperledger/besu
New RPC methods `miner_setExtraData` and `miner_getExtraData` (#7078)
* New RPC methods to set and get block extra data Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Remove redundant methods to set the extra data Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * miner_getExtraData unit tests Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Add CHANGELOG Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Apply suggestions from code review Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>pull/7083/head
parent
b4b6adcf99
commit
40cfc800f7
@ -0,0 +1,41 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.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.methods.miner; |
||||||
|
|
||||||
|
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.JsonRpcResponse; |
||||||
|
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; |
||||||
|
import org.hyperledger.besu.ethereum.core.MiningParameters; |
||||||
|
|
||||||
|
public class MinerGetExtraData implements JsonRpcMethod { |
||||||
|
private final MiningParameters miningParameters; |
||||||
|
|
||||||
|
public MinerGetExtraData(final MiningParameters miningParameters) { |
||||||
|
this.miningParameters = miningParameters; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() { |
||||||
|
return RpcMethod.MINER_GET_EXTRA_DATA.getMethodName(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { |
||||||
|
return new JsonRpcSuccessResponse( |
||||||
|
requestContext.getRequest().getId(), miningParameters.getExtraData().toShortHexString()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.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.methods.miner; |
||||||
|
|
||||||
|
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.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; |
||||||
|
import org.hyperledger.besu.ethereum.core.MiningParameters; |
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
|
||||||
|
import org.apache.tuweni.bytes.Bytes; |
||||||
|
import org.apache.tuweni.bytes.Bytes32; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
public class MinerSetExtraData implements JsonRpcMethod { |
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(MinerSetExtraData.class); |
||||||
|
|
||||||
|
private final MiningParameters miningParameters; |
||||||
|
|
||||||
|
public MinerSetExtraData(final MiningParameters miningParameters) { |
||||||
|
this.miningParameters = miningParameters; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() { |
||||||
|
return RpcMethod.MINER_SET_EXTRA_DATA.getMethodName(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { |
||||||
|
try { |
||||||
|
final String rawParam = requestContext.getRequiredParameter(0, String.class); |
||||||
|
Bytes32.fromHexStringLenient( |
||||||
|
rawParam); // done for validation, we want a hex string and max 32 bytes
|
||||||
|
final var extraData = Bytes.fromHexStringLenient(rawParam); |
||||||
|
miningParameters.setExtraData(extraData); |
||||||
|
LOG.atDebug() |
||||||
|
.setMessage("set extra data, raw=[{}] parsed=[{}], UTF-8=[{}]") |
||||||
|
.addArgument(rawParam) |
||||||
|
.addArgument(extraData::toHexString) |
||||||
|
.addArgument(() -> new String(extraData.toArray(), StandardCharsets.UTF_8)) |
||||||
|
.log(); |
||||||
|
return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), true); |
||||||
|
} catch (final IllegalArgumentException invalidJsonRpcParameters) { |
||||||
|
return new JsonRpcErrorResponse( |
||||||
|
requestContext.getRequest().getId(), |
||||||
|
new JsonRpcError(RpcErrorType.INVALID_PARAMS, invalidJsonRpcParameters.getMessage())); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,62 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.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.methods.miner; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
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.response.JsonRpcResponse; |
||||||
|
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; |
||||||
|
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters; |
||||||
|
import org.hyperledger.besu.ethereum.core.MiningParameters; |
||||||
|
|
||||||
|
import org.apache.tuweni.bytes.Bytes; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
public class MinerGetExtraDataTest { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldReturnDefaultExtraData() { |
||||||
|
final MiningParameters miningParameters = ImmutableMiningParameters.newDefault(); |
||||||
|
final MinerGetExtraData method = new MinerGetExtraData(miningParameters); |
||||||
|
final JsonRpcRequestContext request = |
||||||
|
new JsonRpcRequestContext(new JsonRpcRequest("2.0", method.getName(), new Object[] {})); |
||||||
|
|
||||||
|
final JsonRpcResponse expected = new JsonRpcSuccessResponse(request.getRequest().getId(), "0x"); |
||||||
|
|
||||||
|
final JsonRpcResponse actual = method.response(request); |
||||||
|
assertThat(actual).usingRecursiveComparison().isEqualTo(expected); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldReturnSetAtRuntimeExtraData() { |
||||||
|
final MiningParameters miningParameters = ImmutableMiningParameters.newDefault(); |
||||||
|
final MinerGetExtraData method = new MinerGetExtraData(miningParameters); |
||||||
|
final var extraData = "0x123456"; |
||||||
|
final Bytes extraDataAtRuntime = Bytes.fromHexString(extraData); |
||||||
|
|
||||||
|
miningParameters.setExtraData(extraDataAtRuntime); |
||||||
|
|
||||||
|
final JsonRpcRequestContext request = |
||||||
|
new JsonRpcRequestContext(new JsonRpcRequest("2.0", method.getName(), new Object[] {})); |
||||||
|
|
||||||
|
final JsonRpcResponse expected = |
||||||
|
new JsonRpcSuccessResponse(request.getRequest().getId(), extraData); |
||||||
|
|
||||||
|
final JsonRpcResponse actual = method.response(request); |
||||||
|
assertThat(actual).usingRecursiveComparison().isEqualTo(expected); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,109 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.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.methods.miner; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
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.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.api.jsonrpc.internal.response.RpcErrorType; |
||||||
|
import org.hyperledger.besu.ethereum.core.MiningParameters; |
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
|
||||||
|
import org.apache.tuweni.bytes.Bytes; |
||||||
|
import org.junit.jupiter.api.BeforeEach; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
public class MinerSetExtraDataTest { |
||||||
|
MiningParameters miningParameters = MiningParameters.newDefault(); |
||||||
|
private MinerSetExtraData method; |
||||||
|
|
||||||
|
@BeforeEach |
||||||
|
public void setUp() { |
||||||
|
method = new MinerSetExtraData(miningParameters); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldChangeExtraData() { |
||||||
|
final String newExtraData = " pippo 🐐 | "; |
||||||
|
final var request = |
||||||
|
request(Bytes.wrap(newExtraData.getBytes(StandardCharsets.UTF_8)).toHexString()); |
||||||
|
final JsonRpcResponse expected = new JsonRpcSuccessResponse(request.getRequest().getId(), true); |
||||||
|
|
||||||
|
final JsonRpcResponse actual = method.response(request); |
||||||
|
assertThat(actual).usingRecursiveComparison().isEqualTo(expected); |
||||||
|
final var currExtraData = miningParameters.getExtraData(); |
||||||
|
assertThat(new String(currExtraData.toArray(), StandardCharsets.UTF_8)).isEqualTo(newExtraData); |
||||||
|
} |
||||||
|
|
||||||
|
private JsonRpcRequestContext request(final String newExtraData) { |
||||||
|
return new JsonRpcRequestContext( |
||||||
|
new JsonRpcRequest("2.0", method.getName(), new Object[] {newExtraData})); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldNotTrimLeadingZeros() { |
||||||
|
final var zeroPrefixedExtraData = "0010203"; |
||||||
|
final var request = request(zeroPrefixedExtraData); |
||||||
|
|
||||||
|
final JsonRpcResponse expected = new JsonRpcSuccessResponse(request.getRequest().getId(), true); |
||||||
|
|
||||||
|
final JsonRpcResponse actual = method.response(request); |
||||||
|
assertThat(actual).usingRecursiveComparison().isEqualTo(expected); |
||||||
|
final var currExtraData = miningParameters.getExtraData(); |
||||||
|
assertThat(new String(currExtraData.toArray(), StandardCharsets.UTF_8)) |
||||||
|
.isEqualTo( |
||||||
|
new String( |
||||||
|
Bytes.fromHexStringLenient(zeroPrefixedExtraData).toArray(), |
||||||
|
StandardCharsets.UTF_8)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldReturnErrorWhenExtraDataNotHex() { |
||||||
|
final var request = request("not hex string"); |
||||||
|
|
||||||
|
final JsonRpcResponse expected = |
||||||
|
new JsonRpcErrorResponse( |
||||||
|
request.getRequest().getId(), |
||||||
|
new JsonRpcError( |
||||||
|
RpcErrorType.INVALID_PARAMS, |
||||||
|
"Illegal character 'n' found at index 0 in hex binary representation")); |
||||||
|
|
||||||
|
final JsonRpcResponse actual = method.response(request); |
||||||
|
assertThat(actual).usingRecursiveComparison().isEqualTo(expected); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldReturnErrorWhenExtraDataTooLong() { |
||||||
|
final var tooLongExtraData = "shouldReturnFalseWhenExtraDataTooLong"; |
||||||
|
final var request = |
||||||
|
request(Bytes.wrap(tooLongExtraData.getBytes(StandardCharsets.UTF_8)).toHexString()); |
||||||
|
|
||||||
|
final JsonRpcResponse expected = |
||||||
|
new JsonRpcErrorResponse( |
||||||
|
request.getRequest().getId(), |
||||||
|
new JsonRpcError( |
||||||
|
RpcErrorType.INVALID_PARAMS, |
||||||
|
"Hex value is too large: expected at most 32 bytes but got 37")); |
||||||
|
|
||||||
|
final JsonRpcResponse actual = method.response(request); |
||||||
|
assertThat(actual).usingRecursiveComparison().isEqualTo(expected); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue