Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EIP-7702 Skip CodeDelegation processing for invalid recid #8212

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();
}
}