mirror of https://github.com/hyperledger/besu
commit
c08ad23b3b
@ -0,0 +1,35 @@ |
||||
--- |
||||
name: Release Checklist |
||||
about: items to be completed for each release |
||||
title: '' |
||||
labels: '' |
||||
assignees: '' |
||||
|
||||
--- |
||||
|
||||
- [ ] Confirm anything outstanding for release with other maintainers on #besu-release in Discord |
||||
- [ ] Notify maintainers about updating changelog for in-flight PRs |
||||
- [ ] Update changelog if necessary, and merge a PR for it to main |
||||
- [ ] Optional: for hotfixes, create a release branch and cherry-pick, e.g. `release-<version>-hotfix` |
||||
- [ ] Optional: create a PR into main from the hotfix branch to see the CI checks pass |
||||
- [ ] On the appropriate branch/commit, create a calver tag for the release candidate, format example: `24.4.0-RC2` |
||||
- [ ] Sign-off with team; confirm tag is correct in #besu-release in Discord |
||||
- [ ] Consensys staff start burn-in using the proposed release <version-RCX> tag |
||||
- [ ] Sign off burn-in; convey burn-in results in #besu-release in Discord |
||||
- [ ] Using the same git sha, create a calver tag for the FULL RELEASE, example format `24.4.0` |
||||
- [ ] Using the FULL RELEASE tag, create a release in github to trigger the workflows. Once published: |
||||
- makes the release "latest" in github |
||||
- this is now public and notifies subscribed users |
||||
- publishes artefacts and version-specific docker tags |
||||
- publishes the docker `latest` tag variants |
||||
- [ ] Draft homebrew PR |
||||
- [ ] Draft documentation release |
||||
- [ ] Ensure binary SHAs are correct on the release page |
||||
- [ ] Docker release startup test: |
||||
- `docker run hyperledger/besu:<version>` |
||||
- `docker run hyperledger/besu:<version>-arm64` |
||||
- `docker run --platform linux/amd64 hyperledger/besu:<version>-amd64` |
||||
- `docker run --pull=always hyperledger/besu:latest` (check version is <version>) |
||||
- [ ] Merge homebrew PR |
||||
- [ ] Publish Docs Release |
||||
- [ ] Social announcements |
@ -1,102 +0,0 @@ |
||||
/* |
||||
* 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.evm.operation; |
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedToLong; |
||||
|
||||
import org.hyperledger.besu.datatypes.Address; |
||||
import org.hyperledger.besu.datatypes.Wei; |
||||
import org.hyperledger.besu.evm.EVM; |
||||
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; |
||||
import org.hyperledger.besu.evm.frame.MessageFrame; |
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator; |
||||
import org.hyperledger.besu.evm.internal.Words; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes32; |
||||
|
||||
/** Introduced via EIP-3074 to call another contract with a different authorization context. */ |
||||
public class AuthCallOperation extends AbstractCallOperation { |
||||
|
||||
/** |
||||
* Instantiates a new AuthCallOperation. |
||||
* |
||||
* @param gasCalculator a Prague or later gas calculator |
||||
*/ |
||||
public AuthCallOperation(final GasCalculator gasCalculator) { |
||||
super(0xF7, "AUTHCALL", 7, 1, gasCalculator); |
||||
} |
||||
|
||||
@Override |
||||
protected Address to(final MessageFrame frame) { |
||||
return Words.toAddress(frame.getStackItem(1)); |
||||
} |
||||
|
||||
@Override |
||||
protected Wei value(final MessageFrame frame) { |
||||
return Wei.wrap(frame.getStackItem(2)); |
||||
} |
||||
|
||||
@Override |
||||
protected Wei apparentValue(final MessageFrame frame) { |
||||
return value(frame); |
||||
} |
||||
|
||||
@Override |
||||
protected long inputDataOffset(final MessageFrame frame) { |
||||
return clampedToLong(frame.getStackItem(3)); |
||||
} |
||||
|
||||
@Override |
||||
protected long inputDataLength(final MessageFrame frame) { |
||||
return clampedToLong(frame.getStackItem(4)); |
||||
} |
||||
|
||||
@Override |
||||
protected long outputDataOffset(final MessageFrame frame) { |
||||
return clampedToLong(frame.getStackItem(5)); |
||||
} |
||||
|
||||
@Override |
||||
protected long outputDataLength(final MessageFrame frame) { |
||||
return clampedToLong(frame.getStackItem(6)); |
||||
} |
||||
|
||||
@Override |
||||
protected Address address(final MessageFrame frame) { |
||||
return to(frame); |
||||
} |
||||
|
||||
@Override |
||||
protected Address sender(final MessageFrame frame) { |
||||
return frame.getAuthorizedBy(); |
||||
} |
||||
|
||||
@Override |
||||
public long gasAvailableForChildCall(final MessageFrame frame) { |
||||
return gasCalculator().gasAvailableForChildCall(frame, gas(frame), !value(frame).isZero()); |
||||
} |
||||
|
||||
@Override |
||||
public OperationResult execute(final MessageFrame frame, final EVM evm) { |
||||
if (frame.isStatic() && !value(frame).isZero()) { |
||||
return new OperationResult(cost(frame, true), ExceptionalHaltReason.ILLEGAL_STATE_CHANGE); |
||||
} else if (frame.getAuthorizedBy() != null) { |
||||
return super.execute(frame, evm); |
||||
} else { |
||||
frame.pushStackItem(Bytes32.ZERO); |
||||
return new OperationResult(cost(frame, true), null); |
||||
} |
||||
} |
||||
} |
@ -1,118 +0,0 @@ |
||||
/* |
||||
* 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.evm.operation; |
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedToLong; |
||||
|
||||
import org.hyperledger.besu.crypto.Hash; |
||||
import org.hyperledger.besu.crypto.SECPPublicKey; |
||||
import org.hyperledger.besu.crypto.SECPSignature; |
||||
import org.hyperledger.besu.crypto.SignatureAlgorithm; |
||||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; |
||||
import org.hyperledger.besu.datatypes.Address; |
||||
import org.hyperledger.besu.evm.EVM; |
||||
import org.hyperledger.besu.evm.account.Account; |
||||
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; |
||||
import org.hyperledger.besu.evm.frame.MessageFrame; |
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator; |
||||
import org.hyperledger.besu.evm.internal.Words; |
||||
|
||||
import java.util.Optional; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.apache.tuweni.bytes.Bytes32; |
||||
import org.apache.tuweni.units.bigints.UInt256; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
/** The AUTH operation. */ |
||||
public class AuthOperation extends AbstractOperation { |
||||
|
||||
/** The constant MAGIC defined by EIP-3074 */ |
||||
public static final byte MAGIC = 0x4; |
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AuthOperation.class); |
||||
|
||||
private static final SignatureAlgorithm signatureAlgorithm = |
||||
SignatureAlgorithmFactory.getInstance(); |
||||
|
||||
/** |
||||
* Instantiates a new AuthOperation. |
||||
* |
||||
* @param gasCalculator a Prague or later gas calculator |
||||
*/ |
||||
public AuthOperation(final GasCalculator gasCalculator) { |
||||
super(0xF6, "AUTH", 3, 1, gasCalculator); |
||||
} |
||||
|
||||
@Override |
||||
public OperationResult execute(final MessageFrame frame, final EVM evm) { |
||||
// create authority from stack
|
||||
Address authority = Words.toAddress(frame.getStackItem(0)); |
||||
long offset = clampedToLong(frame.getStackItem(1)); |
||||
long length = clampedToLong(frame.getStackItem(2)); |
||||
|
||||
final long gasCost = |
||||
super.gasCalculator().authOperationGasCost(frame, offset, length, authority); |
||||
if (frame.getRemainingGas() < gasCost) { |
||||
return new OperationResult(gasCost, ExceptionalHaltReason.INSUFFICIENT_GAS); |
||||
} |
||||
|
||||
byte yParity = frame.readMemory(offset, 1).get(0); |
||||
Bytes32 r = Bytes32.wrap(frame.readMemory(offset + 1, 32)); |
||||
Bytes32 s = Bytes32.wrap(frame.readMemory(offset + 33, 32)); |
||||
Bytes32 commit = Bytes32.wrap(frame.readMemory(offset + 65, 32)); |
||||
Bytes32 invoker = Bytes32.leftPad(frame.getContractAddress()); |
||||
// TODO add test for getting sender nonce when account does not exist
|
||||
Bytes32 senderNonce = |
||||
Bytes32.leftPad( |
||||
Bytes.ofUnsignedLong( |
||||
Optional.ofNullable(frame.getWorldUpdater().getAccount(authority)) |
||||
.map(Account::getNonce) |
||||
.orElse(0L))); |
||||
if (evm.getChainId().isEmpty()) { |
||||
frame.pushStackItem(UInt256.ZERO); |
||||
LOG.error("ChainId is not set"); |
||||
return new OperationResult(0, null); |
||||
} |
||||
Bytes authPreImage = |
||||
Bytes.concatenate( |
||||
Bytes.ofUnsignedShort(MAGIC), evm.getChainId().get(), senderNonce, invoker, commit); |
||||
Bytes32 messageHash = Hash.keccak256(authPreImage); |
||||
Optional<SECPPublicKey> publicKey; |
||||
try { |
||||
SECPSignature signature = |
||||
signatureAlgorithm.createSignature( |
||||
r.toUnsignedBigInteger(), s.toUnsignedBigInteger(), yParity); |
||||
publicKey = signatureAlgorithm.recoverPublicKeyFromSignature(messageHash, signature); |
||||
} catch (IllegalArgumentException e) { |
||||
|
||||
frame.pushStackItem(UInt256.ZERO); |
||||
return new OperationResult(gasCost, null); |
||||
} |
||||
if (publicKey.isPresent()) { |
||||
Address signerAddress = Address.extract(publicKey.get()); |
||||
if (signerAddress.equals(authority)) { |
||||
frame.setAuthorizedBy(authority); |
||||
frame.pushStackItem(UInt256.ONE); |
||||
} else { |
||||
frame.pushStackItem(UInt256.ZERO); |
||||
} |
||||
} else { |
||||
frame.pushStackItem(UInt256.ZERO); |
||||
} |
||||
return new OperationResult(gasCost, null); |
||||
} |
||||
} |
@ -1,153 +0,0 @@ |
||||
/* |
||||
* 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.evm.operations; |
||||
|
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.never; |
||||
import static org.mockito.Mockito.verify; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import org.hyperledger.besu.crypto.Hash; |
||||
import org.hyperledger.besu.crypto.KeyPair; |
||||
import org.hyperledger.besu.crypto.SECPSignature; |
||||
import org.hyperledger.besu.crypto.SignatureAlgorithm; |
||||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; |
||||
import org.hyperledger.besu.datatypes.Address; |
||||
import org.hyperledger.besu.evm.EVM; |
||||
import org.hyperledger.besu.evm.account.MutableAccount; |
||||
import org.hyperledger.besu.evm.frame.MessageFrame; |
||||
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator; |
||||
import org.hyperledger.besu.evm.operation.AuthOperation; |
||||
import org.hyperledger.besu.evm.worldstate.WorldUpdater; |
||||
|
||||
import java.util.Optional; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.apache.tuweni.bytes.Bytes32; |
||||
import org.apache.tuweni.units.bigints.UInt256; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
public class AuthOperationTest { |
||||
|
||||
@Test |
||||
public void testAuthOperation() { |
||||
SignatureAlgorithm algo = SignatureAlgorithmFactory.getInstance(); |
||||
KeyPair keys = algo.generateKeyPair(); |
||||
Address authingAddress = Address.extract(keys.getPublicKey()); |
||||
EVM fakeEVM = mock(EVM.class); |
||||
|
||||
Optional<Bytes> chainId = Optional.of(Bytes.of(1)); |
||||
when(fakeEVM.getChainId()).thenReturn(chainId); |
||||
long senderNonce = 0; |
||||
Address invokerAddress = Address.fromHexString("0xdeadbeef"); |
||||
Bytes32 invoker = Bytes32.leftPad(invokerAddress); |
||||
Bytes32 contractCommitment = Bytes32.leftPad(Bytes.fromHexString("0x1234")); |
||||
Bytes authPreImage = |
||||
Bytes.concatenate( |
||||
Bytes.ofUnsignedShort(AuthOperation.MAGIC), |
||||
chainId.get(), |
||||
Bytes32.leftPad(Bytes.ofUnsignedLong(senderNonce)), |
||||
invoker, |
||||
contractCommitment); |
||||
Bytes32 messageHash = Hash.keccak256(authPreImage); |
||||
SECPSignature signature = algo.sign(messageHash, keys); |
||||
|
||||
MessageFrame frame = mock(MessageFrame.class); |
||||
when(frame.getContractAddress()).thenReturn(invokerAddress); |
||||
MutableAccount authingAccount = mock(MutableAccount.class); |
||||
when(authingAccount.getAddress()).thenReturn(authingAddress); |
||||
when(authingAccount.getNonce()).thenReturn(senderNonce); |
||||
when(frame.getRemainingGas()).thenReturn(1000000L); |
||||
WorldUpdater state = mock(WorldUpdater.class); |
||||
|
||||
when(state.getAccount(authingAddress)).thenReturn(authingAccount); |
||||
|
||||
when(frame.getWorldUpdater()).thenReturn(state); |
||||
|
||||
when(frame.getSenderAddress()).thenReturn(authingAddress); |
||||
when(state.getSenderAccount(frame)).thenReturn(authingAccount); |
||||
when(frame.getStackItem(0)).thenReturn(authingAddress); |
||||
when(frame.getStackItem(1)).thenReturn(Bytes.of(0)); |
||||
when(frame.getStackItem(2)).thenReturn(Bytes.of(97)); |
||||
Bytes encodedSignature = signature.encodedBytes(); |
||||
when(frame.readMemory(0, 1)).thenReturn(encodedSignature.slice(64, 1)); |
||||
when(frame.readMemory(1, 32)).thenReturn(Bytes32.wrap(encodedSignature.slice(0, 32).toArray())); |
||||
when(frame.readMemory(33, 32)) |
||||
.thenReturn(Bytes32.wrap(encodedSignature.slice(32, 32).toArray())); |
||||
when(frame.readMemory(65, 32)).thenReturn(contractCommitment); |
||||
|
||||
AuthOperation authOperation = new AuthOperation(new PragueGasCalculator()); |
||||
authOperation.execute(frame, fakeEVM); |
||||
verify(frame).setAuthorizedBy(authingAddress); |
||||
verify(frame).pushStackItem(UInt256.ONE); |
||||
} |
||||
|
||||
@Test |
||||
public void testAuthOperationNegative() { |
||||
SignatureAlgorithm algo = SignatureAlgorithmFactory.getInstance(); |
||||
KeyPair keys = algo.generateKeyPair(); |
||||
Address authingAddress = Address.extract(keys.getPublicKey()); |
||||
EVM fakeEVM = mock(EVM.class); |
||||
|
||||
Optional<Bytes> chainId = Optional.of(Bytes.of(1)); |
||||
when(fakeEVM.getChainId()).thenReturn(chainId); |
||||
long senderNonce = 0; |
||||
Address invokerAddress = Address.fromHexString("0xdeadbeef"); |
||||
Bytes32 invoker = Bytes32.leftPad(invokerAddress); |
||||
Bytes32 contractCommitment = Bytes32.leftPad(Bytes.fromHexString("0x1234")); |
||||
Bytes authPreImage = |
||||
Bytes.concatenate( |
||||
Bytes.ofUnsignedShort(AuthOperation.MAGIC), |
||||
chainId.get(), |
||||
Bytes32.leftPad(Bytes.ofUnsignedLong(senderNonce)), |
||||
invoker, |
||||
contractCommitment); |
||||
Bytes32 messageHash = Hash.keccak256(authPreImage); |
||||
|
||||
// Generate a new key pair to create an incorrect signature
|
||||
KeyPair wrongKeys = algo.generateKeyPair(); |
||||
SECPSignature wrongSignature = algo.sign(messageHash, wrongKeys); |
||||
|
||||
MessageFrame frame = mock(MessageFrame.class); |
||||
when(frame.getRemainingGas()).thenReturn(1000000L); |
||||
when(frame.getContractAddress()).thenReturn(invokerAddress); |
||||
MutableAccount authingAccount = mock(MutableAccount.class); |
||||
when(authingAccount.getAddress()).thenReturn(authingAddress); |
||||
when(authingAccount.getNonce()).thenReturn(senderNonce); |
||||
|
||||
WorldUpdater state = mock(WorldUpdater.class); |
||||
|
||||
when(state.getAccount(authingAddress)).thenReturn(authingAccount); |
||||
|
||||
when(frame.getWorldUpdater()).thenReturn(state); |
||||
|
||||
when(frame.getSenderAddress()).thenReturn(authingAddress); |
||||
when(state.getSenderAccount(frame)).thenReturn(authingAccount); |
||||
when(frame.getStackItem(0)).thenReturn(authingAddress); |
||||
when(frame.getStackItem(1)).thenReturn(Bytes.of(0)); |
||||
when(frame.getStackItem(2)).thenReturn(Bytes.of(97)); |
||||
Bytes encodedSignature = wrongSignature.encodedBytes(); // Use the wrong signature
|
||||
when(frame.readMemory(0, 1)).thenReturn(encodedSignature.slice(64, 1)); |
||||
when(frame.readMemory(1, 32)).thenReturn(Bytes32.wrap(encodedSignature.slice(0, 32).toArray())); |
||||
when(frame.readMemory(33, 32)) |
||||
.thenReturn(Bytes32.wrap(encodedSignature.slice(32, 32).toArray())); |
||||
when(frame.readMemory(65, 32)).thenReturn(contractCommitment); |
||||
|
||||
AuthOperation authOperation = new AuthOperation(new PragueGasCalculator()); |
||||
authOperation.execute(frame, fakeEVM); |
||||
verify(frame, never()).setAuthorizedBy(authingAddress); // The address should not be authorized
|
||||
verify(frame).pushStackItem(UInt256.ZERO); // The stack should contain UInt256.ZERO
|
||||
} |
||||
} |
Loading…
Reference in new issue