diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 106a49c7..8cbb1b12 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,76 +1,54 @@ name: Test on: - push: - branches: - - "master" - - "develop" - pull_request: - types: [ready_for_review, synchronize, opened] + push: + branches: + - "master" + - "develop" + pull_request: + types: [ready_for_review, synchronize, opened] jobs: - format: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - with: - ref: ${{ github.head_ref }} - - - name: Merge Conflict finder - uses: olivernybroe/action-conflict-finder@v1.1 - - - name: Use Java Version 22 - uses: actions/setup-java@v2 - with: - distribution: "adopt" - java-version: "22" - cache: "gradle" - - - name: Format code - run: gradle format - - - name: Commit fixed code - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: "style: resolve style guide violations" - branch: ${{ github.head_ref }} - - unit: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - with: - ref: ${{ github.head_ref }} - - - name: Merge Conflict finder - uses: olivernybroe/action-conflict-finder@v1.1 - - - name: Install Nix - uses: cachix/install-nix-action@v27 - - - name: Install libsecp256k1 - run: | - nix profile install nixpkgs#secp256k1 - env: - NIX_PATH: $HOME/.nix-profile/bin - - - name: Set LD_LIBRARY_PATH - run: echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/.nix-profile/lib" >> $GITHUB_ENV - - - name: Use Java Version 22 - uses: actions/setup-java@v2 - with: - distribution: "adopt" - java-version: "22" - cache: "gradle" - - - name: Install - run: gradle dependencies - - - name: Test - run: gradle test && gradle jacocoTestReport - - - name: Codecov - run: bash <(curl -s https://codecov.io/bash) -t ${{ secrets.CODECOV_TOKEN }} + unit: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Java 17 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '17' + cache: 'gradle' + + - name: Install Dependencies + run: ./gradlew dependencies + + - name: Run Tests + run: ./gradlew test jacocoTestReport + + - name: Upload Test Results + uses: actions/upload-artifact@v3 + with: + name: test-results + path: build/reports/tests/test + + - name: Upload Coverage Report + uses: actions/upload-artifact@v3 + with: + name: code-coverage-report + path: build/reports/jacoco/test/html + + - name: Upload Jacoco XML Report + uses: actions/upload-artifact@v3 + with: + name: jacoco-xml-report + path: build/reports/jacoco/test/jacocoTestReport.xml + + - name: Codecov + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: build/reports/jacoco/test/jacocoTestReport.xml + fail_ci_if_error: true diff --git a/build.gradle b/build.gradle index 31eb39ef..1bc811a1 100644 --- a/build.gradle +++ b/build.gradle @@ -7,11 +7,11 @@ plugins { } group = 'org.arkecosystem' -version = '2.0.0' +version = '2.0.0-mainsail' java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 withJavadocJar() withSourcesJar() } @@ -22,15 +22,13 @@ repositories { } dependencies { - implementation 'org.web3j:core:4.8.7' + // Core dependencies implementation 'org.bitcoinj:bitcoinj-core:0.16.3' - implementation files('libs/secp256k1-api-0.0.1.jar') - implementation files('libs/secp256k1-foreign-0.0.1.jar') + implementation 'org.bouncycastle:bcprov-jdk15on:1.70' + implementation 'org.web3j:core:4.8.7' implementation 'com.google.code.gson:gson:2.11.0' - implementation 'com.google.guava:guava:30.2.0-jre' - testImplementation 'org.slf4j:slf4j-api:2.0.13' - testRuntimeOnly 'org.slf4j:slf4j-simple:2.0.13' + // Test dependencies testImplementation 'org.hamcrest:hamcrest-library:3.0' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.3' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.3' @@ -42,7 +40,6 @@ test { useJUnitPlatform() testLogging { events 'PASSED', 'FAILED', 'SKIPPED' - showStandardStreams = true } } @@ -155,8 +152,13 @@ publishing { } signing { + // Only sign when publishing to remote repository + required { gradle.taskGraph.hasTask("publish") && !gradle.taskGraph.hasTask("publishToMavenLocal") } + def signingKey = findProperty("signingKey") def signingPassword = findProperty("signingPassword") - useInMemoryPgpKeys(signingKey as String, signingPassword as String) - sign publishing.publications.mavenJava + if (signingKey && signingPassword) { + useInMemoryPgpKeys(signingKey as String, signingPassword as String) + sign publishing.publications.mavenJava + } } diff --git a/libs/secp256k1-api-0.0.1.jar b/libs/secp256k1-api-0.0.1.jar deleted file mode 100644 index 2795c7fb..00000000 Binary files a/libs/secp256k1-api-0.0.1.jar and /dev/null differ diff --git a/libs/secp256k1-foreign-0.0.1.jar b/libs/secp256k1-foreign-0.0.1.jar deleted file mode 100644 index 38414c6a..00000000 Binary files a/libs/secp256k1-foreign-0.0.1.jar and /dev/null differ diff --git a/src/main/java/org/arkecosystem/crypto/enums/AbiFunction.java b/src/main/java/org/arkecosystem/crypto/enums/AbiFunction.java new file mode 100644 index 00000000..75202ca8 --- /dev/null +++ b/src/main/java/org/arkecosystem/crypto/enums/AbiFunction.java @@ -0,0 +1,18 @@ +package org.arkecosystem.crypto.enums; + +public enum AbiFunction { + VOTE("vote"), + UNVOTE("unvote"), + VALIDATOR_REGISTRATION("registerValidator"), + VALIDATOR_RESIGNATION("resignValidator"); + + private final String functionName; + + AbiFunction(String functionName) { + this.functionName = functionName; + } + + public String toString() { + return functionName; + } +} diff --git a/src/main/java/org/arkecosystem/crypto/enums/CoreTransactionTypes.java b/src/main/java/org/arkecosystem/crypto/enums/CoreTransactionTypes.java deleted file mode 100644 index a977cb48..00000000 --- a/src/main/java/org/arkecosystem/crypto/enums/CoreTransactionTypes.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.arkecosystem.crypto.enums; - -public enum CoreTransactionTypes { - TRANSFER(0), - SECOND_SIGNATURE_REGISTRATION(1), - VALIDATOR_REGISTRATION(2), - VOTE(3), - MULTI_SIGNATURE_REGISTRATION(4), - MULTI_PAYMENT(6), - VALIDATOR_RESIGNATION(7), - USERNAME_REGISTRATION(8), - USERNAME_RESIGNATION(9); - - private final int value; - - CoreTransactionTypes(int value) { - this.value = value; - } - - public int getValue() { - return value; - } -} diff --git a/src/main/java/org/arkecosystem/crypto/enums/Fees.java b/src/main/java/org/arkecosystem/crypto/enums/Fees.java deleted file mode 100644 index 8a5cf991..00000000 --- a/src/main/java/org/arkecosystem/crypto/enums/Fees.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.arkecosystem.crypto.enums; - -public enum Fees { - TRANSFER(10_000_000L), - SECOND_SIGNATURE_REGISTRATION(500_000_000L), - VALIDATOR_REGISTRATION(2_500_000_000L), - VOTE(100_000_000L), - MULTI_SIGNATURE_REGISTRATION(500_000_000L), - MULTI_PAYMENT(10_000_000L), - VALIDATOR_RESIGNATION(2_500_000_000L), - USERNAME_REGISTRATION(2_500_000_000L), - USERNAME_RESIGNATION(2_500_000_000L); - - private final Long value; - - Fees(Long value) { - this.value = value; - } - - public Long getValue() { - return value; - } -} diff --git a/src/main/java/org/arkecosystem/crypto/enums/TransactionTypeGroup.java b/src/main/java/org/arkecosystem/crypto/enums/TransactionTypeGroup.java deleted file mode 100644 index b61b8c38..00000000 --- a/src/main/java/org/arkecosystem/crypto/enums/TransactionTypeGroup.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.arkecosystem.crypto.enums; - -public enum TransactionTypeGroup { - TEST(0), - CORE(1), - - RESERVED(1000); - - private final int value; - - TransactionTypeGroup(int value) { - this.value = value; - } - - public int getValue() { - return value; - } -} diff --git a/src/main/java/org/arkecosystem/crypto/signature/CompressedPubKeyDataImpl.java b/src/main/java/org/arkecosystem/crypto/signature/CompressedPubKeyDataImpl.java deleted file mode 100644 index f6bc50cb..00000000 --- a/src/main/java/org/arkecosystem/crypto/signature/CompressedPubKeyDataImpl.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.arkecosystem.crypto.signature; - -import java.util.Arrays; -import org.bitcoinj.secp256k1.api.CompressedPubKeyData; - -public class CompressedPubKeyDataImpl implements CompressedPubKeyData { - - private final byte[] bytes; - - public CompressedPubKeyDataImpl(byte[] bytes) { - if (bytes.length != 33) { - throw new IllegalArgumentException("Compressed public key must be 33 bytes"); - } - this.bytes = Arrays.copyOf(bytes, bytes.length); - } - - @Override - public byte[] bytes() { - return Arrays.copyOf(bytes, bytes.length); - } -} diff --git a/src/main/java/org/arkecosystem/crypto/signature/P256k1PrivKeyImpl.java b/src/main/java/org/arkecosystem/crypto/signature/P256k1PrivKeyImpl.java deleted file mode 100644 index 15c5955e..00000000 --- a/src/main/java/org/arkecosystem/crypto/signature/P256k1PrivKeyImpl.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.arkecosystem.crypto.signature; - -import java.math.BigInteger; -import java.util.Arrays; -import org.bitcoinj.secp256k1.api.P256k1PrivKey; - -public class P256k1PrivKeyImpl implements P256k1PrivKey { - private final byte[] encoded; - private boolean destroyed; - - public P256k1PrivKeyImpl(byte[] encoded) { - if (encoded.length != 32) { - throw new IllegalArgumentException("Private key must be 32 bytes"); - } - this.encoded = Arrays.copyOf(encoded, encoded.length); - this.destroyed = false; - } - - @Override - public byte[] getEncoded() { - if (destroyed) { - throw new IllegalStateException("Private key has been destroyed"); - } - return Arrays.copyOf(encoded, encoded.length); - } - - @Override - public void destroy() { - Arrays.fill(encoded, (byte) 0); - this.destroyed = true; - } - - @Override - public BigInteger getS() { - return toInteger(getEncoded()); - } - - /** - * Converts a byte array into a BigInteger. Assumes the byte array represents an unsigned - * integer in big-endian order. - * - * @param bytes the byte array to convert. - * @return the BigInteger representation. - */ - private BigInteger toInteger(byte[] bytes) { - // BigInteger interprets the byte array as a signed integer in big-endian order. - // To handle this correctly for unsigned values (like private keys), we prepend a zero byte - // to ensure the value is always positive. - byte[] unsignedBytes = new byte[bytes.length + 1]; - unsignedBytes[0] = 0; // Leading zero for positive sign - System.arraycopy(bytes, 0, unsignedBytes, 1, bytes.length); - return new BigInteger(unsignedBytes); - } -} diff --git a/src/main/java/org/arkecosystem/crypto/signature/SchnorrSigner.java b/src/main/java/org/arkecosystem/crypto/signature/SchnorrSigner.java deleted file mode 100644 index 40beaf36..00000000 --- a/src/main/java/org/arkecosystem/crypto/signature/SchnorrSigner.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.arkecosystem.crypto.signature; - -import org.arkecosystem.crypto.Schnorr; -import org.bitcoinj.core.ECKey; -import org.bitcoinj.secp256k1.api.P256K1KeyPair; -import org.bitcoinj.secp256k1.api.P256k1PrivKey; -import org.bitcoinj.secp256k1.api.Secp256k1; - -public class SchnorrSigner implements Signer { - @Override - public byte[] sign(byte[] message, ECKey privateKey) { - - Secp256k1 secp = Secp256k1.get(); - - byte[] privKeyBytes = Schnorr.hexStringToByteArray(privateKey.getPrivateKeyAsHex()); - - P256k1PrivKey privKey = new P256k1PrivKeyImpl(privKeyBytes); - - P256K1KeyPair keyPair = secp.ecKeyPairCreate(privKey); - - return secp.schnorrSigSign32(message, keyPair); - } -} diff --git a/src/main/java/org/arkecosystem/crypto/signature/SchnorrVerifier.java b/src/main/java/org/arkecosystem/crypto/signature/SchnorrVerifier.java deleted file mode 100644 index 00117739..00000000 --- a/src/main/java/org/arkecosystem/crypto/signature/SchnorrVerifier.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.arkecosystem.crypto.signature; - -import org.bitcoinj.core.ECKey; -import org.bitcoinj.secp256k1.api.P256K1XOnlyPubKey; -import org.bitcoinj.secp256k1.api.P256k1PubKey; -import org.bitcoinj.secp256k1.api.Secp256k1; - -public class SchnorrVerifier implements Verifier { - @Override - public boolean verify(byte[] hash, ECKey keys, byte[] signature) { - byte[] pubKey = keys.getPubKey(); - - try (Secp256k1 secp = Secp256k1.get()) { - P256k1PubKey pubkey = secp.ecPubKeyParse(new CompressedPubKeyDataImpl(pubKey)).get(); - - P256K1XOnlyPubKey xOnly = pubkey.getXOnly(); - - P256K1XOnlyPubKey xOnly2 = P256K1XOnlyPubKey.parse(xOnly.getSerialized()).get(); - - return secp.schnorrSigVerify(signature, hash, xOnly2).get(); - } - } -} diff --git a/src/main/java/org/arkecosystem/crypto/signature/Signer.java b/src/main/java/org/arkecosystem/crypto/signature/Signer.java deleted file mode 100644 index cae5bc9c..00000000 --- a/src/main/java/org/arkecosystem/crypto/signature/Signer.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.arkecosystem.crypto.signature; - -import org.bitcoinj.core.ECKey; - -public interface Signer { - - byte[] sign(byte[] message, ECKey key); -} diff --git a/src/main/java/org/arkecosystem/crypto/signature/Verifier.java b/src/main/java/org/arkecosystem/crypto/signature/Verifier.java deleted file mode 100644 index 0519a0ed..00000000 --- a/src/main/java/org/arkecosystem/crypto/signature/Verifier.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.arkecosystem.crypto.signature; - -import org.bitcoinj.core.ECKey; - -public interface Verifier { - - boolean verify(byte[] hash, ECKey key, byte[] signature); -} diff --git a/src/main/java/org/arkecosystem/crypto/transactions/Deserializer.java b/src/main/java/org/arkecosystem/crypto/transactions/Deserializer.java index ae9c97f1..fc5d3c2e 100644 --- a/src/main/java/org/arkecosystem/crypto/transactions/Deserializer.java +++ b/src/main/java/org/arkecosystem/crypto/transactions/Deserializer.java @@ -1,165 +1,127 @@ package org.arkecosystem.crypto.transactions; +import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.enums.CoreTransactionTypes; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; -import org.arkecosystem.crypto.transactions.types.MultiPayment; -import org.arkecosystem.crypto.transactions.types.MultiSignatureRegistration; -import org.arkecosystem.crypto.transactions.types.SecondSignatureRegistration; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.arkecosystem.crypto.transactions.types.Transfer; -import org.arkecosystem.crypto.transactions.types.UsernameRegistration; -import org.arkecosystem.crypto.transactions.types.UsernameResignation; -import org.arkecosystem.crypto.transactions.types.ValidatorRegistration; -import org.arkecosystem.crypto.transactions.types.ValidatorResignation; -import org.arkecosystem.crypto.transactions.types.Vote; +import org.arkecosystem.crypto.enums.AbiFunction; +import org.arkecosystem.crypto.transactions.types.*; +import org.arkecosystem.crypto.utils.AbiDecoder; public class Deserializer { + private static final int SIGNATURE_SIZE = 64; + private static final int RECOVERY_SIZE = 1; private final ByteBuffer buffer; - private Transaction transaction; - - private final Map> transactionGroups = new HashMap<>(); public Deserializer(String serialized) { - Map coreTransactionTypes = new HashMap<>(); - coreTransactionTypes.put(CoreTransactionTypes.TRANSFER.getValue(), new Transfer()); - coreTransactionTypes.put( - CoreTransactionTypes.SECOND_SIGNATURE_REGISTRATION.getValue(), - new SecondSignatureRegistration()); - coreTransactionTypes.put( - CoreTransactionTypes.VALIDATOR_REGISTRATION.getValue(), - new ValidatorRegistration()); - coreTransactionTypes.put(CoreTransactionTypes.VOTE.getValue(), new Vote()); - coreTransactionTypes.put( - CoreTransactionTypes.MULTI_SIGNATURE_REGISTRATION.getValue(), - new MultiSignatureRegistration()); - coreTransactionTypes.put(CoreTransactionTypes.MULTI_PAYMENT.getValue(), new MultiPayment()); - coreTransactionTypes.put( - CoreTransactionTypes.VALIDATOR_RESIGNATION.getValue(), new ValidatorResignation()); - coreTransactionTypes.put( - CoreTransactionTypes.USERNAME_RESIGNATION.getValue(), new UsernameResignation()); - coreTransactionTypes.put( - CoreTransactionTypes.USERNAME_REGISTRATION.getValue(), new UsernameRegistration()); - - transactionGroups.put(TransactionTypeGroup.CORE.getValue(), coreTransactionTypes); - - this.buffer = ByteBuffer.wrap(Hex.decode(serialized)).slice(); + byte[] bytes = serialized.contains("\0") ? serialized.getBytes() : Hex.decode(serialized); + this.buffer = ByteBuffer.wrap(bytes); this.buffer.order(ByteOrder.LITTLE_ENDIAN); } - public Transaction deserialize() { - this.buffer.get(); + public static Deserializer newDeserializer(String serialized) { + return new Deserializer(serialized); + } + + public AbstractTransaction deserialize() { + int startPosition = buffer.position(); - deserializeCommon(); - deserializeVendorField(); + AbstractTransaction tempTransaction = new EvmCall(); + deserializeCommon(tempTransaction); + deserializeData(tempTransaction); - this.transaction.deserializeData(this.buffer); + AbstractTransaction transaction = guessTransactionFromTransactionData(tempTransaction); - deserializeSignatures(); + buffer.position(startPosition); - this.transaction.computeId(); + deserializeCommon(transaction); + deserializeData(transaction); + deserializeSignatures(transaction); - return this.transaction; - } + transaction.recoverSender(); - private void deserializeCommon() { - int version = this.buffer.get(); - int network = this.buffer.get(); - int typeGroup = this.buffer.getInt(); - int type = this.buffer.getShort(); - long nonce = this.buffer.getLong(); - - this.transaction = this.transactionGroups.get(typeGroup).get(type); - this.transaction.version = version; - this.transaction.network = network; - this.transaction.typeGroup = typeGroup; - this.transaction.type = type; - this.transaction.nonce = nonce; - - byte[] senderPublicKey = new byte[33]; - this.buffer.get(senderPublicKey); - this.transaction.senderPublicKey = Hex.encode(senderPublicKey); - - this.transaction.fee = this.buffer.getLong(); + transaction.computeId(); + + return transaction; } - private void deserializeVendorField() { - int vendorFieldLength = this.buffer.get(); - if (vendorFieldLength > 0) { - byte[] vendorField = new byte[vendorFieldLength]; - this.buffer.get(vendorField); - transaction.vendorField = new String(vendorField); + private AbstractTransaction guessTransactionFromTransactionData( + AbstractTransaction transactionData) { + if (!"0".equals(transactionData.value)) { + return new Transfer(); } - } - private void deserializeSignatures() { - if (canReadNonMultiSignature()) { - byte[] signatureBuffer = new byte[64]; - buffer.get(signatureBuffer); - transaction.signature = Hex.encode(signatureBuffer); + Map payloadData = decodePayload(transactionData); + if (payloadData == null) { + return new EvmCall(); + } + + String functionName = (String) payloadData.get("functionName"); + + if (functionName.equals(AbiFunction.VOTE.toString())) { + return new Vote(transactionData.toHashMap()); + } else if (functionName.equals(AbiFunction.UNVOTE.toString())) { + return new Unvote(transactionData.toHashMap()); + } else if (functionName.equals(AbiFunction.VALIDATOR_REGISTRATION.toString())) { + return new ValidatorRegistration(transactionData.toHashMap()); + } else if (functionName.equals(AbiFunction.VALIDATOR_RESIGNATION.toString())) { + return new ValidatorResignation(transactionData.toHashMap()); } - if (canReadNonMultiSignature()) { - byte[] signatureBuffer = new byte[64]; - buffer.get(signatureBuffer); - transaction.secondSignature = Hex.encode(signatureBuffer); + return new EvmCall(); + } + + private Map decodePayload(AbstractTransaction transaction) { + String payload = transaction.data != null ? transaction.data : ""; + if (payload.isEmpty()) { + return null; } - if (buffer.hasRemaining()) { - if (buffer.remaining() % 65 == 0) { - transaction.signatures = new ArrayList<>(); - - int count = buffer.remaining() / 65; - Set publicKeyIndexes = new HashSet<>(); - for (int i = 0; i < count; i++) { - byte[] signatureBuffer = new byte[65]; - buffer.get(signatureBuffer); - String multiSignaturePart = Hex.encode(signatureBuffer); - int publicKeyIndex = Integer.parseInt(multiSignaturePart.substring(0, 2), 16); - - if (!publicKeyIndexes.contains(publicKeyIndex)) { - publicKeyIndexes.add(publicKeyIndex); - } else { - throw new RuntimeException("Duplicate participant in multi signature"); - } - - transaction.signatures.add(multiSignaturePart); - } - } else { - throw new RuntimeException("signature buffer not exhausted"); - } + try { + AbiDecoder abiDecoder = new AbiDecoder(); + return abiDecoder.decodeFunctionData(payload); + } catch (Exception e) { + return null; } } - private boolean canReadNonMultiSignature() { - return buffer.hasRemaining() - && (buffer.remaining() % 64 == 0 || buffer.remaining() % 65 != 0); + private void deserializeCommon(AbstractTransaction transaction) { + transaction.network = Byte.toUnsignedInt(buffer.get()); + transaction.nonce = buffer.getLong(); + transaction.gasPrice = buffer.getInt(); + transaction.gasLimit = buffer.getInt(); } - public void setNewTransactionType(Transaction transaction) { - if (this.transactionGroups.containsKey(transaction.getTransactionTypeGroup())) { - this.transactionGroups - .get(transaction.getTransactionTypeGroup()) - .put(transaction.getTransactionType(), transaction); + private void deserializeData(AbstractTransaction transaction) { + byte[] valueBytes = new byte[32]; + buffer.get(valueBytes); + transaction.value = new BigInteger(1, valueBytes).toString(); + + int recipientMarker = Byte.toUnsignedInt(buffer.get()); + if (recipientMarker == 1) { + byte[] recipientBytes = new byte[20]; + buffer.get(recipientBytes); + transaction.recipientAddress = "0x" + Hex.encode(recipientBytes); + } + + int payloadLength = buffer.getInt(); + if (payloadLength > 0) { + byte[] payloadBytes = new byte[payloadLength]; + buffer.get(payloadBytes); + transaction.data = Hex.encode(payloadBytes); } else { - Map newTransactionGroup = new HashMap<>(); - newTransactionGroup.put(transaction.getTransactionType(), transaction); - this.transactionGroups.put(transaction.getTransactionTypeGroup(), newTransactionGroup); + transaction.data = ""; } } - public boolean hasTransactionType(int typeGroup, int type) { - if (!this.transactionGroups.containsKey(typeGroup)) { - return false; + private void deserializeSignatures(AbstractTransaction transaction) { + int signatureLength = SIGNATURE_SIZE + RECOVERY_SIZE; + if (buffer.remaining() >= signatureLength) { + byte[] signatureBytes = new byte[signatureLength]; + buffer.get(signatureBytes); + transaction.signature = Hex.encode(signatureBytes); } - return this.transactionGroups.get(typeGroup).containsKey(type); } } diff --git a/src/main/java/org/arkecosystem/crypto/transactions/Serializer.java b/src/main/java/org/arkecosystem/crypto/transactions/Serializer.java index 7df2093d..d5f9e7e1 100644 --- a/src/main/java/org/arkecosystem/crypto/transactions/Serializer.java +++ b/src/main/java/org/arkecosystem/crypto/transactions/Serializer.java @@ -1,131 +1,82 @@ package org.arkecosystem.crypto.transactions; +import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import org.arkecosystem.crypto.configuration.Network; import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.transactions.types.Transaction; +import org.arkecosystem.crypto.transactions.types.AbstractTransaction; public class Serializer { + private final AbstractTransaction transaction; - private final Transaction transaction; - - private Serializer(Transaction transaction) { + private Serializer(AbstractTransaction transaction) { this.transaction = transaction; } - public static byte[] serialize(Transaction transaction) { - return serialize(transaction, false, false, false); - } - - public static byte[] serialize( - Transaction transaction, - boolean skipSignature, - boolean skipSecondSignature, - boolean skipMultiSignature) { - return new Serializer(transaction) - .serialize(skipSignature, skipSecondSignature, skipMultiSignature); + public static Serializer newSerializer(AbstractTransaction transaction) { + return new Serializer(transaction); } - public byte[] serialize( - boolean skipSignature, boolean skipSecondSignature, boolean skipMultiSignature) { - - byte[] common = serializeCommon(); - byte[] vendorField = serializeVendorField(); - - byte[] typeBuffer = this.transaction.serializeData(); - - byte[] signatures = - serializeSignatures(skipSignature, skipSecondSignature, skipMultiSignature); - - ByteBuffer buffer = - ByteBuffer.allocate( - common.length + vendorField.length + typeBuffer.length + signatures.length); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - buffer.put(common); - buffer.put(vendorField); - buffer.put(typeBuffer); - buffer.put(signatures); - - return buffer.array(); + public static byte[] getBytes(AbstractTransaction transaction, boolean skipSignature) { + return new Serializer(transaction).serialize(skipSignature); } - private byte[] serializeCommon() { - ByteBuffer buffer = ByteBuffer.allocate(58); + public byte[] serialize(boolean skipSignature) { + ByteBuffer buffer = ByteBuffer.allocate(1024); buffer.order(ByteOrder.LITTLE_ENDIAN); - buffer.put((byte) 0xff); - if (this.transaction.version > 0) { - buffer.put((byte) this.transaction.version); - } else { - buffer.put((byte) 0x01); - } - if (this.transaction.network > 0) { - buffer.put((byte) this.transaction.network); - } else { - buffer.put((byte) Network.get().version()); - } + serializeCommon(buffer); - buffer.putInt(this.transaction.typeGroup); - buffer.putShort((short) this.transaction.type); - buffer.putLong(this.transaction.nonce); + serializeData(buffer); - if (this.transaction.senderPublicKey != null) { - buffer.put(Hex.decode(this.transaction.senderPublicKey)); - } - buffer.putLong(this.transaction.fee); + serializeSignatures(buffer, skipSignature); - return buffer.array(); + byte[] result = new byte[buffer.position()]; + buffer.flip(); + buffer.get(result); + return result; } - private byte[] serializeVendorField() { - ByteBuffer buffer = ByteBuffer.allocate(1); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - if (this.transaction.hasVendorField()) { - if (this.transaction.vendorField != null && !this.transaction.vendorField.equals("")) { - int vendorFieldLength = this.transaction.vendorField.length(); - buffer = ByteBuffer.allocate(vendorFieldLength + 1); - buffer.put((byte) vendorFieldLength); - buffer.put(this.transaction.vendorField.getBytes()); - } else if (this.transaction.vendorFieldHex != null - && !this.transaction.vendorFieldHex.isEmpty()) { - int vendorFieldHexLength = this.transaction.vendorFieldHex.length(); - buffer = ByteBuffer.allocate(vendorFieldHexLength + 1); - buffer.put((byte) (vendorFieldHexLength / 2)); - buffer.put(Hex.decode(this.transaction.vendorFieldHex)); - } else { - buffer.put((byte) 0x00); - } - } else { - buffer.put((byte) 0x00); - } - - return buffer.array(); + private void serializeCommon(ByteBuffer buffer) { + buffer.put((byte) transaction.network); + buffer.putLong(transaction.nonce); + buffer.putInt((int) transaction.gasPrice); + buffer.putInt((int) transaction.gasLimit); } - private byte[] serializeSignatures( - boolean skipSignature, boolean skipSecondSignature, boolean skipMultiSignature) { - ByteBuffer buffer = ByteBuffer.allocate(16 * 65); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - if (!skipSignature && this.transaction.signature != null) { - buffer.put(Hex.decode(this.transaction.signature)); + private void serializeData(ByteBuffer buffer) { + // Convert 'value' from String to BigInteger and write as Uint256 (32 bytes) + byte[] valueBytes = new BigInteger(transaction.value).toByteArray(); + byte[] valueBytesPadded = new byte[32]; + int srcPos = Math.max(0, valueBytes.length - 32); + int destPos = 32 - (valueBytes.length - srcPos); + System.arraycopy(valueBytes, srcPos, valueBytesPadded, destPos, valueBytes.length - srcPos); + buffer.put(valueBytesPadded); + + // Write recipient marker and address + if (transaction.recipientAddress != null && !transaction.recipientAddress.isEmpty()) { + buffer.put((byte) 1); + byte[] recipientBytes = + Hex.decode(transaction.recipientAddress.replaceFirst("^0x", "").toLowerCase()); + + buffer.put(recipientBytes); + } else { + buffer.put((byte) 0); } - if (!skipSecondSignature && this.transaction.secondSignature != null) { - buffer.put(Hex.decode(this.transaction.secondSignature)); + // Write payload length as UInt32 and the payload itself if present + String payloadHex = + transaction.data != null ? transaction.data.replaceFirst("^0x", "") : ""; + int payloadLength = payloadHex.length() / 2; + buffer.putInt(payloadLength); + if (payloadLength > 0) { + buffer.put(Hex.decode(payloadHex)); } + } - if (!skipMultiSignature && this.transaction.signatures != null) { - buffer.put(Hex.decode(String.join("", this.transaction.signatures))); + private void serializeSignatures(ByteBuffer buffer, boolean skipSignature) { + if (!skipSignature && transaction.signature != null) { + buffer.put(Hex.decode(transaction.signature)); } - - byte[] result = new byte[buffer.position()]; - buffer.rewind(); - buffer.get(result); - - return result; } } diff --git a/src/main/java/org/arkecosystem/crypto/transactions/TransactionAsset.java b/src/main/java/org/arkecosystem/crypto/transactions/TransactionAsset.java index 5d0fef1d..650d0449 100644 --- a/src/main/java/org/arkecosystem/crypto/transactions/TransactionAsset.java +++ b/src/main/java/org/arkecosystem/crypto/transactions/TransactionAsset.java @@ -1,40 +1,43 @@ package org.arkecosystem.crypto.transactions; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; public class TransactionAsset { - public Signature signature = new Signature(); - public List votes = new ArrayList<>(); - public List unvotes = new ArrayList<>(); - public MultiSignature multiSignature = new MultiSignature(); - public MultiPayment multiPayment = new MultiPayment(); - public HashMap customAsset = new HashMap<>(); - public long amount = 0L; - public String validatorPublicKey; - public String username; - - public static class Signature { - public String publicKey; - } + public EvmCall evmCall = new EvmCall(); // Instance of EvmCall + public String vote = ""; + public String validatorPublicKey = ""; - public static class MultiSignature { - public byte min; - public List publicKeys = new ArrayList<>(); - } + public static class EvmCall { + public long gasLimit = 1000000; // Default gas limit + public String payload = ""; // EVM code in hexadecimal format - public static class MultiPayment { - public List payments = new ArrayList<>(); + // Converts the EvmCall object to a HashMap for serialization + public HashMap toHashMap() { + HashMap map = new HashMap<>(); + map.put("gasLimit", this.gasLimit); + map.put("payload", this.payload); + return map; + } } - public static class Payment { - public long amount; - public String recipientId; + public HashMap toHashMap() { + HashMap map = new HashMap<>(); + + // Adds "evmCall" to the map if it's defined + if (evmCall != null) { + map.put("evmCall", evmCall.toHashMap()); + } + + // Adds "vote" to the map if it's not empty + if (vote != null && !vote.isEmpty()) { + map.put("vote", this.vote); + } - public Payment(long amount, String recipientId) { - this.amount = amount; - this.recipientId = recipientId; + // Adds "vote" to the map if it's not empty + if (validatorPublicKey != null && !validatorPublicKey.isEmpty()) { + map.put("validatorPublicKey", this.validatorPublicKey); } + + return map; } } diff --git a/src/main/java/org/arkecosystem/crypto/transactions/builder/AbstractTransactionBuilder.java b/src/main/java/org/arkecosystem/crypto/transactions/builder/AbstractTransactionBuilder.java index 6cf7db51..c05faa4b 100644 --- a/src/main/java/org/arkecosystem/crypto/transactions/builder/AbstractTransactionBuilder.java +++ b/src/main/java/org/arkecosystem/crypto/transactions/builder/AbstractTransactionBuilder.java @@ -1,69 +1,69 @@ package org.arkecosystem.crypto.transactions.builder; import org.arkecosystem.crypto.configuration.Network; -import org.arkecosystem.crypto.transactions.types.Transaction; +import org.arkecosystem.crypto.transactions.types.AbstractTransaction; public abstract class AbstractTransactionBuilder< TBuilder extends AbstractTransactionBuilder> { - public final Transaction transaction; + public final AbstractTransaction transaction; public AbstractTransactionBuilder() { this.transaction = getTransactionInstance(); - this.transaction.type = this.transaction.getTransactionType(); - this.transaction.version = 2; + + initializeTransactionDefaults(); + } + + private void initializeTransactionDefaults() { + this.transaction.value = "0"; + this.transaction.senderPublicKey = ""; + this.transaction.fee = '5'; + this.transaction.nonce = 1; this.transaction.network = Network.get().version(); - this.transaction.typeGroup = this.transaction.getTransactionTypeGroup(); - this.transaction.nonce = 0; - this.transaction.amount = 0; + this.transaction.gasLimit = 1_000_000; + // Set the default data for the transaction + this.transaction.refreshPayloadData(); } - public TBuilder version(int version) { - this.transaction.version = version; + public TBuilder gasLimit(int gasLimit) { + this.transaction.gasLimit = gasLimit; return this.instance(); } - public TBuilder nonce(long nonce) { - this.transaction.nonce = nonce; + public TBuilder recipientAddress(String recipientAddressId) { + this.transaction.recipientAddress = recipientAddressId; return this.instance(); } - public TBuilder network(int network) { - this.transaction.network = network; + public TBuilder gasPrice(int gasPrice) { + this.transaction.gasPrice = gasPrice; return this.instance(); } - public TBuilder fee(long fee) { - this.transaction.fee = fee; + public TBuilder nonce(long nonce) { + this.transaction.nonce = nonce; return this.instance(); } - public TBuilder amount(long amount) { - this.transaction.amount = amount; + public TBuilder network(int network) { + this.transaction.network = network; return this.instance(); } public TBuilder sign(String passphrase) { this.transaction.sign(passphrase); this.transaction.computeId(); - return this.instance(); } - public TBuilder secondSign(String passphrase) { - this.transaction.secondSign(passphrase); - this.transaction.computeId(); - - return this.instance(); + public boolean verify() { + return this.transaction.verify(); } - public TBuilder multiSign(String passphrase, int index) { - this.transaction.multiSign(passphrase, index); - this.transaction.computeId(); - - return this.instance(); + public String toJson() { + return this.transaction.toJson(); } - protected abstract Transaction getTransactionInstance(); + protected abstract AbstractTransaction getTransactionInstance(); protected abstract TBuilder instance(); } diff --git a/src/main/java/org/arkecosystem/crypto/transactions/builder/EvmCallBuilder.java b/src/main/java/org/arkecosystem/crypto/transactions/builder/EvmCallBuilder.java new file mode 100644 index 00000000..09617603 --- /dev/null +++ b/src/main/java/org/arkecosystem/crypto/transactions/builder/EvmCallBuilder.java @@ -0,0 +1,24 @@ +package org.arkecosystem.crypto.transactions.builder; + +import org.arkecosystem.crypto.transactions.types.AbstractTransaction; +import org.arkecosystem.crypto.transactions.types.EvmCall; + +public class EvmCallBuilder extends AbstractTransactionBuilder { + public EvmCallBuilder payload(String payload) { + String cleanedPayload = payload.replaceFirst("^0x", ""); + + this.transaction.data = cleanedPayload; + + return this.instance(); + } + + @Override + protected AbstractTransaction getTransactionInstance() { + return new EvmCall(); + } + + @Override + protected EvmCallBuilder instance() { + return this; + } +} diff --git a/src/main/java/org/arkecosystem/crypto/transactions/builder/MultiPaymentBuilder.java b/src/main/java/org/arkecosystem/crypto/transactions/builder/MultiPaymentBuilder.java deleted file mode 100644 index 61f56d59..00000000 --- a/src/main/java/org/arkecosystem/crypto/transactions/builder/MultiPaymentBuilder.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.arkecosystem.crypto.transactions.builder; - -import org.arkecosystem.crypto.enums.Fees; -import org.arkecosystem.crypto.transactions.TransactionAsset; -import org.arkecosystem.crypto.transactions.types.MultiPayment; -import org.arkecosystem.crypto.transactions.types.Transaction; - -public class MultiPaymentBuilder extends AbstractTransactionBuilder { - - public MultiPaymentBuilder() { - super(); - this.transaction.fee = Fees.MULTI_PAYMENT.getValue(); - } - - public MultiPaymentBuilder addPayment(String recipientId, long amount) { - if (this.transaction.asset.multiPayment.payments.size() >= 64) { - throw new MaximumPaymentCountExceededError(); - } - this.transaction.asset.multiPayment.payments.add( - new TransactionAsset.Payment(amount, recipientId)); - return this; - } - - public MultiPaymentBuilder vendorField(String vendorField) { - this.transaction.vendorField = vendorField; - - return this; - } - - @Override - public Transaction getTransactionInstance() { - return new MultiPayment(); - } - - @Override - public MultiPaymentBuilder instance() { - return this; - } -} - -class MaximumPaymentCountExceededError extends RuntimeException { - MaximumPaymentCountExceededError() { - super("Expected a maximum of 64 payments"); - } -} diff --git a/src/main/java/org/arkecosystem/crypto/transactions/builder/MultiSignatureRegistrationBuilder.java b/src/main/java/org/arkecosystem/crypto/transactions/builder/MultiSignatureRegistrationBuilder.java deleted file mode 100644 index 7955c5f2..00000000 --- a/src/main/java/org/arkecosystem/crypto/transactions/builder/MultiSignatureRegistrationBuilder.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.arkecosystem.crypto.transactions.builder; - -import java.util.List; -import org.arkecosystem.crypto.enums.Fees; -import org.arkecosystem.crypto.transactions.types.MultiSignatureRegistration; -import org.arkecosystem.crypto.transactions.types.Transaction; - -public class MultiSignatureRegistrationBuilder - extends AbstractTransactionBuilder { - public MultiSignatureRegistrationBuilder() { - super(); - this.transaction.fee = Fees.MULTI_SIGNATURE_REGISTRATION.getValue(); - } - - @Override - public Transaction getTransactionInstance() { - return new MultiSignatureRegistration(); - } - - public MultiSignatureRegistrationBuilder min(int min) { - return this.min((byte) min); - } - - public MultiSignatureRegistrationBuilder min(byte min) { - this.transaction.asset.multiSignature.min = min; - - return this; - } - - public MultiSignatureRegistrationBuilder publicKeys(List publicKeys) { - this.transaction.asset.multiSignature.publicKeys = publicKeys; - - this.transaction.fee = (publicKeys.size() + 1) * this.transaction.fee; - - return this; - } - - @Override - public MultiSignatureRegistrationBuilder instance() { - return this; - } -} diff --git a/src/main/java/org/arkecosystem/crypto/transactions/builder/TransferBuilder.java b/src/main/java/org/arkecosystem/crypto/transactions/builder/TransferBuilder.java index 83ce42bc..5d6698f7 100644 --- a/src/main/java/org/arkecosystem/crypto/transactions/builder/TransferBuilder.java +++ b/src/main/java/org/arkecosystem/crypto/transactions/builder/TransferBuilder.java @@ -1,43 +1,24 @@ package org.arkecosystem.crypto.transactions.builder; -import org.arkecosystem.crypto.enums.Fees; -import org.arkecosystem.crypto.transactions.types.Transaction; +import org.arkecosystem.crypto.transactions.types.AbstractTransaction; import org.arkecosystem.crypto.transactions.types.Transfer; public class TransferBuilder extends AbstractTransactionBuilder { + public TransferBuilder value(String value) { + this.transaction.value = value; - public TransferBuilder() { - super(); - this.transaction.fee = Fees.TRANSFER.getValue(); - } - - public TransferBuilder recipient(String recipientId) { - this.transaction.recipientId = recipientId; - return this; - } + this.transaction.refreshPayloadData(); - public TransferBuilder amount(long amount) { - this.transaction.amount = amount; - return this; - } - - public TransferBuilder expiration(int expiration) { - this.transaction.expiration = expiration; - return this; - } - - public TransferBuilder vendorField(String vendorField) { - this.transaction.vendorField = vendorField; - return this; + return this.instance(); } @Override - public Transaction getTransactionInstance() { + protected AbstractTransaction getTransactionInstance() { return new Transfer(); } @Override - public TransferBuilder instance() { + protected TransferBuilder instance() { return this; } } diff --git a/src/main/java/org/arkecosystem/crypto/transactions/builder/UnvoteBuilder.java b/src/main/java/org/arkecosystem/crypto/transactions/builder/UnvoteBuilder.java new file mode 100644 index 00000000..88ae02b7 --- /dev/null +++ b/src/main/java/org/arkecosystem/crypto/transactions/builder/UnvoteBuilder.java @@ -0,0 +1,17 @@ +package org.arkecosystem.crypto.transactions.builder; + +import org.arkecosystem.crypto.transactions.types.AbstractTransaction; +import org.arkecosystem.crypto.transactions.types.Unvote; + +public class UnvoteBuilder extends AbstractTransactionBuilder { + + @Override + protected AbstractTransaction getTransactionInstance() { + return new Unvote(); + } + + @Override + protected UnvoteBuilder instance() { + return this; + } +} diff --git a/src/main/java/org/arkecosystem/crypto/transactions/builder/UsernameRegistrationBuilder.java b/src/main/java/org/arkecosystem/crypto/transactions/builder/UsernameRegistrationBuilder.java deleted file mode 100644 index 19592102..00000000 --- a/src/main/java/org/arkecosystem/crypto/transactions/builder/UsernameRegistrationBuilder.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.arkecosystem.crypto.transactions.builder; - -import org.arkecosystem.crypto.enums.Fees; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.arkecosystem.crypto.transactions.types.UsernameRegistration; - -public class UsernameRegistrationBuilder - extends AbstractTransactionBuilder { - public UsernameRegistrationBuilder() { - super(); - this.transaction.fee = Fees.VALIDATOR_REGISTRATION.getValue(); - } - - public UsernameRegistrationBuilder usernameAsset(String username) { - this.transaction.asset.username = username; - - return this; - } - - @Override - public Transaction getTransactionInstance() { - return new UsernameRegistration(); - } - - @Override - public UsernameRegistrationBuilder instance() { - return this; - } -} diff --git a/src/main/java/org/arkecosystem/crypto/transactions/builder/UsernameResignationBuilder.java b/src/main/java/org/arkecosystem/crypto/transactions/builder/UsernameResignationBuilder.java deleted file mode 100644 index 7fee046d..00000000 --- a/src/main/java/org/arkecosystem/crypto/transactions/builder/UsernameResignationBuilder.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.arkecosystem.crypto.transactions.builder; - -import org.arkecosystem.crypto.enums.Fees; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.arkecosystem.crypto.transactions.types.UsernameResignation; - -public class UsernameResignationBuilder - extends AbstractTransactionBuilder { - - public UsernameResignationBuilder() { - super(); - this.transaction.fee = Fees.USERNAME_RESIGNATION.getValue(); - } - - @Override - public Transaction getTransactionInstance() { - return new UsernameResignation(); - } - - @Override - public UsernameResignationBuilder instance() { - return this; - } -} diff --git a/src/main/java/org/arkecosystem/crypto/transactions/builder/ValidatorRegistrationBuilder.java b/src/main/java/org/arkecosystem/crypto/transactions/builder/ValidatorRegistrationBuilder.java index 37993eee..01049455 100644 --- a/src/main/java/org/arkecosystem/crypto/transactions/builder/ValidatorRegistrationBuilder.java +++ b/src/main/java/org/arkecosystem/crypto/transactions/builder/ValidatorRegistrationBuilder.java @@ -1,29 +1,25 @@ package org.arkecosystem.crypto.transactions.builder; -import org.arkecosystem.crypto.enums.Fees; -import org.arkecosystem.crypto.transactions.types.Transaction; +import org.arkecosystem.crypto.transactions.types.AbstractTransaction; import org.arkecosystem.crypto.transactions.types.ValidatorRegistration; public class ValidatorRegistrationBuilder extends AbstractTransactionBuilder { - public ValidatorRegistrationBuilder() { - super(); - this.transaction.fee = Fees.VALIDATOR_REGISTRATION.getValue(); - } + public ValidatorRegistrationBuilder validatorPublicKey(String validatorPublicKey) { + this.transaction.validatorPublicKey = validatorPublicKey; - public ValidatorRegistrationBuilder publicKeyAsset(String publicKey) { - this.transaction.asset.validatorPublicKey = publicKey; + this.transaction.refreshPayloadData(); - return this; + return this.instance(); } @Override - public Transaction getTransactionInstance() { + protected AbstractTransaction getTransactionInstance() { return new ValidatorRegistration(); } @Override - public ValidatorRegistrationBuilder instance() { + protected ValidatorRegistrationBuilder instance() { return this; } } diff --git a/src/main/java/org/arkecosystem/crypto/transactions/builder/ValidatorResignationBuilder.java b/src/main/java/org/arkecosystem/crypto/transactions/builder/ValidatorResignationBuilder.java index 220f0e44..72d8499a 100644 --- a/src/main/java/org/arkecosystem/crypto/transactions/builder/ValidatorResignationBuilder.java +++ b/src/main/java/org/arkecosystem/crypto/transactions/builder/ValidatorResignationBuilder.java @@ -1,24 +1,18 @@ package org.arkecosystem.crypto.transactions.builder; -import org.arkecosystem.crypto.enums.Fees; -import org.arkecosystem.crypto.transactions.types.Transaction; +import org.arkecosystem.crypto.transactions.types.AbstractTransaction; import org.arkecosystem.crypto.transactions.types.ValidatorResignation; public class ValidatorResignationBuilder extends AbstractTransactionBuilder { - public ValidatorResignationBuilder() { - super(); - this.transaction.fee = Fees.VALIDATOR_RESIGNATION.getValue(); - } - @Override - public Transaction getTransactionInstance() { + protected AbstractTransaction getTransactionInstance() { return new ValidatorResignation(); } @Override - public ValidatorResignationBuilder instance() { + protected ValidatorResignationBuilder instance() { return this; } } diff --git a/src/main/java/org/arkecosystem/crypto/transactions/builder/VoteBuilder.java b/src/main/java/org/arkecosystem/crypto/transactions/builder/VoteBuilder.java index 33423d85..c9ba1098 100644 --- a/src/main/java/org/arkecosystem/crypto/transactions/builder/VoteBuilder.java +++ b/src/main/java/org/arkecosystem/crypto/transactions/builder/VoteBuilder.java @@ -1,53 +1,25 @@ package org.arkecosystem.crypto.transactions.builder; -import java.util.List; -import org.arkecosystem.crypto.enums.Fees; -import org.arkecosystem.crypto.identities.Address; -import org.arkecosystem.crypto.transactions.types.Transaction; +import org.arkecosystem.crypto.transactions.types.AbstractTransaction; import org.arkecosystem.crypto.transactions.types.Vote; public class VoteBuilder extends AbstractTransactionBuilder { - public VoteBuilder() { - super(); - this.transaction.fee = Fees.VOTE.getValue(); - } - - public VoteBuilder addVotes(List votes) { - this.transaction.asset.votes = votes; - return this; - } + public VoteBuilder vote(String vote) { + this.transaction.vote = vote; - public VoteBuilder addVote(String vote) { - this.transaction.asset.votes.add(vote); - return this; - } + this.transaction.refreshPayloadData(); - public VoteBuilder addUnvotes(List unvotes) { - this.transaction.asset.unvotes = unvotes; - return this; - } - - public VoteBuilder addUnvote(String unvote) { - this.transaction.asset.unvotes.add(unvote); - return this; - } - - public VoteBuilder sign(String passphrase) { - this.transaction.recipientId = Address.fromPassphrase(passphrase); - - super.sign(passphrase); - - return this; + return this.instance(); } @Override - public Transaction getTransactionInstance() { + protected AbstractTransaction getTransactionInstance() { return new Vote(); } @Override - public VoteBuilder instance() { + protected VoteBuilder instance() { return this; } } diff --git a/src/main/java/org/arkecosystem/crypto/transactions/types/AbstractTransaction.java b/src/main/java/org/arkecosystem/crypto/transactions/types/AbstractTransaction.java new file mode 100644 index 00000000..abbf76b6 --- /dev/null +++ b/src/main/java/org/arkecosystem/crypto/transactions/types/AbstractTransaction.java @@ -0,0 +1,267 @@ +package org.arkecosystem.crypto.transactions.types; + +import com.google.gson.GsonBuilder; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.arkecosystem.crypto.encoding.Hex; +import org.arkecosystem.crypto.identities.Address; +import org.arkecosystem.crypto.identities.PrivateKey; +import org.arkecosystem.crypto.transactions.Serializer; +import org.arkecosystem.crypto.utils.AbiDecoder; +import org.arkecosystem.crypto.utils.TransactionHasher; +import org.bitcoinj.core.ECKey; +import org.bitcoinj.core.Sha256Hash; + +public abstract class AbstractTransaction { + public int network; + public long nonce; + public String senderPublicKey; + public String senderAddress; + public String data; + public long fee = 0L; + public String signature; + public String value = "0"; + public String recipientAddress; + public String id; + public int gasLimit; + public int gasPrice; + public String validatorPublicKey; + public String vote; + + public AbstractTransaction() {} + + public AbstractTransaction(Map data) { + if (data.containsKey("network")) { + this.network = ((Number) data.get("network")).intValue(); + } + if (data.containsKey("nonce")) { + this.nonce = Long.parseLong(data.get("nonce").toString()); + } + if (data.containsKey("gasPrice")) { + this.gasPrice = ((Number) data.get("gasPrice")).intValue(); + } + if (data.containsKey("gasLimit")) { + this.gasLimit = ((Number) data.get("gasLimit")).intValue(); + } + if (data.containsKey("recipientAddress")) { + this.recipientAddress = (String) data.get("recipientAddress"); + } + if (data.containsKey("value")) { + this.value = data.get("value").toString(); + } + if (data.containsKey("data")) { + this.data = (String) data.get("data"); + } + if (data.containsKey("signature")) { + this.signature = (String) data.get("signature"); + } + if (data.containsKey("senderPublicKey")) { + this.senderPublicKey = (String) data.get("senderPublicKey"); + } + if (data.containsKey("id")) { + this.id = (String) data.get("id"); + } + if (data.containsKey("validatorPublicKey")) { + this.validatorPublicKey = (String) data.get("validatorPublicKey"); + } + if (data.containsKey("vote")) { + this.vote = (String) data.get("vote"); + } + if (data.containsKey("senderAddress")) { + this.senderAddress = (String) data.get("senderAddress"); + } + } + + public String getPayload() { + return this.data != null ? this.data : ""; + } + + public AbstractTransaction refreshPayloadData() { + this.data = getPayload(); + return this; + } + + public void computeId() { + this.id = this.getId(); + } + + public String getId() { + return Hex.encode(hash(false)); + } + + public byte[] hash(boolean skipSignature) { + HashMap map = new HashMap<>(); + map.put("gasPrice", this.gasPrice); + map.put("network", this.network); + map.put("nonce", this.nonce); + map.put("value", this.value); + map.put("gasLimit", this.gasLimit); + map.put("data", this.data); + map.put("recipientAddress", this.recipientAddress); + if (!skipSignature && this.signature != null) { + map.put("signature", this.signature); + } + return TransactionHasher.toHash(map, skipSignature); + } + + public AbstractTransaction sign(String passphrase) { + byte[] hash = this.hash(true); + + ECKey privateKey = PrivateKey.fromPassphrase(passphrase); + this.senderPublicKey = privateKey.getPublicKeyAsHex(); + + ECKey.ECDSASignature signature = privateKey.sign(Sha256Hash.wrap(hash)); + + int recId = -1; + for (int i = 0; i < 4; i++) { + ECKey k = ECKey.recoverFromSignature(i, signature, Sha256Hash.wrap(hash), true); + if (k != null && k.getPubKeyPoint().equals(privateKey.getPubKeyPoint())) { + recId = i; + break; + } + } + if (recId == -1) { + throw new RuntimeException("Could not find recId"); + } + + byte[] rBytes = bigIntegerToBytes(signature.r, 32); + byte[] sBytes = bigIntegerToBytes(signature.s, 32); + + byte[] signatureBytes = new byte[64]; + System.arraycopy(rBytes, 0, signatureBytes, 0, 32); + System.arraycopy(sBytes, 0, signatureBytes, 32, 32); + + byte[] signatureWithRecId = new byte[65]; + System.arraycopy(signatureBytes, 0, signatureWithRecId, 0, 64); + signatureWithRecId[64] = (byte) recId; + + this.signature = Hex.encode(signatureWithRecId); + + return this; + } + + private static byte[] bigIntegerToBytes(BigInteger b, int numBytes) { + byte[] src = b.toByteArray(); + byte[] dest = new byte[numBytes]; + int srcPos = Math.max(0, src.length - numBytes); + int destPos = Math.max(0, numBytes - src.length); + int length = Math.min(src.length, numBytes); + System.arraycopy(src, srcPos, dest, destPos, length); + return dest; + } + + public void recoverSender() { + if (this.signature == null || this.signature.length() != 130) { + throw new RuntimeException("Invalid signature"); + } + + byte[] signatureWithRecId = Hex.decode(this.signature); + if (signatureWithRecId.length != 65) { + throw new RuntimeException("Invalid signature length"); + } + + byte recId = signatureWithRecId[64]; + byte[] signatureBytes = Arrays.copyOfRange(signatureWithRecId, 0, 64); + + BigInteger r = new BigInteger(1, Arrays.copyOfRange(signatureBytes, 0, 32)); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signatureBytes, 32, 64)); + + ECKey.ECDSASignature signature = new ECKey.ECDSASignature(r, s); + + byte[] hash = this.hash(true); + + ECKey recoveredKey = + ECKey.recoverFromSignature(recId, signature, Sha256Hash.wrap(hash), true); + if (recoveredKey == null) { + throw new RuntimeException("Could not recover public key from signature"); + } + + this.senderPublicKey = recoveredKey.getPublicKeyAsHex(); + + // Compute the sender's address (EVM address) + this.senderAddress = Address.fromPublicKey(this.senderPublicKey); + } + + public boolean verify() { + try { + if (this.senderPublicKey == null) { + this.recoverSender(); + } + + ECKey keys = ECKey.fromPublicOnly(Hex.decode(this.senderPublicKey)); + + byte[] signatureWithRecId = Hex.decode(this.signature); + if (signatureWithRecId.length != 65) { + return false; + } + + byte recId = signatureWithRecId[64]; + byte[] signatureBytes = Arrays.copyOfRange(signatureWithRecId, 0, 64); + + BigInteger r = new BigInteger(1, Arrays.copyOfRange(signatureBytes, 0, 32)); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signatureBytes, 32, 64)); + + ECKey.ECDSASignature signature = new ECKey.ECDSASignature(r, s); + + byte[] hash = this.hash(true); + + ECKey recoveredKey = + ECKey.recoverFromSignature(recId, signature, Sha256Hash.wrap(hash), true); + if (recoveredKey == null) { + return false; + } + + return recoveredKey.getPubKeyPoint().equals(keys.getPubKeyPoint()); + } catch (Exception e) { + return false; + } + } + + public byte[] serialize() { + return serialize(false); + } + + public byte[] serialize(boolean skipSignature) { + return Serializer.newSerializer(this).serialize(skipSignature); + } + + public String toJson() { + return new GsonBuilder().create().toJson(this.toHashMap()); + } + + public HashMap toHashMap() { + HashMap map = new HashMap<>(); + map.put("gasPrice", this.gasPrice); + map.put("network", this.network); + map.put("id", this.id); + map.put("gasLimit", this.gasLimit); + map.put("nonce", this.nonce); + map.put("senderPublicKey", this.senderPublicKey); + map.put("senderAddress", this.senderAddress); + map.put("signature", this.signature); + map.put("recipientAddress", this.recipientAddress); + map.put("value", this.value); + map.put("data", this.data); + return map; + } + + public List decodePayload(Map data) { + if (data == null || !data.containsKey("data")) return null; + + String payload = (String) data.get("data"); + if (payload == null || payload.isEmpty()) return null; + + try { + AbiDecoder abiDecoder = new AbiDecoder(); + Map decodedData = abiDecoder.decodeFunctionData(payload); + return (List) decodedData.get("args"); + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } +} diff --git a/src/main/java/org/arkecosystem/crypto/transactions/types/EvmCall.java b/src/main/java/org/arkecosystem/crypto/transactions/types/EvmCall.java new file mode 100644 index 00000000..61677c55 --- /dev/null +++ b/src/main/java/org/arkecosystem/crypto/transactions/types/EvmCall.java @@ -0,0 +1,18 @@ +package org.arkecosystem.crypto.transactions.types; + +import java.util.Map; + +public class EvmCall extends AbstractTransaction { + public EvmCall() { + super(); + } + + public EvmCall(Map data) { + super(data); + } + + @Override + public String getPayload() { + return this.data != null ? this.data : ""; + } +} diff --git a/src/main/java/org/arkecosystem/crypto/transactions/types/MultiPayment.java b/src/main/java/org/arkecosystem/crypto/transactions/types/MultiPayment.java deleted file mode 100644 index 68455a29..00000000 --- a/src/main/java/org/arkecosystem/crypto/transactions/types/MultiPayment.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.arkecosystem.crypto.transactions.types; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.HashMap; -import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.enums.CoreTransactionTypes; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; -import org.arkecosystem.crypto.transactions.TransactionAsset; -import org.arkecosystem.crypto.utils.Address; -import org.web3j.crypto.Keys; - -public class MultiPayment extends Transaction { - - @Override - public int getTransactionType() { - return CoreTransactionTypes.MULTI_PAYMENT.getValue(); - } - - @Override - public int getTransactionTypeGroup() { - return TransactionTypeGroup.CORE.getValue(); - } - - @Override - public boolean hasVendorField() { - return true; - } - - @Override - public HashMap assetToHashMap() { - HashMap asset = new HashMap<>(); - ArrayList> payments = new ArrayList<>(); - for (TransactionAsset.Payment current : this.asset.multiPayment.payments) { - HashMap payment = new HashMap<>(); - payment.put("amount", String.valueOf(current.amount)); - payment.put("recipientId", current.recipientId); - payments.add(payment); - } - asset.put("payments", payments); - return asset; - } - - @Override - public byte[] serializeData() { - ByteBuffer buffer = ByteBuffer.allocate(2 + this.asset.multiPayment.payments.size() * 28); - buffer.order(ByteOrder.LITTLE_ENDIAN); - buffer.putShort((short) this.asset.multiPayment.payments.size()); - for (TransactionAsset.Payment current : this.asset.multiPayment.payments) { - buffer.putLong(current.amount); - byte[] recipientBytes = Hex.decode(Address.toBufferHexString(current.recipientId)); - buffer.put(recipientBytes); - } - return buffer.array(); - } - - @Override - public void deserializeData(ByteBuffer buffer) { - int paymentLength = buffer.getShort() & 0xff; - - for (int i = 0; i < paymentLength; i++) { - byte[] recipientId = new byte[20]; - long amount = buffer.getLong(); - buffer.get(recipientId); - this.asset.multiPayment.payments.add( - new TransactionAsset.Payment( - amount, Keys.toChecksumAddress("0x" + Hex.encode(recipientId)))); - } - - this.asset.amount = - this.asset.multiPayment.payments.stream().mapToLong(p -> p.amount).sum(); - } -} diff --git a/src/main/java/org/arkecosystem/crypto/transactions/types/MultiSignatureRegistration.java b/src/main/java/org/arkecosystem/crypto/transactions/types/MultiSignatureRegistration.java deleted file mode 100644 index a702f256..00000000 --- a/src/main/java/org/arkecosystem/crypto/transactions/types/MultiSignatureRegistration.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.arkecosystem.crypto.transactions.types; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.HashMap; -import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.enums.CoreTransactionTypes; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; - -public class MultiSignatureRegistration extends Transaction { - - @Override - public int getTransactionType() { - return CoreTransactionTypes.MULTI_SIGNATURE_REGISTRATION.getValue(); - } - - @Override - public int getTransactionTypeGroup() { - return TransactionTypeGroup.CORE.getValue(); - } - - @Override - public HashMap assetToHashMap() { - HashMap asset = new HashMap<>(); - - HashMap publicKey = new HashMap<>(); - publicKey.put("min", this.asset.multiSignature.min); - publicKey.put("publicKeys", this.asset.multiSignature.publicKeys); - - asset.put("multiSignature", publicKey); - return asset; - } - - @Override - public byte[] serializeData() { - ByteBuffer buffer = - ByteBuffer.allocate(2 + this.asset.multiSignature.publicKeys.size() * 33); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - buffer.put(this.asset.multiSignature.min); - - buffer.put((byte) this.asset.multiSignature.publicKeys.size()); - for (String publicKey : this.asset.multiSignature.publicKeys) { - buffer.put(Hex.decode(publicKey)); - } - - return buffer.array(); - } - - @Override - public void deserializeData(ByteBuffer buffer) { - this.asset.multiSignature.min = buffer.get(); - - int publicKeyLength = buffer.get(); - for (int i = 0; i < publicKeyLength; i++) { - byte[] publicKeyBuffer = new byte[33]; - buffer.get(publicKeyBuffer); - this.asset.multiSignature.publicKeys.add(Hex.encode(publicKeyBuffer)); - } - } -} diff --git a/src/main/java/org/arkecosystem/crypto/transactions/types/SecondSignatureRegistration.java b/src/main/java/org/arkecosystem/crypto/transactions/types/SecondSignatureRegistration.java deleted file mode 100644 index 565a31be..00000000 --- a/src/main/java/org/arkecosystem/crypto/transactions/types/SecondSignatureRegistration.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.arkecosystem.crypto.transactions.types; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.HashMap; -import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.enums.CoreTransactionTypes; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; - -public class SecondSignatureRegistration extends Transaction { - @Override - public int getTransactionType() { - return CoreTransactionTypes.SECOND_SIGNATURE_REGISTRATION.getValue(); - } - - @Override - public int getTransactionTypeGroup() { - return TransactionTypeGroup.CORE.getValue(); - } - - @Override - public HashMap assetToHashMap() { - HashMap asset = new HashMap<>(); - - HashMap publicKey = new HashMap<>(); - publicKey.put("publicKey", this.asset.signature.publicKey); - - asset.put("signature", publicKey); - return asset; - } - - @Override - public byte[] serializeData() { - ByteBuffer buffer = ByteBuffer.allocate(33); - buffer.order(ByteOrder.LITTLE_ENDIAN); - buffer.put(Hex.decode(this.asset.signature.publicKey)); - return buffer.array(); - } - - @Override - public void deserializeData(ByteBuffer buffer) { - byte[] publicKeyBuffer = new byte[33]; - buffer.get(publicKeyBuffer); - this.asset.signature.publicKey = Hex.encode(publicKeyBuffer); - - byte[] signatureBuffer = new byte[buffer.remaining()]; - buffer.get(signatureBuffer); - this.signature = Hex.encode(signatureBuffer); - } -} diff --git a/src/main/java/org/arkecosystem/crypto/transactions/types/Transaction.java b/src/main/java/org/arkecosystem/crypto/transactions/types/Transaction.java deleted file mode 100644 index 6e114a11..00000000 --- a/src/main/java/org/arkecosystem/crypto/transactions/types/Transaction.java +++ /dev/null @@ -1,186 +0,0 @@ -package org.arkecosystem.crypto.transactions.types; - -import com.google.gson.GsonBuilder; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.identities.PrivateKey; -import org.arkecosystem.crypto.signature.SchnorrSigner; -import org.arkecosystem.crypto.signature.SchnorrVerifier; -import org.arkecosystem.crypto.signature.Signer; -import org.arkecosystem.crypto.signature.Verifier; -import org.arkecosystem.crypto.transactions.Serializer; -import org.arkecosystem.crypto.transactions.TransactionAsset; -import org.bitcoinj.core.ECKey; -import org.bitcoinj.core.Sha256Hash; - -public abstract class Transaction { - - public int version; - public int network; - public int typeGroup; - public int type; - public long nonce; - public String senderPublicKey; - public long fee = 0L; - public String vendorField; - public String vendorFieldHex; - public TransactionAsset asset = new TransactionAsset(); - public String signature; - public String secondSignature; - public List signatures; - public long amount = 0L; - public int expiration; - public String recipientId; - public String id; - - public void computeId() { - this.id = this.getId(); - } - - public String getId() { - return Hex.encode(Sha256Hash.hash(this.serialize())); - } - - public Transaction sign(String passphrase) { - ECKey privateKey = PrivateKey.fromPassphrase(passphrase); - - this.senderPublicKey = privateKey.getPublicKeyAsHex(); - Sha256Hash hash = Sha256Hash.of(this.serialize(true, true, false)); - - this.signature = Hex.encode(signer().sign(hash.getBytes(), privateKey)); - - return this; - } - - public Transaction secondSign(String passphrase) { - ECKey privateKey = PrivateKey.fromPassphrase(passphrase); - - Sha256Hash hash = Sha256Hash.of(this.serialize(false, true)); - - this.secondSignature = Hex.encode(signer().sign(hash.getBytes(), privateKey)); - - return this; - } - - public Transaction multiSign(String passphrase, int index) { - if (this.signatures == null) { - this.signatures = new ArrayList<>(); - } - - ECKey privateKey = PrivateKey.fromPassphrase(passphrase); - - // This is needed given as no method senderPublicKey() is exposed in the builder - if (this.senderPublicKey == null) { - this.senderPublicKey = privateKey.getPublicKeyAsHex(); - } - - byte[] hash = Sha256Hash.hash(Serializer.serialize(this, true, true, true)); - String signature = Hex.encode(signer().sign(hash, privateKey)); - String indexedSignature = Hex.encode(new byte[] {(byte) index}) + signature; - this.signatures.add(indexedSignature); - - return this; - } - - public boolean verify() { - ECKey keys = ECKey.fromPublicOnly(Hex.decode(this.senderPublicKey)); - - byte[] signature = Hex.decode(this.signature); - byte[] hash = Sha256Hash.hash(this.serialize(true, true, false)); - - return verifier().verify(hash, keys, signature); - } - - public boolean secondVerify(String secondPublicKey) { - ECKey keys = ECKey.fromPublicOnly(Hex.decode(secondPublicKey)); - - byte[] signature = Hex.decode(this.secondSignature); - byte[] hash = Sha256Hash.hash(this.serialize(false, true, false)); - - return verifier().verify(hash, keys, signature); - } - - public String toJson() { - GsonBuilder gsonBuilder = new GsonBuilder(); - return gsonBuilder.create().toJson(this.toHashMap()); - } - - public HashMap toHashMap() { - HashMap map = new HashMap(); - map.put("network", this.network); - map.put("id", this.id); - map.put("amount", String.valueOf(this.amount)); - map.put("fee", String.valueOf(this.fee)); - map.put("recipientId", this.recipientId); - map.put("signature", this.signature); - map.put("senderPublicKey", this.senderPublicKey); - map.put("type", this.type); - map.put("version", this.version); - map.put("nonce", String.valueOf(this.nonce)); - map.put("typeGroup", this.typeGroup); - - if (this.secondSignature != null) { - map.put("secondSignature", this.secondSignature); - } - - if (this.signatures != null) { - map.put("signatures", this.signatures); - } - - if (this.vendorField != null && !this.vendorField.isEmpty()) { - map.put("vendorField", this.vendorField); - } - - if (this.expiration > 0) { - map.put("expiration", this.expiration); - } - - HashMap asset = this.assetToHashMap(); - if (asset != null && !asset.isEmpty()) { - map.put("asset", asset); - } - return map; - } - - public byte[] serialize( - boolean skipSignature, boolean skipSecondSignature, boolean skipMultiSignature) { - return Serializer.serialize(this, skipSignature, skipSecondSignature, skipMultiSignature); - } - - public byte[] serialize(boolean skipSignature, boolean skipSecondSignature) { - return serialize(skipSignature, skipSecondSignature, false); - } - - public byte[] serialize(boolean skipSignature) { - return serialize(skipSignature, false, false); - } - - public byte[] serialize() { - return serialize(false, false, false); - } - - public abstract byte[] serializeData(); - - public abstract void deserializeData(ByteBuffer buffer); - - public abstract int getTransactionType(); - - public abstract int getTransactionTypeGroup(); - - public abstract HashMap assetToHashMap(); - - public boolean hasVendorField() { - return false; - } - - private Signer signer() { - return new SchnorrSigner(); - } - - private Verifier verifier() { - return new SchnorrVerifier(); - } -} diff --git a/src/main/java/org/arkecosystem/crypto/transactions/types/Transfer.java b/src/main/java/org/arkecosystem/crypto/transactions/types/Transfer.java index 021766c3..94283c86 100644 --- a/src/main/java/org/arkecosystem/crypto/transactions/types/Transfer.java +++ b/src/main/java/org/arkecosystem/crypto/transactions/types/Transfer.java @@ -1,58 +1,18 @@ package org.arkecosystem.crypto.transactions.types; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.HashMap; -import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.enums.CoreTransactionTypes; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; -import org.arkecosystem.crypto.utils.Address; -import org.web3j.crypto.Keys; +import java.util.Map; -public class Transfer extends Transaction { - @Override - public int getTransactionType() { - return CoreTransactionTypes.TRANSFER.getValue(); - } - - @Override - public int getTransactionTypeGroup() { - return TransactionTypeGroup.CORE.getValue(); +public class Transfer extends AbstractTransaction { + public Transfer() { + super(); } - @Override - public boolean hasVendorField() { - return true; - } - - @Override - public HashMap assetToHashMap() { - return null; + public Transfer(Map data) { + super(data); } @Override - public byte[] serializeData() { - ByteBuffer buffer = ByteBuffer.allocate(32); - buffer.order(ByteOrder.LITTLE_ENDIAN); - buffer.putLong(this.amount); - buffer.putInt(this.expiration); - - // Convert recipientId to a hex string without the 0x prefix and then to bytes - byte[] recipientBytes = Hex.decode(Address.toBufferHexString(this.recipientId)); - buffer.put(recipientBytes); - - return buffer.array(); - } - - @Override - public void deserializeData(ByteBuffer buffer) { - buffer.order(ByteOrder.LITTLE_ENDIAN); - - this.amount = buffer.getLong(); - this.expiration = buffer.getInt(); - - byte[] recipientId = new byte[20]; - buffer.get(recipientId); - this.recipientId = Keys.toChecksumAddress("0x" + Hex.encode(recipientId)); + public String getPayload() { + return ""; } } diff --git a/src/main/java/org/arkecosystem/crypto/transactions/types/Unvote.java b/src/main/java/org/arkecosystem/crypto/transactions/types/Unvote.java new file mode 100644 index 00000000..8fce58af --- /dev/null +++ b/src/main/java/org/arkecosystem/crypto/transactions/types/Unvote.java @@ -0,0 +1,25 @@ +package org.arkecosystem.crypto.transactions.types; + +import java.util.Map; +import org.arkecosystem.crypto.enums.AbiFunction; +import org.arkecosystem.crypto.utils.AbiEncoder; + +public class Unvote extends AbstractTransaction { + + public Unvote() { + super(); + } + + public Unvote(Map data) { + super(data); + } + + @Override + public String getPayload() { + try { + return new AbiEncoder().encodeFunctionCall(AbiFunction.UNVOTE.toString()); + } catch (Exception e) { + throw new RuntimeException("Error encoding function call", e); + } + } +} diff --git a/src/main/java/org/arkecosystem/crypto/transactions/types/UsernameRegistration.java b/src/main/java/org/arkecosystem/crypto/transactions/types/UsernameRegistration.java deleted file mode 100644 index 806e8b37..00000000 --- a/src/main/java/org/arkecosystem/crypto/transactions/types/UsernameRegistration.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.arkecosystem.crypto.transactions.types; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.HashMap; -import org.arkecosystem.crypto.enums.CoreTransactionTypes; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; - -public class UsernameRegistration extends Transaction { - @Override - public int getTransactionType() { - return CoreTransactionTypes.USERNAME_REGISTRATION.getValue(); - } - - @Override - public int getTransactionTypeGroup() { - return TransactionTypeGroup.CORE.getValue(); - } - - @Override - public HashMap assetToHashMap() { - HashMap asset = new HashMap<>(); - - asset.put("username", this.asset.username); - - return asset; - } - - @Override - public byte[] serializeData() { - byte[] username = this.asset.username.getBytes(); - - ByteBuffer buffer = ByteBuffer.allocate(username.length + 1); - - buffer.order(ByteOrder.LITTLE_ENDIAN); - - buffer.put((byte) username.length); - buffer.put(username); - - return buffer.array(); - } - - @Override - public void deserializeData(ByteBuffer buffer) { - int usernameLength = buffer.get() & 0xff; - - byte[] username = new byte[usernameLength]; - buffer.get(username); - - String utf8Username = new String(username); - this.asset.username = utf8Username; - } -} diff --git a/src/main/java/org/arkecosystem/crypto/transactions/types/UsernameResignation.java b/src/main/java/org/arkecosystem/crypto/transactions/types/UsernameResignation.java deleted file mode 100644 index e3a4223a..00000000 --- a/src/main/java/org/arkecosystem/crypto/transactions/types/UsernameResignation.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.arkecosystem.crypto.transactions.types; - -import java.nio.ByteBuffer; -import java.util.HashMap; -import org.arkecosystem.crypto.enums.CoreTransactionTypes; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; - -public class UsernameResignation extends Transaction { - @Override - public int getTransactionType() { - return CoreTransactionTypes.USERNAME_RESIGNATION.getValue(); - } - - @Override - public int getTransactionTypeGroup() { - return TransactionTypeGroup.CORE.getValue(); - } - - @Override - public HashMap assetToHashMap() { - return null; - } - - @Override - public byte[] serializeData() { - return new byte[0]; - } - - @Override - public void deserializeData(ByteBuffer buffer) {} -} diff --git a/src/main/java/org/arkecosystem/crypto/transactions/types/ValidatorRegistration.java b/src/main/java/org/arkecosystem/crypto/transactions/types/ValidatorRegistration.java index 5b6f5e3a..0b014f55 100644 --- a/src/main/java/org/arkecosystem/crypto/transactions/types/ValidatorRegistration.java +++ b/src/main/java/org/arkecosystem/crypto/transactions/types/ValidatorRegistration.java @@ -1,45 +1,42 @@ package org.arkecosystem.crypto.transactions.types; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.HashMap; -import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.enums.CoreTransactionTypes; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; - -public class ValidatorRegistration extends Transaction { - @Override - public int getTransactionType() { - return CoreTransactionTypes.VALIDATOR_REGISTRATION.getValue(); - } - - @Override - public int getTransactionTypeGroup() { - return TransactionTypeGroup.CORE.getValue(); +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.arkecosystem.crypto.enums.AbiFunction; +import org.arkecosystem.crypto.utils.AbiEncoder; + +public class ValidatorRegistration extends AbstractTransaction { + public ValidatorRegistration() { + super(); // Call the default constructor of AbstractTransaction } - @Override - public HashMap assetToHashMap() { - HashMap asset = new HashMap<>(); - - asset.put("validatorPublicKey", this.asset.validatorPublicKey); - - return asset; - } - - @Override - public byte[] serializeData() { - ByteBuffer buffer = ByteBuffer.allocate(48); - buffer.order(ByteOrder.LITTLE_ENDIAN); - buffer.put(Hex.decode(this.asset.validatorPublicKey)); + public ValidatorRegistration(Map data) { + super(data); - return buffer.array(); + // Use a local decodePayload method since we can't rely on AbstractTransaction's data field + List payload = decodePayload(data); + if (payload != null && !payload.isEmpty()) { + Object arg = payload.get(0); + this.validatorPublicKey = arg.toString().replaceFirst("^0x", ""); + } } @Override - public void deserializeData(ByteBuffer buffer) { - byte[] validatorPublicKey = new byte[48]; - buffer.get(validatorPublicKey); - this.asset.validatorPublicKey = Hex.encode(validatorPublicKey); + public String getPayload() { + if (this.validatorPublicKey == null || this.validatorPublicKey.isEmpty()) { + return ""; + } + + String validatorPublicKeyHex = "0x" + this.validatorPublicKey; + List args = new ArrayList<>(); + args.add(validatorPublicKeyHex); + + try { + return new AbiEncoder() + .encodeFunctionCall(AbiFunction.VALIDATOR_REGISTRATION.toString(), args); + } catch (Exception e) { + throw new RuntimeException("Error encoding function call", e); + } } } diff --git a/src/main/java/org/arkecosystem/crypto/transactions/types/ValidatorResignation.java b/src/main/java/org/arkecosystem/crypto/transactions/types/ValidatorResignation.java index c2efdd0a..079d5773 100644 --- a/src/main/java/org/arkecosystem/crypto/transactions/types/ValidatorResignation.java +++ b/src/main/java/org/arkecosystem/crypto/transactions/types/ValidatorResignation.java @@ -1,31 +1,25 @@ package org.arkecosystem.crypto.transactions.types; -import java.nio.ByteBuffer; -import java.util.HashMap; -import org.arkecosystem.crypto.enums.CoreTransactionTypes; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; +import java.util.Map; +import org.arkecosystem.crypto.enums.AbiFunction; +import org.arkecosystem.crypto.utils.AbiEncoder; -public class ValidatorResignation extends Transaction { - @Override - public int getTransactionType() { - return CoreTransactionTypes.VALIDATOR_RESIGNATION.getValue(); +public class ValidatorResignation extends AbstractTransaction { + public ValidatorResignation() { + super(); } - @Override - public int getTransactionTypeGroup() { - return TransactionTypeGroup.CORE.getValue(); + public ValidatorResignation(Map data) { + super(data); } @Override - public HashMap assetToHashMap() { - return null; + public String getPayload() { + try { + return new AbiEncoder() + .encodeFunctionCall(AbiFunction.VALIDATOR_RESIGNATION.toString()); + } catch (Exception e) { + throw new RuntimeException("Error encoding function call", e); + } } - - @Override - public byte[] serializeData() { - return new byte[0]; - } - - @Override - public void deserializeData(ByteBuffer buffer) {} } diff --git a/src/main/java/org/arkecosystem/crypto/transactions/types/Vote.java b/src/main/java/org/arkecosystem/crypto/transactions/types/Vote.java index 342d7e42..ff58a835 100644 --- a/src/main/java/org/arkecosystem/crypto/transactions/types/Vote.java +++ b/src/main/java/org/arkecosystem/crypto/transactions/types/Vote.java @@ -1,71 +1,40 @@ package org.arkecosystem.crypto.transactions.types; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.enums.CoreTransactionTypes; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; +import java.util.Map; +import org.arkecosystem.crypto.enums.AbiFunction; +import org.arkecosystem.crypto.utils.AbiEncoder; -public class Vote extends Transaction { - @Override - public int getTransactionType() { - return CoreTransactionTypes.VOTE.getValue(); +public class Vote extends AbstractTransaction { + public Vote() { + super(); } - @Override - public int getTransactionTypeGroup() { - return TransactionTypeGroup.CORE.getValue(); - } + public Vote(Map data) { + super(data); - @Override - public HashMap assetToHashMap() { - HashMap asset = new HashMap<>(); - asset.put("votes", this.asset.votes); - asset.put("unvotes", this.asset.unvotes); - return asset; - } - - @Override - public byte[] serializeData() { - ByteBuffer buffer = - ByteBuffer.allocate( - (1 + this.asset.votes.size() * 33) + (1 + this.asset.unvotes.size() * 33)); + List payload = decodePayload(data); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - List votes = new ArrayList<>(this.asset.votes); - List unvotes = new ArrayList<>(this.asset.unvotes); - - buffer.put((byte) votes.size()); - buffer.put(Hex.decode(String.join("", votes))); - - buffer.put((byte) unvotes.size()); - buffer.put(Hex.decode(String.join("", unvotes))); - - return buffer.array(); + if (payload != null && !payload.isEmpty()) { + Object arg = payload.get(0); + this.vote = arg.toString(); + } } @Override - public void deserializeData(ByteBuffer buffer) { - int voteLength = buffer.get(); - - for (int i = 0; i < voteLength; i++) { - byte[] voteBuffer = new byte[33]; - buffer.get(voteBuffer); - String vote = Hex.encode(voteBuffer); - this.asset.votes.add(vote); + public String getPayload() { + if (this.vote == null || this.vote.isEmpty()) { + return ""; } - int unvoteLength = buffer.get(); + List args = new ArrayList<>(); + args.add(this.vote); - for (int i = 0; i < unvoteLength; i++) { - byte[] unvoteBuffer = new byte[33]; - buffer.get(unvoteBuffer); - String unvote = Hex.encode(unvoteBuffer); - this.asset.unvotes.add(unvote); + try { + return new AbiEncoder().encodeFunctionCall(AbiFunction.VOTE.toString(), args); + } catch (Exception e) { + throw new RuntimeException("Error encoding function call", e); } } } diff --git a/src/main/java/org/arkecosystem/crypto/utils/AbiBase.java b/src/main/java/org/arkecosystem/crypto/utils/AbiBase.java new file mode 100644 index 00000000..33d0b4cb --- /dev/null +++ b/src/main/java/org/arkecosystem/crypto/utils/AbiBase.java @@ -0,0 +1,78 @@ +package org.arkecosystem.crypto.utils; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.web3j.crypto.Hash; +import org.web3j.utils.Numeric; + +public abstract class AbiBase { + + protected List> abi; + + public AbiBase() throws IOException { + String abiFilePath = "Abi.Consensus.json"; + + InputStream abiInputStream = getClass().getClassLoader().getResourceAsStream(abiFilePath); + + ObjectMapper mapper = new ObjectMapper(); + Map abiJson = mapper.readValue(abiInputStream, Map.class); + this.abi = (List>) abiJson.get("abi"); + } + + protected String[] getArrayComponents(String type) { + Pattern pattern = Pattern.compile("^(.*)\\[(\\d*)\\]$"); + Matcher matcher = pattern.matcher(type); + if (matcher.find()) { + String innerType = matcher.group(1); + String lengthStr = matcher.group(2); + return new String[] {lengthStr, innerType}; + } + return null; + } + + protected String stripHexPrefix(String hex) { + return Numeric.cleanHexPrefix(hex); + } + + protected boolean isValidAddress(String address) { + return address != null + && address.startsWith("0x") + && address.length() == 42 + && address.substring(2).matches("[0-9a-fA-F]+"); + } + + protected String keccak256(String input) { + return Hash.sha3String(input); + } + + protected String getFunctionSignature(Map abiItem) { + String name = (String) abiItem.get("name"); + List> inputs = (List>) abiItem.get("inputs"); + List types = new ArrayList<>(); + for (Map input : inputs) { + types.add((String) input.get("type")); + } + return name + "(" + String.join(",", types) + ")"; + } + + protected String toFunctionSelector(Map abiItem) { + String signature = getFunctionSignature(abiItem); + String hash = keccak256(signature); + return "0x" + stripHexPrefix(hash).substring(0, 8); + } + + protected String concatHex(List hexes) { + StringBuilder result = new StringBuilder("0x"); + for (String hex : hexes) { + if (hex == null || hex.isEmpty() || hex.equals("0x")) { + continue; + } + result.append(stripHexPrefix(hex)); + } + return result.toString(); + } +} diff --git a/src/main/java/org/arkecosystem/crypto/utils/AbiDecoder.java b/src/main/java/org/arkecosystem/crypto/utils/AbiDecoder.java new file mode 100644 index 00000000..49dc7d19 --- /dev/null +++ b/src/main/java/org/arkecosystem/crypto/utils/AbiDecoder.java @@ -0,0 +1,217 @@ +package org.arkecosystem.crypto.utils; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.*; +import org.web3j.utils.Numeric; + +public class AbiDecoder extends AbiBase { + + public AbiDecoder() throws IOException { + super(); + } + + public Map decodeFunctionData(String data) throws Exception { + data = stripHexPrefix(data); + + String functionSelector = data.substring(0, 8); + + Map abiItem = findFunctionBySelector(functionSelector); + if (abiItem == null) { + throw new Exception("Function selector not found in ABI: " + functionSelector); + } + + String encodedParams = data.substring(8); + List decodedParams = + decodeAbiParameters( + (List>) abiItem.get("inputs"), encodedParams); + + Map result = new HashMap<>(); + result.put("functionName", abiItem.get("name")); + result.put("args", decodedParams); + + return result; + } + + private Map findFunctionBySelector(String selector) { + for (Map item : this.abi) { + if ("function".equals(item.get("type"))) { + String functionSignature = getFunctionSignature(item); + String functionSelector = + stripHexPrefix(keccak256(functionSignature)).substring(0, 8); + if (functionSelector.equals(selector)) { + return item; + } + } + } + return null; + } + + private List decodeAbiParameters(List> params, String data) + throws Exception { + if ((data == null || data.isEmpty()) && !params.isEmpty()) { + throw new Exception("No data to decode"); + } + + byte[] bytes = Numeric.hexStringToByteArray(data); + int cursor = 0; + + List values = new ArrayList<>(); + for (Map param : params) { + Object[] result = decodeParameter(bytes, cursor, param); + Object value = result[0]; + int consumed = (int) result[1]; + cursor += consumed; + values.add(value); + } + + return values; + } + + private Object[] decodeParameter(byte[] bytes, int offset, Map param) + throws Exception { + String type = (String) param.get("type"); + String[] arrayComponents = getArrayComponents(type); + if (arrayComponents != null) { + String lengthStr = arrayComponents[0]; + String baseType = arrayComponents[1]; + param.put("type", baseType); + + Integer length = + lengthStr != null && !lengthStr.isEmpty() ? Integer.parseInt(lengthStr) : null; + + return decodeArray(bytes, offset, param, length); + } + + switch (type) { + case "address": + return decodeAddress(bytes, offset); + case "bool": + return decodeBool(bytes, offset); + case "string": + return decodeString(bytes, offset); + case "bytes": + return decodeDynamicBytes(bytes, offset); + default: + if (type.matches("^bytes(\\d+)$")) { + int size = Integer.parseInt(type.substring(5)); + return decodeFixedBytes(bytes, offset, size); + } else if (type.matches("^(u?int)(\\d+)$")) { + boolean signed = type.startsWith("int"); + int bits = Integer.parseInt(type.replaceAll("\\D", "")); + return decodeNumber(bytes, offset, bits, signed); + } else if ("tuple".equals(type)) { + return decodeTuple(bytes, offset, param); + } else { + throw new Exception("Unsupported type: " + type); + } + } + } + + private Object[] decodeAddress(byte[] bytes, int offset) { + byte[] data = Arrays.copyOfRange(bytes, offset, offset + 32); + byte[] addressBytes = Arrays.copyOfRange(data, 12, 32); + String address = "0x" + Numeric.toHexStringNoPrefix(addressBytes); + address = org.web3j.crypto.Keys.toChecksumAddress(address); + + return new Object[] {address, 32}; + } + + private Object[] decodeBool(byte[] bytes, int offset) { + byte[] data = Arrays.copyOfRange(bytes, offset, offset + 32); + boolean value = new BigInteger(data).compareTo(BigInteger.ZERO) != 0; + + return new Object[] {value, 32}; + } + + private Object[] decodeNumber(byte[] bytes, int offset, int bits, boolean signed) { + byte[] data = Arrays.copyOfRange(bytes, offset, offset + 32); + BigInteger value = new BigInteger(1, data); + if (signed && value.testBit(bits - 1)) { + value = value.subtract(BigInteger.ONE.shiftLeft(bits)); + } + + return new Object[] {value.toString(), 32}; + } + + private Object[] decodeString(byte[] bytes, int offset) { + int dataOffset = readUInt(bytes, offset).intValue(); + int stringOffset = offset + dataOffset; + int length = readUInt(bytes, stringOffset).intValue(); + byte[] stringData = + Arrays.copyOfRange(bytes, stringOffset + 32, stringOffset + 32 + length); + String value = new String(stringData); + + return new Object[] {value, 32}; + } + + private Object[] decodeDynamicBytes(byte[] bytes, int offset) { + int dataOffset = readUInt(bytes, offset).intValue(); + int bytesOffset = offset + dataOffset; + int length = readUInt(bytes, bytesOffset).intValue(); + byte[] bytesData = Arrays.copyOfRange(bytes, bytesOffset + 32, bytesOffset + 32 + length); + String value = "0x" + Numeric.toHexStringNoPrefix(bytesData); + + return new Object[] {value, 32}; + } + + private Object[] decodeFixedBytes(byte[] bytes, int offset, int size) { + byte[] data = Arrays.copyOfRange(bytes, offset, offset + 32); + String value = "0x" + Numeric.toHexStringNoPrefix(Arrays.copyOfRange(data, 0, size)); + + return new Object[] {value, 32}; + } + + private Object[] decodeArray( + byte[] bytes, int offset, Map param, Integer length) throws Exception { + String baseType = (String) param.get("type"); + Map elementType = new HashMap<>(param); + elementType.put("type", baseType); + + int arrayLength; + int cursor; + if (length == null) { + int dataOffset = readUInt(bytes, offset).intValue(); + int arrayOffset = offset + dataOffset; + arrayLength = readUInt(bytes, arrayOffset).intValue(); + cursor = arrayOffset + 32; + } else { + arrayLength = length; + cursor = offset; + } + + List values = new ArrayList<>(); + for (int i = 0; i < arrayLength; i++) { + Object[] result = decodeParameter(bytes, cursor, elementType); + Object value = result[0]; + int consumed = (int) result[1]; + cursor += consumed; + values.add(value); + } + + return new Object[] {values, 32}; + } + + private Object[] decodeTuple(byte[] bytes, int offset, Map param) + throws Exception { + List> components = (List>) param.get("components"); + Map values = new LinkedHashMap<>(); + int cursor = offset; + + for (Map component : components) { + Object[] result = decodeParameter(bytes, cursor, component); + Object value = result[0]; + int consumed = (int) result[1]; + cursor += consumed; + String name = (String) component.getOrDefault("name", ""); + values.put(name, value); + } + + return new Object[] {values, 32}; + } + + private BigInteger readUInt(byte[] bytes, int offset) { + byte[] data = Arrays.copyOfRange(bytes, offset, offset + 32); + return new BigInteger(1, data); + } +} diff --git a/src/main/java/org/arkecosystem/crypto/utils/AbiEncoder.java b/src/main/java/org/arkecosystem/crypto/utils/AbiEncoder.java new file mode 100644 index 00000000..e53b2eb7 --- /dev/null +++ b/src/main/java/org/arkecosystem/crypto/utils/AbiEncoder.java @@ -0,0 +1,348 @@ +package org.arkecosystem.crypto.utils; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.*; +import org.web3j.utils.Numeric; + +public class AbiEncoder extends AbiBase { + + public AbiEncoder() throws IOException { + super(); + } + + public String encodeFunctionCall(String functionName) throws Exception { + return encodeFunctionCall(functionName, Collections.emptyList()); + } + + public String encodeFunctionCall(String functionName, List args) throws Exception { + Map parameters = new HashMap<>(); + parameters.put("abi", this.abi); + parameters.put("functionName", functionName); + parameters.put("args", args); + + return encodeFunctionData(parameters); + } + + private String encodeFunctionData(Map parameters) throws Exception { + List args = (List) parameters.getOrDefault("args", new ArrayList<>()); + + Object[] result = prepareEncodeFunctionData(parameters); + Map abiItem = (Map) result[0]; + String signature = (String) result[1]; + + String data; + List> inputs = (List>) abiItem.get("inputs"); + if (inputs != null && !inputs.isEmpty()) { + data = encodeAbiParameters(inputs, args); + } else { + data = null; + } + + return concatHex(Arrays.asList(signature, data != null ? data : "0x")); + } + + private Object[] prepareEncodeFunctionData(Map params) throws Exception { + List> abi = (List>) params.get("abi"); + String functionName = (String) params.get("functionName"); + + if (functionName == null) { + List> functions = new ArrayList<>(); + for (Map item : abi) { + if ("function".equals(item.get("type"))) { + functions.add(item); + } + } + if (functions.size() == 1) { + Map abiItem = functions.get(0); + functionName = (String) abiItem.get("name"); + } else { + throw new Exception("Function name is not provided and ABI has multiple functions"); + } + } + + Map abiItem = + getAbiItem( + abi, + functionName, + (List) params.getOrDefault("args", new ArrayList<>())); + if (abiItem == null) { + throw new Exception("Function not found in ABI: " + functionName); + } + + String signature = toFunctionSelector(abiItem); + + return new Object[] {abiItem, signature}; + } + + private Map getAbiItem( + List> abi, String name, List args) throws Exception { + List> matchingItems = new ArrayList<>(); + for (Map item : abi) { + if ("function".equals(item.get("type")) && name.equals(item.get("name"))) { + matchingItems.add(item); + } + } + + if (matchingItems.isEmpty()) { + throw new Exception("Function not found in ABI: " + name); + } + + for (Map item : matchingItems) { + List> inputs = (List>) item.get("inputs"); + if (inputs.size() == args.size()) { + return item; + } + } + + throw new Exception("Function with matching arguments not found in ABI: " + name); + } + + private String encodeAbiParameters(List> params, List values) + throws Exception { + if (params.size() != values.size()) { + throw new Exception("Length of parameters and values do not match"); + } + + List> preparedParams = prepareParams(params, values); + String data = encodeParams(preparedParams); + + return data != null && !data.isEmpty() ? data : "0x"; + } + + private List> prepareParams( + List> params, List values) throws Exception { + List> preparedParams = new ArrayList<>(); + for (int i = 0; i < params.size(); i++) { + Map preparedParam = prepareParam(params.get(i), values.get(i)); + preparedParams.add(preparedParam); + } + return preparedParams; + } + + private Map prepareParam(Map param, Object value) + throws Exception { + String type = (String) param.get("type"); + String[] arrayComponents = getArrayComponents(type); + if (arrayComponents != null) { + String lengthStr = arrayComponents[0]; + String innerType = arrayComponents[1]; + Integer length = + lengthStr != null && !lengthStr.isEmpty() ? Integer.parseInt(lengthStr) : null; + Map innerParam = new HashMap<>(); + innerParam.put("name", param.get("name")); + innerParam.put("type", innerType); + + return encodeArray(value, length, innerParam); + } + + switch (type) { + case "tuple": + return encodeTuple(value, param); + case "address": + return encodeAddress((String) value); + case "bool": + return encodeBool((Boolean) value); + default: + if (type.startsWith("uint") || type.startsWith("int")) { + boolean signed = type.startsWith("int"); + return encodeNumber(value, signed); + } else if (type.startsWith("bytes")) { + return encodeBytes((String) value, param); + } else if ("string".equals(type)) { + return encodeString((String) value); + } else { + throw new Exception("Invalid ABI type: " + type); + } + } + } + + private Map encodeArray(Object value, Integer length, Map param) + throws Exception { + boolean dynamic = length == null; + + if (!(value instanceof List)) { + throw new Exception("Invalid array value"); + } + List valueList = (List) value; + if (!dynamic && valueList.size() != length) { + throw new Exception("Array length mismatch"); + } + + boolean dynamicChild = false; + List> preparedParams = new ArrayList<>(); + for (Object v : valueList) { + Map preparedParam = prepareParam(param, v); + if ((Boolean) preparedParam.get("dynamic")) { + dynamicChild = true; + } + preparedParams.add(preparedParam); + } + + if (dynamic || dynamicChild) { + String data = encodeParams(preparedParams); + if (dynamic) { + String lengthHex = String.format("%064x", valueList.size()); + return Map.of("dynamic", true, "encoded", "0x" + lengthHex + data.substring(2)); + } + if (dynamicChild) { + return Map.of("dynamic", true, "encoded", data); + } + } + StringBuilder encoded = new StringBuilder(); + for (Map p : preparedParams) { + encoded.append(stripHexPrefix((String) p.get("encoded"))); + } + + return Map.of("dynamic", false, "encoded", "0x" + encoded.toString()); + } + + private String encodeParams(List> preparedParams) { + int staticSize = 0; + for (Map param : preparedParams) { + boolean dynamic = (Boolean) param.get("dynamic"); + if (dynamic) { + staticSize += 32; + } else { + staticSize += (stripHexPrefix((String) param.get("encoded")).length()) / 2; + } + } + + List staticParams = new ArrayList<>(); + List dynamicParams = new ArrayList<>(); + int dynamicSize = 0; + for (Map param : preparedParams) { + boolean dynamic = (Boolean) param.get("dynamic"); + if (dynamic) { + String offset = String.format("%064x", staticSize + dynamicSize); + staticParams.add(offset); + dynamicParams.add(stripHexPrefix((String) param.get("encoded"))); + dynamicSize += (stripHexPrefix((String) param.get("encoded")).length()) / 2; + } else { + staticParams.add(stripHexPrefix((String) param.get("encoded"))); + } + } + + return "0x" + String.join("", staticParams) + String.join("", dynamicParams); + } + + private Map encodeAddress(String value) throws Exception { + if (!isValidAddress(value)) { + throw new Exception("Invalid address: " + value); + } + value = stripHexPrefix(value).toLowerCase(); + + // Pad the string to 64 characters with leading zeros + String paddedValue = String.format("%64s", value).replace(' ', '0'); + + return Map.of("dynamic", false, "encoded", "0x" + paddedValue); + } + + private Map encodeBool(Boolean value) { + String encoded = String.format("%064x", value ? 1 : 0); + + return Map.of("dynamic", false, "encoded", "0x" + encoded); + } + + private Map encodeNumber(Object value, boolean signed) throws Exception { + BigInteger bigValue; + if (value instanceof BigInteger) { + bigValue = (BigInteger) value; + } else if (value instanceof Number) { + bigValue = BigInteger.valueOf(((Number) value).longValue()); + } else if (value instanceof String) { + bigValue = new BigInteger((String) value); + } else { + throw new Exception("Invalid number value"); + } + + if (signed) { + if (bigValue.compareTo(BigInteger.ZERO) < 0) { + bigValue = BigInteger.ONE.shiftLeft(256).add(bigValue); + } + } else { + if (bigValue.compareTo(BigInteger.ZERO) < 0) { + throw new Exception("Negative value provided for unsigned integer type"); + } + } + String hex = bigValue.toString(16); + String encoded = String.format("%064s", hex); + + return Map.of("dynamic", false, "encoded", "0x" + encoded); + } + + private Map encodeBytes(String value, Map param) + throws Exception { + int bytesSize = (stripHexPrefix(value).length()) / 2; + String paramType = (String) param.get("type"); + String paramSizeStr = paramType.substring(5); + if (paramSizeStr.isEmpty()) { + String lengthHex = String.format("%064x", bytesSize); + String valuePadded = stripHexPrefix(value); + int padding = (32 - (bytesSize % 32)) % 32; + if (padding > 0) { + valuePadded = valuePadded + "0".repeat(padding * 2); + } + + return Map.of("dynamic", true, "encoded", "0x" + lengthHex + valuePadded); + } + int paramSize = Integer.parseInt(paramSizeStr); + if (bytesSize != paramSize) { + throw new Exception( + "Bytes size mismatch: expected " + paramSize + ", got " + bytesSize); + } + String valuePadded = String.format("%-64s", stripHexPrefix(value)).replace(' ', '0'); + + return Map.of("dynamic", false, "encoded", "0x" + valuePadded); + } + + private Map encodeString(String value) { + String hexValue = Numeric.toHexStringNoPrefix(value.getBytes()); + String lengthHex = String.format("%064x", value.length()); + String valuePadded = hexValue; + int padding = (32 - (value.length() % 32)) % 32; + if (padding > 0) { + valuePadded = valuePadded + "00".repeat(padding); + } + + return Map.of("dynamic", true, "encoded", "0x" + lengthHex + valuePadded); + } + + private Map encodeTuple(Object value, Map param) + throws Exception { + boolean dynamic = false; + List> preparedParams = new ArrayList<>(); + + List> components = (List>) param.get("components"); + Map valueMap; + if (value instanceof Map) { + valueMap = (Map) value; + } else { + throw new Exception("Tuple value must be a Map"); + } + + for (Map component : components) { + String key = (String) component.get("name"); + if (!valueMap.containsKey(key)) { + throw new Exception("Tuple value missing component: " + key); + } + Map preparedParam = prepareParam(component, valueMap.get(key)); + if ((Boolean) preparedParam.get("dynamic")) { + dynamic = true; + } + preparedParams.add(preparedParam); + } + + if (dynamic) { + String encoded = encodeParams(preparedParams); + return Map.of("dynamic", true, "encoded", encoded); + } + + StringBuilder encoded = new StringBuilder("0x"); + for (Map p : preparedParams) { + encoded.append(stripHexPrefix((String) p.get("encoded"))); + } + + return Map.of("dynamic", false, "encoded", encoded.toString()); + } +} diff --git a/src/main/java/org/arkecosystem/crypto/utils/Schnorr.java b/src/main/java/org/arkecosystem/crypto/utils/Schnorr.java deleted file mode 100644 index 23d13508..00000000 --- a/src/main/java/org/arkecosystem/crypto/utils/Schnorr.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.arkecosystem.crypto; - -public class Schnorr { - - public static byte[] hexStringToByteArray(String s) { - int len = s.length(); - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - data[i / 2] = - (byte) - ((Character.digit(s.charAt(i), 16) << 4) - + Character.digit(s.charAt(i + 1), 16)); - } - return data; - } -} diff --git a/src/main/java/org/arkecosystem/crypto/utils/TransactionHasher.java b/src/main/java/org/arkecosystem/crypto/utils/TransactionHasher.java new file mode 100644 index 00000000..68d18a93 --- /dev/null +++ b/src/main/java/org/arkecosystem/crypto/utils/TransactionHasher.java @@ -0,0 +1,194 @@ +package org.arkecosystem.crypto.utils; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.arkecosystem.crypto.encoding.Hex; +import org.bitcoinj.core.Sha256Hash; + +public class TransactionHasher { + + /** + * Generates the transaction hash. + * + * @param transaction The transaction data. + * @param skipSignature Whether to skip the signature fields. + * @return The hash of the transaction. + */ + public static byte[] toHash(Map transaction, boolean skipSignature) { + // Process recipientAddress + String hex = ((String) transaction.get("recipientAddress")).replaceFirst("^0x", ""); + + // @TODO: see if this is necessary + if (hex.length() % 2 != 0) { + hex = "0" + hex; + } + byte[] recipientAddress = Hex.decode(hex.toLowerCase()); + + // Build the fields array + List fields = new ArrayList<>(); + fields.add(toBeArray(new BigInteger(transaction.get("network").toString()))); + fields.add(toBeArray(new BigInteger(transaction.get("nonce").toString()))); + fields.add( + toBeArray( + new BigInteger( + transaction.get("gasPrice").toString()))); // maxPriorityFeePerGas + fields.add( + toBeArray(new BigInteger(transaction.get("gasPrice").toString()))); // maxFeePerGas + fields.add(toBeArray(new BigInteger(transaction.get("gasLimit").toString()))); + fields.add(recipientAddress); + fields.add(toBeArray(new BigInteger(transaction.get("value").toString()))); + String dataHex = + transaction.get("data") != null + ? ((String) transaction.get("data")).replaceFirst("^0x", "") + : ""; + byte[] data = Hex.decode(dataHex); + fields.add(data); + fields.add(new ArrayList<>()); // Access list is unused + + if (!skipSignature) { + byte[] signatureBuffer = Hex.decode((String) transaction.get("signature")); + byte[] r = Arrays.copyOfRange(signatureBuffer, 0, 32); + byte[] s = Arrays.copyOfRange(signatureBuffer, 32, 64); + int v = signatureBuffer[64] & 0xFF; + + fields.add(toBeArray(BigInteger.valueOf(v))); + fields.add(r); + fields.add(s); + } + + byte eip1559Prefix = 0x02; // Marker for Type 2 (EIP-1559) transaction + + byte[] encoded = encodeRlp(fields); + + byte[] hashInput = new byte[1 + encoded.length]; + hashInput[0] = eip1559Prefix; + System.arraycopy(encoded, 0, hashInput, 1, encoded.length); + + // Use SHA256 for hashing + return Sha256Hash.hash(hashInput); + } + + /** + * Converts a big integer to a big-endian byte array without leading zero bytes. + * + * @param value The big integer value. + * @return The byte array representation. + */ + private static byte[] toBeArray(BigInteger value) { + if (value.equals(BigInteger.ZERO)) { + return new byte[0]; // Empty array represents zero + } + + byte[] temp = value.toByteArray(); + // Remove leading zero byte if present + if (temp[0] == 0x00) { + temp = Arrays.copyOfRange(temp, 1, temp.length); + } + return temp; + } + + /** + * Encodes the length for RLP encoding. + * + * @param len The length to encode. + * @return The encoded length as a byte array. + */ + private static byte[] encodeLength(int len) { + if (len == 0) { + return new byte[0]; + } + + List lenBytes = new ArrayList<>(); + while (len > 0) { + lenBytes.add(0, (byte) (len & 0xFF)); + len >>= 8; + } + + byte[] result = new byte[lenBytes.size()]; + for (int i = 0; i < lenBytes.size(); i++) { + result[i] = lenBytes.get(i); + } + return result; + } + + /** + * RLP encoding function. + * + * @param input The input to encode. + * @return The RLP-encoded byte array. + */ + private static byte[] encodeRlp(Object input) { + try { + if (input instanceof byte[]) { + byte[] inputBytes = (byte[]) input; + int len = inputBytes.length; + if (len == 1 && (inputBytes[0] & 0xFF) <= 0x7F) { + return inputBytes; + } else if (len <= 55) { + byte[] result = new byte[1 + len]; + result[0] = (byte) (0x80 + len); + System.arraycopy(inputBytes, 0, result, 1, len); + return result; + } else { + byte[] lenBytes = encodeLength(len); + byte[] result = new byte[1 + lenBytes.length + len]; + result[0] = (byte) (0xB7 + lenBytes.length); + System.arraycopy(lenBytes, 0, result, 1, lenBytes.length); + System.arraycopy(inputBytes, 0, result, 1 + lenBytes.length, len); + return result; + } + } else if (input instanceof String) { + byte[] inputBytes = ((String) input).getBytes("UTF-8"); + return encodeRlp(inputBytes); + } else if (input instanceof List) { + @SuppressWarnings("unchecked") + List inputList = (List) input; + byte[] output = new byte[0]; + for (Object item : inputList) { + byte[] encodedItem = encodeRlp(item); + output = concatenate(output, encodedItem); + } + int len = output.length; + if (len <= 55) { + byte[] result = new byte[1 + len]; + result[0] = (byte) (0xC0 + len); + System.arraycopy(output, 0, result, 1, len); + return result; + } else { + byte[] lenBytes = encodeLength(len); + byte[] result = new byte[1 + lenBytes.length + len]; + result[0] = (byte) (0xF7 + lenBytes.length); + System.arraycopy(lenBytes, 0, result, 1, lenBytes.length); + System.arraycopy(output, 0, result, 1 + lenBytes.length, len); + return result; + } + } else if (input instanceof BigInteger) { + return encodeRlp(toBeArray((BigInteger) input)); + } else if (input == null) { + return encodeRlp(new byte[0]); + } else { + // Handle numbers and other types as strings + return encodeRlp(input.toString().getBytes("UTF-8")); + } + } catch (Exception e) { + throw new RuntimeException("Error in RLP encoding", e); + } + } + + /** + * Helper method to concatenate two byte arrays. + * + * @param a First byte array. + * @param b Second byte array. + * @return Concatenated byte array. + */ + private static byte[] concatenate(byte[] a, byte[] b) { + byte[] output = new byte[a.length + b.length]; + System.arraycopy(a, 0, output, 0, a.length); + System.arraycopy(b, 0, output, a.length, b.length); + return output; + } +} diff --git a/src/main/resources/Abi.Consensus.json b/src/main/resources/Abi.Consensus.json new file mode 100644 index 00000000..69ed3739 --- /dev/null +++ b/src/main/resources/Abi.Consensus.json @@ -0,0 +1,1032 @@ +{ + "abi": [ + { + "type": "constructor", + "inputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "activeValidatorsCount", + "inputs": [], + "outputs": [ + { "name": "", "type": "uint256", "internalType": "uint256" } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "calculateTopValidators", + "inputs": [ + { "name": "n", "type": "uint8", "internalType": "uint8" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getAllValidators", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple[]", + "internalType": "struct Validator[]", + "components": [ + { + "name": "addr", + "type": "address", + "internalType": "address" + }, + { + "name": "data", + "type": "tuple", + "internalType": "struct ValidatorData", + "components": [ + { + "name": "votersCount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "voteBalance", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "isResigned", + "type": "bool", + "internalType": "bool" + }, + { + "name": "bls12_381_public_key", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getRounds", + "inputs": [ + { + "name": "offset", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "count", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "tuple[]", + "internalType": "struct Round[]", + "components": [ + { + "name": "round", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "validators", + "type": "tuple[]", + "internalType": "struct RoundValidator[]", + "components": [ + { + "name": "addr", + "type": "address", + "internalType": "address" + }, + { + "name": "voteBalance", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getRoundsCount", + "inputs": [], + "outputs": [ + { "name": "", "type": "uint256", "internalType": "uint256" } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getTopValidators", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple[]", + "internalType": "struct Validator[]", + "components": [ + { + "name": "addr", + "type": "address", + "internalType": "address" + }, + { + "name": "data", + "type": "tuple", + "internalType": "struct ValidatorData", + "components": [ + { + "name": "votersCount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "voteBalance", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "isResigned", + "type": "bool", + "internalType": "bool" + }, + { + "name": "bls12_381_public_key", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getValidator", + "inputs": [ + { + "name": "_addr", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct Validator", + "components": [ + { + "name": "addr", + "type": "address", + "internalType": "address" + }, + { + "name": "data", + "type": "tuple", + "internalType": "struct ValidatorData", + "components": [ + { + "name": "votersCount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "voteBalance", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "isResigned", + "type": "bool", + "internalType": "bool" + }, + { + "name": "bls12_381_public_key", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getVotes", + "inputs": [ + { + "name": "addr", + "type": "address", + "internalType": "address" + }, + { + "name": "count", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "tuple[]", + "internalType": "struct VoteResult[]", + "components": [ + { + "name": "voter", + "type": "address", + "internalType": "address" + }, + { + "name": "validator", + "type": "address", + "internalType": "address" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getVotesCount", + "inputs": [], + "outputs": [ + { "name": "", "type": "uint256", "internalType": "uint256" } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "isValidatorRegistered", + "inputs": [ + { "name": "addr", "type": "address", "internalType": "address" } + ], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "registerValidator", + "inputs": [ + { + "name": "bls12_381_public_key", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "registeredValidatorsCount", + "inputs": [], + "outputs": [ + { "name": "", "type": "uint256", "internalType": "uint256" } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "resignValidator", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "resignedValidatorsCount", + "inputs": [], + "outputs": [ + { "name": "", "type": "uint256", "internalType": "uint256" } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "unvote", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "updateValidator", + "inputs": [ + { + "name": "_validator", + "type": "tuple", + "internalType": "struct Validator", + "components": [ + { + "name": "addr", + "type": "address", + "internalType": "address" + }, + { + "name": "data", + "type": "tuple", + "internalType": "struct ValidatorData", + "components": [ + { + "name": "votersCount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "voteBalance", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "isResigned", + "type": "bool", + "internalType": "bool" + }, + { + "name": "bls12_381_public_key", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ] + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "updateVoters", + "inputs": [ + { + "name": "voters", + "type": "address[]", + "internalType": "address[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "vote", + "inputs": [ + { "name": "addr", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "Unvoted", + "inputs": [ + { + "name": "voter", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "validator", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ValidatorRegistered", + "inputs": [ + { + "name": "addr", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "bls12_381_public_key", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ValidatorResigned", + "inputs": [ + { + "name": "addr", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Voted", + "inputs": [ + { + "name": "voter", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "validator", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + } + ], + "bytecode": { + "object": "0x60a06040525f80805560018190556007819055600880546001600160a01b0319908116909155600980549091169055600c5534801561003c575f5ffd5b5033608052608051612bdb61007c5f395f81816103e4015281816104c30152818161069301528181610a0901528181610f2101526114e90152612bdb5ff3fe608060405234801561000f575f5ffd5b5060043610610111575f3560e01c80636dd7d8ea1161009e578063b85f5da21161006e578063b85f5da214610202578063d04a68c71461020a578063eb9019d414610245578063f1bd0b3714610265578063f3513a371461026c575f5ffd5b80636dd7d8ea146101bf578063a09686c4146101d2578063afeea115146101da578063b5cfa68c146101ef575f5ffd5b80632bdf6d43116100e45780632bdf6d431461015c5780633174b6891461017157806340f74f4714610179578063602a9eee1461019957806362525879146101ac575f5ffd5b80630777cbef146101155780630d2bd9091461012c5780631904bb2e146101345780631b605b8614610154575b5f5ffd5b6001545b6040519081526020015b60405180910390f35b600d54610119565b610147610142366004612457565b610274565b60405161012391906124fb565b600754610119565b61016f61016a36600461250d565b6103d9565b005b61016f610467565b61018c61018736600461257c565b6104b6565b604051610123919061259c565b61016f6101a7366004612655565b610689565b61016f6101ba3660046126b1565b610964565b61016f6101cd366004612457565b6109ff565b600e54610119565b6101e2610d79565b60405161012391906126e7565b61016f6101fd36600461273e565b610f16565b61016f6113bb565b610235610218366004612457565b6001600160a01b03165f9081526003602052604090205460ff1690565b6040519015158152602001610123565b61025861025336600461275e565b6114dc565b6040516101239190612786565b5f54610119565b6101e261171d565b61027c612361565b6001600160a01b0382165f9081526003602052604090205460ff166102e85760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f724461746120646f65736e2774206578697374730000000060448201526064015b60405180910390fd5b6040805180820182526001600160a01b0384168082525f90815260026020818152918490208451608081018652815481526001820154818501529181015460ff16151594820194909452600384018054939492850193919291606084019190610350906127e0565b80601f016020809104026020016040519081016040528092919081815260200182805461037c906127e0565b80156103c75780601f1061039e576101008083540402835291602001916103c7565b820191905f5260205f20905b8154815290600101906020018083116103aa57829003601f168201915b50505091909252505050905292915050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146104215760405162461bcd60e51b81526004016102df90612812565b5f5b818110156104625761045a83838381811061044057610440612847565b90506020020160208101906104559190612457565b6118b4565b600101610423565b505050565b7f6572af8bf9a0a86efb88dcc30011626a15c9c4603503aa4466a3f87a1867deef33610491611994565b604080516001600160a01b0393841681529290911660208301520160405180910390a1565b6060336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146105005760405162461bcd60e51b81526004016102df90612812565b600e548290841061051257505f610536565b600e5461051f848661286f565b111561053657600e54610533908590612882565b90505b5f816001600160401b0381111561054f5761054f612895565b60405190808252806020026020018201604052801561059457816020015b604080518082019091525f81526060602082015281526020019060019003908161056d5790505b5090505f5b8281101561067e57604051806040016040528082886105b8919061286f565b6105c390600161286f565b8152602001600e6105d4848a61286f565b815481106105e4576105e4612847565b905f5260205f2001805480602002602001604051908101604052809291908181526020015f905b82821015610652575f848152602090819020604080518082019091526002850290910180546001600160a01b0316825260019081015482840152908352909201910161060b565b5050505081525082828151811061066b5761066b612847565b6020908102919091010152600101610599565b509150505b92915050565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036107015760405162461bcd60e51b815260206004820152601c60248201527f43616c6c65722069732074686520636f6e7472616374206f776e65720000000060448201526064016102df565b335f9081526003602052604090205460ff16156107605760405162461bcd60e51b815260206004820152601f60248201527f56616c696461746f7220697320616c726561647920726567697374657265640060448201526064016102df565b5f82826040516107719291906128a9565b60408051918290039091205f8181526004602052919091205490915060ff16156107e95760405162461bcd60e51b815260206004820152602360248201527f424c5331322d333831206b657920697320616c726561647920726567697374656044820152621c995960ea1b60648201526084016102df565b6107f38383611bfc565b5f60405180608001604052805f81526020015f81526020015f1515815260200185858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920182905250939094525050805492935090508061085b836128b8565b9091555050335f9081526003602081815260408084208054600160ff1991821681179092556002808552958390208751815593870151918401919091559085015193820180549091169315159390931790925560608301518392918201906108c39082612914565b5050505f82815260046020526040808220805460ff191660019081179091556005805491820181559092527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db090910180546001600160a01b0319163390811790915590517f61809fa303a3a57f4d70552f533f3e0b003173d424590cd4bb22a2afe000990c9161095691879087906129ce565b60405180910390a150505050565b6109746102186020830183612457565b6109c05760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f724461746120646f65736e2774206578697374730000000060448201526064016102df565b6109cd6020820182612a0d565b60025f6109dd6020850185612457565b6001600160a01b0316815260208101919091526040015f206104628282612ae4565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163303610a775760405162461bcd60e51b815260206004820152601c60248201527f43616c6c65722069732074686520636f6e7472616374206f776e65720000000060448201526064016102df565b6001600160a01b0381165f9081526003602052604090205460ff16610ade5760405162461bcd60e51b815260206004820152601760248201527f4d75737420766f746520666f722076616c696461746f7200000000000000000060448201526064016102df565b6001600160a01b0381165f9081526002602081905260409091209081015460ff1615610b575760405162461bcd60e51b815260206004820152602260248201527f4d75737420766f746520666f7220756e72657369676e65642076616c6964617460448201526137b960f11b60648201526084016102df565b335f90815260066020526040902080546001600160a01b03848116911603610bc15760405162461bcd60e51b815260206004820181905260248201527f416c726561647920766f74656420666f7220746869732076616c696461746f7260448201526064016102df565b80546001600160a01b031615610bdb57610bd9611994565b505b604080516080810182526001600160a01b03808616825233803160208085019182525f85870181815260608701828152948252600690925295909520935184549084166001600160a01b03199182161785559051600185015593516002840180549184169186169190911790555160039092018054928216929093169190911790915560085416610c8d5760088054336001600160a01b03199182168117909255600980549091169091179055610ce4565b600980546001600160a01b039081165f9081526006602052604080822060030180546001600160a01b0319908116339081179092558554828552929093206002018054841692909416919091179092558254161790555b60078054905f610cf3836128b8565b9190505550336001600160a01b031631826001015f828254610d15919061286f565b9091555050815460019083905f90610d2e90849061286f565b9091555050604080513381526001600160a01b03851660208201527fce0c7a2a940807f7dc2ce7a615c2532e915e6c0ac9a08bc4ed9d515a710a53e2910160405180910390a1505050565b600d546060905f906001600160401b03811115610d9857610d98612895565b604051908082528060200260200182016040528015610dd157816020015b610dbe612361565b815260200190600190039081610db65790505b5090505f5b600d54811015610f10575f600d8281548110610df457610df4612847565b5f9182526020808320909101546001600160a01b03168083526002808352604093849020845180860186528381528551608081018752825481526001830154818701529282015460ff1615159583019590955260038101805493965090949384019285916060840191610e66906127e0565b80601f0160208091040260200160405190810160405280929190818152602001828054610e92906127e0565b8015610edd5780601f10610eb457610100808354040283529160200191610edd565b820191905f5260205f20905b815481529060010190602001808311610ec057829003601f168201915b505050505081525050815250848481518110610efb57610efb612847565b60209081029190910101525050600101610dd6565b50919050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610f5e5760405162461bcd60e51b81526004016102df90612812565b610f66611c5e565b610f6e611dad565b600a80546001600160a01b03191690556001545f80549091610fa09160ff8516918491610f9b9190612882565b611dfe565b90508060ff165f03610fb0575050565b5f5b60055481101561125b575f60058281548110610fd057610fd0612847565b5f9182526020808320909101546001600160a01b03168083526002918290526040909220908101549192509060ff161561100b575050611253565b600a546001600160a01b03166110465750600a80546001600160a01b0319166001600160a01b03929092169190911790556001600c55611253565b8360ff16600c5410156110645761105d8285611e8f565b5050611253565b600a546001600160a01b039081165f908152600260208181526040928390208351808501855294871685528351608081018552865481526001870154818401529286015460ff161515938301939093526003850180549394611240949093928401929187916060840191906110d8906127e0565b80601f0160208091040260200160405190810160405280929190818152602001828054611104906127e0565b801561114f5780601f106111265761010080835404028352916020019161114f565b820191905f5260205f20905b81548152906001019060200180831161113257829003601f168201915b505050919092525050509052604080518082018252600a546001600160a01b031681528151608081018352855481526001860154602082810191909152600287015460ff161515938201939093526003860180549293840192879160608401916111b8906127e0565b80601f01602080910402602001604051908101604052809291908181526020018280546111e4906127e0565b801561122f5780601f106112065761010080835404028352916020019161122f565b820191905f5260205f20905b81548152906001019060200180831161121257829003601f168201915b50505050508152505081525061226e565b1561124f5761124f8386611e8f565b5050505b600101610fb2565b50600e80546001810182555f918252600a547fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fd909101916001600160a01b03909116906112aa90600d906123aa565b8260ff166001600160401b038111156112c5576112c5612895565b6040519080825280602002602001820160405280156112ee578160200160208202803683370190505b50805161130391600d916020909101906123c5565b505f5b8360ff168110156113b35781600d828154811061132557611325612847565b5f918252602080832090910180546001600160a01b039485166001600160a01b03199182161790915560408051808201825296851680885280855260028085528286206001908101548a87019081528b548083018d558c89528789209b519302909a01805492891692909516919091178455975192880192909255908352600b909152902054169101611306565b505050505b50565b335f9081526003602052604090205460ff166114195760405162461bcd60e51b815260206004820152601960248201527f43616c6c6572206973206e6f7420612076616c696461746f720000000000000060448201526064016102df565b335f9081526002602081905260409091209081015460ff161561147e5760405162461bcd60e51b815260206004820152601d60248201527f56616c696461746f7220697320616c72656164792072657369676e656400000060448201526064016102df565b60028101805460ff19166001908117909155805481905f906114a190839061286f565b90915550506040513381527f24250fc1ec78a1405ddd4cc8b75964858af228d05faa8d4bc1302966d8a541179060200160405180910390a150565b6060336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146115265760405162461bcd60e51b81526004016102df90612812565b5f611534835f600754611dfe565b6001600160401b0381111561154b5761154b612895565b60405190808252806020026020018201604052801561158f57816020015b604080518082019091525f80825260208201528152602001906001900390816115695790505b506008549091506001600160a01b03908116908516156115c857506001600160a01b038085165f90815260066020526040902060030154165b5f5b6001600160a01b038216158015906115e157508481105b15611656576001600160a01b038083165f818152600660209081526040918290208251808401909352928252825490931692810192909252908483611625816128b8565b94508151811061163757611637612847565b6020908102919091010152600301546001600160a01b031691506115ca565b8083510361166957829350505050610683565b5f816001600160401b0381111561168257611682612895565b6040519080825280602002602001820160405280156116c657816020015b604080518082019091525f80825260208201528152602001906001900390816116a05790505b5090505f5b82811015611712578481815181106116e5576116e5612847565b60200260200101518282815181106116ff576116ff612847565b60209081029190910101526001016116cb565b509695505050505050565b6005546060905f906001600160401b0381111561173c5761173c612895565b60405190808252806020026020018201604052801561177557816020015b611762612361565b81526020019060019003908161175a5790505b5090505f5b600554811015610f10575f6005828154811061179857611798612847565b5f9182526020808320909101546001600160a01b03168083526002808352604093849020845180860186528381528551608081018752825481526001830154818701529282015460ff161515958301959095526003810180549396509094938401928591606084019161180a906127e0565b80601f0160208091040260200160405190810160405280929190818152602001828054611836906127e0565b80156118815780601f1061185857610100808354040283529160200191611881565b820191905f5260205f20905b81548152906001019060200180831161186457829003601f168201915b50505050508152505081525084848151811061189f5761189f612847565b6020908102919091010152505060010161177a565b6001600160a01b038082165f90815260066020526040902080549091166118d9575050565b60018101546001600160a01b0383163181101561193a57611904816001600160a01b03851631612882565b82546001600160a01b03165f908152600260205260408120600101805490919061192f90849061286f565b9091555061197f9050565b61194e6001600160a01b0384163182612882565b82546001600160a01b03165f9081526002602052604081206001018054909190611979908490612882565b90915550505b506001600160a01b0390911631600190910155565b335f90815260066020526040812080546001600160a01b0316611a075760405162461bcd60e51b815260206004820152602560248201527f4d75737420766f746520666f722076616c696461746f72206265666f726520756044820152646e766f746560d81b60648201526084016102df565b6009546008546001600160a01b03918216911603611a4057600880546001600160a01b0319908116909155600980549091169055611b53565b600954336001600160a01b0390911603611a99576002810180546001600160a01b039081165f90815260066020526040902060030180546001600160a01b03199081169091559154600980549093169116179055611b53565b600854336001600160a01b0390911603611afd576009546001600160a01b039081165f9081526006602052604080822060020180546001600160a01b0319908116909155600880548086168552929093206003015491169216919091179055611b53565b60038181018054600280850180546001600160a01b039081165f9081526006602052604080822090970180549583166001600160a01b031996871617905591549454811682529490200180549290931691161790555b80546001600160a01b03165f818152600260205260408120600180850154908201805492939192909190611b88908490612882565b9091555050805460019082905f90611ba1908490612882565b9091555050335f90815260066020526040812080546001600160a01b0319908116825560018201839055600282018054821690556003909101805490911690556007805491611bef83612b71565b9091555091949350505050565b60308114611c5a5760405162461bcd60e51b815260206004820152602560248201527f424c5331322d333831207075626c69634b6579206c656e67746820697320696e6044820152641d985b1a5960da1b60648201526084016102df565b5050565b6005545f611c6d600183612882565b90505b8015611c5a575f611c8282600161286f565b60408051426020820152449181019190915260608101849052608001604051602081830303815290604052805190602001205f1c611cc09190612b86565b90505f60058381548110611cd657611cd6612847565b5f91825260209091200154600580546001600160a01b0390921692509083908110611d0357611d03612847565b5f91825260209091200154600580546001600160a01b039092169185908110611d2e57611d2e612847565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055508060058381548110611d6d57611d6d612847565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b0316021790555050508080611da590612b71565b915050611c70565b600a546001600160a01b03165b6001600160a01b03811615611df7576001600160a01b039081165f908152600b6020526040902080546001600160a01b0319811690915516611dba565b505f600c55565b5f81831115611e675760405162461bcd60e51b815260206004820152602f60248201527f4d696e696d756d2073686f756c64206265206c657373207468616e206f72206560448201526e7175616c20746f206d6178696d756d60881b60648201526084016102df565b82841015611e76575081611e88565b81841115611e85575080611e88565b50825b9392505050565b6001600160a01b0382165f9081526002602081815260408084208151608081018352815481526001820154938101939093529283015460ff16151590820152600382018054919291606084019190611ee6906127e0565b80601f0160208091040260200160405190810160405280929190818152602001828054611f12906127e0565b8015611f5d5780601f10611f3457610100808354040283529160200191611f5d565b820191905f5260205f20905b815481529060010190602001808311611f4057829003601f168201915b505050919092525050604080518082018252600a546001600160a01b03168082525f90815260026020818152918490208451608081018652815481526001820154818501529181015460ff1615159482019490945260038401805496975061207e9693955091850193909290916060840191611fd8906127e0565b80601f0160208091040260200160405190810160405280929190818152602001828054612004906127e0565b801561204f5780601f106120265761010080835404028352916020019161204f565b820191905f5260205f20905b81548152906001019060200180831161203257829003601f168201915b5050505050815250508152506040518060400160405280866001600160a01b031681526020018481525061226e565b156120915761208c836122b2565b61220f565b600a546001600160a01b039081165f818152600b6020526040902054909116905b6001600160a01b0382166120cf576120ca8186612303565b61220c565b6040805180820182526001600160a01b0384168082525f90815260026020818152918490208451608081018652815481526001820154818501529181015460ff161515948201949094526003840180546121dc9593850193916060840191612136906127e0565b80601f0160208091040260200160405190810160405280929190818152602001828054612162906127e0565b80156121ad5780601f10612184576101008083540402835291602001916121ad565b820191905f5260205f20905b81548152906001019060200180831161219057829003601f168201915b5050505050815250508152506040518060400160405280886001600160a01b031681526020018681525061226e565b156121eb576120ca8186612303565b506001600160a01b038082165f908152600b602052604090205416906120b2565b50505b8160ff16600c54111561046257600a80546001600160a01b039081165f908152600b6020526040812080546001600160a01b03198082169092558454931692168217909255600c8054919261226383612b71565b919050555050505050565b5f8160200151602001518360200151602001510361229e5750805182516001600160a01b03918216911611610683565b506020908101518101519181015101511190565b600a80546001600160a01b038381165f818152600b602052604081208054939094166001600160a01b031993841617909355835490911617909155600c8054916122fb836128b8565b919050555050565b6001600160a01b038281165f818152600b602052604080822080548686168085529284208054919096166001600160a01b03199182161790955592825282549093169092179055600c805491612358836128b8565b91905055505050565b60405180604001604052805f6001600160a01b031681526020016123a560405180608001604052805f81526020015f81526020015f15158152602001606081525090565b905290565b5080545f8255905f5260205f20908101906113b89190612428565b828054828255905f5260205f20908101928215612418579160200282015b8281111561241857825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906123e3565b50612424929150612428565b5090565b5b80821115612424575f8155600101612429565b80356001600160a01b0381168114612452575f5ffd5b919050565b5f60208284031215612467575f5ffd5b611e888261243c565b60018060a01b0381511682525f602082015160406020850152805160408501526020810151606085015260408101511515608085015260608101519050608060a085015280518060c08601525f5b818110156124db57602081840181015160e08884010152016124be565b505f60e0828701015260e0601f19601f8301168601019250505092915050565b602081525f611e886020830184612470565b5f5f6020838503121561251e575f5ffd5b82356001600160401b03811115612533575f5ffd5b8301601f81018513612543575f5ffd5b80356001600160401b03811115612558575f5ffd5b8560208260051b840101111561256c575f5ffd5b6020919091019590945092505050565b5f5f6040838503121561258d575f5ffd5b50508035926020909101359150565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b8281101561264957868503603f19018452815180518652602090810151604082880181905281519088018190529101905f9060608801905b8083101561263157835180516001600160a01b031683526020908101518184015290930192600192909201916040909101906125fa565b509650505060209384019391909101906001016125c2565b50929695505050505050565b5f5f60208385031215612666575f5ffd5b82356001600160401b0381111561267b575f5ffd5b8301601f8101851361268b575f5ffd5b80356001600160401b038111156126a0575f5ffd5b85602082840101111561256c575f5ffd5b5f602082840312156126c1575f5ffd5b81356001600160401b038111156126d6575f5ffd5b820160408185031215611e88575f5ffd5b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b8281101561264957603f19878603018452612729858351612470565b9450602093840193919091019060010161270d565b5f6020828403121561274e575f5ffd5b813560ff81168114611e88575f5ffd5b5f5f6040838503121561276f575f5ffd5b6127788361243c565b946020939093013593505050565b602080825282518282018190525f918401906040840190835b818110156127d557835180516001600160a01b03908116855260209182015116818501529093019260409092019160010161279f565b509095945050505050565b600181811c908216806127f457607f821691505b602082108103610f1057634e487b7160e01b5f52602260045260245ffd5b6020808252818101527f43616c6c6572206973206e6f742074686520636f6e7472616374206f776e6572604082015260600190565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b808201808211156106835761068361285b565b818103818111156106835761068361285b565b634e487b7160e01b5f52604160045260245ffd5b818382375f9101908152919050565b5f600182016128c9576128c961285b565b5060010190565b601f82111561046257805f5260205f20601f840160051c810160208510156128f55750805b601f840160051c820191505b818110156113b3575f8155600101612901565b81516001600160401b0381111561292d5761292d612895565b6129418161293b84546127e0565b846128d0565b6020601f821160018114612973575f831561295c5750848201515b5f19600385901b1c1916600184901b1784556113b3565b5f84815260208120601f198516915b828110156129a25787850151825560209485019460019092019101612982565b50848210156129bf57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6001600160a01b03841681526040602082018190528101829052818360608301375f818301606090810191909152601f909201601f1916010192915050565b5f8235607e19833603018112612a21575f5ffd5b9190910192915050565b6001600160401b03831115612a4257612a42612895565b612a5683612a5083546127e0565b836128d0565b5f601f841160018114612a87575f8515612a705750838201355b5f19600387901b1c1916600186901b1783556113b3565b5f83815260208120601f198716915b82811015612ab65786850135825560209485019460019092019101612a96565b5086821015612ad2575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b8135815560208201356001820155600281016040830135801515808214612b09575f5ffd5b60ff19835416915060ff8116821783555050506060820135601e19833603018112612b32575f5ffd5b820180356001600160401b03811115612b49575f5ffd5b602082019150803603821315612b5d575f5ffd5b612b6b818360038601612a2b565b50505050565b5f81612b7f57612b7f61285b565b505f190190565b5f82612ba057634e487b7160e01b5f52601260045260245ffd5b50069056fea264697066735822122071b882f5aa13e15b9bba503811bfdf20f770de1ab04c653b0dfdade41b9c0bed64736f6c634300081b0033", + "sourceMap": "1600:14234:23:-:0;;;1701:1;1656:46;;;1708:44;;;;2044:32;;;;2082:40;;;-1:-1:-1;;;;;;2082:40:23;;;;;;2128;;;;;;;;2271:39;;2406:50;;;;;;;;;-1:-1:-1;2439:10:23;2430:19;;1600:14234;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;", + "linkReferences": {} + }, + "deployedBytecode": { + "object": "0x608060405234801561000f575f5ffd5b5060043610610111575f3560e01c80636dd7d8ea1161009e578063b85f5da21161006e578063b85f5da214610202578063d04a68c71461020a578063eb9019d414610245578063f1bd0b3714610265578063f3513a371461026c575f5ffd5b80636dd7d8ea146101bf578063a09686c4146101d2578063afeea115146101da578063b5cfa68c146101ef575f5ffd5b80632bdf6d43116100e45780632bdf6d431461015c5780633174b6891461017157806340f74f4714610179578063602a9eee1461019957806362525879146101ac575f5ffd5b80630777cbef146101155780630d2bd9091461012c5780631904bb2e146101345780631b605b8614610154575b5f5ffd5b6001545b6040519081526020015b60405180910390f35b600d54610119565b610147610142366004612457565b610274565b60405161012391906124fb565b600754610119565b61016f61016a36600461250d565b6103d9565b005b61016f610467565b61018c61018736600461257c565b6104b6565b604051610123919061259c565b61016f6101a7366004612655565b610689565b61016f6101ba3660046126b1565b610964565b61016f6101cd366004612457565b6109ff565b600e54610119565b6101e2610d79565b60405161012391906126e7565b61016f6101fd36600461273e565b610f16565b61016f6113bb565b610235610218366004612457565b6001600160a01b03165f9081526003602052604090205460ff1690565b6040519015158152602001610123565b61025861025336600461275e565b6114dc565b6040516101239190612786565b5f54610119565b6101e261171d565b61027c612361565b6001600160a01b0382165f9081526003602052604090205460ff166102e85760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f724461746120646f65736e2774206578697374730000000060448201526064015b60405180910390fd5b6040805180820182526001600160a01b0384168082525f90815260026020818152918490208451608081018652815481526001820154818501529181015460ff16151594820194909452600384018054939492850193919291606084019190610350906127e0565b80601f016020809104026020016040519081016040528092919081815260200182805461037c906127e0565b80156103c75780601f1061039e576101008083540402835291602001916103c7565b820191905f5260205f20905b8154815290600101906020018083116103aa57829003601f168201915b50505091909252505050905292915050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146104215760405162461bcd60e51b81526004016102df90612812565b5f5b818110156104625761045a83838381811061044057610440612847565b90506020020160208101906104559190612457565b6118b4565b600101610423565b505050565b7f6572af8bf9a0a86efb88dcc30011626a15c9c4603503aa4466a3f87a1867deef33610491611994565b604080516001600160a01b0393841681529290911660208301520160405180910390a1565b6060336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146105005760405162461bcd60e51b81526004016102df90612812565b600e548290841061051257505f610536565b600e5461051f848661286f565b111561053657600e54610533908590612882565b90505b5f816001600160401b0381111561054f5761054f612895565b60405190808252806020026020018201604052801561059457816020015b604080518082019091525f81526060602082015281526020019060019003908161056d5790505b5090505f5b8281101561067e57604051806040016040528082886105b8919061286f565b6105c390600161286f565b8152602001600e6105d4848a61286f565b815481106105e4576105e4612847565b905f5260205f2001805480602002602001604051908101604052809291908181526020015f905b82821015610652575f848152602090819020604080518082019091526002850290910180546001600160a01b0316825260019081015482840152908352909201910161060b565b5050505081525082828151811061066b5761066b612847565b6020908102919091010152600101610599565b509150505b92915050565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036107015760405162461bcd60e51b815260206004820152601c60248201527f43616c6c65722069732074686520636f6e7472616374206f776e65720000000060448201526064016102df565b335f9081526003602052604090205460ff16156107605760405162461bcd60e51b815260206004820152601f60248201527f56616c696461746f7220697320616c726561647920726567697374657265640060448201526064016102df565b5f82826040516107719291906128a9565b60408051918290039091205f8181526004602052919091205490915060ff16156107e95760405162461bcd60e51b815260206004820152602360248201527f424c5331322d333831206b657920697320616c726561647920726567697374656044820152621c995960ea1b60648201526084016102df565b6107f38383611bfc565b5f60405180608001604052805f81526020015f81526020015f1515815260200185858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920182905250939094525050805492935090508061085b836128b8565b9091555050335f9081526003602081815260408084208054600160ff1991821681179092556002808552958390208751815593870151918401919091559085015193820180549091169315159390931790925560608301518392918201906108c39082612914565b5050505f82815260046020526040808220805460ff191660019081179091556005805491820181559092527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db090910180546001600160a01b0319163390811790915590517f61809fa303a3a57f4d70552f533f3e0b003173d424590cd4bb22a2afe000990c9161095691879087906129ce565b60405180910390a150505050565b6109746102186020830183612457565b6109c05760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f724461746120646f65736e2774206578697374730000000060448201526064016102df565b6109cd6020820182612a0d565b60025f6109dd6020850185612457565b6001600160a01b0316815260208101919091526040015f206104628282612ae4565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163303610a775760405162461bcd60e51b815260206004820152601c60248201527f43616c6c65722069732074686520636f6e7472616374206f776e65720000000060448201526064016102df565b6001600160a01b0381165f9081526003602052604090205460ff16610ade5760405162461bcd60e51b815260206004820152601760248201527f4d75737420766f746520666f722076616c696461746f7200000000000000000060448201526064016102df565b6001600160a01b0381165f9081526002602081905260409091209081015460ff1615610b575760405162461bcd60e51b815260206004820152602260248201527f4d75737420766f746520666f7220756e72657369676e65642076616c6964617460448201526137b960f11b60648201526084016102df565b335f90815260066020526040902080546001600160a01b03848116911603610bc15760405162461bcd60e51b815260206004820181905260248201527f416c726561647920766f74656420666f7220746869732076616c696461746f7260448201526064016102df565b80546001600160a01b031615610bdb57610bd9611994565b505b604080516080810182526001600160a01b03808616825233803160208085019182525f85870181815260608701828152948252600690925295909520935184549084166001600160a01b03199182161785559051600185015593516002840180549184169186169190911790555160039092018054928216929093169190911790915560085416610c8d5760088054336001600160a01b03199182168117909255600980549091169091179055610ce4565b600980546001600160a01b039081165f9081526006602052604080822060030180546001600160a01b0319908116339081179092558554828552929093206002018054841692909416919091179092558254161790555b60078054905f610cf3836128b8565b9190505550336001600160a01b031631826001015f828254610d15919061286f565b9091555050815460019083905f90610d2e90849061286f565b9091555050604080513381526001600160a01b03851660208201527fce0c7a2a940807f7dc2ce7a615c2532e915e6c0ac9a08bc4ed9d515a710a53e2910160405180910390a1505050565b600d546060905f906001600160401b03811115610d9857610d98612895565b604051908082528060200260200182016040528015610dd157816020015b610dbe612361565b815260200190600190039081610db65790505b5090505f5b600d54811015610f10575f600d8281548110610df457610df4612847565b5f9182526020808320909101546001600160a01b03168083526002808352604093849020845180860186528381528551608081018752825481526001830154818701529282015460ff1615159583019590955260038101805493965090949384019285916060840191610e66906127e0565b80601f0160208091040260200160405190810160405280929190818152602001828054610e92906127e0565b8015610edd5780601f10610eb457610100808354040283529160200191610edd565b820191905f5260205f20905b815481529060010190602001808311610ec057829003601f168201915b505050505081525050815250848481518110610efb57610efb612847565b60209081029190910101525050600101610dd6565b50919050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610f5e5760405162461bcd60e51b81526004016102df90612812565b610f66611c5e565b610f6e611dad565b600a80546001600160a01b03191690556001545f80549091610fa09160ff8516918491610f9b9190612882565b611dfe565b90508060ff165f03610fb0575050565b5f5b60055481101561125b575f60058281548110610fd057610fd0612847565b5f9182526020808320909101546001600160a01b03168083526002918290526040909220908101549192509060ff161561100b575050611253565b600a546001600160a01b03166110465750600a80546001600160a01b0319166001600160a01b03929092169190911790556001600c55611253565b8360ff16600c5410156110645761105d8285611e8f565b5050611253565b600a546001600160a01b039081165f908152600260208181526040928390208351808501855294871685528351608081018552865481526001870154818401529286015460ff161515938301939093526003850180549394611240949093928401929187916060840191906110d8906127e0565b80601f0160208091040260200160405190810160405280929190818152602001828054611104906127e0565b801561114f5780601f106111265761010080835404028352916020019161114f565b820191905f5260205f20905b81548152906001019060200180831161113257829003601f168201915b505050919092525050509052604080518082018252600a546001600160a01b031681528151608081018352855481526001860154602082810191909152600287015460ff161515938201939093526003860180549293840192879160608401916111b8906127e0565b80601f01602080910402602001604051908101604052809291908181526020018280546111e4906127e0565b801561122f5780601f106112065761010080835404028352916020019161122f565b820191905f5260205f20905b81548152906001019060200180831161121257829003601f168201915b50505050508152505081525061226e565b1561124f5761124f8386611e8f565b5050505b600101610fb2565b50600e80546001810182555f918252600a547fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fd909101916001600160a01b03909116906112aa90600d906123aa565b8260ff166001600160401b038111156112c5576112c5612895565b6040519080825280602002602001820160405280156112ee578160200160208202803683370190505b50805161130391600d916020909101906123c5565b505f5b8360ff168110156113b35781600d828154811061132557611325612847565b5f918252602080832090910180546001600160a01b039485166001600160a01b03199182161790915560408051808201825296851680885280855260028085528286206001908101548a87019081528b548083018d558c89528789209b519302909a01805492891692909516919091178455975192880192909255908352600b909152902054169101611306565b505050505b50565b335f9081526003602052604090205460ff166114195760405162461bcd60e51b815260206004820152601960248201527f43616c6c6572206973206e6f7420612076616c696461746f720000000000000060448201526064016102df565b335f9081526002602081905260409091209081015460ff161561147e5760405162461bcd60e51b815260206004820152601d60248201527f56616c696461746f7220697320616c72656164792072657369676e656400000060448201526064016102df565b60028101805460ff19166001908117909155805481905f906114a190839061286f565b90915550506040513381527f24250fc1ec78a1405ddd4cc8b75964858af228d05faa8d4bc1302966d8a541179060200160405180910390a150565b6060336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146115265760405162461bcd60e51b81526004016102df90612812565b5f611534835f600754611dfe565b6001600160401b0381111561154b5761154b612895565b60405190808252806020026020018201604052801561158f57816020015b604080518082019091525f80825260208201528152602001906001900390816115695790505b506008549091506001600160a01b03908116908516156115c857506001600160a01b038085165f90815260066020526040902060030154165b5f5b6001600160a01b038216158015906115e157508481105b15611656576001600160a01b038083165f818152600660209081526040918290208251808401909352928252825490931692810192909252908483611625816128b8565b94508151811061163757611637612847565b6020908102919091010152600301546001600160a01b031691506115ca565b8083510361166957829350505050610683565b5f816001600160401b0381111561168257611682612895565b6040519080825280602002602001820160405280156116c657816020015b604080518082019091525f80825260208201528152602001906001900390816116a05790505b5090505f5b82811015611712578481815181106116e5576116e5612847565b60200260200101518282815181106116ff576116ff612847565b60209081029190910101526001016116cb565b509695505050505050565b6005546060905f906001600160401b0381111561173c5761173c612895565b60405190808252806020026020018201604052801561177557816020015b611762612361565b81526020019060019003908161175a5790505b5090505f5b600554811015610f10575f6005828154811061179857611798612847565b5f9182526020808320909101546001600160a01b03168083526002808352604093849020845180860186528381528551608081018752825481526001830154818701529282015460ff161515958301959095526003810180549396509094938401928591606084019161180a906127e0565b80601f0160208091040260200160405190810160405280929190818152602001828054611836906127e0565b80156118815780601f1061185857610100808354040283529160200191611881565b820191905f5260205f20905b81548152906001019060200180831161186457829003601f168201915b50505050508152505081525084848151811061189f5761189f612847565b6020908102919091010152505060010161177a565b6001600160a01b038082165f90815260066020526040902080549091166118d9575050565b60018101546001600160a01b0383163181101561193a57611904816001600160a01b03851631612882565b82546001600160a01b03165f908152600260205260408120600101805490919061192f90849061286f565b9091555061197f9050565b61194e6001600160a01b0384163182612882565b82546001600160a01b03165f9081526002602052604081206001018054909190611979908490612882565b90915550505b506001600160a01b0390911631600190910155565b335f90815260066020526040812080546001600160a01b0316611a075760405162461bcd60e51b815260206004820152602560248201527f4d75737420766f746520666f722076616c696461746f72206265666f726520756044820152646e766f746560d81b60648201526084016102df565b6009546008546001600160a01b03918216911603611a4057600880546001600160a01b0319908116909155600980549091169055611b53565b600954336001600160a01b0390911603611a99576002810180546001600160a01b039081165f90815260066020526040902060030180546001600160a01b03199081169091559154600980549093169116179055611b53565b600854336001600160a01b0390911603611afd576009546001600160a01b039081165f9081526006602052604080822060020180546001600160a01b0319908116909155600880548086168552929093206003015491169216919091179055611b53565b60038181018054600280850180546001600160a01b039081165f9081526006602052604080822090970180549583166001600160a01b031996871617905591549454811682529490200180549290931691161790555b80546001600160a01b03165f818152600260205260408120600180850154908201805492939192909190611b88908490612882565b9091555050805460019082905f90611ba1908490612882565b9091555050335f90815260066020526040812080546001600160a01b0319908116825560018201839055600282018054821690556003909101805490911690556007805491611bef83612b71565b9091555091949350505050565b60308114611c5a5760405162461bcd60e51b815260206004820152602560248201527f424c5331322d333831207075626c69634b6579206c656e67746820697320696e6044820152641d985b1a5960da1b60648201526084016102df565b5050565b6005545f611c6d600183612882565b90505b8015611c5a575f611c8282600161286f565b60408051426020820152449181019190915260608101849052608001604051602081830303815290604052805190602001205f1c611cc09190612b86565b90505f60058381548110611cd657611cd6612847565b5f91825260209091200154600580546001600160a01b0390921692509083908110611d0357611d03612847565b5f91825260209091200154600580546001600160a01b039092169185908110611d2e57611d2e612847565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055508060058381548110611d6d57611d6d612847565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b0316021790555050508080611da590612b71565b915050611c70565b600a546001600160a01b03165b6001600160a01b03811615611df7576001600160a01b039081165f908152600b6020526040902080546001600160a01b0319811690915516611dba565b505f600c55565b5f81831115611e675760405162461bcd60e51b815260206004820152602f60248201527f4d696e696d756d2073686f756c64206265206c657373207468616e206f72206560448201526e7175616c20746f206d6178696d756d60881b60648201526084016102df565b82841015611e76575081611e88565b81841115611e85575080611e88565b50825b9392505050565b6001600160a01b0382165f9081526002602081815260408084208151608081018352815481526001820154938101939093529283015460ff16151590820152600382018054919291606084019190611ee6906127e0565b80601f0160208091040260200160405190810160405280929190818152602001828054611f12906127e0565b8015611f5d5780601f10611f3457610100808354040283529160200191611f5d565b820191905f5260205f20905b815481529060010190602001808311611f4057829003601f168201915b505050919092525050604080518082018252600a546001600160a01b03168082525f90815260026020818152918490208451608081018652815481526001820154818501529181015460ff1615159482019490945260038401805496975061207e9693955091850193909290916060840191611fd8906127e0565b80601f0160208091040260200160405190810160405280929190818152602001828054612004906127e0565b801561204f5780601f106120265761010080835404028352916020019161204f565b820191905f5260205f20905b81548152906001019060200180831161203257829003601f168201915b5050505050815250508152506040518060400160405280866001600160a01b031681526020018481525061226e565b156120915761208c836122b2565b61220f565b600a546001600160a01b039081165f818152600b6020526040902054909116905b6001600160a01b0382166120cf576120ca8186612303565b61220c565b6040805180820182526001600160a01b0384168082525f90815260026020818152918490208451608081018652815481526001820154818501529181015460ff161515948201949094526003840180546121dc9593850193916060840191612136906127e0565b80601f0160208091040260200160405190810160405280929190818152602001828054612162906127e0565b80156121ad5780601f10612184576101008083540402835291602001916121ad565b820191905f5260205f20905b81548152906001019060200180831161219057829003601f168201915b5050505050815250508152506040518060400160405280886001600160a01b031681526020018681525061226e565b156121eb576120ca8186612303565b506001600160a01b038082165f908152600b602052604090205416906120b2565b50505b8160ff16600c54111561046257600a80546001600160a01b039081165f908152600b6020526040812080546001600160a01b03198082169092558454931692168217909255600c8054919261226383612b71565b919050555050505050565b5f8160200151602001518360200151602001510361229e5750805182516001600160a01b03918216911611610683565b506020908101518101519181015101511190565b600a80546001600160a01b038381165f818152600b602052604081208054939094166001600160a01b031993841617909355835490911617909155600c8054916122fb836128b8565b919050555050565b6001600160a01b038281165f818152600b602052604080822080548686168085529284208054919096166001600160a01b03199182161790955592825282549093169092179055600c805491612358836128b8565b91905055505050565b60405180604001604052805f6001600160a01b031681526020016123a560405180608001604052805f81526020015f81526020015f15158152602001606081525090565b905290565b5080545f8255905f5260205f20908101906113b89190612428565b828054828255905f5260205f20908101928215612418579160200282015b8281111561241857825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906123e3565b50612424929150612428565b5090565b5b80821115612424575f8155600101612429565b80356001600160a01b0381168114612452575f5ffd5b919050565b5f60208284031215612467575f5ffd5b611e888261243c565b60018060a01b0381511682525f602082015160406020850152805160408501526020810151606085015260408101511515608085015260608101519050608060a085015280518060c08601525f5b818110156124db57602081840181015160e08884010152016124be565b505f60e0828701015260e0601f19601f8301168601019250505092915050565b602081525f611e886020830184612470565b5f5f6020838503121561251e575f5ffd5b82356001600160401b03811115612533575f5ffd5b8301601f81018513612543575f5ffd5b80356001600160401b03811115612558575f5ffd5b8560208260051b840101111561256c575f5ffd5b6020919091019590945092505050565b5f5f6040838503121561258d575f5ffd5b50508035926020909101359150565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b8281101561264957868503603f19018452815180518652602090810151604082880181905281519088018190529101905f9060608801905b8083101561263157835180516001600160a01b031683526020908101518184015290930192600192909201916040909101906125fa565b509650505060209384019391909101906001016125c2565b50929695505050505050565b5f5f60208385031215612666575f5ffd5b82356001600160401b0381111561267b575f5ffd5b8301601f8101851361268b575f5ffd5b80356001600160401b038111156126a0575f5ffd5b85602082840101111561256c575f5ffd5b5f602082840312156126c1575f5ffd5b81356001600160401b038111156126d6575f5ffd5b820160408185031215611e88575f5ffd5b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b8281101561264957603f19878603018452612729858351612470565b9450602093840193919091019060010161270d565b5f6020828403121561274e575f5ffd5b813560ff81168114611e88575f5ffd5b5f5f6040838503121561276f575f5ffd5b6127788361243c565b946020939093013593505050565b602080825282518282018190525f918401906040840190835b818110156127d557835180516001600160a01b03908116855260209182015116818501529093019260409092019160010161279f565b509095945050505050565b600181811c908216806127f457607f821691505b602082108103610f1057634e487b7160e01b5f52602260045260245ffd5b6020808252818101527f43616c6c6572206973206e6f742074686520636f6e7472616374206f776e6572604082015260600190565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b808201808211156106835761068361285b565b818103818111156106835761068361285b565b634e487b7160e01b5f52604160045260245ffd5b818382375f9101908152919050565b5f600182016128c9576128c961285b565b5060010190565b601f82111561046257805f5260205f20601f840160051c810160208510156128f55750805b601f840160051c820191505b818110156113b3575f8155600101612901565b81516001600160401b0381111561292d5761292d612895565b6129418161293b84546127e0565b846128d0565b6020601f821160018114612973575f831561295c5750848201515b5f19600385901b1c1916600184901b1784556113b3565b5f84815260208120601f198516915b828110156129a25787850151825560209485019460019092019101612982565b50848210156129bf57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6001600160a01b03841681526040602082018190528101829052818360608301375f818301606090810191909152601f909201601f1916010192915050565b5f8235607e19833603018112612a21575f5ffd5b9190910192915050565b6001600160401b03831115612a4257612a42612895565b612a5683612a5083546127e0565b836128d0565b5f601f841160018114612a87575f8515612a705750838201355b5f19600387901b1c1916600186901b1783556113b3565b5f83815260208120601f198716915b82811015612ab65786850135825560209485019460019092019101612a96565b5086821015612ad2575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b8135815560208201356001820155600281016040830135801515808214612b09575f5ffd5b60ff19835416915060ff8116821783555050506060820135601e19833603018112612b32575f5ffd5b820180356001600160401b03811115612b49575f5ffd5b602082019150803603821315612b5d575f5ffd5b612b6b818360038601612a2b565b50505050565b5f81612b7f57612b7f61285b565b505f190190565b5f82612ba057634e487b7160e01b5f52601260045260245ffd5b50069056fea264697066735822122071b882f5aa13e15b9bba503811bfdf20f770de1ab04c653b0dfdade41b9c0bed64736f6c634300081b0033", + "sourceMap": "1600:14234:23:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8206:113;8288:24;;8206:113;;;160:25:32;;;148:2;133:18;8206:113:23;;;;;;;;8325:118;8405:24;:31;8325:118;;10111:244;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;10595:91::-;10667:12;;10595:91;;13764:228;;;;;;:::i;:::-;;:::i;:::-;;12617:79;;;:::i;14098:529::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;8449:951::-;;;;;;:::i;:::-;;:::i;10361:228::-;;;;;;:::i;:::-;;:::i;11543:1068::-;;;;;;:::i;:::-;;:::i;13998:94::-;14071:7;:14;13998:94;;6999:458;;;:::i;:::-;;;;;;;:::i;3531:1701::-;;;;;;:::i;:::-;;:::i;9406:400::-;;;:::i;9812:125::-;;;;;;:::i;:::-;-1:-1:-1;;;;;9901:29:23;9878:4;9901:29;;;:23;:29;;;;;;;;;9812:125;;;;6494:14:32;;6487:22;6469:41;;6457:2;6442:18;9812:125:23;6329:187:32;10692:845:23;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;8083:117::-;8141:7;8167:26;8083:117;;7628:449;;;:::i;10111:244::-;10169:16;;:::i;:::-;-1:-1:-1;;;;;9901:29:23;;9878:4;9901:29;;;:23;:29;;;;;;;;10197:71;;;;-1:-1:-1;;;10197:71:23;;7839:2:32;10197:71:23;;;7821:21:32;7878:2;7858:18;;;7851:30;7917;7897:18;;;7890:58;7965:18;;10197:71:23;;;;;;;;;10285:63;;;;;;;;-1:-1:-1;;;;;10285:63:23;;;;;-1:-1:-1;10315:31:23;;;:24;10285:63;10315:31;;;;;;;10285:63;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10315:31;10285:63;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;10285:63:23;;;;-1:-1:-1;;;10285:63:23;;10278:70;10111:244;-1:-1:-1;;10111:244:23:o;13764:228::-;2501:10;-1:-1:-1;;;;;2515:6:23;2501:20;;2493:65;;;;-1:-1:-1;;;2493:65:23;;;;;;;:::i;:::-;13899:9:::1;13894:92;13914:17:::0;;::::1;13894:92;;;13952:23;13965:6;;13972:1;13965:9;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;13952:12;:23::i;:::-;13933:3;;13894:92;;;;13764:228:::0;;:::o;12617:79::-;12659:30;12667:10;12679:9;:7;:9::i;:::-;12659:30;;;-1:-1:-1;;;;;9064:32:32;;;9046:51;;9133:32;;;;9128:2;9113:18;;9106:60;9019:18;12659:30:23;;;;;;;12617:79::o;14098:529::-;14179:14;2501:10;-1:-1:-1;;;;;2515:6:23;2501:20;;2493:65;;;;-1:-1:-1;;;2493:65:23;;;;;;;:::i;:::-;14250:7:::1;:14:::0;14221:5;;14240:24;::::1;14236:163;;-1:-1:-1::0;14288:1:23::1;14236:163;;;14327:7;:14:::0;14310::::1;14319:5:::0;14310:6;:14:::1;:::i;:::-;:31;14306:93;;;14365:7;:14:::0;:23:::1;::::0;14382:6;;14365:23:::1;:::i;:::-;14357:31;;14306:93;14409:21;14445:5;-1:-1:-1::0;;;;;14433:18:23::1;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1::0;;;;;;;;;;;;;;;;;14433:18:23::1;;;;;;;;;;;;;;;-1:-1:-1::0;14409:42:23;-1:-1:-1;14466:9:23::1;14461:136;14485:5;14481:1;:9;14461:136;;;14523:63;;;;;;;;14546:1;14537:6;:10;;;;:::i;:::-;:14;::::0;14550:1:::1;14537:14;:::i;:::-;14523:63:::0;;::::1;;14565:7;14573:10;14582:1:::0;14573:6;:10:::1;:::i;:::-;14565:19;;;;;;;;:::i;:::-;;;;;;;;14523:63;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;;;::::1;::::0;;;;::::1;::::0;;;;::::1;::::0;;;::::1;::::0;::::1;::::0;;::::1;::::0;;-1:-1:-1;;;;;14523:63:23::1;::::0;;;;;::::1;::::0;;;::::1;::::0;;;;;;::::1;::::0;::::1;;;;;;;;;;::::0;14511:6:::1;14518:1;14511:9;;;;;;;;:::i;:::-;;::::0;;::::1;::::0;;;;;:75;14492:3:::1;;14461:136;;;-1:-1:-1::0;14614:6:23;-1:-1:-1;;2568:1:23::1;14098:529:::0;;;;:::o;8449:951::-;-1:-1:-1;;;;;2638:6:23;2624:20;:10;:20;2616:61;;;;-1:-1:-1;;;2616:61:23;;9906:2:32;2616:61:23;;;9888:21:32;9945:2;9925:18;;;9918:30;9984;9964:18;;;9957:58;10032:18;;2616:61:23;9704:352:32;2616:61:23;8578:10:::1;8554:35;::::0;;;:23:::1;:35;::::0;;;;;::::1;;8553:36;8545:80;;;::::0;-1:-1:-1;;;8545:80:23;;10263:2:32;8545:80:23::1;::::0;::::1;10245:21:32::0;10302:2;10282:18;;;10275:30;10341:33;10321:18;;;10314:61;10392:18;;8545:80:23::1;10061:355:32::0;8545:80:23::1;8636:27;8676:20;;8666:31;;;;;;;:::i;:::-;;::::0;;;;;::::1;::::0;;;8717:42:::1;::::0;;;:21:::1;:42;::::0;;;;;;8666:31;;-1:-1:-1;8717:42:23::1;;8716:43;8708:91;;;::::0;-1:-1:-1;;;8708:91:23;;10899:2:32;8708:91:23::1;::::0;::::1;10881:21:32::0;10938:2;10918:18;;;10911:30;10977:34;10957:18;;;10950:62;-1:-1:-1;;;11028:18:32;;;11021:33;11071:19;;8708:91:23::1;10697:399:32::0;8708:91:23::1;8810:46;8835:20;;8810:24;:46::i;:::-;8867:30;8900:168;;;;;;;;8941:1;8900:168;;;;8969:1;8900:168;;;;8996:5;8900:168;;;;;;9037:20;;8900:168;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;::::0;;;-1:-1:-1;8900:168:23;;;;-1:-1:-1;;9079:28:23;;8867:201;;-1:-1:-1;8900:168:23;-1:-1:-1;8900:168:23;9079:28:::1;::::0;::::1;:::i;:::-;::::0;;;-1:-1:-1;;9141:10:23::1;9117:35;::::0;;;:23:::1;:35;::::0;;;;;;;:42;;9155:4:::1;-1:-1:-1::0;;9117:42:23;;::::1;::::0;::::1;::::0;;;9169:24:::1;:36:::0;;;;;;;:48;;;;;;::::1;::::0;;;::::1;::::0;;;;;;::::1;::::0;;;::::1;::::0;;;;::::1;::::0;::::1;;::::0;;;::::1;::::0;;;::::1;::::0;::::1;::::0;;;:36;:48;::::1;::::0;::::1;::::0;;::::1;:::i;:::-;-1:-1:-1::0;;;9227:42:23::1;::::0;;;:21:::1;:42;::::0;;;;;:49;;-1:-1:-1;;9227:49:23::1;9272:4;9227:49:::0;;::::1;::::0;;;9286:21:::1;:38:::0;;;;::::1;::::0;;;;;;;;::::1;::::0;;-1:-1:-1;;;;;;9286:38:23::1;9313:10;9286:38:::0;;::::1;::::0;;;9340:53;;::::1;::::0;::::1;::::0;9372:20;;;;9340:53:::1;:::i;:::-;;;;;;;;8535:865;;8449:951:::0;;:::o;10361:228::-;10442:38;10464:15;;;;:10;:15;:::i;10442:38::-;10434:79;;;;-1:-1:-1;;;10434:79:23;;7839:2:32;10434:79:23;;;7821:21:32;7878:2;7858:18;;;7851:30;7917;7897:18;;;7890:58;7965:18;;10434:79:23;7637:352:32;10434:79:23;10567:15;;;;:10;:15;:::i;:::-;10523:24;:41;10548:15;;;;:10;:15;:::i;:::-;-1:-1:-1;;;;;10523:41:23;;;;;;;;;;;;-1:-1:-1;10523:41:23;:59;;:41;:59;:::i;11543:1068::-;-1:-1:-1;;;;;2638:6:23;2624:20;:10;:20;2616:61;;;;-1:-1:-1;;;2616:61:23;;9906:2:32;2616:61:23;;;9888:21:32;9945:2;9925:18;;;9918:30;9984;9964:18;;;9957:58;10032:18;;2616:61:23;9704:352:32;2616:61:23;-1:-1:-1;;;;;9901:29:23;;9878:4;9901:29;;;:23;:29;;;;;;;;11603:63:::1;;;::::0;-1:-1:-1;;;11603:63:23;;16739:2:32;11603:63:23::1;::::0;::::1;16721:21:32::0;16778:2;16758:18;;;16751:30;16817:25;16797:18;;;16790:53;16860:18;;11603:63:23::1;16537:347:32::0;11603:63:23::1;-1:-1:-1::0;;;;;11715:30:23;::::1;11677:35;11715:30:::0;;;:24:::1;:30;::::0;;;;;;;11764:24;;::::1;::::0;::::1;;11763:25;11755:72;;;::::0;-1:-1:-1;;;11755:72:23;;17091:2:32;11755:72:23::1;::::0;::::1;17073:21:32::0;17130:2;17110:18;;;17103:30;17169:34;17149:18;;;17142:62;-1:-1:-1;;;17220:18:32;;;17213:32;17262:19;;11755:72:23::1;16889:398:32::0;11755:72:23::1;11867:10;11838:18;11859:19:::0;;;:7:::1;:19;::::0;;;;11896:15;;-1:-1:-1;;;;;11896:23:23;;::::1;:15:::0;::::1;:23:::0;11888:68:::1;;;::::0;-1:-1:-1;;;11888:68:23;;17494:2:32;11888:68:23::1;::::0;::::1;17476:21:32::0;;;17513:18;;;17506:30;17572:34;17552:18;;;17545:62;17624:18;;11888:68:23::1;17292:356:32::0;11888:68:23::1;11971:15:::0;;-1:-1:-1;;;;;11971:15:23::1;:29:::0;11967:69:::1;;12016:9;:7;:9::i;:::-;;11967:69;12068:88;::::0;;::::1;::::0;::::1;::::0;;-1:-1:-1;;;;;12068:88:23;;::::1;::::0;;12100:10:::1;:18:::0;::::1;12068:88;::::0;;::::1;::::0;;;-1:-1:-1;12068:88:23;;;;;;;;;;;;12046:19;;;:7:::1;:19:::0;;;;;;;:110;;;;;;::::1;-1:-1:-1::0;;;;;;12046:110:23;;::::1;;::::0;;;;;;::::1;::::0;;;::::1;::::0;::::1;::::0;;;;::::1;::::0;;::::1;::::0;;;::::1;::::0;;;::::1;::::0;;::::1;::::0;;;;::::1;::::0;;;::::1;::::0;;;::::1;::::0;;;12171:11:::1;::::0;::::1;12167:277;;12212:11;:24:::0;;12226:10:::1;-1:-1:-1::0;;;;;;12212:24:23;;::::1;::::0;::::1;::::0;;;12250:11:::1;:24:::0;;;;::::1;::::0;;::::1;::::0;;12167:277:::1;;;12313:11;::::0;;-1:-1:-1;;;;;12313:11:23;;::::1;12305:20;::::0;;;:7:::1;:20;::::0;;;;;:25:::1;;:38:::0;;-1:-1:-1;;;;;;12305:38:23;;::::1;12333:10;12305:38:::0;;::::1;::::0;;;12384:11;;12357:19;;;;;;;:24:::1;;:38:::0;;;::::1;12384:11:::0;;;::::1;12357:38:::0;;;::::1;::::0;;;12409:24;;::::1;;::::0;;12167:277:::1;12453:12;:14:::0;;;:12:::1;:14;::::0;::::1;:::i;:::-;;;;;;12507:10;-1:-1:-1::0;;;;;12507:18:23::1;;12478:13;:25;;;:47;;;;;;;:::i;:::-;::::0;;;-1:-1:-1;;12535:30:23;;12564:1:::1;::::0;12535:13;;:25:::1;::::0;:30:::1;::::0;12564:1;;12535:30:::1;:::i;:::-;::::0;;;-1:-1:-1;;12581:23:23::1;::::0;;12587:10:::1;9046:51:32::0;;-1:-1:-1;;;;;9133:32:32;;9128:2;9113:18;;9106:60;12581:23:23::1;::::0;9019:18:32;12581:23:23::1;;;;;;;11593:1018;;11543:1068:::0;:::o;6999:458::-;7122:24;:31;7048:18;;7078:25;;-1:-1:-1;;;;;7106:48:23;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;-1:-1:-1;7078:76:23;-1:-1:-1;7169:9:23;7164:263;7188:24;:31;7184:35;;7164:263;;;7240:12;7255:24;7280:1;7255:27;;;;;;;;:::i;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;7255:27:23;7325:30;;;:24;:30;;;;;;;;7381:35;;;;;;;;;;;;;;;;;;;;;7255:27;7381:35;;;;;;;;;;;;;;;;;;;;;;;;;;;7255:27;;-1:-1:-1;7325:30:23;;7381:35;;;;7325:30;;7381:35;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7369:6;7376:1;7369:9;;;;;;;;:::i;:::-;;;;;;;;;;:47;-1:-1:-1;;7221:3:23;;7164:263;;;-1:-1:-1;7444:6:23;6999:458;-1:-1:-1;6999:458:23:o;3531:1701::-;2501:10;-1:-1:-1;;;;;2515:6:23;2501:20;;2493:65;;;;-1:-1:-1;;;2493:65:23;;;;;;;:::i;:::-;3601:9:::1;:7;:9::i;:::-;3620:21;:19;:21::i;:::-;3652:18;:31:::0;;-1:-1:-1;;;;;;3652:31:23::1;::::0;;;3754:24;3681:1:::1;3725:26:::0;;3681:1;;3712:67:::1;::::0;::::1;::::0;::::1;::::0;3681:1;;3725:53:::1;::::0;3754:24;3725:53:::1;:::i;:::-;3712:6;:67::i;:::-;3694:86;;3853:3;:8;;3860:1;3853:8:::0;3849:45:::1;;3877:7;3531:1701:::0;:::o;3849:45::-:1;3909:9;3904:870;3928:21;:28:::0;3924:32;::::1;3904:870;;;3977:12;3992:21;4014:1;3992:24;;;;;;;;:::i;:::-;;::::0;;;::::1;::::0;;;;;::::1;::::0;-1:-1:-1;;;;;3992:24:23::1;4060:30:::0;;;:24:::1;:30:::0;;;;;;;;4108:15;;::::1;::::0;3992:24;;-1:-1:-1;4060:30:23;4108:15:::1;;4104:62;;;4143:8;;;;4104:62;4184:18;::::0;-1:-1:-1;;;;;4184:18:23::1;4180:163;;-1:-1:-1::0;4236:18:23::1;:25:::0;;-1:-1:-1;;;;;;4236:25:23::1;-1:-1:-1::0;;;;;4236:25:23;;;::::1;::::0;;;::::1;::::0;;-1:-1:-1;4279:19:23::1;:23:::0;4320:8:::1;;4180:163;4383:3;4361:25;;:19;;:25;4357:119;;;4406:29;4425:4;4431:3;4406:18;:29::i;:::-;4453:8;;;;4357:119;4548:18;::::0;-1:-1:-1;;;;;4548:18:23;;::::1;4490:30;4523:44:::0;;;:24:::1;:44;::::0;;;;;;;;4597:35;;;;::::1;::::0;;;;::::1;::::0;;;;::::1;::::0;::::1;::::0;;;;;;4548:18;4597:35;::::1;::::0;;;::::1;::::0;;;::::1;::::0;::::1;;;;::::0;;;;;;;::::1;::::0;::::1;::::0;;4523:44;;4586:102:::1;::::0;4597:35;;;;::::1;::::0;;4626:4;;4597:35;;;;;::::1;::::0;::::1;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1::0;;;4597:35:23;;;;-1:-1:-1;;;4597:35:23;;4634:53:::1;::::0;;;;::::1;::::0;;4651:18:::1;::::0;-1:-1:-1;;;;;4651:18:23::1;4634:53:::0;;;;::::1;::::0;::::1;::::0;;;;;;4651:18;4634:53;::::1;::::0;::::1;::::0;;::::1;::::0;;;;::::1;::::0;::::1;::::0;::::1;;;;::::0;;;;;;;::::1;::::0;::::1;::::0;;;;;::::1;::::0;4677:8;;4634:53;;;;::::1;::::0;::::1;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;::::0;4586:10:::1;:102::i;:::-;4582:182;;;4720:29;4739:4;4745:3;4720:18;:29::i;:::-;3963:811;;;3904:870;3958:3;;3904:870;;;-1:-1:-1::0;4817:7:23::1;:14:::0;;::::1;::::0;::::1;::::0;;4784:30:::1;4817:14:::0;;;4857:18:::1;::::0;4817:14;;;::::1;::::0;-1:-1:-1;;;;;4857:18:23;;::::1;::::0;4885:31:::1;::::0;4892:24:::1;::::0;4885:31:::1;:::i;:::-;4967:3;4953:18;;-1:-1:-1::0;;;;;4953:18:23::1;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;-1:-1:-1;4953:18:23::1;-1:-1:-1::0;4926:45:23;;::::1;::::0;:24:::1;::::0;:45:::1;::::0;;::::1;::::0;::::1;:::i;:::-;-1:-1:-1::0;4986:9:23::1;4981:245;5005:3;5001:7;;:1;:7;4981:245;;;5059:4;5029:24;5054:1;5029:27;;;;;;;;:::i;:::-;;::::0;;;::::1;::::0;;;;;::::1;:34:::0;;-1:-1:-1;;;;;5029:34:23;;::::1;-1:-1:-1::0;;;;;;5029:34:23;;::::1;;::::0;;;5088:85:::1;::::0;;;;::::1;::::0;;;;::::1;::::0;;;5129:30;;;:24:::1;:30:::0;;;;;;5029:34;5129:42;;::::1;::::0;5088:85;;::::1;::::0;;;5077:97;;;;::::1;::::0;;;;;;;;;;;::::1;::::0;;::::1;::::0;;;;::::1;::::0;;;::::1;::::0;;;::::1;::::0;;;;;;::::1;::::0;;;;5195:20;;;:14:::1;:20:::0;;;;;;::::1;::::0;5010:3:::1;4981:245;;;;3591:1641;;;2568:1;3531:1701:::0;:::o;9406:400::-;9482:10;9878:4;9901:29;;;:23;:29;;;;;;;;9452:71;;;;-1:-1:-1;;;9452:71:23;;17855:2:32;9452:71:23;;;17837:21:32;17894:2;17874:18;;;17867:30;17933:27;17913:18;;;17906:55;17978:18;;9452:71:23;17653:349:32;9452:71:23;9593:10;9534:31;9568:36;;;:24;:36;;;;;;;;9623:20;;;;;;9622:21;9614:63;;;;-1:-1:-1;;;9614:63:23;;18209:2:32;9614:63:23;;;18191:21:32;18248:2;18228:18;;;18221:30;18287:31;18267:18;;;18260:59;18336:18;;9614:63:23;18007:353:32;9614:63:23;9688:20;;;:27;;-1:-1:-1;;9688:27:23;9711:4;9688:27;;;;;;9725:29;;9711:4;;9688:20;;9725:29;;9711:4;;9725:29;:::i;:::-;;;;-1:-1:-1;;9770:29:23;;9788:10;18511:51:32;;9770:29:23;;18499:2:32;18484:18;9770:29:23;;;;;;;9442:364;9406:400::o;10692:845::-;10770:19;2501:10;-1:-1:-1;;;;;2515:6:23;2501:20;;2493:65;;;;-1:-1:-1;;;2493:65:23;;;;;;;:::i;:::-;10801:26:::1;10847:30;10854:5;10861:1;10864:12;;10847:6;:30::i;:::-;-1:-1:-1::0;;;;;10830:48:23::1;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1::0;;;;;;;;;;;;;;;;;10830:48:23::1;;;;;;;;;;;;;;;-1:-1:-1::0;10904:11:23::1;::::0;10801:77;;-1:-1:-1;;;;;;10904:11:23;;::::1;::::0;10930:18;::::1;::::0;10926:74:::1;;-1:-1:-1::0;;;;;;10971:13:23;;::::1;;::::0;;;:7:::1;:13;::::0;;;;:18:::1;;::::0;::::1;10926:74;11010:9;11033:211;-1:-1:-1::0;;;;;11040:18:23;::::1;::::0;;::::1;::::0;:31:::1;;;11066:5;11062:1;:9;11040:31;11033:211;;;-1:-1:-1::0;;;;;11108:13:23;;::::1;11087:18;11108:13:::0;;;:7:::1;:13;::::0;;;;;;;;11149:53;;;;::::1;::::0;;;;;;11185:15;;;;::::1;11149:53:::0;;::::1;::::0;;;;11108:13;11135:6;11142:3;::::1;::::0;::::1;:::i;:::-;;;11135:11;;;;;;;;:::i;:::-;;::::0;;::::1;::::0;;;;;:67;11223:10:::1;;::::0;-1:-1:-1;;;;;11223:10:23::1;::::0;-1:-1:-1;11033:211:23::1;;;11275:1;11258:6;:13;:18:::0;11254:62:::1;;11299:6;11292:13;;;;;;;11254:62;11374:25;11419:1;-1:-1:-1::0;;;;;11402:19:23::1;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1::0;;;;;;;;;;;;;;;;;11402:19:23::1;;;;;;;;;;;;;;;-1:-1:-1::0;11374:47:23;-1:-1:-1;11436:9:23::1;11431:77;11455:1;11451;:5;11431:77;;;11488:6;11495:1;11488:9;;;;;;;;:::i;:::-;;;;;;;11477:5;11483:1;11477:8;;;;;;;;:::i;:::-;;::::0;;::::1;::::0;;;;;:20;11458:3:::1;;11431:77;;;-1:-1:-1::0;11525:5:23;10692:845;-1:-1:-1;;;;;;10692:845:23:o;7628:449::-;7751:21;:28;7677:18;;7707:25;;-1:-1:-1;;;;;7735:45:23;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;-1:-1:-1;7707:73:23;-1:-1:-1;7795:9:23;7790:257;7814:21;:28;7810:32;;7790:257;;;7863:12;7878:21;7900:1;7878:24;;;;;;;;:::i;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;7878:24:23;7945:30;;;:24;:30;;;;;;;;8001:35;;;;;;;;;;;;;;;;;;;;;7878:24;8001:35;;;;;;;;;;;;;;;;;;;;;;;;;;;7878:24;;-1:-1:-1;7945:30:23;;8001:35;;;;7945:30;;8001:35;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7989:6;7996:1;7989:9;;;;;;;;:::i;:::-;;;;;;;;;;:47;-1:-1:-1;;7844:3:23;;7790:257;;14633:523;-1:-1:-1;;;;;14708:13:23;;;14687:18;14708:13;;;:7;:13;;;;;14735:15;;14708:13;;14735:15;14731:66;;14780:7;14633:523;:::o;14731:66::-;14830:13;;;;-1:-1:-1;;;;;14873:12:23;;;14858:27;;14854:257;;;14958:27;14973:12;-1:-1:-1;;;;;14958:12:23;;;:27;:::i;:::-;14926:15;;-1:-1:-1;;;;;14926:15:23;14901:41;;;;:24;:41;;;;;14926:15;14901:53;:84;;:53;;:41;:84;;;;;:::i;:::-;;;;-1:-1:-1;14854:257:23;;-1:-1:-1;14854:257:23;;15073:27;-1:-1:-1;;;;;15088:12:23;;;15073;:27;:::i;:::-;15041:15;;-1:-1:-1;;;;;15041:15:23;15016:41;;;;:24;:41;;;;;15041:15;15016:53;:84;;:53;;:41;:84;;;;;:::i;:::-;;;;-1:-1:-1;;14854:257:23;-1:-1:-1;;;;;;15137:12:23;;;;15121:13;;;;:28;14633:523::o;12702:1056::-;12787:10;12739:7;12779:19;;;:7;:19;;;;;12816:15;;-1:-1:-1;;;;;12816:15:23;12808:79;;;;-1:-1:-1;;;12808:79:23;;18775:2:32;12808:79:23;;;18757:21:32;18814:2;18794:18;;;18787:30;18853:34;18833:18;;;18826:62;-1:-1:-1;;;18904:18:32;;;18897:35;18949:19;;12808:79:23;18573:401:32;12808:79:23;12917:11;;12902;;-1:-1:-1;;;;;12917:11:23;;;12902;;:26;12898:528;;12944:11;:24;;-1:-1:-1;;;;;;12944:24:23;;;;;;12982:11;:24;;;;;;;12898:528;;;13027:11;;13042:10;-1:-1:-1;;;;;13027:11:23;;;:25;13023:403;;13076:10;;;;;-1:-1:-1;;;;;13076:10:23;;;13103:1;13068:19;;;:7;:19;;;;;:24;;:37;;-1:-1:-1;;;;;;13068:37:23;;;;;;13133:10;;13119:11;:24;;;;;13133:10;;13119:24;;;13023:403;;;13164:11;;13179:10;-1:-1:-1;;;;;13164:11:23;;;:25;13160:266;;13213:11;;-1:-1:-1;;;;;13213:11:23;;;13241:1;13205:20;;;:7;:20;;;;;;:25;;:38;;-1:-1:-1;;;;;;13205:38:23;;;;;;13279:11;;;;;;13271:20;;;;;;:25;;;13257:39;;13271:25;;13257:39;;;;;;13160:266;;;13354:10;;;;;;13335;;;;;;-1:-1:-1;;;;;13335:10:23;;;13354;13327:19;;;:7;:19;;;;;;:24;;;:37;;13354:10;;;-1:-1:-1;;;;;;13327:37:23;;;;;;13405:10;;13386;;;;13378:19;;;;;:24;:37;;13405:10;;;;13378:37;;;;;13160:266;13460:15;;-1:-1:-1;;;;;13460:15:23;13436:21;13524:41;;;:24;:41;;;;;13460:15;13605:13;;;;13576:25;;;:42;;13524:41;;13605:13;;13576:25;;13436:21;13576:42;;13605:13;;13576:42;:::i;:::-;;;;-1:-1:-1;;13628:30:23;;13657:1;;13628:13;;:25;;:30;;13657:1;;13628:30;:::i;:::-;;;;-1:-1:-1;;13684:10:23;13676:19;;;;:7;:19;;;;;13669:26;;-1:-1:-1;;;;;;13669:26:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13706:12;:14;;;;;;:::i;:::-;;;;-1:-1:-1;13738:13:23;;12702:1056;-1:-1:-1;;;;12702:1056:23:o;9943:162::-;10054:2;10034:22;;10026:72;;;;-1:-1:-1;;;10026:72:23;;19322:2:32;10026:72:23;;;19304:21:32;19361:2;19341:18;;;19334:30;19400:34;19380:18;;;19373:62;-1:-1:-1;;;19451:18:32;;;19444:35;19496:19;;10026:72:23;19120:401:32;10026:72:23;9943:162;;:::o;2701:523::-;2751:21;:28;2739:9;2806:5;2810:1;2751:28;2806:5;:::i;:::-;2794:17;;2789:429;2813:5;;2789:429;;2901:9;2991:5;:1;2995;2991:5;:::i;:::-;2931:54;;;2948:15;2931:54;;;19711:19:32;2965:16:23;19746:12:32;;;19739:28;;;;19783:12;;;19776:28;;;19820:12;;2931:54:23;;;;;;;;;;;;2921:65;;;;;;2913:74;;:84;;;;:::i;:::-;2901:96;;3058:12;3073:21;3095:1;3073:24;;;;;;;;:::i;:::-;;;;;;;;;;;3138:21;:24;;-1:-1:-1;;;;;3073:24:23;;;;-1:-1:-1;3138:21:23;3160:1;;3138:24;;;;;;:::i;:::-;;;;;;;;;;;3111:21;:24;;-1:-1:-1;;;;;3138:24:23;;;;3133:1;;3111:24;;;;;;:::i;:::-;;;;;;;;;:51;;;;;-1:-1:-1;;;;;3111:51:23;;;;;-1:-1:-1;;;;;3111:51:23;;;;;;3203:4;3176:21;3198:1;3176:24;;;;;;;;:::i;:::-;;;;;;;;;:31;;;;;-1:-1:-1;;;;;3176:31:23;;;;;-1:-1:-1;;;;;3176:31:23;;;;;;2825:393;;2820:3;;;;;:::i;:::-;;;;2789:429;;3230:295;3295:18;;-1:-1:-1;;;;;3295:18:23;3324:162;-1:-1:-1;;;;;3331:18:23;;;3324:162;;-1:-1:-1;;;;;3408:23:23;;;3365:15;3408:23;;;:14;:23;;;;;;;-1:-1:-1;;;;;;3445:30:23;;;;;3408:23;3324:162;;;-1:-1:-1;3517:1:23;3495:19;:23;3230:295::o;15495:337::-;15574:7;15608:3;15601;:10;;15593:70;;;;-1:-1:-1;;;15593:70:23;;20259:2:32;15593:70:23;;;20241:21:32;20298:2;20278:18;;;20271:30;20337:34;20317:18;;;20310:62;-1:-1:-1;;;20388:18:32;;;20381:45;20443:19;;15593:70:23;20057:411:32;15593:70:23;15685:3;15677:5;:11;15673:153;;;-1:-1:-1;15711:3:23;15704:10;;15673:153;15743:3;15735:5;:11;15731:95;;;-1:-1:-1;15769:3:23;15762:10;;15731:95;-1:-1:-1;15810:5:23;15731:95;15495:337;;;;;:::o;5238:1390::-;-1:-1:-1;;;;;5338:30:23;;5310:25;5338:30;;;:24;:30;;;;;;;;5310:58;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5338:30;5310:58;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5310:58:23;;;;-1:-1:-1;;5424:89:23;;;;;;;;5441:18;;-1:-1:-1;;;;;5441:18:23;5424:89;;;-1:-1:-1;5467:44:23;;;:24;5424:89;5467:44;;;;;;;5424:89;;;;;;;;;;;5441:18;5424:89;;;;;;;;;;;;;;;;;;;;;;;;;;;5310:58;;-1:-1:-1;5396:184:23;;5424:89;;-1:-1:-1;5424:89:23;;;;;;5467:44;;5424:89;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5531:35;;;;;;;;5548:4;-1:-1:-1;;;;;5531:35:23;;;;;5560:4;5531:35;;;5396:10;:184::i;:::-;5379:999;;;5605:16;5616:4;5605:10;:16::i;:::-;5379:999;;;5685:18;;-1:-1:-1;;;;;5685:18:23;;;5652:15;5670:34;;;:14;:34;;;;;;;;;;5770:598;-1:-1:-1;;;;;5805:21:23;;5801:122;;5850:27;5862:8;5872:4;5850:11;:27::i;:::-;5899:5;;5801:122;6002:67;;;;;;;;-1:-1:-1;;;;;6002:67:23;;;;;-1:-1:-1;6034:33:23;;;:24;6002:67;6034:33;;;;;;;6002:67;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5966:186;;6002:67;;;;6034:33;6002:67;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6095:35;;;;;;;;6112:4;-1:-1:-1;;;;;6095:35:23;;;;;6124:4;6095:35;;;5966:10;:186::i;:::-;5941:325;;;6193:27;6205:8;6215:4;6193:11;:27::i;5941:325::-;-1:-1:-1;;;;;;6330:23:23;;;;;;;:14;:23;;;;;;;;5770:598;;;5638:740;;5379:999;6414:3;6392:25;;:19;;:25;6388:234;;;6463:18;;;-1:-1:-1;;;;;6463:18:23;;;6433:12;6448:34;;;:14;:34;;;;;;;-1:-1:-1;;;;;;6496:41:23;;;;;;6551:25;;6448:34;;6551:25;;;;;;;6590:19;:21;;6448:34;;6590:21;;;:::i;:::-;;;;;;6419:203;5300:1328;5238:1390;;:::o;15162:327::-;15263:4;15314:10;:15;;;:27;;;15283:10;:15;;;:27;;;:58;15279:129;;-1:-1:-1;15382:15:23;;15364;;-1:-1:-1;;;;;15364:33:23;;;;;;15357:40;;15279:129;-1:-1:-1;15455:15:23;;;;;:27;;;15425:15;;;;:27;;:57;;15162:327::o;6634:167::-;6710:18;;;-1:-1:-1;;;;;6687:20:23;;;6710:18;6687:20;;;:14;:20;;;;;:41;;6710:18;;;;-1:-1:-1;;;;;;6687:41:23;;;;;;;6738:25;;;;;;;;;6773:19;:21;;;;;;:::i;:::-;;;;;;6634:167;:::o;6807:186::-;-1:-1:-1;;;;;6898:20:23;;;;;;;:14;:20;;;;;;;;6875;;;;;;;;;:43;;6898:20;;;;-1:-1:-1;;;;;;6875:43:23;;;;;;;6928:20;;;:27;;;;;;;;;;6965:19;:21;;;;;;:::i;:::-;;;;;;6807:186;;:::o;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;196:173:32;264:20;;-1:-1:-1;;;;;313:31:32;;303:42;;293:70;;359:1;356;349:12;293:70;196:173;;;:::o;374:186::-;433:6;486:2;474:9;465:7;461:23;457:32;454:52;;;502:1;499;492:12;454:52;525:29;544:9;525:29;:::i;565:871::-;682:1;678;673:3;669:11;665:19;657:5;651:12;647:38;642:3;635:51;617:3;732:4;725:5;721:16;715:23;770:4;763;758:3;754:14;747:28;813:12;807:19;800:4;795:3;791:14;784:43;881:4;867:12;863:23;857:30;852:2;847:3;843:12;836:52;958:4;944:12;940:23;934:30;927:38;920:46;913:4;908:3;904:14;897:70;1022:2;1008:12;1004:21;998:28;976:50;;1057:4;1051:3;1046;1042:13;1035:27;1091:14;1085:21;1137:6;1131:3;1126;1122:13;1115:29;1162:1;1172:147;1186:6;1183:1;1180:13;1172:147;;;1302:4;1278:22;;;1274:33;;1268:40;1262:3;1249:11;;;1245:21;1238:71;1201:12;1172:147;;;1176:3;1363:1;1357:3;1348:6;1343:3;1339:16;1335:26;1328:37;1426:3;1419:2;1415:7;1410:2;1402:6;1398:15;1394:29;1389:3;1385:39;1381:49;1374:56;;;;565:871;;;;:::o;1441:266::-;1626:2;1615:9;1608:21;1589:4;1646:55;1697:2;1686:9;1682:18;1674:6;1646:55;:::i;1712:610::-;1798:6;1806;1859:2;1847:9;1838:7;1834:23;1830:32;1827:52;;;1875:1;1872;1865:12;1827:52;1915:9;1902:23;-1:-1:-1;;;;;1940:6:32;1937:30;1934:50;;;1980:1;1977;1970:12;1934:50;2003:22;;2056:4;2048:13;;2044:27;-1:-1:-1;2034:55:32;;2085:1;2082;2075:12;2034:55;2125:2;2112:16;-1:-1:-1;;;;;2143:6:32;2140:30;2137:50;;;2183:1;2180;2173:12;2137:50;2236:7;2231:2;2221:6;2218:1;2214:14;2210:2;2206:23;2202:32;2199:45;2196:65;;;2257:1;2254;2247:12;2196:65;2288:2;2280:11;;;;;2310:6;;-1:-1:-1;1712:610:32;-1:-1:-1;;;1712:610:32:o;2327:346::-;2395:6;2403;2456:2;2444:9;2435:7;2431:23;2427:32;2424:52;;;2472:1;2469;2462:12;2424:52;-1:-1:-1;;2517:23:32;;;2637:2;2622:18;;;2609:32;;-1:-1:-1;2327:346:32:o;2678:1554::-;2868:4;2916:2;2905:9;2901:18;2946:2;2935:9;2928:21;2969:6;3004;2998:13;3035:6;3027;3020:22;3073:2;3062:9;3058:18;3051:25;;3135:2;3125:6;3122:1;3118:14;3107:9;3103:30;3099:39;3085:53;;3173:2;3165:6;3161:15;3194:1;3204:999;3218:6;3215:1;3212:13;3204:999;;;3283:22;;;-1:-1:-1;;3279:36:32;3267:49;;3339:13;;3422:9;;3407:25;;3479:2;3471:11;;;3465:18;3391:2;3503:15;;;3496:27;;;3584:19;;3379:15;;;3616:24;;;3706:21;;;-1:-1:-1;;3674:2:32;3662:15;;;3765:330;3781:8;3776:3;3773:17;3765:330;;;3854:15;;3904:9;;-1:-1:-1;;;;;3900:35:32;3886:50;;3990:2;3982:11;;;3976:18;3960:14;;;3953:42;4064:17;;;;3932:1;3800:11;;;;;4032:2;4021:14;;;;3765:330;;;-1:-1:-1;4118:5:32;-1:-1:-1;;;4158:2:32;4181:12;;;;4146:15;;;;;3240:1;3233:9;3204:999;;;-1:-1:-1;4220:6:32;;2678:1554;-1:-1:-1;;;;;;2678:1554:32:o;4237:586::-;4307:6;4315;4368:2;4356:9;4347:7;4343:23;4339:32;4336:52;;;4384:1;4381;4374:12;4336:52;4424:9;4411:23;-1:-1:-1;;;;;4449:6:32;4446:30;4443:50;;;4489:1;4486;4479:12;4443:50;4512:22;;4565:4;4557:13;;4553:27;-1:-1:-1;4543:55:32;;4594:1;4591;4584:12;4543:55;4634:2;4621:16;-1:-1:-1;;;;;4652:6:32;4649:30;4646:50;;;4692:1;4689;4682:12;4646:50;4737:7;4732:2;4723:6;4719:2;4715:15;4711:24;4708:37;4705:57;;;4758:1;4755;4748:12;4828:389;4917:6;4970:2;4958:9;4949:7;4945:23;4941:32;4938:52;;;4986:1;4983;4976:12;4938:52;5026:9;5013:23;-1:-1:-1;;;;;5051:6:32;5048:30;5045:50;;;5091:1;5088;5081:12;5045:50;5114:22;;5170:2;5152:16;;;5148:25;5145:45;;;5186:1;5183;5176:12;5222:828;5420:4;5468:2;5457:9;5453:18;5498:2;5487:9;5480:21;5521:6;5556;5550:13;5587:6;5579;5572:22;5625:2;5614:9;5610:18;5603:25;;5687:2;5677:6;5674:1;5670:14;5659:9;5655:30;5651:39;5637:53;;5725:2;5717:6;5713:15;5746:1;5756:265;5770:6;5767:1;5764:13;5756:265;;;5863:2;5859:7;5847:9;5839:6;5835:22;5831:36;5826:3;5819:49;5891:50;5934:6;5925;5919:13;5891:50;:::i;:::-;5881:60;-1:-1:-1;5976:2:32;5999:12;;;;5964:15;;;;;5792:1;5785:9;5756:265;;6055:269;6112:6;6165:2;6153:9;6144:7;6140:23;6136:32;6133:52;;;6181:1;6178;6171:12;6133:52;6220:9;6207:23;6270:4;6263:5;6259:16;6252:5;6249:27;6239:55;;6290:1;6287;6280:12;6521:300;6589:6;6597;6650:2;6638:9;6629:7;6625:23;6621:32;6618:52;;;6666:1;6663;6656:12;6618:52;6689:29;6708:9;6689:29;:::i;:::-;6679:39;6787:2;6772:18;;;;6759:32;;-1:-1:-1;;;6521:300:32:o;6826:806::-;7074:2;7086:21;;;7156:13;;7059:18;;;7178:22;;;7026:4;;7257:15;;;7231:2;7216:18;;;7026:4;7300:306;7314:6;7311:1;7308:13;7300:306;;;7373:13;;7415:9;;-1:-1:-1;;;;;7411:35:32;;;7399:48;;7499:2;7491:11;;;7485:18;7481:44;7467:12;;;7460:66;7581:15;;;;7555:2;7546:12;;;;7443:1;7329:9;7300:306;;;-1:-1:-1;7623:3:32;;6826:806;-1:-1:-1;;;;;6826:806:32:o;7994:380::-;8073:1;8069:12;;;;8116;;;8137:61;;8191:4;8183:6;8179:17;8169:27;;8137:61;8244:2;8236:6;8233:14;8213:18;8210:38;8207:161;;8290:10;8285:3;8281:20;8278:1;8271:31;8325:4;8322:1;8315:15;8353:4;8350:1;8343:15;8379:356;8581:2;8563:21;;;8600:18;;;8593:30;8659:34;8654:2;8639:18;;8632:62;8726:2;8711:18;;8379:356::o;8740:127::-;8801:10;8796:3;8792:20;8789:1;8782:31;8832:4;8829:1;8822:15;8856:4;8853:1;8846:15;9177:127;9238:10;9233:3;9229:20;9226:1;9219:31;9269:4;9266:1;9259:15;9293:4;9290:1;9283:15;9309:125;9374:9;;;9395:10;;;9392:36;;;9408:18;;:::i;9439:128::-;9506:9;;;9527:11;;;9524:37;;;9541:18;;:::i;9572:127::-;9633:10;9628:3;9624:20;9621:1;9614:31;9664:4;9661:1;9654:15;9688:4;9685:1;9678:15;10421:271;10604:6;10596;10591:3;10578:33;10560:3;10630:16;;10655:13;;;10630:16;10421:271;-1:-1:-1;10421:271:32:o;11101:135::-;11140:3;11161:17;;;11158:43;;11181:18;;:::i;:::-;-1:-1:-1;11228:1:32;11217:13;;11101:135::o;11366:517::-;11467:2;11462:3;11459:11;11456:421;;;11503:5;11500:1;11493:16;11547:4;11544:1;11534:18;11617:2;11605:10;11601:19;11598:1;11594:27;11588:4;11584:38;11653:4;11641:10;11638:20;11635:47;;;-1:-1:-1;11676:4:32;11635:47;11731:2;11726:3;11722:12;11719:1;11715:20;11709:4;11705:31;11695:41;;11786:81;11804:2;11797:5;11794:13;11786:81;;;11863:1;11849:16;;11830:1;11819:13;11786:81;;12059:1295;12183:3;12177:10;-1:-1:-1;;;;;12202:6:32;12199:30;12196:56;;;12232:18;;:::i;:::-;12261:96;12350:6;12310:38;12342:4;12336:11;12310:38;:::i;:::-;12304:4;12261:96;:::i;:::-;12406:4;12437:2;12426:14;;12454:1;12449:648;;;;13141:1;13158:6;13155:89;;;-1:-1:-1;13210:19:32;;;13204:26;13155:89;-1:-1:-1;;12016:1:32;12012:11;;;12008:24;12004:29;11994:40;12040:1;12036:11;;;11991:57;13257:81;;12419:929;;12449:648;11313:1;11306:14;;;11350:4;11337:18;;-1:-1:-1;;12485:20:32;;;12602:222;12616:7;12613:1;12610:14;12602:222;;;12698:19;;;12692:26;12677:42;;12805:4;12790:20;;;;12758:1;12746:14;;;;12632:12;12602:222;;;12606:3;12852:6;12843:7;12840:19;12837:201;;;12913:19;;;12907:26;-1:-1:-1;;12996:1:32;12992:14;;;13008:3;12988:24;12984:37;12980:42;12965:58;12950:74;;12837:201;-1:-1:-1;;;;13084:1:32;13068:14;;;13064:22;13051:36;;-1:-1:-1;12059:1295:32:o;13359:485::-;-1:-1:-1;;;;;13544:32:32;;13526:51;;13613:2;13608;13593:18;;13586:30;;;13632:18;;13625:34;;;13652:6;13701;13696:2;13681:18;;13668:48;13765:1;13736:22;;;13760:2;13732:31;;;13725:42;;;;13828:2;13807:15;;;-1:-1:-1;;13803:29:32;13788:45;13784:54;;13359:485;-1:-1:-1;;13359:485:32:o;13849:332::-;13949:4;14007:11;13994:25;14101:3;14097:8;14086;14070:14;14066:29;14062:44;14042:18;14038:69;14028:97;;14121:1;14118;14111:12;14028:97;14142:33;;;;;13849:332;-1:-1:-1;;13849:332:32:o;14186:1178::-;-1:-1:-1;;;;;14287:3:32;14284:27;14281:53;;;14314:18;;:::i;:::-;14343:93;14432:3;14392:38;14424:4;14418:11;14392:38;:::i;:::-;14386:4;14343:93;:::i;:::-;14462:1;14487:2;14482:3;14479:11;14504:1;14499:607;;;;15150:1;15167:3;15164:93;;;-1:-1:-1;15223:19:32;;;15210:33;15164:93;-1:-1:-1;;12016:1:32;12012:11;;;12008:24;12004:29;11994:40;12040:1;12036:11;;;11991:57;15270:78;;14472:886;;14499:607;11313:1;11306:14;;;11350:4;11337:18;;-1:-1:-1;;14535:17:32;;;14649:229;14663:7;14660:1;14657:14;14649:229;;;14752:19;;;14739:33;14724:49;;14859:4;14844:20;;;;14812:1;14800:14;;;;14679:12;14649:229;;;14653:3;14906;14897:7;14894:16;14891:159;;;15030:1;15026:6;15020:3;15014;15011:1;15007:11;15003:21;14999:34;14995:39;14982:9;14977:3;14973:19;14960:33;14956:79;14948:6;14941:95;14891:159;;;15093:1;15087:3;15084:1;15080:11;15076:19;15070:4;15063:33;14472:886;;14186:1178;;;:::o;15369:1163::-;15556:19;;15584:21;;15674:2;15663:14;;15650:28;15704:1;15694:12;;15687:29;15753:1;15743:12;;15803:2;15792:14;;15779:28;15833:15;;15826:23;15868:15;;;15858:43;;15897:1;15894;15887:12;15858:43;15952:3;15948:8;15935:10;15929:17;15925:32;15910:47;;16005:3;16001:2;15997:12;15988:7;15985:25;15973:10;15966:45;;;;16070:2;16063:5;16059:14;16046:28;16153:2;16149:7;16141:5;16125:14;16121:26;16117:40;16097:18;16093:65;16083:93;;16172:1;16169;16162:12;16083:93;16197:30;;16250:18;;-1:-1:-1;;;;;16280:30:32;;16277:50;;;16323:1;16320;16313:12;16277:50;16360:2;16354:4;16350:13;16336:27;;16407:6;16391:14;16387:27;16379:6;16375:40;16372:60;;;16428:1;16425;16418:12;16372:60;16441:85;16519:6;16511;16507:1;16501:4;16497:12;16441:85;:::i;:::-;;;15369:1163;;:::o;18979:136::-;19018:3;19046:5;19036:39;;19055:18;;:::i;:::-;-1:-1:-1;;;19091:18:32;;18979:136::o;19843:209::-;19875:1;19901;19891:132;;19945:10;19940:3;19936:20;19933:1;19926:31;19980:4;19977:1;19970:15;20008:4;20005:1;19998:15;19891:132;-1:-1:-1;20037:9:32;;19843:209::o", + "linkReferences": {}, + "immutableReferences": { + "39841": [ + { "start": 996, "length": 32 }, + { "start": 1219, "length": 32 }, + { "start": 1683, "length": 32 }, + { "start": 2569, "length": 32 }, + { "start": 3873, "length": 32 }, + { "start": 5353, "length": 32 } + ] + } + }, + "methodIdentifiers": { + "activeValidatorsCount()": "0d2bd909", + "calculateTopValidators(uint8)": "b5cfa68c", + "getAllValidators()": "f3513a37", + "getRounds(uint256,uint256)": "40f74f47", + "getRoundsCount()": "a09686c4", + "getTopValidators()": "afeea115", + "getValidator(address)": "1904bb2e", + "getVotes(address,uint256)": "eb9019d4", + "getVotesCount()": "1b605b86", + "isValidatorRegistered(address)": "d04a68c7", + "registerValidator(bytes)": "602a9eee", + "registeredValidatorsCount()": "f1bd0b37", + "resignValidator()": "b85f5da2", + "resignedValidatorsCount()": "0777cbef", + "unvote()": "3174b689", + "updateValidator((address,(uint256,uint256,bool,bytes)))": "62525879", + "updateVoters(address[])": "2bdf6d43", + "vote(address)": "6dd7d8ea" + }, + "rawMetadata": "{\"compiler\":{\"version\":\"0.8.27+commit.40a35a09\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"Unvoted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"bls12_381_public_key\",\"type\":\"bytes\"}],\"name\":\"ValidatorRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"ValidatorResigned\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"Voted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"activeValidatorsCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"n\",\"type\":\"uint8\"}],\"name\":\"calculateTopValidators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllValidators\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"votersCount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"voteBalance\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isResigned\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"bls12_381_public_key\",\"type\":\"bytes\"}],\"internalType\":\"struct ValidatorData\",\"name\":\"data\",\"type\":\"tuple\"}],\"internalType\":\"struct Validator[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"offset\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"getRounds\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"round\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"voteBalance\",\"type\":\"uint256\"}],\"internalType\":\"struct RoundValidator[]\",\"name\":\"validators\",\"type\":\"tuple[]\"}],\"internalType\":\"struct Round[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRoundsCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getTopValidators\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"votersCount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"voteBalance\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isResigned\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"bls12_381_public_key\",\"type\":\"bytes\"}],\"internalType\":\"struct ValidatorData\",\"name\":\"data\",\"type\":\"tuple\"}],\"internalType\":\"struct Validator[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"getValidator\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"votersCount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"voteBalance\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isResigned\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"bls12_381_public_key\",\"type\":\"bytes\"}],\"internalType\":\"struct ValidatorData\",\"name\":\"data\",\"type\":\"tuple\"}],\"internalType\":\"struct Validator\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"getVotes\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"internalType\":\"struct VoteResult[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getVotesCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"isValidatorRegistered\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"bls12_381_public_key\",\"type\":\"bytes\"}],\"name\":\"registerValidator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"registeredValidatorsCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"resignValidator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"resignedValidatorsCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unvote\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"votersCount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"voteBalance\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isResigned\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"bls12_381_public_key\",\"type\":\"bytes\"}],\"internalType\":\"struct ValidatorData\",\"name\":\"data\",\"type\":\"tuple\"}],\"internalType\":\"struct Validator\",\"name\":\"_validator\",\"type\":\"tuple\"}],\"name\":\"updateValidator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"voters\",\"type\":\"address[]\"}],\"name\":\"updateVoters\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"vote\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/consensus/Consensus.sol\":\"Consensus\"},\"evmVersion\":\"shanghai\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@contracts/=src/\",\":@forge-std/=forge-std/src/\"]},\"sources\":{\"src/consensus/Consensus.sol\":{\"keccak256\":\"0x5b736870bfb3308f6f109276d30415ab555c65a1c1165afbe50c56220ed9d129\",\"urls\":[\"bzz-raw://0d9655228a0caa1a1802d479cc6bd490d8d0f36061f285e855d23e32e55f311c\",\"dweb:/ipfs/QmUfv3d2My7CuFjq7PM6jmxLoXWLrErfGkCT8s1tNKvcCZ\"]}},\"version\":1}", + "metadata": { + "compiler": { "version": "0.8.27+commit.40a35a09" }, + "language": "Solidity", + "output": { + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "voter", + "type": "address", + "indexed": false + }, + { + "internalType": "address", + "name": "validator", + "type": "address", + "indexed": false + } + ], + "type": "event", + "name": "Unvoted", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address", + "indexed": false + }, + { + "internalType": "bytes", + "name": "bls12_381_public_key", + "type": "bytes", + "indexed": false + } + ], + "type": "event", + "name": "ValidatorRegistered", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address", + "indexed": false + } + ], + "type": "event", + "name": "ValidatorResigned", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "voter", + "type": "address", + "indexed": false + }, + { + "internalType": "address", + "name": "validator", + "type": "address", + "indexed": false + } + ], + "type": "event", + "name": "Voted", + "anonymous": false + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "activeValidatorsCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ] + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "n", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "calculateTopValidators" + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "getAllValidators", + "outputs": [ + { + "internalType": "struct Validator[]", + "name": "", + "type": "tuple[]", + "components": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + }, + { + "internalType": "struct ValidatorData", + "name": "data", + "type": "tuple", + "components": [ + { + "internalType": "uint256", + "name": "votersCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "voteBalance", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isResigned", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "bls12_381_public_key", + "type": "bytes" + } + ] + } + ] + } + ] + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "offset", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function", + "name": "getRounds", + "outputs": [ + { + "internalType": "struct Round[]", + "name": "", + "type": "tuple[]", + "components": [ + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + }, + { + "internalType": "struct RoundValidator[]", + "name": "validators", + "type": "tuple[]", + "components": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "voteBalance", + "type": "uint256" + } + ] + } + ] + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "getRoundsCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "getTopValidators", + "outputs": [ + { + "internalType": "struct Validator[]", + "name": "", + "type": "tuple[]", + "components": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + }, + { + "internalType": "struct ValidatorData", + "name": "data", + "type": "tuple", + "components": [ + { + "internalType": "uint256", + "name": "votersCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "voteBalance", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isResigned", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "bls12_381_public_key", + "type": "bytes" + } + ] + } + ] + } + ] + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function", + "name": "getValidator", + "outputs": [ + { + "internalType": "struct Validator", + "name": "", + "type": "tuple", + "components": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + }, + { + "internalType": "struct ValidatorData", + "name": "data", + "type": "tuple", + "components": [ + { + "internalType": "uint256", + "name": "votersCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "voteBalance", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isResigned", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "bls12_381_public_key", + "type": "bytes" + } + ] + } + ] + } + ] + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function", + "name": "getVotes", + "outputs": [ + { + "internalType": "struct VoteResult[]", + "name": "", + "type": "tuple[]", + "components": [ + { + "internalType": "address", + "name": "voter", + "type": "address" + }, + { + "internalType": "address", + "name": "validator", + "type": "address" + } + ] + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "getVotesCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ] + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function", + "name": "isValidatorRegistered", + "outputs": [ + { "internalType": "bool", "name": "", "type": "bool" } + ] + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "bls12_381_public_key", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "registerValidator" + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "registeredValidatorsCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ] + }, + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "function", + "name": "resignValidator" + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "resignedValidatorsCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ] + }, + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "function", + "name": "unvote" + }, + { + "inputs": [ + { + "internalType": "struct Validator", + "name": "_validator", + "type": "tuple", + "components": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + }, + { + "internalType": "struct ValidatorData", + "name": "data", + "type": "tuple", + "components": [ + { + "internalType": "uint256", + "name": "votersCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "voteBalance", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isResigned", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "bls12_381_public_key", + "type": "bytes" + } + ] + } + ] + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "updateValidator" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "voters", + "type": "address[]" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "updateVoters" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "vote" + } + ], + "devdoc": { "kind": "dev", "methods": {}, "version": 1 }, + "userdoc": { "kind": "user", "methods": {}, "version": 1 } + }, + "settings": { + "remappings": ["@contracts/=src/", "@forge-std/=forge-std/src/"], + "optimizer": { "enabled": true, "runs": 200 }, + "metadata": { "bytecodeHash": "ipfs" }, + "compilationTarget": { "src/consensus/Consensus.sol": "Consensus" }, + "evmVersion": "shanghai", + "libraries": {} + }, + "sources": { + "src/consensus/Consensus.sol": { + "keccak256": "0x5b736870bfb3308f6f109276d30415ab555c65a1c1165afbe50c56220ed9d129", + "urls": [ + "bzz-raw://0d9655228a0caa1a1802d479cc6bd490d8d0f36061f285e855d23e32e55f311c", + "dweb:/ipfs/QmUfv3d2My7CuFjq7PM6jmxLoXWLrErfGkCT8s1tNKvcCZ" + ], + "license": null + } + }, + "version": 1 + }, + "id": 23 +} diff --git a/src/test/java/org/arkecosystem/crypto/AbstractTest.java b/src/test/java/org/arkecosystem/crypto/AbstractTest.java new file mode 100644 index 00000000..fad3eeed --- /dev/null +++ b/src/test/java/org/arkecosystem/crypto/AbstractTest.java @@ -0,0 +1,27 @@ +package org.arkecosystem.crypto; + +import com.google.gson.Gson; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; + +public abstract class AbstractTest { + public String passphrase; + + @BeforeEach + public void setUp() { + this.passphrase = "my super secret passphrase"; + } + + protected Map loadFixture(String name) throws Exception { + + String resourcePath = "/transactions/" + name + ".json"; + InputStream inputStream = getClass().getResourceAsStream(resourcePath); + if (inputStream == null) { + throw new Exception("Fixture not found: " + resourcePath); + } + String json = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); + return new Gson().fromJson(json, Map.class); + } +} diff --git a/src/test/java/org/arkecosystem/crypto/SchnorrTest.java b/src/test/java/org/arkecosystem/crypto/SchnorrTest.java deleted file mode 100644 index 008fe682..00000000 --- a/src/test/java/org/arkecosystem/crypto/SchnorrTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.arkecosystem.crypto; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.arkecosystem.crypto.encoding.Hex; -import org.junit.jupiter.api.Test; - -class SchnorrTest { - @Test - public void convertHexStringToByteArray() { - assertEquals( - Hex.encode( - Schnorr.hexStringToByteArray("b693449AdDa7EFc015D87944EAE8b7C37EB1690A")), - "b693449adda7efc015d87944eae8b7c37eb1690a"); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/signature/FixtureSignVerificationTest.java b/src/test/java/org/arkecosystem/crypto/signature/FixtureSignVerificationTest.java deleted file mode 100644 index c338e6ec..00000000 --- a/src/test/java/org/arkecosystem/crypto/signature/FixtureSignVerificationTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.arkecosystem.crypto.signature; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.google.gson.internal.LinkedTreeMap; -import org.arkecosystem.crypto.identities.PublicKey; -import org.arkecosystem.crypto.transactions.Deserializer; -import org.arkecosystem.crypto.transactions.FixtureLoader; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -public class FixtureSignVerificationTest { - - private final String secondPassphrase = "this is a top secret second passphrase"; - - @ParameterizedTest - @ValueSource( - strings = { - "transactions/transfer/transfer-sign", - "transactions/vote/vote-sign", - "transactions/vote/unvote-sign", - "transactions/validator_registration/validator-registration-sign", - "transactions/validator_resignation/validator-resignation-sign", - "transactions/multi_payment/multi-payment-sign", - "transactions/multi_payment/multi-payment-with-vendor-field-sign", - "transactions/username_resignation/username-resignation-sign", - "transactions/username_registration/username-registration-sign", - "transactions/multi_signature_registration/multi-signature-registration-sign", - }) - void checkSchnorrSignature(String file) { - LinkedTreeMap fixture = FixtureLoader.load(file); - - Transaction actual = new Deserializer(fixture.get("serialized").toString()).deserialize(); - - if (actual.signature != null) assertTrue(actual.verify()); - - if (actual.secondSignature != null) { - checkSecondSignature(actual); - } - } - - private void checkSecondSignature(Transaction actual) { - String secondPublicKey = PublicKey.fromPassphrase(secondPassphrase); - assertTrue(actual.secondVerify(secondPublicKey)); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/signature/SchnorrSignerTest.java b/src/test/java/org/arkecosystem/crypto/signature/SchnorrSignerTest.java deleted file mode 100644 index 858c5c5f..00000000 --- a/src/test/java/org/arkecosystem/crypto/signature/SchnorrSignerTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.arkecosystem.crypto.signature; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.arkecosystem.crypto.Schnorr; -import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.identities.PrivateKey; -import org.bitcoinj.core.ECKey; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -class SchnorrSignerTest { - - private Signer signer; - - @BeforeEach - void setUp() { - signer = new SchnorrSigner(); - } - - @Test - void sign() { - String message = "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"; - ECKey privateKey = PrivateKey.fromPassphrase("some passphrase"); - String otherValidSignature = - "d39f6c989c185699c2f3a8674dcdd86944f9e11debc57e179bf51de3f3604546c565a37302417ab0914aa45b46f66154fbd845f883942a4cdd8654d1aaecb5c3"; - - String result = Hex.encode(signer.sign(Schnorr.hexStringToByteArray(message), privateKey)); - - // Since the signature is non-deterministic, we can only check the length - assertEquals(otherValidSignature.length(), result.length()); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/signature/SchnorrVerifierTest.java b/src/test/java/org/arkecosystem/crypto/signature/SchnorrVerifierTest.java deleted file mode 100644 index 7f09c61c..00000000 --- a/src/test/java/org/arkecosystem/crypto/signature/SchnorrVerifierTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.arkecosystem.crypto.signature; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.arkecosystem.crypto.Schnorr; -import org.arkecosystem.crypto.identities.PrivateKey; -import org.bitcoinj.core.ECKey; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -class SchnorrVerifierTest { - - private Verifier verifier; - - @BeforeEach - void setUp() { - verifier = new SchnorrVerifier(); - } - - @Test - void verify() { - byte[] message = - Schnorr.hexStringToByteArray( - "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"); - ECKey privateKey = PrivateKey.fromPassphrase("some passphrase"); - String signature = - "d39f6c989c185699c2f3a8674dcdd86944f9e11debc57e179bf51de3f3604546c565a37302417ab0914aa45b46f66154fbd845f883942a4cdd8654d1aaecb5c3"; - - assertTrue(verifier.verify(message, privateKey, Schnorr.hexStringToByteArray(signature))); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/DeserializerTest.java b/src/test/java/org/arkecosystem/crypto/transactions/DeserializerTest.java new file mode 100644 index 00000000..d34d8581 --- /dev/null +++ b/src/test/java/org/arkecosystem/crypto/transactions/DeserializerTest.java @@ -0,0 +1,136 @@ +package org.arkecosystem.crypto.transactions; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Map; +import org.arkecosystem.crypto.AbstractTest; +import org.arkecosystem.crypto.encoding.Hex; +import org.arkecosystem.crypto.transactions.types.*; +import org.junit.jupiter.api.Test; + +public class DeserializerTest extends AbstractTest { + + @Test + public void it_should_deserialize_a_transfer_signed_with_a_passphrase() throws Exception { + Map fixture = loadFixture("transfer"); + + AbstractTransaction transaction = assertTransaction(fixture); + + assertEquals("10000000000000000000", transaction.value); + + assertTrue(transaction instanceof Transfer); + } + + @Test + public void it_should_deserialize_a_vote_signed_with_a_passphrase() throws Exception { + Map fixture = loadFixture("vote"); + + AbstractTransaction transaction = assertTransaction(fixture); + + assertEquals("0x512F366D524157BcF734546eB29a6d687B762255", transaction.vote); + + assertEquals( + "749744e0d689c46e37ff2993a984599eac4989a9ef0028337b335c9d43abf936", transaction.id); + + assertTrue(transaction instanceof Vote); + } + + @Test + public void it_should_deserialize_a_unvote_signed_with_a_passphrase() throws Exception { + Map fixture = loadFixture("unvote"); + + AbstractTransaction transaction = assertTransaction(fixture); + + assertTrue(transaction instanceof Unvote); + } + + @Test + public void it_should_deserialize_a_validator_registration_signed_with_a_passphrase() + throws Exception { + Map fixture = loadFixture("validator-registration"); + + AbstractTransaction transaction = assertTransaction(fixture); + + assertTrue(transaction instanceof ValidatorRegistration); + } + + @Test + public void it_should_deserialize_a_validator_resignation_signed_with_a_passphrase() + throws Exception { + Map fixture = loadFixture("validator-resignation"); + + AbstractTransaction transaction = assertTransaction(fixture); + + assertTrue(transaction instanceof ValidatorResignation); + } + + private AbstractTransaction assertTransaction(Map fixture) throws Exception { + AbstractTransaction transaction = + assertDeserialized( + fixture, + new String[] { + "id", "nonce", "gasPrice", "gasLimit", "signature", + }); + + assertTrue(transaction.verify()); + + return transaction; + } + + private AbstractTransaction assertDeserialized(Map fixture, String[] keys) + throws Exception { + String serializedHex = (String) fixture.get("serialized"); + byte[] serializedBytes = Hex.decode(serializedHex); + + Deserializer deserializer = new Deserializer(serializedHex); + AbstractTransaction transaction = deserializer.deserialize(); + + byte[] reserializedBytes = transaction.serialize(false); + String reserializedHex = Hex.encode(reserializedBytes); + + assertEquals(serializedHex, reserializedHex, "Serialized transaction does not match"); + + Map expectedData = (Map) fixture.get("data"); + + for (String key : keys) { + Object expectedValue = expectedData.get(key); + Object actualValue = getFieldValue(transaction, key); + + if (expectedValue != null) { + if (expectedValue instanceof Number) { + assertEquals( + ((Number) expectedValue).intValue(), + ((Number) actualValue).intValue(), + "Field " + key + " does not match"); + } else { + + assertEquals( + expectedValue.toString(), + actualValue.toString(), + "Field " + key + " does not match"); + } + } else { + assertNull(actualValue, "Field " + key + " should be null"); + } + } + + return transaction; + } + + private Object getFieldValue(AbstractTransaction transaction, String field) { + switch (field) { + case "id": + return transaction.id; + case "nonce": + return transaction.nonce; + case "gasPrice": + return transaction.gasPrice; + case "gasLimit": + return transaction.gasLimit; + case "signature": + return transaction.signature; + default: + return null; + } + } +} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/FixtureLoader.java b/src/test/java/org/arkecosystem/crypto/transactions/FixtureLoader.java deleted file mode 100644 index 11607e8d..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/FixtureLoader.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.arkecosystem.crypto.transactions; - -import com.google.gson.Gson; -import com.google.gson.internal.LinkedTreeMap; -import java.io.IOException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import org.arkecosystem.crypto.transactions.types.Transaction; - -public class FixtureLoader { - - private static String readFile(String path) throws IOException { - ClassLoader classLoader = Transaction.class.getClassLoader(); - URL resource = classLoader.getResource(String.format("%s.json", path)); - return new String( - Files.readAllBytes(Paths.get(resource.getPath())), StandardCharsets.UTF_8); - } - - public static LinkedTreeMap load(String path) { - try { - return new Gson() - .fromJson(readFile(path), new LinkedTreeMap().getClass()); - } catch (IOException e) { - e.printStackTrace(); - return null; - } - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/SerializerTest.java b/src/test/java/org/arkecosystem/crypto/transactions/SerializerTest.java new file mode 100644 index 00000000..20c75dc7 --- /dev/null +++ b/src/test/java/org/arkecosystem/crypto/transactions/SerializerTest.java @@ -0,0 +1,82 @@ +package org.arkecosystem.crypto.transactions; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Map; +import org.arkecosystem.crypto.AbstractTest; +import org.arkecosystem.crypto.encoding.Hex; +import org.arkecosystem.crypto.transactions.types.*; +import org.junit.jupiter.api.Test; + +public class SerializerTest extends AbstractTest { + + @Test + public void it_should_serialize_a_transfer_transaction() throws Exception { + Map fixture = loadFixture("transfer"); + + Map data = (Map) fixture.get("data"); + + Transfer transaction = new Transfer(data); + + byte[] serializedBytes = transaction.serialize(false); + String serializedHex = Hex.encode(serializedBytes); + + assertEquals(fixture.get("serialized"), serializedHex); + } + + @Test + public void it_should_serialize_a_vote_transaction() throws Exception { + Map fixture = loadFixture("vote"); + + Map data = (Map) fixture.get("data"); + + Vote transaction = new Vote(data); + + byte[] serializedBytes = transaction.serialize(false); + String serializedHex = Hex.encode(serializedBytes); + + assertEquals(fixture.get("serialized"), serializedHex); + } + + @Test + public void it_should_serialize_a_unvote_transaction() throws Exception { + Map fixture = loadFixture("unvote"); + + Map data = (Map) fixture.get("data"); + + Unvote transaction = new Unvote(data); + + byte[] serializedBytes = transaction.serialize(false); + String serializedHex = Hex.encode(serializedBytes); + + assertEquals(fixture.get("serialized"), serializedHex); + } + + @Test + public void it_should_serialize_a_validator_registration_transaction() throws Exception { + Map fixture = loadFixture("validator-registration"); + + Map data = (Map) fixture.get("data"); + + ValidatorRegistration transaction = new ValidatorRegistration(data); + + byte[] serializedBytes = transaction.serialize(false); + String serializedHex = Hex.encode(serializedBytes); + + assertEquals(fixture.get("serialized"), serializedHex); + } + + @Test + public void it_should_serialize_a_validator_resignation_transaction() throws Exception { + Map fixture = loadFixture("validator-resignation"); + + Map data = (Map) fixture.get("data"); + + ValidatorResignation transaction = new ValidatorResignation(data); + + byte[] serializedBytes = transaction.serialize(false); + String serializedHex = Hex.encode(serializedBytes); + + assertEquals(fixture.get("serialized"), serializedHex); + } +} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/builder/EvmCallBuilderTest.java b/src/test/java/org/arkecosystem/crypto/transactions/builder/EvmCallBuilderTest.java new file mode 100644 index 00000000..5d12c560 --- /dev/null +++ b/src/test/java/org/arkecosystem/crypto/transactions/builder/EvmCallBuilderTest.java @@ -0,0 +1,29 @@ +package org.arkecosystem.crypto.transactions.builder; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Map; +import org.arkecosystem.crypto.AbstractTest; +import org.junit.jupiter.api.Test; + +public class EvmCallBuilderTest extends AbstractTest { + + @Test + public void it_should_sign_it_with_a_passphrase() throws Exception { + Map fixture = loadFixture("evm-sign"); + + Map data = (Map) fixture.get("data"); + + EvmCallBuilder builder = + new EvmCallBuilder() + .gasPrice(((Number) data.get("gasPrice")).intValue()) + .nonce(Long.parseLong(data.get("nonce").toString())) + .network(((Number) data.get("network")).intValue()) + .payload((String) data.get("data")) + .gasLimit(((Number) data.get("gasLimit")).intValue()) + .recipientAddress("0xE536720791A7DaDBeBdBCD8c8546fb0791a11901") + .sign(this.passphrase); + + assertTrue(builder.verify()); + } +} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/builder/MultiPaymentBuilderTest.java b/src/test/java/org/arkecosystem/crypto/transactions/builder/MultiPaymentBuilderTest.java deleted file mode 100644 index ab66bd60..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/builder/MultiPaymentBuilderTest.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.arkecosystem.crypto.transactions.builder; - -import static org.junit.jupiter.api.Assertions.*; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -class MultiPaymentBuilderTest { - @Test - void build() { - Transaction actual = - new MultiPaymentBuilder() - .addPayment("0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A", 1) - .addPayment("0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A", 2) - .addPayment("0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A", 3) - .vendorField("This is a transaction from Java") - .sign("this is a top secret passphrase") - .transaction; - - HashMap actualHashMap = actual.toHashMap(); - HashMap actualAsset = (HashMap) actualHashMap.get("asset"); - ArrayList payments = (ArrayList) actualAsset.get("payments"); - HashMap payment = (HashMap) payments.get(0); - assertEquals(payment.get("amount"), "1"); - assertEquals(payment.get("recipientId"), "0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A"); - - assertTrue(actual.verify()); - } - - @Test - void buildSecondSignature() { - Transaction actual = - new MultiPaymentBuilder() - .addPayment("0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A", 1) - .addPayment("0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A", 2) - .addPayment("0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A", 3) - .vendorField("This is a transaction from Java") - .sign("this is a top secret passphrase") - .secondSign("this is a top secret second passphrase") - .transaction; - - assertTrue(actual.verify()); - assertTrue( - actual.secondVerify( - "03699e966b2525f9088a6941d8d94f7869964a000efe65783d78ac82e1199fe609")); - } - - @Test - void testMaxPayments() { - MultiPaymentBuilder actual = new MultiPaymentBuilder(); - for (int i = 0; i < 64; i++) { - actual.addPayment("0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A", 1); - } - Throwable exception = - assertThrows( - MaximumPaymentCountExceededError.class, - () -> actual.addPayment("0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A", 1)); - assertEquals("Expected a maximum of 64 payments", exception.getMessage()); - } - - @Test - void buildMultiSignature() { - Transaction actual = - new MultiPaymentBuilder() - .addPayment("0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A", 1) - .addPayment("0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A", 2) - .addPayment("0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A", 3) - .vendorField("This is a transaction from Java") - .multiSign("secret 1", 0) - .multiSign("secret 2", 1) - .multiSign("secret 3", 2) - .sign("this is a top secret passphrase") - .transaction; - - assertTrue(actual.verify()); - - HashMap actualHashMap = actual.toHashMap(); - - assertNotNull(actualHashMap.get("signatures")); - - List actualSignatures = (List) actualHashMap.get("signatures"); - - assertEquals(3, actualSignatures.size()); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/builder/MultiSignatureRegistrationBuilderTest.java b/src/test/java/org/arkecosystem/crypto/transactions/builder/MultiSignatureRegistrationBuilderTest.java deleted file mode 100644 index 3751034f..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/builder/MultiSignatureRegistrationBuilderTest.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.arkecosystem.crypto.transactions.builder; - -import static org.junit.jupiter.api.Assertions.*; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import org.arkecosystem.crypto.enums.Fees; -import org.arkecosystem.crypto.identities.PublicKey; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -public class MultiSignatureRegistrationBuilderTest { - - @Test - @SuppressWarnings("unchecked") - void build() { - - String key1 = PublicKey.fromPassphrase("secret 1"); - String key2 = PublicKey.fromPassphrase("secret 2"); - String key3 = PublicKey.fromPassphrase("secret 3"); - - List publicKeys = Arrays.asList(key1, key2, key3); - - Transaction actual = - new MultiSignatureRegistrationBuilder() - .nonce(3) - .fee(Fees.MULTI_SIGNATURE_REGISTRATION.getValue()) - .publicKeys(publicKeys) - .min(3) - .multiSign("secret 1", 0) - .multiSign("secret 2", 1) - .multiSign("secret 3", 2) - .sign("secret 1") - .transaction; - - assertTrue(actual.verify()); - - HashMap actualHashMap = actual.toHashMap(); - - assertNotNull(actualHashMap.get("asset")); - HashMap actualAsset = (HashMap) actualHashMap.get("asset"); - - assertNotNull(actualAsset.get("multiSignature")); - HashMap actualMultisignature = (HashMap) actualAsset.get("multiSignature"); - - byte actualMin = (byte) actualMultisignature.get("min"); - List actualPublicKeys = (List) actualMultisignature.get("publicKeys"); - - assertEquals(publicKeys, actualPublicKeys); - assertEquals(3, actualMin); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/builder/TransferBuilderTest.java b/src/test/java/org/arkecosystem/crypto/transactions/builder/TransferBuilderTest.java index 00e061af..f8c53e7d 100644 --- a/src/test/java/org/arkecosystem/crypto/transactions/builder/TransferBuilderTest.java +++ b/src/test/java/org/arkecosystem/crypto/transactions/builder/TransferBuilderTest.java @@ -1,94 +1,36 @@ package org.arkecosystem.crypto.transactions.builder; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; -import java.util.HashMap; -import java.util.List; -import org.arkecosystem.crypto.enums.Fees; -import org.arkecosystem.crypto.transactions.types.Transaction; +import java.util.Map; +import org.arkecosystem.crypto.AbstractTest; +import org.arkecosystem.crypto.encoding.Hex; import org.junit.jupiter.api.Test; -class TransferBuilderTest { +public class TransferBuilderTest extends AbstractTest { @Test - void build() { - Transaction actual = - new TransferBuilder() - .recipient("0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A") - .amount(133380000000L) - .expiration(100000) - .vendorField("This is a transaction from Java") - .version(2) - .nonce(3) - .network(23) - .fee(Fees.TRANSFER.getValue()) - .sign("this is a top secret passphrase") - .transaction; - - assertTrue(actual.verify()); + public void it_should_sign_it_with_a_passphrase() throws Exception { + Map fixture = loadFixture("transfer"); - HashMap actualHashMap = actual.toHashMap(); - assertEquals( - actualHashMap.get("recipientId"), "0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A"); - assertEquals(actualHashMap.get("amount"), "133380000000"); - assertEquals(actualHashMap.get("expiration"), 100000); - assertEquals(actualHashMap.get("vendorField"), "This is a transaction from Java"); - assertEquals(actualHashMap.get("version"), 2); - assertEquals(actualHashMap.get("nonce"), "3"); - assertEquals(actualHashMap.get("network"), 23); - assertEquals(actualHashMap.get("fee"), Fees.TRANSFER.getValue().toString()); - assertEquals(actualHashMap.get("id"), actual.id); - } + Map data = (Map) fixture.get("data"); - @Test - void buildSecondSignature() { - Transaction actual = + TransferBuilder builder = new TransferBuilder() - .recipient("0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A") - .amount(133380000000L) - .expiration(100000) - .vendorField("This is a transaction from Java") - .version(2) - .nonce(3) - .sign("this is a top secret passphrase") - .secondSign("this is a top secret second passphrase") - .transaction; - - assertTrue(actual.verify()); - assertTrue( - actual.secondVerify( - "03699e966b2525f9088a6941d8d94f7869964a000efe65783d78ac82e1199fe609")); - - HashMap actualHashMap = actual.toHashMap(); - assertEquals(actualHashMap.get("secondSignature"), actual.secondSignature); - } - - @Test - void buildMultiSignature() { - Transaction actual = - new TransferBuilder() - .recipient("0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A") - .amount(133380000000L) - .expiration(100000) - .vendorField("This is a transaction from Java") - .nonce(3) - .fee(Fees.TRANSFER.getValue()) - .multiSign("secret 1", 0) - .multiSign("secret 2", 1) - .multiSign("secret 3", 2) - .sign("secret 1") - .transaction; - - assertTrue(actual.verify()); - - HashMap actualHashMap = actual.toHashMap(); - - assertNotNull(actualHashMap.get("signatures")); - - List actualSignatures = (List) actualHashMap.get("signatures"); - - assertEquals(3, actualSignatures.size()); + .gasPrice(((Number) data.get("gasPrice")).intValue()) + .nonce(Long.parseLong(data.get("nonce").toString())) + .network(((Number) data.get("network")).intValue()) + .gasLimit(((Number) data.get("gasLimit")).intValue()) + .recipientAddress((String) data.get("recipientAddress")) + .value((String) data.get("value")) + .sign(this.passphrase); + + byte[] serializedBytes = builder.transaction.serialize(false); + String serializedHex = Hex.encode(serializedBytes); + + // Compare the serialized transaction + assertEquals(fixture.get("serialized"), serializedHex); + assertEquals(data.get("id"), builder.transaction.getId()); + assertTrue(builder.verify()); } } diff --git a/src/test/java/org/arkecosystem/crypto/transactions/builder/UnvoteBuilderTest.java b/src/test/java/org/arkecosystem/crypto/transactions/builder/UnvoteBuilderTest.java new file mode 100644 index 00000000..dfdf0da1 --- /dev/null +++ b/src/test/java/org/arkecosystem/crypto/transactions/builder/UnvoteBuilderTest.java @@ -0,0 +1,34 @@ +package org.arkecosystem.crypto.transactions.builder; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Map; +import org.arkecosystem.crypto.AbstractTest; +import org.arkecosystem.crypto.encoding.Hex; +import org.junit.jupiter.api.Test; + +public class UnvoteBuilderTest extends AbstractTest { + + @Test + public void it_should_sign_it_with_a_passphrase() throws Exception { + Map fixture = loadFixture("unvote"); + + Map data = (Map) fixture.get("data"); + + UnvoteBuilder builder = + new UnvoteBuilder() + .gasPrice(((Number) data.get("gasPrice")).intValue()) + .nonce(Long.parseLong(data.get("nonce").toString())) + .network(((Number) data.get("network")).intValue()) + .gasLimit(((Number) data.get("gasLimit")).intValue()) + .recipientAddress((String) data.get("recipientAddress")) + .sign(this.passphrase); + + byte[] serializedBytes = builder.transaction.serialize(false); + String serializedHex = Hex.encode(serializedBytes); + + assertEquals(fixture.get("serialized"), serializedHex); + assertEquals(data.get("id"), builder.transaction.getId()); + assertTrue(builder.verify()); + } +} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/builder/UsernameRegistrationBuilderTest.java b/src/test/java/org/arkecosystem/crypto/transactions/builder/UsernameRegistrationBuilderTest.java deleted file mode 100644 index d373b3f5..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/builder/UsernameRegistrationBuilderTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.arkecosystem.crypto.transactions.builder; - -import static org.junit.jupiter.api.Assertions.*; - -import java.util.HashMap; -import java.util.List; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -class UsernameRegistrationBuilderTest { - @Test - void build() { - Transaction actual = - new UsernameRegistrationBuilder() - .usernameAsset("alfonsobries") - .nonce(3) - .sign("this is a top secret passphrase") - .transaction; - - HashMap actualHashMap = actual.toHashMap(); - - HashMap asset = (HashMap) actualHashMap.get("asset"); - - assertEquals(asset.get("username"), "alfonsobries"); - - assertTrue(actual.verify()); - } - - @Test - void buildMultiSignature() { - Transaction actual = - new UsernameRegistrationBuilder() - .usernameAsset("alfonsobries") - .nonce(3) - .multiSign("secret 1", 0) - .multiSign("secret 2", 1) - .multiSign("secret 3", 2) - .sign("this is a top secret passphrase") - .transaction; - - assertTrue(actual.verify()); - - HashMap actualHashMap = actual.toHashMap(); - - assertNotNull(actualHashMap.get("signatures")); - - List actualSignatures = (List) actualHashMap.get("signatures"); - - assertEquals(3, actualSignatures.size()); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/builder/UsernameResignationBuilderTest.java b/src/test/java/org/arkecosystem/crypto/transactions/builder/UsernameResignationBuilderTest.java deleted file mode 100644 index e96e39ed..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/builder/UsernameResignationBuilderTest.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.arkecosystem.crypto.transactions.builder; - -import static org.junit.jupiter.api.Assertions.*; - -import java.util.HashMap; -import java.util.List; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -public class UsernameResignationBuilderTest { - @Test - void build() { - Transaction actual = - new UsernameResignationBuilder() - .nonce(3) - .sign("this is a top secret passphrase") - .transaction; - - HashMap actualHashMap = actual.toHashMap(); - HashMap actualAsset = (HashMap) actualHashMap.get("asset"); - assertNull(actualAsset); - - assertTrue(actual.verify()); - } - - @Test - void buildSecondSignature() { - Transaction actual = - new UsernameResignationBuilder() - .nonce(3) - .sign("this is a top secret passphrase") - .secondSign("this is a top secret second passphrase") - .transaction; - - assertTrue(actual.verify()); - assertTrue( - actual.secondVerify( - "03699e966b2525f9088a6941d8d94f7869964a000efe65783d78ac82e1199fe609")); - } - - @Test - void buildMultiSignature() { - Transaction actual = - new UsernameResignationBuilder() - .nonce(3) - .multiSign("secret 1", 0) - .multiSign("secret 2", 1) - .multiSign("secret 3", 2) - .sign("this is a top secret passphrase") - .transaction; - - assertTrue(actual.verify()); - - HashMap actualHashMap = actual.toHashMap(); - - assertNotNull(actualHashMap.get("signatures")); - - List actualSignatures = (List) actualHashMap.get("signatures"); - - assertEquals(3, actualSignatures.size()); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/builder/ValidatorRegistrationBuilderTest.java b/src/test/java/org/arkecosystem/crypto/transactions/builder/ValidatorRegistrationBuilderTest.java index 65ffa8f8..340fa7e5 100644 --- a/src/test/java/org/arkecosystem/crypto/transactions/builder/ValidatorRegistrationBuilderTest.java +++ b/src/test/java/org/arkecosystem/crypto/transactions/builder/ValidatorRegistrationBuilderTest.java @@ -2,54 +2,35 @@ import static org.junit.jupiter.api.Assertions.*; -import java.util.HashMap; -import java.util.List; -import org.arkecosystem.crypto.transactions.types.Transaction; +import java.util.Map; +import org.arkecosystem.crypto.AbstractTest; +import org.arkecosystem.crypto.encoding.Hex; import org.junit.jupiter.api.Test; -class ValidatorRegistrationBuilderTest { - @Test - void build() { - Transaction actual = - new ValidatorRegistrationBuilder() - .publicKeyAsset( - "a08058db53e2665c84a40f5152e76dd2b652125a6079130d4c315e728bcf4dd1dfb44ac26e82302331d61977d3141118") - .nonce(3) - .sign("this is a top secret passphrase") - .transaction; +public class ValidatorRegistrationBuilderTest extends AbstractTest { - HashMap actualHashMap = actual.toHashMap(); - - HashMap asset = (HashMap) actualHashMap.get("asset"); + @Test + public void it_should_sign_it_with_a_passphrase() throws Exception { + Map fixture = loadFixture("validator-registration"); - assertEquals( - asset.get("validatorPublicKey"), - "a08058db53e2665c84a40f5152e76dd2b652125a6079130d4c315e728bcf4dd1dfb44ac26e82302331d61977d3141118"); + Map data = (Map) fixture.get("data"); - assertTrue(actual.verify()); - } - - @Test - void buildMultiSignature() { - Transaction actual = + ValidatorRegistrationBuilder builder = new ValidatorRegistrationBuilder() - .publicKeyAsset( + .gasPrice(((Number) data.get("gasPrice")).intValue()) + .nonce(Long.parseLong(data.get("nonce").toString())) + .network(((Number) data.get("network")).intValue()) + .gasLimit(((Number) data.get("gasLimit")).intValue()) + .validatorPublicKey( "a08058db53e2665c84a40f5152e76dd2b652125a6079130d4c315e728bcf4dd1dfb44ac26e82302331d61977d3141118") - .nonce(3) - .multiSign("secret 1", 0) - .multiSign("secret 2", 1) - .multiSign("secret 3", 2) - .sign("this is a top secret passphrase") - .transaction; - - assertTrue(actual.verify()); - - HashMap actualHashMap = actual.toHashMap(); - - assertNotNull(actualHashMap.get("signatures")); + .recipientAddress((String) data.get("recipientAddress")) + .sign(this.passphrase); - List actualSignatures = (List) actualHashMap.get("signatures"); + byte[] serializedBytes = builder.transaction.serialize(false); + String serializedHex = Hex.encode(serializedBytes); - assertEquals(3, actualSignatures.size()); + assertEquals(fixture.get("serialized"), serializedHex); + assertEquals(data.get("id"), builder.transaction.getId()); + assertTrue(builder.verify()); } } diff --git a/src/test/java/org/arkecosystem/crypto/transactions/builder/ValidatorResignationBuilderTest.java b/src/test/java/org/arkecosystem/crypto/transactions/builder/ValidatorResignationBuilderTest.java index 960a93fa..69de48a1 100644 --- a/src/test/java/org/arkecosystem/crypto/transactions/builder/ValidatorResignationBuilderTest.java +++ b/src/test/java/org/arkecosystem/crypto/transactions/builder/ValidatorResignationBuilderTest.java @@ -2,61 +2,33 @@ import static org.junit.jupiter.api.Assertions.*; -import java.util.HashMap; -import java.util.List; -import org.arkecosystem.crypto.transactions.types.Transaction; +import java.util.Map; +import org.arkecosystem.crypto.AbstractTest; +import org.arkecosystem.crypto.encoding.Hex; import org.junit.jupiter.api.Test; -public class ValidatorResignationBuilderTest { - @Test - void build() { - Transaction actual = - new ValidatorResignationBuilder() - .nonce(3) - .sign("this is a top secret passphrase") - .transaction; - - HashMap actualHashMap = actual.toHashMap(); - HashMap actualAsset = (HashMap) actualHashMap.get("asset"); - assertNull(actualAsset); - - assertTrue(actual.verify()); - } - - @Test - void buildSecondSignature() { - Transaction actual = - new ValidatorResignationBuilder() - .nonce(3) - .sign("this is a top secret passphrase") - .secondSign("this is a top secret second passphrase") - .transaction; - - assertTrue(actual.verify()); - assertTrue( - actual.secondVerify( - "03699e966b2525f9088a6941d8d94f7869964a000efe65783d78ac82e1199fe609")); - } +public class ValidatorResignationBuilderTest extends AbstractTest { @Test - void buildMultiSignature() { - Transaction actual = - new ValidatorResignationBuilder() - .nonce(3) - .multiSign("secret 1", 0) - .multiSign("secret 2", 1) - .multiSign("secret 3", 2) - .sign("this is a top secret passphrase") - .transaction; - - assertTrue(actual.verify()); + public void it_should_sign_it_with_a_passphrase() throws Exception { + Map fixture = loadFixture("validator-resignation"); - HashMap actualHashMap = actual.toHashMap(); + Map data = (Map) fixture.get("data"); - assertNotNull(actualHashMap.get("signatures")); - - List actualSignatures = (List) actualHashMap.get("signatures"); - - assertEquals(3, actualSignatures.size()); + ValidatorResignationBuilder builder = + new ValidatorResignationBuilder() + .gasPrice(((Number) data.get("gasPrice")).intValue()) + .nonce(Long.parseLong(data.get("nonce").toString())) + .network(((Number) data.get("network")).intValue()) + .gasLimit(((Number) data.get("gasLimit")).intValue()) + .recipientAddress((String) data.get("recipientAddress")) + .sign(this.passphrase); + + byte[] serializedBytes = builder.transaction.serialize(false); + String serializedHex = Hex.encode(serializedBytes); + + assertEquals(fixture.get("serialized"), serializedHex); + assertEquals(data.get("id"), builder.transaction.getId()); + assertTrue(builder.verify()); } } diff --git a/src/test/java/org/arkecosystem/crypto/transactions/builder/VoteBuilderTest.java b/src/test/java/org/arkecosystem/crypto/transactions/builder/VoteBuilderTest.java index 8aca1456..cd6bc6ee 100644 --- a/src/test/java/org/arkecosystem/crypto/transactions/builder/VoteBuilderTest.java +++ b/src/test/java/org/arkecosystem/crypto/transactions/builder/VoteBuilderTest.java @@ -1,157 +1,35 @@ package org.arkecosystem.crypto.transactions.builder; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import org.arkecosystem.crypto.transactions.types.Transaction; +import java.util.Map; +import org.arkecosystem.crypto.AbstractTest; +import org.arkecosystem.crypto.encoding.Hex; import org.junit.jupiter.api.Test; -class VoteBuilderTest { - @Test - void buildVote() { - Transaction actual = - new VoteBuilder() - .addVote( - "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192") - .version(2) - .nonce(3) - .sign("this is a top secret passphrase") - .transaction; - - assertTrue(actual.verify()); - - HashMap actualHashMap = actual.toHashMap(); - HashMap actualAsset = (HashMap) actualHashMap.get("asset"); - List actualVotes = (List) actualAsset.get("votes"); - - assertEquals( - actualVotes, - Arrays.asList( - "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192")); - } - - @Test - void buildVotes() { - Transaction actual = - new VoteBuilder() - .addVotes( - Arrays.asList( - "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192")) - .version(2) - .nonce(3) - .sign("this is a top secret passphrase") - .transaction; - - assertTrue(actual.verify()); - - HashMap actualHashMap = actual.toHashMap(); - HashMap actualAsset = (HashMap) actualHashMap.get("asset"); - List actualVotes = (List) actualAsset.get("votes"); - - assertEquals( - actualVotes, - Arrays.asList( - "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192")); - } - - @Test - void buildUnvote() { - Transaction actual = - new VoteBuilder() - .addUnvote( - "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed193") - .nonce(3) - .sign("this is a top secret passphrase") - .transaction; - - assertTrue(actual.verify()); - - HashMap actualHashMap = actual.toHashMap(); - HashMap actualAsset = (HashMap) actualHashMap.get("asset"); - List actualUnvotes = (List) actualAsset.get("unvotes"); - - assertEquals( - actualUnvotes, - Arrays.asList( - "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed193")); - } - - @Test - void buildUnvoteVote() { - Transaction actual = - new VoteBuilder() - .addVotes( - Arrays.asList( - "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192")) - .addUnvotes( - Arrays.asList( - "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed193")) - .nonce(3) - .sign("this is a top secret passphrase") - .transaction; - - assertTrue(actual.verify()); - - HashMap actualHashMap = actual.toHashMap(); - HashMap actualAsset = (HashMap) actualHashMap.get("asset"); - List actualVotes = (List) actualAsset.get("votes"); - List actualUnvotes = (List) actualAsset.get("unvotes"); - - assertEquals( - actualVotes, - Arrays.asList( - "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192")); - - assertEquals( - actualUnvotes, - Arrays.asList( - "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed193")); - } +public class VoteBuilderTest extends AbstractTest { @Test - void buildVoteSecondSignature() { - Transaction actual = - new VoteBuilder() - .addVote( - "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192") - .version(2) - .nonce(3) - .sign("this is a top secret passphrase") - .secondSign("this is a top secret second passphrase") - .transaction; + public void it_should_sign_it_with_a_passphrase() throws Exception { + Map fixture = loadFixture("vote"); - assertTrue(actual.verify()); - assertTrue( - actual.secondVerify( - "03699e966b2525f9088a6941d8d94f7869964a000efe65783d78ac82e1199fe609")); - } + Map data = (Map) fixture.get("data"); - @Test - void buildMultiSignature() { - Transaction actual = + VoteBuilder builder = new VoteBuilder() - .addVote( - "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192") - .version(2) - .nonce(3) - .multiSign("secret 1", 0) - .multiSign("secret 2", 1) - .multiSign("secret 3", 2) - .sign("this is a top secret passphrase") - .transaction; - - assertTrue(actual.verify()); - - HashMap actualHashMap = actual.toHashMap(); - - assertNotNull(actualHashMap.get("signatures")); - - List actualSignatures = (List) actualHashMap.get("signatures"); - - assertEquals(3, actualSignatures.size()); + .gasPrice(((Number) data.get("gasPrice")).intValue()) + .nonce(Long.parseLong(data.get("nonce").toString())) + .network(((Number) data.get("network")).intValue()) + .vote("0x512F366D524157BcF734546eB29a6d687B762255") + .gasLimit(((Number) data.get("gasLimit")).intValue()) + .recipientAddress((String) data.get("recipientAddress")) + .sign(this.passphrase); + + byte[] serializedBytes = builder.transaction.serialize(false); + String serializedHex = Hex.encode(serializedBytes); + + assertEquals(fixture.get("serialized"), serializedHex); + assertEquals(data.get("id"), builder.transaction.getId()); + assertTrue(builder.verify()); } } diff --git a/src/test/java/org/arkecosystem/crypto/transactions/deserializers/DeserializerTest.java b/src/test/java/org/arkecosystem/crypto/transactions/deserializers/DeserializerTest.java deleted file mode 100644 index 70327228..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/deserializers/DeserializerTest.java +++ /dev/null @@ -1,100 +0,0 @@ -package org.arkecosystem.crypto.transactions.deserializers; - -import static org.junit.jupiter.api.Assertions.*; - -import com.google.gson.internal.LinkedTreeMap; -import java.nio.ByteBuffer; -import java.util.HashMap; -import org.arkecosystem.crypto.transactions.Deserializer; -import org.arkecosystem.crypto.transactions.FixtureLoader; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class DeserializerTest { - @Test - void checkNewTransactionType() { - LinkedTreeMap fixture = - FixtureLoader.load("transactions/transfer/transfer-sign"); - - Deserializer deserializer = new Deserializer(fixture.get("serialized").toString()); - deserializer.setNewTransactionType( - new Transaction() { - @Override - public byte[] serializeData() { - return new byte[0]; - } - - @Override - public void deserializeData(ByteBuffer buffer) {} - - @Override - public int getTransactionType() { - return 1; - } - - @Override - public int getTransactionTypeGroup() { - return 2; - } - - @Override - public HashMap assetToHashMap() { - return null; - } - }); - - assertTrue(deserializer.hasTransactionType(2, 1)); - assertFalse(deserializer.hasTransactionType(2, 2)); - assertFalse(deserializer.hasTransactionType(3, 1)); - } - - @Test - void checkNewTransactionToCoreGroup() { - LinkedTreeMap fixture = - FixtureLoader.load("transactions/transfer/transfer-sign"); - - Deserializer deserializer = new Deserializer(fixture.get("serialized").toString()); - deserializer.setNewTransactionType( - new Transaction() { - @Override - public byte[] serializeData() { - return new byte[0]; - } - - @Override - public void deserializeData(ByteBuffer buffer) {} - - @Override - public int getTransactionType() { - return 11; - } - - @Override - public int getTransactionTypeGroup() { - return 1; - } - - @Override - public HashMap assetToHashMap() { - return null; - } - }); - assertTrue(deserializer.hasTransactionType(1, 11)); - } - - @Test - void duplicateParticipantInMultiSignature() { - String serialized = - "ff011e0100000000000100000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3809698000000000000010000000000000000000000b693449adda7efc015d87944eae8b7c37eb1690af25db2b781b79f671b7848284de63f3cb898f9938c0b6203a11cace4aaa4b962eabe9c4a67c207a3f8baea3b4f4cef90a0b67a2bfd84cd61dcc771597f52656d005afa0050c85a2ac9b34accb0f47c6a9cc9eee831ac713e62e846898d1b75d6a33034680bce95f638c6dfabf8056f8afa9ef73a3c35741234faf01d0a346e3e7d005afa0050c85a2ac9b34accb0f47c6a9cc9eee831ac713e62e846898d1b75d6a33034680bce95f638c6dfabf8056f8afa9ef73a3c35741234faf01d0a346e3e7d02d6af0f5a85a7967d677b2f1f86e00b8ca37facb714467e12810a692d5bcbdfcac4e4c2d4b79a70c7366405339bf84a854308d20cb48652816a9cf37fb3e86e00"; - - Exception thrown = - Assertions.assertThrows( - RuntimeException.class, - () -> { - Deserializer deserializer = new Deserializer(serialized); - deserializer.deserialize(); - }); - assertEquals("Duplicate participant in multi signature", thrown.getMessage()); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/deserializers/MultiPaymentTest.java b/src/test/java/org/arkecosystem/crypto/transactions/deserializers/MultiPaymentTest.java deleted file mode 100644 index 1c58f300..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/deserializers/MultiPaymentTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.arkecosystem.crypto.transactions.deserializers; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import com.google.gson.internal.LinkedTreeMap; -import java.util.ArrayList; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; -import org.arkecosystem.crypto.transactions.Deserializer; -import org.arkecosystem.crypto.transactions.FixtureLoader; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -class MultiPaymentTest { - @Test - void passphrase() { - LinkedTreeMap fixture = - FixtureLoader.load("transactions/multi_payment/multi-payment-sign"); - - LinkedTreeMap data = (LinkedTreeMap) fixture.get("data"); - - Transaction actual = new Deserializer(fixture.get("serialized").toString()).deserialize(); - - assertEquals(((Double) data.get("version")).intValue(), actual.version); - assertEquals(((Double) data.get("network")).intValue(), actual.network); - assertEquals(TransactionTypeGroup.CORE.getValue(), actual.typeGroup); - assertEquals(((Double) data.get("type")).intValue(), actual.type); - assertEquals((Long.valueOf((String) data.get("nonce"))), actual.nonce); - assertEquals(data.get("senderPublicKey").toString(), actual.senderPublicKey); - assertEquals((Long.valueOf((String) data.get("fee"))), actual.fee); - assertEquals(data.get("signature").toString(), actual.signature); - assertEquals(data.get("id").toString(), actual.id); - - LinkedTreeMap asset = (LinkedTreeMap) data.get("asset"); - ArrayList payments = ((ArrayList) asset.get("payments")); - for (int i = 0; i < payments.size(); i++) { - String recipientId = - (String) ((LinkedTreeMap) payments.get(i)).get("recipientId"); - String amount = - ((String) ((LinkedTreeMap) payments.get(i)).get("amount")); - assertEquals(recipientId, actual.asset.multiPayment.payments.get(i).recipientId); - assertEquals(Long.valueOf(amount), actual.asset.multiPayment.payments.get(i).amount); - } - } - - @Test - void passphraseVendorField() { - LinkedTreeMap fixture = - FixtureLoader.load( - "transactions/multi_payment/multi-payment-with-vendor-field-sign"); - - LinkedTreeMap data = (LinkedTreeMap) fixture.get("data"); - - Transaction actual = new Deserializer(fixture.get("serialized").toString()).deserialize(); - - assertEquals(((Double) data.get("version")).intValue(), actual.version); - assertEquals(((Double) data.get("network")).intValue(), actual.network); - assertEquals(TransactionTypeGroup.CORE.getValue(), actual.typeGroup); - assertEquals(((Double) data.get("type")).intValue(), actual.type); - assertEquals((Long.valueOf((String) data.get("nonce"))), actual.nonce); - assertEquals(data.get("senderPublicKey").toString(), actual.senderPublicKey); - assertEquals((Long.valueOf((String) data.get("fee"))), actual.fee); - assertEquals(data.get("signature").toString(), actual.signature); - assertEquals(data.get("id").toString(), actual.id); - - LinkedTreeMap asset = (LinkedTreeMap) data.get("asset"); - ArrayList payments = ((ArrayList) asset.get("payments")); - for (int i = 0; i < payments.size(); i++) { - String recipientId = - (String) ((LinkedTreeMap) payments.get(i)).get("recipientId"); - String amount = - ((String) ((LinkedTreeMap) payments.get(i)).get("amount")); - assertEquals(recipientId, actual.asset.multiPayment.payments.get(i).recipientId); - assertEquals(Long.valueOf(amount), actual.asset.multiPayment.payments.get(i).amount); - } - - assertEquals(data.get("vendorField").toString(), actual.vendorField); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/deserializers/TransferTest.java b/src/test/java/org/arkecosystem/crypto/transactions/deserializers/TransferTest.java deleted file mode 100644 index c8a44ac3..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/deserializers/TransferTest.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.arkecosystem.crypto.transactions.deserializers; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import com.google.gson.internal.LinkedTreeMap; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; -import org.arkecosystem.crypto.transactions.Deserializer; -import org.arkecosystem.crypto.transactions.FixtureLoader; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -class TransferTest { - - @Test - void passphrase() { - LinkedTreeMap fixture = - FixtureLoader.load("transactions/transfer/transfer-sign"); - - LinkedTreeMap data = (LinkedTreeMap) fixture.get("data"); - - Transaction actual = new Deserializer(fixture.get("serialized").toString()).deserialize(); - - assertEquals(((Double) data.get("version")).intValue(), actual.version); - assertEquals(((Double) data.get("network")).intValue(), actual.network); - assertEquals(TransactionTypeGroup.CORE.getValue(), actual.typeGroup); - assertEquals(((Double) data.get("type")).intValue(), actual.type); - assertEquals((Long.valueOf((String) data.get("nonce"))), actual.nonce); - assertEquals(data.get("senderPublicKey").toString(), actual.senderPublicKey); - assertEquals((Long.valueOf((String) data.get("fee"))), actual.fee); - assertEquals(data.get("signature").toString(), actual.signature); - assertEquals(data.get("id").toString(), actual.id); - - assertEquals(data.get("recipientId").toString(), actual.recipientId); - assertEquals((Long.valueOf((String) data.get("amount"))), actual.amount); - assertEquals(((Double) data.get("expiration")).intValue(), actual.expiration); - } - - @Test - void passphraseVendorField() { - LinkedTreeMap fixture = - FixtureLoader.load("transactions/transfer/transfer-with-vendor-field-sign"); - - LinkedTreeMap data = (LinkedTreeMap) fixture.get("data"); - - Transaction actual = new Deserializer(fixture.get("serialized").toString()).deserialize(); - - assertEquals(((Double) data.get("version")).intValue(), actual.version); - assertEquals(((Double) data.get("network")).intValue(), actual.network); - assertEquals(TransactionTypeGroup.CORE.getValue(), actual.typeGroup); - assertEquals(((Double) data.get("type")).intValue(), actual.type); - assertEquals((Long.valueOf((String) data.get("nonce"))), actual.nonce); - assertEquals(data.get("senderPublicKey").toString(), actual.senderPublicKey); - assertEquals((Long.valueOf((String) data.get("fee"))), actual.fee); - assertEquals(data.get("signature").toString(), actual.signature); - assertEquals(data.get("id").toString(), actual.id); - - System.out.println("AAAA"); - System.out.println(data.get("vendorField").toString()); - - assertEquals(data.get("recipientId").toString(), actual.recipientId); - assertEquals((Long.valueOf((String) data.get("amount"))), actual.amount); - assertEquals(((Double) data.get("expiration")).intValue(), actual.expiration); - assertEquals(data.get("vendorField").toString(), actual.vendorField); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/deserializers/UsernameRegistrationTest.java b/src/test/java/org/arkecosystem/crypto/transactions/deserializers/UsernameRegistrationTest.java deleted file mode 100644 index 0853ed71..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/deserializers/UsernameRegistrationTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.arkecosystem.crypto.transactions.deserializers; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import com.google.gson.internal.LinkedTreeMap; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; -import org.arkecosystem.crypto.transactions.Deserializer; -import org.arkecosystem.crypto.transactions.FixtureLoader; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -public class UsernameRegistrationTest { - - @Test - void passphrase() { - LinkedTreeMap fixture = - FixtureLoader.load("transactions/username_registration/username-registration-sign"); - - LinkedTreeMap data = (LinkedTreeMap) fixture.get("data"); - - Transaction actual = new Deserializer(fixture.get("serialized").toString()).deserialize(); - - assertEquals(((Double) data.get("version")).intValue(), actual.version); - assertEquals(((Double) data.get("network")).intValue(), actual.network); - assertEquals(TransactionTypeGroup.CORE.getValue(), actual.typeGroup); - assertEquals(((Double) data.get("type")).intValue(), actual.type); - assertEquals((Long.valueOf((String) data.get("nonce"))), actual.nonce); - assertEquals(data.get("senderPublicKey").toString(), actual.senderPublicKey); - assertEquals((Long.valueOf((String) data.get("fee"))), actual.fee); - assertEquals(data.get("signature").toString(), actual.signature); - assertEquals(data.get("id").toString(), actual.id); - - LinkedTreeMap asset = (LinkedTreeMap) data.get("asset"); - - assertEquals((asset.get("username")), actual.asset.username); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/deserializers/UsernameResignationTest.java b/src/test/java/org/arkecosystem/crypto/transactions/deserializers/UsernameResignationTest.java deleted file mode 100644 index 6dfa1ff7..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/deserializers/UsernameResignationTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.arkecosystem.crypto.transactions.deserializers; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -import com.google.gson.internal.LinkedTreeMap; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; -import org.arkecosystem.crypto.transactions.Deserializer; -import org.arkecosystem.crypto.transactions.FixtureLoader; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -class UsernameResignationTest { - - @Test - void passphrase() { - LinkedTreeMap fixture = - FixtureLoader.load("transactions/username_resignation/username-resignation-sign"); - - LinkedTreeMap data = (LinkedTreeMap) fixture.get("data"); - - Transaction actual = new Deserializer(fixture.get("serialized").toString()).deserialize(); - - assertEquals(((Double) data.get("version")).intValue(), actual.version); - assertEquals(((Double) data.get("network")).intValue(), actual.network); - assertEquals(TransactionTypeGroup.CORE.getValue(), actual.typeGroup); - assertEquals(((Double) data.get("type")).intValue(), actual.type); - assertEquals((Long.valueOf((String) data.get("nonce"))), actual.nonce); - assertEquals(data.get("senderPublicKey").toString(), actual.senderPublicKey); - assertEquals((Long.valueOf((String) data.get("fee"))), actual.fee); - assertEquals(data.get("signature").toString(), actual.signature); - assertEquals(data.get("id").toString(), actual.id); - - assertNull(data.get("asset")); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/deserializers/ValidatorRegistrationTest.java b/src/test/java/org/arkecosystem/crypto/transactions/deserializers/ValidatorRegistrationTest.java deleted file mode 100644 index c0b1edda..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/deserializers/ValidatorRegistrationTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.arkecosystem.crypto.transactions.deserializers; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import com.google.gson.internal.LinkedTreeMap; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; -import org.arkecosystem.crypto.transactions.Deserializer; -import org.arkecosystem.crypto.transactions.FixtureLoader; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -public class ValidatorRegistrationTest { - - @Test - void passphrase() { - LinkedTreeMap fixture = - FixtureLoader.load( - "transactions/validator_registration/validator-registration-sign"); - - LinkedTreeMap data = (LinkedTreeMap) fixture.get("data"); - - Transaction actual = new Deserializer(fixture.get("serialized").toString()).deserialize(); - - assertEquals(((Double) data.get("version")).intValue(), actual.version); - assertEquals(((Double) data.get("network")).intValue(), actual.network); - assertEquals(TransactionTypeGroup.CORE.getValue(), actual.typeGroup); - assertEquals(((Double) data.get("type")).intValue(), actual.type); - assertEquals((Long.valueOf((String) data.get("nonce"))), actual.nonce); - assertEquals(data.get("senderPublicKey").toString(), actual.senderPublicKey); - assertEquals((Long.valueOf((String) data.get("fee"))), actual.fee); - assertEquals(data.get("signature").toString(), actual.signature); - assertEquals(data.get("id").toString(), actual.id); - - LinkedTreeMap asset = (LinkedTreeMap) data.get("asset"); - - assertEquals((asset.get("validatorPublicKey")), actual.asset.validatorPublicKey); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/deserializers/ValidatorResignationTest.java b/src/test/java/org/arkecosystem/crypto/transactions/deserializers/ValidatorResignationTest.java deleted file mode 100644 index dcfd7407..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/deserializers/ValidatorResignationTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.arkecosystem.crypto.transactions.deserializers; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -import com.google.gson.internal.LinkedTreeMap; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; -import org.arkecosystem.crypto.transactions.Deserializer; -import org.arkecosystem.crypto.transactions.FixtureLoader; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -class ValidatorResignationTest { - - @Test - void passphrase() { - LinkedTreeMap fixture = - FixtureLoader.load("transactions/validator_resignation/validator-resignation-sign"); - - LinkedTreeMap data = (LinkedTreeMap) fixture.get("data"); - - Transaction actual = new Deserializer(fixture.get("serialized").toString()).deserialize(); - - assertEquals(((Double) data.get("version")).intValue(), actual.version); - assertEquals(((Double) data.get("network")).intValue(), actual.network); - assertEquals(TransactionTypeGroup.CORE.getValue(), actual.typeGroup); - assertEquals(((Double) data.get("type")).intValue(), actual.type); - assertEquals((Long.valueOf((String) data.get("nonce"))), actual.nonce); - assertEquals(data.get("senderPublicKey").toString(), actual.senderPublicKey); - assertEquals((Long.valueOf((String) data.get("fee"))), actual.fee); - assertEquals(data.get("signature").toString(), actual.signature); - assertEquals(data.get("id").toString(), actual.id); - - assertNull(data.get("asset")); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/deserializers/VoteTest.java b/src/test/java/org/arkecosystem/crypto/transactions/deserializers/VoteTest.java deleted file mode 100644 index d86f2a07..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/deserializers/VoteTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.arkecosystem.crypto.transactions.deserializers; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import com.google.gson.internal.LinkedTreeMap; -import org.arkecosystem.crypto.enums.TransactionTypeGroup; -import org.arkecosystem.crypto.transactions.Deserializer; -import org.arkecosystem.crypto.transactions.FixtureLoader; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -class VoteTest { - - @Test - void passphraseVote() { - LinkedTreeMap fixture = FixtureLoader.load("transactions/vote/vote-sign"); - - LinkedTreeMap data = (LinkedTreeMap) fixture.get("data"); - - Transaction actual = new Deserializer(fixture.get("serialized").toString()).deserialize(); - - assertEquals(((Double) data.get("version")).intValue(), actual.version); - assertEquals(((Double) data.get("network")).intValue(), actual.network); - assertEquals(TransactionTypeGroup.CORE.getValue(), actual.typeGroup); - assertEquals(((Double) data.get("type")).intValue(), actual.type); - assertEquals((Long.valueOf((String) data.get("nonce"))), actual.nonce); - assertEquals(data.get("senderPublicKey").toString(), actual.senderPublicKey); - assertEquals((Long.valueOf((String) data.get("fee"))), actual.fee); - assertEquals(data.get("signature").toString(), actual.signature); - assertEquals(data.get("id").toString(), actual.id); - - LinkedTreeMap assetV2 = (LinkedTreeMap) data.get("asset"); - assertEquals((assetV2.get("votes")), actual.asset.votes); - } - - @Test - void passphraseUnvote() { - LinkedTreeMap fixture = FixtureLoader.load("transactions/vote/unvote-sign"); - - LinkedTreeMap data = (LinkedTreeMap) fixture.get("data"); - - Transaction actual = new Deserializer(fixture.get("serialized").toString()).deserialize(); - - assertEquals(((Double) data.get("version")).intValue(), actual.version); - assertEquals(((Double) data.get("network")).intValue(), actual.network); - assertEquals(TransactionTypeGroup.CORE.getValue(), actual.typeGroup); - assertEquals(((Double) data.get("type")).intValue(), actual.type); - assertEquals((Long.valueOf((String) data.get("nonce"))), actual.nonce); - assertEquals(data.get("senderPublicKey").toString(), actual.senderPublicKey); - assertEquals((Long.valueOf((String) data.get("fee"))), actual.fee); - assertEquals(data.get("signature").toString(), actual.signature); - assertEquals(data.get("id").toString(), actual.id); - - LinkedTreeMap asset = (LinkedTreeMap) data.get("asset"); - assertEquals((asset.get("unvotes")), actual.asset.unvotes); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/serializers/MultiPaymentTest.java b/src/test/java/org/arkecosystem/crypto/transactions/serializers/MultiPaymentTest.java deleted file mode 100644 index 017d047b..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/serializers/MultiPaymentTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.arkecosystem.crypto.transactions.serializers; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import com.google.gson.internal.LinkedTreeMap; -import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.transactions.Deserializer; -import org.arkecosystem.crypto.transactions.FixtureLoader; -import org.arkecosystem.crypto.transactions.Serializer; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -class MultiPaymentTest { - @Test - void passphrase() { - LinkedTreeMap fixture = - FixtureLoader.load("transactions/multi_payment/multi-payment-sign"); - Transaction transaction = - new Deserializer(fixture.get("serialized").toString()).deserialize(); - - String actual = Hex.encode(Serializer.serialize(transaction)); - - assertEquals(fixture.get("serialized").toString(), actual); - } - - @Test - void passphraseVendorField() { - LinkedTreeMap fixture = - FixtureLoader.load( - "transactions/multi_payment/multi-payment-with-vendor-field-sign"); - Transaction transaction = - new Deserializer(fixture.get("serialized").toString()).deserialize(); - - String actual = Hex.encode(Serializer.serialize(transaction)); - - assertEquals(fixture.get("serialized").toString(), actual); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/serializers/TransferTest.java b/src/test/java/org/arkecosystem/crypto/transactions/serializers/TransferTest.java deleted file mode 100644 index 9b2d26ea..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/serializers/TransferTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.arkecosystem.crypto.transactions.serializers; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import com.google.gson.internal.LinkedTreeMap; -import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.transactions.Deserializer; -import org.arkecosystem.crypto.transactions.FixtureLoader; -import org.arkecosystem.crypto.transactions.Serializer; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -class TransferTest { - - @Test - void passphrase() { - LinkedTreeMap fixture = - FixtureLoader.load("transactions/transfer/transfer-sign"); - Transaction transaction = - new Deserializer(fixture.get("serialized").toString()).deserialize(); - - String actual = Hex.encode(Serializer.serialize(transaction)); - - assertEquals(fixture.get("serialized").toString(), actual); - } - - @Test - void passphraseVendorField() { - LinkedTreeMap fixture = - FixtureLoader.load("transactions/transfer/transfer-with-vendor-field-sign"); - Transaction transaction = - new Deserializer(fixture.get("serialized").toString()).deserialize(); - - String actual = Hex.encode(Serializer.serialize(transaction)); - - assertEquals(fixture.get("serialized").toString(), actual); - } - - @Test - void secondPassphrase() { - LinkedTreeMap fixture = - FixtureLoader.load("transactions/transfer/transfer-with-vendor-field-sign"); - Transaction transaction = - new Deserializer(fixture.get("serialized").toString()).deserialize(); - - String actual = Hex.encode(Serializer.serialize(transaction)); - - assertEquals(fixture.get("serialized").toString(), actual); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/serializers/UsernameRegistrationTest.java b/src/test/java/org/arkecosystem/crypto/transactions/serializers/UsernameRegistrationTest.java deleted file mode 100644 index c07191d6..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/serializers/UsernameRegistrationTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.arkecosystem.crypto.transactions.serializers; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import com.google.gson.internal.LinkedTreeMap; -import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.transactions.Deserializer; -import org.arkecosystem.crypto.transactions.FixtureLoader; -import org.arkecosystem.crypto.transactions.Serializer; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -class UsernameRegistrationTest { - @Test - void passphrase() { - LinkedTreeMap fixture = - FixtureLoader.load("transactions/username_registration/username-registration-sign"); - Transaction transaction = - new Deserializer(fixture.get("serialized").toString()).deserialize(); - - String actual = Hex.encode(Serializer.serialize(transaction)); - - assertEquals(fixture.get("serialized").toString(), actual); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/serializers/UsernameResignationTest.java b/src/test/java/org/arkecosystem/crypto/transactions/serializers/UsernameResignationTest.java deleted file mode 100644 index ec77bef7..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/serializers/UsernameResignationTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.arkecosystem.crypto.transactions.serializers; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import com.google.gson.internal.LinkedTreeMap; -import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.transactions.Deserializer; -import org.arkecosystem.crypto.transactions.FixtureLoader; -import org.arkecosystem.crypto.transactions.Serializer; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -class UsernameResignationTest { - @Test - void passphrase() { - LinkedTreeMap fixture = - FixtureLoader.load("transactions/username_resignation/username-resignation-sign"); - - Transaction transaction = - new Deserializer(fixture.get("serialized").toString()).deserialize(); - - String actual = Hex.encode(Serializer.serialize(transaction)); - - assertEquals(fixture.get("serialized").toString(), actual); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/serializers/ValidatorRegistrationTest.java b/src/test/java/org/arkecosystem/crypto/transactions/serializers/ValidatorRegistrationTest.java deleted file mode 100644 index ec1d8cb6..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/serializers/ValidatorRegistrationTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.arkecosystem.crypto.transactions.serializers; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import com.google.gson.internal.LinkedTreeMap; -import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.transactions.Deserializer; -import org.arkecosystem.crypto.transactions.FixtureLoader; -import org.arkecosystem.crypto.transactions.Serializer; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -class ValidatorRegistrationTest { - @Test - void passphrase() { - LinkedTreeMap fixture = - FixtureLoader.load( - "transactions/validator_registration/validator-registration-sign"); - Transaction transaction = - new Deserializer(fixture.get("serialized").toString()).deserialize(); - - String actual = Hex.encode(Serializer.serialize(transaction)); - - assertEquals(fixture.get("serialized").toString(), actual); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/serializers/ValidatorResignationTest.java b/src/test/java/org/arkecosystem/crypto/transactions/serializers/ValidatorResignationTest.java deleted file mode 100644 index 208e1d11..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/serializers/ValidatorResignationTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.arkecosystem.crypto.transactions.serializers; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import com.google.gson.internal.LinkedTreeMap; -import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.transactions.Deserializer; -import org.arkecosystem.crypto.transactions.FixtureLoader; -import org.arkecosystem.crypto.transactions.Serializer; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -class ValidatorResignationTest { - - @Test - void passphrase() { - LinkedTreeMap fixture = - FixtureLoader.load("transactions/validator_resignation/validator-resignation-sign"); - - Transaction transaction = - new Deserializer(fixture.get("serialized").toString()).deserialize(); - - String actual = Hex.encode(Serializer.serialize(transaction)); - - assertEquals(fixture.get("serialized").toString(), actual); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/transactions/serializers/VoteTest.java b/src/test/java/org/arkecosystem/crypto/transactions/serializers/VoteTest.java deleted file mode 100644 index c136fa66..00000000 --- a/src/test/java/org/arkecosystem/crypto/transactions/serializers/VoteTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.arkecosystem.crypto.transactions.serializers; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import com.google.gson.internal.LinkedTreeMap; -import org.arkecosystem.crypto.encoding.Hex; -import org.arkecosystem.crypto.transactions.Deserializer; -import org.arkecosystem.crypto.transactions.FixtureLoader; -import org.arkecosystem.crypto.transactions.Serializer; -import org.arkecosystem.crypto.transactions.types.Transaction; -import org.junit.jupiter.api.Test; - -class VoteTest { - @Test - void passphraseVote() { - LinkedTreeMap fixture = FixtureLoader.load("transactions/vote/vote-sign"); - Transaction transaction = - new Deserializer(fixture.get("serialized").toString()).deserialize(); - - String actual = Hex.encode(Serializer.serialize(transaction)); - - assertEquals(fixture.get("serialized").toString(), actual); - } - - @Test - void passphraseUnvote() { - LinkedTreeMap fixture = FixtureLoader.load("transactions/vote/unvote-sign"); - Transaction transaction = - new Deserializer(fixture.get("serialized").toString()).deserialize(); - - String actual = Hex.encode(Serializer.serialize(transaction)); - - assertEquals(fixture.get("serialized").toString(), actual); - } -} diff --git a/src/test/java/org/arkecosystem/crypto/utils/AbiDecoderTest.java b/src/test/java/org/arkecosystem/crypto/utils/AbiDecoderTest.java new file mode 100644 index 00000000..2e474717 --- /dev/null +++ b/src/test/java/org/arkecosystem/crypto/utils/AbiDecoderTest.java @@ -0,0 +1,35 @@ +package org.arkecosystem.crypto.utils; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class AbiDecoderTest { + + private AbiDecoder decoder; + + @BeforeEach + void setUp() throws Exception { + decoder = new AbiDecoder(); + } + + @Test + void it_should_decode_vote_payload() throws Exception { + String functionName = "vote"; + List args = Arrays.asList("0x512F366D524157BcF734546eB29a6d687B762255"); + String data = "0x6dd7d8ea000000000000000000000000512f366d524157bcf734546eb29a6d687b762255"; + + Map decodedData = decoder.decodeFunctionData(data); + + Map expectedData = new HashMap<>(); + expectedData.put("functionName", functionName); + expectedData.put("args", args); + + assertEquals(expectedData, decodedData); + } +} diff --git a/src/test/java/org/arkecosystem/crypto/utils/AbiEncoderTest.java b/src/test/java/org/arkecosystem/crypto/utils/AbiEncoderTest.java new file mode 100644 index 00000000..9d7a2bec --- /dev/null +++ b/src/test/java/org/arkecosystem/crypto/utils/AbiEncoderTest.java @@ -0,0 +1,30 @@ +package org.arkecosystem.crypto.utils; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class AbiEncoderTest { + + private AbiEncoder encoder; + + @BeforeEach + void setUp() throws Exception { + encoder = new AbiEncoder(); + } + + @Test + void it_should_encode_vote_function_call() throws Exception { + String functionName = "vote"; + List args = Arrays.asList("0x512F366D524157BcF734546eB29a6d687B762255"); + String expectedEncodedData = + "0x6dd7d8ea000000000000000000000000512f366d524157bcf734546eb29a6d687b762255"; + + String encodedData = encoder.encodeFunctionCall(functionName, args); + + assertEquals(expectedEncodedData, encodedData); + } +} diff --git a/src/test/resources/transactions/evm-sign.json b/src/test/resources/transactions/evm-sign.json new file mode 100644 index 00000000..600ffa2d --- /dev/null +++ b/src/test/resources/transactions/evm-sign.json @@ -0,0 +1,16 @@ +{ + "data": { + "network": 30, + "nonce": "13", + "gasPrice": 5, + "gasLimit": 1000000, + "value": "0", + "recipientAddress": "0xE536720791A7DaDBeBdBCD8c8546fb0791a11901", + "data": "a9059cbb00000000000000000000000027fa7caffaae77ddb9ab232fdbda56d5e5af2393000000000000000000000000000000000000000000000000016345785d8a0000", + "signature": "ba30f9042519079895c7408b0e92046c3f20680e0a9294e38ab3cfdd19b26cd4036fe2a80644abb922f1ad7cd682811a83c20120a8030df47b244a3bc44f4dbd00", + "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", + "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", + "id": "3935ff0fe84ea6ac42fc889ed7cda4f97ddd11fd2d1c31e9201f14866acb6edc" + }, + "serialized": "1e0d000000000000000500000040420f00000000000000000000000000000000000000000000000000000000000000000001e536720791a7dadbebdbcd8c8546fb0791a1190144000000a9059cbb00000000000000000000000027fa7caffaae77ddb9ab232fdbda56d5e5af2393000000000000000000000000000000000000000000000000016345785d8a0000ba30f9042519079895c7408b0e92046c3f20680e0a9294e38ab3cfdd19b26cd4036fe2a80644abb922f1ad7cd682811a83c20120a8030df47b244a3bc44f4dbd00" +} diff --git a/src/test/resources/transactions/multi_payment/multi-payment-multi-sign.json b/src/test/resources/transactions/multi_payment/multi-payment-multi-sign.json deleted file mode 100644 index 07f4b78b..00000000 --- a/src/test/resources/transactions/multi_payment/multi-payment-multi-sign.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 6, - "nonce": "0", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "10000000", - "amount": "3", - "asset": { - "payments": [ - { - "amount": "1", - "recipientId": "0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A" - }, - { - "amount": "2", - "recipientId": "0x27FA7CaFFaAE77dDb9AB232FDBDa56D5e5Af2393" - } - ] - }, - "signature": "cf4e94776e768110e62961747f39b68993d4ff5ec2171cad2fab6d77babb2ce31181f005876031d43e6d7732770c6aca73b33057c7aad6279cf2da03829a7b62", - "signatures": [ - "006581c24bbe49e57127604b18d2efdd8d1d2bfe23f9b1e7f15b3f46a647b976e79884b61331546c5844ded781974e181a03a4066f858020fb347ad9628773b465", - "011fb86a8ddbddbed012ff46fc0f3c9a30f3c1544d7a32b9355c318ad453837615e8399f676856c97cf6dcb8d18507ff9c0ffe195d23624e296182477644ed3689", - "023aa80e3663d737716818978ffbae59a8a9f32257a341e1391ca1e0fd744f3382b41e507dca92fa91f98e2024b67938420aaf0c268266dfe4d1f92e81cb3a0421" - ], - "id": "5349b6d57228cb2aeb77e198d6adc93f745e3abb9b4624929b7e29f4a920c0a5" - }, - "serialized": "ff011e0100000006000000000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d380969800000000000002000100000000000000b693449adda7efc015d87944eae8b7c37eb1690a020000000000000027fa7caffaae77ddb9ab232fdbda56d5e5af2393cf4e94776e768110e62961747f39b68993d4ff5ec2171cad2fab6d77babb2ce31181f005876031d43e6d7732770c6aca73b33057c7aad6279cf2da03829a7b62006581c24bbe49e57127604b18d2efdd8d1d2bfe23f9b1e7f15b3f46a647b976e79884b61331546c5844ded781974e181a03a4066f858020fb347ad9628773b465011fb86a8ddbddbed012ff46fc0f3c9a30f3c1544d7a32b9355c318ad453837615e8399f676856c97cf6dcb8d18507ff9c0ffe195d23624e296182477644ed3689023aa80e3663d737716818978ffbae59a8a9f32257a341e1391ca1e0fd744f3382b41e507dca92fa91f98e2024b67938420aaf0c268266dfe4d1f92e81cb3a0421" -} diff --git a/src/test/resources/transactions/multi_payment/multi-payment-sign.json b/src/test/resources/transactions/multi_payment/multi-payment-sign.json deleted file mode 100644 index f936e771..00000000 --- a/src/test/resources/transactions/multi_payment/multi-payment-sign.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 6, - "nonce": "0", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "10000000", - "amount": "3", - "asset": { - "payments": [ - { - "amount": "1", - "recipientId": "0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A" - }, - { - "amount": "2", - "recipientId": "0x27FA7CaFFaAE77dDb9AB232FDBDa56D5e5Af2393" - } - ] - }, - "signature": "e782cc5a7622ea23463aa3d6795850b7163abf6b02c29795f9230f4b0537404cbccf82de6a44ecf6e98391bff601f033524c8704f4081ada4f72e98aea5ae173", - "id": "c820af7342cde48a2d578e6324329fc557ffb4e40fa3208b348cf07fd2020e43" - }, - "serialized": "ff011e0100000006000000000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d380969800000000000002000100000000000000b693449adda7efc015d87944eae8b7c37eb1690a020000000000000027fa7caffaae77ddb9ab232fdbda56d5e5af2393e782cc5a7622ea23463aa3d6795850b7163abf6b02c29795f9230f4b0537404cbccf82de6a44ecf6e98391bff601f033524c8704f4081ada4f72e98aea5ae173" -} diff --git a/src/test/resources/transactions/multi_payment/multi-payment-with-vendor-field-sign.json b/src/test/resources/transactions/multi_payment/multi-payment-with-vendor-field-sign.json deleted file mode 100644 index 685b9222..00000000 --- a/src/test/resources/transactions/multi_payment/multi-payment-with-vendor-field-sign.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 6, - "nonce": "0", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "10000000", - "amount": "3", - "vendorField": "this is a top secret vendor field", - "asset": { - "payments": [ - { - "amount": "1", - "recipientId": "0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A" - }, - { - "amount": "2", - "recipientId": "0x27FA7CaFFaAE77dDb9AB232FDBDa56D5e5Af2393" - } - ] - }, - "signature": "ff68aa386548aba97004d99d616d5bdd1f13074dd85fdb0ab636d18585d4436030f66f42fa257c2b18873a076906af31a2b3b838081d9ec79f685f32d4640955", - "id": "4c759d085999e25996e2af264c79aac76c44e48d3f5f2432c56d8a2e4fd99349" - }, - "serialized": "ff011e0100000006000000000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d380969800000000002174686973206973206120746f70207365637265742076656e646f72206669656c6402000100000000000000b693449adda7efc015d87944eae8b7c37eb1690a020000000000000027fa7caffaae77ddb9ab232fdbda56d5e5af2393ff68aa386548aba97004d99d616d5bdd1f13074dd85fdb0ab636d18585d4436030f66f42fa257c2b18873a076906af31a2b3b838081d9ec79f685f32d4640955" -} diff --git a/src/test/resources/transactions/multi_signature_registration/multi-signature-registration-sign.json b/src/test/resources/transactions/multi_signature_registration/multi-signature-registration-sign.json deleted file mode 100644 index bf65bea5..00000000 --- a/src/test/resources/transactions/multi_signature_registration/multi-signature-registration-sign.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 4, - "nonce": "2", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "500000000", - "amount": "0", - "asset": { - "multiSignature": { - "min": 2, - "publicKeys": [ - "029fab3cb2f5e248ae7cbb4de646741da4d73c493b2a03ab5c71507fb2c0dcca92", - "03629f9dbf7f1e91cefa845126189816ceae357bdd1f41bd14787318a7d5b55d48", - "027941d2059f89a26d89e87d3385e261a0ede1234aaeaa487012b69d6b67962dc5" - ] - } - }, - "signature": "2b33558cdc62933ff56feb646d1f47a98104bf34b894895d5e816f86e556f87fce8485e55aa32dfa1cd86456a66a58ef7a68dff4af51e2f7fcf75b983540872e", - "signatures": [ - "000caa6864c71362b369c71107f463f29c43c361e54260cbc54d791b7385dbe76f29d12b9befbe4f98792d7046481afcf0c156408310192a93d8413e5380438f27", - "0153a22c5ce2b1894f0a141adc19de567077d2d268f4c7e1476e9558ecb4411d486cd0d7aa3e9c7716739a055a8a1a64a162d0362645d63d13791a9876ef8b5a88", - "021d56997f0c9e21201c59e1b7b6be8d5a609908a4f65bf266144baf5e61e3f14bc2651f62187f4ffa17c8b010fcb0fba94a04df56bc25e5cb20935ec5fb7ad632" - ], - "id": "1941926b880ab606633b3a2361df784f6085ded8b5d3cf52e94998e1468b3197" - }, - "serialized": "ff011e0100000004000200000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d30065cd1d00000000000203029fab3cb2f5e248ae7cbb4de646741da4d73c493b2a03ab5c71507fb2c0dcca9203629f9dbf7f1e91cefa845126189816ceae357bdd1f41bd14787318a7d5b55d48027941d2059f89a26d89e87d3385e261a0ede1234aaeaa487012b69d6b67962dc52b33558cdc62933ff56feb646d1f47a98104bf34b894895d5e816f86e556f87fce8485e55aa32dfa1cd86456a66a58ef7a68dff4af51e2f7fcf75b983540872e000caa6864c71362b369c71107f463f29c43c361e54260cbc54d791b7385dbe76f29d12b9befbe4f98792d7046481afcf0c156408310192a93d8413e5380438f270153a22c5ce2b1894f0a141adc19de567077d2d268f4c7e1476e9558ecb4411d486cd0d7aa3e9c7716739a055a8a1a64a162d0362645d63d13791a9876ef8b5a88021d56997f0c9e21201c59e1b7b6be8d5a609908a4f65bf266144baf5e61e3f14bc2651f62187f4ffa17c8b010fcb0fba94a04df56bc25e5cb20935ec5fb7ad632" -} diff --git a/src/test/resources/transactions/transfer.json b/src/test/resources/transactions/transfer.json new file mode 100644 index 00000000..4c577082 --- /dev/null +++ b/src/test/resources/transactions/transfer.json @@ -0,0 +1,16 @@ +{ + "data": { + "network": 30, + "nonce": "12", + "gasPrice": 5, + "gasLimit": 21000, + "value": "10000000000000000000", + "recipientAddress": "0x07Ac3E438719be72a9e2591bB6015F10E8Af2468", + "data": "", + "signature": "b3bc84c8caf1b75c18a78dde87df9f555161003d341eafad659ab672501185e413a26284c3c95056809c7d440c4ffab26179c538864c4d14534ebd5a961852bf01", + "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", + "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", + "id": "b5d7b17d30da123d9eebc8bb6012c1a4e950e1dad2b080404bb052c30b8a8b2e" + }, + "serialized": "1e0c0000000000000005000000085200000000000000000000000000000000000000000000000000008ac7230489e800000107ac3e438719be72a9e2591bb6015f10e8af246800000000b3bc84c8caf1b75c18a78dde87df9f555161003d341eafad659ab672501185e413a26284c3c95056809c7d440c4ffab26179c538864c4d14534ebd5a961852bf01" +} diff --git a/src/test/resources/transactions/transfer/transfer-multi-sign.json b/src/test/resources/transactions/transfer/transfer-multi-sign.json deleted file mode 100644 index 1db6a1de..00000000 --- a/src/test/resources/transactions/transfer/transfer-multi-sign.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 0, - "nonce": "1", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "10000000", - "amount": "1", - "expiration": 0, - "recipientId": "0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A", - "signature": "f25db2b781b79f671b7848284de63f3cb898f9938c0b6203a11cace4aaa4b962eabe9c4a67c207a3f8baea3b4f4cef90a0b67a2bfd84cd61dcc771597f52656d", - "signatures": [ - "005afa0050c85a2ac9b34accb0f47c6a9cc9eee831ac713e62e846898d1b75d6a33034680bce95f638c6dfabf8056f8afa9ef73a3c35741234faf01d0a346e3e7d", - "0104bd019e5a6ea9ee5cc41d9f39efe2a2df2cc653369fdfcb56d5219fa282a8368067586748feb2483883c9eca50a5ae73abd7139d4d1885914ed9d5e5c63f8fb", - "02d6af0f5a85a7967d677b2f1f86e00b8ca37facb714467e12810a692d5bcbdfcac4e4c2d4b79a70c7366405339bf84a854308d20cb48652816a9cf37fb3e86e00" - ], - "id": "dcb9d29590313cf6e1b53f120e621e34c39e45fce9114272158036cb99be9c89" - }, - "serialized": "ff011e0100000000000100000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3809698000000000000010000000000000000000000b693449adda7efc015d87944eae8b7c37eb1690af25db2b781b79f671b7848284de63f3cb898f9938c0b6203a11cace4aaa4b962eabe9c4a67c207a3f8baea3b4f4cef90a0b67a2bfd84cd61dcc771597f52656d005afa0050c85a2ac9b34accb0f47c6a9cc9eee831ac713e62e846898d1b75d6a33034680bce95f638c6dfabf8056f8afa9ef73a3c35741234faf01d0a346e3e7d0104bd019e5a6ea9ee5cc41d9f39efe2a2df2cc653369fdfcb56d5219fa282a8368067586748feb2483883c9eca50a5ae73abd7139d4d1885914ed9d5e5c63f8fb02d6af0f5a85a7967d677b2f1f86e00b8ca37facb714467e12810a692d5bcbdfcac4e4c2d4b79a70c7366405339bf84a854308d20cb48652816a9cf37fb3e86e00" -} diff --git a/src/test/resources/transactions/transfer/transfer-sign.json b/src/test/resources/transactions/transfer/transfer-sign.json deleted file mode 100644 index c1032bcf..00000000 --- a/src/test/resources/transactions/transfer/transfer-sign.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 0, - "nonce": "1", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "10000000", - "amount": "1", - "expiration": 0, - "recipientId": "0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A", - "signature": "97dd98ed1066aab76011114cbf5ac00d741812cac326cda7966e1af08874d7212f350d769221d2a78ea185d77ac4e2c5c718e4a84b1a1ddcbf504d642eb94b6a", - "id": "e20265dba26594c4202e224e8d1069dfae840a8db7cc6acfafefa2b3b02787e5" - }, - "serialized": "ff011e0100000000000100000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3809698000000000000010000000000000000000000b693449adda7efc015d87944eae8b7c37eb1690a97dd98ed1066aab76011114cbf5ac00d741812cac326cda7966e1af08874d7212f350d769221d2a78ea185d77ac4e2c5c718e4a84b1a1ddcbf504d642eb94b6a" -} diff --git a/src/test/resources/transactions/transfer/transfer-with-vendor-field-sign.json b/src/test/resources/transactions/transfer/transfer-with-vendor-field-sign.json deleted file mode 100644 index f2f57e11..00000000 --- a/src/test/resources/transactions/transfer/transfer-with-vendor-field-sign.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 0, - "nonce": "1", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "10000000", - "amount": "1", - "vendorField": "this is a top secret vendor field", - "expiration": 0, - "recipientId": "0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A", - "signature": "d7062dd749406741ab848cc0bb6bcc82c04d8b46b699803154a30bda8d13bd6dc6c06fcbbb6a6edab3b83772008e50524b4563a33c0f08009b4cc72f38986981", - "id": "59cc59e3609f86418ef04fff270fb0f59ea2ff5a383857a3c157056f0925d59d" - }, - "serialized": "ff011e0100000000000100000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d380969800000000002174686973206973206120746f70207365637265742076656e646f72206669656c64010000000000000000000000b693449adda7efc015d87944eae8b7c37eb1690ad7062dd749406741ab848cc0bb6bcc82c04d8b46b699803154a30bda8d13bd6dc6c06fcbbb6a6edab3b83772008e50524b4563a33c0f08009b4cc72f38986981" -} diff --git a/src/test/resources/transactions/unvote.json b/src/test/resources/transactions/unvote.json new file mode 100644 index 00000000..d5e3a7cf --- /dev/null +++ b/src/test/resources/transactions/unvote.json @@ -0,0 +1,16 @@ +{ + "data": { + "network": 30, + "nonce": "13", + "gasPrice": 5, + "gasLimit": 200000, + "value": "0", + "recipientAddress": "0x522B3294E6d06aA25Ad0f1B8891242E335D3B459", + "data": "3174b689", + "signature": "d7534ec92c06a8547d0f2b3d3259dff5b0b17f8673d68dff9af023009c9c450e24205cb5f4fd6165d71c8b3ba3e9f741d1853110d44bd1e798e87f1a5d6a89c501", + "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", + "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", + "id": "92ca281a6699a4eb08e8e5c4a644c216026f6c6d3560611c50cab54d1300b690" + }, + "serialized": "1e0d0000000000000005000000400d0300000000000000000000000000000000000000000000000000000000000000000001522b3294e6d06aa25ad0f1b8891242e335d3b459040000003174b689d7534ec92c06a8547d0f2b3d3259dff5b0b17f8673d68dff9af023009c9c450e24205cb5f4fd6165d71c8b3ba3e9f741d1853110d44bd1e798e87f1a5d6a89c501" +} diff --git a/src/test/resources/transactions/username_registration/username-registration-multi-sign.json b/src/test/resources/transactions/username_registration/username-registration-multi-sign.json deleted file mode 100644 index 9f69e174..00000000 --- a/src/test/resources/transactions/username_registration/username-registration-multi-sign.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 8, - "nonce": "6", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "2500000000", - "amount": "0", - "asset": { - "username": "simple_tx_tester" - }, - "signature": "9a74c959d0cd4ad4352292417de3ed8ed0cac9aad390bb5d93a5ef5d2bdbf54dad9bbf690f2079ad397141acd182b6e5c4285bd64b068083431daffd5dbc5b09", - "signatures": [ - "003b5b94d80da3d41f514d77a624f26f7a2336c34ce1ec62f06c61635db31b27135a58b9ee705cbaa45addd6e75098833c80935541ccc050199443a3d4c62c7fdd", - "01335feff7cc3d6e3524dd9892e029d2dea77c76afdeb9445bc0d14c587fd922c6c38e3542b978aeb370dc349b4a1221015f33051407f10ca051a1caa2109fc510", - "02fe8b1d0be5a77455b38f9df31a379e1a80bcab4003a225cbf79d426fcd248e03ff5f499a404d879270086e292aa45ac1af979aa9af4a64e4881ec559fc87e779" - ], - "id": "ffaead0f6003e125bb2a66169e87efe29c05dedaec71898abfcbc7a1f846f2b3" - }, - "serialized": "ff011e0100000008000600000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d300f9029500000000001073696d706c655f74785f7465737465729a74c959d0cd4ad4352292417de3ed8ed0cac9aad390bb5d93a5ef5d2bdbf54dad9bbf690f2079ad397141acd182b6e5c4285bd64b068083431daffd5dbc5b09003b5b94d80da3d41f514d77a624f26f7a2336c34ce1ec62f06c61635db31b27135a58b9ee705cbaa45addd6e75098833c80935541ccc050199443a3d4c62c7fdd01335feff7cc3d6e3524dd9892e029d2dea77c76afdeb9445bc0d14c587fd922c6c38e3542b978aeb370dc349b4a1221015f33051407f10ca051a1caa2109fc51002fe8b1d0be5a77455b38f9df31a379e1a80bcab4003a225cbf79d426fcd248e03ff5f499a404d879270086e292aa45ac1af979aa9af4a64e4881ec559fc87e779" -} diff --git a/src/test/resources/transactions/username_registration/username-registration-sign.json b/src/test/resources/transactions/username_registration/username-registration-sign.json deleted file mode 100644 index f40a509d..00000000 --- a/src/test/resources/transactions/username_registration/username-registration-sign.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 8, - "nonce": "4", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "2500000000", - "amount": "0", - "asset": { - "username": "simple_tx_tester" - }, - "signature": "70014805da364407f71ca7e09dae5a685af562b985279e370d1a936ebbe7c2545dca35b0f0f30b72eaee0ca042beb6ef2cab5666dd42831a7d0dacac3d962563", - "id": "a50b909f5b483ef1a9bebff66582de169782d3a8adc3e8fca7b183c39738c721" - }, - "serialized": "ff011e0100000008000400000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d300f9029500000000001073696d706c655f74785f74657374657270014805da364407f71ca7e09dae5a685af562b985279e370d1a936ebbe7c2545dca35b0f0f30b72eaee0ca042beb6ef2cab5666dd42831a7d0dacac3d962563" -} diff --git a/src/test/resources/transactions/username_resignation/username-resignation-multi-sign.json b/src/test/resources/transactions/username_resignation/username-resignation-multi-sign.json deleted file mode 100644 index bc2d7e17..00000000 --- a/src/test/resources/transactions/username_resignation/username-resignation-multi-sign.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 9, - "nonce": "6", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "2500000000", - "amount": "0", - "signature": "730f5d46f0ee978f5437899cea0912a87d9854f384a94f3b5b88fbdd527573bb6c6ed13b3da0a695e41efe2b14bafd587db4563b8f29e10f78736959250e8414", - "signatures": [ - "00fe3edd3082e95f42177e6f786f22e3df982e1b85446f9291b6effcdbfe311ec7b9d4df7e68a36ddb6afe04564586c9a3c4245d47077bda91c1fda53d294d7317", - "014e0ca11609da9444233dd13a3561c4ac6f779a177389b498749d70076126b1081d6bab0106938b2431c04c8b4cecb78f8d348ba81e7a03eee5f9fc9b0f1a3131", - "023a7826b28719baf87f4a3a5a10b51d2ce26172a01d92d41691edf7630fd5bd51d9ace6fdfa1361c7a0834d6a6017c3fba4f642fcb2a49e251fbcf6464b5256cc" - ], - "id": "d0210c375d32eec25ca9513099b98e559c3acb136d08011874149217e17ff8f1" - }, - "serialized": "ff011e0100000009000600000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d300f902950000000000730f5d46f0ee978f5437899cea0912a87d9854f384a94f3b5b88fbdd527573bb6c6ed13b3da0a695e41efe2b14bafd587db4563b8f29e10f78736959250e841400fe3edd3082e95f42177e6f786f22e3df982e1b85446f9291b6effcdbfe311ec7b9d4df7e68a36ddb6afe04564586c9a3c4245d47077bda91c1fda53d294d7317014e0ca11609da9444233dd13a3561c4ac6f779a177389b498749d70076126b1081d6bab0106938b2431c04c8b4cecb78f8d348ba81e7a03eee5f9fc9b0f1a3131023a7826b28719baf87f4a3a5a10b51d2ce26172a01d92d41691edf7630fd5bd51d9ace6fdfa1361c7a0834d6a6017c3fba4f642fcb2a49e251fbcf6464b5256cc" -} diff --git a/src/test/resources/transactions/username_resignation/username-resignation-sign.json b/src/test/resources/transactions/username_resignation/username-resignation-sign.json deleted file mode 100644 index 9209da01..00000000 --- a/src/test/resources/transactions/username_resignation/username-resignation-sign.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 9, - "nonce": "4", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "2500000000", - "amount": "0", - "signature": "72b0322b72019f688fc21f367d68b38828c6e3966e52b3fd46f0813cc7c2cdfd58097c4eef8b52896aba1300098e20a38659529b45db9dfca7aa23d560457769", - "id": "1f90e26793dd55b6b2464002dc537882df94199249ba0bd07fcb4512851ed22b" - }, - "serialized": "ff011e0100000009000400000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d300f90295000000000072b0322b72019f688fc21f367d68b38828c6e3966e52b3fd46f0813cc7c2cdfd58097c4eef8b52896aba1300098e20a38659529b45db9dfca7aa23d560457769" -} diff --git a/src/test/resources/transactions/validator-registration.json b/src/test/resources/transactions/validator-registration.json new file mode 100644 index 00000000..170909cc --- /dev/null +++ b/src/test/resources/transactions/validator-registration.json @@ -0,0 +1,16 @@ +{ + "data": { + "network": 30, + "nonce": "12", + "gasPrice": 5, + "gasLimit": 500000, + "value": "0", + "recipientAddress": "0x522B3294E6d06aA25Ad0f1B8891242E335D3B459", + "data": "602a9eee00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030a08058db53e2665c84a40f5152e76dd2b652125a6079130d4c315e728bcf4dd1dfb44ac26e82302331d61977d314111800000000000000000000000000000000", + "signature": "91b2ca61808b94392afa151ee893784a5221ab27b8fdf5871cc17c75e87acca8396530b2f320641326f00199478552e673d124406b44bcbe6075966016658d2201", + "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", + "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", + "id": "3457dfd59d42a174feb30a1aac757e54caddd87d21e6483386a3440cc0fa6c5f" + }, + "serialized": "1e0c000000000000000500000020a10700000000000000000000000000000000000000000000000000000000000000000001522b3294e6d06aa25ad0f1b8891242e335d3b45984000000602a9eee00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030a08058db53e2665c84a40f5152e76dd2b652125a6079130d4c315e728bcf4dd1dfb44ac26e82302331d61977d31411180000000000000000000000000000000091b2ca61808b94392afa151ee893784a5221ab27b8fdf5871cc17c75e87acca8396530b2f320641326f00199478552e673d124406b44bcbe6075966016658d2201" +} diff --git a/src/test/resources/transactions/validator-resignation.json b/src/test/resources/transactions/validator-resignation.json new file mode 100644 index 00000000..77a37778 --- /dev/null +++ b/src/test/resources/transactions/validator-resignation.json @@ -0,0 +1,16 @@ +{ + "data": { + "network": 30, + "nonce": "12", + "gasPrice": 5, + "gasLimit": 150000, + "value": "0", + "recipientAddress": "0x522B3294E6d06aA25Ad0f1B8891242E335D3B459", + "data": "b85f5da2", + "signature": "94fd248dc5984b56be6c9661c5a32fa062fb21af62b1474a33d985302f9bda8a044c30e4feb1f06da437c15d9e997816aa3233b3f142cd780e1ff69b80269d0d00", + "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", + "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", + "id": "ab469546888715725add275778bcf0c1dd68afc163b48018e22a044db718e5b9" + }, + "serialized": "1e0c0000000000000005000000f0490200000000000000000000000000000000000000000000000000000000000000000001522b3294e6d06aa25ad0f1b8891242e335d3b45904000000b85f5da294fd248dc5984b56be6c9661c5a32fa062fb21af62b1474a33d985302f9bda8a044c30e4feb1f06da437c15d9e997816aa3233b3f142cd780e1ff69b80269d0d00" +} diff --git a/src/test/resources/transactions/validator_registration/validator-registration-multi-sign.json b/src/test/resources/transactions/validator_registration/validator-registration-multi-sign.json deleted file mode 100644 index 7e7f3fc8..00000000 --- a/src/test/resources/transactions/validator_registration/validator-registration-multi-sign.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 2, - "nonce": "0", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "2500000000", - "amount": "0", - "asset": { - "validatorPublicKey": "a08058db53e2665c84a40f5152e76dd2b652125a6079130d4c315e728bcf4dd1dfb44ac26e82302331d61977d3141118" - }, - "signature": "0604c8ecb42e1cc125da7e88fbb6e8b3c3cb703890701b91f8277bf84493ac4994417e8c090309f8147316091d879a1a3c1ce15a6476e029c6f0597a5cfd3533", - "signatures": [ - "00ad621bb19f24c785eded7e3953b575e25fca5b26890eda8ac87cb029d8a28d2c2ef084d98d617fddf7677b27488705e3c5c6d6568a2c44753212e3589e93b89c", - "018de3507e97d8e8e88f77c2cac0c4a7b8767b8527b2a53d81a119e2dbf6672fc8595de1c9ddbb1c88fadf7266923b2289dfbd3b266059c77609bdec52e8efeb61", - "021cbf94035e39cf80b4ccb7ee7d9cfac747ab6e1f73ef4a68b5600ee48c1e94861076718d11ab3908ecc6252c0ea515f6586f966e50842846cce5eada78d84453" - ], - "id": "91c5b9e9dd0915a0e2a44edcae3cd0182ffcdcc3921721fc4a88e5b91a3a1d90" - }, - "serialized": "ff011e0100000002000000000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d300f902950000000000a08058db53e2665c84a40f5152e76dd2b652125a6079130d4c315e728bcf4dd1dfb44ac26e82302331d61977d31411180604c8ecb42e1cc125da7e88fbb6e8b3c3cb703890701b91f8277bf84493ac4994417e8c090309f8147316091d879a1a3c1ce15a6476e029c6f0597a5cfd353300ad621bb19f24c785eded7e3953b575e25fca5b26890eda8ac87cb029d8a28d2c2ef084d98d617fddf7677b27488705e3c5c6d6568a2c44753212e3589e93b89c018de3507e97d8e8e88f77c2cac0c4a7b8767b8527b2a53d81a119e2dbf6672fc8595de1c9ddbb1c88fadf7266923b2289dfbd3b266059c77609bdec52e8efeb61021cbf94035e39cf80b4ccb7ee7d9cfac747ab6e1f73ef4a68b5600ee48c1e94861076718d11ab3908ecc6252c0ea515f6586f966e50842846cce5eada78d84453" -} diff --git a/src/test/resources/transactions/validator_registration/validator-registration-sign.json b/src/test/resources/transactions/validator_registration/validator-registration-sign.json deleted file mode 100644 index efbd345d..00000000 --- a/src/test/resources/transactions/validator_registration/validator-registration-sign.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 2, - "nonce": "0", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "2500000000", - "amount": "0", - "asset": { - "validatorPublicKey": "a08058db53e2665c84a40f5152e76dd2b652125a6079130d4c315e728bcf4dd1dfb44ac26e82302331d61977d3141118" - }, - "signature": "449a02672bf54a67ecdbeab20d3fbc16a1358397ebbe28398338f97d9823752f0b3e6c715ce6ad47d8f4df95a7a1ef97b76d159513ba680edecf9e3dd4d76719", - "id": "18659c72ed03091989bf960450ab156d04794ea037357c2f4839d362a1ad576a" - }, - "serialized": "ff011e0100000002000000000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d300f902950000000000a08058db53e2665c84a40f5152e76dd2b652125a6079130d4c315e728bcf4dd1dfb44ac26e82302331d61977d3141118449a02672bf54a67ecdbeab20d3fbc16a1358397ebbe28398338f97d9823752f0b3e6c715ce6ad47d8f4df95a7a1ef97b76d159513ba680edecf9e3dd4d76719" -} diff --git a/src/test/resources/transactions/validator_resignation/validator-resignation-multi-sign.json b/src/test/resources/transactions/validator_resignation/validator-resignation-multi-sign.json deleted file mode 100644 index 9ce2df83..00000000 --- a/src/test/resources/transactions/validator_resignation/validator-resignation-multi-sign.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 7, - "nonce": "0", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "2500000000", - "amount": "0", - "signature": "a32aeb2cd0695a71f35953e2bac27b36ba51c6fb3f36b98a5e05072500a96206b3afc95efa47d2bbce291b12307c4bd5e79171930e13844a0bc2036516e26644", - "signatures": [ - "00e78bd07e68978b0bcc911c3b4cce17957a347b3ceda0fe78fca3624c6e9d752c46ba75d31004661d097bc6c2471f86fb6727ac9e385c931d2850fc1ad3eb98c0", - "01706c56c0a5568df17342bb0fffec66cbf948425153f31ee3f16760c372ae4e020f2489695c3f3a80524bbba6fa97aef074259e8dc9305966dfc1a40a632872d0", - "021e218f51b7803c327c092f89af015573371506dd8f27a96dacaf29286d8ecc5d955443cfb1a5d93f359ebef67fdb842fc3e9a8d9691e57a5f627cb11425526e0" - ], - "id": "97ff3cf77125fea2ce33f23ef4ce85d56e207bc47e1595c004a319976fdca64c" - }, - "serialized": "ff011e0100000007000000000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d300f902950000000000a32aeb2cd0695a71f35953e2bac27b36ba51c6fb3f36b98a5e05072500a96206b3afc95efa47d2bbce291b12307c4bd5e79171930e13844a0bc2036516e2664400e78bd07e68978b0bcc911c3b4cce17957a347b3ceda0fe78fca3624c6e9d752c46ba75d31004661d097bc6c2471f86fb6727ac9e385c931d2850fc1ad3eb98c001706c56c0a5568df17342bb0fffec66cbf948425153f31ee3f16760c372ae4e020f2489695c3f3a80524bbba6fa97aef074259e8dc9305966dfc1a40a632872d0021e218f51b7803c327c092f89af015573371506dd8f27a96dacaf29286d8ecc5d955443cfb1a5d93f359ebef67fdb842fc3e9a8d9691e57a5f627cb11425526e0" -} diff --git a/src/test/resources/transactions/validator_resignation/validator-resignation-sign.json b/src/test/resources/transactions/validator_resignation/validator-resignation-sign.json deleted file mode 100644 index 4492199d..00000000 --- a/src/test/resources/transactions/validator_resignation/validator-resignation-sign.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 7, - "nonce": "0", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "2500000000", - "amount": "0", - "signature": "f9d93dc161b5351a9e6f89e97bd3a4d1b73349c2339145021dee2654f5a8116aa78c6c3742b5fc39d0c9265735b866954d8c6c8383130d08659b1aff3967d4eb", - "id": "431b779764f51e227eca907706bd339de36c3b23bd46c603283f3a932d12b936" - }, - "serialized": "ff011e0100000007000000000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d300f902950000000000f9d93dc161b5351a9e6f89e97bd3a4d1b73349c2339145021dee2654f5a8116aa78c6c3742b5fc39d0c9265735b866954d8c6c8383130d08659b1aff3967d4eb" -} diff --git a/src/test/resources/transactions/vote.json b/src/test/resources/transactions/vote.json new file mode 100644 index 00000000..d7992974 --- /dev/null +++ b/src/test/resources/transactions/vote.json @@ -0,0 +1,16 @@ +{ + "data": { + "network": 30, + "nonce": "12", + "gasPrice": 5, + "gasLimit": 200000, + "value": "0", + "recipientAddress": "0x522B3294E6d06aA25Ad0f1B8891242E335D3B459", + "data": "6dd7d8ea000000000000000000000000512f366d524157bcf734546eb29a6d687b762255", + "signature": "e1fd7b0ddc466072e2eac37b73283e8303d80ceb2dd2d64a8d6cdf5866662bc5261a08ca2d64942b6bb93b42ed820f1c8c1c92ce2312d380cc83fea022bfc2f301", + "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", + "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", + "id": "749744e0d689c46e37ff2993a984599eac4989a9ef0028337b335c9d43abf936" + }, + "serialized": "1e0c0000000000000005000000400d0300000000000000000000000000000000000000000000000000000000000000000001522b3294e6d06aa25ad0f1b8891242e335d3b459240000006dd7d8ea000000000000000000000000512f366d524157bcf734546eb29a6d687b762255e1fd7b0ddc466072e2eac37b73283e8303d80ceb2dd2d64a8d6cdf5866662bc5261a08ca2d64942b6bb93b42ed820f1c8c1c92ce2312d380cc83fea022bfc2f301" +} diff --git a/src/test/resources/transactions/vote/unvote-sign.json b/src/test/resources/transactions/vote/unvote-sign.json deleted file mode 100644 index e169bdf6..00000000 --- a/src/test/resources/transactions/vote/unvote-sign.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 3, - "nonce": "1", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "100000000", - "amount": "0", - "asset": { - "unvotes": [ - "03f25455408f9a7e6c6a056b121e68fbda98f3511d22e9ef27b0ebaf1ef9e4eabc" - ], - "votes": [] - }, - "signature": "b7c16d70bd8503d3228d57dcb32468601e687849ebbbfe18b9ef2404ecf907f362a4e0a3326b6149d21256a77c7ba8ccbafddacc5bcb07c5f209e861a7f4cc66", - "id": "110a8a1bf940068cb555261f6715402623360bac6aebcdfde8c649d2a03b00db" - }, - "serialized": "ff011e0100000003000100000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d300e1f5050000000000000103f25455408f9a7e6c6a056b121e68fbda98f3511d22e9ef27b0ebaf1ef9e4eabcb7c16d70bd8503d3228d57dcb32468601e687849ebbbfe18b9ef2404ecf907f362a4e0a3326b6149d21256a77c7ba8ccbafddacc5bcb07c5f209e861a7f4cc66" -} diff --git a/src/test/resources/transactions/vote/vote-multi-sign.json b/src/test/resources/transactions/vote/vote-multi-sign.json deleted file mode 100644 index 1b9b0a55..00000000 --- a/src/test/resources/transactions/vote/vote-multi-sign.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 3, - "nonce": "1", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "100000000", - "amount": "0", - "asset": { - "votes": [ - "03f25455408f9a7e6c6a056b121e68fbda98f3511d22e9ef27b0ebaf1ef9e4eabc" - ] - }, - "signature": "72a097e05983575a69ab7c2c15d80beea988fc02e08124a969b281fa09d1e47ff0b36706f9a9bb6b2e18db929f21b08571c4be36fe2882fecfb990e465bfebde", - "signatures": [ - "0035173f507953f5a7f9da525384b9a1021b553c1089f060e6e77118ab5aa68549ea89d4b63391941e95c0142dd195fd8c032c0a9aca9e1a72943dde8ecd663070", - "010dba0e40f903c7ec76e1f1e7ef3c145e69a878b13e833d132b9f1fa267ba3b2ab049941ede13faee3da9611ac27cb52a6888bbd093a253e7d7bea4c633d4a706", - "02556654e210ed6426d20c0fe246d3270bcdea97d5ab59f5c90888731b079ff83ab53cac3bc80680f093f2e5a78216a0ed894c6985bd89b3802b2d8ff3f6b4c584" - ], - "id": "e344602ba9fe82d035af6c8b759682bab0317ca523212724542217803c093adb" - }, - "serialized": "ff011e0100000003000100000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d300e1f50500000000000103f25455408f9a7e6c6a056b121e68fbda98f3511d22e9ef27b0ebaf1ef9e4eabc0072a097e05983575a69ab7c2c15d80beea988fc02e08124a969b281fa09d1e47ff0b36706f9a9bb6b2e18db929f21b08571c4be36fe2882fecfb990e465bfebde0035173f507953f5a7f9da525384b9a1021b553c1089f060e6e77118ab5aa68549ea89d4b63391941e95c0142dd195fd8c032c0a9aca9e1a72943dde8ecd663070010dba0e40f903c7ec76e1f1e7ef3c145e69a878b13e833d132b9f1fa267ba3b2ab049941ede13faee3da9611ac27cb52a6888bbd093a253e7d7bea4c633d4a70602556654e210ed6426d20c0fe246d3270bcdea97d5ab59f5c90888731b079ff83ab53cac3bc80680f093f2e5a78216a0ed894c6985bd89b3802b2d8ff3f6b4c584" -} diff --git a/src/test/resources/transactions/vote/vote-sign.json b/src/test/resources/transactions/vote/vote-sign.json deleted file mode 100644 index 0f2b988a..00000000 --- a/src/test/resources/transactions/vote/vote-sign.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "data": { - "version": 1, - "network": 30, - "typeGroup": 1, - "type": 3, - "nonce": "1", - "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", - "fee": "100000000", - "amount": "0", - "asset": { - "votes": [ - "03f25455408f9a7e6c6a056b121e68fbda98f3511d22e9ef27b0ebaf1ef9e4eabc" - ] - }, - "signature": "e0a99c2ebd108c6ba1828f4d11e5ce8cdf2e67152575cabea2e86ab59153a1f1493b768af6dfe0a33eec22edf2dd87c25e1531bf285bb4131e4fcdbfce4c8781", - "id": "81bffacc80ae1c3b3f127cbaf5d2867d3209c38610d2cc5a341702fc252cd330" - }, - "serialized": "ff011e0100000003000100000000000000023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d300e1f50500000000000103f25455408f9a7e6c6a056b121e68fbda98f3511d22e9ef27b0ebaf1ef9e4eabc00e0a99c2ebd108c6ba1828f4d11e5ce8cdf2e67152575cabea2e86ab59153a1f1493b768af6dfe0a33eec22edf2dd87c25e1531bf285bb4131e4fcdbfce4c8781" -}