diff --git a/.github/workflows/mvn-publish.yml b/.github/workflows/mvn-publish.yml
index 5b2c638..bdc62d4 100644
--- a/.github/workflows/mvn-publish.yml
+++ b/.github/workflows/mvn-publish.yml
@@ -11,40 +11,12 @@ on:
jobs:
publish:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-java@v1
- with:
- java-version: 1.8
- - uses: actions/cache@v2
- with:
- key: ${{ hashFiles('pom.xml') }}
- path: ~/.m2/repository
- - name: Prepare version
- id: maven-version
- run: |
- mvn_version=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
- git_ref=${GITHUB_REF##*/}
- pr_number=${{ github.event.number }}
- if [[ $git_ref =~ v[0-9]+\.[0-9]+\.[0-9]+$ ]] ; then
- if [[ v$mvn_version != $git_ref ]] ; then
- echo Maven version $mvn_version does not match tag $git_ref
- exit 1
- fi
- elif [[ $pr_number != "" && $mvn_version =~ -SNAPSHOT$ ]] ; then
- pattern="s/(.+)-SNAPSHOT/\1-"$pr_number"-SNAPSHOT/g"
- mvn_version=$(echo $mvn_version | sed -E $pattern)
- mvn versions:set -DnewVersion=$mvn_version -DgenerateBackupPoms=false -q -DforceStdout
- elif [[ ! $mvn_version =~ -SNAPSHOT$ ]] ; then
- echo Refusing to publish non-snapshot version $mvn_version
- echo '::set-output name=skip-publish::true'
- fi
- - uses: samuelmeuli/action-maven-publish@v1.4.0
- if:
- ${{ steps.maven-version.outputs.skip-publish != 'true' }}
- with:
- gpg_private_key: ${{ secrets.OSSRH_GPG_KEY_ASCII }}
- gpg_passphrase: ${{ secrets.OSSRH_GPG_PASSPHRASE }}
- nexus_username: ${{ secrets.OSSRH_USERNAME }}
- nexus_password: ${{ secrets.OSSRH_PASSWORD }}
+ uses: wavesplatform/protobuf-schemas/.github/workflows/mvn-publish.yml@e7cf7fb33a89e52117ac932596a7338f1e23e82b
+ with:
+ github-ref-name: ${{ github.ref_name }}
+ github-event-number: ${{ github.event.number }}
+ secrets:
+ OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
+ OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
+ OSSRH_GPG_KEY: ${{ secrets.OSSRH_GPG_KEY }}
+ OSSRH_GPG_PASSPHRASE: ${{ secrets.OSSRH_GPG_PASSPHRASE }}
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index ef836d0..9f92129 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -10,31 +10,14 @@ on:
jobs:
run_tests:
-
runs-on: ubuntu-latest
-
steps:
- - name: Checkout the repository
- uses: actions/checkout@v2
+ - uses: actions/checkout@v5
+ - uses: actions/setup-java@v5
with:
- fetch-depth: 1
-
- - name: Set up JDK 8
- uses: actions/setup-java@v1
- with:
- java-version: 8
-
- - name: Cache Maven packages
- uses: actions/cache@v2
- with:
- path: ~/.m2
- key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
- restore-keys: ${{ runner.os }}-m2
-
- - name: Run tests
- run: mvn -B test -Dmaven.test.failure.ignore=true
-
+ java-version: 11
+ distribution: temurin
+ cache: maven
+ - run: mvn -B test -Dmaven.test.failure.ignore=true
- name: Publish test report
uses: scacap/action-surefire-report@v1
- with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/pom.xml b/pom.xml
index bcef07d..db1e2e9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,8 @@
com.wavesplatform
waves-transactions
- 1.2.5
+ 1.2.6-SNAPSHOT
+ jar
UTF-8
@@ -43,80 +44,51 @@
-
-
-
- deploy
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 3.3.0
-
- 8
-
-
-
- attach-javadocs
-
- jar
-
-
-
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
- 1.6
-
-
-
- --pinentry-mode
- loopback
-
-
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
-
-
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.6.8
- true
-
- ossrh
- https://oss.sonatype.org/
- true
-
-
-
-
-
-
-
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ 3.6.2
+
+
+ enforce-maven
+
+ enforce
+
+
+
+
+ 3.6.3
+
+
+
+
+
+
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ 0.9.0
+ true
+
+ true
+ published
+
+
org.apache.maven.plugins
maven-compiler-plugin
- 3.8.1
+ 3.14.1
- 8
- 8
+ 11
+ 11
org.apache.maven.plugins
maven-source-plugin
- 3.2.1
+ 3.3.1
attach-sources
@@ -126,15 +98,29 @@
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 3.2.8
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
org.apache.maven.plugins
maven-surefire-plugin
- 2.22.2
+ 3.5.4
org.apache.maven.plugins
maven-jar-plugin
- 3.2.0
+ 3.5.0
@@ -151,52 +137,76 @@
- org.apache.maven.plugins
- maven-dependency-plugin
- 3.3.0
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.12.0
+
+
+ attach-javadocs
+
+ jar
+
+
+ 11
+ none
+
+
+
+
+ 11
+ none
+
+
+
+ central-portal-snapshots
+ https://central.sonatype.com/repository/maven-snapshots/
+
+ false
+
+
+ true
+
+
+
+
- ossrh
- https://oss.sonatype.org/content/repositories/snapshots
+ central
+ https://central.sonatype.com/repository/maven-snapshots/
-
- ossrh
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
com.wavesplatform
waves-crypto
- 2.0.5
+ 2.0.6
com.wavesplatform
protobuf-schemas
- 1.5.2
+ 1.6.0
-
-
com.fasterxml.jackson.core
jackson-databind
2.16.1
-
-
org.web3j
- core
+ crypto
4.9.8
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+
+
-
-
org.junit.jupiter
junit-jupiter
diff --git a/src/main/java/com/wavesplatform/transactions/CommitToGenerationTransaction.java b/src/main/java/com/wavesplatform/transactions/CommitToGenerationTransaction.java
new file mode 100644
index 0000000..fc1f1c5
--- /dev/null
+++ b/src/main/java/com/wavesplatform/transactions/CommitToGenerationTransaction.java
@@ -0,0 +1,138 @@
+package com.wavesplatform.transactions;
+
+import com.google.common.primitives.Ints;
+import com.wavesplatform.crypto.BlsUtils;
+import com.wavesplatform.crypto.Bytes;
+import com.wavesplatform.transactions.account.BlsPublicKey;
+import com.wavesplatform.transactions.account.BlsSignature;
+import com.wavesplatform.transactions.account.PrivateKey;
+import com.wavesplatform.transactions.account.PublicKey;
+import com.wavesplatform.transactions.common.Amount;
+import com.wavesplatform.transactions.common.Proof;
+import supranational.blst.SecretKey;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+
+public class CommitToGenerationTransaction extends Transaction {
+
+ public static final int TYPE = 19;
+ public static final int LATEST_VERSION = 1;
+ public static final long MIN_FEE = 100_00000;
+
+ private final Integer generationPeriodStart;
+ private final BlsPublicKey endorserPublicKey;
+ private final BlsSignature commitmentSignature;
+
+ public CommitToGenerationTransaction(PublicKey sender, int generationPeriodStart, BlsPublicKey endorserPublicKey, BlsSignature commitmentSignature, Amount fee, int version, byte chainId, long timestamp, List proofs) {
+ super(TYPE, version, chainId, sender, fee, timestamp, proofs);
+ this.generationPeriodStart = generationPeriodStart;
+ this.endorserPublicKey = endorserPublicKey;
+ this.commitmentSignature = commitmentSignature;
+ }
+
+ public static CommitToGenerationTransaction fromBytes(byte[] bytes) throws IOException {
+ return (CommitToGenerationTransaction) Transaction.fromBytes(bytes);
+ }
+
+ public static CommitToGenerationTransaction fromJson(String json) throws IOException {
+ return (CommitToGenerationTransaction) Transaction.fromJson(json);
+ }
+
+
+ public int generationPeriodStart() {
+ return generationPeriodStart;
+ }
+
+ public BlsPublicKey endorserPublicKey() {
+ return endorserPublicKey;
+ }
+
+ public BlsSignature commitmentSignature() {
+ return commitmentSignature;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+ CommitToGenerationTransaction that = (CommitToGenerationTransaction) o;
+ return this.generationPeriodStart.equals(that.generationPeriodStart)
+ && this.endorserPublicKey.equals(that.endorserPublicKey)
+ && this.commitmentSignature.equals(that.commitmentSignature);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), generationPeriodStart, endorserPublicKey, commitmentSignature);
+ }
+
+ public static CommitToGenerationTransactionBuilder builder(int generationPeriodStart) {
+ return new CommitToGenerationTransactionBuilder(generationPeriodStart);
+ }
+
+ public static class CommitToGenerationTransactionBuilder
+ extends TransactionBuilder {
+ private final Integer generationPeriodStart;
+ private BlsPublicKey endorserPublicKey;
+ private BlsSignature commitmentSignature;
+
+ protected CommitToGenerationTransactionBuilder(int generationPeriodStart) {
+ super(LATEST_VERSION, MIN_FEE);
+ this.generationPeriodStart = generationPeriodStart;
+ this.endorserPublicKey = null;
+ this.commitmentSignature = null;
+ }
+
+ public CommitToGenerationTransactionBuilder endorserPublicKey(BlsPublicKey pk) {
+ this.endorserPublicKey = pk;
+ return this;
+ }
+
+ public CommitToGenerationTransactionBuilder commitmentSignature(BlsSignature commitmentSignature) {
+ this.commitmentSignature = commitmentSignature;
+ return this;
+ }
+
+ protected CommitToGenerationTransaction _build() {
+ return new CommitToGenerationTransaction(sender, generationPeriodStart, endorserPublicKey, commitmentSignature, feeWithExtra(), version, chainId, timestamp, Proof.emptyList());
+ }
+
+ @Override
+ public CommitToGenerationTransaction getSignedWith(PrivateKey privateKey) {
+ if (sender == null) {
+ sender = privateKey.publicKey();
+ }
+ CommitToGenerationTransaction unsignedTx = getUnsigned();
+ SecretKey blsSk = BlsUtils.mkBlsSecretKey(privateKey.bytes());
+
+ byte[] blsPubBytes = this.endorserPublicKey != null
+ ? this.endorserPublicKey.bytes()
+ : BlsUtils.mkBlsPublicKey(blsSk);
+
+ byte[] blsSigBytes = this.commitmentSignature != null
+ ? this.commitmentSignature.bytes()
+ : BlsUtils.sign(blsSk, Bytes.concat(blsPubBytes, Ints.toByteArray(unsignedTx.generationPeriodStart()))
+ );
+
+ CommitToGenerationTransaction txWithBls = new CommitToGenerationTransaction(
+ unsignedTx.sender(),
+ unsignedTx.generationPeriodStart(),
+ BlsPublicKey.as(blsPubBytes),
+ BlsSignature.as(blsSigBytes),
+ unsignedTx.fee(),
+ unsignedTx.version(),
+ unsignedTx.chainId(),
+ unsignedTx.timestamp(),
+ Proof.emptyList()
+ );
+
+ return txWithBls.addProof(privateKey);
+ }
+
+ }
+
+
+}
diff --git a/src/main/java/com/wavesplatform/transactions/account/BlsPublicKey.java b/src/main/java/com/wavesplatform/transactions/account/BlsPublicKey.java
new file mode 100644
index 0000000..2ae53bf
--- /dev/null
+++ b/src/main/java/com/wavesplatform/transactions/account/BlsPublicKey.java
@@ -0,0 +1,45 @@
+package com.wavesplatform.transactions.account;
+
+import com.wavesplatform.transactions.common.Base58String;
+
+import java.util.Arrays;
+
+public class BlsPublicKey extends Base58String {
+
+ public static final int BYTES_LENGTH = 48;
+ public BlsPublicKey(byte[] blsPublicKey) throws IllegalArgumentException {
+ super(blsPublicKey);
+ if (blsPublicKey.length != BYTES_LENGTH )
+ throw new IllegalArgumentException("BLSPublic key has wrong size in bytes. "
+ + "Expected: " + BYTES_LENGTH + ", actual: " + blsPublicKey.length);
+ }
+
+ public BlsPublicKey(String blsPublicKey) {
+ super(blsPublicKey);
+ Base58String bls = new Base58String(blsPublicKey);
+
+ if ( bls.bytes().length != BYTES_LENGTH)
+ throw new IllegalArgumentException("BLSPublic key has wrong size in bytes. "
+ + "Expected: " + BYTES_LENGTH + ", actual: " + bls.bytes().length);
+ }
+
+ public static BlsPublicKey as(byte[] bytes) {
+ return new BlsPublicKey(bytes);
+ }
+
+ public static BlsPublicKey as(String base58Encoded) {
+ return new BlsPublicKey(base58Encoded);
+ }
+
+ public boolean equals(byte[] anotherKey) {
+ return Arrays.equals(this.bytes, anotherKey);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ BlsPublicKey bls = (BlsPublicKey) o;
+ return Arrays.equals(bytes, bls.bytes);
+ }
+}
diff --git a/src/main/java/com/wavesplatform/transactions/account/BlsSignature.java b/src/main/java/com/wavesplatform/transactions/account/BlsSignature.java
new file mode 100644
index 0000000..7073f83
--- /dev/null
+++ b/src/main/java/com/wavesplatform/transactions/account/BlsSignature.java
@@ -0,0 +1,44 @@
+package com.wavesplatform.transactions.account;
+
+import com.wavesplatform.transactions.common.Base58String;
+
+import java.util.Arrays;
+
+public class BlsSignature extends Base58String {
+ public static final int BYTES_LENGTH = 96;
+ public BlsSignature(byte[] blsSignature) throws IllegalArgumentException {
+ super(blsSignature);
+ if (blsSignature.length != BYTES_LENGTH )
+ throw new IllegalArgumentException("BLSPublic key has wrong size in bytes. "
+ + "Expected: " + BYTES_LENGTH + ", actual: " + blsSignature.length);
+ }
+
+ public BlsSignature(String blsSignature) {
+ super(blsSignature);
+ Base58String bls = new Base58String(blsSignature);
+
+ if ( bls.bytes().length != BYTES_LENGTH)
+ throw new IllegalArgumentException("BlsSignature key has wrong size in bytes. "
+ + "Expected: " + BYTES_LENGTH + ", actual: " + bls.bytes().length);
+ }
+
+ public static BlsSignature as(byte[] bytes) {
+ return new BlsSignature(bytes);
+ }
+
+ public static BlsSignature as(String base58Encoded) {
+ return new BlsSignature(base58Encoded);
+ }
+
+ public boolean equals(byte[] anotherKey) {
+ return Arrays.equals(this.bytes, anotherKey);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ BlsSignature blsSig = (BlsSignature) o;
+ return Arrays.equals(bytes, blsSig.bytes);
+ }
+}
diff --git a/src/main/java/com/wavesplatform/transactions/common/Base64String.java b/src/main/java/com/wavesplatform/transactions/common/Base64String.java
index d7d0eec..6812e59 100644
--- a/src/main/java/com/wavesplatform/transactions/common/Base64String.java
+++ b/src/main/java/com/wavesplatform/transactions/common/Base64String.java
@@ -9,7 +9,7 @@
public class Base64String implements ByteString {
- private final byte[] bytes;
+ protected final byte[] bytes;
private final Supplier encoded;
public static Base64String empty() {
diff --git a/src/main/java/com/wavesplatform/transactions/serializers/ProtobufConverter.java b/src/main/java/com/wavesplatform/transactions/serializers/ProtobufConverter.java
index f0f2c93..8aa6984 100644
--- a/src/main/java/com/wavesplatform/transactions/serializers/ProtobufConverter.java
+++ b/src/main/java/com/wavesplatform/transactions/serializers/ProtobufConverter.java
@@ -12,6 +12,8 @@
import com.wavesplatform.protobuf.transaction.TransactionOuterClass.SignedTransaction;
import com.wavesplatform.transactions.*;
import com.wavesplatform.transactions.account.Address;
+import com.wavesplatform.transactions.account.BlsPublicKey;
+import com.wavesplatform.transactions.account.BlsSignature;
import com.wavesplatform.transactions.account.PublicKey;
import com.wavesplatform.transactions.common.*;
import com.wavesplatform.transactions.data.*;
@@ -286,6 +288,18 @@ else if (e.getValueCase() == VALUE_NOT_SET)
.fee(pbAmountToAmount(pbTx.getFee()))
.timestamp(pbTx.getTimestamp())
.getUnsigned();
+ } else if (pbTx.hasCommitToGeneration()) {
+ TransactionOuterClass.CommitToGenerationTransactionData commitToGeneration = pbTx.getCommitToGeneration();
+ tx = CommitToGenerationTransaction
+ .builder(commitToGeneration.getGenerationPeriodStart())
+ .endorserPublicKey(BlsPublicKey.as(commitToGeneration.getEndorserPublicKey().toByteArray()))
+ .commitmentSignature(BlsSignature.as(commitToGeneration.getCommitmentSignature().toByteArray()))
+ .version(pbTx.getVersion())
+ .chainId((byte) pbTx.getChainId())
+ .sender(PublicKey.as(pbTx.getSenderPublicKey().toByteArray()))
+ .fee(pbAmountToAmount(pbTx.getFee()))
+ .timestamp(pbTx.getTimestamp())
+ .getUnsigned();
} else throw new InvalidProtocolBufferException("Can't recognize transaction type");
pbSignedTx.getProofsList().forEach(p -> tx.proofs().add(Proof.as(p.toByteArray())));
@@ -553,6 +567,13 @@ public static TransactionOuterClass.Transaction toUnsignedProtobuf(Transaction t
.setName(uaiTx.name())
.setDescription(uaiTx.description())
.build());
+ } else if (tx instanceof CommitToGenerationTransaction) {
+ CommitToGenerationTransaction commitToGeneration = (CommitToGenerationTransaction) tx;
+ protoBuilder.setCommitToGeneration(TransactionOuterClass.CommitToGenerationTransactionData.newBuilder()
+ .setGenerationPeriodStart(commitToGeneration.generationPeriodStart())
+ .setEndorserPublicKey(ByteString.copyFrom(commitToGeneration.endorserPublicKey().bytes()))
+ .setCommitmentSignature(ByteString.copyFrom(commitToGeneration.commitmentSignature().bytes()))
+ .build());
}
return protoBuilder.build();
diff --git a/src/main/java/com/wavesplatform/transactions/serializers/Scheme.java b/src/main/java/com/wavesplatform/transactions/serializers/Scheme.java
index c1761f1..5f02dfc 100644
--- a/src/main/java/com/wavesplatform/transactions/serializers/Scheme.java
+++ b/src/main/java/com/wavesplatform/transactions/serializers/Scheme.java
@@ -33,6 +33,9 @@ public static Scheme of(int txType, int txVersion) {
if (txType == EthereumTransaction.TYPE_TAG) {
return Scheme.PROTOBUF;
}
+ if (txType == CommitToGenerationTransaction.TYPE) {
+ return Scheme.PROTOBUF;
+ }
throw new IllegalArgumentException("Unsupported transaction type " + txType + " with version " + txVersion);
}
diff --git a/src/main/java/com/wavesplatform/transactions/serializers/json/JsonSerializer.java b/src/main/java/com/wavesplatform/transactions/serializers/json/JsonSerializer.java
index 4db8779..5aa8ffc 100644
--- a/src/main/java/com/wavesplatform/transactions/serializers/json/JsonSerializer.java
+++ b/src/main/java/com/wavesplatform/transactions/serializers/json/JsonSerializer.java
@@ -8,6 +8,8 @@
import com.wavesplatform.crypto.base.Base64;
import com.wavesplatform.transactions.*;
import com.wavesplatform.transactions.account.Address;
+import com.wavesplatform.transactions.account.BlsPublicKey;
+import com.wavesplatform.transactions.account.BlsSignature;
import com.wavesplatform.transactions.account.PublicKey;
import com.wavesplatform.transactions.common.*;
import com.wavesplatform.transactions.data.*;
@@ -16,7 +18,6 @@
import com.wavesplatform.transactions.invocation.*;
import com.wavesplatform.transactions.mass.Transfer;
import com.wavesplatform.transactions.serializers.Scheme;
-import org.bouncycastle.util.encoders.Hex;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.Sign;
import org.web3j.crypto.SignedRawTransaction;
@@ -278,6 +279,12 @@ public static Transaction fromJson(JsonNode json) throws IOException {
default:
throw new IOException("Unsupported payload type");
}
+ } else if (type == CommitToGenerationTransaction.TYPE) {
+ if (!fee.assetId().isWaves())
+ throw new IOException("feeAssetId field must be null for CommitToGenerationTransaction");
+ return new CommitToGenerationTransaction(sender, json.get("generationPeriodStart").asInt(),
+ blsPublicKeyFromJson(json, "endorserPublicKey"), blsSignatureFromJson(json, "commitmentSignature"),
+ fee, version, chainId, timestamp, proofs);
}
throw new IOException("Can't parse json of transaction with type " + type);
@@ -510,6 +517,11 @@ else if (e instanceof DeleteEntry) {
EthereumTransaction.Transfer transfer = (EthereumTransaction.Transfer) et.payload();
transferToJson(payload.put("type", "transfer"), transfer.recipient(), transfer.amount());
}
+ } else if (tx instanceof CommitToGenerationTransaction) {
+ CommitToGenerationTransaction gct = (CommitToGenerationTransaction) tx;
+ jsObject.put("generationPeriodStart", gct.generationPeriodStart());
+ jsObject.put("endorserPublicKey", gct.endorserPublicKey().toString());
+ jsObject.put("commitmentSignature", gct.commitmentSignature().toString());
}
jsObject.put("fee", tx.fee().value());
@@ -633,6 +645,17 @@ public static Base64String scriptFromJson(JsonNode json) {
return base64FromJson(json, "script");
}
+ public static BlsPublicKey blsPublicKeyFromJson(JsonNode json, String fieldName){
+ return BlsPublicKey.as(base58FromJson(json, fieldName).bytes());
+ }
+
+ public static BlsSignature blsSignatureFromJson(JsonNode json, String fieldName){
+ return BlsSignature.as(base58FromJson(json, fieldName).bytes());
+ }
+
+ public static Base58String base58FromJson(JsonNode json, String fieldName) {
+ return json.hasNonNull(fieldName) ? new Base58String(json.get(fieldName).asText()) : Base58String.empty();
+ }
public static Base64String base64FromJson(JsonNode json, String fieldName) {
return json.hasNonNull(fieldName) ? new Base64String(json.get(fieldName).asText()) : Base64String.empty();
}
diff --git a/src/test/java/com/wavesplatform/transactions/CommitToGenerationTransactionTest.java b/src/test/java/com/wavesplatform/transactions/CommitToGenerationTransactionTest.java
new file mode 100644
index 0000000..055c32e
--- /dev/null
+++ b/src/test/java/com/wavesplatform/transactions/CommitToGenerationTransactionTest.java
@@ -0,0 +1,114 @@
+package com.wavesplatform.transactions;
+
+import com.wavesplatform.crypto.base.Base64;
+import com.wavesplatform.transactions.account.BlsPublicKey;
+import com.wavesplatform.transactions.account.BlsSignature;
+import com.wavesplatform.transactions.account.PublicKey;
+import com.wavesplatform.transactions.common.Amount;
+import com.wavesplatform.transactions.common.AssetId;
+import com.wavesplatform.transactions.common.Id;
+import com.wavesplatform.transactions.common.Proof;
+import com.wavesplatform.transactions.serializers.json.JsonSerializer;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+public class CommitToGenerationTransactionTest {
+
+ @BeforeAll
+ static void beforeAll() {
+ WavesConfig.chainId('D');
+ }
+
+ private static final PublicKey sender = PublicKey.as("Bn21Eg8HbwZWZQMHXnTFnb64MhVjgH2HygDekQDbjjMq");
+ private static final long timestamp = 1766488084192L;
+
+ private static final long fee = CommitToGenerationTransaction.MIN_FEE;
+
+ static Stream transactionsProvider() {
+
+ return Stream.of(
+ arguments(1, 2941,
+ "5t9zL1oqXW6kL3YUAuF8r4rKUaPwohPVrpMWR8Y1bAJtSMipP3TQJYZvpBFB7GWZwo",
+ "u9CTxLENQWyd5egnHrnbnnDKy7mvtrWJfR1AuCL4e4e7uuRTobzxVy1SQBz7ayoY5mRfiTG3PR8niJPT3fdVfsD97EkagBha8ehrLAzdutWSLbiQF2VDwPfi7uvTcp5csMC",
+ Id.as("2r6kpnJbGqNTmLxi9cPuavgxqumGufnisnN8vhuSCpaX"),
+ Proof.list(Proof.as("2H7TeCFEBSq6gb325Up2iZPr1jbZk99XugFijYTLbvgH2Ts8NQyCusG5p84FxKySEsVz7RUqsmQQJcCGU3xx75jt")),
+ Base64.decode("CEQSIKAdJBa6JVvXU0LO8aUp5j7w3LObv4C4vPpcq9RBuqMQGgUQgK3iBCDgnZTXtDMoAcIHlwEI/RYSMIUWaUuByJgaIrJ8Tzg7DtK6VMpZ+NMqFQnZmUZ8JQ+gtn/5ek2c2QZ2u5gPLugyohpgl11dxTrp2qTsGNyxzCVVgWCv64BdM6pqdAYmfawlVYZgg3u4l4uz1j7FM2GTQaWnCKANY7gWy3cdwzwtk7hnLZ67TBpiMZMIaXV+9Hd3sebB5UcswbwmLheDDjBcid5z"),
+ Base64.decode("Cs8BCEQSIKAdJBa6JVvXU0LO8aUp5j7w3LObv4C4vPpcq9RBuqMQGgUQgK3iBCDgnZTXtDMoAcIHlwEI/RYSMIUWaUuByJgaIrJ8Tzg7DtK6VMpZ+NMqFQnZmUZ8JQ+gtn/5ek2c2QZ2u5gPLugyohpgl11dxTrp2qTsGNyxzCVVgWCv64BdM6pqdAYmfawlVYZgg3u4l4uz1j7FM2GTQaWnCKANY7gWy3cdwzwtk7hnLZ67TBpiMZMIaXV+9Hd3sebB5UcswbwmLheDDjBcid5zEkA/6SwuagKVYJeSgi5V6yNtzKeV7cy6+SjybD9d+bopeoERTmNPLEDduAtBy4JP5upf9S+Yf9cH1nnMgDI0FagH"),
+ "{\"id\":\"2r6kpnJbGqNTmLxi9cPuavgxqumGufnisnN8vhuSCpaX\",\"type\":19,\"version\":1,\"chainId\":68,\"senderPublicKey\":\"Bn21Eg8HbwZWZQMHXnTFnb64MhVjgH2HygDekQDbjjMq\",\"sender\":\"3FmjX4FAeDXE4ZdDj2JKxzE4QtbxaioXzxM\",\"generationPeriodStart\":2941,\"endorserPublicKey\":\"5t9zL1oqXW6kL3YUAuF8r4rKUaPwohPVrpMWR8Y1bAJtSMipP3TQJYZvpBFB7GWZwo\",\"commitmentSignature\":\"u9CTxLENQWyd5egnHrnbnnDKy7mvtrWJfR1AuCL4e4e7uuRTobzxVy1SQBz7ayoY5mRfiTG3PR8niJPT3fdVfsD97EkagBha8ehrLAzdutWSLbiQF2VDwPfi7uvTcp5csMC\",\"proofs\":[\"2H7TeCFEBSq6gb325Up2iZPr1jbZk99XugFijYTLbvgH2Ts8NQyCusG5p84FxKySEsVz7RUqsmQQJcCGU3xx75jt\"],\"fee\":10000000,\"feeAssetId\":null,\"timestamp\":1766488084192}"
+ )
+ );
+ }
+
+ @ParameterizedTest(name = "{index}: v{0} to {1} of {2} wavelets")
+ @MethodSource("transactionsProvider")
+ void commitToGenerationTransaction(int version, int generationPeriodStart, String endorserPublicKey,
+ String commitmentSignature, Id expectedId, List proofs,
+ byte[] expectedBodyBytes, byte[] expectedBytes, String expectedJson) throws IOException {
+ CommitToGenerationTransaction builtTx = CommitToGenerationTransaction
+ .builder(generationPeriodStart)
+ .endorserPublicKey(BlsPublicKey.as(endorserPublicKey))
+ .commitmentSignature(BlsSignature.as(commitmentSignature))
+ .chainId(WavesConfig.chainId())
+ .fee(fee)
+ .timestamp(timestamp)
+ .sender(sender)
+ .version(version)
+ .getUnsigned()
+ .addProofs(proofs);
+
+ assertAll("Tx created via builder must be equal to expected bytes",
+ () -> assertThat(builtTx.bodyBytes()).isEqualTo(expectedBodyBytes),
+ () -> assertThat(builtTx.id()).isEqualTo(expectedId),
+ () -> assertThat(builtTx.toBytes()).isEqualTo(expectedBytes)
+ );
+
+ CommitToGenerationTransaction constructedTx = new CommitToGenerationTransaction(sender, generationPeriodStart,
+ BlsPublicKey.as(endorserPublicKey), BlsSignature.as(commitmentSignature), Amount.of(fee),
+ version, WavesConfig.chainId(), timestamp, proofs);
+
+ assertAll("Txs created via builder and constructor are equal",
+ () -> assertThat(builtTx.bodyBytes()).isEqualTo(constructedTx.bodyBytes()),
+ () -> assertThat(builtTx.id()).isEqualTo(constructedTx.id()),
+ () -> assertThat(builtTx.toBytes()).isEqualTo(constructedTx.toBytes())
+ );
+
+ CommitToGenerationTransaction deserTx = CommitToGenerationTransaction.fromBytes(expectedBytes);
+
+ assertAll("Tx must be deserializable from expected bytes",
+ () -> assertThat(deserTx.generationPeriodStart()).isEqualTo(generationPeriodStart),
+ () -> assertThat(deserTx.endorserPublicKey()).isEqualTo(BlsPublicKey.as(endorserPublicKey)),
+ () -> assertThat(deserTx.commitmentSignature()).isEqualTo(BlsSignature.as(commitmentSignature)),
+
+ () -> assertThat(deserTx.version()).isEqualTo(version),
+ () -> assertThat(deserTx.chainId()).isEqualTo(WavesConfig.chainId()),
+ () -> assertThat(deserTx.sender()).isEqualTo(sender),
+ () -> assertThat(deserTx.fee()).isEqualTo(Amount.of(fee, AssetId.WAVES)),
+ () -> assertThat(deserTx.timestamp()).isEqualTo(timestamp),
+ () -> assertThat(deserTx.proofs()).isEqualTo(proofs),
+
+ () -> assertThat(deserTx.bodyBytes()).isEqualTo(expectedBodyBytes),
+ () -> assertThat(deserTx.toBytes()).isEqualTo(expectedBytes),
+ () -> assertThat(deserTx.id()).isEqualTo(expectedId)
+ );
+
+ assertThat(builtTx)
+ .describedAs("Tx must be equal to deserialized tx")
+ .isEqualTo(deserTx);
+
+ Assertions.assertThat(JsonSerializer.JSON_MAPPER.readTree(Transaction.fromJson(expectedJson).toJson()))
+ .describedAs("Tx serialized to json must be equal to expected")
+ .isEqualTo(JsonSerializer.JSON_MAPPER.readTree(expectedJson));
+ }
+
+}