-
Notifications
You must be signed in to change notification settings - Fork 147
WIP - 2250 - Tx compressor #2382
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
Closed
Closed
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
ccacfe0
WIP - 2250 - Tx compressor
Filter94 1caf329
Reverting unrelated changes
Filter94 2c38e18
Fixed some bugs, added more comparison tests
Filter94 6c4287e
Spotless
Filter94 78e5e79
Test improvement
Filter94 ae0399d
Merge branch 'main' of github.com:Consensys/linea-monorepo into 2250/…
Filter94 0edc9ae
Minor fixes
Filter94 4e06c14
Fixed merge conflict
Filter94 f3bc57a
New compression method and fixes
Filter94 7e75a7d
Added the forgotten header file
Filter94 d3b431d
Fixed generated header
Filter94 118d1e5
Released the compressor and used it in the gradle module
Filter94 b0a6df3
Spotless
Filter94 81a9e32
Merge branch 'main' of github.com:Consensys/linea-monorepo into 2250/…
Filter94 8df7791
Added recompression to CanWrite
Filter94 c160e65
Added dictionaries to the tx compressor
Filter94 041b05a
Added option to disable recompression, changed the interface
Filter94 fab6d47
Regenerated the headers file
Filter94 eeedbd4
Updated the binary
Filter94 bd46fd7
Fixed performance issue in the TxCompressor
Filter94 da45a15
Binaries update
Filter94 1aa945f
Changed the benchmark to use RLP encoded transactions without signatu…
Filter94 6e796f6
Fixed some benchmarks
Filter94 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule constraints
added at
b88da9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| plugins { | ||
| id 'net.consensys.zkevm.kotlin-common-minimal-conventions' | ||
| id 'net.consensys.zkevm.linea-native-libs-helper' | ||
| } | ||
|
|
||
| description = 'Java JNA wrapper for Linea Transaction Compressor Library implemented in GO Lang' | ||
|
|
||
| dependencies { | ||
| compileOnly "net.java.dev.jna:jna:${libs.versions.jna.get()}" | ||
| compileOnly project(":jvm-libs:generic:extensions:kotlin") | ||
| compileOnly "org.apache.logging.log4j:log4j-api:${libs.versions.log4j.get()}" | ||
| compileOnly "org.apache.logging.log4j:log4j-core:${libs.versions.log4j.get()}" | ||
|
|
||
| testImplementation "net.java.dev.jna:jna:${libs.versions.jna.get()}" | ||
| testImplementation project(":jvm-libs:generic:extensions:kotlin") | ||
| testImplementation project(":jvm-libs:linea:blob-compressor") | ||
| testImplementation testFixtures(project(":jvm-libs:linea:blob-compressor")) | ||
| testImplementation project(':jvm-libs:linea:besu-libs') | ||
| testImplementation project(':jvm-libs:linea:besu-rlp-and-mappers') | ||
| } | ||
|
|
||
| jar { | ||
| dependsOn configurations.runtimeClasspath | ||
| } | ||
|
|
||
| test { | ||
| // we cannot have more 1 compressor per JVM, hence we disable parallel execution | ||
| // because multiple threads would cause issues with the native library | ||
| systemProperties["junit.jupiter.execution.parallel.enabled"] = false | ||
| maxParallelForks = 1 | ||
| } | ||
|
|
||
| def libsZipDownloadOutputDir = project.parent.layout.buildDirectory.asFile.get().absolutePath | ||
|
|
||
| task downloadNativeLibs { | ||
| doLast { | ||
| // TODO: Update URL when tx-compressor native libs are released | ||
| // For now, we'll use the same release as blob-compressor since they share the dictionary | ||
| fetchLibFromZip("https://github.com/Consensys/linea-monorepo/releases/download/blob-libs-v2.1.0-rc1/linea-blob-libs-v2.1.0-rc1.zip", "tx_compressor", libsZipDownloadOutputDir) | ||
| } | ||
| } | ||
|
|
||
| compileKotlin { | ||
| dependsOn tasks.downloadNativeLibs | ||
| } | ||
123 changes: 123 additions & 0 deletions
123
jvm-libs/linea/tx-compressor/src/main/kotlin/linea/blob/GoNativeTxCompressor.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| package linea.blob | ||
|
|
||
| import com.sun.jna.Library | ||
| import com.sun.jna.Native | ||
| import linea.jvm.ResourcesUtil.copyResourceToTmpDir | ||
|
|
||
| /** | ||
| * JNA interface for the Go native transaction compressor library. | ||
| * | ||
| * This compressor operates at the transaction level rather than the block level, | ||
| * maintaining compression context across transactions for better compression ratios. | ||
| * It is designed for sequencer block building where transactions are added one by one | ||
| * until the compressed size threshold is reached. | ||
| */ | ||
| interface GoNativeTxCompressor { | ||
|
|
||
| /** | ||
| * TxInit initializes the transaction compressor. | ||
| * | ||
| * @param dataLimit Maximum size of compressed data in bytes. The caller should | ||
| * account for blob overhead (~100 bytes) when setting this limit. | ||
| * @param dictPath Path to the compression dictionary | ||
| * @return true if the compressor was successfully initialized, false otherwise | ||
| */ | ||
| fun TxInit(dataLimit: Int, dictPath: String): Boolean | ||
|
|
||
| /** | ||
| * TxReset resets the compressor to its initial state. | ||
| * Must be called between each block being built. | ||
| */ | ||
| fun TxReset() | ||
|
|
||
| /** | ||
| * TxWrite appends an RLP-encoded transaction to the compressed data. | ||
| * | ||
| * @param data bytes of the RLP encoded transaction | ||
| * @param data_len number of bytes | ||
| * @return true if the transaction was appended, false if it would exceed the limit | ||
| * or if an error occurred (check TxError() for details) | ||
| */ | ||
| fun TxWrite(data: ByteArray, data_len: Int): Boolean | ||
|
|
||
| /** | ||
| * TxCanWrite checks if an RLP-encoded transaction can be appended without actually appending it. | ||
| * | ||
| * @param data bytes of the RLP encoded transaction | ||
| * @param data_len number of bytes | ||
| * @return true if the transaction could be appended, false otherwise | ||
| */ | ||
| fun TxCanWrite(data: ByteArray, data_len: Int): Boolean | ||
|
|
||
| /** | ||
| * TxLen returns the current length of the compressed data. | ||
| * | ||
| * @return number of bytes of compressed data | ||
| */ | ||
| fun TxLen(): Int | ||
|
|
||
| /** | ||
| * TxWritten returns the number of uncompressed bytes written to the compressor. | ||
| * | ||
| * @return number of uncompressed bytes written | ||
| */ | ||
| fun TxWritten(): Int | ||
|
|
||
| /** | ||
| * TxBytes fills out with the compressed data. | ||
| * The caller must allocate out and ensure that len(out) == TxLen() | ||
| * | ||
| * @param out The ByteArray to be filled with compressed data | ||
| */ | ||
| fun TxBytes(out: ByteArray) | ||
|
|
||
| /** | ||
| * TxError returns the last error message. | ||
| * Should be checked if TxWrite or TxCanWrite returns false. | ||
| * | ||
| * @return error message string, or null if no error | ||
| */ | ||
| fun TxError(): String? | ||
| } | ||
|
|
||
| interface GoNativeTxCompressorJnaLib : GoNativeTxCompressor, Library | ||
|
|
||
| enum class TxCompressorVersion(val version: String) { | ||
| V1("v1.0.0"), | ||
| } | ||
|
|
||
| class GoNativeTxCompressorFactory { | ||
| companion object { | ||
| private const val DICTIONARY_NAME = "compressor-dictionaries/v2025-04-21.bin" | ||
| val dictionaryPath = | ||
| copyResourceToTmpDir(DICTIONARY_NAME, GoNativeTxCompressorFactory::class.java.classLoader) | ||
|
|
||
| private fun getLibFileName(version: String) = "tx_compressor_jna_$version" | ||
|
|
||
| @JvmStatic | ||
| private val loadedVersions = mutableMapOf<TxCompressorVersion, GoNativeTxCompressor>() | ||
|
|
||
| @JvmStatic | ||
| fun getInstance(version: TxCompressorVersion): GoNativeTxCompressor { | ||
| synchronized(loadedVersions) { | ||
| return loadedVersions[version] | ||
| ?: loadLib(version) | ||
| .also { loadedVersions[version] = it } | ||
| } | ||
| } | ||
|
|
||
| private fun loadLib(version: TxCompressorVersion): GoNativeTxCompressor { | ||
| val extractedLibFile = Native.extractFromResourcePath( | ||
| getLibFileName(version.version), | ||
| GoNativeTxCompressorFactory::class.java.classLoader, | ||
| ) | ||
|
|
||
| return Native.load( | ||
| /* name = */ | ||
| extractedLibFile.toString(), | ||
| /* interfaceClass = */ | ||
| GoNativeTxCompressorJnaLib::class.java, | ||
| ) | ||
| } | ||
| } | ||
| } |
151 changes: 151 additions & 0 deletions
151
jvm-libs/linea/tx-compressor/src/main/kotlin/linea/blob/TxCompressor.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| package linea.blob | ||
|
|
||
| import linea.kotlin.encodeHex | ||
| import org.apache.logging.log4j.LogManager | ||
|
|
||
| class TxCompressionException(message: String) : RuntimeException(message) | ||
|
|
||
| /** | ||
| * Interface for transaction-level compression. | ||
| * | ||
| * This compressor operates at the transaction level rather than the block level, | ||
| * maintaining compression context across transactions for better compression ratios. | ||
| * It is designed for sequencer block building where transactions are added one by one | ||
| * until the compressed size threshold is reached. | ||
| */ | ||
| interface TxCompressor { | ||
|
|
||
| /** | ||
| * Checks if an RLP-encoded transaction can be appended without actually appending it. | ||
| * | ||
| * @param rlpEncodedTx RLP-encoded transaction bytes | ||
| * @return true if the transaction could be appended, false if it would exceed the limit | ||
| * @throws TxCompressionException if the transaction is invalid | ||
| */ | ||
| fun canAppendTransaction(rlpEncodedTx: ByteArray): Boolean | ||
|
|
||
| /** | ||
| * Appends an RLP-encoded transaction to the compressed data. | ||
| * | ||
| * @param rlpEncodedTx RLP-encoded transaction bytes | ||
| * @return AppendResult containing whether the transaction was appended and size information | ||
| * @throws TxCompressionException if the transaction is invalid | ||
| */ | ||
| fun appendTransaction(rlpEncodedTx: ByteArray): AppendResult | ||
|
|
||
| /** | ||
| * Returns the current compressed size in bytes. | ||
| */ | ||
| fun getCompressedSize(): Int | ||
|
|
||
| /** | ||
| * Returns the number of uncompressed bytes written. | ||
| */ | ||
| fun getUncompressedSize(): Int | ||
|
|
||
| /** | ||
| * Returns the compressed data. | ||
| */ | ||
| fun getCompressedData(): ByteArray | ||
|
|
||
| /** | ||
| * Resets the compressor to its initial state. | ||
| * Must be called between each block being built. | ||
| */ | ||
| fun reset() | ||
|
|
||
| /** | ||
| * Returns the compressed data and resets the compressor. | ||
| */ | ||
| fun getCompressedDataAndReset(): ByteArray { | ||
| val compressedData = getCompressedData() | ||
| reset() | ||
| return compressedData | ||
| } | ||
|
|
||
| data class AppendResult( | ||
| /** Whether the transaction was appended (false if it would exceed the limit) */ | ||
| val txAppended: Boolean, | ||
| /** Compressed size before attempting to append */ | ||
| val compressedSizeBefore: Int, | ||
| /** Compressed size after (same as before if not appended) */ | ||
| val compressedSizeAfter: Int, | ||
| ) | ||
| } | ||
|
|
||
| /** | ||
| * Go-backed implementation of TxCompressor using JNA bindings. | ||
| */ | ||
| class GoBackedTxCompressor private constructor( | ||
| internal val goNativeTxCompressor: GoNativeTxCompressor, | ||
| ) : TxCompressor { | ||
|
|
||
| companion object { | ||
| @JvmStatic | ||
| fun getInstance(compressorVersion: TxCompressorVersion, dataLimit: Int): GoBackedTxCompressor { | ||
| require(dataLimit > 0) { "dataLimit=$dataLimit must be greater than 0" } | ||
|
|
||
| val goNativeTxCompressor = GoNativeTxCompressorFactory.getInstance(compressorVersion) | ||
| val initialized = goNativeTxCompressor.TxInit( | ||
| dataLimit = dataLimit, | ||
| dictPath = GoNativeTxCompressorFactory.dictionaryPath.toString(), | ||
| ) | ||
| if (!initialized) { | ||
| throw InstantiationException(goNativeTxCompressor.TxError()) | ||
| } | ||
| return GoBackedTxCompressor(goNativeTxCompressor) | ||
| } | ||
| } | ||
|
|
||
| private val log = LogManager.getLogger(GoBackedTxCompressor::class.java) | ||
|
|
||
| override fun canAppendTransaction(rlpEncodedTx: ByteArray): Boolean { | ||
| val canWrite = goNativeTxCompressor.TxCanWrite(rlpEncodedTx, rlpEncodedTx.size) | ||
| val error = goNativeTxCompressor.TxError() | ||
| if (error != null) { | ||
| log.error("Failure while checking transaction: {}", rlpEncodedTx.encodeHex()) | ||
| throw TxCompressionException(error) | ||
| } | ||
| return canWrite | ||
| } | ||
|
|
||
| override fun appendTransaction(rlpEncodedTx: ByteArray): TxCompressor.AppendResult { | ||
| val compressionSizeBefore = goNativeTxCompressor.TxLen() | ||
| val appended = goNativeTxCompressor.TxWrite(rlpEncodedTx, rlpEncodedTx.size) | ||
| val compressedSizeAfter = goNativeTxCompressor.TxLen() | ||
|
|
||
| log.trace( | ||
| "transaction compressed: txRlpSize={} compressionDataBefore={} compressionDataAfter={} compressionRatio={}", | ||
| rlpEncodedTx.size, | ||
| compressionSizeBefore, | ||
| compressedSizeAfter, | ||
| 1.0 - ((compressedSizeAfter - compressionSizeBefore).toDouble() / rlpEncodedTx.size), | ||
| ) | ||
|
|
||
| val error = goNativeTxCompressor.TxError() | ||
| if (error != null) { | ||
| log.error("Failure while writing the following RLP encoded transaction: {}", rlpEncodedTx.encodeHex()) | ||
| throw TxCompressionException(error) | ||
| } | ||
|
|
||
| return TxCompressor.AppendResult(appended, compressionSizeBefore, compressedSizeAfter) | ||
| } | ||
|
|
||
| override fun getCompressedSize(): Int { | ||
| return goNativeTxCompressor.TxLen() | ||
| } | ||
|
|
||
| override fun getUncompressedSize(): Int { | ||
| return goNativeTxCompressor.TxWritten() | ||
| } | ||
|
|
||
| override fun getCompressedData(): ByteArray { | ||
| val compressedData = ByteArray(goNativeTxCompressor.TxLen()) | ||
| goNativeTxCompressor.TxBytes(compressedData) | ||
| return compressedData | ||
| } | ||
|
|
||
| override fun reset() { | ||
| goNativeTxCompressor.TxReset() | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.