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