Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 3 additions & 30 deletions src/Nethermind/Nethermind.Taiko/Rpc/TaikoExtendedEthModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,9 @@ public class TaikoExtendedEthModule(
}

/// <summary>
/// Traverses the blockchain backwards to find the last Shasta block of the given Shasta batch ID.
/// Traverses the blockchain backwards to find the last Shasta block of the given batch ID.
/// </summary>
Comment thread
jmadibekov marked this conversation as resolved.
/// <param name="batchId">The batch ID.</param>
/// <returns>The last block ID.</returns>
/// <param name="batchId">The Shasta batch identifier for which to find the last corresponding block.</param>
private UInt256? GetLastBlockByBatchId(UInt256 batchId)
{
Block? currentBlock = blockFinder.Head;
Expand All @@ -96,8 +95,7 @@ public class TaikoExtendedEthModule(
break;
}

UInt256? proposalId = ExtractAnchorV4ProposalId(currentBlock.Transactions[0].Data);

UInt256? proposalId = currentBlock.Header.DecodeShastaProposalID();
if (proposalId is null)
{
return null;
Expand All @@ -118,29 +116,4 @@ private static bool HasAnchorV4Prefix(ReadOnlyMemory<byte> data)
{
return data.Length >= 4 && AnchorV4Selector.AsSpan().SequenceEqual(data.Span[..4]);
}

private static UInt256? ExtractAnchorV4ProposalId(ReadOnlyMemory<byte> data)
{
// Calldata layout: 4-byte selector + ABI-encoded arguments.
// The first 32 bytes hold the offset (relative to args start) where the proposal id is stored.
const int selectorLength = 4;
const int dataLength = 32;

if (data.Length <= selectorLength + dataLength)
{
return null;
}

ReadOnlySpan<byte> args = data.Span[selectorLength..];
var offset = new UInt256(args[..dataLength], true);

// Check if the offset is invalid
if (offset > int.MaxValue || offset + dataLength > args.Length)
{
return null;
}

ReadOnlySpan<byte> proposalIdBytes = args.Slice((int)offset, dataLength);
return new UInt256(proposalIdBytes, true);
}
}
2 changes: 1 addition & 1 deletion src/Nethermind/Nethermind.Taiko/TaikoBlockValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class TaikoBlockValidator(
private static readonly byte[] AnchorSelector = Keccak.Compute("anchor(bytes32,bytes32,uint64,uint32)").Bytes[..4].ToArray();
private static readonly byte[] AnchorV2Selector = Keccak.Compute("anchorV2(uint64,bytes32,uint32,(uint8,uint8,uint32,uint64,uint32))").Bytes[..4].ToArray();
private static readonly byte[] AnchorV3Selector = Keccak.Compute("anchorV3(uint64,bytes32,uint32,(uint8,uint8,uint32,uint64,uint32),bytes32[])").Bytes[..4].ToArray();
public static readonly byte[] AnchorV4Selector = Keccak.Compute("anchorV4((uint48,address,bytes),(uint48,bytes32,bytes32))").Bytes[..4].ToArray();
public static readonly byte[] AnchorV4Selector = Keccak.Compute("anchorV4((uint48,bytes32,bytes32))").Bytes[..4].ToArray();


public static readonly Address GoldenTouchAccount = new("0x0000777735367b36bC9B61C50022d9D0700dB4Ec");
Expand Down
40 changes: 33 additions & 7 deletions src/Nethermind/Nethermind.Taiko/TaikoHeaderHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,44 @@

using System;
using Nethermind.Core;
using Nethermind.Int256;

namespace Nethermind.Taiko;

/// <summary>
/// Helper methods for decoding Taiko block header extraData.
/// Shasta extraData layout: [basefeeSharingPctg (1 byte)][proposalId (6 bytes)]
/// </summary>
internal static class TaikoHeaderHelper
{
public const int ShastaExtraDataMinLen = 2;
public const int ShastaExtraDataBasefeeSharingPctgIndex = 0;
public const int ShastaExtraDataProposalIDIndex = 1;
public const int ShastaExtraDataProposalIDLength = 6;
public const int ShastaExtraDataLen = 1 + ShastaExtraDataProposalIDLength;

public static byte? DecodeOntakeExtraData(this BlockHeader header) => header.ExtraData is { Length: >= 32 } ? Math.Min(header.ExtraData[31], (byte)100) : null;
/// <summary>
/// Decodes Ontake/Pacaya extraData to get basefeeSharingPctg (last byte, capped at 100).
/// </summary>
public static byte? DecodeOntakeExtraData(this BlockHeader header) =>
header.ExtraData is { Length: >= 32 } ? Math.Min(header.ExtraData[31], (byte)100) : null;

// Two bytes encoded in the extra data for Shasta
// - First byte: basefeeSharingPctg
// - Second byte: isLowBondProposal (lowest bit)
// Returns only the basefeeSharingPctg
public static byte? DecodeShastaExtraData(this BlockHeader header) => header.ExtraData is { Length: < ShastaExtraDataMinLen } ? null : header.ExtraData[0];
/// <summary>
/// Decodes Shasta extraData to get basefeeSharingPctg (first byte).
/// </summary>
public static byte? DecodeShastaBasefeeSharingPctg(this BlockHeader header) =>
header.ExtraData is { Length: < ShastaExtraDataLen } ? null : header.ExtraData[ShastaExtraDataBasefeeSharingPctgIndex];
Comment thread
jmadibekov marked this conversation as resolved.

/// <summary>
/// Decodes Shasta extraData to get proposalId (bytes 1-6).
/// </summary>
public static UInt256? DecodeShastaProposalID(this BlockHeader header)
{
if (header.ExtraData is null || header.ExtraData.Length < ShastaExtraDataLen)
{
return null;
}

ReadOnlySpan<byte> proposalIdBytes = header.ExtraData.AsSpan(ShastaExtraDataProposalIDIndex, ShastaExtraDataProposalIDLength);
return new UInt256(proposalIdBytes, true);
}
Comment thread
jmadibekov marked this conversation as resolved.
}
6 changes: 3 additions & 3 deletions src/Nethermind/Nethermind.Taiko/TaikoHeaderValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ protected override bool ValidateExtraData(BlockHeader header, IReleaseSpec spec,
{
var taikoSpec = (ITaikoReleaseSpec)spec;

if (taikoSpec.IsShastaEnabled && header.ExtraData is { Length: < TaikoHeaderHelper.ShastaExtraDataMinLen })
if (taikoSpec.IsShastaEnabled && header.ExtraData is { Length: < TaikoHeaderHelper.ShastaExtraDataLen })
{
error = $"ExtraData must be at least {TaikoHeaderHelper.ShastaExtraDataMinLen} bytes for Shasta, but got {header.ExtraData.Length}";
error = $"ExtraData must be at least {TaikoHeaderHelper.ShastaExtraDataLen} bytes for Shasta, but got {header.ExtraData.Length}";
if (_logger.IsWarn) _logger.Warn($"Invalid block header ({header.Hash}) - {error}");
return false;
}
Expand Down Expand Up @@ -93,7 +93,7 @@ private bool ValidateEip4396Header(BlockHeader header, BlockHeader parent, IRele
ulong parentBlockTime = 0;
if (header.Number > 1)
{
BlockHeader? grandParent = _blockTree?.FindHeader(parent.ParentHash!, BlockTreeLookupOptions.None);
BlockHeader? grandParent = _blockTree?.FindHeader(parent.ParentHash!, BlockTreeLookupOptions.None, blockNumber: parent.Number - 1);
if (grandParent is null)
{
error = $"Ancestor block not found for parent {parent.ParentHash}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ protected override void PayFees(Transaction tx, BlockHeader header, IReleaseSpec
var taikoSpec = (ITaikoReleaseSpec)spec;
if (taikoSpec.IsOntakeEnabled || taikoSpec.IsShastaEnabled)
{
byte basefeeSharingPct = (taikoSpec.IsShastaEnabled ? header.DecodeShastaExtraData() : header.DecodeOntakeExtraData()) ?? 0;
byte basefeeSharingPct = (taikoSpec.IsShastaEnabled ? header.DecodeShastaBasefeeSharingPctg() : header.DecodeOntakeExtraData()) ?? 0;

UInt256 feeCoinbase = baseFees * basefeeSharingPct / 100;

Expand Down