Skip to content

Commit

Permalink
EIP-7702 Skip CodeDelegation processing for invalid recid (#8212)
Browse files Browse the repository at this point in the history
In CodeDelegation.authorizer(), return empty to skip code delegation processing if recId outside native lib's bounds

---------

Signed-off-by: Simon Dudley <[email protected]>
Signed-off-by: Daniel Lehrner <[email protected]>
Co-authored-by: Daniel Lehrner <[email protected]>
Co-authored-by: Karim Taam <[email protected]>
  • Loading branch information
3 people authored Feb 3, 2025
1 parent 2412586 commit 47a5efc
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public static org.hyperledger.besu.datatypes.CodeDelegation createCodeDelegation
Bytes.fromHexStringLenient(nonce).toLong(),
SIGNATURE_ALGORITHM
.get()
.createSignature(
.createCodeDelegationSignature(
Bytes.fromHexStringLenient(r).toUnsignedBigInteger(),
Bytes.fromHexStringLenient(s).toUnsignedBigInteger(),
Bytes.fromHexStringLenient(v).get(0)));
Expand All @@ -121,6 +121,12 @@ public SECPSignature signature() {

@Override
public Optional<Address> authorizer() {
// recId needs to be between 0 and 3, otherwise the signature is invalid
// which means we can't recover the authorizer.
if (signature.getRecId() < 0 || signature.getRecId() > 3) {
return Optional.empty();
}

return authorizerSupplier.get();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,6 @@ private void processCodeDelegation(
return;
}

if (codeDelegation.signature().getRecId() != 0 && codeDelegation.signature().getRecId() != 1) {
LOG.trace("Invalid signature for code delegation. RecId must be 0 or 1.");
return;
}

final Optional<Address> authorizer = codeDelegation.authorizer();
if (authorizer.isEmpty()) {
LOG.trace("Invalid signature for code delegation");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* 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.core;

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

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 java.math.BigInteger;
import java.util.Optional;

import org.apache.tuweni.bytes.Bytes32;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

class CodeDelegationTest {

private BigInteger chainId;
private Address address;
private long nonce;
private SECPSignature signature;
private SignatureAlgorithm signatureAlgorithm;

@BeforeEach
void setUp() {
chainId = BigInteger.valueOf(1);
address = Address.fromHexString("0x1234567890abcdef1234567890abcdef12345678");
nonce = 100;

signatureAlgorithm = SignatureAlgorithmFactory.getInstance();
KeyPair keyPair = signatureAlgorithm.generateKeyPair();
signature = signatureAlgorithm.sign(Bytes32.fromHexStringLenient("deadbeef"), keyPair);
}

@Test
void shouldCreateCodeDelegationSuccessfully() {
CodeDelegation delegation = new CodeDelegation(chainId, address, nonce, signature);

assertThat(delegation.chainId()).isEqualTo(chainId);
assertThat(delegation.address()).isEqualTo(address);
assertThat(delegation.nonce()).isEqualTo(nonce);
assertThat(delegation.signature()).isEqualTo(signature);
}

@Test
void shouldBuildCodeDelegationWithBuilder() {
CodeDelegation delegation =
(CodeDelegation)
CodeDelegation.builder()
.chainId(chainId)
.address(address)
.nonce(nonce)
.signature(signature)
.build();

assertThat(delegation).isNotNull();
assertThat(delegation.chainId()).isEqualTo(chainId);
assertThat(delegation.address()).isEqualTo(address);
assertThat(delegation.nonce()).isEqualTo(nonce);
assertThat(delegation.signature()).isEqualTo(signature);
}

@Test
void shouldThrowWhenBuildingWithoutAddress() {
assertThatThrownBy(
() ->
CodeDelegation.builder().chainId(chainId).nonce(nonce).signature(signature).build())
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("Address must be set");
}

@Test
void shouldThrowWhenBuildingWithoutNonce() {
assertThatThrownBy(
() ->
CodeDelegation.builder()
.chainId(chainId)
.address(address)
.signature(signature)
.build())
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("Nonce must be set");
}

@Test
void shouldThrowWhenBuildingWithoutSignature() {
assertThatThrownBy(
() -> CodeDelegation.builder().chainId(chainId).address(address).nonce(nonce).build())
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("Signature must be set");
}

@Test
void shouldCreateCodeDelegationUsingFactoryMethod() {
CodeDelegation delegation =
(CodeDelegation)
CodeDelegation.createCodeDelegation(
chainId, address, "0x64", "0x1b", "0xabcdef", "0x123456");

assertThat(delegation).isNotNull();
assertThat(delegation.chainId()).isEqualTo(chainId);
assertThat(delegation.address()).isEqualTo(address);
assertThat(delegation.nonce()).isEqualTo(100);
}

@Test
void shouldReturnAuthorizerWhenSignatureIsValid() {
CodeDelegation delegation = new CodeDelegation(chainId, address, nonce, signature);

Optional<Address> authorizer = delegation.authorizer();

assertThat(authorizer).isNotEmpty();
}

@Test
void shouldReturnEmptyAuthorizerWhenSignatureInvalid() {
SECPSignature invalidSignature = Mockito.mock(SECPSignature.class);
Mockito.when(invalidSignature.getRecId()).thenReturn((byte) 5); // Invalid recId (>3)

CodeDelegation delegation = new CodeDelegation(chainId, address, nonce, invalidSignature);

Optional<Address> authorizer = delegation.authorizer();

assertThat(authorizer).isEmpty();
}

@Test
void shouldReturnCorrectSignatureValues() {
CodeDelegation delegation = new CodeDelegation(chainId, address, nonce, signature);

assertThat(delegation.v()).isEqualTo(signature.getRecId());
assertThat(delegation.r()).isEqualTo(signature.getR());
assertThat(delegation.s()).isEqualTo(signature.getS());
}

@Test
void shouldSignAndBuildUsingKeyPair() {
KeyPair keyPair = signatureAlgorithm.generateKeyPair();

CodeDelegation delegation =
(CodeDelegation)
CodeDelegation.builder()
.chainId(chainId)
.address(address)
.nonce(nonce)
.signAndBuild(keyPair);

assertThat(delegation).isNotNull();
assertThat(delegation.signature()).isNotNull();
}
}

0 comments on commit 47a5efc

Please sign in to comment.