Skip to content

Commit

Permalink
Implement linea_setExtraData (#19)
Browse files Browse the repository at this point in the history
* Implement linea_setExtraData

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>

* Fix and test

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>

* Apply suggestions from code review

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>

* Apply suggestions from code review

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>

---------

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
  • Loading branch information
fab-10 committed Jun 21, 2024
1 parent b368c08 commit ea317ab
Show file tree
Hide file tree
Showing 14 changed files with 675 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,24 @@
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import linea.plugin.acc.test.tests.web3j.generated.SimpleStorage;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt32;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
Expand All @@ -38,6 +46,10 @@
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
import org.hyperledger.besu.tests.acceptance.dsl.condition.txpool.TxPoolConditions;
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;
import org.hyperledger.besu.tests.acceptance.dsl.node.RunnableNode;
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.BesuNodeConfigurationBuilder;
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.NodeConfigurationFactory;
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationFactory;
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationFactory.CliqueOptions;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolTransactions;
import org.junit.jupiter.api.AfterEach;
Expand Down Expand Up @@ -66,7 +78,7 @@ public class LineaPluginTestBase extends AcceptanceTestBase {
@BeforeEach
public void setup() throws Exception {
minerNode =
besu.createCliqueNodeWithExtraCliOptionsAndRpcApis(
createCliqueNodeWithExtraCliOptionsAndRpcApis(
"miner1", LINEA_CLIQUE_OPTIONS, getTestCliOptions(), Set.of("LINEA", "MINER"));
minerNode.setTransactionPoolConfiguration(
ImmutableTransactionPoolConfiguration.builder()
Expand All @@ -86,6 +98,58 @@ public void stop() {
cluster.close();
}

protected Optional<Bytes32> maybeCustomGenesisExtraData() {
return Optional.empty();
}

private BesuNode createCliqueNodeWithExtraCliOptionsAndRpcApis(
final String name,
final CliqueOptions cliqueOptions,
final List<String> extraCliOptions,
final Set<String> extraRpcApis)
throws IOException {
final NodeConfigurationFactory node = new NodeConfigurationFactory();

final var nodeConfBuilder =
new BesuNodeConfigurationBuilder()
.name(name)
.miningEnabled()
.jsonRpcConfiguration(node.createJsonRpcWithCliqueEnabledConfig(extraRpcApis))
.webSocketConfiguration(node.createWebSocketEnabledConfig())
.inProcessRpcConfiguration(node.createInProcessRpcConfiguration(extraRpcApis))
.devMode(false)
.jsonRpcTxPool()
.genesisConfigProvider(
validators -> Optional.of(provideGenesisConfig(validators, cliqueOptions)))
.extraCLIOptions(extraCliOptions);

return besu.create(nodeConfBuilder.build());
}

private String provideGenesisConfig(
final Collection<? extends RunnableNode> validators, final CliqueOptions cliqueOptions) {
final var genesis =
GenesisConfigurationFactory.createCliqueGenesisConfig(validators, cliqueOptions).get();

return maybeCustomGenesisExtraData()
.map(ed -> setGenesisCustomExtraData(genesis, ed))
.orElse(genesis);
}

private String setGenesisCustomExtraData(final String genesis, final Bytes32 customExtraData) {
final var om = new ObjectMapper();
final ObjectNode root;
try {
root = (ObjectNode) om.readTree(genesis);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
final var existingExtraData = Bytes.fromHexString(root.get("extraData").asText());
final var updatedExtraData = Bytes.concatenate(customExtraData, existingExtraData.slice(32));
root.put("extraData", updatedExtraData.toHexString());
return root.toPrettyString();
}

protected void sendTransactionsWithGivenLengthPayload(
final SimpleStorage simpleStorage,
final List<String> accounts,
Expand Down Expand Up @@ -225,4 +289,14 @@ protected String sendTransactionWithGivenLengthPayload(
BigInteger.ZERO)
.getTransactionHash();
}

protected Bytes32 createExtraDataPricingField(
final long fixedCostKWei, final long variableCostKWei, final long minGasPriceKWei) {
final UInt32 fixed = UInt32.valueOf(BigInteger.valueOf(fixedCostKWei));
final UInt32 variable = UInt32.valueOf(BigInteger.valueOf(variableCostKWei));
final UInt32 min = UInt32.valueOf(BigInteger.valueOf(minGasPriceKWei));

return Bytes32.rightPad(
Bytes.concatenate(Bytes.of((byte) 1), fixed.toBytes(), variable.toBytes(), min.toBytes()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@

import linea.plugin.acc.test.LineaPluginTestBase;
import linea.plugin.acc.test.TestCommandLineOptionsBuilder;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt32;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
Expand Down Expand Up @@ -62,7 +60,7 @@ public void updateMinGasPriceViaExtraData() {
final var extraData =
createExtraDataPricingField(
0, MIN_GAS_PRICE.toLong() / WEI_IN_KWEI, doubleMinGasPrice.toLong() / WEI_IN_KWEI);
final var reqSetExtraData = new ExtraDataPricingTest.MinerSetExtraDataRequest(extraData);
final var reqSetExtraData = new MinerSetExtraDataRequest(extraData);
final var respSetExtraData = reqSetExtraData.execute(minerNode.nodeRequests());

assertThat(respSetExtraData).isTrue();
Expand Down Expand Up @@ -99,9 +97,9 @@ public void updateProfitabilityParamsViaExtraData() throws IOException {
// when this first tx is mined the above extra data pricing will have effect on following txs
final TransferTransaction profitableTx =
accountTransactions.createTransfer(sender, recipient, 1);
final var protitableTx = minerNode.execute(profitableTx);
final var profitableTxHash = minerNode.execute(profitableTx);

minerNode.verify(eth.expectSuccessfulTransactionReceipt(protitableTx.toHexString()));
minerNode.verify(eth.expectSuccessfulTransactionReceipt(profitableTxHash.toHexString()));

// this tx will be evaluated with the previously set extra data pricing to be unprofitable
final RawTransaction unprofitableTx =
Expand Down Expand Up @@ -149,14 +147,4 @@ public Boolean execute(final NodeRequests nodeRequests) {

static class MinerSetExtraDataResponse extends org.web3j.protocol.core.Response<Boolean> {}
}

private Bytes32 createExtraDataPricingField(
final long fixedCostKWei, final long variableCostKWei, final long minGasPriceKWei) {
final UInt32 fixed = UInt32.valueOf(BigInteger.valueOf(fixedCostKWei));
final UInt32 variable = UInt32.valueOf(BigInteger.valueOf(variableCostKWei));
final UInt32 min = UInt32.valueOf(BigInteger.valueOf(minGasPriceKWei));

return Bytes32.rightPad(
Bytes.concatenate(Bytes.of((byte) 1), fixed.toBytes(), variable.toBytes(), min.toBytes()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright Consensys Software Inc.
*
* Licensed under the Apache License, Version 2.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 linea.plugin.acc.test.extradata;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;
import java.util.Optional;

import linea.plugin.acc.test.LineaPluginTestBase;
import linea.plugin.acc.test.TestCommandLineOptionsBuilder;
import org.apache.tuweni.bytes.Bytes32;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.account.TransferTransaction;
import org.junit.jupiter.api.Test;

public class StartupExtraDataPricingTest extends LineaPluginTestBase {
private static final Wei VARIABLE_GAS_COST = Wei.of(1_200_300_000);
private static final Wei MIN_GAS_PRICE = VARIABLE_GAS_COST.divide(2);
private static final int WEI_IN_KWEI = 1000;

@Override
public List<String> getTestCliOptions() {
return getTestCommandLineOptionsBuilder().build();
}

protected TestCommandLineOptionsBuilder getTestCommandLineOptionsBuilder() {
return new TestCommandLineOptionsBuilder()
.set("--plugin-linea-extra-data-pricing-enabled=", Boolean.TRUE.toString());
}

@Override
protected Optional<Bytes32> maybeCustomGenesisExtraData() {
final var genesisExtraData =
createExtraDataPricingField(
0, VARIABLE_GAS_COST.toLong() / WEI_IN_KWEI, MIN_GAS_PRICE.toLong() / WEI_IN_KWEI);

return Optional.of(genesisExtraData);
}

@Test
public void minGasPriceSetFromChainHeadExtraDataAtStartup() {
// at startup the min gas price should be set from the current chain head block extra data
assertThat(minerNode.getMiningParameters().getMinTransactionGasPrice())
.isEqualTo(MIN_GAS_PRICE);

final Account sender = accounts.getSecondaryBenefactor();
final Account recipient = accounts.createAccount("recipient");

final TransferTransaction transferTx = accountTransactions.createTransfer(sender, recipient, 1);
final var txHash = minerNode.execute(transferTx);

minerNode.verify(eth.expectSuccessfulTransactionReceipt(txHash.toHexString()));
}
}
Loading

0 comments on commit ea317ab

Please sign in to comment.