mirror of https://github.com/hyperledger/besu
Keccak256 mining (#1882)
* Decouple PoW from ethash Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Address code review comments, create a dev network for ecip1049, prepare for keccak hasher Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Add PoW function and a few simple tests as test vectors Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Make the PoWHasher hash function a bit easier to understand Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * simplify and call out the code of the keccak hash function Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * support fixed difficulty for keccak mining Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Fix the dev network config Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Add comment to KeccakHasher Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Increase fixed difficulty for the ecip1049 dev network to produce hashes a bit less often Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * spotless Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * fix test expectations Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Fix javadoc issue Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * add acceptance test using keccak mining Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * add changelog entry Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Address code review comments Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com>pull/1985/head
parent
a729dbd774
commit
db23aef122
@ -0,0 +1,67 @@ |
|||||||
|
/* |
||||||
|
* 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.tests.acceptance.mining; |
||||||
|
|
||||||
|
import org.hyperledger.besu.cli.config.NetworkName; |
||||||
|
import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; |
||||||
|
import org.hyperledger.besu.tests.acceptance.dsl.account.Account; |
||||||
|
import org.hyperledger.besu.tests.acceptance.dsl.node.Node; |
||||||
|
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.BesuNodeConfigurationBuilder; |
||||||
|
|
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
public class KeccakMiningAcceptanceTest extends AcceptanceTestBase { |
||||||
|
|
||||||
|
private Node minerNode; |
||||||
|
|
||||||
|
@Before |
||||||
|
public void setUp() throws Exception { |
||||||
|
minerNode = |
||||||
|
besu.create( |
||||||
|
new BesuNodeConfigurationBuilder() |
||||||
|
.name("miner1") |
||||||
|
.devMode(false) |
||||||
|
.network(NetworkName.ECIP1049_DEV) |
||||||
|
.miningEnabled() |
||||||
|
.jsonRpcEnabled() |
||||||
|
.webSocketEnabled() |
||||||
|
.build()); |
||||||
|
cluster.start(minerNode); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldMineTransactions() { |
||||||
|
final Account sender = accounts.createAccount("account1"); |
||||||
|
final Account receiver = accounts.createAccount("account2"); |
||||||
|
minerNode.execute(accountTransactions.createTransfer(sender, 50)); |
||||||
|
cluster.verify(sender.balanceEquals(50)); |
||||||
|
|
||||||
|
minerNode.execute(accountTransactions.createIncrementalTransfers(sender, receiver, 1)); |
||||||
|
cluster.verify(receiver.balanceEquals(1)); |
||||||
|
|
||||||
|
minerNode.execute(accountTransactions.createIncrementalTransfers(sender, receiver, 2)); |
||||||
|
cluster.verify(receiver.balanceEquals(3)); |
||||||
|
|
||||||
|
minerNode.execute(accountTransactions.createIncrementalTransfers(sender, receiver, 3)); |
||||||
|
cluster.verify(receiver.balanceEquals(6)); |
||||||
|
|
||||||
|
minerNode.execute(accountTransactions.createIncrementalTransfers(sender, receiver, 4)); |
||||||
|
cluster.verify(receiver.balanceEquals(10)); |
||||||
|
|
||||||
|
minerNode.execute(accountTransactions.createIncrementalTransfers(sender, receiver, 5)); |
||||||
|
cluster.verify(receiver.balanceEquals(15)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
import java.util.OptionalLong; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode; |
||||||
|
import com.google.common.collect.ImmutableMap; |
||||||
|
|
||||||
|
public class Keccak256ConfigOptions { |
||||||
|
|
||||||
|
public static final Keccak256ConfigOptions DEFAULT = |
||||||
|
new Keccak256ConfigOptions(JsonUtil.createEmptyObjectNode()); |
||||||
|
|
||||||
|
private final ObjectNode keccak256ConfigRoot; |
||||||
|
|
||||||
|
Keccak256ConfigOptions(final ObjectNode keccak256ConfigRoot) { |
||||||
|
this.keccak256ConfigRoot = keccak256ConfigRoot; |
||||||
|
} |
||||||
|
|
||||||
|
public OptionalLong getFixedDifficulty() { |
||||||
|
return JsonUtil.getLong(keccak256ConfigRoot, "fixeddifficulty"); |
||||||
|
} |
||||||
|
|
||||||
|
Map<String, Object> asMap() { |
||||||
|
final ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder(); |
||||||
|
getFixedDifficulty().ifPresent(l -> builder.put("fixeddifficulty", l)); |
||||||
|
return builder.build(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
/** An enumeration of supported Proof-of-work algorithms. */ |
||||||
|
public enum PowAlgorithm { |
||||||
|
UNSUPPORTED, |
||||||
|
ETHASH, |
||||||
|
KECCAK256; |
||||||
|
|
||||||
|
public static PowAlgorithm fromString(final String str) { |
||||||
|
for (final PowAlgorithm powAlgorithm : PowAlgorithm.values()) { |
||||||
|
if (powAlgorithm.name().equalsIgnoreCase(str)) { |
||||||
|
return powAlgorithm; |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
{ |
||||||
|
"config": { |
||||||
|
"chainId": 2021, |
||||||
|
"ecip1049Block": 0, |
||||||
|
"keccak256": { |
||||||
|
"fixeddifficulty": 10000 |
||||||
|
} |
||||||
|
}, |
||||||
|
"nonce": "0x42", |
||||||
|
"timestamp": "0x0", |
||||||
|
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", |
||||||
|
"gasLimit": "0x1fffffffffffff", |
||||||
|
"difficulty": "0x1000000", |
||||||
|
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", |
||||||
|
"coinbase": "0x0000000000000000000000000000000000000000", |
||||||
|
"alloc": { |
||||||
|
"fe3b557e8fb62b89f4916b721be55ceb828dbd73": { |
||||||
|
"privateKey": "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", |
||||||
|
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored", |
||||||
|
"balance": "0xad78ebc5ac6200000" |
||||||
|
}, |
||||||
|
"627306090abaB3A6e1400e9345bC60c78a8BEf57": { |
||||||
|
"privateKey": "c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3", |
||||||
|
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored", |
||||||
|
"balance": "90000000000000000000000" |
||||||
|
}, |
||||||
|
"f17f52151EbEF6C7334FAD080c5704D77216b732": { |
||||||
|
"privateKey": "ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f", |
||||||
|
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored", |
||||||
|
"balance": "90000000000000000000000" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
/* |
||||||
|
* 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.mainnet; |
||||||
|
|
||||||
|
import org.hyperledger.besu.ethereum.core.Hash; |
||||||
|
|
||||||
|
import java.security.MessageDigest; |
||||||
|
|
||||||
|
import org.apache.tuweni.bytes.Bytes; |
||||||
|
import org.apache.tuweni.bytes.Bytes32; |
||||||
|
import org.bouncycastle.jcajce.provider.digest.Keccak; |
||||||
|
|
||||||
|
/** |
||||||
|
* Hasher for Keccak-256 PoW. |
||||||
|
* |
||||||
|
* <p>Block is valid if keccak256(keccak256(rlp(unsealed header)), nonce) is less than or equal to |
||||||
|
* 2^256 / difficulty mixhash = keccak256(rlp(unsealed header)) which is stored in the block. |
||||||
|
* Currently this process is ethash(rlp(unsealed header)) So to validate a block we check |
||||||
|
* keccak256(mixhash, nonce) is less than or equal to 2^256 / difficulty |
||||||
|
*/ |
||||||
|
public class KeccakHasher implements PoWHasher { |
||||||
|
|
||||||
|
public static final KeccakHasher KECCAK256 = new KeccakHasher(); |
||||||
|
|
||||||
|
private KeccakHasher() {} |
||||||
|
|
||||||
|
private static final ThreadLocal<MessageDigest> KECCAK_256 = |
||||||
|
ThreadLocal.withInitial(Keccak.Digest256::new); |
||||||
|
|
||||||
|
@Override |
||||||
|
public PoWSolution hash( |
||||||
|
final long nonce, |
||||||
|
final long number, |
||||||
|
final EpochCalculator epochCalc, |
||||||
|
final Bytes prePowHash) { |
||||||
|
|
||||||
|
MessageDigest digest = KECCAK_256.get(); |
||||||
|
digest.update(prePowHash.toArrayUnsafe()); |
||||||
|
digest.update(Bytes.ofUnsignedLong(nonce).toArrayUnsafe()); |
||||||
|
Bytes32 solution = Bytes32.wrap(digest.digest()); |
||||||
|
Hash mixHash = Hash.wrap(solution); |
||||||
|
|
||||||
|
return new PoWSolution(nonce, mixHash, solution, prePowHash); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,120 @@ |
|||||||
|
/* |
||||||
|
* 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.mainnet; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
import org.hyperledger.besu.ethereum.core.Address; |
||||||
|
import org.hyperledger.besu.ethereum.core.Difficulty; |
||||||
|
import org.hyperledger.besu.ethereum.core.Hash; |
||||||
|
import org.hyperledger.besu.ethereum.core.LogsBloomFilter; |
||||||
|
import org.hyperledger.besu.ethereum.core.SealableBlockHeader; |
||||||
|
|
||||||
|
import org.apache.tuweni.bytes.Bytes; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
public class KeccakHasherTest { |
||||||
|
|
||||||
|
private static class KeccakSealableBlockHeader extends SealableBlockHeader { |
||||||
|
|
||||||
|
protected KeccakSealableBlockHeader( |
||||||
|
final Hash parentHash, |
||||||
|
final Hash ommersHash, |
||||||
|
final Address coinbase, |
||||||
|
final Hash stateRoot, |
||||||
|
final Hash transactionsRoot, |
||||||
|
final Hash receiptsRoot, |
||||||
|
final LogsBloomFilter logsBloom, |
||||||
|
final Difficulty difficulty, |
||||||
|
final long number, |
||||||
|
final long gasLimit, |
||||||
|
final long gasUsed, |
||||||
|
final long timestamp, |
||||||
|
final Bytes extraData, |
||||||
|
final Long baseFee) { |
||||||
|
super( |
||||||
|
parentHash, |
||||||
|
ommersHash, |
||||||
|
coinbase, |
||||||
|
stateRoot, |
||||||
|
transactionsRoot, |
||||||
|
receiptsRoot, |
||||||
|
logsBloom, |
||||||
|
difficulty, |
||||||
|
number, |
||||||
|
gasLimit, |
||||||
|
gasUsed, |
||||||
|
timestamp, |
||||||
|
extraData, |
||||||
|
baseFee); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testHasher() { |
||||||
|
|
||||||
|
PoWSolution solution = |
||||||
|
KeccakHasher.KECCAK256.hash( |
||||||
|
12345678L, |
||||||
|
42L, |
||||||
|
new EpochCalculator.DefaultEpochCalculator(), |
||||||
|
Bytes.fromHexString( |
||||||
|
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")); |
||||||
|
|
||||||
|
assertThat(solution.getMixHash()) |
||||||
|
.isEqualTo( |
||||||
|
Bytes.fromHexString( |
||||||
|
"0xeffd292d6666dba4d6a4c221dd6d4b34b4ec3972a4cb0d944a8a8936cceca713")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testHasherFromBlock() { |
||||||
|
|
||||||
|
KeccakSealableBlockHeader header = |
||||||
|
new KeccakSealableBlockHeader( |
||||||
|
Hash.fromHexString( |
||||||
|
"0xad22d4d8f0e94032cb32e86027e0a5533d945ed95088264e91dd71e4fbaebeda"), |
||||||
|
Hash.fromHexString( |
||||||
|
"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"), |
||||||
|
Address.fromHexString("0x6A9ECfa04e99726eC105517AC7ae1aba550BeA6c"), |
||||||
|
Hash.fromHexString( |
||||||
|
"0x43e3325393fbc583a5a0b56e98073fb81e82d992b52406a79d662b690a4d2753"), |
||||||
|
Hash.fromHexString( |
||||||
|
"0x40c339f7715932ec591d8c0c588bacfaed9bddc7519a1e6e87cf45be639de810"), |
||||||
|
Hash.fromHexString( |
||||||
|
"0xeb1e644436f93be8a9938dfe598cb7fd729f9d201b6f7c0695bee883b3ea6a5b"), |
||||||
|
LogsBloomFilter.fromHexString( |
||||||
|
"0x0800012104000104c00400108000400000003000000040008400000002800100000a00000000000001010401040001000001002000000000020020080000240200000000012260010000084800420200040000100000030800802000112020001a200800020000000000500010100a00000000020401480000000010001048000011104800c002410000000010800000000014200040000400000000000000600020c00000004010080000000020100200000200000800001024c4000000080100004002004808000102920408810000002000008000000008000120400020008200d80000000010010000008028004000010000008220000200000100100800"), |
||||||
|
Difficulty.of(3963642329L), |
||||||
|
4156209L, |
||||||
|
8000000L, |
||||||
|
7987824L, |
||||||
|
1538483791L, |
||||||
|
Bytes.fromHexString("0xd88301080f846765746888676f312e31302e31856c696e7578"), |
||||||
|
null); |
||||||
|
|
||||||
|
PoWSolution result = |
||||||
|
KeccakHasher.KECCAK256.hash( |
||||||
|
Bytes.fromHexString("0xf245822d3412da7f").toLong(), |
||||||
|
4156209L, |
||||||
|
new EpochCalculator.DefaultEpochCalculator(), |
||||||
|
EthHash.hashHeader(header)); |
||||||
|
|
||||||
|
assertThat(result.getMixHash()) |
||||||
|
.isEqualTo( |
||||||
|
Bytes.fromHexString( |
||||||
|
"0xd033f82e170ff16640e902fad569243c39bce9e4da948ccc298c541b34cd263b")); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue