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