unconfirmedTransactions = TransactionImporter.getInstance().unconfirmedTransactionsCache;
+ if (unconfirmedTransactions == null) {
+ unconfirmedTransactions = repository.getTransactionRepository().getUnconfirmedTransactions();
+ }
// We exclude CHAT transactions as they never get included into blocks and
// have spam/DoS prevention by requiring proof of work
@@ -632,7 +637,7 @@ public abstract class Transaction {
}
/**
- * Returns sorted, unconfirmed transactions, excluding invalid.
+ * Returns sorted, unconfirmed transactions, excluding invalid and unconfirmable.
*
* @return sorted, unconfirmed transactions
* @throws DataException
@@ -650,7 +655,8 @@ public abstract class Transaction {
TransactionData transactionData = unconfirmedTransactionsIterator.next();
Transaction transaction = Transaction.fromData(repository, transactionData);
- if (transaction.isStillValidUnconfirmed(latestBlockData.getTimestamp()) != ValidationResult.OK)
+ // Must be confirmable and valid
+ if (!transaction.isConfirmable() || transaction.isStillValidUnconfirmed(latestBlockData.getTimestamp()) != ValidationResult.OK)
unconfirmedTransactionsIterator.remove();
}
@@ -888,6 +894,17 @@ public abstract class Transaction {
/* To be optionally overridden */
}
+ /**
+ * Returns whether transaction is 'confirmable' - i.e. is of a type that
+ * can be included in a block. Some transactions are 'unconfirmable'
+ * and therefore must remain in the mempool until they expire.
+ * @return
+ */
+ public boolean isConfirmable() {
+ /* To be optionally overridden */
+ return true;
+ }
+
/**
* Returns whether transaction can be added to the blockchain.
*
diff --git a/src/main/resources/blockchain.json b/src/main/resources/blockchain.json
index c6151204..760da97c 100644
--- a/src/main/resources/blockchain.json
+++ b/src/main/resources/blockchain.json
@@ -3,8 +3,12 @@
"transactionExpiryPeriod": 86400000,
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
- "unitFee": "0.001",
+ "unitFees": [
+ { "timestamp": 0, "fee": "0.001" },
+ { "timestamp": 1692118800000, "fee": "0.01" }
+ ],
"nameRegistrationUnitFees": [
+ { "timestamp": 0, "fee": "0.001" },
{ "timestamp": 1645372800000, "fee": "5" },
{ "timestamp": 1651420800000, "fee": "1.25" }
],
@@ -18,13 +22,15 @@
"maxRewardSharesPerFounderMintingAccount": 6,
"maxRewardSharesByTimestamp": [
{ "timestamp": 0, "maxShares": 6 },
- { "timestamp": 1657382400000, "maxShares": 3 }
+ { "timestamp": 1657382400000, "maxShares": 3 },
+ { "timestamp": 1698508800000, "maxShares": 2 }
],
"founderEffectiveMintingLevel": 10,
"onlineAccountSignaturesMinLifetime": 43200000,
"onlineAccountSignaturesMaxLifetime": 86400000,
"onlineAccountsModulusV2Timestamp": 1659801600000,
"selfSponsorshipAlgoV1SnapshotTimestamp": 1670230000000,
+ "mempowTransactionUpdatesTimestamp": 1693558800000,
"rewardsByHeight": [
{ "height": 1, "reward": 5.00 },
{ "height": 259201, "reward": 4.75 },
diff --git a/src/main/resources/i18n/ApiError_de.properties b/src/main/resources/i18n/ApiError_de.properties
index 8f5bffeb..f00c2b45 100644
--- a/src/main/resources/i18n/ApiError_de.properties
+++ b/src/main/resources/i18n/ApiError_de.properties
@@ -4,52 +4,52 @@
# "localeLang": "de",
### Common ###
-JSON = JSON Nachricht konnte nicht geparst werden
+JSON = JSON-Nachricht konnte nicht geparst werden
-INSUFFICIENT_BALANCE = Kein Ausgleich
+INSUFFICIENT_BALANCE = Guthaben reicht nicht aus
UNAUTHORIZED = API-Aufruf nicht autorisiert
REPOSITORY_ISSUE = Repository-Fehler
-NON_PRODUCTION = Dieser APi-Aufruf ist nicht gestattet für Produtkion
+NON_PRODUCTION = dieser API-Aufruf ist für Produktionssysteme nicht gestattet
-BLOCKCHAIN_NEEDS_SYNC = Blockchain muss sich erst verbinden
+BLOCKCHAIN_NEEDS_SYNC = Blockchain muss sich erst synchronisieren
-NO_TIME_SYNC = noch keine Uhrensynchronisation
+NO_TIME_SYNC = Uhrzeit noch nicht synchronisiert
### Validation ###
-INVALID_SIGNATURE = ungültige Signatur
+INVALID_SIGNATURE = Signatur ungültig
-INVALID_ADDRESS = ungültige Adresse
+INVALID_ADDRESS = Adresse ungültig
-INVALID_PUBLIC_KEY = ungültiger public key
+INVALID_PUBLIC_KEY = öffentlicher Schlüssel ungültig
-INVALID_DATA = ungültige Daten
+INVALID_DATA = Daten ungültig
-INVALID_NETWORK_ADDRESS = ungültige Netzwerk Adresse
+INVALID_NETWORK_ADDRESS = Netzwerk Adresse ungültig
-ADDRESS_UNKNOWN = Account Adresse unbekannt
+ADDRESS_UNKNOWN = Kontoadresse unbekannt
-INVALID_CRITERIA = ungültige Suchkriterien
+INVALID_CRITERIA = Suchkriterien ungültig
-INVALID_REFERENCE = ungültige Referenz
+INVALID_REFERENCE = Referenz ungültig
TRANSFORMATION_ERROR = konnte JSON nicht in eine Transaktion umwandeln
-INVALID_PRIVATE_KEY = ungültiger private key
+INVALID_PRIVATE_KEY = öffentlicher Schlüssel ungültig
-INVALID_HEIGHT = ungültige block height
+INVALID_HEIGHT = Blockhöhe ungültig
-CANNOT_MINT = Account kann nicht minten
+CANNOT_MINT = Konto kann nicht prägen
### Blocks ###
-BLOCK_UNKNOWN = block unbekannt
+BLOCK_UNKNOWN = Block unbekannt
### Transactions ###
TRANSACTION_UNKNOWN = Transaktion unbekannt
-PUBLIC_KEY_NOT_FOUND = public key wurde nicht gefunden
+PUBLIC_KEY_NOT_FOUND = öffentlicher Schlüssel wurde nicht gefunden
# this one is special in that caller expected to pass two additional strings, hence the two %s
TRANSACTION_INVALID = Transaktion ungültig: %s (%s)
@@ -58,19 +58,19 @@ TRANSACTION_INVALID = Transaktion ungültig: %s (%s)
NAME_UNKNOWN = Name unbekannt
### Asset ###
-INVALID_ASSET_ID = ungültige asset ID
+INVALID_ASSET_ID = Vermögenswert-Kennung ungültig
-INVALID_ORDER_ID = ungültige asset order ID
+INVALID_ORDER_ID = Vermögenswert-Auftragssnummer ungültig
-ORDER_UNKNOWN = unbekannte asset order ID
+ORDER_UNKNOWN = Vermögenswert-Auftragssnummer unbekannt
### Groups ###
GROUP_UNKNOWN = Gruppe unbekannt
### Foreign Blockchain ###
-FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = fremde Blockchain oder ElectrumX Netzwerk Problem
+FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = fremde Blockchain oder ElectrumX Netzwerkproblem
-FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = unzureichend Bilanz auf fremde blockchain
+FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = unzureichendes Guthaben auf fremder blockchain
FOREIGN_BLOCKCHAIN_TOO_SOON = zu früh um fremde Blockchain-Transaktionen zu übertragen (Sperrzeit/mittlere Blockzeit)
@@ -80,4 +80,4 @@ ORDER_SIZE_TOO_SMALL = Bestellmenge zu niedrig
### Data ###
FILE_NOT_FOUND = Datei nicht gefunden
-NO_REPLY = Peer hat nicht mit Daten verbinden
+NO_REPLY = Peer hat nicht in vorgegebener Zeit geantwortet
diff --git a/src/main/resources/i18n/ApiError_nl.properties b/src/main/resources/i18n/ApiError_nl.properties
index 0501fe9a..8c8a3b09 100644
--- a/src/main/resources/i18n/ApiError_nl.properties
+++ b/src/main/resources/i18n/ApiError_nl.properties
@@ -4,36 +4,36 @@
# "localeLang": "nl",
### Common ###
-JSON = lezen van JSON bericht gefaald
+JSON = lezen van JSON bericht is mislukt
-INSUFFICIENT_BALANCE = insufficient balance
+INSUFFICIENT_BALANCE = onvoldoende saldo
UNAUTHORIZED = ongeautoriseerde API call
-REPOSITORY_ISSUE = repository fout
+REPOSITORY_ISSUE = fout in repository
NON_PRODUCTION = deze API call is niet toegestaan voor productiesystemen
-BLOCKCHAIN_NEEDS_SYNC = blockchain dient eerst gesynchronizeerd te worden
+BLOCKCHAIN_NEEDS_SYNC = blockchain dient eerst te synchronizeren
-NO_TIME_SYNC = klok nog niet gesynchronizeerd
+NO_TIME_SYNC = klok is nog niet gesynchronizeerd
### Validation ###
-INVALID_SIGNATURE = ongeldige handtekening
+INVALID_SIGNATURE = ongeldige signature
INVALID_ADDRESS = ongeldig adres
INVALID_PUBLIC_KEY = ongeldige public key
-INVALID_DATA = ongeldige gegevens
+INVALID_DATA = ongeldige data
INVALID_NETWORK_ADDRESS = ongeldig netwerkadres
-ADDRESS_UNKNOWN = account adres onbekend
+ADDRESS_UNKNOWN = account-adres onbekend
INVALID_CRITERIA = ongeldige zoekcriteria
-INVALID_REFERENCE = ongeldige verwijzing
+INVALID_REFERENCE = ongeldige referentie
TRANSFORMATION_ERROR = JSON kon niet omgezet worden in transactie
@@ -44,10 +44,10 @@ INVALID_HEIGHT = ongeldige blokhoogte
CANNOT_MINT = account kan niet minten
### Blocks ###
-BLOCK_UNKNOWN = blok onbekend
+BLOCK_UNKNOWN = blok niet gekend
### Transactions ###
-TRANSACTION_UNKNOWN = onbekende transactie
+TRANSACTION_UNKNOWN = transactie niet gekend
PUBLIC_KEY_NOT_FOUND = public key niet gevonden
@@ -55,29 +55,29 @@ PUBLIC_KEY_NOT_FOUND = public key niet gevonden
TRANSACTION_INVALID = ongeldige transactie: %s (%s)
### Naming ###
-NAME_UNKNOWN = onbekende naam
+NAME_UNKNOWN = naam niet gekend
### Asset ###
INVALID_ASSET_ID = ongeldige asset ID
INVALID_ORDER_ID = ongeldige asset order ID
-ORDER_UNKNOWN = onbekende asset order ID
+ORDER_UNKNOWN = niet gekende asset order ID
### Groups ###
-GROUP_UNKNOWN = onbekende groep
+GROUP_UNKNOWN = groep niet gekend
### Foreign Blockchain ###
-FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = blockchain of ElectrumX network probleem
+FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = vreemde blockchain of ElectrumX networkprobleem
-FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = onvoldoende saldo blockchain
+FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = onvoldoende saldo bij vreemde blockchain
-FOREIGN_BLOCKCHAIN_TOO_SOON = nog niet gereed om de blockchain transactie uittevoeren (LockTime/median block time)
+FOREIGN_BLOCKCHAIN_TOO_SOON = te vroeg om de blockchain transactie uit te sturen (LockTime/median block time)
### Trade Portal ###
-ORDER_SIZE_TOO_SMALL = order bedrag te laag
+ORDER_SIZE_TOO_SMALL = order-bedrag te laag
### Data ###
-FILE_NOT_FOUND = file niet gevonden
+FILE_NOT_FOUND = bestand niet gevonden
-NO_REPLY = peer reageerd niet met data
+NO_REPLY = peer reageerde niet binnen toegelaten tijd
diff --git a/src/main/resources/i18n/SysTray_de.properties b/src/main/resources/i18n/SysTray_de.properties
index 7fa041b3..c92130f1 100644
--- a/src/main/resources/i18n/SysTray_de.properties
+++ b/src/main/resources/i18n/SysTray_de.properties
@@ -1,7 +1,7 @@
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
# SysTray pop-up menu
-APPLYING_UPDATE_AND_RESTARTING = Automatisches Update anwenden und neu starten …
+APPLYING_UPDATE_AND_RESTARTING = Automatisches Update anwenden und neu starten...
AUTO_UPDATE = Automatisches Update
@@ -19,7 +19,7 @@ CONNECTION = Verbindung
CONNECTIONS = Verbindungen
-CREATING_BACKUP_OF_DB_FILES = Erstellen Backup von Datenbank Dateien …
+CREATING_BACKUP_OF_DB_FILES = Erstelle Backup von Datenbank Dateien...
DB_BACKUP = Datenbank Backup
@@ -31,18 +31,18 @@ EXIT = Verlassen
LITE_NODE = Lite node
-MINTING_DISABLED = Kein minting
+MINTING_DISABLED = Münzprägung inaktiv
-MINTING_ENABLED = \u2714 Minting aktiviert
+MINTING_ENABLED = \u2714 Münzprägung aktiv
-OPEN_UI = Öffne UI
+OPEN_UI = Öffne Benutzeroberfläche
-PERFORMING_DB_CHECKPOINT = Speichern von unbestätigten Datenbankänderungen...
+PERFORMING_DB_CHECKPOINT = Speichere unerfasste Datenbankänderungen...
-PERFORMING_DB_MAINTENANCE = Planmäßige Wartung durchführen...
+PERFORMING_DB_MAINTENANCE = Planmäßige Wartung wird durchgeführt...
-SYNCHRONIZE_CLOCK = Synchronisiere Uhr
+SYNCHRONIZE_CLOCK = Synchronisiere Uhrzeit
-SYNCHRONIZING_BLOCKCHAIN = Synchronisierung der Blockchain
+SYNCHRONIZING_BLOCKCHAIN = Synchronisiere
-SYNCHRONIZING_CLOCK = Synchronisierung der Uhr
+SYNCHRONIZING_CLOCK = Uhrzeit wird synchronisiert
diff --git a/src/main/resources/i18n/SysTray_nl.properties b/src/main/resources/i18n/SysTray_nl.properties
index c2acb7ce..3d7de024 100644
--- a/src/main/resources/i18n/SysTray_nl.properties
+++ b/src/main/resources/i18n/SysTray_nl.properties
@@ -1,17 +1,17 @@
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
# SysTray pop-up menu
-APPLYING_UPDATE_AND_RESTARTING = Automatische update en herstart worden uitgevoerd...
+APPLYING_UPDATE_AND_RESTARTING = Bezig met automatische update en herstart...
AUTO_UPDATE = Automatische Update
-BLOCK_HEIGHT = Block hoogte
+BLOCK_HEIGHT = blok-hoogte
-BLOCKS_REMAINING = blocks remaining
+BLOCKS_REMAINING = overblijvende blokken
-BUILD_VERSION = Versie nummer
+BUILD_VERSION = Versienummer
-CHECK_TIME_ACCURACY = Controleer accuraatheid van de tijd
+CHECK_TIME_ACCURACY = Controleer of de tijd correct is
CONNECTING = Verbinden
@@ -33,16 +33,16 @@ LITE_NODE = Lite node
MINTING_DISABLED = Minten is uitgeschakeld
-MINTING_ENABLED = \u2714 Minten is ingeschakeld
+MINTING_ENABLED = \u2714 Minten is actief
OPEN_UI = Open UI
-PERFORMING_DB_CHECKPOINT = De database wordt gecontroleerd...
+PERFORMING_DB_CHECKPOINT = De database wordt bijgewerkt...
-PERFORMING_DB_MAINTENANCE = Uitvoeren van gepland onderhoud...
+PERFORMING_DB_MAINTENANCE = Bezig met gepland onderhoud...
SYNCHRONIZE_CLOCK = Synchronizeer klok
-SYNCHRONIZING_BLOCKCHAIN = Aan het synchronizeren
+SYNCHRONIZING_BLOCKCHAIN = Bezig met synchronizeren
SYNCHRONIZING_CLOCK = Klok wordt gesynchronizeerd
diff --git a/src/main/resources/i18n/TransactionValidity_de.properties b/src/main/resources/i18n/TransactionValidity_de.properties
index 1827482b..eab7fb9e 100644
--- a/src/main/resources/i18n/TransactionValidity_de.properties
+++ b/src/main/resources/i18n/TransactionValidity_de.properties
@@ -1,60 +1,60 @@
#
-ACCOUNT_ALREADY_EXISTS = Account existiert bereits
+ACCOUNT_ALREADY_EXISTS = Konto existiert bereits
-ACCOUNT_CANNOT_REWARD_SHARE = Account kann keine Belohnung teilen
+ACCOUNT_CANNOT_REWARD_SHARE = Konto kann nicht an Belohnungsbeteiligung teilnehmen
-ADDRESS_ABOVE_RATE_LIMIT = address hat das angegebene Geschwindigkeitlimit erreicht
+ADDRESS_ABOVE_RATE_LIMIT = Adresse hat festgelegtes Tarif-Limit erreicht
-ADDRESS_BLOCKED = Addresse ist geblockt
+ADDRESS_BLOCKED = diese Adresse ist gesperrt
-ALREADY_GROUP_ADMIN = bereits Gruppen Admin
+ALREADY_GROUP_ADMIN = bereits Gruppenadmin
-ALREADY_GROUP_MEMBER = bereits Gruppen Mitglied
+ALREADY_GROUP_MEMBER = bereits Gruppenmitglied
ALREADY_VOTED_FOR_THAT_OPTION = bereits für diese Option gestimmt
-ASSET_ALREADY_EXISTS = asset existiert bereits
+ASSET_ALREADY_EXISTS = Vermögenswert existiert bereits
-ASSET_DOES_NOT_EXIST = asset nicht gefunden
+ASSET_DOES_NOT_EXIST = Vermögenswert existiert nicht
-ASSET_DOES_NOT_MATCH_AT = asset passt nicht mit AT's asset
+ASSET_DOES_NOT_MATCH_AT = Vermögenswert stimmt nicht mit dem Vermögenswert von AT überein
-ASSET_NOT_SPENDABLE = asset ist nicht ausgabefähig
+ASSET_NOT_SPENDABLE = Vermögenswert ist nicht auszahlbar
AT_ALREADY_EXISTS = AT existiert bereits
-AT_IS_FINISHED = AT ist fertig
+AT_IS_FINISHED = AT ist beendet
-AT_UNKNOWN = AT unbekannt
+AT_UNKNOWN = AT unbekannt
-BAN_EXISTS = ban besteht bereits
+BAN_EXISTS = Bann ist bereits vorhanden
-BAN_UNKNOWN = ban unbekannt
+BAN_UNKNOWN = Bann unbekannt
-BANNED_FROM_GROUP = von der gruppe gebannt
+BANNED_FROM_GROUP = aus der Gruppe verbannt
-BUYER_ALREADY_OWNER = Käufer ist bereits Besitzer
+BUYER_ALREADY_OWNER = Käufer ist bereits Eigentümer
-CLOCK_NOT_SYNCED = Uhr nicht synchronisiert
+CLOCK_NOT_SYNCED = Uhrzeit ist nicht synchronisiert
-DUPLICATE_MESSAGE = Adresse sendete doppelte Nachricht
+DUPLICATE_MESSAGE = Adresse hat doppelte Nachricht gesendet
-DUPLICATE_OPTION = Duplizierungsmöglichkeit
+DUPLICATE_OPTION = doppelte Option
-GROUP_ALREADY_EXISTS = Gruppe besteht bereits
+GROUP_ALREADY_EXISTS = Gruppe existiert bereits
-GROUP_APPROVAL_DECIDED = Gruppenfreigabe bereits beschlossen
+GROUP_APPROVAL_DECIDED = Gruppenzulassung bereits beschlossen
-GROUP_APPROVAL_NOT_REQUIRED = Gruppenfreigabe nicht erforderlich
+GROUP_APPROVAL_NOT_REQUIRED = Gruppenzustimmung nicht erforderlich
-GROUP_DOES_NOT_EXIST = Gruppe nicht vorhanden
+GROUP_DOES_NOT_EXIST = Gruppe existiert nicht
GROUP_ID_MISMATCH = Gruppen-ID stimmt nicht überein
-GROUP_OWNER_CANNOT_LEAVE = Gruppenbesitzer kann Gruppe nicht verlassen
+GROUP_OWNER_CANNOT_LEAVE = Gruppeneigentümer kann Gruppe nicht verlassen
-HAVE_EQUALS_WANT = das bessesene-asset ist das selbe wie das gesuchte-asset
+HAVE_EQUALS_WANT = Haben-Vermögenswert ist derselbe wie Wollen-Vermögenswert
INCORRECT_NONCE = falsche PoW-Nonce
@@ -64,81 +64,81 @@ INVALID_ADDRESS = ungültige Adresse
INVALID_AMOUNT = ungültiger Betrag
-INVALID_ASSET_OWNER = Ungültiger Eigentümer
+INVALID_ASSET_OWNER = ungültiger Vermögenswert-Eigentümer
INVALID_AT_TRANSACTION = ungültige AT-Transaktion
-INVALID_AT_TYPE_LENGTH = ungültige AT 'Typ' Länge
+INVALID_AT_TYPE_LENGTH = ungültige AT-Typ-Länge
-INVALID_BUT_OK = ungültig aber OK
+INVALID_BUT_OK = ungültig, aber OK
-INVALID_CREATION_BYTES = ungültige Erstellungs der bytes
+INVALID_CREATION_BYTES = ungültige Erstellungsbytes
-INVALID_DATA_LENGTH = ungültige Datenlänge
+INVALID_DATA_LENGTH = unzulässige Datenlänge
-INVALID_DESCRIPTION_LENGTH = ungültige Länge der Beschreibung
+INVALID_DESCRIPTION_LENGTH = unzulässige Länge der Beschreibung
INVALID_GROUP_APPROVAL_THRESHOLD = ungültiger Schwellenwert für die Gruppenzulassung
-INVALID_GROUP_BLOCK_DELAY = Ungültige Blockverzögerung der Gruppenfreigabe
+INVALID_GROUP_BLOCK_DELAY = ungültige Blockverzögerung bei der Gruppenfreigabe
INVALID_GROUP_ID = ungültige Gruppen-ID
-INVALID_GROUP_OWNER = ungültiger Gruppenbesitzer
+INVALID_GROUP_OWNER = ungültiger Gruppeneigentümer
-INVALID_LIFETIME = unzulässige Lebensdauer
+INVALID_LIFETIME = unzulässige Gültigkeitsdauer
INVALID_NAME_LENGTH = ungültige Namenslänge
-INVALID_NAME_OWNER = ungültiger Besitzername
+INVALID_NAME_OWNER = ungültiger Eigentümer des Namens
INVALID_OPTION_LENGTH = ungültige Länge der Optionen
-INVALID_OPTIONS_COUNT = Anzahl ungültiger Optionen
+INVALID_OPTIONS_COUNT = ungültige Anzahl von Optionen
INVALID_ORDER_CREATOR = ungültiger Auftragsersteller
-INVALID_PAYMENTS_COUNT = Anzahl ungültiger Zahlungen
+INVALID_PAYMENTS_COUNT = ungültige Anzahl der Zahlungen
INVALID_PUBLIC_KEY = ungültiger öffentlicher Schlüssel
-INVALID_QUANTITY = unzulässige Menge
+INVALID_QUANTITY = ungültige Menge
INVALID_REFERENCE = ungültige Referenz
INVALID_RETURN = ungültige Rückgabe
-INVALID_REWARD_SHARE_PERCENT = ungültig Prozent der Belohnunganteile
+INVALID_REWARD_SHARE_PERCENT = ungültiger Belohnungsbeteiligungs-Anteil
-INVALID_SELLER = unzulässiger Verkäufer
+INVALID_SELLER = ungültiger Verkäufer
INVALID_TAGS_LENGTH = ungültige 'tags'-Länge
-INVALID_TIMESTAMP_SIGNATURE = Ungültige Zeitstempel-Signatur
+INVALID_TIMESTAMP_SIGNATURE = ungültige Zeitstempel-Signatur
-INVALID_TX_GROUP_ID = Ungültige Transaktionsgruppen-ID
+INVALID_TX_GROUP_ID = ungültige Transaktionsgruppen-ID
-INVALID_VALUE_LENGTH = ungültige 'Wert'-Länge
+INVALID_VALUE_LENGTH = ungültige 'value'-Länge
INVITE_UNKNOWN = Gruppeneinladung unbekannt
-JOIN_REQUEST_EXISTS = Gruppeneinladung existiert bereits
+JOIN_REQUEST_EXISTS = Gruppenverbindungsanfrage existiert bereits
-MAXIMUM_REWARD_SHARES = die maximale Anzahl von Reward-Shares für dieses Konto erreicht
+MAXIMUM_REWARD_SHARES = maximale Anzahl von Belohnungsbeteiligungen für dieses Konto bereits erreicht
MISSING_CREATOR = fehlender Ersteller
-MULTIPLE_NAMES_FORBIDDEN = mehrere registrierte Namen pro Konto sind untersagt
+MULTIPLE_NAMES_FORBIDDEN = mehrere registrierte Namen pro Konto sind verboten
-NAME_ALREADY_FOR_SALE = Name bereits zum Verkauf
+NAME_ALREADY_FOR_SALE = Name steht bereits zum Verkauf
NAME_ALREADY_REGISTERED = Name bereits registriert
-NAME_BLOCKED = Name geblockt
+NAME_BLOCKED = dieser Name ist gesperrt
-NAME_DOES_NOT_EXIST = Name nicht vorhanden
+NAME_DOES_NOT_EXIST = Name existiert nicht
-NAME_NOT_FOR_SALE = Name ist unverkäuflich
+NAME_NOT_FOR_SALE = Name steht nicht zum Verkauf
NAME_NOT_NORMALIZED = Name nicht in Unicode-'normalisierter' Form
@@ -150,46 +150,46 @@ NEGATIVE_PRICE = ungültiger/negativer Preis
NO_BALANCE = unzureichendes Guthaben
-NO_BLOCKCHAIN_LOCK = die Blockchain des Knotens ist beschäftigt
+NO_BLOCKCHAIN_LOCK = die Blockchain des Knotens ist derzeit beschäftigt
NO_FLAG_PERMISSION = Konto hat diese Berechtigung nicht
-NOT_GROUP_ADMIN = Account ist kein Gruppenadmin
+NOT_GROUP_ADMIN = Konto ist kein Gruppenadmin
-NOT_GROUP_MEMBER = Account kein Gruppenmitglied
+NOT_GROUP_MEMBER = Konto ist kein Gruppenmitglied
-NOT_MINTING_ACCOUNT = Account kann nicht minten
+NOT_MINTING_ACCOUNT = Konto kann nicht prägen
NOT_YET_RELEASED = Funktion noch nicht freigegeben
OK = OK
-ORDER_ALREADY_CLOSED = Asset Trade Order ist bereits geschlossen
+ORDER_ALREADY_CLOSED = Vermögenswert-Handelsauftrag ist bereits geschlossen
-ORDER_DOES_NOT_EXIST = asset trade order existiert nicht
+ORDER_DOES_NOT_EXIST = Vermögenswert-Handelsauftrag existiert nicht
-POLL_ALREADY_EXISTS = Umfrage bereits vorhanden
+POLL_ALREADY_EXISTS = Umfrage existiert bereits
-POLL_DOES_NOT_EXIST = Umfrage nicht vorhanden
+POLL_DOES_NOT_EXIST = Umfrage existiert nicht
-POLL_OPTION_DOES_NOT_EXIST = Umfrageoption existiert nicht
+POLL_OPTION_DOES_NOT_EXIST = Umfrageoption nicht vorhanden
PUBLIC_KEY_UNKNOWN = öffentlicher Schlüssel unbekannt
-REWARD_SHARE_UNKNOWN = Geteilte Belohnungen unbekant
+REWARD_SHARE_UNKNOWN = Belohnungsbeteiligung unbekannt
-SELF_SHARE_EXISTS = Selbstbeteiligung (Geteilte Belohnungen) sind breits vorhanden
+SELF_SHARE_EXISTS = Selbst-Beteiligung (Belohnungsbeteiligung) existiert bereits
TIMESTAMP_TOO_NEW = Zeitstempel zu neu
TIMESTAMP_TOO_OLD = Zeitstempel zu alt
-TOO_MANY_UNCONFIRMED = Account hat zu viele unbestätigte Transaktionen am laufen
+TOO_MANY_UNCONFIRMED = Konto hat zu viele ausstehende unbestätigte Transaktionen
-TRANSACTION_ALREADY_CONFIRMED = Transaktionen sind bereits bestätigt
+TRANSACTION_ALREADY_CONFIRMED = Transaktion wurde bereits bestätigt
-TRANSACTION_ALREADY_EXISTS = Transaktionen existiert bereits
+TRANSACTION_ALREADY_EXISTS = Transaktion existiert bereits
-TRANSACTION_UNKNOWN = Unbekante Transaktion
+TRANSACTION_UNKNOWN = Transaktion unbekannt
-TX_GROUP_ID_MISMATCH = Transaktion Gruppen ID stimmt nicht überein
+TX_GROUP_ID_MISMATCH = die Gruppen-ID der Transaktion stimmt nicht überein
diff --git a/src/main/resources/i18n/TransactionValidity_nl.properties b/src/main/resources/i18n/TransactionValidity_nl.properties
index 36b0fec9..f92adf72 100644
--- a/src/main/resources/i18n/TransactionValidity_nl.properties
+++ b/src/main/resources/i18n/TransactionValidity_nl.properties
@@ -1,28 +1,28 @@
#
-ACCOUNT_ALREADY_EXISTS = account bestaat al
+ACCOUNT_ALREADY_EXISTS = account bestaat reeds
ACCOUNT_CANNOT_REWARD_SHARE = account kan geen beloningen delen
-ADDRESS_ABOVE_RATE_LIMIT = adres heeft een waarde limiet bereikt
+ADDRESS_ABOVE_RATE_LIMIT = adres heeft opgegeven limietwaarde bereikt
-ADDRESS_BLOCKED = adres is geblokkeerd
+ADDRESS_BLOCKED = dit adres is geblokkeerd
-ALREADY_GROUP_ADMIN = groeps administrator bestaat al
+ALREADY_GROUP_ADMIN = reeds gekend als groepsadministrator
-ALREADY_GROUP_MEMBER = groeps lid bestaat al
+ALREADY_GROUP_MEMBER = reeds gekend als groepslid
ALREADY_VOTED_FOR_THAT_OPTION = reeds gestemd voor die optie
-ASSET_ALREADY_EXISTS = asset bestaat al
+ASSET_ALREADY_EXISTS = asset bestaat reeds
ASSET_DOES_NOT_EXIST = asset bestaat niet
ASSET_DOES_NOT_MATCH_AT = asset komt niet overeen met de asset van de AT
-ASSET_NOT_SPENDABLE = asset is niet toerijkend
+ASSET_NOT_SPENDABLE = asset kan niet uitbetaald worden
-AT_ALREADY_EXISTS = AT bestaat al
+AT_ALREADY_EXISTS = AT bestaat reeds
AT_IS_FINISHED = AT is afgelopen
@@ -38,25 +38,25 @@ BUYER_ALREADY_OWNER = koper is al de eigenaar
CLOCK_NOT_SYNCED = klok is niet gesynchronizeerd
-DUPLICATE_MESSAGE = dubbel adres bericht
+DUPLICATE_MESSAGE = adres heeft dubbel bericht verzonden
DUPLICATE_OPTION = dubbele optie
GROUP_ALREADY_EXISTS = groep bestaat reeds
-GROUP_APPROVAL_DECIDED = groeps goedkeuring afgewezen
+GROUP_APPROVAL_DECIDED = groeps-goedkeuring afgewezen
-GROUP_APPROVAL_NOT_REQUIRED = groeps goedkeuring niet vereist
+GROUP_APPROVAL_NOT_REQUIRED = groeps-goedkeuring niet vereist
GROUP_DOES_NOT_EXIST = groep bestaat niet
GROUP_ID_MISMATCH = groeps ID komt niet overeen
-GROUP_OWNER_CANNOT_LEAVE = groep eigenaar kan de groep niet verlaten
+GROUP_OWNER_CANNOT_LEAVE = groep-eigenaar kan groep niet verlaten
-HAVE_EQUALS_WANT = asset is gelijk aan Want-asset
+HAVE_EQUALS_WANT = asset is gelijk aan Wens-asset
-INCORRECT_NONCE = incorrecte PoW nonce
+INCORRECT_NONCE = foutieve PoW nonce
INSUFFICIENT_FEE = vergoeding te laag
@@ -72,19 +72,19 @@ INVALID_AT_TYPE_LENGTH = ongeldige lengte voor AT type
INVALID_BUT_OK = ongeldig maar is in orde
-INVALID_CREATION_BYTES = ongeldige gecreerde bytes
+INVALID_CREATION_BYTES = ongeldige creatie-bytes
-INVALID_DATA_LENGTH = ongeldige data lengte
+INVALID_DATA_LENGTH = ongeldige data-lengte
-INVALID_DESCRIPTION_LENGTH = ongeldige lengte voor de beschrijving
+INVALID_DESCRIPTION_LENGTH = ongeldige lengte voor beschrijving
INVALID_GROUP_APPROVAL_THRESHOLD = ongeldige drempelwaarde voor groepsgoedkeuring
-INVALID_GROUP_BLOCK_DELAY = ongeldige groep blok vertraging
+INVALID_GROUP_BLOCK_DELAY = ongeldige blok-vertraging bij groepsgoedkeuring
INVALID_GROUP_ID = ongeldige groep-ID
-INVALID_GROUP_OWNER = ongeldige groep eigenaar
+INVALID_GROUP_OWNER = ongeldige groep-eigenaar
INVALID_LIFETIME = ongeldige levensduur
@@ -94,27 +94,27 @@ INVALID_NAME_OWNER = ongeldige naam voor eigenaar
INVALID_OPTION_LENGTH = ongeldige lengte voor opties
-INVALID_OPTIONS_COUNT = ongeldige hoeveelheid opties
+INVALID_OPTIONS_COUNT = ongeldig aantal opties
-INVALID_ORDER_CREATOR = ongeldige gebruiker voor deze order
+INVALID_ORDER_CREATOR = ongeldige order-creatie-gebruiker
-INVALID_PAYMENTS_COUNT = ongeldige betalings waarde
+INVALID_PAYMENTS_COUNT = ongeldig aantal betalingen
INVALID_PUBLIC_KEY = ongeldige public key
INVALID_QUANTITY = ongeldige hoeveelheid
-INVALID_REFERENCE = ongeldige verwijzing
+INVALID_REFERENCE = ongeldige referentie
-INVALID_RETURN = ongeldige return
+INVALID_RETURN = ongeldig resultaat
-INVALID_REWARD_SHARE_PERCENT = ongeldig belonings percentage
+INVALID_REWARD_SHARE_PERCENT = ongeldig belonings-deelpercentage
INVALID_SELLER = ongeldige verkoper
INVALID_TAGS_LENGTH = ongeldige lengte voor 'tags'
-INVALID_TIMESTAMP_SIGNATURE = ongeldig tijd aanduiding
+INVALID_TIMESTAMP_SIGNATURE = ongeldig tijd-aanduiding
INVALID_TX_GROUP_ID = ongeldige transactiegroep-ID
@@ -124,15 +124,15 @@ INVITE_UNKNOWN = onbekende groepsuitnodiging
JOIN_REQUEST_EXISTS = aanvraag om lid van groep te worden bestaat al
-MAXIMUM_REWARD_SHARES = limiet aan belonen voor dit account bereikt
+MAXIMUM_REWARD_SHARES = maximum bereikt voor beloning-delen voor dit account
-MISSING_CREATOR = ontbrekende aanmaker
+MISSING_CREATOR = creator niet gekend
MULTIPLE_NAMES_FORBIDDEN = het registreren van meerdere namen op een account is niet toegestaan
-NAME_ALREADY_FOR_SALE = naam reeds te koop
+NAME_ALREADY_FOR_SALE = naam is reeds te koop
-NAME_ALREADY_REGISTERED = naam reeds geregistreerd
+NAME_ALREADY_REGISTERED = naam is reeds geregistreerd
NAME_BLOCKED = deze naam is geblokkeerd
@@ -140,7 +140,7 @@ NAME_DOES_NOT_EXIST = naam bestaat niet
NAME_NOT_FOR_SALE = naam is niet te koop
-NAME_NOT_NORMALIZED = naam voldoet niet aan Unicode-vorm
+NAME_NOT_NORMALIZED = naam niet in Unicode-'nomaal'-vorm
NEGATIVE_AMOUNT = negatieve hoeveelheid
@@ -148,9 +148,9 @@ NEGATIVE_FEE = negatieve vergoeding
NEGATIVE_PRICE = negatieve prijs
-NO_BALANCE = onvoldoende balans
+NO_BALANCE = onvoldoende saldo
-NO_BLOCKCHAIN_LOCK = geen blockchain slot
+NO_BLOCKCHAIN_LOCK = blockchain op node is momenteel bezet
NO_FLAG_PERMISSION = account heeft hier geen toestemming voor
@@ -176,9 +176,9 @@ POLL_OPTION_DOES_NOT_EXIST = peilingsoptie bestaat niet
PUBLIC_KEY_UNKNOWN = public key onbekend
-REWARD_SHARE_UNKNOWN = beloning vergoeding onbekend
+REWARD_SHARE_UNKNOWN = beloningsdeel is onbekend
-SELF_SHARE_EXISTS = zelf vergoeding bestaat reeds
+SELF_SHARE_EXISTS = zelf-beloning (belonings-delen) bestaat reeds
TIMESTAMP_TOO_NEW = tijdstempel te nieuw
@@ -186,10 +186,10 @@ TIMESTAMP_TOO_OLD = tijdstempel te oud
TOO_MANY_UNCONFIRMED = account heeft te veel onbevestigde transacties in afwachting
-TRANSACTION_ALREADY_CONFIRMED = transactie is reeds bevestigd
+TRANSACTION_ALREADY_CONFIRMED = transactie werd reeds bevestigd
-TRANSACTION_ALREADY_EXISTS = transactie bestaat al
+TRANSACTION_ALREADY_EXISTS = transactie bestaat reeds
TRANSACTION_UNKNOWN = transactie onbekend
-TX_GROUP_ID_MISMATCH = groep ID komt niet overeen
+TX_GROUP_ID_MISMATCH = groep-ID komt niet overeen
diff --git a/src/test/java/org/qortal/test/MemoryPoWTests.java b/src/test/java/org/qortal/test/MemoryPoWTests.java
index 662fab19..3b0045e5 100644
--- a/src/test/java/org/qortal/test/MemoryPoWTests.java
+++ b/src/test/java/org/qortal/test/MemoryPoWTests.java
@@ -3,6 +3,8 @@ package org.qortal.test;
import org.junit.Ignore;
import org.junit.Test;
import org.qortal.crypto.MemoryPoW;
+import org.qortal.repository.DataException;
+import org.qortal.test.common.Common;
import static org.junit.Assert.*;
@@ -39,13 +41,14 @@ public class MemoryPoWTests {
}
@Test
- public void testMultipleComputes() {
+ public void testMultipleComputes() throws DataException {
+ Common.useDefaultSettings();
Random random = new Random();
- final int sampleSize = 20;
+ final int sampleSize = 10;
final long stddevDivisor = sampleSize * (sampleSize - 1);
- for (int difficulty = 8; difficulty < 16; difficulty += 2) {
+ for (int difficulty = 8; difficulty <= 16; difficulty++) {
byte[] data = new byte[256];
long[] times = new long[sampleSize];
diff --git a/src/test/java/org/qortal/test/MessageTests.java b/src/test/java/org/qortal/test/MessageTests.java
index f08c7b2f..c76c715e 100644
--- a/src/test/java/org/qortal/test/MessageTests.java
+++ b/src/test/java/org/qortal/test/MessageTests.java
@@ -1,5 +1,6 @@
package org.qortal.test;
+import org.apache.commons.lang3.reflect.FieldUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -14,12 +15,9 @@ import org.qortal.group.Group.ApprovalThreshold;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.repository.RepositoryManager;
-import org.qortal.test.common.BlockUtils;
-import org.qortal.test.common.Common;
-import org.qortal.test.common.GroupUtils;
-import org.qortal.test.common.TestAccount;
-import org.qortal.test.common.TransactionUtils;
+import org.qortal.test.common.*;
import org.qortal.test.common.transaction.TestTransaction;
+import org.qortal.transaction.DeployAtTransaction;
import org.qortal.transaction.MessageTransaction;
import org.qortal.transaction.Transaction;
import org.qortal.transaction.Transaction.TransactionType;
@@ -31,6 +29,7 @@ import org.qortal.utils.NTP;
import static org.junit.Assert.*;
+import java.util.List;
import java.util.Random;
public class MessageTests extends Common {
@@ -85,7 +84,7 @@ public class MessageTests extends Common {
byte[] randomReference = new byte[64];
random.nextBytes(randomReference);
- long minimumFee = BlockChain.getInstance().getUnitFee();
+ long minimumFee = BlockChain.getInstance().getUnitFeeAtTimestamp(System.currentTimeMillis());
try (final Repository repository = RepositoryManager.getRepository()) {
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
@@ -139,7 +138,7 @@ public class MessageTests extends Common {
}
@Test
- public void withRecipentWithAmount() throws DataException {
+ public void withRecipientWithAmount() throws DataException {
testMessage(Group.NO_GROUP, recipient, 123L, Asset.QORT);
}
@@ -153,6 +152,140 @@ public class MessageTests extends Common {
testMessage(1, null, 0L, null);
}
+ @Test
+ public void atRecipientNoFeeWithNonce() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
+ String atRecipient = deployAt();
+ MessageTransaction transaction = testFeeNonce(repository, false, true, atRecipient, true);
+
+ // Transaction should be confirmable because it's to an AT, and therefore should be present in a block
+ assertTrue(transaction.isConfirmable());
+ TransactionUtils.signAndMint(repository, transaction.getTransactionData(), alice);
+ assertTrue(isTransactionConfirmed(repository, transaction));
+ assertEquals(16, transaction.getPoWDifficulty());
+
+ BlockUtils.orphanLastBlock(repository);
+ }
+ }
+
+ @Test
+ public void regularRecipientNoFeeWithNonce() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
+
+ // Transaction should not be present in db yet
+ List messageTransactionsData = repository.getMessageRepository().getMessagesByParticipants(null, recipient, null, null, null);
+ assertTrue(messageTransactionsData.isEmpty());
+
+ MessageTransaction transaction = testFeeNonce(repository, false, true, recipient, true);
+
+ // Transaction shouldn't be confirmable because it's not to an AT, and therefore shouldn't be present in a block
+ assertFalse(transaction.isConfirmable());
+ TransactionUtils.signAndMint(repository, transaction.getTransactionData(), alice);
+ assertFalse(isTransactionConfirmed(repository, transaction));
+ assertEquals(12, transaction.getPoWDifficulty());
+
+ // Transaction should be found when trade bot searches for it
+ messageTransactionsData = repository.getMessageRepository().getMessagesByParticipants(null, recipient, null, null, null);
+ assertEquals(1, messageTransactionsData.size());
+
+ BlockUtils.orphanLastBlock(repository);
+ }
+ }
+
+ @Test
+ public void noRecipientNoFeeWithNonce() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
+
+ MessageTransaction transaction = testFeeNonce(repository, false, true, null, true);
+
+ // Transaction shouldn't be confirmable because it's not to an AT, and therefore shouldn't be present in a block
+ assertFalse(transaction.isConfirmable());
+ TransactionUtils.signAndMint(repository, transaction.getTransactionData(), alice);
+ assertFalse(isTransactionConfirmed(repository, transaction));
+ assertEquals(12, transaction.getPoWDifficulty());
+
+ BlockUtils.orphanLastBlock(repository);
+ }
+ }
+
+ @Test
+ public void atRecipientWithFeeNoNonce() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
+ String atRecipient = deployAt();
+ MessageTransaction transaction = testFeeNonce(repository, true, false, atRecipient, true);
+
+ // Transaction should be confirmable because it's to an AT, and therefore should be present in a block
+ assertTrue(transaction.isConfirmable());
+ TransactionUtils.signAndMint(repository, transaction.getTransactionData(), alice);
+ assertTrue(isTransactionConfirmed(repository, transaction));
+ assertEquals(16, transaction.getPoWDifficulty());
+
+ BlockUtils.orphanLastBlock(repository);
+ }
+ }
+
+ @Test
+ public void regularRecipientWithFeeNoNonce() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
+
+ MessageTransaction transaction = testFeeNonce(repository, true, false, recipient, true);
+
+ // Transaction shouldn't be confirmable because it's not to an AT, and therefore shouldn't be present in a block
+ assertFalse(transaction.isConfirmable());
+ TransactionUtils.signAndMint(repository, transaction.getTransactionData(), alice);
+ assertFalse(isTransactionConfirmed(repository, transaction));
+ assertEquals(12, transaction.getPoWDifficulty());
+
+ BlockUtils.orphanLastBlock(repository);
+ }
+ }
+
+ @Test
+ public void atRecipientNoFeeWithNonceLegacyDifficulty() throws DataException, IllegalAccessException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+
+ // Set mempowTransactionUpdatesTimestamp to a high value, so that it hasn't activated key
+ FieldUtils.writeField(BlockChain.getInstance(), "mempowTransactionUpdatesTimestamp", Long.MAX_VALUE, true);
+
+ PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
+ String atRecipient = deployAt();
+ MessageTransaction transaction = testFeeNonce(repository, false, true, atRecipient, true);
+
+ // Transaction should be confirmable because all MESSAGE transactions confirmed prior to the feature trigger
+ assertTrue(transaction.isConfirmable());
+ TransactionUtils.signAndMint(repository, transaction.getTransactionData(), alice);
+ assertTrue(isTransactionConfirmed(repository, transaction));
+ assertEquals(14, transaction.getPoWDifficulty()); // Legacy difficulty was 14 in all cases
+
+ BlockUtils.orphanLastBlock(repository);
+ }
+ }
+
+ @Test
+ public void regularRecipientNoFeeWithNonceLegacyDifficulty() throws DataException, IllegalAccessException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+
+ // Set mempowTransactionUpdatesTimestamp to a high value, so that it hasn't activated key
+ FieldUtils.writeField(BlockChain.getInstance(), "mempowTransactionUpdatesTimestamp", Long.MAX_VALUE, true);
+
+ PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
+ MessageTransaction transaction = testFeeNonce(repository, false, true, recipient, true);
+
+ // Transaction should be confirmable because all MESSAGE transactions confirmed prior to the feature trigger
+ assertTrue(transaction.isConfirmable());
+ TransactionUtils.signAndMint(repository, transaction.getTransactionData(), alice);
+ assertTrue(isTransactionConfirmed(repository, transaction)); // All MESSAGE transactions would confirm before feature trigger
+ assertEquals(14, transaction.getPoWDifficulty()); // Legacy difficulty was 14 in all cases
+
+ BlockUtils.orphanLastBlock(repository);
+ }
+ }
+
@Test
public void serializationTests() throws DataException, TransformationException {
// with recipient, with amount
@@ -165,6 +298,24 @@ public class MessageTests extends Common {
testSerialization(null, 0L, null);
}
+ private String deployAt() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice");
+ byte[] creationBytes = AtUtils.buildSimpleAT();
+ long fundingAmount = 1_00000000L;
+ DeployAtTransaction deployAtTransaction = AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount);
+
+ String address = deployAtTransaction.getATAccount().getAddress();
+ assertNotNull(address);
+ return address;
+ }
+ }
+
+ private boolean isTransactionConfirmed(Repository repository, MessageTransaction transaction) throws DataException {
+ TransactionData queriedTransactionData = repository.getTransactionRepository().fromSignature(transaction.getTransactionData().getSignature());
+ return queriedTransactionData.getBlockHeight() != null && queriedTransactionData.getBlockHeight() > 0;
+ }
+
private boolean isValid(int txGroupId, String recipient, long amount, Long assetId) throws DataException {
try (final Repository repository = RepositoryManager.getRepository()) {
TestAccount alice = Common.getTestAccount(repository, "alice");
@@ -195,41 +346,48 @@ public class MessageTests extends Common {
return messageTransaction.hasValidReference();
}
- private void testFeeNonce(boolean withFee, boolean withNonce, boolean isValid) throws DataException {
+
+ private MessageTransaction testFeeNonce(boolean withFee, boolean withNonce, boolean isValid) throws DataException {
try (final Repository repository = RepositoryManager.getRepository()) {
- TestAccount alice = Common.getTestAccount(repository, "alice");
-
- int txGroupId = 0;
- int nonce = 0;
- long amount = 0;
- long assetId = Asset.QORT;
- byte[] data = new byte[1];
- boolean isText = false;
- boolean isEncrypted = false;
-
- MessageTransactionData transactionData = new MessageTransactionData(TestTransaction.generateBase(alice, txGroupId),
- version, nonce, recipient, amount, assetId, data, isText, isEncrypted);
-
- MessageTransaction transaction = new MessageTransaction(repository, transactionData);
-
- if (withFee)
- transactionData.setFee(transaction.calcRecommendedFee());
- else
- transactionData.setFee(0L);
-
- if (withNonce) {
- transaction.computeNonce();
- } else {
- transactionData.setNonce(-1);
- }
-
- transaction.sign(alice);
-
- assertEquals(isValid, transaction.isSignatureValid());
+ return testFeeNonce(repository, withFee, withNonce, recipient, isValid);
}
}
- private void testMessage(int txGroupId, String recipient, long amount, Long assetId) throws DataException {
+ private MessageTransaction testFeeNonce(Repository repository, boolean withFee, boolean withNonce, String recipient, boolean isValid) throws DataException {
+ TestAccount alice = Common.getTestAccount(repository, "alice");
+
+ int txGroupId = 0;
+ int nonce = 0;
+ long amount = 0;
+ long assetId = Asset.QORT;
+ byte[] data = new byte[1];
+ boolean isText = false;
+ boolean isEncrypted = false;
+
+ MessageTransactionData transactionData = new MessageTransactionData(TestTransaction.generateBase(alice, txGroupId),
+ version, nonce, recipient, amount, assetId, data, isText, isEncrypted);
+
+ MessageTransaction transaction = new MessageTransaction(repository, transactionData);
+
+ if (withFee)
+ transactionData.setFee(transaction.calcRecommendedFee());
+ else
+ transactionData.setFee(0L);
+
+ if (withNonce) {
+ transaction.computeNonce();
+ } else {
+ transactionData.setNonce(-1);
+ }
+
+ transaction.sign(alice);
+
+ assertEquals(isValid, transaction.isSignatureValid());
+
+ return transaction;
+ }
+
+ private MessageTransaction testMessage(int txGroupId, String recipient, long amount, Long assetId) throws DataException {
try (final Repository repository = RepositoryManager.getRepository()) {
TestAccount alice = Common.getTestAccount(repository, "alice");
@@ -244,6 +402,8 @@ public class MessageTests extends Common {
TransactionUtils.signAndMint(repository, transactionData, alice);
BlockUtils.orphanLastBlock(repository);
+
+ return new MessageTransaction(repository, transactionData);
}
}
diff --git a/src/test/java/org/qortal/test/arbitrary/ArbitraryTransactionMetadataTests.java b/src/test/java/org/qortal/test/arbitrary/ArbitraryTransactionMetadataTests.java
index 47c68b25..9ac73166 100644
--- a/src/test/java/org/qortal/test/arbitrary/ArbitraryTransactionMetadataTests.java
+++ b/src/test/java/org/qortal/test/arbitrary/ArbitraryTransactionMetadataTests.java
@@ -11,6 +11,7 @@ import org.qortal.arbitrary.ArbitraryDataReader;
import org.qortal.arbitrary.exception.MissingDataException;
import org.qortal.arbitrary.misc.Category;
import org.qortal.arbitrary.misc.Service;
+import org.qortal.block.BlockChain;
import org.qortal.controller.arbitrary.ArbitraryDataManager;
import org.qortal.data.arbitrary.ArbitraryResourceMetadata;
import org.qortal.data.transaction.ArbitraryTransactionData;
@@ -24,6 +25,7 @@ import org.qortal.test.common.TransactionUtils;
import org.qortal.test.common.transaction.TestTransaction;
import org.qortal.transaction.RegisterNameTransaction;
import org.qortal.utils.Base58;
+import org.qortal.utils.NTP;
import java.io.IOException;
import java.nio.file.Files;
@@ -106,8 +108,9 @@ public class ArbitraryTransactionMetadataTests extends Common {
// Create PUT transaction
Path path1 = ArbitraryUtils.generateRandomDataPath(dataLength);
+ long fee = BlockChain.getInstance().getUnitFeeAtTimestamp(NTP.getTime());
ArbitraryDataFile arbitraryDataFile = ArbitraryUtils.createAndMintTxn(repository, publicKey58, path1, name,
- identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize, 0L, true,
+ identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize, fee, false,
title, description, tags, category);
// Check the chunk count is correct
@@ -157,8 +160,9 @@ public class ArbitraryTransactionMetadataTests extends Common {
// Create PUT transaction
Path path1 = ArbitraryUtils.generateRandomDataPath(dataLength);
+ long fee = BlockChain.getInstance().getUnitFeeAtTimestamp(NTP.getTime());
ArbitraryDataFile arbitraryDataFile = ArbitraryUtils.createAndMintTxn(repository, publicKey58, path1, name,
- identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize, 0L, true,
+ identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize, fee, false,
title, description, tags, category);
// Check the chunk count is correct
@@ -220,8 +224,9 @@ public class ArbitraryTransactionMetadataTests extends Common {
// Create PUT transaction
Path path1 = ArbitraryUtils.generateRandomDataPath(dataLength);
+ long fee = BlockChain.getInstance().getUnitFeeAtTimestamp(NTP.getTime());
ArbitraryDataFile arbitraryDataFile = ArbitraryUtils.createAndMintTxn(repository, publicKey58, path1, name,
- identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize, 0L, true,
+ identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize, fee, false,
title, description, tags, category);
// Check the chunk count is correct
@@ -272,8 +277,9 @@ public class ArbitraryTransactionMetadataTests extends Common {
// Create PUT transaction
Path path1 = ArbitraryUtils.generateRandomDataPath(dataLength);
+ long fee = BlockChain.getInstance().getUnitFeeAtTimestamp(NTP.getTime());
ArbitraryDataFile arbitraryDataFile = ArbitraryUtils.createAndMintTxn(repository, publicKey58, path1, name,
- identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize, 0L, true,
+ identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize, fee, false,
title, description, tags, category);
// Check the chunk count is correct
@@ -316,8 +322,9 @@ public class ArbitraryTransactionMetadataTests extends Common {
// Create PUT transaction
Path path1 = ArbitraryUtils.generateRandomDataPath(dataLength);
+ long fee = BlockChain.getInstance().getUnitFeeAtTimestamp(NTP.getTime());
ArbitraryDataFile arbitraryDataFile = ArbitraryUtils.createAndMintTxn(repository, publicKey58, path1, name,
- identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize, 0L, true,
+ identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize, fee, false,
title, description, tags, category);
// Check the metadata is correct
diff --git a/src/test/java/org/qortal/test/arbitrary/ArbitraryTransactionTests.java b/src/test/java/org/qortal/test/arbitrary/ArbitraryTransactionTests.java
index 855aeafd..089f0475 100644
--- a/src/test/java/org/qortal/test/arbitrary/ArbitraryTransactionTests.java
+++ b/src/test/java/org/qortal/test/arbitrary/ArbitraryTransactionTests.java
@@ -10,6 +10,7 @@ import org.qortal.arbitrary.ArbitraryDataTransactionBuilder;
import org.qortal.arbitrary.exception.MissingDataException;
import org.qortal.arbitrary.misc.Category;
import org.qortal.arbitrary.misc.Service;
+import org.qortal.block.BlockChain;
import org.qortal.controller.arbitrary.ArbitraryDataManager;
import org.qortal.crypto.Crypto;
import org.qortal.data.PaymentData;
@@ -50,51 +51,6 @@ public class ArbitraryTransactionTests extends Common {
Common.useDefaultSettings();
}
- @Test
- public void testDifficultyTooLow() throws IllegalAccessException, DataException, IOException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
- String publicKey58 = Base58.encode(alice.getPublicKey());
- String name = "TEST"; // Can be anything for this test
- String identifier = null; // Not used for this test
- Service service = Service.ARBITRARY_DATA;
- int chunkSize = 100;
- int dataLength = 900; // Actual data length will be longer due to encryption
-
- // Register the name to Alice
- RegisterNameTransactionData registerNameTransactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, "");
- registerNameTransactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(registerNameTransactionData.getTimestamp()));
- TransactionUtils.signAndMint(repository, registerNameTransactionData, alice);
-
- // Set difficulty to 1
- FieldUtils.writeField(ArbitraryDataManager.getInstance(), "powDifficulty", 1, true);
-
- // Create PUT transaction
- Path path1 = ArbitraryUtils.generateRandomDataPath(dataLength);
- ArbitraryDataFile arbitraryDataFile = ArbitraryUtils.createAndMintTxn(repository, publicKey58, path1, name, identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize);
-
- // Check that nonce validation succeeds
- byte[] signature = arbitraryDataFile.getSignature();
- TransactionData transactionData = repository.getTransactionRepository().fromSignature(signature);
- ArbitraryTransaction transaction = new ArbitraryTransaction(repository, transactionData);
- assertTrue(transaction.isSignatureValid());
-
- // Increase difficulty to 15
- FieldUtils.writeField(ArbitraryDataManager.getInstance(), "powDifficulty", 15, true);
-
- // Make sure the nonce validation fails
- // Note: there is a very tiny chance this could succeed due to being extremely lucky
- // and finding a high difficulty nonce in the first couple of cycles. It will be rare
- // enough that we shouldn't need to account for it.
- assertFalse(transaction.isSignatureValid());
-
- // Reduce difficulty back to 1, to double check
- FieldUtils.writeField(ArbitraryDataManager.getInstance(), "powDifficulty", 1, true);
- assertTrue(transaction.isSignatureValid());
-
- }
- }
-
@Test
public void testNonceAndFee() throws IllegalAccessException, DataException, IOException {
try (final Repository repository = RepositoryManager.getRepository()) {
@@ -497,8 +453,9 @@ public class ArbitraryTransactionTests extends Common {
// Create PUT transaction
Path path1 = ArbitraryUtils.generateRandomDataPath(dataLength, true);
+ long fee = BlockChain.getInstance().getUnitFeeAtTimestamp(NTP.getTime());
ArbitraryDataFile arbitraryDataFile = ArbitraryUtils.createAndMintTxn(repository, publicKey58, path1, name,
- identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize, 0L, true,
+ identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize, fee, false,
null, null, null, null);
byte[] signature = arbitraryDataFile.getSignature();
@@ -556,8 +513,9 @@ public class ArbitraryTransactionTests extends Common {
// Create PUT transaction
Path path1 = ArbitraryUtils.generateRandomDataPath(dataLength, true);
+ long fee = BlockChain.getInstance().getUnitFeeAtTimestamp(NTP.getTime());
ArbitraryDataFile arbitraryDataFile = ArbitraryUtils.createAndMintTxn(repository, publicKey58, path1, name,
- identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize, 0L, true,
+ identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize, fee, false,
title, description, tags, category);
byte[] signature = arbitraryDataFile.getSignature();
@@ -614,8 +572,9 @@ public class ArbitraryTransactionTests extends Common {
// Create PUT transaction
Path path1 = ArbitraryUtils.generateRandomDataPath(dataLength, true);
+ long fee = BlockChain.getInstance().getUnitFeeAtTimestamp(NTP.getTime());
ArbitraryDataFile arbitraryDataFile = ArbitraryUtils.createAndMintTxn(repository, publicKey58, path1, name,
- identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize, 0L, true,
+ identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize, fee, false,
null, null, null, null);
byte[] signature = arbitraryDataFile.getSignature();
diff --git a/src/test/java/org/qortal/test/common/ArbitraryUtils.java b/src/test/java/org/qortal/test/common/ArbitraryUtils.java
index 1741d22c..e08eb0ac 100644
--- a/src/test/java/org/qortal/test/common/ArbitraryUtils.java
+++ b/src/test/java/org/qortal/test/common/ArbitraryUtils.java
@@ -5,10 +5,12 @@ import org.qortal.arbitrary.ArbitraryDataFile;
import org.qortal.arbitrary.ArbitraryDataTransactionBuilder;
import org.qortal.arbitrary.misc.Category;
import org.qortal.arbitrary.misc.Service;
+import org.qortal.block.BlockChain;
import org.qortal.data.transaction.ArbitraryTransactionData;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.transaction.Transaction;
+import org.qortal.utils.NTP;
import java.io.BufferedWriter;
import java.io.File;
@@ -20,16 +22,15 @@ import java.nio.file.Paths;
import java.util.List;
import java.util.Random;
-import static org.junit.Assert.assertEquals;
-
public class ArbitraryUtils {
public static ArbitraryDataFile createAndMintTxn(Repository repository, String publicKey58, Path path, String name, String identifier,
ArbitraryTransactionData.Method method, Service service, PrivateKeyAccount account,
int chunkSize) throws DataException {
+ long fee = BlockChain.getInstance().getUnitFeeAtTimestamp(NTP.getTime());
return ArbitraryUtils.createAndMintTxn(repository, publicKey58, path, name, identifier, method, service,
- account, chunkSize, 0L, true, null, null, null, null);
+ account, chunkSize, fee, false, null, null, null, null);
}
public static ArbitraryDataFile createAndMintTxn(Repository repository, String publicKey58, Path path, String name, String identifier,
@@ -47,7 +48,9 @@ public class ArbitraryUtils {
}
ArbitraryTransactionData transactionData = txnBuilder.getArbitraryTransactionData();
Transaction.ValidationResult result = TransactionUtils.signAndImport(repository, transactionData, account);
- assertEquals(Transaction.ValidationResult.OK, result);
+ if (result != Transaction.ValidationResult.OK) {
+ throw new DataException(String.format("Arbitrary transaction invalid: %s", result.toString()));
+ }
BlockUtils.mintBlock(repository);
// We need a new ArbitraryDataFile instance because the files will have been moved to the signature's folder
diff --git a/src/test/java/org/qortal/test/common/transaction/TestTransaction.java b/src/test/java/org/qortal/test/common/transaction/TestTransaction.java
index 11fdf58e..b580ecd3 100644
--- a/src/test/java/org/qortal/test/common/transaction/TestTransaction.java
+++ b/src/test/java/org/qortal/test/common/transaction/TestTransaction.java
@@ -7,13 +7,15 @@ import org.qortal.block.BlockChain;
import org.qortal.data.transaction.BaseTransactionData;
import org.qortal.group.Group;
import org.qortal.repository.DataException;
+import org.qortal.utils.NTP;
public abstract class TestTransaction {
protected static final Random random = new Random();
public static BaseTransactionData generateBase(PrivateKeyAccount account, int txGroupId) throws DataException {
- return new BaseTransactionData(System.currentTimeMillis(), txGroupId, account.getLastReference(), account.getPublicKey(), BlockChain.getInstance().getUnitFee(), null);
+ long timestamp = System.currentTimeMillis();
+ return new BaseTransactionData(timestamp, txGroupId, account.getLastReference(), account.getPublicKey(), BlockChain.getInstance().getUnitFeeAtTimestamp(timestamp), null);
}
public static BaseTransactionData generateBase(PrivateKeyAccount account) throws DataException {
diff --git a/src/test/java/org/qortal/test/crosschain/ACCTTests.java b/src/test/java/org/qortal/test/crosschain/ACCTTests.java
new file mode 100644
index 00000000..6af27a96
--- /dev/null
+++ b/src/test/java/org/qortal/test/crosschain/ACCTTests.java
@@ -0,0 +1,790 @@
+package org.qortal.test.crosschain;
+
+import com.google.common.hash.HashCode;
+import com.google.common.primitives.Bytes;
+import org.junit.Before;
+import org.junit.Test;
+import org.qortal.account.Account;
+import org.qortal.account.PrivateKeyAccount;
+import org.qortal.asset.Asset;
+import org.qortal.block.Block;
+import org.qortal.crosschain.ACCT;
+import org.qortal.crosschain.AcctMode;
+import org.qortal.crypto.Crypto;
+import org.qortal.data.at.ATData;
+import org.qortal.data.at.ATStateData;
+import org.qortal.data.crosschain.CrossChainTradeData;
+import org.qortal.data.transaction.BaseTransactionData;
+import org.qortal.data.transaction.DeployAtTransactionData;
+import org.qortal.data.transaction.MessageTransactionData;
+import org.qortal.data.transaction.TransactionData;
+import org.qortal.group.Group;
+import org.qortal.repository.DataException;
+import org.qortal.repository.Repository;
+import org.qortal.repository.RepositoryManager;
+import org.qortal.test.common.BlockUtils;
+import org.qortal.test.common.Common;
+import org.qortal.test.common.TransactionUtils;
+import org.qortal.transaction.DeployAtTransaction;
+import org.qortal.transaction.MessageTransaction;
+import org.qortal.utils.Amounts;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.function.Function;
+
+import static org.junit.Assert.*;
+
+public abstract class ACCTTests extends Common {
+
+ public static final byte[] secretA = "This string is exactly 32 bytes!".getBytes();
+ public static final byte[] hashOfSecretA = Crypto.hash160(secretA); // daf59884b4d1aec8c1b17102530909ee43c0151a
+ public static final int tradeTimeout = 20; // blocks
+ public static final long redeemAmount = 80_40200000L;
+ public static final long fundingAmount = 123_45600000L;
+ public static final long foreignAmount = 864200L; // 0.00864200 foreign units
+
+ protected static final Random RANDOM = new Random();
+
+ protected abstract byte[] getPublicKey();
+
+ protected abstract byte[] buildQortalAT(String address, byte[] publicKey, long redeemAmount, long foreignAmount, int tradeTimeout);
+
+ protected abstract ACCT getInstance();
+
+ protected abstract int calcRefundTimeout(long partnersOfferMessageTransactionTimestamp, int lockTimeA);
+
+ protected abstract byte[] buildTradeMessage(String address, byte[] publicKey, byte[] hashOfSecretA, int lockTimeA, int refundTimeout);
+
+ protected abstract byte[] buildRedeemMessage(byte[] secretA, String address);
+
+ protected abstract byte[] getCodeBytesHash();
+
+ protected abstract String getSymbol();
+
+ protected abstract String getName();
+
+ @Before
+ public void beforeTest() throws DataException {
+ Common.useDefaultSettings();
+ }
+
+ @Test
+ public void testCompile() {
+ PrivateKeyAccount tradeAccount = createTradeAccount(null);
+
+ byte[] creationBytes = buildQortalAT(tradeAccount.getAddress(), getPublicKey(), redeemAmount, foreignAmount, tradeTimeout);
+ assertNotNull(creationBytes);
+
+ System.out.println("AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
+ }
+
+
+ @Test
+ public void testDeploy() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
+ PrivateKeyAccount tradeAccount = createTradeAccount(repository);
+
+ PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
+
+ long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
+ long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
+
+ long expectedBalance = deployersInitialBalance - fundingAmount - deployAtTransaction.getTransactionData().getFee();
+ long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
+
+ assertEquals("Deployer's post-deployment balance incorrect", expectedBalance, actualBalance);
+
+ expectedBalance = fundingAmount;
+ actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
+
+ assertEquals("AT's post-deployment balance incorrect", expectedBalance, actualBalance);
+
+ expectedBalance = partnersInitialBalance;
+ actualBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ assertEquals("Partner's post-deployment balance incorrect", expectedBalance, actualBalance);
+
+ // Test orphaning
+ BlockUtils.orphanLastBlock(repository);
+
+ expectedBalance = deployersInitialBalance;
+ actualBalance = deployer.getConfirmedBalance(Asset.QORT);
+
+ assertEquals("Deployer's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
+
+ expectedBalance = 0;
+ actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
+
+ assertEquals("AT's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
+
+ expectedBalance = partnersInitialBalance;
+ actualBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ assertEquals("Partner's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ @Test
+ public void testOfferCancel() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
+ PrivateKeyAccount tradeAccount = createTradeAccount(repository);
+
+ PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
+
+ long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
+ long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
+ Account at = deployAtTransaction.getATAccount();
+ String atAddress = at.getAddress();
+
+ long deployAtFee = deployAtTransaction.getTransactionData().getFee();
+ long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
+
+ // Send creator's address to AT, instead of typical partner's address
+ byte[] messageData = getInstance().buildCancelMessage(deployer.getAddress());
+ MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
+ long messageFee = messageTransaction.getTransactionData().getFee();
+
+ // AT should process 'cancel' message in next block
+ BlockUtils.mintBlock(repository);
+
+ describeAt(repository, atAddress);
+
+ // Check AT is finished
+ ATData atData = repository.getATRepository().fromATAddress(atAddress);
+ assertTrue(atData.getIsFinished());
+
+ // AT should be in CANCELLED mode
+ CrossChainTradeData tradeData = getInstance().populateTradeData(repository, atData);
+ assertEquals(AcctMode.CANCELLED, tradeData.mode);
+
+ // Check balances
+ long expectedMinimumBalance = deployersPostDeploymentBalance;
+ long expectedMaximumBalance = deployersInitialBalance - deployAtFee - messageFee;
+
+ long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
+
+ assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
+ assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
+
+ // Test orphaning
+ BlockUtils.orphanLastBlock(repository);
+
+ // Check balances
+ long expectedBalance = deployersPostDeploymentBalance - messageFee;
+ actualBalance = deployer.getConfirmedBalance(Asset.QORT);
+
+ assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ @Test
+ public void testOfferCancelInvalidLength() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
+ PrivateKeyAccount tradeAccount = createTradeAccount(repository);
+
+ PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
+
+ long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
+ long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
+ Account at = deployAtTransaction.getATAccount();
+ String atAddress = at.getAddress();
+
+ long deployAtFee = deployAtTransaction.getTransactionData().getFee();
+ long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
+
+ // Instead of sending creator's address to AT, send too-short/invalid message
+ byte[] messageData = new byte[7];
+ RANDOM.nextBytes(messageData);
+ MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
+ long messageFee = messageTransaction.getTransactionData().getFee();
+
+ // AT should process 'cancel' message in next block
+ // As message is too short, it will be padded to 32bytes but cancel code doesn't care about message content, so should be ok
+ BlockUtils.mintBlock(repository);
+
+ describeAt(repository, atAddress);
+
+ // Check AT is finished
+ ATData atData = repository.getATRepository().fromATAddress(atAddress);
+ assertTrue(atData.getIsFinished());
+
+ // AT should be in CANCELLED mode
+ CrossChainTradeData tradeData = getInstance().populateTradeData(repository, atData);
+ assertEquals(AcctMode.CANCELLED, tradeData.mode);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ @Test
+ public void testTradingInfoProcessing() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
+ PrivateKeyAccount tradeAccount = createTradeAccount(repository);
+
+ PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
+
+ long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
+ long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
+ Account at = deployAtTransaction.getATAccount();
+ String atAddress = at.getAddress();
+
+ long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
+ int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
+ int refundTimeout = calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
+
+ // Send trade info to AT
+ byte[] messageData = buildTradeMessage(partner.getAddress(), getPublicKey(), hashOfSecretA, lockTimeA, refundTimeout);
+ MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
+
+ Block postDeploymentBlock = BlockUtils.mintBlock(repository);
+ int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
+
+ long deployAtFee = deployAtTransaction.getTransactionData().getFee();
+ long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
+
+ describeAt(repository, atAddress);
+
+ ATData atData = repository.getATRepository().fromATAddress(atAddress);
+ CrossChainTradeData tradeData = getInstance().populateTradeData(repository, atData);
+
+ // AT should be in TRADE mode
+ assertEquals(AcctMode.TRADING, tradeData.mode);
+
+ // Check hashOfSecretA was extracted correctly
+ assertTrue(Arrays.equals(hashOfSecretA, tradeData.hashOfSecretA));
+
+ // Check trade partner Qortal address was extracted correctly
+ assertEquals(partner.getAddress(), tradeData.qortalPartnerAddress);
+
+ // Check trade partner's Foreign Coin PKH was extracted correctly
+ assertTrue(Arrays.equals(getPublicKey(), tradeData.partnerForeignPKH));
+
+ // Test orphaning
+ BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
+
+ // Check balances
+ long expectedBalance = deployersPostDeploymentBalance;
+ long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
+
+ assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
+ }
+ }
+
+ // TEST SENDING TRADING INFO BUT NOT FROM AT CREATOR (SHOULD BE IGNORED)
+ @SuppressWarnings("unused")
+ @Test
+ public void testIncorrectTradeSender() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
+ PrivateKeyAccount tradeAccount = createTradeAccount(repository);
+
+ PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
+
+ PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
+
+ long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
+ long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
+ Account at = deployAtTransaction.getATAccount();
+ String atAddress = at.getAddress();
+
+ long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
+ int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
+ int refundTimeout = calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
+
+ // Send trade info to AT BUT NOT FROM AT CREATOR
+ byte[] messageData = buildTradeMessage(partner.getAddress(), getPublicKey(), hashOfSecretA, lockTimeA, refundTimeout);
+ MessageTransaction messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
+
+ BlockUtils.mintBlock(repository);
+
+ long expectedBalance = partnersInitialBalance;
+ long actualBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ assertEquals("Partner's post-initial-payout balance incorrect", expectedBalance, actualBalance);
+
+ describeAt(repository, atAddress);
+
+ ATData atData = repository.getATRepository().fromATAddress(atAddress);
+ CrossChainTradeData tradeData = getInstance().populateTradeData(repository, atData);
+
+ // AT should still be in OFFER mode
+ assertEquals(AcctMode.OFFERING, tradeData.mode);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ @Test
+ public void testAutomaticTradeRefund() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
+ PrivateKeyAccount tradeAccount = createTradeAccount(repository);
+
+ PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
+
+ long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
+ long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
+ Account at = deployAtTransaction.getATAccount();
+ String atAddress = at.getAddress();
+
+ long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
+ int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
+ int refundTimeout = calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
+
+ // Send trade info to AT
+ byte[] messageData = buildTradeMessage(partner.getAddress(), getPublicKey(), hashOfSecretA, lockTimeA, refundTimeout);
+ MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
+
+ Block postDeploymentBlock = BlockUtils.mintBlock(repository);
+ int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
+
+ // Check refund
+ long deployAtFee = deployAtTransaction.getTransactionData().getFee();
+ long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
+
+ checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
+
+ describeAt(repository, atAddress);
+
+ // Check AT is finished
+ ATData atData = repository.getATRepository().fromATAddress(atAddress);
+ assertTrue(atData.getIsFinished());
+
+ // AT should be in REFUNDED mode
+ CrossChainTradeData tradeData = getInstance().populateTradeData(repository, atData);
+ assertEquals(AcctMode.REFUNDED, tradeData.mode);
+
+ // Test orphaning
+ BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
+
+ // Check balances
+ long expectedBalance = deployersPostDeploymentBalance;
+ long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
+
+ assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ @Test
+ public void testCorrectSecretCorrectSender() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
+ PrivateKeyAccount tradeAccount = createTradeAccount(repository);
+
+ PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
+
+ long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
+ long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
+ Account at = deployAtTransaction.getATAccount();
+ String atAddress = at.getAddress();
+
+ long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
+ int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
+ int refundTimeout = calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
+
+ // Send trade info to AT
+ byte[] messageData = buildTradeMessage(partner.getAddress(), getPublicKey(), hashOfSecretA, lockTimeA, refundTimeout);
+ MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
+
+ // Give AT time to process message
+ BlockUtils.mintBlock(repository);
+
+ // Send correct secret to AT, from correct account
+ messageData = buildRedeemMessage(secretA, partner.getAddress());
+ messageTransaction = sendMessage(repository, partner, messageData, atAddress);
+
+ // AT should send funds in the next block
+ ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
+ BlockUtils.mintBlock(repository);
+
+ describeAt(repository, atAddress);
+
+ // Check AT is finished
+ ATData atData = repository.getATRepository().fromATAddress(atAddress);
+ assertTrue(atData.getIsFinished());
+
+ // AT should be in REDEEMED mode
+ CrossChainTradeData tradeData = getInstance().populateTradeData(repository, atData);
+ assertEquals(AcctMode.REDEEMED, tradeData.mode);
+
+ // Check balances
+ long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee() + redeemAmount;
+ long actualBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ assertEquals("Partner's post-redeem balance incorrect", expectedBalance, actualBalance);
+
+ // Orphan redeem
+ BlockUtils.orphanLastBlock(repository);
+
+ // Check balances
+ expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
+ actualBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ assertEquals("Partner's post-orphan/pre-redeem balance incorrect", expectedBalance, actualBalance);
+
+ // Check AT state
+ ATStateData postOrphanAtStateData = repository.getATRepository().getLatestATState(atAddress);
+
+ assertTrue("AT states mismatch", Arrays.equals(preRedeemAtStateData.getStateData(), postOrphanAtStateData.getStateData()));
+ }
+ }
+
+ @SuppressWarnings("unused")
+ @Test
+ public void testCorrectSecretIncorrectSender() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
+ PrivateKeyAccount tradeAccount = createTradeAccount(repository);
+
+ PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
+
+ PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
+
+ long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
+ long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
+ long deployAtFee = deployAtTransaction.getTransactionData().getFee();
+
+ Account at = deployAtTransaction.getATAccount();
+ String atAddress = at.getAddress();
+
+ long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
+ int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
+ int refundTimeout = calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
+
+ // Send trade info to AT
+ byte[] messageData = buildTradeMessage(partner.getAddress(), getPublicKey(), hashOfSecretA, lockTimeA, refundTimeout);
+ MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
+
+ // Give AT time to process message
+ BlockUtils.mintBlock(repository);
+
+ // Send correct secret to AT, but from wrong account
+ messageData = buildRedeemMessage(secretA, partner.getAddress());
+ messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
+
+ // AT should NOT send funds in the next block
+ ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
+ BlockUtils.mintBlock(repository);
+
+ describeAt(repository, atAddress);
+
+ // Check AT is NOT finished
+ ATData atData = repository.getATRepository().fromATAddress(atAddress);
+ assertFalse(atData.getIsFinished());
+
+ // AT should still be in TRADE mode
+ CrossChainTradeData tradeData = getInstance().populateTradeData(repository, atData);
+ assertEquals(AcctMode.TRADING, tradeData.mode);
+
+ // Check balances
+ long expectedBalance = partnersInitialBalance;
+ long actualBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
+
+ // Check eventual refund
+ checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ @Test
+ public void testIncorrectSecretCorrectSender() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
+ PrivateKeyAccount tradeAccount = createTradeAccount(repository);
+
+ PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
+
+ long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
+ long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
+ long deployAtFee = deployAtTransaction.getTransactionData().getFee();
+
+ Account at = deployAtTransaction.getATAccount();
+ String atAddress = at.getAddress();
+
+ long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
+ int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
+ int refundTimeout = calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
+
+ // Send trade info to AT
+ byte[] messageData = buildTradeMessage(partner.getAddress(), getPublicKey(), hashOfSecretA, lockTimeA, refundTimeout);
+ MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
+
+ // Give AT time to process message
+ BlockUtils.mintBlock(repository);
+
+ // Send incorrect secret to AT, from correct account
+ byte[] wrongSecret = new byte[32];
+ RANDOM.nextBytes(wrongSecret);
+ messageData = buildRedeemMessage(wrongSecret, partner.getAddress());
+ messageTransaction = sendMessage(repository, partner, messageData, atAddress);
+
+ // AT should NOT send funds in the next block
+ ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
+ BlockUtils.mintBlock(repository);
+
+ describeAt(repository, atAddress);
+
+ // Check AT is NOT finished
+ ATData atData = repository.getATRepository().fromATAddress(atAddress);
+ assertFalse(atData.getIsFinished());
+
+ // AT should still be in TRADE mode
+ CrossChainTradeData tradeData = getInstance().populateTradeData(repository, atData);
+ assertEquals(AcctMode.TRADING, tradeData.mode);
+
+ long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
+ long actualBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
+
+ // Check eventual refund
+ checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ @Test
+ public void testCorrectSecretCorrectSenderInvalidMessageLength() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
+ PrivateKeyAccount tradeAccount = createTradeAccount(repository);
+
+ PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
+
+ long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
+ long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
+ Account at = deployAtTransaction.getATAccount();
+ String atAddress = at.getAddress();
+
+ long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
+ int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
+ int refundTimeout = calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
+
+ // Send trade info to AT
+ byte[] messageData = buildTradeMessage(partner.getAddress(), getPublicKey(), hashOfSecretA, lockTimeA, refundTimeout);
+ MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
+
+ // Give AT time to process message
+ BlockUtils.mintBlock(repository);
+
+ // Send correct secret to AT, from correct account, but missing receive address, hence incorrect length
+ messageData = Bytes.concat(secretA);
+ messageTransaction = sendMessage(repository, partner, messageData, atAddress);
+
+ // AT should NOT send funds in the next block
+ ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
+ BlockUtils.mintBlock(repository);
+
+ describeAt(repository, atAddress);
+
+ // Check AT is NOT finished
+ ATData atData = repository.getATRepository().fromATAddress(atAddress);
+ assertFalse(atData.getIsFinished());
+
+ // AT should be in TRADING mode
+ CrossChainTradeData tradeData = getInstance().populateTradeData(repository, atData);
+ assertEquals(AcctMode.TRADING, tradeData.mode);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ @Test
+ public void testDescribeDeployed() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
+ PrivateKeyAccount tradeAccount = createTradeAccount(repository);
+
+ PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
+
+ long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
+ long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
+
+ DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
+
+ List executableAts = repository.getATRepository().getAllExecutableATs();
+
+ for (ATData atData : executableAts) {
+ String atAddress = atData.getATAddress();
+ byte[] codeBytes = atData.getCodeBytes();
+ byte[] codeHash = Crypto.digest(codeBytes);
+
+ System.out.println(String.format("%s: code length: %d byte%s, code hash: %s",
+ atAddress,
+ codeBytes.length,
+ (codeBytes.length != 1 ? "s": ""),
+ HashCode.fromBytes(codeHash)));
+
+ // Not one of ours?
+ if (!Arrays.equals(codeHash, getCodeBytesHash()))
+ continue;
+
+ describeAt(repository, atAddress);
+ }
+ }
+ }
+
+ protected int calcTestLockTimeA(long messageTimestamp) {
+ return (int) (messageTimestamp / 1000L + tradeTimeout * 60);
+ }
+
+ protected DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer, String tradeAddress) throws DataException {
+ byte[] creationBytes = buildQortalAT(tradeAddress, getPublicKey(), redeemAmount, foreignAmount, tradeTimeout);
+
+ long txTimestamp = System.currentTimeMillis();
+ byte[] lastReference = deployer.getLastReference();
+
+ if (lastReference == null) {
+ System.err.println(String.format("Qortal account %s has no last reference", deployer.getAddress()));
+ System.exit(2);
+ }
+
+ Long fee = null;
+ String name = "QORT-" + getSymbol() + " cross-chain trade";
+ String description = String.format("Qortal-" + getName() + " cross-chain trade");
+ String atType = "ACCT";
+ String tags = "QORT-" + getSymbol() + " ACCT";
+
+ BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, deployer.getPublicKey(), fee, null);
+ TransactionData deployAtTransactionData = new DeployAtTransactionData(baseTransactionData, name, description, atType, tags, creationBytes, fundingAmount, Asset.QORT);
+
+ DeployAtTransaction deployAtTransaction = new DeployAtTransaction(repository, deployAtTransactionData);
+
+ fee = deployAtTransaction.calcRecommendedFee();
+ deployAtTransactionData.setFee(fee);
+
+ TransactionUtils.signAndMint(repository, deployAtTransactionData, deployer);
+
+ return deployAtTransaction;
+ }
+
+ protected MessageTransaction sendMessage(Repository repository, PrivateKeyAccount sender, byte[] data, String recipient) throws DataException {
+ long txTimestamp = System.currentTimeMillis();
+ byte[] lastReference = sender.getLastReference();
+
+ if (lastReference == null) {
+ System.err.println(String.format("Qortal account %s has no last reference", sender.getAddress()));
+ System.exit(2);
+ }
+
+ Long fee = null;
+ int version = 4;
+ int nonce = 0;
+ long amount = 0;
+ Long assetId = null; // because amount is zero
+
+ BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, sender.getPublicKey(), fee, null);
+ TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, version, nonce, recipient, amount, assetId, data, false, false);
+
+ MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData);
+
+ fee = messageTransaction.calcRecommendedFee();
+ messageTransactionData.setFee(fee);
+
+ TransactionUtils.signAndMint(repository, messageTransactionData, sender);
+
+ return messageTransaction;
+ }
+
+ protected void checkTradeRefund(Repository repository, Account deployer, long deployersInitialBalance, long deployAtFee) throws DataException {
+ long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
+ int refundTimeout = tradeTimeout / 2 + 1; // close enough
+
+ // AT should automatically refund deployer after 'refundTimeout' blocks
+ for (int blockCount = 0; blockCount <= refundTimeout; ++blockCount)
+ BlockUtils.mintBlock(repository);
+
+ // We don't bother to exactly calculate QORT spent running AT for several blocks, but we do know the expected range
+ long expectedMinimumBalance = deployersPostDeploymentBalance;
+ long expectedMaximumBalance = deployersInitialBalance - deployAtFee;
+
+ long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
+
+ assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
+ assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
+ }
+
+ protected void describeAt(Repository repository, String atAddress) throws DataException {
+ ATData atData = repository.getATRepository().fromATAddress(atAddress);
+ CrossChainTradeData tradeData = getInstance().populateTradeData(repository, atData);
+
+ Function epochMilliFormatter = (timestamp) -> LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC).format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM));
+ int currentBlockHeight = repository.getBlockRepository().getBlockchainHeight();
+
+ System.out.print(String.format("%s:\n"
+ + "\tmode: %s\n"
+ + "\tcreator: %s,\n"
+ + "\tcreation timestamp: %s,\n"
+ + "\tcurrent balance: %s QORT,\n"
+ + "\tis finished: %b,\n"
+ + "\tredeem payout: %s QORT,\n"
+ + "\texpected " + getName() + ": %s " + getSymbol() + ",\n"
+ + "\tcurrent block height: %d,\n",
+ tradeData.qortalAtAddress,
+ tradeData.mode,
+ tradeData.qortalCreator,
+ epochMilliFormatter.apply(tradeData.creationTimestamp),
+ Amounts.prettyAmount(tradeData.qortBalance),
+ atData.getIsFinished(),
+ Amounts.prettyAmount(tradeData.qortAmount),
+ Amounts.prettyAmount(tradeData.expectedForeignAmount),
+ currentBlockHeight));
+
+ describeRefundAt(tradeData, epochMilliFormatter);
+ }
+
+ protected void describeRefundAt(CrossChainTradeData tradeData, Function epochMilliFormatter) {
+ if (tradeData.mode != AcctMode.OFFERING && tradeData.mode != AcctMode.CANCELLED) {
+ System.out.println(String.format("\trefund timeout: %d minutes,\n"
+ + "\trefund height: block %d,\n"
+ + "\tHASH160 of secret-A: %s,\n"
+ + "\t" + getName() + " P2SH-A nLockTime: %d (%s),\n"
+ + "\ttrade partner: %s\n"
+ + "\tpartner's receiving address: %s",
+ tradeData.refundTimeout,
+ tradeData.tradeRefundHeight,
+ HashCode.fromBytes(tradeData.hashOfSecretA).toString().substring(0, 40),
+ tradeData.lockTimeA, epochMilliFormatter.apply(tradeData.lockTimeA * 1000L),
+ tradeData.qortalPartnerAddress,
+ tradeData.qortalPartnerReceivingAddress));
+ }
+ }
+
+ protected PrivateKeyAccount createTradeAccount(Repository repository) {
+ // We actually use a known test account with funds to avoid PoW compute
+ return Common.getTestAccount(repository, "alice");
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/qortal/test/crosschain/BitcoinTests.java b/src/test/java/org/qortal/test/crosschain/BitcoinTests.java
index 1096d7ad..684f1cd6 100644
--- a/src/test/java/org/qortal/test/crosschain/BitcoinTests.java
+++ b/src/test/java/org/qortal/test/crosschain/BitcoinTests.java
@@ -1,121 +1,59 @@
package org.qortal.test.crosschain;
-import static org.junit.Assert.*;
-
-import java.util.Arrays;
-
-import org.bitcoinj.core.Transaction;
-import org.bitcoinj.store.BlockStoreException;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.qortal.crosschain.Bitcoin;
-import org.qortal.crosschain.ForeignBlockchainException;
-import org.qortal.crosschain.BitcoinyHTLC;
-import org.qortal.repository.DataException;
-import org.qortal.test.common.Common;
+import org.qortal.crosschain.Bitcoiny;
-public class BitcoinTests extends Common {
+public class BitcoinTests extends BitcoinyTests {
- private Bitcoin bitcoin;
-
- @Before
- public void beforeTest() throws DataException {
- Common.useDefaultSettings(); // TestNet3
- bitcoin = Bitcoin.getInstance();
+ @Override
+ protected String getCoinName() {
+ return "Bitcoin";
}
- @After
- public void afterTest() {
+ @Override
+ protected String getCoinSymbol() {
+ return "BTC";
+ }
+
+ @Override
+ protected Bitcoiny getCoin() {
+ return Bitcoin.getInstance();
+ }
+
+ @Override
+ protected void resetCoinForTesting() {
Bitcoin.resetForTesting();
- bitcoin = null;
+ }
+
+ @Override
+ protected String getDeterministicKey58() {
+ return "tprv8ZgxMBicQKsPdahhFSrCdvC1bsWyzHHZfTneTVqUXN6s1wEtZLwAkZXzFP6TYLg2aQMecZLXLre5bTVGajEB55L1HYJcawpdFG66STVAWPJ";
+ }
+
+ @Override
+ protected String getRecipient() {
+ return "2N8WCg52ULCtDSMjkgVTm5mtPdCsUptkHWE";
}
@Test
@Ignore("Often fails due to unreliable BTC testnet ElectrumX servers")
- public void testGetMedianBlockTime() throws BlockStoreException, ForeignBlockchainException {
- System.out.println(String.format("Starting BTC instance..."));
- System.out.println(String.format("BTC instance started"));
-
- long before = System.currentTimeMillis();
- System.out.println(String.format("Bitcoin median blocktime: %d", bitcoin.getMedianBlockTime()));
- long afterFirst = System.currentTimeMillis();
-
- System.out.println(String.format("Bitcoin median blocktime: %d", bitcoin.getMedianBlockTime()));
- long afterSecond = System.currentTimeMillis();
-
- long firstPeriod = afterFirst - before;
- long secondPeriod = afterSecond - afterFirst;
-
- System.out.println(String.format("1st call: %d ms, 2nd call: %d ms", firstPeriod, secondPeriod));
-
- assertTrue("2nd call should be quicker than 1st", secondPeriod < firstPeriod);
- assertTrue("2nd call should take less than 5 seconds", secondPeriod < 5000L);
- }
+ public void testGetMedianBlockTime() {}
@Test
@Ignore("Often fails due to unreliable BTC testnet ElectrumX servers")
- public void testFindHtlcSecret() throws ForeignBlockchainException {
- // This actually exists on TEST3 but can take a while to fetch
- String p2shAddress = "2N8WCg52ULCtDSMjkgVTm5mtPdCsUptkHWE";
-
- byte[] expectedSecret = "This string is exactly 32 bytes!".getBytes();
- byte[] secret = BitcoinyHTLC.findHtlcSecret(bitcoin, p2shAddress);
-
- assertNotNull(secret);
- assertTrue("secret incorrect", Arrays.equals(expectedSecret, secret));
- }
+ public void testFindHtlcSecret() {}
@Test
@Ignore("Often fails due to unreliable BTC testnet ElectrumX servers")
- public void testBuildSpend() {
- String xprv58 = "tprv8ZgxMBicQKsPdahhFSrCdvC1bsWyzHHZfTneTVqUXN6s1wEtZLwAkZXzFP6TYLg2aQMecZLXLre5bTVGajEB55L1HYJcawpdFG66STVAWPJ";
-
- String recipient = "2N8WCg52ULCtDSMjkgVTm5mtPdCsUptkHWE";
- long amount = 1000L;
-
- Transaction transaction = bitcoin.buildSpend(xprv58, recipient, amount);
- assertNotNull(transaction);
-
- // Check spent key caching doesn't affect outcome
-
- transaction = bitcoin.buildSpend(xprv58, recipient, amount);
- assertNotNull(transaction);
- }
+ public void testBuildSpend() {}
@Test
@Ignore("Often fails due to unreliable BTC testnet ElectrumX servers")
- public void testGetWalletBalance() throws ForeignBlockchainException {
- String xprv58 = "tprv8ZgxMBicQKsPdahhFSrCdvC1bsWyzHHZfTneTVqUXN6s1wEtZLwAkZXzFP6TYLg2aQMecZLXLre5bTVGajEB55L1HYJcawpdFG66STVAWPJ";
-
- Long balance = bitcoin.getWalletBalance(xprv58);
-
- assertNotNull(balance);
-
- System.out.println(bitcoin.format(balance));
-
- // Check spent key caching doesn't affect outcome
-
- Long repeatBalance = bitcoin.getWalletBalance(xprv58);
-
- assertNotNull(repeatBalance);
-
- System.out.println(bitcoin.format(repeatBalance));
-
- assertEquals(balance, repeatBalance);
- }
+ public void testGetWalletBalance() {}
@Test
@Ignore("Often fails due to unreliable BTC testnet ElectrumX servers")
- public void testGetUnusedReceiveAddress() throws ForeignBlockchainException {
- String xprv58 = "tprv8ZgxMBicQKsPdahhFSrCdvC1bsWyzHHZfTneTVqUXN6s1wEtZLwAkZXzFP6TYLg2aQMecZLXLre5bTVGajEB55L1HYJcawpdFG66STVAWPJ";
-
- String address = bitcoin.getUnusedReceiveAddress(xprv58);
-
- assertNotNull(address);
-
- System.out.println(address);
- }
-
+ public void testGetUnusedReceiveAddress() {}
}
diff --git a/src/test/java/org/qortal/test/crosschain/BitcoinyTests.java b/src/test/java/org/qortal/test/crosschain/BitcoinyTests.java
new file mode 100644
index 00000000..b29fffd4
--- /dev/null
+++ b/src/test/java/org/qortal/test/crosschain/BitcoinyTests.java
@@ -0,0 +1,130 @@
+package org.qortal.test.crosschain;
+
+import org.bitcoinj.core.Transaction;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.qortal.crosschain.Bitcoiny;
+import org.qortal.crosschain.BitcoinyHTLC;
+import org.qortal.crosschain.ForeignBlockchainException;
+import org.qortal.repository.DataException;
+import org.qortal.test.common.Common;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+public abstract class BitcoinyTests extends Common {
+
+ protected Bitcoiny bitcoiny;
+
+ protected abstract String getCoinName();
+
+ protected abstract String getCoinSymbol();
+
+ protected abstract Bitcoiny getCoin();
+
+ protected abstract void resetCoinForTesting();
+
+ protected abstract String getDeterministicKey58();
+
+ protected abstract String getRecipient();
+
+ @Before
+ public void beforeTest() throws DataException {
+ Common.useDefaultSettings(); // TestNet3
+ bitcoiny = getCoin();
+ }
+
+ @After
+ public void afterTest() {
+ resetCoinForTesting();
+ bitcoiny = null;
+ }
+
+ @Test
+ public void testGetMedianBlockTime() throws ForeignBlockchainException {
+ System.out.println(String.format("Starting " + getCoinSymbol() + " instance..."));
+ System.out.println(String.format(getCoinSymbol() + " instance started"));
+
+ long before = System.currentTimeMillis();
+ System.out.println(String.format(getCoinName() + " median blocktime: %d", bitcoiny.getMedianBlockTime()));
+ long afterFirst = System.currentTimeMillis();
+
+ System.out.println(String.format(getCoinName() + " median blocktime: %d", bitcoiny.getMedianBlockTime()));
+ long afterSecond = System.currentTimeMillis();
+
+ long firstPeriod = afterFirst - before;
+ long secondPeriod = afterSecond - afterFirst;
+
+ System.out.println(String.format("1st call: %d ms, 2nd call: %d ms", firstPeriod, secondPeriod));
+
+ makeGetMedianBlockTimeAssertions(firstPeriod, secondPeriod);
+ }
+
+ public void makeGetMedianBlockTimeAssertions(long firstPeriod, long secondPeriod) {
+ assertTrue("2nd call should be quicker than 1st", secondPeriod < firstPeriod);
+ assertTrue("2nd call should take less than 5 seconds", secondPeriod < 5000L);
+ }
+
+ @Test
+ public void testFindHtlcSecret() throws ForeignBlockchainException {
+ // This actually exists on TEST3 but can take a while to fetch
+ String p2shAddress = "2N8WCg52ULCtDSMjkgVTm5mtPdCsUptkHWE";
+
+ byte[] expectedSecret = "This string is exactly 32 bytes!".getBytes();
+ byte[] secret = BitcoinyHTLC.findHtlcSecret(bitcoiny, p2shAddress);
+
+ assertNotNull(secret);
+ assertTrue("secret incorrect", Arrays.equals(expectedSecret, secret));
+ }
+
+ @Test
+ public void testBuildSpend() {
+ String xprv58 = getDeterministicKey58();
+
+ String recipient = getRecipient();
+ long amount = 1000L;
+
+ Transaction transaction = bitcoiny.buildSpend(xprv58, recipient, amount);
+ assertNotNull(transaction);
+
+ // Check spent key caching doesn't affect outcome
+
+ transaction = bitcoiny.buildSpend(xprv58, recipient, amount);
+ assertNotNull(transaction);
+ }
+
+ @Test
+ public void testGetWalletBalance() throws ForeignBlockchainException {
+ String xprv58 = getDeterministicKey58();
+
+ Long balance = bitcoiny.getWalletBalance(xprv58);
+
+ assertNotNull(balance);
+
+ System.out.println(bitcoiny.format(balance));
+
+ // Check spent key caching doesn't affect outcome
+
+ Long repeatBalance = bitcoiny.getWalletBalance(xprv58);
+
+ assertNotNull(repeatBalance);
+
+ System.out.println(bitcoiny.format(repeatBalance));
+
+ assertEquals(balance, repeatBalance);
+ }
+
+ @Test
+ public void testGetUnusedReceiveAddress() throws ForeignBlockchainException {
+ String xprv58 = getDeterministicKey58();
+
+ String address = bitcoiny.getUnusedReceiveAddress(xprv58);
+
+ assertNotNull(address);
+
+ System.out.println(address);
+ }
+
+}
diff --git a/src/test/java/org/qortal/test/crosschain/DigibyteTests.java b/src/test/java/org/qortal/test/crosschain/DigibyteTests.java
index dbe81c82..d95f1bd5 100644
--- a/src/test/java/org/qortal/test/crosschain/DigibyteTests.java
+++ b/src/test/java/org/qortal/test/crosschain/DigibyteTests.java
@@ -1,115 +1,48 @@
package org.qortal.test.crosschain;
-import static org.junit.Assert.*;
-
-import java.util.Arrays;
-
-import org.bitcoinj.core.Transaction;
-import org.bitcoinj.store.BlockStoreException;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
-import org.qortal.crosschain.ForeignBlockchainException;
+import org.qortal.crosschain.Bitcoiny;
import org.qortal.crosschain.Digibyte;
-import org.qortal.crosschain.BitcoinyHTLC;
-import org.qortal.repository.DataException;
-import org.qortal.test.common.Common;
-public class DigibyteTests extends Common {
+public class DigibyteTests extends BitcoinyTests {
- private Digibyte digibyte;
-
- @Before
- public void beforeTest() throws DataException {
- Common.useDefaultSettings(); // TestNet3
- digibyte = Digibyte.getInstance();
+ @Override
+ protected String getCoinName() {
+ return "Digibyte";
}
- @After
- public void afterTest() {
+ @Override
+ protected String getCoinSymbol() {
+ return "DGB";
+ }
+
+ @Override
+ protected Bitcoiny getCoin() {
+ return Digibyte.getInstance();
+ }
+
+ @Override
+ protected void resetCoinForTesting() {
Digibyte.resetForTesting();
- digibyte = null;
}
- @Test
- public void testGetMedianBlockTime() throws BlockStoreException, ForeignBlockchainException {
- long before = System.currentTimeMillis();
- System.out.println(String.format("Digibyte median blocktime: %d", digibyte.getMedianBlockTime()));
- long afterFirst = System.currentTimeMillis();
+ @Override
+ protected String getDeterministicKey58() {
+ return "xpub661MyMwAqRbcEnabTLX5uebYcsE3uG5y7ve9jn1VK8iY1MaU3YLoLJEe8sTu2YVav5Zka5qf2dmMssfxmXJTqZnazZL2kL7M2tNKwEoC34R";
+ }
- System.out.println(String.format("Digibyte median blocktime: %d", digibyte.getMedianBlockTime()));
- long afterSecond = System.currentTimeMillis();
-
- long firstPeriod = afterFirst - before;
- long secondPeriod = afterSecond - afterFirst;
-
- System.out.println(String.format("1st call: %d ms, 2nd call: %d ms", firstPeriod, secondPeriod));
-
- assertTrue("2nd call should be quicker than 1st", secondPeriod < firstPeriod);
- assertTrue("2nd call should take less than 5 seconds", secondPeriod < 5000L);
+ @Override
+ protected String getRecipient() {
+ return "2N8WCg52ULCtDSMjkgVTm5mtPdCsUptkHWE";
}
@Test
@Ignore(value = "Doesn't work, to be fixed later")
- public void testFindHtlcSecret() throws ForeignBlockchainException {
- // This actually exists on TEST3 but can take a while to fetch
- String p2shAddress = "2N8WCg52ULCtDSMjkgVTm5mtPdCsUptkHWE";
-
- byte[] expectedSecret = "This string is exactly 32 bytes!".getBytes();
- byte[] secret = BitcoinyHTLC.findHtlcSecret(digibyte, p2shAddress);
-
- assertNotNull("secret not found", secret);
- assertTrue("secret incorrect", Arrays.equals(expectedSecret, secret));
- }
+ public void testFindHtlcSecret() {}
@Test
@Ignore(value = "No testnet nodes available, so we can't regularly test buildSpend yet")
- public void testBuildSpend() {
- String xprv58 = "tprv8ZgxMBicQKsPdahhFSrCdvC1bsWyzHHZfTneTVqUXN6s1wEtZLwAkZXzFP6TYLg2aQMecZLXLre5bTVGajEB55L1HYJcawpdFG66STVAWPJ";
+ public void testBuildSpend() {}
- String recipient = "2N8WCg52ULCtDSMjkgVTm5mtPdCsUptkHWE";
- long amount = 1000L;
-
- Transaction transaction = digibyte.buildSpend(xprv58, recipient, amount);
- assertNotNull("insufficient funds", transaction);
-
- // Check spent key caching doesn't affect outcome
-
- transaction = digibyte.buildSpend(xprv58, recipient, amount);
- assertNotNull("insufficient funds", transaction);
}
-
- @Test
- public void testGetWalletBalance() throws ForeignBlockchainException {
- String xprv58 = "xpub661MyMwAqRbcEnabTLX5uebYcsE3uG5y7ve9jn1VK8iY1MaU3YLoLJEe8sTu2YVav5Zka5qf2dmMssfxmXJTqZnazZL2kL7M2tNKwEoC34R";
-
- Long balance = digibyte.getWalletBalance(xprv58);
-
- assertNotNull(balance);
-
- System.out.println(digibyte.format(balance));
-
- // Check spent key caching doesn't affect outcome
-
- Long repeatBalance = digibyte.getWalletBalance(xprv58);
-
- assertNotNull(repeatBalance);
-
- System.out.println(digibyte.format(repeatBalance));
-
- assertEquals(balance, repeatBalance);
- }
-
- @Test
- public void testGetUnusedReceiveAddress() throws ForeignBlockchainException {
- String xprv58 = "xpub661MyMwAqRbcEnabTLX5uebYcsE3uG5y7ve9jn1VK8iY1MaU3YLoLJEe8sTu2YVav5Zka5qf2dmMssfxmXJTqZnazZL2kL7M2tNKwEoC34R";
-
- String address = digibyte.getUnusedReceiveAddress(xprv58);
-
- assertNotNull(address);
-
- System.out.println(address);
- }
-
-}
diff --git a/src/test/java/org/qortal/test/crosschain/DogecoinTests.java b/src/test/java/org/qortal/test/crosschain/DogecoinTests.java
index 6c070d09..62982437 100644
--- a/src/test/java/org/qortal/test/crosschain/DogecoinTests.java
+++ b/src/test/java/org/qortal/test/crosschain/DogecoinTests.java
@@ -1,115 +1,47 @@
package org.qortal.test.crosschain;
-import org.bitcoinj.core.Transaction;
-import org.bitcoinj.store.BlockStoreException;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
-import org.qortal.crosschain.BitcoinyHTLC;
-import org.qortal.crosschain.ForeignBlockchainException;
+import org.qortal.crosschain.Bitcoiny;
import org.qortal.crosschain.Dogecoin;
-import org.qortal.repository.DataException;
-import org.qortal.test.common.Common;
-import java.util.Arrays;
+public class DogecoinTests extends BitcoinyTests {
-import static org.junit.Assert.*;
-
-public class DogecoinTests extends Common {
-
- private Dogecoin dogecoin;
-
- @Before
- public void beforeTest() throws DataException {
- Common.useDefaultSettings(); // TestNet3
- dogecoin = Dogecoin.getInstance();
+ @Override
+ protected String getCoinName() {
+ return "Dogecoin";
}
- @After
- public void afterTest() {
+ @Override
+ protected String getCoinSymbol() {
+ return "DOGE";
+ }
+
+ @Override
+ protected Bitcoiny getCoin() {
+ return Dogecoin.getInstance();
+ }
+
+ @Override
+ protected void resetCoinForTesting() {
Dogecoin.resetForTesting();
- dogecoin = null;
}
- @Test
- public void testGetMedianBlockTime() throws BlockStoreException, ForeignBlockchainException {
- long before = System.currentTimeMillis();
- System.out.println(String.format("Dogecoin median blocktime: %d", dogecoin.getMedianBlockTime()));
- long afterFirst = System.currentTimeMillis();
+ @Override
+ protected String getDeterministicKey58() {
+ return "dgpv51eADS3spNJh9drNeW1Tc1P9z2LyaQRXPBortsq6yice1k47C2u2Prvgxycr2ihNBWzKZ2LthcBBGiYkWZ69KUTVkcLVbnjq7pD8mnApEru";
+ }
- System.out.println(String.format("Dogecoin median blocktime: %d", dogecoin.getMedianBlockTime()));
- long afterSecond = System.currentTimeMillis();
-
- long firstPeriod = afterFirst - before;
- long secondPeriod = afterSecond - afterFirst;
-
- System.out.println(String.format("1st call: %d ms, 2nd call: %d ms", firstPeriod, secondPeriod));
-
- assertTrue("2nd call should be quicker than 1st", secondPeriod < firstPeriod);
- assertTrue("2nd call should take less than 5 seconds", secondPeriod < 5000L);
+ @Override
+ protected String getRecipient() {
+ return null;
}
@Test
@Ignore(value = "Doesn't work, to be fixed later")
- public void testFindHtlcSecret() throws ForeignBlockchainException {
- // This actually exists on TEST3 but can take a while to fetch
- String p2shAddress = "2N8WCg52ULCtDSMjkgVTm5mtPdCsUptkHWE";
-
- byte[] expectedSecret = "This string is exactly 32 bytes!".getBytes();
- byte[] secret = BitcoinyHTLC.findHtlcSecret(dogecoin, p2shAddress);
-
- assertNotNull("secret not found", secret);
- assertTrue("secret incorrect", Arrays.equals(expectedSecret, secret));
- }
+ public void testFindHtlcSecret() {}
@Test
@Ignore(value = "No testnet nodes available, so we can't regularly test buildSpend yet")
- public void testBuildSpend() {
- String xprv58 = "dgpv51eADS3spNJh9drNeW1Tc1P9z2LyaQRXPBortsq6yice1k47C2u2Prvgxycr2ihNBWzKZ2LthcBBGiYkWZ69KUTVkcLVbnjq7pD8mnApEru";
-
- String recipient = "DP1iFao33xdEPa5vaArpj7sykfzKNeiJeX";
- long amount = 1000L;
-
- Transaction transaction = dogecoin.buildSpend(xprv58, recipient, amount);
- assertNotNull("insufficient funds", transaction);
-
- // Check spent key caching doesn't affect outcome
-
- transaction = dogecoin.buildSpend(xprv58, recipient, amount);
- assertNotNull("insufficient funds", transaction);
- }
-
- @Test
- public void testGetWalletBalance() throws ForeignBlockchainException {
- String xprv58 = "dgpv51eADS3spNJh9drNeW1Tc1P9z2LyaQRXPBortsq6yice1k47C2u2Prvgxycr2ihNBWzKZ2LthcBBGiYkWZ69KUTVkcLVbnjq7pD8mnApEru";
-
- Long balance = dogecoin.getWalletBalance(xprv58);
-
- assertNotNull(balance);
-
- System.out.println(dogecoin.format(balance));
-
- // Check spent key caching doesn't affect outcome
-
- Long repeatBalance = dogecoin.getWalletBalance(xprv58);
-
- assertNotNull(repeatBalance);
-
- System.out.println(dogecoin.format(repeatBalance));
-
- assertEquals(balance, repeatBalance);
- }
-
- @Test
- public void testGetUnusedReceiveAddress() throws ForeignBlockchainException {
- String xprv58 = "dgpv51eADS3spNJh9drNeW1Tc1P9z2LyaQRXPBortsq6yice1k47C2u2Prvgxycr2ihNBWzKZ2LthcBBGiYkWZ69KUTVkcLVbnjq7pD8mnApEru";
-
- String address = dogecoin.getUnusedReceiveAddress(xprv58);
-
- assertNotNull(address);
-
- System.out.println(address);
- }
-
+ public void testBuildSpend() {}
}
diff --git a/src/test/java/org/qortal/test/crosschain/LitecoinTests.java b/src/test/java/org/qortal/test/crosschain/LitecoinTests.java
index 5ea7bc95..66c631e5 100644
--- a/src/test/java/org/qortal/test/crosschain/LitecoinTests.java
+++ b/src/test/java/org/qortal/test/crosschain/LitecoinTests.java
@@ -1,113 +1,43 @@
package org.qortal.test.crosschain;
-import static org.junit.Assert.*;
-
-import java.util.Arrays;
-
-import org.bitcoinj.core.Transaction;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
-import org.qortal.crosschain.ForeignBlockchainException;
+import org.qortal.crosschain.Bitcoiny;
import org.qortal.crosschain.Litecoin;
-import org.qortal.crosschain.BitcoinyHTLC;
-import org.qortal.repository.DataException;
-import org.qortal.test.common.Common;
-public class LitecoinTests extends Common {
+public class LitecoinTests extends BitcoinyTests {
- private Litecoin litecoin;
-
- @Before
- public void beforeTest() throws DataException {
- Common.useDefaultSettings(); // TestNet3
- litecoin = Litecoin.getInstance();
+ @Override
+ protected String getCoinName() {
+ return "Litecoin";
}
- @After
- public void afterTest() {
+ @Override
+ protected String getCoinSymbol() {
+ return "LTC";
+ }
+
+ @Override
+ protected Bitcoiny getCoin() {
+ return Litecoin.getInstance();
+ }
+
+ @Override
+ protected void resetCoinForTesting() {
Litecoin.resetForTesting();
- litecoin = null;
}
- @Test
- public void testGetMedianBlockTime() throws ForeignBlockchainException {
- long before = System.currentTimeMillis();
- System.out.println(String.format("Litecoin median blocktime: %d", litecoin.getMedianBlockTime()));
- long afterFirst = System.currentTimeMillis();
+ @Override
+ protected String getDeterministicKey58() {
+ return "tprv8ZgxMBicQKsPdahhFSrCdvC1bsWyzHHZfTneTVqUXN6s1wEtZLwAkZXzFP6TYLg2aQMecZLXLre5bTVGajEB55L1HYJcawpdFG66STVAWPJ";
+ }
- System.out.println(String.format("Litecoin median blocktime: %d", litecoin.getMedianBlockTime()));
- long afterSecond = System.currentTimeMillis();
-
- long firstPeriod = afterFirst - before;
- long secondPeriod = afterSecond - afterFirst;
-
- System.out.println(String.format("1st call: %d ms, 2nd call: %d ms", firstPeriod, secondPeriod));
-
- assertTrue("2nd call should be quicker than 1st", secondPeriod < firstPeriod);
- assertTrue("2nd call should take less than 5 seconds", secondPeriod < 5000L);
+ @Override
+ protected String getRecipient() {
+ return "2N8WCg52ULCtDSMjkgVTm5mtPdCsUptkHWE";
}
@Test
@Ignore(value = "Doesn't work, to be fixed later")
- public void testFindHtlcSecret() throws ForeignBlockchainException {
- // This actually exists on TEST3 but can take a while to fetch
- String p2shAddress = "2N8WCg52ULCtDSMjkgVTm5mtPdCsUptkHWE";
-
- byte[] expectedSecret = "This string is exactly 32 bytes!".getBytes();
- byte[] secret = BitcoinyHTLC.findHtlcSecret(litecoin, p2shAddress);
-
- assertNotNull("secret not found", secret);
- assertTrue("secret incorrect", Arrays.equals(expectedSecret, secret));
- }
-
- @Test
- public void testBuildSpend() {
- String xprv58 = "tprv8ZgxMBicQKsPdahhFSrCdvC1bsWyzHHZfTneTVqUXN6s1wEtZLwAkZXzFP6TYLg2aQMecZLXLre5bTVGajEB55L1HYJcawpdFG66STVAWPJ";
-
- String recipient = "2N8WCg52ULCtDSMjkgVTm5mtPdCsUptkHWE";
- long amount = 1000L;
-
- Transaction transaction = litecoin.buildSpend(xprv58, recipient, amount);
- assertNotNull("insufficient funds", transaction);
-
- // Check spent key caching doesn't affect outcome
-
- transaction = litecoin.buildSpend(xprv58, recipient, amount);
- assertNotNull("insufficient funds", transaction);
- }
-
- @Test
- public void testGetWalletBalance() throws ForeignBlockchainException {
- String xprv58 = "tprv8ZgxMBicQKsPdahhFSrCdvC1bsWyzHHZfTneTVqUXN6s1wEtZLwAkZXzFP6TYLg2aQMecZLXLre5bTVGajEB55L1HYJcawpdFG66STVAWPJ";
-
- Long balance = litecoin.getWalletBalance(xprv58);
-
- assertNotNull(balance);
-
- System.out.println(litecoin.format(balance));
-
- // Check spent key caching doesn't affect outcome
-
- Long repeatBalance = litecoin.getWalletBalance(xprv58);
-
- assertNotNull(repeatBalance);
-
- System.out.println(litecoin.format(repeatBalance));
-
- assertEquals(balance, repeatBalance);
- }
-
- @Test
- public void testGetUnusedReceiveAddress() throws ForeignBlockchainException {
- String xprv58 = "tprv8ZgxMBicQKsPdahhFSrCdvC1bsWyzHHZfTneTVqUXN6s1wEtZLwAkZXzFP6TYLg2aQMecZLXLre5bTVGajEB55L1HYJcawpdFG66STVAWPJ";
-
- String address = litecoin.getUnusedReceiveAddress(xprv58);
-
- assertNotNull(address);
-
- System.out.println(address);
- }
-
+ public void testFindHtlcSecret() {}
}
diff --git a/src/test/java/org/qortal/test/crosschain/PirateChainTests.java b/src/test/java/org/qortal/test/crosschain/PirateChainTests.java
index 9502e45a..b212aea1 100644
--- a/src/test/java/org/qortal/test/crosschain/PirateChainTests.java
+++ b/src/test/java/org/qortal/test/crosschain/PirateChainTests.java
@@ -3,57 +3,53 @@ package org.qortal.test.crosschain;
import cash.z.wallet.sdk.rpc.CompactFormats.*;
import com.google.common.hash.HashCode;
import com.google.common.primitives.Bytes;
-import org.bitcoinj.core.Transaction;
-import org.bitcoinj.store.BlockStoreException;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.qortal.controller.tradebot.TradeBot;
import org.qortal.crosschain.*;
import org.qortal.crypto.Crypto;
-import org.qortal.repository.DataException;
-import org.qortal.test.common.Common;
import org.qortal.transform.TransformationException;
-import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.*;
import static org.qortal.crosschain.BitcoinyHTLC.Status.*;
-public class PirateChainTests extends Common {
+public class PirateChainTests extends BitcoinyTests {
- private PirateChain pirateChain;
-
- @Before
- public void beforeTest() throws DataException {
- Common.useDefaultSettings();
- pirateChain = PirateChain.getInstance();
+ @Override
+ protected String getCoinName() {
+ return "PirateChain";
}
- @After
- public void afterTest() {
+ @Override
+ protected String getCoinSymbol() {
+ return "ARRR";
+ }
+
+ @Override
+ protected Bitcoiny getCoin() {
+ return PirateChain.getInstance();
+ }
+
+ @Override
+ protected void resetCoinForTesting() {
Litecoin.resetForTesting();
- pirateChain = null;
}
- @Test
- public void testGetMedianBlockTime() throws BlockStoreException, ForeignBlockchainException {
- long before = System.currentTimeMillis();
- System.out.println(String.format("Pirate Chain median blocktime: %d", pirateChain.getMedianBlockTime()));
- long afterFirst = System.currentTimeMillis();
+ @Override
+ protected String getDeterministicKey58() {
+ return null;
+ }
- System.out.println(String.format("Pirate Chain median blocktime: %d", pirateChain.getMedianBlockTime()));
- long afterSecond = System.currentTimeMillis();
+ @Override
+ protected String getRecipient() {
+ return null;
+ }
- long firstPeriod = afterFirst - before;
- long secondPeriod = afterSecond - afterFirst;
-
- System.out.println(String.format("1st call: %d ms, 2nd call: %d ms", firstPeriod, secondPeriod));
-
- assertTrue("1st call should take less than 5 seconds", firstPeriod < 5000L);
- assertTrue("2nd call should take less than 5 seconds", secondPeriod < 5000L);
+ public void makeGetMedianBlockTimeAssertions(long firstPeriod, long secondPeriod) {
+ assertTrue("1st call should take less than 5 seconds", firstPeriod < 5000L);
+ assertTrue("2nd call should take less than 5 seconds", secondPeriod < 5000L);
}
@Test
@@ -62,7 +58,7 @@ public class PirateChainTests extends Common {
int count = 20;
long before = System.currentTimeMillis();
- List compactBlocks = pirateChain.getCompactBlocks(startHeight, count);
+ List compactBlocks = ((PirateChain) bitcoiny).getCompactBlocks(startHeight, count);
long after = System.currentTimeMillis();
System.out.println(String.format("Retrieval took: %d ms", after-before));
@@ -82,7 +78,7 @@ public class PirateChainTests extends Common {
Bytes.reverse(txBytes);
String txHashBE = HashCode.fromBytes(txBytes).toString();
- byte[] rawTransaction = pirateChain.getBlockchainProvider().getRawTransaction(txHashBE);
+ byte[] rawTransaction = bitcoiny.getBlockchainProvider().getRawTransaction(txHashBE);
assertNotNull(rawTransaction);
}
@@ -121,7 +117,7 @@ public class PirateChainTests extends Common {
String p2shAddress = "ba6Q5HWrWtmfU2WZqQbrFdRYsafA45cUAt";
long p2shFee = 10000;
final long minimumAmount = 10000 + p2shFee;
- BitcoinyHTLC.Status htlcStatus = PirateChainHTLC.determineHtlcStatus(pirateChain.getBlockchainProvider(), p2shAddress, minimumAmount);
+ BitcoinyHTLC.Status htlcStatus = PirateChainHTLC.determineHtlcStatus(bitcoiny.getBlockchainProvider(), p2shAddress, minimumAmount);
assertEquals(FUNDED, htlcStatus);
}
@@ -130,7 +126,7 @@ public class PirateChainTests extends Common {
String p2shAddress = "bYZrzSSgGp8aEGvihukoMGU8sXYrx19Wka";
long p2shFee = 10000;
final long minimumAmount = 10000 + p2shFee;
- BitcoinyHTLC.Status htlcStatus = PirateChainHTLC.determineHtlcStatus(pirateChain.getBlockchainProvider(), p2shAddress, minimumAmount);
+ BitcoinyHTLC.Status htlcStatus = PirateChainHTLC.determineHtlcStatus(bitcoiny.getBlockchainProvider(), p2shAddress, minimumAmount);
assertEquals(REDEEMED, htlcStatus);
}
@@ -139,14 +135,14 @@ public class PirateChainTests extends Common {
String p2shAddress = "bE49izfVxz8odhu8c2BcUaVFUnt7NLFRgv";
long p2shFee = 10000;
final long minimumAmount = 10000 + p2shFee;
- BitcoinyHTLC.Status htlcStatus = PirateChainHTLC.determineHtlcStatus(pirateChain.getBlockchainProvider(), p2shAddress, minimumAmount);
+ BitcoinyHTLC.Status htlcStatus = PirateChainHTLC.determineHtlcStatus(bitcoiny.getBlockchainProvider(), p2shAddress, minimumAmount);
assertEquals(REFUNDED, htlcStatus);
}
@Test
public void testGetTxidForUnspentAddress() throws ForeignBlockchainException {
String p2shAddress = "ba6Q5HWrWtmfU2WZqQbrFdRYsafA45cUAt";
- String txid = PirateChainHTLC.getFundingTxid(pirateChain.getBlockchainProvider(), p2shAddress);
+ String txid = PirateChainHTLC.getFundingTxid(bitcoiny.getBlockchainProvider(), p2shAddress);
// Reverse the byte order of the txid used by block explorers, to get to big-endian form
byte[] expectedTxidLE = HashCode.fromString("fea4b0c1abcf8f0f3ddc2fa2f9438501ee102aad62a9ff18a5ce7d08774755c0").asBytes();
@@ -161,7 +157,7 @@ public class PirateChainTests extends Common {
String p2shAddress = "ba6Q5HWrWtmfU2WZqQbrFdRYsafA45cUAt";
long p2shFee = 10000;
final long minimumAmount = 10000 + p2shFee;
- String txid = PirateChainHTLC.getUnspentFundingTxid(pirateChain.getBlockchainProvider(), p2shAddress, minimumAmount);
+ String txid = PirateChainHTLC.getUnspentFundingTxid(bitcoiny.getBlockchainProvider(), p2shAddress, minimumAmount);
// Reverse the byte order of the txid used by block explorers, to get to big-endian form
byte[] expectedTxidLE = HashCode.fromString("fea4b0c1abcf8f0f3ddc2fa2f9438501ee102aad62a9ff18a5ce7d08774755c0").asBytes();
@@ -174,7 +170,7 @@ public class PirateChainTests extends Common {
@Test
public void testGetTxidForSpentAddress() throws ForeignBlockchainException {
String p2shAddress = "bE49izfVxz8odhu8c2BcUaVFUnt7NLFRgv"; //"t3KtVxeEb8srJofo6atMEpMpEP6TjEi8VqA";
- String txid = PirateChainHTLC.getFundingTxid(pirateChain.getBlockchainProvider(), p2shAddress);
+ String txid = PirateChainHTLC.getFundingTxid(bitcoiny.getBlockchainProvider(), p2shAddress);
// Reverse the byte order of the txid used by block explorers, to get to big-endian form
byte[] expectedTxidLE = HashCode.fromString("fb386fc8eea0fbf3ea37047726b92c39441652b32d8d62a274331687f7a1eca8").asBytes();
@@ -187,7 +183,7 @@ public class PirateChainTests extends Common {
@Test
public void testGetTransactionsForAddress() throws ForeignBlockchainException {
String p2shAddress = "bE49izfVxz8odhu8c2BcUaVFUnt7NLFRgv"; //"t3KtVxeEb8srJofo6atMEpMpEP6TjEi8VqA";
- List transactions = pirateChain.getBlockchainProvider()
+ List transactions = bitcoiny.getBlockchainProvider()
.getAddressBitcoinyTransactions(p2shAddress, false);
assertEquals(2, transactions.size());
@@ -232,66 +228,17 @@ public class PirateChainTests extends Common {
@Test
@Ignore(value = "Doesn't work, to be fixed later")
- public void testFindHtlcSecret() throws ForeignBlockchainException {
- // This actually exists on TEST3 but can take a while to fetch
- String p2shAddress = "2N8WCg52ULCtDSMjkgVTm5mtPdCsUptkHWE";
-
- byte[] expectedSecret = "This string is exactly 32 bytes!".getBytes();
- byte[] secret = BitcoinyHTLC.findHtlcSecret(pirateChain, p2shAddress);
-
- assertNotNull("secret not found", secret);
- assertTrue("secret incorrect", Arrays.equals(expectedSecret, secret));
- }
+ public void testFindHtlcSecret() {}
@Test
@Ignore(value = "Needs adapting for Pirate Chain")
- public void testBuildSpend() {
- String xprv58 = "tprv8ZgxMBicQKsPdahhFSrCdvC1bsWyzHHZfTneTVqUXN6s1wEtZLwAkZXzFP6TYLg2aQMecZLXLre5bTVGajEB55L1HYJcawpdFG66STVAWPJ";
-
- String recipient = "2N8WCg52ULCtDSMjkgVTm5mtPdCsUptkHWE";
- long amount = 1000L;
-
- Transaction transaction = pirateChain.buildSpend(xprv58, recipient, amount);
- assertNotNull("insufficient funds", transaction);
-
- // Check spent key caching doesn't affect outcome
-
- transaction = pirateChain.buildSpend(xprv58, recipient, amount);
- assertNotNull("insufficient funds", transaction);
- }
+ public void testBuildSpend() {}
@Test
@Ignore(value = "Needs adapting for Pirate Chain")
- public void testGetWalletBalance() throws ForeignBlockchainException {
- String xprv58 = "tprv8ZgxMBicQKsPdahhFSrCdvC1bsWyzHHZfTneTVqUXN6s1wEtZLwAkZXzFP6TYLg2aQMecZLXLre5bTVGajEB55L1HYJcawpdFG66STVAWPJ";
-
- Long balance = pirateChain.getWalletBalance(xprv58);
-
- assertNotNull(balance);
-
- System.out.println(pirateChain.format(balance));
-
- // Check spent key caching doesn't affect outcome
-
- Long repeatBalance = pirateChain.getWalletBalance(xprv58);
-
- assertNotNull(repeatBalance);
-
- System.out.println(pirateChain.format(repeatBalance));
-
- assertEquals(balance, repeatBalance);
- }
+ public void testGetWalletBalance() {}
@Test
@Ignore(value = "Needs adapting for Pirate Chain")
- public void testGetUnusedReceiveAddress() throws ForeignBlockchainException {
- String xprv58 = "tprv8ZgxMBicQKsPdahhFSrCdvC1bsWyzHHZfTneTVqUXN6s1wEtZLwAkZXzFP6TYLg2aQMecZLXLre5bTVGajEB55L1HYJcawpdFG66STVAWPJ";
-
- String address = pirateChain.getUnusedReceiveAddress(xprv58);
-
- assertNotNull(address);
-
- System.out.println(address);
- }
-
-}
+ public void testGetUnusedReceiveAddress() {}
+}
\ No newline at end of file
diff --git a/src/test/java/org/qortal/test/crosschain/RavencoinTests.java b/src/test/java/org/qortal/test/crosschain/RavencoinTests.java
index 866c41a2..d7581f74 100644
--- a/src/test/java/org/qortal/test/crosschain/RavencoinTests.java
+++ b/src/test/java/org/qortal/test/crosschain/RavencoinTests.java
@@ -1,115 +1,47 @@
package org.qortal.test.crosschain;
-import static org.junit.Assert.*;
-
-import java.util.Arrays;
-
-import org.bitcoinj.core.Transaction;
-import org.bitcoinj.store.BlockStoreException;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
-import org.qortal.crosschain.ForeignBlockchainException;
+import org.qortal.crosschain.Bitcoiny;
import org.qortal.crosschain.Ravencoin;
-import org.qortal.crosschain.BitcoinyHTLC;
-import org.qortal.repository.DataException;
-import org.qortal.test.common.Common;
-public class RavencoinTests extends Common {
+public class RavencoinTests extends BitcoinyTests {
- private Ravencoin ravencoin;
-
- @Before
- public void beforeTest() throws DataException {
- Common.useDefaultSettings(); // TestNet3
- ravencoin = Ravencoin.getInstance();
+ @Override
+ protected String getCoinName() {
+ return "Ravencoin";
}
- @After
- public void afterTest() {
+ @Override
+ protected String getCoinSymbol() {
+ return "RVN";
+ }
+
+ @Override
+ protected Bitcoiny getCoin() {
+ return Ravencoin.getInstance();
+ }
+
+ @Override
+ protected void resetCoinForTesting() {
Ravencoin.resetForTesting();
- ravencoin = null;
}
- @Test
- public void testGetMedianBlockTime() throws BlockStoreException, ForeignBlockchainException {
- long before = System.currentTimeMillis();
- System.out.println(String.format("Ravencoin median blocktime: %d", ravencoin.getMedianBlockTime()));
- long afterFirst = System.currentTimeMillis();
+ @Override
+ protected String getDeterministicKey58() {
+ return "xpub661MyMwAqRbcEt3Ge1wNmkagyb1J7yTQu4Kquvy77Ycg2iPoh7Urg8s9Jdwp7YmrqGkDKJpUVjsZXSSsQgmAVUC17ZVQQeoWMzm7vDTt1y7";
+ }
- System.out.println(String.format("Ravencoin median blocktime: %d", ravencoin.getMedianBlockTime()));
- long afterSecond = System.currentTimeMillis();
-
- long firstPeriod = afterFirst - before;
- long secondPeriod = afterSecond - afterFirst;
-
- System.out.println(String.format("1st call: %d ms, 2nd call: %d ms", firstPeriod, secondPeriod));
-
- assertTrue("2nd call should be quicker than 1st", secondPeriod < firstPeriod);
- assertTrue("2nd call should take less than 5 seconds", secondPeriod < 5000L);
+ @Override
+ protected String getRecipient() {
+ return null;
}
@Test
@Ignore(value = "Doesn't work, to be fixed later")
- public void testFindHtlcSecret() throws ForeignBlockchainException {
- // This actually exists on TEST3 but can take a while to fetch
- String p2shAddress = "2N8WCg52ULCtDSMjkgVTm5mtPdCsUptkHWE";
-
- byte[] expectedSecret = "This string is exactly 32 bytes!".getBytes();
- byte[] secret = BitcoinyHTLC.findHtlcSecret(ravencoin, p2shAddress);
-
- assertNotNull("secret not found", secret);
- assertTrue("secret incorrect", Arrays.equals(expectedSecret, secret));
- }
+ public void testFindHtlcSecret() {}
@Test
@Ignore(value = "No testnet nodes available, so we can't regularly test buildSpend yet")
- public void testBuildSpend() {
- String xprv58 = "tprv8ZgxMBicQKsPdahhFSrCdvC1bsWyzHHZfTneTVqUXN6s1wEtZLwAkZXzFP6TYLg2aQMecZLXLre5bTVGajEB55L1HYJcawpdFG66STVAWPJ";
-
- String recipient = "2N8WCg52ULCtDSMjkgVTm5mtPdCsUptkHWE";
- long amount = 1000L;
-
- Transaction transaction = ravencoin.buildSpend(xprv58, recipient, amount);
- assertNotNull("insufficient funds", transaction);
-
- // Check spent key caching doesn't affect outcome
-
- transaction = ravencoin.buildSpend(xprv58, recipient, amount);
- assertNotNull("insufficient funds", transaction);
- }
-
- @Test
- public void testGetWalletBalance() throws ForeignBlockchainException {
- String xprv58 = "xpub661MyMwAqRbcEt3Ge1wNmkagyb1J7yTQu4Kquvy77Ycg2iPoh7Urg8s9Jdwp7YmrqGkDKJpUVjsZXSSsQgmAVUC17ZVQQeoWMzm7vDTt1y7";
-
- Long balance = ravencoin.getWalletBalance(xprv58);
-
- assertNotNull(balance);
-
- System.out.println(ravencoin.format(balance));
-
- // Check spent key caching doesn't affect outcome
-
- Long repeatBalance = ravencoin.getWalletBalance(xprv58);
-
- assertNotNull(repeatBalance);
-
- System.out.println(ravencoin.format(repeatBalance));
-
- assertEquals(balance, repeatBalance);
- }
-
- @Test
- public void testGetUnusedReceiveAddress() throws ForeignBlockchainException {
- String xprv58 = "xpub661MyMwAqRbcEt3Ge1wNmkagyb1J7yTQu4Kquvy77Ycg2iPoh7Urg8s9Jdwp7YmrqGkDKJpUVjsZXSSsQgmAVUC17ZVQQeoWMzm7vDTt1y7";
-
- String address = ravencoin.getUnusedReceiveAddress(xprv58);
-
- assertNotNull(address);
-
- System.out.println(address);
- }
-
+ public void testBuildSpend() {}
}
diff --git a/src/test/java/org/qortal/test/crosschain/bitcoinv1/BitcoinACCTv1Tests.java b/src/test/java/org/qortal/test/crosschain/bitcoinv1/BitcoinACCTv1Tests.java
index 4487e874..cc33eb43 100644
--- a/src/test/java/org/qortal/test/crosschain/bitcoinv1/BitcoinACCTv1Tests.java
+++ b/src/test/java/org/qortal/test/crosschain/bitcoinv1/BitcoinACCTv1Tests.java
@@ -2,507 +2,89 @@ package org.qortal.test.crosschain.bitcoinv1;
import static org.junit.Assert.*;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-import java.time.format.FormatStyle;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
import java.util.function.Function;
-import org.junit.Before;
import org.junit.Test;
import org.qortal.account.Account;
import org.qortal.account.PrivateKeyAccount;
import org.qortal.asset.Asset;
-import org.qortal.block.Block;
+import org.qortal.crosschain.ACCT;
import org.qortal.crosschain.BitcoinACCTv1;
import org.qortal.crosschain.AcctMode;
import org.qortal.crypto.Crypto;
import org.qortal.data.at.ATData;
import org.qortal.data.at.ATStateData;
import org.qortal.data.crosschain.CrossChainTradeData;
-import org.qortal.data.transaction.BaseTransactionData;
-import org.qortal.data.transaction.DeployAtTransactionData;
-import org.qortal.data.transaction.MessageTransactionData;
-import org.qortal.data.transaction.TransactionData;
-import org.qortal.group.Group;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.repository.RepositoryManager;
import org.qortal.test.common.BlockUtils;
import org.qortal.test.common.Common;
-import org.qortal.test.common.TransactionUtils;
+import org.qortal.test.crosschain.ACCTTests;
import org.qortal.transaction.DeployAtTransaction;
import org.qortal.transaction.MessageTransaction;
-import org.qortal.utils.Amounts;
import com.google.common.hash.HashCode;
-import com.google.common.primitives.Bytes;
-public class BitcoinACCTv1Tests extends Common {
+public class BitcoinACCTv1Tests extends ACCTTests {
- public static final byte[] secretA = "This string is exactly 32 bytes!".getBytes();
- public static final byte[] hashOfSecretA = Crypto.hash160(secretA); // daf59884b4d1aec8c1b17102530909ee43c0151a
public static final byte[] secretB = "This string is roughly 32 bytes?".getBytes();
public static final byte[] hashOfSecretB = Crypto.hash160(secretB); // 31f0dd71decf59bbc8ef0661f4030479255cfa58
public static final byte[] bitcoinPublicKeyHash = HashCode.fromString("bb00bb11bb22bb33bb44bb55bb66bb77bb88bb99").asBytes();
- public static final int tradeTimeout = 20; // blocks
- public static final long redeemAmount = 80_40200000L;
- public static final long fundingAmount = 123_45600000L;
- public static final long bitcoinAmount = 864200L; // 0.00864200 BTC
- private static final Random RANDOM = new Random();
+ private static final String SYMBOL = "BTC";
- @Before
- public void beforeTest() throws DataException {
- Common.useDefaultSettings();
+ private static final String NAME = "Bitcoin";
+
+ @Override
+ protected byte[] getPublicKey() {
+ return bitcoinPublicKeyHash;
}
- @Test
- public void testCompile() {
- PrivateKeyAccount tradeAccount = createTradeAccount(null);
-
- byte[] creationBytes = BitcoinACCTv1.buildQortalAT(tradeAccount.getAddress(), bitcoinPublicKeyHash, hashOfSecretB, redeemAmount, bitcoinAmount, tradeTimeout);
- assertNotNull(creationBytes);
-
- System.out.println("AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
+ @Override
+ protected byte[] buildQortalAT(String address, byte[] publicKey, long redeemAmount, long foreignAmount, int tradeTimeout) {
+ return BitcoinACCTv1.buildQortalAT(address,publicKey, hashOfSecretB, redeemAmount, foreignAmount,tradeTimeout);
}
- @Test
- public void testDeploy() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
+ @Override
+ protected ACCT getInstance() {
+ return BitcoinACCTv1.getInstance();
+ }
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
+ @Override
+ protected int calcRefundTimeout(long partnersOfferMessageTransactionTimestamp, int lockTimeA) {
+ return BitcoinACCTv1.calcLockTimeB(partnersOfferMessageTransactionTimestamp, lockTimeA);
+ }
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
+ @Override
+ protected byte[] buildTradeMessage(String address, byte[] publicKey, byte[] hashOfSecretA, int lockTimeA, int refundTimeout) {
+ return BitcoinACCTv1.buildTradeMessage(address, publicKey, hashOfSecretA, lockTimeA, refundTimeout);
+ }
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
+ @Override
+ protected byte[] buildRedeemMessage(byte[] secretA, String address) {
+ return BitcoinACCTv1.buildRedeemMessage(secretA,secretB,address);
+ }
- long expectedBalance = deployersInitialBalance - fundingAmount - deployAtTransaction.getTransactionData().getFee();
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
+ @Override
+ protected byte[] getCodeBytesHash() {
+ return BitcoinACCTv1.CODE_BYTES_HASH;
+ }
- assertEquals("Deployer's post-deployment balance incorrect", expectedBalance, actualBalance);
+ @Override
+ protected String getSymbol() {
+ return SYMBOL;
+ }
- expectedBalance = fundingAmount;
- actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
-
- assertEquals("AT's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = partnersInitialBalance;
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- // Test orphaning
- BlockUtils.orphanLastBlock(repository);
-
- expectedBalance = deployersInitialBalance;
- actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = 0;
- actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
-
- assertEquals("AT's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = partnersInitialBalance;
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected String getName() {
+ return NAME;
}
@SuppressWarnings("unused")
+ @Override
@Test
- public void testOfferCancel() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- // Send creator's address to AT, instead of typical partner's address
- byte[] messageData = BitcoinACCTv1.getInstance().buildCancelMessage(deployer.getAddress());
- MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
- long messageFee = messageTransaction.getTransactionData().getFee();
-
- // AT should process 'cancel' message in next block
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in CANCELLED mode
- CrossChainTradeData tradeData = BitcoinACCTv1.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.CANCELLED, tradeData.mode);
-
- // Check balances
- long expectedMinimumBalance = deployersPostDeploymentBalance;
- long expectedMaximumBalance = deployersInitialBalance - deployAtFee - messageFee;
-
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
- assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
-
- // Test orphaning
- BlockUtils.orphanLastBlock(repository);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance - messageFee;
- actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testOfferCancelInvalidLength() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- // Instead of sending creator's address to AT, send too-short/invalid message
- byte[] messageData = new byte[7];
- RANDOM.nextBytes(messageData);
- MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
- long messageFee = messageTransaction.getTransactionData().getFee();
-
- // AT should process 'cancel' message in next block
- // As message is too short, it will be padded to 32bytes but cancel code doesn't care about message content, so should be ok
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in CANCELLED mode
- CrossChainTradeData tradeData = BitcoinACCTv1.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.CANCELLED, tradeData.mode);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testTradingInfoProcessing() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int lockTimeB = BitcoinACCTv1.calcLockTimeB(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = BitcoinACCTv1.buildTradeMessage(partner.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, lockTimeB);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- Block postDeploymentBlock = BlockUtils.mintBlock(repository);
- int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- describeAt(repository, atAddress);
-
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = BitcoinACCTv1.getInstance().populateTradeData(repository, atData);
-
- // AT should be in TRADE mode
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- // Check hashOfSecretA was extracted correctly
- assertTrue(Arrays.equals(hashOfSecretA, tradeData.hashOfSecretA));
-
- // Check trade partner Qortal address was extracted correctly
- assertEquals(partner.getAddress(), tradeData.qortalPartnerAddress);
-
- // Check trade partner's Bitcoin PKH was extracted correctly
- assertTrue(Arrays.equals(bitcoinPublicKeyHash, tradeData.partnerForeignPKH));
-
- // Test orphaning
- BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance;
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
- }
-
- // TEST SENDING TRADING INFO BUT NOT FROM AT CREATOR (SHOULD BE IGNORED)
- @SuppressWarnings("unused")
- @Test
- public void testIncorrectTradeSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int lockTimeB = BitcoinACCTv1.calcLockTimeB(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT BUT NOT FROM AT CREATOR
- byte[] messageData = BitcoinACCTv1.buildTradeMessage(partner.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, lockTimeB);
- MessageTransaction messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
-
- BlockUtils.mintBlock(repository);
-
- long expectedBalance = partnersInitialBalance;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-initial-payout balance incorrect", expectedBalance, actualBalance);
-
- describeAt(repository, atAddress);
-
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = BitcoinACCTv1.getInstance().populateTradeData(repository, atData);
-
- // AT should still be in OFFER mode
- assertEquals(AcctMode.OFFERING, tradeData.mode);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testAutomaticTradeRefund() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int lockTimeB = BitcoinACCTv1.calcLockTimeB(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = BitcoinACCTv1.buildTradeMessage(partner.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, lockTimeB);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- Block postDeploymentBlock = BlockUtils.mintBlock(repository);
- int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
-
- // Check refund
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in REFUNDED mode
- CrossChainTradeData tradeData = BitcoinACCTv1.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.REFUNDED, tradeData.mode);
-
- // Test orphaning
- BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance;
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretsCorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int lockTimeB = BitcoinACCTv1.calcLockTimeB(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = BitcoinACCTv1.buildTradeMessage(partner.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, lockTimeB);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secrets to AT, from correct account
- messageData = BitcoinACCTv1.buildRedeemMessage(secretA, secretB, partner.getAddress());
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in REDEEMED mode
- CrossChainTradeData tradeData = BitcoinACCTv1.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.REDEEMED, tradeData.mode);
-
- // Check balances
- long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee() + redeemAmount;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-redeem balance incorrect", expectedBalance, actualBalance);
-
- // Orphan redeem
- BlockUtils.orphanLastBlock(repository);
-
- // Check balances
- expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-orphan/pre-redeem balance incorrect", expectedBalance, actualBalance);
-
- // Check AT state
- ATStateData postOrphanAtStateData = repository.getATRepository().getLatestATState(atAddress);
-
- assertTrue("AT states mismatch", Arrays.equals(preRedeemAtStateData.getStateData(), postOrphanAtStateData.getStateData()));
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretsIncorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
-
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int lockTimeB = BitcoinACCTv1.calcLockTimeB(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = BitcoinACCTv1.buildTradeMessage(partner.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, lockTimeB);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secrets to AT, but from wrong account
- messageData = BitcoinACCTv1.buildRedeemMessage(secretA, secretB, partner.getAddress());
- messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should still be in TRADE mode
- CrossChainTradeData tradeData = BitcoinACCTv1.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- // Check balances
- long expectedBalance = partnersInitialBalance;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
-
- // Check eventual refund
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testIncorrectSecretsCorrectSender() throws DataException {
+ public void testIncorrectSecretCorrectSender() throws DataException {
try (final Repository repository = RepositoryManager.getRepository()) {
PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
PrivateKeyAccount tradeAccount = createTradeAccount(repository);
@@ -582,197 +164,8 @@ public class BitcoinACCTv1Tests extends Common {
}
}
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretsCorrectSenderInvalidMessageLength() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int lockTimeB = BitcoinACCTv1.calcLockTimeB(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = BitcoinACCTv1.buildTradeMessage(partner.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, lockTimeB);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secrets to AT, from correct account, but missing receive address, hence incorrect length
- messageData = Bytes.concat(secretA, secretB);
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should be in TRADING mode
- CrossChainTradeData tradeData = BitcoinACCTv1.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testDescribeDeployed() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
-
- List executableAts = repository.getATRepository().getAllExecutableATs();
-
- for (ATData atData : executableAts) {
- String atAddress = atData.getATAddress();
- byte[] codeBytes = atData.getCodeBytes();
- byte[] codeHash = Crypto.digest(codeBytes);
-
- System.out.println(String.format("%s: code length: %d byte%s, code hash: %s",
- atAddress,
- codeBytes.length,
- (codeBytes.length != 1 ? "s": ""),
- HashCode.fromBytes(codeHash)));
-
- // Not one of ours?
- if (!Arrays.equals(codeHash, BitcoinACCTv1.CODE_BYTES_HASH))
- continue;
-
- describeAt(repository, atAddress);
- }
- }
- }
-
- private int calcTestLockTimeA(long messageTimestamp) {
- return (int) (messageTimestamp / 1000L + tradeTimeout * 60);
- }
-
- private DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer, String tradeAddress) throws DataException {
- byte[] creationBytes = BitcoinACCTv1.buildQortalAT(tradeAddress, bitcoinPublicKeyHash, hashOfSecretB, redeemAmount, bitcoinAmount, tradeTimeout);
-
- long txTimestamp = System.currentTimeMillis();
- byte[] lastReference = deployer.getLastReference();
-
- if (lastReference == null) {
- System.err.println(String.format("Qortal account %s has no last reference", deployer.getAddress()));
- System.exit(2);
- }
-
- Long fee = null;
- String name = "QORT-BTC cross-chain trade";
- String description = String.format("Qortal-Bitcoin cross-chain trade");
- String atType = "ACCT";
- String tags = "QORT-BTC ACCT";
-
- BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, deployer.getPublicKey(), fee, null);
- TransactionData deployAtTransactionData = new DeployAtTransactionData(baseTransactionData, name, description, atType, tags, creationBytes, fundingAmount, Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = new DeployAtTransaction(repository, deployAtTransactionData);
-
- fee = deployAtTransaction.calcRecommendedFee();
- deployAtTransactionData.setFee(fee);
-
- TransactionUtils.signAndMint(repository, deployAtTransactionData, deployer);
-
- return deployAtTransaction;
- }
-
- private MessageTransaction sendMessage(Repository repository, PrivateKeyAccount sender, byte[] data, String recipient) throws DataException {
- long txTimestamp = System.currentTimeMillis();
- byte[] lastReference = sender.getLastReference();
-
- if (lastReference == null) {
- System.err.println(String.format("Qortal account %s has no last reference", sender.getAddress()));
- System.exit(2);
- }
-
- Long fee = null;
- int version = 4;
- int nonce = 0;
- long amount = 0;
- Long assetId = null; // because amount is zero
-
- BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, sender.getPublicKey(), fee, null);
- TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, version, nonce, recipient, amount, assetId, data, false, false);
-
- MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData);
-
- fee = messageTransaction.calcRecommendedFee();
- messageTransactionData.setFee(fee);
-
- TransactionUtils.signAndMint(repository, messageTransactionData, sender);
-
- return messageTransaction;
- }
-
- private void checkTradeRefund(Repository repository, Account deployer, long deployersInitialBalance, long deployAtFee) throws DataException {
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
- int refundTimeout = tradeTimeout * 3 / 4 + 1; // close enough
-
- // AT should automatically refund deployer after 'refundTimeout' blocks
- for (int blockCount = 0; blockCount <= refundTimeout; ++blockCount)
- BlockUtils.mintBlock(repository);
-
- // We don't bother to exactly calculate QORT spent running AT for several blocks, but we do know the expected range
- long expectedMinimumBalance = deployersPostDeploymentBalance;
- long expectedMaximumBalance = deployersInitialBalance - deployAtFee;
-
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
- assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
- }
-
- private void describeAt(Repository repository, String atAddress) throws DataException {
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = BitcoinACCTv1.getInstance().populateTradeData(repository, atData);
-
- Function epochMilliFormatter = (timestamp) -> LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC).format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM));
- int currentBlockHeight = repository.getBlockRepository().getBlockchainHeight();
-
- System.out.print(String.format("%s:\n"
- + "\tmode: %s\n"
- + "\tcreator: %s,\n"
- + "\tcreation timestamp: %s,\n"
- + "\tcurrent balance: %s QORT,\n"
- + "\tis finished: %b,\n"
- + "\tHASH160 of secret-B: %s,\n"
- + "\tredeem payout: %s QORT,\n"
- + "\texpected bitcoin: %s BTC,\n"
- + "\tcurrent block height: %d,\n",
- tradeData.qortalAtAddress,
- tradeData.mode,
- tradeData.qortalCreator,
- epochMilliFormatter.apply(tradeData.creationTimestamp),
- Amounts.prettyAmount(tradeData.qortBalance),
- atData.getIsFinished(),
- HashCode.fromBytes(tradeData.hashOfSecretB).toString().substring(0, 40),
- Amounts.prettyAmount(tradeData.qortAmount),
- Amounts.prettyAmount(tradeData.expectedForeignAmount),
- currentBlockHeight));
-
+ @Override
+ protected void describeRefundAt(CrossChainTradeData tradeData, Function epochMilliFormatter) {
if (tradeData.mode != AcctMode.OFFERING && tradeData.mode != AcctMode.CANCELLED) {
System.out.println(String.format("\trefund height: block %d,\n"
+ "\tHASH160 of secret-A: %s,\n"
@@ -786,10 +179,4 @@ public class BitcoinACCTv1Tests extends Common {
tradeData.qortalPartnerAddress));
}
}
-
- private PrivateKeyAccount createTradeAccount(Repository repository) {
- // We actually use a known test account with funds to avoid PoW compute
- return Common.getTestAccount(repository, "alice");
- }
-
}
diff --git a/src/test/java/org/qortal/test/crosschain/bitcoinv3/BitcoinACCTv3Tests.java b/src/test/java/org/qortal/test/crosschain/bitcoinv3/BitcoinACCTv3Tests.java
index 01345727..5e0048bf 100644
--- a/src/test/java/org/qortal/test/crosschain/bitcoinv3/BitcoinACCTv3Tests.java
+++ b/src/test/java/org/qortal/test/crosschain/bitcoinv3/BitcoinACCTv3Tests.java
@@ -1,769 +1,58 @@
package org.qortal.test.crosschain.bitcoinv3;
import com.google.common.hash.HashCode;
-import com.google.common.primitives.Bytes;
-import org.junit.Before;
-import org.junit.Test;
-import org.qortal.account.Account;
-import org.qortal.account.PrivateKeyAccount;
-import org.qortal.asset.Asset;
-import org.qortal.block.Block;
-import org.qortal.crosschain.AcctMode;
+import org.qortal.crosschain.ACCT;
import org.qortal.crosschain.BitcoinACCTv3;
-import org.qortal.crypto.Crypto;
-import org.qortal.data.at.ATData;
-import org.qortal.data.at.ATStateData;
-import org.qortal.data.crosschain.CrossChainTradeData;
-import org.qortal.data.transaction.BaseTransactionData;
-import org.qortal.data.transaction.DeployAtTransactionData;
-import org.qortal.data.transaction.MessageTransactionData;
-import org.qortal.data.transaction.TransactionData;
-import org.qortal.group.Group;
-import org.qortal.repository.DataException;
-import org.qortal.repository.Repository;
-import org.qortal.repository.RepositoryManager;
-import org.qortal.test.common.BlockUtils;
-import org.qortal.test.common.Common;
-import org.qortal.test.common.TransactionUtils;
-import org.qortal.transaction.DeployAtTransaction;
-import org.qortal.transaction.MessageTransaction;
-import org.qortal.utils.Amounts;
+import org.qortal.test.crosschain.ACCTTests;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-import java.time.format.FormatStyle;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-import java.util.function.Function;
+public class BitcoinACCTv3Tests extends ACCTTests {
-import static org.junit.Assert.*;
-
-public class BitcoinACCTv3Tests extends Common {
-
- public static final byte[] secretA = "This string is exactly 32 bytes!".getBytes();
- public static final byte[] hashOfSecretA = Crypto.hash160(secretA); // daf59884b4d1aec8c1b17102530909ee43c0151a
public static final byte[] bitcoinPublicKeyHash = HashCode.fromString("bb00bb11bb22bb33bb44bb55bb66bb77bb88bb99").asBytes();
- public static final int tradeTimeout = 20; // blocks
- public static final long redeemAmount = 80_40200000L;
- public static final long fundingAmount = 123_45600000L;
- public static final long bitcoinAmount = 864200L; // 0.00864200 BTC
+ private static final String SYMBOL = "BTC";
+ private static final String NAME = "Bitcoin";
- private static final Random RANDOM = new Random();
-
- @Before
- public void beforeTest() throws DataException {
- Common.useDefaultSettings();
+ @Override
+ protected byte[] getPublicKey() {
+ return bitcoinPublicKeyHash;
}
- @Test
- public void testCompile() {
- PrivateKeyAccount tradeAccount = createTradeAccount(null);
-
- byte[] creationBytes = BitcoinACCTv3.buildQortalAT(tradeAccount.getAddress(), bitcoinPublicKeyHash, redeemAmount, bitcoinAmount, tradeTimeout);
- assertNotNull(creationBytes);
-
- System.out.println("AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
+ @Override
+ protected byte[] buildQortalAT(String address, byte[] publicKey, long redeemAmount, long foreignAmount, int tradeTimeout) {
+ return BitcoinACCTv3.buildQortalAT(address, publicKey, redeemAmount, foreignAmount, tradeTimeout);
}
- @Test
- public void testDeploy() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
-
- long expectedBalance = deployersInitialBalance - fundingAmount - deployAtTransaction.getTransactionData().getFee();
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = fundingAmount;
- actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
-
- assertEquals("AT's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = partnersInitialBalance;
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- // Test orphaning
- BlockUtils.orphanLastBlock(repository);
-
- expectedBalance = deployersInitialBalance;
- actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = 0;
- actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
-
- assertEquals("AT's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = partnersInitialBalance;
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected ACCT getInstance() {
+ return BitcoinACCTv3.getInstance();
}
- @SuppressWarnings("unused")
- @Test
- public void testOfferCancel() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- // Send creator's address to AT, instead of typical partner's address
- byte[] messageData = BitcoinACCTv3.getInstance().buildCancelMessage(deployer.getAddress());
- MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
- long messageFee = messageTransaction.getTransactionData().getFee();
-
- // AT should process 'cancel' message in next block
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in CANCELLED mode
- CrossChainTradeData tradeData = BitcoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.CANCELLED, tradeData.mode);
-
- // Check balances
- long expectedMinimumBalance = deployersPostDeploymentBalance;
- long expectedMaximumBalance = deployersInitialBalance - deployAtFee - messageFee;
-
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
- assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
-
- // Test orphaning
- BlockUtils.orphanLastBlock(repository);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance - messageFee;
- actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected int calcRefundTimeout(long partnersOfferMessageTransactionTimestamp, int lockTimeA) {
+ return BitcoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
}
- @SuppressWarnings("unused")
- @Test
- public void testOfferCancelInvalidLength() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- // Instead of sending creator's address to AT, send too-short/invalid message
- byte[] messageData = new byte[7];
- RANDOM.nextBytes(messageData);
- MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
- long messageFee = messageTransaction.getTransactionData().getFee();
-
- // AT should process 'cancel' message in next block
- // As message is too short, it will be padded to 32bytes but cancel code doesn't care about message content, so should be ok
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in CANCELLED mode
- CrossChainTradeData tradeData = BitcoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.CANCELLED, tradeData.mode);
- }
+ @Override
+ protected byte[] buildTradeMessage(String address, byte[] publicKey, byte[] hashOfSecretA, int lockTimeA, int refundTimeout) {
+ return BitcoinACCTv3.buildTradeMessage(address, publicKey, hashOfSecretA, lockTimeA, refundTimeout);
}
- @SuppressWarnings("unused")
- @Test
- public void testTradingInfoProcessing() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = BitcoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = BitcoinACCTv3.buildTradeMessage(partner.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- Block postDeploymentBlock = BlockUtils.mintBlock(repository);
- int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- describeAt(repository, atAddress);
-
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = BitcoinACCTv3.getInstance().populateTradeData(repository, atData);
-
- // AT should be in TRADE mode
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- // Check hashOfSecretA was extracted correctly
- assertTrue(Arrays.equals(hashOfSecretA, tradeData.hashOfSecretA));
-
- // Check trade partner Qortal address was extracted correctly
- assertEquals(partner.getAddress(), tradeData.qortalPartnerAddress);
-
- // Check trade partner's Bitcoin PKH was extracted correctly
- assertTrue(Arrays.equals(bitcoinPublicKeyHash, tradeData.partnerForeignPKH));
-
- // Test orphaning
- BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance;
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected byte[] buildRedeemMessage(byte[] secretA, String address) {
+ return BitcoinACCTv3.buildRedeemMessage(secretA, address);
}
- // TEST SENDING TRADING INFO BUT NOT FROM AT CREATOR (SHOULD BE IGNORED)
- @SuppressWarnings("unused")
- @Test
- public void testIncorrectTradeSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = BitcoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT BUT NOT FROM AT CREATOR
- byte[] messageData = BitcoinACCTv3.buildTradeMessage(partner.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
-
- BlockUtils.mintBlock(repository);
-
- long expectedBalance = partnersInitialBalance;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-initial-payout balance incorrect", expectedBalance, actualBalance);
-
- describeAt(repository, atAddress);
-
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = BitcoinACCTv3.getInstance().populateTradeData(repository, atData);
-
- // AT should still be in OFFER mode
- assertEquals(AcctMode.OFFERING, tradeData.mode);
- }
+ @Override
+ protected byte[] getCodeBytesHash() {
+ return BitcoinACCTv3.CODE_BYTES_HASH;
}
- @SuppressWarnings("unused")
- @Test
- public void testAutomaticTradeRefund() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = BitcoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = BitcoinACCTv3.buildTradeMessage(partner.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- Block postDeploymentBlock = BlockUtils.mintBlock(repository);
- int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
-
- // Check refund
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in REFUNDED mode
- CrossChainTradeData tradeData = BitcoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.REFUNDED, tradeData.mode);
-
- // Test orphaning
- BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance;
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected String getSymbol() {
+ return SYMBOL;
}
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretCorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = BitcoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = BitcoinACCTv3.buildTradeMessage(partner.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, from correct account
- messageData = BitcoinACCTv3.buildRedeemMessage(secretA, partner.getAddress());
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in REDEEMED mode
- CrossChainTradeData tradeData = BitcoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.REDEEMED, tradeData.mode);
-
- // Check balances
- long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee() + redeemAmount;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-redeem balance incorrect", expectedBalance, actualBalance);
-
- // Orphan redeem
- BlockUtils.orphanLastBlock(repository);
-
- // Check balances
- expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-orphan/pre-redeem balance incorrect", expectedBalance, actualBalance);
-
- // Check AT state
- ATStateData postOrphanAtStateData = repository.getATRepository().getLatestATState(atAddress);
-
- assertTrue("AT states mismatch", Arrays.equals(preRedeemAtStateData.getStateData(), postOrphanAtStateData.getStateData()));
- }
+ @Override
+ protected String getName() {
+ return NAME;
}
-
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretIncorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
-
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = BitcoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = BitcoinACCTv3.buildTradeMessage(partner.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, but from wrong account
- messageData = BitcoinACCTv3.buildRedeemMessage(secretA, partner.getAddress());
- messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should still be in TRADE mode
- CrossChainTradeData tradeData = BitcoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- // Check balances
- long expectedBalance = partnersInitialBalance;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
-
- // Check eventual refund
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testIncorrectSecretCorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
-
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = BitcoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = BitcoinACCTv3.buildTradeMessage(partner.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send incorrect secret to AT, from correct account
- byte[] wrongSecret = new byte[32];
- RANDOM.nextBytes(wrongSecret);
- messageData = BitcoinACCTv3.buildRedeemMessage(wrongSecret, partner.getAddress());
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should still be in TRADE mode
- CrossChainTradeData tradeData = BitcoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
-
- // Check eventual refund
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretCorrectSenderInvalidMessageLength() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = BitcoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = BitcoinACCTv3.buildTradeMessage(partner.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, from correct account, but missing receive address, hence incorrect length
- messageData = Bytes.concat(secretA);
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should be in TRADING mode
- CrossChainTradeData tradeData = BitcoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testDescribeDeployed() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
-
- List executableAts = repository.getATRepository().getAllExecutableATs();
-
- for (ATData atData : executableAts) {
- String atAddress = atData.getATAddress();
- byte[] codeBytes = atData.getCodeBytes();
- byte[] codeHash = Crypto.digest(codeBytes);
-
- System.out.println(String.format("%s: code length: %d byte%s, code hash: %s",
- atAddress,
- codeBytes.length,
- (codeBytes.length != 1 ? "s": ""),
- HashCode.fromBytes(codeHash)));
-
- // Not one of ours?
- if (!Arrays.equals(codeHash, BitcoinACCTv3.CODE_BYTES_HASH))
- continue;
-
- describeAt(repository, atAddress);
- }
- }
- }
-
- private int calcTestLockTimeA(long messageTimestamp) {
- return (int) (messageTimestamp / 1000L + tradeTimeout * 60);
- }
-
- private DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer, String tradeAddress) throws DataException {
- byte[] creationBytes = BitcoinACCTv3.buildQortalAT(tradeAddress, bitcoinPublicKeyHash, redeemAmount, bitcoinAmount, tradeTimeout);
-
- long txTimestamp = System.currentTimeMillis();
- byte[] lastReference = deployer.getLastReference();
-
- if (lastReference == null) {
- System.err.println(String.format("Qortal account %s has no last reference", deployer.getAddress()));
- System.exit(2);
- }
-
- Long fee = null;
- String name = "QORT-BTC cross-chain trade";
- String description = String.format("Qortal-Bitcoin cross-chain trade");
- String atType = "ACCT";
- String tags = "QORT-BTC ACCT";
-
- BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, deployer.getPublicKey(), fee, null);
- TransactionData deployAtTransactionData = new DeployAtTransactionData(baseTransactionData, name, description, atType, tags, creationBytes, fundingAmount, Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = new DeployAtTransaction(repository, deployAtTransactionData);
-
- fee = deployAtTransaction.calcRecommendedFee();
- deployAtTransactionData.setFee(fee);
-
- TransactionUtils.signAndMint(repository, deployAtTransactionData, deployer);
-
- return deployAtTransaction;
- }
-
- private MessageTransaction sendMessage(Repository repository, PrivateKeyAccount sender, byte[] data, String recipient) throws DataException {
- long txTimestamp = System.currentTimeMillis();
- byte[] lastReference = sender.getLastReference();
-
- if (lastReference == null) {
- System.err.println(String.format("Qortal account %s has no last reference", sender.getAddress()));
- System.exit(2);
- }
-
- Long fee = null;
- int version = 4;
- int nonce = 0;
- long amount = 0;
- Long assetId = null; // because amount is zero
-
- BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, sender.getPublicKey(), fee, null);
- TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, version, nonce, recipient, amount, assetId, data, false, false);
-
- MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData);
-
- fee = messageTransaction.calcRecommendedFee();
- messageTransactionData.setFee(fee);
-
- TransactionUtils.signAndMint(repository, messageTransactionData, sender);
-
- return messageTransaction;
- }
-
- private void checkTradeRefund(Repository repository, Account deployer, long deployersInitialBalance, long deployAtFee) throws DataException {
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
- int refundTimeout = tradeTimeout / 2 + 1; // close enough
-
- // AT should automatically refund deployer after 'refundTimeout' blocks
- for (int blockCount = 0; blockCount <= refundTimeout; ++blockCount)
- BlockUtils.mintBlock(repository);
-
- // We don't bother to exactly calculate QORT spent running AT for several blocks, but we do know the expected range
- long expectedMinimumBalance = deployersPostDeploymentBalance;
- long expectedMaximumBalance = deployersInitialBalance - deployAtFee;
-
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
- assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
- }
-
- private void describeAt(Repository repository, String atAddress) throws DataException {
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = BitcoinACCTv3.getInstance().populateTradeData(repository, atData);
-
- Function epochMilliFormatter = (timestamp) -> LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC).format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM));
- int currentBlockHeight = repository.getBlockRepository().getBlockchainHeight();
-
- System.out.print(String.format("%s:\n"
- + "\tmode: %s\n"
- + "\tcreator: %s,\n"
- + "\tcreation timestamp: %s,\n"
- + "\tcurrent balance: %s QORT,\n"
- + "\tis finished: %b,\n"
- + "\tredeem payout: %s QORT,\n"
- + "\texpected Bitcoin: %s BTC,\n"
- + "\tcurrent block height: %d,\n",
- tradeData.qortalAtAddress,
- tradeData.mode,
- tradeData.qortalCreator,
- epochMilliFormatter.apply(tradeData.creationTimestamp),
- Amounts.prettyAmount(tradeData.qortBalance),
- atData.getIsFinished(),
- Amounts.prettyAmount(tradeData.qortAmount),
- Amounts.prettyAmount(tradeData.expectedForeignAmount),
- currentBlockHeight));
-
- if (tradeData.mode != AcctMode.OFFERING && tradeData.mode != AcctMode.CANCELLED) {
- System.out.println(String.format("\trefund timeout: %d minutes,\n"
- + "\trefund height: block %d,\n"
- + "\tHASH160 of secret-A: %s,\n"
- + "\tBitcoin P2SH-A nLockTime: %d (%s),\n"
- + "\ttrade partner: %s\n"
- + "\tpartner's receiving address: %s",
- tradeData.refundTimeout,
- tradeData.tradeRefundHeight,
- HashCode.fromBytes(tradeData.hashOfSecretA).toString().substring(0, 40),
- tradeData.lockTimeA, epochMilliFormatter.apply(tradeData.lockTimeA * 1000L),
- tradeData.qortalPartnerAddress,
- tradeData.qortalPartnerReceivingAddress));
- }
- }
-
- private PrivateKeyAccount createTradeAccount(Repository repository) {
- // We actually use a known test account with funds to avoid PoW compute
- return Common.getTestAccount(repository, "alice");
- }
-
}
diff --git a/src/test/java/org/qortal/test/crosschain/digibytev3/DigibyteACCTv3Tests.java b/src/test/java/org/qortal/test/crosschain/digibytev3/DigibyteACCTv3Tests.java
index d13aba4c..01ead678 100644
--- a/src/test/java/org/qortal/test/crosschain/digibytev3/DigibyteACCTv3Tests.java
+++ b/src/test/java/org/qortal/test/crosschain/digibytev3/DigibyteACCTv3Tests.java
@@ -1,769 +1,58 @@
package org.qortal.test.crosschain.digibytev3;
import com.google.common.hash.HashCode;
-import com.google.common.primitives.Bytes;
-import org.junit.Before;
-import org.junit.Test;
-import org.qortal.account.Account;
-import org.qortal.account.PrivateKeyAccount;
-import org.qortal.asset.Asset;
-import org.qortal.block.Block;
-import org.qortal.crosschain.AcctMode;
+import org.qortal.crosschain.ACCT;
import org.qortal.crosschain.DigibyteACCTv3;
-import org.qortal.crypto.Crypto;
-import org.qortal.data.at.ATData;
-import org.qortal.data.at.ATStateData;
-import org.qortal.data.crosschain.CrossChainTradeData;
-import org.qortal.data.transaction.BaseTransactionData;
-import org.qortal.data.transaction.DeployAtTransactionData;
-import org.qortal.data.transaction.MessageTransactionData;
-import org.qortal.data.transaction.TransactionData;
-import org.qortal.group.Group;
-import org.qortal.repository.DataException;
-import org.qortal.repository.Repository;
-import org.qortal.repository.RepositoryManager;
-import org.qortal.test.common.BlockUtils;
-import org.qortal.test.common.Common;
-import org.qortal.test.common.TransactionUtils;
-import org.qortal.transaction.DeployAtTransaction;
-import org.qortal.transaction.MessageTransaction;
-import org.qortal.utils.Amounts;
+import org.qortal.test.crosschain.ACCTTests;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-import java.time.format.FormatStyle;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-import java.util.function.Function;
+public class DigibyteACCTv3Tests extends ACCTTests {
-import static org.junit.Assert.*;
-
-public class DigibyteACCTv3Tests extends Common {
-
- public static final byte[] secretA = "This string is exactly 32 bytes!".getBytes();
- public static final byte[] hashOfSecretA = Crypto.hash160(secretA); // daf59884b4d1aec8c1b17102530909ee43c0151a
public static final byte[] digibytePublicKeyHash = HashCode.fromString("bb00bb11bb22bb33bb44bb55bb66bb77bb88bb99").asBytes();
- public static final int tradeTimeout = 20; // blocks
- public static final long redeemAmount = 80_40200000L;
- public static final long fundingAmount = 123_45600000L;
- public static final long digibyteAmount = 864200L; // 0.00864200 DGB
+ private static final String SYMBOL = "DGB";
+ private static final String NAME = "DigiByte";
- private static final Random RANDOM = new Random();
-
- @Before
- public void beforeTest() throws DataException {
- Common.useDefaultSettings();
+ @Override
+ protected byte[] getPublicKey() {
+ return digibytePublicKeyHash;
}
- @Test
- public void testCompile() {
- PrivateKeyAccount tradeAccount = createTradeAccount(null);
-
- byte[] creationBytes = DigibyteACCTv3.buildQortalAT(tradeAccount.getAddress(), digibytePublicKeyHash, redeemAmount, digibyteAmount, tradeTimeout);
- assertNotNull(creationBytes);
-
- System.out.println("AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
+ @Override
+ protected byte[] buildQortalAT(String address, byte[] publicKey, long redeemAmount, long foreignAmount, int tradeTimeout) {
+ return DigibyteACCTv3.buildQortalAT(address, publicKey, redeemAmount, foreignAmount, tradeTimeout);
}
- @Test
- public void testDeploy() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
-
- long expectedBalance = deployersInitialBalance - fundingAmount - deployAtTransaction.getTransactionData().getFee();
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = fundingAmount;
- actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
-
- assertEquals("AT's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = partnersInitialBalance;
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- // Test orphaning
- BlockUtils.orphanLastBlock(repository);
-
- expectedBalance = deployersInitialBalance;
- actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = 0;
- actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
-
- assertEquals("AT's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = partnersInitialBalance;
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected ACCT getInstance() {
+ return DigibyteACCTv3.getInstance();
}
- @SuppressWarnings("unused")
- @Test
- public void testOfferCancel() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- // Send creator's address to AT, instead of typical partner's address
- byte[] messageData = DigibyteACCTv3.getInstance().buildCancelMessage(deployer.getAddress());
- MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
- long messageFee = messageTransaction.getTransactionData().getFee();
-
- // AT should process 'cancel' message in next block
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in CANCELLED mode
- CrossChainTradeData tradeData = DigibyteACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.CANCELLED, tradeData.mode);
-
- // Check balances
- long expectedMinimumBalance = deployersPostDeploymentBalance;
- long expectedMaximumBalance = deployersInitialBalance - deployAtFee - messageFee;
-
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
- assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
-
- // Test orphaning
- BlockUtils.orphanLastBlock(repository);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance - messageFee;
- actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected int calcRefundTimeout(long partnersOfferMessageTransactionTimestamp, int lockTimeA) {
+ return DigibyteACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
}
- @SuppressWarnings("unused")
- @Test
- public void testOfferCancelInvalidLength() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- // Instead of sending creator's address to AT, send too-short/invalid message
- byte[] messageData = new byte[7];
- RANDOM.nextBytes(messageData);
- MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
- long messageFee = messageTransaction.getTransactionData().getFee();
-
- // AT should process 'cancel' message in next block
- // As message is too short, it will be padded to 32bytes but cancel code doesn't care about message content, so should be ok
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in CANCELLED mode
- CrossChainTradeData tradeData = DigibyteACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.CANCELLED, tradeData.mode);
- }
+ @Override
+ protected byte[] buildTradeMessage(String address, byte[] publicKey, byte[] hashOfSecretA, int lockTimeA, int refundTimeout) {
+ return DigibyteACCTv3.buildTradeMessage(address, publicKey, hashOfSecretA, lockTimeA, refundTimeout);
}
- @SuppressWarnings("unused")
- @Test
- public void testTradingInfoProcessing() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = DigibyteACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = DigibyteACCTv3.buildTradeMessage(partner.getAddress(), digibytePublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- Block postDeploymentBlock = BlockUtils.mintBlock(repository);
- int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- describeAt(repository, atAddress);
-
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = DigibyteACCTv3.getInstance().populateTradeData(repository, atData);
-
- // AT should be in TRADE mode
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- // Check hashOfSecretA was extracted correctly
- assertTrue(Arrays.equals(hashOfSecretA, tradeData.hashOfSecretA));
-
- // Check trade partner Qortal address was extracted correctly
- assertEquals(partner.getAddress(), tradeData.qortalPartnerAddress);
-
- // Check trade partner's digibyte PKH was extracted correctly
- assertTrue(Arrays.equals(digibytePublicKeyHash, tradeData.partnerForeignPKH));
-
- // Test orphaning
- BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance;
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected byte[] buildRedeemMessage(byte[] secretA, String address) {
+ return DigibyteACCTv3.buildRedeemMessage(secretA, address);
}
- // TEST SENDING TRADING INFO BUT NOT FROM AT CREATOR (SHOULD BE IGNORED)
- @SuppressWarnings("unused")
- @Test
- public void testIncorrectTradeSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = DigibyteACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT BUT NOT FROM AT CREATOR
- byte[] messageData = DigibyteACCTv3.buildTradeMessage(partner.getAddress(), digibytePublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
-
- BlockUtils.mintBlock(repository);
-
- long expectedBalance = partnersInitialBalance;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-initial-payout balance incorrect", expectedBalance, actualBalance);
-
- describeAt(repository, atAddress);
-
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = DigibyteACCTv3.getInstance().populateTradeData(repository, atData);
-
- // AT should still be in OFFER mode
- assertEquals(AcctMode.OFFERING, tradeData.mode);
- }
+ @Override
+ protected byte[] getCodeBytesHash() {
+ return DigibyteACCTv3.CODE_BYTES_HASH;
}
- @SuppressWarnings("unused")
- @Test
- public void testAutomaticTradeRefund() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = DigibyteACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = DigibyteACCTv3.buildTradeMessage(partner.getAddress(), digibytePublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- Block postDeploymentBlock = BlockUtils.mintBlock(repository);
- int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
-
- // Check refund
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in REFUNDED mode
- CrossChainTradeData tradeData = DigibyteACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.REFUNDED, tradeData.mode);
-
- // Test orphaning
- BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance;
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected String getSymbol() {
+ return SYMBOL;
}
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretCorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = DigibyteACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = DigibyteACCTv3.buildTradeMessage(partner.getAddress(), digibytePublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, from correct account
- messageData = DigibyteACCTv3.buildRedeemMessage(secretA, partner.getAddress());
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in REDEEMED mode
- CrossChainTradeData tradeData = DigibyteACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.REDEEMED, tradeData.mode);
-
- // Check balances
- long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee() + redeemAmount;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-redeem balance incorrect", expectedBalance, actualBalance);
-
- // Orphan redeem
- BlockUtils.orphanLastBlock(repository);
-
- // Check balances
- expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-orphan/pre-redeem balance incorrect", expectedBalance, actualBalance);
-
- // Check AT state
- ATStateData postOrphanAtStateData = repository.getATRepository().getLatestATState(atAddress);
-
- assertTrue("AT states mismatch", Arrays.equals(preRedeemAtStateData.getStateData(), postOrphanAtStateData.getStateData()));
- }
+ @Override
+ protected String getName() {
+ return NAME;
}
-
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretIncorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
-
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = DigibyteACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = DigibyteACCTv3.buildTradeMessage(partner.getAddress(), digibytePublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, but from wrong account
- messageData = DigibyteACCTv3.buildRedeemMessage(secretA, partner.getAddress());
- messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should still be in TRADE mode
- CrossChainTradeData tradeData = DigibyteACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- // Check balances
- long expectedBalance = partnersInitialBalance;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
-
- // Check eventual refund
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testIncorrectSecretCorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
-
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = DigibyteACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = DigibyteACCTv3.buildTradeMessage(partner.getAddress(), digibytePublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send incorrect secret to AT, from correct account
- byte[] wrongSecret = new byte[32];
- RANDOM.nextBytes(wrongSecret);
- messageData = DigibyteACCTv3.buildRedeemMessage(wrongSecret, partner.getAddress());
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should still be in TRADE mode
- CrossChainTradeData tradeData = DigibyteACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
-
- // Check eventual refund
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretCorrectSenderInvalidMessageLength() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = DigibyteACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = DigibyteACCTv3.buildTradeMessage(partner.getAddress(), digibytePublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, from correct account, but missing receive address, hence incorrect length
- messageData = Bytes.concat(secretA);
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should be in TRADING mode
- CrossChainTradeData tradeData = DigibyteACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testDescribeDeployed() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
-
- List executableAts = repository.getATRepository().getAllExecutableATs();
-
- for (ATData atData : executableAts) {
- String atAddress = atData.getATAddress();
- byte[] codeBytes = atData.getCodeBytes();
- byte[] codeHash = Crypto.digest(codeBytes);
-
- System.out.println(String.format("%s: code length: %d byte%s, code hash: %s",
- atAddress,
- codeBytes.length,
- (codeBytes.length != 1 ? "s": ""),
- HashCode.fromBytes(codeHash)));
-
- // Not one of ours?
- if (!Arrays.equals(codeHash, DigibyteACCTv3.CODE_BYTES_HASH))
- continue;
-
- describeAt(repository, atAddress);
- }
- }
- }
-
- private int calcTestLockTimeA(long messageTimestamp) {
- return (int) (messageTimestamp / 1000L + tradeTimeout * 60);
- }
-
- private DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer, String tradeAddress) throws DataException {
- byte[] creationBytes = DigibyteACCTv3.buildQortalAT(tradeAddress, digibytePublicKeyHash, redeemAmount, digibyteAmount, tradeTimeout);
-
- long txTimestamp = System.currentTimeMillis();
- byte[] lastReference = deployer.getLastReference();
-
- if (lastReference == null) {
- System.err.println(String.format("Qortal account %s has no last reference", deployer.getAddress()));
- System.exit(2);
- }
-
- Long fee = null;
- String name = "QORT-DGB cross-chain trade";
- String description = String.format("Qortal-Digibyte cross-chain trade");
- String atType = "ACCT";
- String tags = "QORT-DGB ACCT";
-
- BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, deployer.getPublicKey(), fee, null);
- TransactionData deployAtTransactionData = new DeployAtTransactionData(baseTransactionData, name, description, atType, tags, creationBytes, fundingAmount, Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = new DeployAtTransaction(repository, deployAtTransactionData);
-
- fee = deployAtTransaction.calcRecommendedFee();
- deployAtTransactionData.setFee(fee);
-
- TransactionUtils.signAndMint(repository, deployAtTransactionData, deployer);
-
- return deployAtTransaction;
- }
-
- private MessageTransaction sendMessage(Repository repository, PrivateKeyAccount sender, byte[] data, String recipient) throws DataException {
- long txTimestamp = System.currentTimeMillis();
- byte[] lastReference = sender.getLastReference();
-
- if (lastReference == null) {
- System.err.println(String.format("Qortal account %s has no last reference", sender.getAddress()));
- System.exit(2);
- }
-
- Long fee = null;
- int version = 4;
- int nonce = 0;
- long amount = 0;
- Long assetId = null; // because amount is zero
-
- BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, sender.getPublicKey(), fee, null);
- TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, version, nonce, recipient, amount, assetId, data, false, false);
-
- MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData);
-
- fee = messageTransaction.calcRecommendedFee();
- messageTransactionData.setFee(fee);
-
- TransactionUtils.signAndMint(repository, messageTransactionData, sender);
-
- return messageTransaction;
- }
-
- private void checkTradeRefund(Repository repository, Account deployer, long deployersInitialBalance, long deployAtFee) throws DataException {
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
- int refundTimeout = tradeTimeout / 2 + 1; // close enough
-
- // AT should automatically refund deployer after 'refundTimeout' blocks
- for (int blockCount = 0; blockCount <= refundTimeout; ++blockCount)
- BlockUtils.mintBlock(repository);
-
- // We don't bother to exactly calculate QORT spent running AT for several blocks, but we do know the expected range
- long expectedMinimumBalance = deployersPostDeploymentBalance;
- long expectedMaximumBalance = deployersInitialBalance - deployAtFee;
-
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
- assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
- }
-
- private void describeAt(Repository repository, String atAddress) throws DataException {
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = DigibyteACCTv3.getInstance().populateTradeData(repository, atData);
-
- Function epochMilliFormatter = (timestamp) -> LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC).format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM));
- int currentBlockHeight = repository.getBlockRepository().getBlockchainHeight();
-
- System.out.print(String.format("%s:\n"
- + "\tmode: %s\n"
- + "\tcreator: %s,\n"
- + "\tcreation timestamp: %s,\n"
- + "\tcurrent balance: %s QORT,\n"
- + "\tis finished: %b,\n"
- + "\tredeem payout: %s QORT,\n"
- + "\texpected digibyte: %s DGB,\n"
- + "\tcurrent block height: %d,\n",
- tradeData.qortalAtAddress,
- tradeData.mode,
- tradeData.qortalCreator,
- epochMilliFormatter.apply(tradeData.creationTimestamp),
- Amounts.prettyAmount(tradeData.qortBalance),
- atData.getIsFinished(),
- Amounts.prettyAmount(tradeData.qortAmount),
- Amounts.prettyAmount(tradeData.expectedForeignAmount),
- currentBlockHeight));
-
- if (tradeData.mode != AcctMode.OFFERING && tradeData.mode != AcctMode.CANCELLED) {
- System.out.println(String.format("\trefund timeout: %d minutes,\n"
- + "\trefund height: block %d,\n"
- + "\tHASH160 of secret-A: %s,\n"
- + "\tDigibyte P2SH-A nLockTime: %d (%s),\n"
- + "\ttrade partner: %s\n"
- + "\tpartner's receiving address: %s",
- tradeData.refundTimeout,
- tradeData.tradeRefundHeight,
- HashCode.fromBytes(tradeData.hashOfSecretA).toString().substring(0, 40),
- tradeData.lockTimeA, epochMilliFormatter.apply(tradeData.lockTimeA * 1000L),
- tradeData.qortalPartnerAddress,
- tradeData.qortalPartnerReceivingAddress));
- }
- }
-
- private PrivateKeyAccount createTradeAccount(Repository repository) {
- // We actually use a known test account with funds to avoid PoW compute
- return Common.getTestAccount(repository, "alice");
- }
-
}
diff --git a/src/test/java/org/qortal/test/crosschain/dogecoinv3/DogecoinACCTv3Tests.java b/src/test/java/org/qortal/test/crosschain/dogecoinv3/DogecoinACCTv3Tests.java
index 7056e433..551173f7 100644
--- a/src/test/java/org/qortal/test/crosschain/dogecoinv3/DogecoinACCTv3Tests.java
+++ b/src/test/java/org/qortal/test/crosschain/dogecoinv3/DogecoinACCTv3Tests.java
@@ -1,769 +1,58 @@
package org.qortal.test.crosschain.dogecoinv3;
import com.google.common.hash.HashCode;
-import com.google.common.primitives.Bytes;
-import org.junit.Before;
-import org.junit.Test;
-import org.qortal.account.Account;
-import org.qortal.account.PrivateKeyAccount;
-import org.qortal.asset.Asset;
-import org.qortal.block.Block;
-import org.qortal.crosschain.AcctMode;
+import org.qortal.crosschain.ACCT;
import org.qortal.crosschain.DogecoinACCTv3;
-import org.qortal.crypto.Crypto;
-import org.qortal.data.at.ATData;
-import org.qortal.data.at.ATStateData;
-import org.qortal.data.crosschain.CrossChainTradeData;
-import org.qortal.data.transaction.BaseTransactionData;
-import org.qortal.data.transaction.DeployAtTransactionData;
-import org.qortal.data.transaction.MessageTransactionData;
-import org.qortal.data.transaction.TransactionData;
-import org.qortal.group.Group;
-import org.qortal.repository.DataException;
-import org.qortal.repository.Repository;
-import org.qortal.repository.RepositoryManager;
-import org.qortal.test.common.BlockUtils;
-import org.qortal.test.common.Common;
-import org.qortal.test.common.TransactionUtils;
-import org.qortal.transaction.DeployAtTransaction;
-import org.qortal.transaction.MessageTransaction;
-import org.qortal.utils.Amounts;
+import org.qortal.test.crosschain.ACCTTests;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-import java.time.format.FormatStyle;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-import java.util.function.Function;
+public class DogecoinACCTv3Tests extends ACCTTests {
-import static org.junit.Assert.*;
-
-public class DogecoinACCTv3Tests extends Common {
-
- public static final byte[] secretA = "This string is exactly 32 bytes!".getBytes();
- public static final byte[] hashOfSecretA = Crypto.hash160(secretA); // daf59884b4d1aec8c1b17102530909ee43c0151a
public static final byte[] dogecoinPublicKeyHash = HashCode.fromString("bb00bb11bb22bb33bb44bb55bb66bb77bb88bb99").asBytes();
- public static final int tradeTimeout = 20; // blocks
- public static final long redeemAmount = 80_40200000L;
- public static final long fundingAmount = 123_45600000L;
- public static final long dogecoinAmount = 864200L; // 0.00864200 DOGE
+ private static final String SYMBOL = "DOGE";
+ private static final String NAME = "Dogecoin";
- private static final Random RANDOM = new Random();
-
- @Before
- public void beforeTest() throws DataException {
- Common.useDefaultSettings();
+ @Override
+ protected byte[] getPublicKey() {
+ return dogecoinPublicKeyHash;
}
- @Test
- public void testCompile() {
- PrivateKeyAccount tradeAccount = createTradeAccount(null);
-
- byte[] creationBytes = DogecoinACCTv3.buildQortalAT(tradeAccount.getAddress(), dogecoinPublicKeyHash, redeemAmount, dogecoinAmount, tradeTimeout);
- assertNotNull(creationBytes);
-
- System.out.println("AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
+ @Override
+ protected byte[] buildQortalAT(String address, byte[] publicKey, long redeemAmount, long foreignAmount, int tradeTimeout) {
+ return DogecoinACCTv3.buildQortalAT(address, publicKey, redeemAmount, foreignAmount, tradeTimeout);
}
- @Test
- public void testDeploy() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
-
- long expectedBalance = deployersInitialBalance - fundingAmount - deployAtTransaction.getTransactionData().getFee();
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = fundingAmount;
- actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
-
- assertEquals("AT's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = partnersInitialBalance;
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- // Test orphaning
- BlockUtils.orphanLastBlock(repository);
-
- expectedBalance = deployersInitialBalance;
- actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = 0;
- actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
-
- assertEquals("AT's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = partnersInitialBalance;
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected ACCT getInstance() {
+ return DogecoinACCTv3.getInstance();
}
- @SuppressWarnings("unused")
- @Test
- public void testOfferCancel() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- // Send creator's address to AT, instead of typical partner's address
- byte[] messageData = DogecoinACCTv3.getInstance().buildCancelMessage(deployer.getAddress());
- MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
- long messageFee = messageTransaction.getTransactionData().getFee();
-
- // AT should process 'cancel' message in next block
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in CANCELLED mode
- CrossChainTradeData tradeData = DogecoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.CANCELLED, tradeData.mode);
-
- // Check balances
- long expectedMinimumBalance = deployersPostDeploymentBalance;
- long expectedMaximumBalance = deployersInitialBalance - deployAtFee - messageFee;
-
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
- assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
-
- // Test orphaning
- BlockUtils.orphanLastBlock(repository);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance - messageFee;
- actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected int calcRefundTimeout(long partnersOfferMessageTransactionTimestamp, int lockTimeA) {
+ return DogecoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
}
- @SuppressWarnings("unused")
- @Test
- public void testOfferCancelInvalidLength() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- // Instead of sending creator's address to AT, send too-short/invalid message
- byte[] messageData = new byte[7];
- RANDOM.nextBytes(messageData);
- MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
- long messageFee = messageTransaction.getTransactionData().getFee();
-
- // AT should process 'cancel' message in next block
- // As message is too short, it will be padded to 32bytes but cancel code doesn't care about message content, so should be ok
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in CANCELLED mode
- CrossChainTradeData tradeData = DogecoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.CANCELLED, tradeData.mode);
- }
+ @Override
+ protected byte[] buildTradeMessage(String address, byte[] publicKey, byte[] hashOfSecretA, int lockTimeA, int refundTimeout) {
+ return DogecoinACCTv3.buildTradeMessage(address, publicKey, hashOfSecretA, lockTimeA, refundTimeout);
}
- @SuppressWarnings("unused")
- @Test
- public void testTradingInfoProcessing() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = DogecoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = DogecoinACCTv3.buildTradeMessage(partner.getAddress(), dogecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- Block postDeploymentBlock = BlockUtils.mintBlock(repository);
- int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- describeAt(repository, atAddress);
-
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = DogecoinACCTv3.getInstance().populateTradeData(repository, atData);
-
- // AT should be in TRADE mode
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- // Check hashOfSecretA was extracted correctly
- assertTrue(Arrays.equals(hashOfSecretA, tradeData.hashOfSecretA));
-
- // Check trade partner Qortal address was extracted correctly
- assertEquals(partner.getAddress(), tradeData.qortalPartnerAddress);
-
- // Check trade partner's dogecoin PKH was extracted correctly
- assertTrue(Arrays.equals(dogecoinPublicKeyHash, tradeData.partnerForeignPKH));
-
- // Test orphaning
- BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance;
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected byte[] buildRedeemMessage(byte[] secretA, String address) {
+ return DogecoinACCTv3.buildRedeemMessage(secretA, address);
}
- // TEST SENDING TRADING INFO BUT NOT FROM AT CREATOR (SHOULD BE IGNORED)
- @SuppressWarnings("unused")
- @Test
- public void testIncorrectTradeSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = DogecoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT BUT NOT FROM AT CREATOR
- byte[] messageData = DogecoinACCTv3.buildTradeMessage(partner.getAddress(), dogecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
-
- BlockUtils.mintBlock(repository);
-
- long expectedBalance = partnersInitialBalance;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-initial-payout balance incorrect", expectedBalance, actualBalance);
-
- describeAt(repository, atAddress);
-
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = DogecoinACCTv3.getInstance().populateTradeData(repository, atData);
-
- // AT should still be in OFFER mode
- assertEquals(AcctMode.OFFERING, tradeData.mode);
- }
+ @Override
+ protected byte[] getCodeBytesHash() {
+ return DogecoinACCTv3.CODE_BYTES_HASH;
}
- @SuppressWarnings("unused")
- @Test
- public void testAutomaticTradeRefund() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = DogecoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = DogecoinACCTv3.buildTradeMessage(partner.getAddress(), dogecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- Block postDeploymentBlock = BlockUtils.mintBlock(repository);
- int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
-
- // Check refund
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in REFUNDED mode
- CrossChainTradeData tradeData = DogecoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.REFUNDED, tradeData.mode);
-
- // Test orphaning
- BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance;
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected String getSymbol() {
+ return SYMBOL;
}
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretCorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = DogecoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = DogecoinACCTv3.buildTradeMessage(partner.getAddress(), dogecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, from correct account
- messageData = DogecoinACCTv3.buildRedeemMessage(secretA, partner.getAddress());
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in REDEEMED mode
- CrossChainTradeData tradeData = DogecoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.REDEEMED, tradeData.mode);
-
- // Check balances
- long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee() + redeemAmount;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-redeem balance incorrect", expectedBalance, actualBalance);
-
- // Orphan redeem
- BlockUtils.orphanLastBlock(repository);
-
- // Check balances
- expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-orphan/pre-redeem balance incorrect", expectedBalance, actualBalance);
-
- // Check AT state
- ATStateData postOrphanAtStateData = repository.getATRepository().getLatestATState(atAddress);
-
- assertTrue("AT states mismatch", Arrays.equals(preRedeemAtStateData.getStateData(), postOrphanAtStateData.getStateData()));
- }
+ @Override
+ protected String getName() {
+ return NAME;
}
-
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretIncorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
-
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = DogecoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = DogecoinACCTv3.buildTradeMessage(partner.getAddress(), dogecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, but from wrong account
- messageData = DogecoinACCTv3.buildRedeemMessage(secretA, partner.getAddress());
- messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should still be in TRADE mode
- CrossChainTradeData tradeData = DogecoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- // Check balances
- long expectedBalance = partnersInitialBalance;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
-
- // Check eventual refund
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testIncorrectSecretCorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
-
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = DogecoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = DogecoinACCTv3.buildTradeMessage(partner.getAddress(), dogecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send incorrect secret to AT, from correct account
- byte[] wrongSecret = new byte[32];
- RANDOM.nextBytes(wrongSecret);
- messageData = DogecoinACCTv3.buildRedeemMessage(wrongSecret, partner.getAddress());
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should still be in TRADE mode
- CrossChainTradeData tradeData = DogecoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
-
- // Check eventual refund
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretCorrectSenderInvalidMessageLength() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = DogecoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = DogecoinACCTv3.buildTradeMessage(partner.getAddress(), dogecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, from correct account, but missing receive address, hence incorrect length
- messageData = Bytes.concat(secretA);
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should be in TRADING mode
- CrossChainTradeData tradeData = DogecoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testDescribeDeployed() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
-
- List executableAts = repository.getATRepository().getAllExecutableATs();
-
- for (ATData atData : executableAts) {
- String atAddress = atData.getATAddress();
- byte[] codeBytes = atData.getCodeBytes();
- byte[] codeHash = Crypto.digest(codeBytes);
-
- System.out.println(String.format("%s: code length: %d byte%s, code hash: %s",
- atAddress,
- codeBytes.length,
- (codeBytes.length != 1 ? "s": ""),
- HashCode.fromBytes(codeHash)));
-
- // Not one of ours?
- if (!Arrays.equals(codeHash, DogecoinACCTv3.CODE_BYTES_HASH))
- continue;
-
- describeAt(repository, atAddress);
- }
- }
- }
-
- private int calcTestLockTimeA(long messageTimestamp) {
- return (int) (messageTimestamp / 1000L + tradeTimeout * 60);
- }
-
- private DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer, String tradeAddress) throws DataException {
- byte[] creationBytes = DogecoinACCTv3.buildQortalAT(tradeAddress, dogecoinPublicKeyHash, redeemAmount, dogecoinAmount, tradeTimeout);
-
- long txTimestamp = System.currentTimeMillis();
- byte[] lastReference = deployer.getLastReference();
-
- if (lastReference == null) {
- System.err.println(String.format("Qortal account %s has no last reference", deployer.getAddress()));
- System.exit(2);
- }
-
- Long fee = null;
- String name = "QORT-DOGE cross-chain trade";
- String description = String.format("Qortal-Dogecoin cross-chain trade");
- String atType = "ACCT";
- String tags = "QORT-DOGE ACCT";
-
- BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, deployer.getPublicKey(), fee, null);
- TransactionData deployAtTransactionData = new DeployAtTransactionData(baseTransactionData, name, description, atType, tags, creationBytes, fundingAmount, Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = new DeployAtTransaction(repository, deployAtTransactionData);
-
- fee = deployAtTransaction.calcRecommendedFee();
- deployAtTransactionData.setFee(fee);
-
- TransactionUtils.signAndMint(repository, deployAtTransactionData, deployer);
-
- return deployAtTransaction;
- }
-
- private MessageTransaction sendMessage(Repository repository, PrivateKeyAccount sender, byte[] data, String recipient) throws DataException {
- long txTimestamp = System.currentTimeMillis();
- byte[] lastReference = sender.getLastReference();
-
- if (lastReference == null) {
- System.err.println(String.format("Qortal account %s has no last reference", sender.getAddress()));
- System.exit(2);
- }
-
- Long fee = null;
- int version = 4;
- int nonce = 0;
- long amount = 0;
- Long assetId = null; // because amount is zero
-
- BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, sender.getPublicKey(), fee, null);
- TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, version, nonce, recipient, amount, assetId, data, false, false);
-
- MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData);
-
- fee = messageTransaction.calcRecommendedFee();
- messageTransactionData.setFee(fee);
-
- TransactionUtils.signAndMint(repository, messageTransactionData, sender);
-
- return messageTransaction;
- }
-
- private void checkTradeRefund(Repository repository, Account deployer, long deployersInitialBalance, long deployAtFee) throws DataException {
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
- int refundTimeout = tradeTimeout / 2 + 1; // close enough
-
- // AT should automatically refund deployer after 'refundTimeout' blocks
- for (int blockCount = 0; blockCount <= refundTimeout; ++blockCount)
- BlockUtils.mintBlock(repository);
-
- // We don't bother to exactly calculate QORT spent running AT for several blocks, but we do know the expected range
- long expectedMinimumBalance = deployersPostDeploymentBalance;
- long expectedMaximumBalance = deployersInitialBalance - deployAtFee;
-
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
- assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
- }
-
- private void describeAt(Repository repository, String atAddress) throws DataException {
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = DogecoinACCTv3.getInstance().populateTradeData(repository, atData);
-
- Function epochMilliFormatter = (timestamp) -> LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC).format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM));
- int currentBlockHeight = repository.getBlockRepository().getBlockchainHeight();
-
- System.out.print(String.format("%s:\n"
- + "\tmode: %s\n"
- + "\tcreator: %s,\n"
- + "\tcreation timestamp: %s,\n"
- + "\tcurrent balance: %s QORT,\n"
- + "\tis finished: %b,\n"
- + "\tredeem payout: %s QORT,\n"
- + "\texpected dogecoin: %s DOGE,\n"
- + "\tcurrent block height: %d,\n",
- tradeData.qortalAtAddress,
- tradeData.mode,
- tradeData.qortalCreator,
- epochMilliFormatter.apply(tradeData.creationTimestamp),
- Amounts.prettyAmount(tradeData.qortBalance),
- atData.getIsFinished(),
- Amounts.prettyAmount(tradeData.qortAmount),
- Amounts.prettyAmount(tradeData.expectedForeignAmount),
- currentBlockHeight));
-
- if (tradeData.mode != AcctMode.OFFERING && tradeData.mode != AcctMode.CANCELLED) {
- System.out.println(String.format("\trefund timeout: %d minutes,\n"
- + "\trefund height: block %d,\n"
- + "\tHASH160 of secret-A: %s,\n"
- + "\tDogecoin P2SH-A nLockTime: %d (%s),\n"
- + "\ttrade partner: %s\n"
- + "\tpartner's receiving address: %s",
- tradeData.refundTimeout,
- tradeData.tradeRefundHeight,
- HashCode.fromBytes(tradeData.hashOfSecretA).toString().substring(0, 40),
- tradeData.lockTimeA, epochMilliFormatter.apply(tradeData.lockTimeA * 1000L),
- tradeData.qortalPartnerAddress,
- tradeData.qortalPartnerReceivingAddress));
- }
- }
-
- private PrivateKeyAccount createTradeAccount(Repository repository) {
- // We actually use a known test account with funds to avoid PoW compute
- return Common.getTestAccount(repository, "alice");
- }
-
}
diff --git a/src/test/java/org/qortal/test/crosschain/litecoinv1/LitecoinACCTv1Tests.java b/src/test/java/org/qortal/test/crosschain/litecoinv1/LitecoinACCTv1Tests.java
index 609ff5f3..91a450d0 100644
--- a/src/test/java/org/qortal/test/crosschain/litecoinv1/LitecoinACCTv1Tests.java
+++ b/src/test/java/org/qortal/test/crosschain/litecoinv1/LitecoinACCTv1Tests.java
@@ -1,770 +1,60 @@
package org.qortal.test.crosschain.litecoinv1;
-import static org.junit.Assert.*;
-
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-import java.time.format.FormatStyle;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-import java.util.function.Function;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.qortal.account.Account;
-import org.qortal.account.PrivateKeyAccount;
-import org.qortal.asset.Asset;
-import org.qortal.block.Block;
+import org.qortal.crosschain.ACCT;
import org.qortal.crosschain.LitecoinACCTv1;
-import org.qortal.crosschain.AcctMode;
-import org.qortal.crypto.Crypto;
-import org.qortal.data.at.ATData;
-import org.qortal.data.at.ATStateData;
-import org.qortal.data.crosschain.CrossChainTradeData;
-import org.qortal.data.transaction.BaseTransactionData;
-import org.qortal.data.transaction.DeployAtTransactionData;
-import org.qortal.data.transaction.MessageTransactionData;
-import org.qortal.data.transaction.TransactionData;
-import org.qortal.group.Group;
-import org.qortal.repository.DataException;
-import org.qortal.repository.Repository;
-import org.qortal.repository.RepositoryManager;
-import org.qortal.test.common.BlockUtils;
-import org.qortal.test.common.Common;
-import org.qortal.test.common.TransactionUtils;
-import org.qortal.transaction.DeployAtTransaction;
-import org.qortal.transaction.MessageTransaction;
-import org.qortal.utils.Amounts;
+import org.qortal.test.crosschain.ACCTTests;
import com.google.common.hash.HashCode;
-import com.google.common.primitives.Bytes;
-public class LitecoinACCTv1Tests extends Common {
+public class LitecoinACCTv1Tests extends ACCTTests {
- public static final byte[] secretA = "This string is exactly 32 bytes!".getBytes();
- public static final byte[] hashOfSecretA = Crypto.hash160(secretA); // daf59884b4d1aec8c1b17102530909ee43c0151a
public static final byte[] litecoinPublicKeyHash = HashCode.fromString("bb00bb11bb22bb33bb44bb55bb66bb77bb88bb99").asBytes();
- public static final int tradeTimeout = 20; // blocks
- public static final long redeemAmount = 80_40200000L;
- public static final long fundingAmount = 123_45600000L;
- public static final long litecoinAmount = 864200L; // 0.00864200 LTC
+ private static final String SYMBOL = "LTC";
- private static final Random RANDOM = new Random();
+ private static final String NAME = "Litecoin";
- @Before
- public void beforeTest() throws DataException {
- Common.useDefaultSettings();
+ @Override
+ protected byte[] getPublicKey() {
+ return litecoinPublicKeyHash;
}
- @Test
- public void testCompile() {
- PrivateKeyAccount tradeAccount = createTradeAccount(null);
-
- byte[] creationBytes = LitecoinACCTv1.buildQortalAT(tradeAccount.getAddress(), litecoinPublicKeyHash, redeemAmount, litecoinAmount, tradeTimeout);
- assertNotNull(creationBytes);
-
- System.out.println("AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
+ @Override
+ protected byte[] buildQortalAT(String address, byte[] publicKey, long redeemAmount, long foreignAmount, int tradeTimeout) {
+ return LitecoinACCTv1.buildQortalAT(address, publicKey, redeemAmount, foreignAmount, tradeTimeout);
}
- @Test
- public void testDeploy() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
-
- long expectedBalance = deployersInitialBalance - fundingAmount - deployAtTransaction.getTransactionData().getFee();
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = fundingAmount;
- actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
-
- assertEquals("AT's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = partnersInitialBalance;
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- // Test orphaning
- BlockUtils.orphanLastBlock(repository);
-
- expectedBalance = deployersInitialBalance;
- actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = 0;
- actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
-
- assertEquals("AT's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = partnersInitialBalance;
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected ACCT getInstance() {
+ return LitecoinACCTv1.getInstance();
}
- @SuppressWarnings("unused")
- @Test
- public void testOfferCancel() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- // Send creator's address to AT, instead of typical partner's address
- byte[] messageData = LitecoinACCTv1.getInstance().buildCancelMessage(deployer.getAddress());
- MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
- long messageFee = messageTransaction.getTransactionData().getFee();
-
- // AT should process 'cancel' message in next block
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in CANCELLED mode
- CrossChainTradeData tradeData = LitecoinACCTv1.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.CANCELLED, tradeData.mode);
-
- // Check balances
- long expectedMinimumBalance = deployersPostDeploymentBalance;
- long expectedMaximumBalance = deployersInitialBalance - deployAtFee - messageFee;
-
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
- assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
-
- // Test orphaning
- BlockUtils.orphanLastBlock(repository);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance - messageFee;
- actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected int calcRefundTimeout(long partnersOfferMessageTransactionTimestamp, int lockTimeA) {
+ return LitecoinACCTv1.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
}
- @SuppressWarnings("unused")
- @Test
- public void testOfferCancelInvalidLength() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- // Instead of sending creator's address to AT, send too-short/invalid message
- byte[] messageData = new byte[7];
- RANDOM.nextBytes(messageData);
- MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
- long messageFee = messageTransaction.getTransactionData().getFee();
-
- // AT should process 'cancel' message in next block
- // As message is too short, it will be padded to 32bytes but cancel code doesn't care about message content, so should be ok
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in CANCELLED mode
- CrossChainTradeData tradeData = LitecoinACCTv1.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.CANCELLED, tradeData.mode);
- }
+ @Override
+ protected byte[] buildTradeMessage(String address, byte[] publicKey, byte[] hashOfSecretA, int lockTimeA, int refundTimeout) {
+ return LitecoinACCTv1.buildTradeMessage(address, publicKey, hashOfSecretA, lockTimeA, refundTimeout);
}
- @SuppressWarnings("unused")
- @Test
- public void testTradingInfoProcessing() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = LitecoinACCTv1.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = LitecoinACCTv1.buildTradeMessage(partner.getAddress(), litecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- Block postDeploymentBlock = BlockUtils.mintBlock(repository);
- int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- describeAt(repository, atAddress);
-
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = LitecoinACCTv1.getInstance().populateTradeData(repository, atData);
-
- // AT should be in TRADE mode
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- // Check hashOfSecretA was extracted correctly
- assertTrue(Arrays.equals(hashOfSecretA, tradeData.hashOfSecretA));
-
- // Check trade partner Qortal address was extracted correctly
- assertEquals(partner.getAddress(), tradeData.qortalPartnerAddress);
-
- // Check trade partner's Litecoin PKH was extracted correctly
- assertTrue(Arrays.equals(litecoinPublicKeyHash, tradeData.partnerForeignPKH));
-
- // Test orphaning
- BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance;
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected byte[] buildRedeemMessage(byte[] secretA, String address) {
+ return LitecoinACCTv1.buildRedeemMessage(secretA, address);
}
- // TEST SENDING TRADING INFO BUT NOT FROM AT CREATOR (SHOULD BE IGNORED)
- @SuppressWarnings("unused")
- @Test
- public void testIncorrectTradeSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = LitecoinACCTv1.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT BUT NOT FROM AT CREATOR
- byte[] messageData = LitecoinACCTv1.buildTradeMessage(partner.getAddress(), litecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
-
- BlockUtils.mintBlock(repository);
-
- long expectedBalance = partnersInitialBalance;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-initial-payout balance incorrect", expectedBalance, actualBalance);
-
- describeAt(repository, atAddress);
-
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = LitecoinACCTv1.getInstance().populateTradeData(repository, atData);
-
- // AT should still be in OFFER mode
- assertEquals(AcctMode.OFFERING, tradeData.mode);
- }
+ @Override
+ protected byte[] getCodeBytesHash() {
+ return LitecoinACCTv1.CODE_BYTES_HASH;
}
- @SuppressWarnings("unused")
- @Test
- public void testAutomaticTradeRefund() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = LitecoinACCTv1.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = LitecoinACCTv1.buildTradeMessage(partner.getAddress(), litecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- Block postDeploymentBlock = BlockUtils.mintBlock(repository);
- int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
-
- // Check refund
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in REFUNDED mode
- CrossChainTradeData tradeData = LitecoinACCTv1.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.REFUNDED, tradeData.mode);
-
- // Test orphaning
- BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance;
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected String getSymbol() {
+ return SYMBOL;
}
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretCorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = LitecoinACCTv1.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = LitecoinACCTv1.buildTradeMessage(partner.getAddress(), litecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, from correct account
- messageData = LitecoinACCTv1.buildRedeemMessage(secretA, partner.getAddress());
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in REDEEMED mode
- CrossChainTradeData tradeData = LitecoinACCTv1.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.REDEEMED, tradeData.mode);
-
- // Check balances
- long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee() + redeemAmount;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-redeem balance incorrect", expectedBalance, actualBalance);
-
- // Orphan redeem
- BlockUtils.orphanLastBlock(repository);
-
- // Check balances
- expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-orphan/pre-redeem balance incorrect", expectedBalance, actualBalance);
-
- // Check AT state
- ATStateData postOrphanAtStateData = repository.getATRepository().getLatestATState(atAddress);
-
- assertTrue("AT states mismatch", Arrays.equals(preRedeemAtStateData.getStateData(), postOrphanAtStateData.getStateData()));
- }
+ @Override
+ protected String getName() {
+ return NAME;
}
-
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretIncorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
-
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = LitecoinACCTv1.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = LitecoinACCTv1.buildTradeMessage(partner.getAddress(), litecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, but from wrong account
- messageData = LitecoinACCTv1.buildRedeemMessage(secretA, partner.getAddress());
- messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should still be in TRADE mode
- CrossChainTradeData tradeData = LitecoinACCTv1.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- // Check balances
- long expectedBalance = partnersInitialBalance;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
-
- // Check eventual refund
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testIncorrectSecretCorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
-
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = LitecoinACCTv1.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = LitecoinACCTv1.buildTradeMessage(partner.getAddress(), litecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send incorrect secret to AT, from correct account
- byte[] wrongSecret = new byte[32];
- RANDOM.nextBytes(wrongSecret);
- messageData = LitecoinACCTv1.buildRedeemMessage(wrongSecret, partner.getAddress());
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should still be in TRADE mode
- CrossChainTradeData tradeData = LitecoinACCTv1.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
-
- // Check eventual refund
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretCorrectSenderInvalidMessageLength() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = LitecoinACCTv1.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = LitecoinACCTv1.buildTradeMessage(partner.getAddress(), litecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, from correct account, but missing receive address, hence incorrect length
- messageData = Bytes.concat(secretA);
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should be in TRADING mode
- CrossChainTradeData tradeData = LitecoinACCTv1.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testDescribeDeployed() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
-
- List executableAts = repository.getATRepository().getAllExecutableATs();
-
- for (ATData atData : executableAts) {
- String atAddress = atData.getATAddress();
- byte[] codeBytes = atData.getCodeBytes();
- byte[] codeHash = Crypto.digest(codeBytes);
-
- System.out.println(String.format("%s: code length: %d byte%s, code hash: %s",
- atAddress,
- codeBytes.length,
- (codeBytes.length != 1 ? "s": ""),
- HashCode.fromBytes(codeHash)));
-
- // Not one of ours?
- if (!Arrays.equals(codeHash, LitecoinACCTv1.CODE_BYTES_HASH))
- continue;
-
- describeAt(repository, atAddress);
- }
- }
- }
-
- private int calcTestLockTimeA(long messageTimestamp) {
- return (int) (messageTimestamp / 1000L + tradeTimeout * 60);
- }
-
- private DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer, String tradeAddress) throws DataException {
- byte[] creationBytes = LitecoinACCTv1.buildQortalAT(tradeAddress, litecoinPublicKeyHash, redeemAmount, litecoinAmount, tradeTimeout);
-
- long txTimestamp = System.currentTimeMillis();
- byte[] lastReference = deployer.getLastReference();
-
- if (lastReference == null) {
- System.err.println(String.format("Qortal account %s has no last reference", deployer.getAddress()));
- System.exit(2);
- }
-
- Long fee = null;
- String name = "QORT-LTC cross-chain trade";
- String description = String.format("Qortal-Litecoin cross-chain trade");
- String atType = "ACCT";
- String tags = "QORT-LTC ACCT";
-
- BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, deployer.getPublicKey(), fee, null);
- TransactionData deployAtTransactionData = new DeployAtTransactionData(baseTransactionData, name, description, atType, tags, creationBytes, fundingAmount, Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = new DeployAtTransaction(repository, deployAtTransactionData);
-
- fee = deployAtTransaction.calcRecommendedFee();
- deployAtTransactionData.setFee(fee);
-
- TransactionUtils.signAndMint(repository, deployAtTransactionData, deployer);
-
- return deployAtTransaction;
- }
-
- private MessageTransaction sendMessage(Repository repository, PrivateKeyAccount sender, byte[] data, String recipient) throws DataException {
- long txTimestamp = System.currentTimeMillis();
- byte[] lastReference = sender.getLastReference();
-
- if (lastReference == null) {
- System.err.println(String.format("Qortal account %s has no last reference", sender.getAddress()));
- System.exit(2);
- }
-
- Long fee = null;
- int version = 4;
- int nonce = 0;
- long amount = 0;
- Long assetId = null; // because amount is zero
-
- BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, sender.getPublicKey(), fee, null);
- TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, version, nonce, recipient, amount, assetId, data, false, false);
-
- MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData);
-
- fee = messageTransaction.calcRecommendedFee();
- messageTransactionData.setFee(fee);
-
- TransactionUtils.signAndMint(repository, messageTransactionData, sender);
-
- return messageTransaction;
- }
-
- private void checkTradeRefund(Repository repository, Account deployer, long deployersInitialBalance, long deployAtFee) throws DataException {
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
- int refundTimeout = tradeTimeout / 2 + 1; // close enough
-
- // AT should automatically refund deployer after 'refundTimeout' blocks
- for (int blockCount = 0; blockCount <= refundTimeout; ++blockCount)
- BlockUtils.mintBlock(repository);
-
- // We don't bother to exactly calculate QORT spent running AT for several blocks, but we do know the expected range
- long expectedMinimumBalance = deployersPostDeploymentBalance;
- long expectedMaximumBalance = deployersInitialBalance - deployAtFee;
-
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
- assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
- }
-
- private void describeAt(Repository repository, String atAddress) throws DataException {
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = LitecoinACCTv1.getInstance().populateTradeData(repository, atData);
-
- Function epochMilliFormatter = (timestamp) -> LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC).format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM));
- int currentBlockHeight = repository.getBlockRepository().getBlockchainHeight();
-
- System.out.print(String.format("%s:\n"
- + "\tmode: %s\n"
- + "\tcreator: %s,\n"
- + "\tcreation timestamp: %s,\n"
- + "\tcurrent balance: %s QORT,\n"
- + "\tis finished: %b,\n"
- + "\tredeem payout: %s QORT,\n"
- + "\texpected Litecoin: %s LTC,\n"
- + "\tcurrent block height: %d,\n",
- tradeData.qortalAtAddress,
- tradeData.mode,
- tradeData.qortalCreator,
- epochMilliFormatter.apply(tradeData.creationTimestamp),
- Amounts.prettyAmount(tradeData.qortBalance),
- atData.getIsFinished(),
- Amounts.prettyAmount(tradeData.qortAmount),
- Amounts.prettyAmount(tradeData.expectedForeignAmount),
- currentBlockHeight));
-
- if (tradeData.mode != AcctMode.OFFERING && tradeData.mode != AcctMode.CANCELLED) {
- System.out.println(String.format("\trefund timeout: %d minutes,\n"
- + "\trefund height: block %d,\n"
- + "\tHASH160 of secret-A: %s,\n"
- + "\tLitecoin P2SH-A nLockTime: %d (%s),\n"
- + "\ttrade partner: %s\n"
- + "\tpartner's receiving address: %s",
- tradeData.refundTimeout,
- tradeData.tradeRefundHeight,
- HashCode.fromBytes(tradeData.hashOfSecretA).toString().substring(0, 40),
- tradeData.lockTimeA, epochMilliFormatter.apply(tradeData.lockTimeA * 1000L),
- tradeData.qortalPartnerAddress,
- tradeData.qortalPartnerReceivingAddress));
- }
- }
-
- private PrivateKeyAccount createTradeAccount(Repository repository) {
- // We actually use a known test account with funds to avoid PoW compute
- return Common.getTestAccount(repository, "alice");
- }
-
}
diff --git a/src/test/java/org/qortal/test/crosschain/litecoinv3/LitecoinACCTv3Tests.java b/src/test/java/org/qortal/test/crosschain/litecoinv3/LitecoinACCTv3Tests.java
index 009af5ea..a1a0bfcc 100644
--- a/src/test/java/org/qortal/test/crosschain/litecoinv3/LitecoinACCTv3Tests.java
+++ b/src/test/java/org/qortal/test/crosschain/litecoinv3/LitecoinACCTv3Tests.java
@@ -1,769 +1,58 @@
package org.qortal.test.crosschain.litecoinv3;
import com.google.common.hash.HashCode;
-import com.google.common.primitives.Bytes;
-import org.junit.Before;
-import org.junit.Test;
-import org.qortal.account.Account;
-import org.qortal.account.PrivateKeyAccount;
-import org.qortal.asset.Asset;
-import org.qortal.block.Block;
-import org.qortal.crosschain.AcctMode;
-import org.qortal.crosschain.LitecoinACCTv3;
-import org.qortal.crypto.Crypto;
-import org.qortal.data.at.ATData;
-import org.qortal.data.at.ATStateData;
-import org.qortal.data.crosschain.CrossChainTradeData;
-import org.qortal.data.transaction.BaseTransactionData;
-import org.qortal.data.transaction.DeployAtTransactionData;
-import org.qortal.data.transaction.MessageTransactionData;
-import org.qortal.data.transaction.TransactionData;
-import org.qortal.group.Group;
-import org.qortal.repository.DataException;
-import org.qortal.repository.Repository;
-import org.qortal.repository.RepositoryManager;
-import org.qortal.test.common.BlockUtils;
-import org.qortal.test.common.Common;
-import org.qortal.test.common.TransactionUtils;
-import org.qortal.transaction.DeployAtTransaction;
-import org.qortal.transaction.MessageTransaction;
-import org.qortal.utils.Amounts;
+import org.qortal.crosschain.ACCT;
+import org.qortal.crosschain.LitecoinACCTv1;
+import org.qortal.test.crosschain.ACCTTests;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-import java.time.format.FormatStyle;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-import java.util.function.Function;
+public class LitecoinACCTv3Tests extends ACCTTests {
-import static org.junit.Assert.*;
-
-public class LitecoinACCTv3Tests extends Common {
-
- public static final byte[] secretA = "This string is exactly 32 bytes!".getBytes();
- public static final byte[] hashOfSecretA = Crypto.hash160(secretA); // daf59884b4d1aec8c1b17102530909ee43c0151a
public static final byte[] litecoinPublicKeyHash = HashCode.fromString("bb00bb11bb22bb33bb44bb55bb66bb77bb88bb99").asBytes();
- public static final int tradeTimeout = 20; // blocks
- public static final long redeemAmount = 80_40200000L;
- public static final long fundingAmount = 123_45600000L;
- public static final long litecoinAmount = 864200L; // 0.00864200 LTC
+ private static final String SYMBOL = "LTC";
+ private static final String NAME = "Litecoin";
- private static final Random RANDOM = new Random();
-
- @Before
- public void beforeTest() throws DataException {
- Common.useDefaultSettings();
+ @Override
+ protected byte[] getPublicKey() {
+ return litecoinPublicKeyHash;
}
- @Test
- public void testCompile() {
- PrivateKeyAccount tradeAccount = createTradeAccount(null);
-
- byte[] creationBytes = LitecoinACCTv3.buildQortalAT(tradeAccount.getAddress(), litecoinPublicKeyHash, redeemAmount, litecoinAmount, tradeTimeout);
- assertNotNull(creationBytes);
-
- System.out.println("AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
+ @Override
+ protected byte[] buildQortalAT(String address, byte[] publicKey, long redeemAmount, long foreignAmount, int tradeTimeout) {
+ return LitecoinACCTv1.buildQortalAT(address, publicKey, redeemAmount, foreignAmount, tradeTimeout);
}
- @Test
- public void testDeploy() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
-
- long expectedBalance = deployersInitialBalance - fundingAmount - deployAtTransaction.getTransactionData().getFee();
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = fundingAmount;
- actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
-
- assertEquals("AT's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = partnersInitialBalance;
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- // Test orphaning
- BlockUtils.orphanLastBlock(repository);
-
- expectedBalance = deployersInitialBalance;
- actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = 0;
- actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
-
- assertEquals("AT's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = partnersInitialBalance;
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected ACCT getInstance() {
+ return LitecoinACCTv1.getInstance();
}
- @SuppressWarnings("unused")
- @Test
- public void testOfferCancel() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- // Send creator's address to AT, instead of typical partner's address
- byte[] messageData = LitecoinACCTv3.getInstance().buildCancelMessage(deployer.getAddress());
- MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
- long messageFee = messageTransaction.getTransactionData().getFee();
-
- // AT should process 'cancel' message in next block
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in CANCELLED mode
- CrossChainTradeData tradeData = LitecoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.CANCELLED, tradeData.mode);
-
- // Check balances
- long expectedMinimumBalance = deployersPostDeploymentBalance;
- long expectedMaximumBalance = deployersInitialBalance - deployAtFee - messageFee;
-
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
- assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
-
- // Test orphaning
- BlockUtils.orphanLastBlock(repository);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance - messageFee;
- actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected int calcRefundTimeout(long partnersOfferMessageTransactionTimestamp, int lockTimeA) {
+ return LitecoinACCTv1.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
}
- @SuppressWarnings("unused")
- @Test
- public void testOfferCancelInvalidLength() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- // Instead of sending creator's address to AT, send too-short/invalid message
- byte[] messageData = new byte[7];
- RANDOM.nextBytes(messageData);
- MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
- long messageFee = messageTransaction.getTransactionData().getFee();
-
- // AT should process 'cancel' message in next block
- // As message is too short, it will be padded to 32bytes but cancel code doesn't care about message content, so should be ok
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in CANCELLED mode
- CrossChainTradeData tradeData = LitecoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.CANCELLED, tradeData.mode);
- }
+ @Override
+ protected byte[] buildTradeMessage(String address, byte[] publicKey, byte[] hashOfSecretA, int lockTimeA, int refundTimeout) {
+ return LitecoinACCTv1.buildTradeMessage(address, publicKey, hashOfSecretA, lockTimeA, refundTimeout);
}
- @SuppressWarnings("unused")
- @Test
- public void testTradingInfoProcessing() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = LitecoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = LitecoinACCTv3.buildTradeMessage(partner.getAddress(), litecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- Block postDeploymentBlock = BlockUtils.mintBlock(repository);
- int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- describeAt(repository, atAddress);
-
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = LitecoinACCTv3.getInstance().populateTradeData(repository, atData);
-
- // AT should be in TRADE mode
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- // Check hashOfSecretA was extracted correctly
- assertTrue(Arrays.equals(hashOfSecretA, tradeData.hashOfSecretA));
-
- // Check trade partner Qortal address was extracted correctly
- assertEquals(partner.getAddress(), tradeData.qortalPartnerAddress);
-
- // Check trade partner's Litecoin PKH was extracted correctly
- assertTrue(Arrays.equals(litecoinPublicKeyHash, tradeData.partnerForeignPKH));
-
- // Test orphaning
- BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance;
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected byte[] buildRedeemMessage(byte[] secretA, String address) {
+ return LitecoinACCTv1.buildRedeemMessage(secretA, address);
}
- // TEST SENDING TRADING INFO BUT NOT FROM AT CREATOR (SHOULD BE IGNORED)
- @SuppressWarnings("unused")
- @Test
- public void testIncorrectTradeSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = LitecoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT BUT NOT FROM AT CREATOR
- byte[] messageData = LitecoinACCTv3.buildTradeMessage(partner.getAddress(), litecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
-
- BlockUtils.mintBlock(repository);
-
- long expectedBalance = partnersInitialBalance;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-initial-payout balance incorrect", expectedBalance, actualBalance);
-
- describeAt(repository, atAddress);
-
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = LitecoinACCTv3.getInstance().populateTradeData(repository, atData);
-
- // AT should still be in OFFER mode
- assertEquals(AcctMode.OFFERING, tradeData.mode);
- }
+ @Override
+ protected byte[] getCodeBytesHash() {
+ return LitecoinACCTv1.CODE_BYTES_HASH;
}
- @SuppressWarnings("unused")
- @Test
- public void testAutomaticTradeRefund() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = LitecoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = LitecoinACCTv3.buildTradeMessage(partner.getAddress(), litecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- Block postDeploymentBlock = BlockUtils.mintBlock(repository);
- int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
-
- // Check refund
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in REFUNDED mode
- CrossChainTradeData tradeData = LitecoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.REFUNDED, tradeData.mode);
-
- // Test orphaning
- BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance;
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected String getSymbol() {
+ return SYMBOL;
}
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretCorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = LitecoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = LitecoinACCTv3.buildTradeMessage(partner.getAddress(), litecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, from correct account
- messageData = LitecoinACCTv3.buildRedeemMessage(secretA, partner.getAddress());
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in REDEEMED mode
- CrossChainTradeData tradeData = LitecoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.REDEEMED, tradeData.mode);
-
- // Check balances
- long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee() + redeemAmount;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-redeem balance incorrect", expectedBalance, actualBalance);
-
- // Orphan redeem
- BlockUtils.orphanLastBlock(repository);
-
- // Check balances
- expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-orphan/pre-redeem balance incorrect", expectedBalance, actualBalance);
-
- // Check AT state
- ATStateData postOrphanAtStateData = repository.getATRepository().getLatestATState(atAddress);
-
- assertTrue("AT states mismatch", Arrays.equals(preRedeemAtStateData.getStateData(), postOrphanAtStateData.getStateData()));
- }
+ @Override
+ protected String getName() {
+ return NAME;
}
-
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretIncorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
-
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = LitecoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = LitecoinACCTv3.buildTradeMessage(partner.getAddress(), litecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, but from wrong account
- messageData = LitecoinACCTv3.buildRedeemMessage(secretA, partner.getAddress());
- messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should still be in TRADE mode
- CrossChainTradeData tradeData = LitecoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- // Check balances
- long expectedBalance = partnersInitialBalance;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
-
- // Check eventual refund
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testIncorrectSecretCorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
-
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = LitecoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = LitecoinACCTv3.buildTradeMessage(partner.getAddress(), litecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send incorrect secret to AT, from correct account
- byte[] wrongSecret = new byte[32];
- RANDOM.nextBytes(wrongSecret);
- messageData = LitecoinACCTv3.buildRedeemMessage(wrongSecret, partner.getAddress());
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should still be in TRADE mode
- CrossChainTradeData tradeData = LitecoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
-
- // Check eventual refund
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretCorrectSenderInvalidMessageLength() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = LitecoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = LitecoinACCTv3.buildTradeMessage(partner.getAddress(), litecoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, from correct account, but missing receive address, hence incorrect length
- messageData = Bytes.concat(secretA);
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should be in TRADING mode
- CrossChainTradeData tradeData = LitecoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testDescribeDeployed() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
-
- List executableAts = repository.getATRepository().getAllExecutableATs();
-
- for (ATData atData : executableAts) {
- String atAddress = atData.getATAddress();
- byte[] codeBytes = atData.getCodeBytes();
- byte[] codeHash = Crypto.digest(codeBytes);
-
- System.out.println(String.format("%s: code length: %d byte%s, code hash: %s",
- atAddress,
- codeBytes.length,
- (codeBytes.length != 1 ? "s": ""),
- HashCode.fromBytes(codeHash)));
-
- // Not one of ours?
- if (!Arrays.equals(codeHash, LitecoinACCTv3.CODE_BYTES_HASH))
- continue;
-
- describeAt(repository, atAddress);
- }
- }
- }
-
- private int calcTestLockTimeA(long messageTimestamp) {
- return (int) (messageTimestamp / 1000L + tradeTimeout * 60);
- }
-
- private DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer, String tradeAddress) throws DataException {
- byte[] creationBytes = LitecoinACCTv3.buildQortalAT(tradeAddress, litecoinPublicKeyHash, redeemAmount, litecoinAmount, tradeTimeout);
-
- long txTimestamp = System.currentTimeMillis();
- byte[] lastReference = deployer.getLastReference();
-
- if (lastReference == null) {
- System.err.println(String.format("Qortal account %s has no last reference", deployer.getAddress()));
- System.exit(2);
- }
-
- Long fee = null;
- String name = "QORT-LTC cross-chain trade";
- String description = String.format("Qortal-Litecoin cross-chain trade");
- String atType = "ACCT";
- String tags = "QORT-LTC ACCT";
-
- BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, deployer.getPublicKey(), fee, null);
- TransactionData deployAtTransactionData = new DeployAtTransactionData(baseTransactionData, name, description, atType, tags, creationBytes, fundingAmount, Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = new DeployAtTransaction(repository, deployAtTransactionData);
-
- fee = deployAtTransaction.calcRecommendedFee();
- deployAtTransactionData.setFee(fee);
-
- TransactionUtils.signAndMint(repository, deployAtTransactionData, deployer);
-
- return deployAtTransaction;
- }
-
- private MessageTransaction sendMessage(Repository repository, PrivateKeyAccount sender, byte[] data, String recipient) throws DataException {
- long txTimestamp = System.currentTimeMillis();
- byte[] lastReference = sender.getLastReference();
-
- if (lastReference == null) {
- System.err.println(String.format("Qortal account %s has no last reference", sender.getAddress()));
- System.exit(2);
- }
-
- Long fee = null;
- int version = 4;
- int nonce = 0;
- long amount = 0;
- Long assetId = null; // because amount is zero
-
- BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, sender.getPublicKey(), fee, null);
- TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, version, nonce, recipient, amount, assetId, data, false, false);
-
- MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData);
-
- fee = messageTransaction.calcRecommendedFee();
- messageTransactionData.setFee(fee);
-
- TransactionUtils.signAndMint(repository, messageTransactionData, sender);
-
- return messageTransaction;
- }
-
- private void checkTradeRefund(Repository repository, Account deployer, long deployersInitialBalance, long deployAtFee) throws DataException {
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
- int refundTimeout = tradeTimeout / 2 + 1; // close enough
-
- // AT should automatically refund deployer after 'refundTimeout' blocks
- for (int blockCount = 0; blockCount <= refundTimeout; ++blockCount)
- BlockUtils.mintBlock(repository);
-
- // We don't bother to exactly calculate QORT spent running AT for several blocks, but we do know the expected range
- long expectedMinimumBalance = deployersPostDeploymentBalance;
- long expectedMaximumBalance = deployersInitialBalance - deployAtFee;
-
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
- assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
- }
-
- private void describeAt(Repository repository, String atAddress) throws DataException {
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = LitecoinACCTv3.getInstance().populateTradeData(repository, atData);
-
- Function epochMilliFormatter = (timestamp) -> LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC).format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM));
- int currentBlockHeight = repository.getBlockRepository().getBlockchainHeight();
-
- System.out.print(String.format("%s:\n"
- + "\tmode: %s\n"
- + "\tcreator: %s,\n"
- + "\tcreation timestamp: %s,\n"
- + "\tcurrent balance: %s QORT,\n"
- + "\tis finished: %b,\n"
- + "\tredeem payout: %s QORT,\n"
- + "\texpected Litecoin: %s LTC,\n"
- + "\tcurrent block height: %d,\n",
- tradeData.qortalAtAddress,
- tradeData.mode,
- tradeData.qortalCreator,
- epochMilliFormatter.apply(tradeData.creationTimestamp),
- Amounts.prettyAmount(tradeData.qortBalance),
- atData.getIsFinished(),
- Amounts.prettyAmount(tradeData.qortAmount),
- Amounts.prettyAmount(tradeData.expectedForeignAmount),
- currentBlockHeight));
-
- if (tradeData.mode != AcctMode.OFFERING && tradeData.mode != AcctMode.CANCELLED) {
- System.out.println(String.format("\trefund timeout: %d minutes,\n"
- + "\trefund height: block %d,\n"
- + "\tHASH160 of secret-A: %s,\n"
- + "\tLitecoin P2SH-A nLockTime: %d (%s),\n"
- + "\ttrade partner: %s\n"
- + "\tpartner's receiving address: %s",
- tradeData.refundTimeout,
- tradeData.tradeRefundHeight,
- HashCode.fromBytes(tradeData.hashOfSecretA).toString().substring(0, 40),
- tradeData.lockTimeA, epochMilliFormatter.apply(tradeData.lockTimeA * 1000L),
- tradeData.qortalPartnerAddress,
- tradeData.qortalPartnerReceivingAddress));
- }
- }
-
- private PrivateKeyAccount createTradeAccount(Repository repository) {
- // We actually use a known test account with funds to avoid PoW compute
- return Common.getTestAccount(repository, "alice");
- }
-
}
diff --git a/src/test/java/org/qortal/test/crosschain/piratechainv3/PirateChainACCTv3Tests.java b/src/test/java/org/qortal/test/crosschain/piratechainv3/PirateChainACCTv3Tests.java
index f9ac9de1..18099872 100644
--- a/src/test/java/org/qortal/test/crosschain/piratechainv3/PirateChainACCTv3Tests.java
+++ b/src/test/java/org/qortal/test/crosschain/piratechainv3/PirateChainACCTv3Tests.java
@@ -1,771 +1,58 @@
package org.qortal.test.crosschain.piratechainv3;
import com.google.common.hash.HashCode;
-import com.google.common.primitives.Bytes;
-import org.junit.Before;
-import org.junit.Test;
-import org.qortal.account.Account;
-import org.qortal.account.PrivateKeyAccount;
-import org.qortal.asset.Asset;
-import org.qortal.block.Block;
-import org.qortal.crosschain.AcctMode;
+import org.qortal.crosschain.ACCT;
import org.qortal.crosschain.PirateChainACCTv3;
-import org.qortal.crypto.Crypto;
-import org.qortal.data.at.ATData;
-import org.qortal.data.at.ATStateData;
-import org.qortal.data.crosschain.CrossChainTradeData;
-import org.qortal.data.transaction.BaseTransactionData;
-import org.qortal.data.transaction.DeployAtTransactionData;
-import org.qortal.data.transaction.MessageTransactionData;
-import org.qortal.data.transaction.TransactionData;
-import org.qortal.group.Group;
-import org.qortal.repository.DataException;
-import org.qortal.repository.Repository;
-import org.qortal.repository.RepositoryManager;
-import org.qortal.test.common.BlockUtils;
-import org.qortal.test.common.Common;
-import org.qortal.test.common.TransactionUtils;
-import org.qortal.transaction.DeployAtTransaction;
-import org.qortal.transaction.MessageTransaction;
-import org.qortal.utils.Amounts;
+import org.qortal.test.crosschain.ACCTTests;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-import java.time.format.FormatStyle;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-import java.util.function.Function;
+public class PirateChainACCTv3Tests extends ACCTTests {
-import static org.junit.Assert.*;
-
-public class PirateChainACCTv3Tests extends Common {
-
- public static final byte[] secretA = "This string is exactly 32 bytes!".getBytes();
- public static final byte[] hashOfSecretA = Crypto.hash160(secretA); // daf59884b4d1aec8c1b17102530909ee43c0151a
public static final byte[] pirateChainPublicKey = HashCode.fromString("aabb00bb11bb22bb33bb44bb55bb66bb77bb88bb99cc00cc11cc22cc33cc44cc55").asBytes(); // 33 bytes
- public static final int tradeTimeout = 20; // blocks
- public static final long redeemAmount = 80_40200000L;
- public static final long fundingAmount = 123_45600000L;
- public static final long arrrAmount = 864200L; // 0.00864200 ARRR
+ private static final String SYMBOL = "ARRR";
+ private static final String NAME = "Pirate Chain";
- private static final Random RANDOM = new Random();
-
- @Before
- public void beforeTest() throws DataException {
- Common.useDefaultSettings();
+ @Override
+ protected byte[] getPublicKey() {
+ return pirateChainPublicKey;
}
- @Test
- public void testCompile() {
- PrivateKeyAccount tradeAccount = createTradeAccount(null);
-
- byte[] creationBytes = PirateChainACCTv3.buildQortalAT(tradeAccount.getAddress(), pirateChainPublicKey, redeemAmount, arrrAmount, tradeTimeout);
- assertNotNull(creationBytes);
-
- System.out.println("AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
+ @Override
+ protected byte[] buildQortalAT(String address, byte[] publicKey, long redeemAmount, long foreignAmount, int tradeTimeout) {
+ return PirateChainACCTv3.buildQortalAT(address, publicKey, redeemAmount, foreignAmount, tradeTimeout);
}
- @Test
- public void testDeploy() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
-
- long expectedBalance = deployersInitialBalance - fundingAmount - deployAtTransaction.getTransactionData().getFee();
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = fundingAmount;
- actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
-
- assertEquals("AT's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = partnersInitialBalance;
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- // Test orphaning
- BlockUtils.orphanLastBlock(repository);
-
- expectedBalance = deployersInitialBalance;
- actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = 0;
- actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
-
- assertEquals("AT's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = partnersInitialBalance;
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected ACCT getInstance() {
+ return PirateChainACCTv3.getInstance();
}
- @SuppressWarnings("unused")
- @Test
- public void testOfferCancel() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- // Send creator's address to AT, instead of typical partner's address
- byte[] messageData = PirateChainACCTv3.getInstance().buildCancelMessage(deployer.getAddress());
- MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
- long messageFee = messageTransaction.getTransactionData().getFee();
-
- // AT should process 'cancel' message in next block
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in CANCELLED mode
- CrossChainTradeData tradeData = PirateChainACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.CANCELLED, tradeData.mode);
-
- // Check balances
- long expectedMinimumBalance = deployersPostDeploymentBalance;
- long expectedMaximumBalance = deployersInitialBalance - deployAtFee - messageFee;
-
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
- assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
-
- // Test orphaning
- BlockUtils.orphanLastBlock(repository);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance - messageFee;
- actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected int calcRefundTimeout(long partnersOfferMessageTransactionTimestamp, int lockTimeA) {
+ return PirateChainACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
}
- @SuppressWarnings("unused")
- @Test
- public void testOfferCancelInvalidLength() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- // Instead of sending creator's address to AT, send too-short/invalid message
- byte[] messageData = new byte[7];
- RANDOM.nextBytes(messageData);
- MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
- long messageFee = messageTransaction.getTransactionData().getFee();
-
- // AT should process 'cancel' message in next block
- // As message is too short, it will be padded to 32bytes but cancel code doesn't care about message content, so should be ok
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in CANCELLED mode
- CrossChainTradeData tradeData = PirateChainACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.CANCELLED, tradeData.mode);
- }
+ @Override
+ protected byte[] buildTradeMessage(String address, byte[] publicKey, byte[] hashOfSecretA, int lockTimeA, int refundTimeout) {
+ return PirateChainACCTv3.buildTradeMessage(address, publicKey, hashOfSecretA, lockTimeA, refundTimeout);
}
- @SuppressWarnings("unused")
- @Test
- public void testTradingInfoProcessing() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = PirateChainACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = PirateChainACCTv3.buildTradeMessage(partner.getAddress(), pirateChainPublicKey, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- Block postDeploymentBlock = BlockUtils.mintBlock(repository);
- int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- describeAt(repository, atAddress);
-
- System.out.println(String.format("pirateChainPublicKey: %s", HashCode.fromBytes(pirateChainPublicKey)));
-
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = PirateChainACCTv3.getInstance().populateTradeData(repository, atData);
-
- // AT should be in TRADE mode
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- // Check hashOfSecretA was extracted correctly
- assertTrue(Arrays.equals(hashOfSecretA, tradeData.hashOfSecretA));
-
- // Check trade partner Qortal address was extracted correctly
- assertEquals(partner.getAddress(), tradeData.qortalPartnerAddress);
-
- // Check trade partner's Litecoin PKH was extracted correctly
- assertTrue(Arrays.equals(pirateChainPublicKey, tradeData.partnerForeignPKH));
-
- // Test orphaning
- BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance;
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected byte[] buildRedeemMessage(byte[] secretA, String address) {
+ return PirateChainACCTv3.buildRedeemMessage(secretA, address);
}
- // TEST SENDING TRADING INFO BUT NOT FROM AT CREATOR (SHOULD BE IGNORED)
- @SuppressWarnings("unused")
- @Test
- public void testIncorrectTradeSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = PirateChainACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT BUT NOT FROM AT CREATOR
- byte[] messageData = PirateChainACCTv3.buildTradeMessage(partner.getAddress(), pirateChainPublicKey, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
-
- BlockUtils.mintBlock(repository);
-
- long expectedBalance = partnersInitialBalance;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-initial-payout balance incorrect", expectedBalance, actualBalance);
-
- describeAt(repository, atAddress);
-
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = PirateChainACCTv3.getInstance().populateTradeData(repository, atData);
-
- // AT should still be in OFFER mode
- assertEquals(AcctMode.OFFERING, tradeData.mode);
- }
+ @Override
+ protected byte[] getCodeBytesHash() {
+ return PirateChainACCTv3.CODE_BYTES_HASH;
}
- @SuppressWarnings("unused")
- @Test
- public void testAutomaticTradeRefund() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = PirateChainACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = PirateChainACCTv3.buildTradeMessage(partner.getAddress(), pirateChainPublicKey, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- Block postDeploymentBlock = BlockUtils.mintBlock(repository);
- int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
-
- // Check refund
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in REFUNDED mode
- CrossChainTradeData tradeData = PirateChainACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.REFUNDED, tradeData.mode);
-
- // Test orphaning
- BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance;
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected String getSymbol() {
+ return SYMBOL;
}
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretCorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = PirateChainACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = PirateChainACCTv3.buildTradeMessage(partner.getAddress(), pirateChainPublicKey, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, from correct account
- messageData = PirateChainACCTv3.buildRedeemMessage(secretA, partner.getAddress());
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in REDEEMED mode
- CrossChainTradeData tradeData = PirateChainACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.REDEEMED, tradeData.mode);
-
- // Check balances
- long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee() + redeemAmount;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-redeem balance incorrect", expectedBalance, actualBalance);
-
- // Orphan redeem
- BlockUtils.orphanLastBlock(repository);
-
- // Check balances
- expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-orphan/pre-redeem balance incorrect", expectedBalance, actualBalance);
-
- // Check AT state
- ATStateData postOrphanAtStateData = repository.getATRepository().getLatestATState(atAddress);
-
- assertTrue("AT states mismatch", Arrays.equals(preRedeemAtStateData.getStateData(), postOrphanAtStateData.getStateData()));
- }
+ @Override
+ protected String getName() {
+ return NAME;
}
-
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretIncorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
-
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = PirateChainACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = PirateChainACCTv3.buildTradeMessage(partner.getAddress(), pirateChainPublicKey, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, but from wrong account
- messageData = PirateChainACCTv3.buildRedeemMessage(secretA, partner.getAddress());
- messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should still be in TRADE mode
- CrossChainTradeData tradeData = PirateChainACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- // Check balances
- long expectedBalance = partnersInitialBalance;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
-
- // Check eventual refund
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testIncorrectSecretCorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
-
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = PirateChainACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = PirateChainACCTv3.buildTradeMessage(partner.getAddress(), pirateChainPublicKey, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send incorrect secret to AT, from correct account
- byte[] wrongSecret = new byte[32];
- RANDOM.nextBytes(wrongSecret);
- messageData = PirateChainACCTv3.buildRedeemMessage(wrongSecret, partner.getAddress());
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should still be in TRADE mode
- CrossChainTradeData tradeData = PirateChainACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
-
- // Check eventual refund
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretCorrectSenderInvalidMessageLength() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = PirateChainACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = PirateChainACCTv3.buildTradeMessage(partner.getAddress(), pirateChainPublicKey, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, from correct account, but missing receive address, hence incorrect length
- messageData = Bytes.concat(secretA);
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should be in TRADING mode
- CrossChainTradeData tradeData = PirateChainACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testDescribeDeployed() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
-
- List executableAts = repository.getATRepository().getAllExecutableATs();
-
- for (ATData atData : executableAts) {
- String atAddress = atData.getATAddress();
- byte[] codeBytes = atData.getCodeBytes();
- byte[] codeHash = Crypto.digest(codeBytes);
-
- System.out.println(String.format("%s: code length: %d byte%s, code hash: %s",
- atAddress,
- codeBytes.length,
- (codeBytes.length != 1 ? "s": ""),
- HashCode.fromBytes(codeHash)));
-
- // Not one of ours?
- if (!Arrays.equals(codeHash, PirateChainACCTv3.CODE_BYTES_HASH))
- continue;
-
- describeAt(repository, atAddress);
- }
- }
- }
-
- private int calcTestLockTimeA(long messageTimestamp) {
- return (int) (messageTimestamp / 1000L + tradeTimeout * 60);
- }
-
- private DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer, String tradeAddress) throws DataException {
- byte[] creationBytes = PirateChainACCTv3.buildQortalAT(tradeAddress, pirateChainPublicKey, redeemAmount, arrrAmount, tradeTimeout);
-
- long txTimestamp = System.currentTimeMillis();
- byte[] lastReference = deployer.getLastReference();
-
- if (lastReference == null) {
- System.err.println(String.format("Qortal account %s has no last reference", deployer.getAddress()));
- System.exit(2);
- }
-
- Long fee = null;
- String name = "QORT-ARRR cross-chain trade";
- String description = String.format("Qortal-PirateChain cross-chain trade");
- String atType = "ACCT";
- String tags = "QORT-ARRR ACCT";
-
- BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, deployer.getPublicKey(), fee, null);
- TransactionData deployAtTransactionData = new DeployAtTransactionData(baseTransactionData, name, description, atType, tags, creationBytes, fundingAmount, Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = new DeployAtTransaction(repository, deployAtTransactionData);
-
- fee = deployAtTransaction.calcRecommendedFee();
- deployAtTransactionData.setFee(fee);
-
- TransactionUtils.signAndMint(repository, deployAtTransactionData, deployer);
-
- return deployAtTransaction;
- }
-
- private MessageTransaction sendMessage(Repository repository, PrivateKeyAccount sender, byte[] data, String recipient) throws DataException {
- long txTimestamp = System.currentTimeMillis();
- byte[] lastReference = sender.getLastReference();
-
- if (lastReference == null) {
- System.err.println(String.format("Qortal account %s has no last reference", sender.getAddress()));
- System.exit(2);
- }
-
- Long fee = null;
- int version = 4;
- int nonce = 0;
- long amount = 0;
- Long assetId = null; // because amount is zero
-
- BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, sender.getPublicKey(), fee, null);
- TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, version, nonce, recipient, amount, assetId, data, false, false);
-
- MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData);
-
- fee = messageTransaction.calcRecommendedFee();
- messageTransactionData.setFee(fee);
-
- TransactionUtils.signAndMint(repository, messageTransactionData, sender);
-
- return messageTransaction;
- }
-
- private void checkTradeRefund(Repository repository, Account deployer, long deployersInitialBalance, long deployAtFee) throws DataException {
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
- int refundTimeout = tradeTimeout / 2 + 1; // close enough
-
- // AT should automatically refund deployer after 'refundTimeout' blocks
- for (int blockCount = 0; blockCount <= refundTimeout; ++blockCount)
- BlockUtils.mintBlock(repository);
-
- // We don't bother to exactly calculate QORT spent running AT for several blocks, but we do know the expected range
- long expectedMinimumBalance = deployersPostDeploymentBalance;
- long expectedMaximumBalance = deployersInitialBalance - deployAtFee;
-
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
- assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
- }
-
- private void describeAt(Repository repository, String atAddress) throws DataException {
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = PirateChainACCTv3.getInstance().populateTradeData(repository, atData);
-
- Function epochMilliFormatter = (timestamp) -> LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC).format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM));
- int currentBlockHeight = repository.getBlockRepository().getBlockchainHeight();
-
- System.out.print(String.format("%s:\n"
- + "\tmode: %s\n"
- + "\tcreator: %s,\n"
- + "\tcreation timestamp: %s,\n"
- + "\tcurrent balance: %s QORT,\n"
- + "\tis finished: %b,\n"
- + "\tredeem payout: %s QORT,\n"
- + "\texpected ARRR: %s ARRR,\n"
- + "\tcurrent block height: %d,\n",
- tradeData.qortalAtAddress,
- tradeData.mode,
- tradeData.qortalCreator,
- epochMilliFormatter.apply(tradeData.creationTimestamp),
- Amounts.prettyAmount(tradeData.qortBalance),
- atData.getIsFinished(),
- Amounts.prettyAmount(tradeData.qortAmount),
- Amounts.prettyAmount(tradeData.expectedForeignAmount),
- currentBlockHeight));
-
- if (tradeData.mode != AcctMode.OFFERING && tradeData.mode != AcctMode.CANCELLED) {
- System.out.println(String.format("\trefund timeout: %d minutes,\n"
- + "\trefund height: block %d,\n"
- + "\tHASH160 of secret-A: %s,\n"
- + "\tPirate Chain P2SH-A nLockTime: %d (%s),\n"
- + "\ttrade partner: %s\n"
- + "\tpartner's receiving address: %s",
- tradeData.refundTimeout,
- tradeData.tradeRefundHeight,
- HashCode.fromBytes(tradeData.hashOfSecretA).toString().substring(0, 40),
- tradeData.lockTimeA, epochMilliFormatter.apply(tradeData.lockTimeA * 1000L),
- tradeData.qortalPartnerAddress,
- tradeData.qortalPartnerReceivingAddress));
- }
- }
-
- private PrivateKeyAccount createTradeAccount(Repository repository) {
- // We actually use a known test account with funds to avoid PoW compute
- return Common.getTestAccount(repository, "alice");
- }
-
}
diff --git a/src/test/java/org/qortal/test/crosschain/ravencoinv3/RavencoinACCTv3Tests.java b/src/test/java/org/qortal/test/crosschain/ravencoinv3/RavencoinACCTv3Tests.java
index 012d5f5d..1af0f7d6 100644
--- a/src/test/java/org/qortal/test/crosschain/ravencoinv3/RavencoinACCTv3Tests.java
+++ b/src/test/java/org/qortal/test/crosschain/ravencoinv3/RavencoinACCTv3Tests.java
@@ -1,769 +1,58 @@
package org.qortal.test.crosschain.ravencoinv3;
import com.google.common.hash.HashCode;
-import com.google.common.primitives.Bytes;
-import org.junit.Before;
-import org.junit.Test;
-import org.qortal.account.Account;
-import org.qortal.account.PrivateKeyAccount;
-import org.qortal.asset.Asset;
-import org.qortal.block.Block;
-import org.qortal.crosschain.AcctMode;
+import org.qortal.crosschain.ACCT;
import org.qortal.crosschain.RavencoinACCTv3;
-import org.qortal.crypto.Crypto;
-import org.qortal.data.at.ATData;
-import org.qortal.data.at.ATStateData;
-import org.qortal.data.crosschain.CrossChainTradeData;
-import org.qortal.data.transaction.BaseTransactionData;
-import org.qortal.data.transaction.DeployAtTransactionData;
-import org.qortal.data.transaction.MessageTransactionData;
-import org.qortal.data.transaction.TransactionData;
-import org.qortal.group.Group;
-import org.qortal.repository.DataException;
-import org.qortal.repository.Repository;
-import org.qortal.repository.RepositoryManager;
-import org.qortal.test.common.BlockUtils;
-import org.qortal.test.common.Common;
-import org.qortal.test.common.TransactionUtils;
-import org.qortal.transaction.DeployAtTransaction;
-import org.qortal.transaction.MessageTransaction;
-import org.qortal.utils.Amounts;
+import org.qortal.test.crosschain.ACCTTests;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-import java.time.format.FormatStyle;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-import java.util.function.Function;
+public class RavencoinACCTv3Tests extends ACCTTests {
-import static org.junit.Assert.*;
-
-public class RavencoinACCTv3Tests extends Common {
-
- public static final byte[] secretA = "This string is exactly 32 bytes!".getBytes();
- public static final byte[] hashOfSecretA = Crypto.hash160(secretA); // daf59884b4d1aec8c1b17102530909ee43c0151a
public static final byte[] ravencoinPublicKeyHash = HashCode.fromString("bb00bb11bb22bb33bb44bb55bb66bb77bb88bb99").asBytes();
- public static final int tradeTimeout = 20; // blocks
- public static final long redeemAmount = 80_40200000L;
- public static final long fundingAmount = 123_45600000L;
- public static final long ravencoinAmount = 864200L; // 0.00864200 RVN
+ private static final String SYMBOL = "RVN";
+ private static final String NAME = "Ravencoin";
- private static final Random RANDOM = new Random();
-
- @Before
- public void beforeTest() throws DataException {
- Common.useDefaultSettings();
+ @Override
+ protected byte[] getPublicKey() {
+ return ravencoinPublicKeyHash;
}
- @Test
- public void testCompile() {
- PrivateKeyAccount tradeAccount = createTradeAccount(null);
-
- byte[] creationBytes = RavencoinACCTv3.buildQortalAT(tradeAccount.getAddress(), ravencoinPublicKeyHash, redeemAmount, ravencoinAmount, tradeTimeout);
- assertNotNull(creationBytes);
-
- System.out.println("AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
+ @Override
+ protected byte[] buildQortalAT(String address, byte[] publicKey, long redeemAmount, long foreignAmount, int tradeTimeout) {
+ return RavencoinACCTv3.buildQortalAT(address, publicKey, redeemAmount, foreignAmount, tradeTimeout);
}
- @Test
- public void testDeploy() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
-
- long expectedBalance = deployersInitialBalance - fundingAmount - deployAtTransaction.getTransactionData().getFee();
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = fundingAmount;
- actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
-
- assertEquals("AT's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = partnersInitialBalance;
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-deployment balance incorrect", expectedBalance, actualBalance);
-
- // Test orphaning
- BlockUtils.orphanLastBlock(repository);
-
- expectedBalance = deployersInitialBalance;
- actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = 0;
- actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT);
-
- assertEquals("AT's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
-
- expectedBalance = partnersInitialBalance;
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected ACCT getInstance() {
+ return RavencoinACCTv3.getInstance();
}
- @SuppressWarnings("unused")
- @Test
- public void testOfferCancel() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- // Send creator's address to AT, instead of typical partner's address
- byte[] messageData = RavencoinACCTv3.getInstance().buildCancelMessage(deployer.getAddress());
- MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
- long messageFee = messageTransaction.getTransactionData().getFee();
-
- // AT should process 'cancel' message in next block
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in CANCELLED mode
- CrossChainTradeData tradeData = RavencoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.CANCELLED, tradeData.mode);
-
- // Check balances
- long expectedMinimumBalance = deployersPostDeploymentBalance;
- long expectedMaximumBalance = deployersInitialBalance - deployAtFee - messageFee;
-
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
- assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
-
- // Test orphaning
- BlockUtils.orphanLastBlock(repository);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance - messageFee;
- actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected int calcRefundTimeout(long partnersOfferMessageTransactionTimestamp, int lockTimeA) {
+ return RavencoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
}
- @SuppressWarnings("unused")
- @Test
- public void testOfferCancelInvalidLength() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- // Instead of sending creator's address to AT, send too-short/invalid message
- byte[] messageData = new byte[7];
- RANDOM.nextBytes(messageData);
- MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
- long messageFee = messageTransaction.getTransactionData().getFee();
-
- // AT should process 'cancel' message in next block
- // As message is too short, it will be padded to 32bytes but cancel code doesn't care about message content, so should be ok
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in CANCELLED mode
- CrossChainTradeData tradeData = RavencoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.CANCELLED, tradeData.mode);
- }
+ @Override
+ protected byte[] buildTradeMessage(String address, byte[] publicKey, byte[] hashOfSecretA, int lockTimeA, int refundTimeout) {
+ return RavencoinACCTv3.buildTradeMessage(address, publicKey, hashOfSecretA, lockTimeA, refundTimeout);
}
- @SuppressWarnings("unused")
- @Test
- public void testTradingInfoProcessing() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = RavencoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = RavencoinACCTv3.buildTradeMessage(partner.getAddress(), ravencoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- Block postDeploymentBlock = BlockUtils.mintBlock(repository);
- int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
-
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- describeAt(repository, atAddress);
-
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = RavencoinACCTv3.getInstance().populateTradeData(repository, atData);
-
- // AT should be in TRADE mode
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- // Check hashOfSecretA was extracted correctly
- assertTrue(Arrays.equals(hashOfSecretA, tradeData.hashOfSecretA));
-
- // Check trade partner Qortal address was extracted correctly
- assertEquals(partner.getAddress(), tradeData.qortalPartnerAddress);
-
- // Check trade partner's ravencoin PKH was extracted correctly
- assertTrue(Arrays.equals(ravencoinPublicKeyHash, tradeData.partnerForeignPKH));
-
- // Test orphaning
- BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance;
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected byte[] buildRedeemMessage(byte[] secretA, String address) {
+ return RavencoinACCTv3.buildRedeemMessage(secretA, address);
}
- // TEST SENDING TRADING INFO BUT NOT FROM AT CREATOR (SHOULD BE IGNORED)
- @SuppressWarnings("unused")
- @Test
- public void testIncorrectTradeSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = RavencoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT BUT NOT FROM AT CREATOR
- byte[] messageData = RavencoinACCTv3.buildTradeMessage(partner.getAddress(), ravencoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
-
- BlockUtils.mintBlock(repository);
-
- long expectedBalance = partnersInitialBalance;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-initial-payout balance incorrect", expectedBalance, actualBalance);
-
- describeAt(repository, atAddress);
-
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = RavencoinACCTv3.getInstance().populateTradeData(repository, atData);
-
- // AT should still be in OFFER mode
- assertEquals(AcctMode.OFFERING, tradeData.mode);
- }
+ @Override
+ protected byte[] getCodeBytesHash() {
+ return RavencoinACCTv3.CODE_BYTES_HASH;
}
- @SuppressWarnings("unused")
- @Test
- public void testAutomaticTradeRefund() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = RavencoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = RavencoinACCTv3.buildTradeMessage(partner.getAddress(), ravencoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- Block postDeploymentBlock = BlockUtils.mintBlock(repository);
- int postDeploymentBlockHeight = postDeploymentBlock.getBlockData().getHeight();
-
- // Check refund
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
-
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in REFUNDED mode
- CrossChainTradeData tradeData = RavencoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.REFUNDED, tradeData.mode);
-
- // Test orphaning
- BlockUtils.orphanToBlock(repository, postDeploymentBlockHeight);
-
- // Check balances
- long expectedBalance = deployersPostDeploymentBalance;
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
- }
+ @Override
+ protected String getSymbol() {
+ return SYMBOL;
}
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretCorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = RavencoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = RavencoinACCTv3.buildTradeMessage(partner.getAddress(), ravencoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, from correct account
- messageData = RavencoinACCTv3.buildRedeemMessage(secretA, partner.getAddress());
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertTrue(atData.getIsFinished());
-
- // AT should be in REDEEMED mode
- CrossChainTradeData tradeData = RavencoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.REDEEMED, tradeData.mode);
-
- // Check balances
- long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee() + redeemAmount;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-redeem balance incorrect", expectedBalance, actualBalance);
-
- // Orphan redeem
- BlockUtils.orphanLastBlock(repository);
-
- // Check balances
- expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
- actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's post-orphan/pre-redeem balance incorrect", expectedBalance, actualBalance);
-
- // Check AT state
- ATStateData postOrphanAtStateData = repository.getATRepository().getLatestATState(atAddress);
-
- assertTrue("AT states mismatch", Arrays.equals(preRedeemAtStateData.getStateData(), postOrphanAtStateData.getStateData()));
- }
+ @Override
+ protected String getName() {
+ return NAME;
}
-
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretIncorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
-
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = RavencoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = RavencoinACCTv3.buildTradeMessage(partner.getAddress(), ravencoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, but from wrong account
- messageData = RavencoinACCTv3.buildRedeemMessage(secretA, partner.getAddress());
- messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should still be in TRADE mode
- CrossChainTradeData tradeData = RavencoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- // Check balances
- long expectedBalance = partnersInitialBalance;
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
-
- // Check eventual refund
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testIncorrectSecretCorrectSender() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- long deployAtFee = deployAtTransaction.getTransactionData().getFee();
-
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = RavencoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = RavencoinACCTv3.buildTradeMessage(partner.getAddress(), ravencoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send incorrect secret to AT, from correct account
- byte[] wrongSecret = new byte[32];
- RANDOM.nextBytes(wrongSecret);
- messageData = RavencoinACCTv3.buildRedeemMessage(wrongSecret, partner.getAddress());
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should still be in TRADE mode
- CrossChainTradeData tradeData = RavencoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
-
- long expectedBalance = partnersInitialBalance - messageTransaction.getTransactionData().getFee();
- long actualBalance = partner.getConfirmedBalance(Asset.QORT);
-
- assertEquals("Partner's balance incorrect", expectedBalance, actualBalance);
-
- // Check eventual refund
- checkTradeRefund(repository, deployer, deployersInitialBalance, deployAtFee);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testCorrectSecretCorrectSenderInvalidMessageLength() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
- Account at = deployAtTransaction.getATAccount();
- String atAddress = at.getAddress();
-
- long partnersOfferMessageTransactionTimestamp = System.currentTimeMillis();
- int lockTimeA = calcTestLockTimeA(partnersOfferMessageTransactionTimestamp);
- int refundTimeout = RavencoinACCTv3.calcRefundTimeout(partnersOfferMessageTransactionTimestamp, lockTimeA);
-
- // Send trade info to AT
- byte[] messageData = RavencoinACCTv3.buildTradeMessage(partner.getAddress(), ravencoinPublicKeyHash, hashOfSecretA, lockTimeA, refundTimeout);
- MessageTransaction messageTransaction = sendMessage(repository, tradeAccount, messageData, atAddress);
-
- // Give AT time to process message
- BlockUtils.mintBlock(repository);
-
- // Send correct secret to AT, from correct account, but missing receive address, hence incorrect length
- messageData = Bytes.concat(secretA);
- messageTransaction = sendMessage(repository, partner, messageData, atAddress);
-
- // AT should NOT send funds in the next block
- ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
- BlockUtils.mintBlock(repository);
-
- describeAt(repository, atAddress);
-
- // Check AT is NOT finished
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- assertFalse(atData.getIsFinished());
-
- // AT should be in TRADING mode
- CrossChainTradeData tradeData = RavencoinACCTv3.getInstance().populateTradeData(repository, atData);
- assertEquals(AcctMode.TRADING, tradeData.mode);
- }
- }
-
- @SuppressWarnings("unused")
- @Test
- public void testDescribeDeployed() throws DataException {
- try (final Repository repository = RepositoryManager.getRepository()) {
- PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
- PrivateKeyAccount tradeAccount = createTradeAccount(repository);
-
- PrivateKeyAccount partner = Common.getTestAccount(repository, "dilbert");
-
- long deployersInitialBalance = deployer.getConfirmedBalance(Asset.QORT);
- long partnersInitialBalance = partner.getConfirmedBalance(Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
-
- List executableAts = repository.getATRepository().getAllExecutableATs();
-
- for (ATData atData : executableAts) {
- String atAddress = atData.getATAddress();
- byte[] codeBytes = atData.getCodeBytes();
- byte[] codeHash = Crypto.digest(codeBytes);
-
- System.out.println(String.format("%s: code length: %d byte%s, code hash: %s",
- atAddress,
- codeBytes.length,
- (codeBytes.length != 1 ? "s": ""),
- HashCode.fromBytes(codeHash)));
-
- // Not one of ours?
- if (!Arrays.equals(codeHash, RavencoinACCTv3.CODE_BYTES_HASH))
- continue;
-
- describeAt(repository, atAddress);
- }
- }
- }
-
- private int calcTestLockTimeA(long messageTimestamp) {
- return (int) (messageTimestamp / 1000L + tradeTimeout * 60);
- }
-
- private DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer, String tradeAddress) throws DataException {
- byte[] creationBytes = RavencoinACCTv3.buildQortalAT(tradeAddress, ravencoinPublicKeyHash, redeemAmount, ravencoinAmount, tradeTimeout);
-
- long txTimestamp = System.currentTimeMillis();
- byte[] lastReference = deployer.getLastReference();
-
- if (lastReference == null) {
- System.err.println(String.format("Qortal account %s has no last reference", deployer.getAddress()));
- System.exit(2);
- }
-
- Long fee = null;
- String name = "QORT-RVN cross-chain trade";
- String description = String.format("Qortal-Ravencoin cross-chain trade");
- String atType = "ACCT";
- String tags = "QORT-RVN ACCT";
-
- BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, deployer.getPublicKey(), fee, null);
- TransactionData deployAtTransactionData = new DeployAtTransactionData(baseTransactionData, name, description, atType, tags, creationBytes, fundingAmount, Asset.QORT);
-
- DeployAtTransaction deployAtTransaction = new DeployAtTransaction(repository, deployAtTransactionData);
-
- fee = deployAtTransaction.calcRecommendedFee();
- deployAtTransactionData.setFee(fee);
-
- TransactionUtils.signAndMint(repository, deployAtTransactionData, deployer);
-
- return deployAtTransaction;
- }
-
- private MessageTransaction sendMessage(Repository repository, PrivateKeyAccount sender, byte[] data, String recipient) throws DataException {
- long txTimestamp = System.currentTimeMillis();
- byte[] lastReference = sender.getLastReference();
-
- if (lastReference == null) {
- System.err.println(String.format("Qortal account %s has no last reference", sender.getAddress()));
- System.exit(2);
- }
-
- Long fee = null;
- int version = 4;
- int nonce = 0;
- long amount = 0;
- Long assetId = null; // because amount is zero
-
- BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, sender.getPublicKey(), fee, null);
- TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, version, nonce, recipient, amount, assetId, data, false, false);
-
- MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData);
-
- fee = messageTransaction.calcRecommendedFee();
- messageTransactionData.setFee(fee);
-
- TransactionUtils.signAndMint(repository, messageTransactionData, sender);
-
- return messageTransaction;
- }
-
- private void checkTradeRefund(Repository repository, Account deployer, long deployersInitialBalance, long deployAtFee) throws DataException {
- long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
- int refundTimeout = tradeTimeout / 2 + 1; // close enough
-
- // AT should automatically refund deployer after 'refundTimeout' blocks
- for (int blockCount = 0; blockCount <= refundTimeout; ++blockCount)
- BlockUtils.mintBlock(repository);
-
- // We don't bother to exactly calculate QORT spent running AT for several blocks, but we do know the expected range
- long expectedMinimumBalance = deployersPostDeploymentBalance;
- long expectedMaximumBalance = deployersInitialBalance - deployAtFee;
-
- long actualBalance = deployer.getConfirmedBalance(Asset.QORT);
-
- assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance, expectedMinimumBalance), actualBalance > expectedMinimumBalance);
- assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance, expectedMaximumBalance), actualBalance < expectedMaximumBalance);
- }
-
- private void describeAt(Repository repository, String atAddress) throws DataException {
- ATData atData = repository.getATRepository().fromATAddress(atAddress);
- CrossChainTradeData tradeData = RavencoinACCTv3.getInstance().populateTradeData(repository, atData);
-
- Function epochMilliFormatter = (timestamp) -> LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC).format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM));
- int currentBlockHeight = repository.getBlockRepository().getBlockchainHeight();
-
- System.out.print(String.format("%s:\n"
- + "\tmode: %s\n"
- + "\tcreator: %s,\n"
- + "\tcreation timestamp: %s,\n"
- + "\tcurrent balance: %s QORT,\n"
- + "\tis finished: %b,\n"
- + "\tredeem payout: %s QORT,\n"
- + "\texpected ravencoin: %s RVN,\n"
- + "\tcurrent block height: %d,\n",
- tradeData.qortalAtAddress,
- tradeData.mode,
- tradeData.qortalCreator,
- epochMilliFormatter.apply(tradeData.creationTimestamp),
- Amounts.prettyAmount(tradeData.qortBalance),
- atData.getIsFinished(),
- Amounts.prettyAmount(tradeData.qortAmount),
- Amounts.prettyAmount(tradeData.expectedForeignAmount),
- currentBlockHeight));
-
- if (tradeData.mode != AcctMode.OFFERING && tradeData.mode != AcctMode.CANCELLED) {
- System.out.println(String.format("\trefund timeout: %d minutes,\n"
- + "\trefund height: block %d,\n"
- + "\tHASH160 of secret-A: %s,\n"
- + "\tRavencoin P2SH-A nLockTime: %d (%s),\n"
- + "\ttrade partner: %s\n"
- + "\tpartner's receiving address: %s",
- tradeData.refundTimeout,
- tradeData.tradeRefundHeight,
- HashCode.fromBytes(tradeData.hashOfSecretA).toString().substring(0, 40),
- tradeData.lockTimeA, epochMilliFormatter.apply(tradeData.lockTimeA * 1000L),
- tradeData.qortalPartnerAddress,
- tradeData.qortalPartnerReceivingAddress));
- }
- }
-
- private PrivateKeyAccount createTradeAccount(Repository repository) {
- // We actually use a known test account with funds to avoid PoW compute
- return Common.getTestAccount(repository, "alice");
- }
-
}
diff --git a/src/test/java/org/qortal/test/naming/BuySellTests.java b/src/test/java/org/qortal/test/naming/BuySellTests.java
index 4530820e..f0e97b94 100644
--- a/src/test/java/org/qortal/test/naming/BuySellTests.java
+++ b/src/test/java/org/qortal/test/naming/BuySellTests.java
@@ -44,7 +44,7 @@ public class BuySellTests extends Common {
bob = Common.getTestAccount(repository, "bob");
name = "test name" + " " + random.nextInt(1_000_000);
- price = random.nextInt(1000) * Amounts.MULTIPLIER;
+ price = (random.nextInt(1000) + 1) * Amounts.MULTIPLIER;
}
@After
diff --git a/src/test/java/org/qortal/test/naming/MiscTests.java b/src/test/java/org/qortal/test/naming/MiscTests.java
index 2bcd098d..401b03b9 100644
--- a/src/test/java/org/qortal/test/naming/MiscTests.java
+++ b/src/test/java/org/qortal/test/naming/MiscTests.java
@@ -20,6 +20,7 @@ import org.qortal.repository.Repository;
import org.qortal.repository.RepositoryManager;
import org.qortal.test.common.*;
import org.qortal.test.common.transaction.TestTransaction;
+import org.qortal.transaction.PaymentTransaction;
import org.qortal.transaction.RegisterNameTransaction;
import org.qortal.transaction.Transaction;
import org.qortal.transaction.Transaction.ValidationResult;
@@ -329,15 +330,19 @@ public class MiscTests extends Common {
public void testRegisterNameFeeIncrease() throws Exception {
try (final Repository repository = RepositoryManager.getRepository()) {
- // Set nameRegistrationUnitFeeTimestamp to a time far in the future
+ // Add original fee to nameRegistrationUnitFees
+ UnitFeesByTimestamp originalFee = new UnitFeesByTimestamp();
+ originalFee.timestamp = 0L;
+ originalFee.fee = new AmountTypeAdapter().unmarshal("0.1");
+
+ // Add a time far in the future to nameRegistrationUnitFees
UnitFeesByTimestamp futureFeeIncrease = new UnitFeesByTimestamp();
futureFeeIncrease.timestamp = 9999999999999L; // 20 Nov 2286
futureFeeIncrease.fee = new AmountTypeAdapter().unmarshal("5");
- FieldUtils.writeField(BlockChain.getInstance(), "nameRegistrationUnitFees", Arrays.asList(futureFeeIncrease), true);
+ FieldUtils.writeField(BlockChain.getInstance(), "nameRegistrationUnitFees", Arrays.asList(originalFee, futureFeeIncrease), true);
assertEquals(futureFeeIncrease.fee, BlockChain.getInstance().getNameRegistrationUnitFeeAtTimestamp(futureFeeIncrease.timestamp));
// Validate unit fees pre and post timestamp
- assertEquals(10000000, BlockChain.getInstance().getUnitFee()); // 0.1 QORT
assertEquals(10000000, BlockChain.getInstance().getNameRegistrationUnitFeeAtTimestamp(futureFeeIncrease.timestamp - 1)); // 0.1 QORT
assertEquals(500000000, BlockChain.getInstance().getNameRegistrationUnitFeeAtTimestamp(futureFeeIncrease.timestamp)); // 5 QORT
@@ -362,7 +367,7 @@ public class MiscTests extends Common {
futureFeeIncrease.timestamp = now + (60 * 60 * 1000L); // 1 hour in the future
futureFeeIncrease.fee = new AmountTypeAdapter().unmarshal("10");
- FieldUtils.writeField(BlockChain.getInstance(), "nameRegistrationUnitFees", Arrays.asList(pastFeeIncrease, futureFeeIncrease), true);
+ FieldUtils.writeField(BlockChain.getInstance(), "nameRegistrationUnitFees", Arrays.asList(originalFee, pastFeeIncrease, futureFeeIncrease), true);
assertEquals(pastFeeIncrease.fee, BlockChain.getInstance().getNameRegistrationUnitFeeAtTimestamp(pastFeeIncrease.timestamp));
assertEquals(futureFeeIncrease.fee, BlockChain.getInstance().getNameRegistrationUnitFeeAtTimestamp(futureFeeIncrease.timestamp));
@@ -387,4 +392,124 @@ public class MiscTests extends Common {
}
}
+ // test reading the name registration fee schedule from blockchain.json / test-chain-v2.json
+ @Test
+ public void testRegisterNameFeeScheduleInTestchainData() throws Exception {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+
+ final long expectedFutureFeeIncreaseTimestamp = 9999999999999L; // 20 Nov 2286, as per test-chain-v2.json
+ final long expectedFutureFeeIncreaseValue = new AmountTypeAdapter().unmarshal("5");
+
+ assertEquals(expectedFutureFeeIncreaseValue, BlockChain.getInstance().getNameRegistrationUnitFeeAtTimestamp(expectedFutureFeeIncreaseTimestamp));
+
+ // Validate unit fees pre and post timestamp
+ assertEquals(10000000, BlockChain.getInstance().getNameRegistrationUnitFeeAtTimestamp(expectedFutureFeeIncreaseTimestamp - 1)); // 0.1 QORT
+ assertEquals(500000000, BlockChain.getInstance().getNameRegistrationUnitFeeAtTimestamp(expectedFutureFeeIncreaseTimestamp)); // 5 QORT
+
+ // Register-name
+ PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
+ String name = "test-name";
+ String data = "{\"age\":30}";
+
+ RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
+ transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(transactionData.getTimestamp()));
+ assertEquals(10000000L, transactionData.getFee().longValue());
+ TransactionUtils.signAndMint(repository, transactionData, alice);
+ }
+ }
+
+
+
+ // test general unit fee increase
+ @Test
+ public void testUnitFeeIncrease() throws Exception {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+
+ // Add original fee to unitFees
+ UnitFeesByTimestamp originalFee = new UnitFeesByTimestamp();
+ originalFee.timestamp = 0L;
+ originalFee.fee = new AmountTypeAdapter().unmarshal("0.1");
+
+ // Add a time far in the future to unitFees
+ UnitFeesByTimestamp futureFeeIncrease = new UnitFeesByTimestamp();
+ futureFeeIncrease.timestamp = 9999999999999L; // 20 Nov 2286
+ futureFeeIncrease.fee = new AmountTypeAdapter().unmarshal("1");
+ FieldUtils.writeField(BlockChain.getInstance(), "unitFees", Arrays.asList(originalFee, futureFeeIncrease), true);
+ assertEquals(futureFeeIncrease.fee, BlockChain.getInstance().getUnitFeeAtTimestamp(futureFeeIncrease.timestamp));
+
+ // Validate unit fees pre and post timestamp
+ assertEquals(10000000, BlockChain.getInstance().getUnitFeeAtTimestamp(futureFeeIncrease.timestamp - 1)); // 0.1 QORT
+ assertEquals(100000000, BlockChain.getInstance().getUnitFeeAtTimestamp(futureFeeIncrease.timestamp)); // 1 QORT
+
+ // Payment
+ PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
+ PrivateKeyAccount bob = Common.getTestAccount(repository, "bob");
+ PrivateKeyAccount chloe = Common.getTestAccount(repository, "chloe");
+
+ PaymentTransactionData transactionData = new PaymentTransactionData(TestTransaction.generateBase(alice), bob.getAddress(), 100000);
+ transactionData.setFee(new PaymentTransaction(null, null).getUnitFee(transactionData.getTimestamp()));
+ assertEquals(10000000L, transactionData.getFee().longValue());
+ TransactionUtils.signAndMint(repository, transactionData, alice);
+
+ // Set fee increase to a time in the past
+ Long now = NTP.getTime();
+ UnitFeesByTimestamp pastFeeIncrease = new UnitFeesByTimestamp();
+ pastFeeIncrease.timestamp = now - 1000L; // 1 second ago
+ pastFeeIncrease.fee = new AmountTypeAdapter().unmarshal("3");
+
+ // Set another increase in the future
+ futureFeeIncrease = new UnitFeesByTimestamp();
+ futureFeeIncrease.timestamp = now + (60 * 60 * 1000L); // 1 hour in the future
+ futureFeeIncrease.fee = new AmountTypeAdapter().unmarshal("10");
+
+ FieldUtils.writeField(BlockChain.getInstance(), "unitFees", Arrays.asList(originalFee, pastFeeIncrease, futureFeeIncrease), true);
+ assertEquals(originalFee.fee, BlockChain.getInstance().getUnitFeeAtTimestamp(originalFee.timestamp));
+ assertEquals(pastFeeIncrease.fee, BlockChain.getInstance().getUnitFeeAtTimestamp(pastFeeIncrease.timestamp));
+ assertEquals(futureFeeIncrease.fee, BlockChain.getInstance().getUnitFeeAtTimestamp(futureFeeIncrease.timestamp));
+
+ // Send another payment transaction
+ // Fee should be determined automatically
+ transactionData = new PaymentTransactionData(TestTransaction.generateBase(alice), bob.getAddress(), 50000);
+ assertEquals(300000000L, transactionData.getFee().longValue());
+ Transaction transaction = Transaction.fromData(repository, transactionData);
+ transaction.sign(alice);
+ ValidationResult result = transaction.importAsUnconfirmed();
+ assertEquals("Transaction should be valid", ValidationResult.OK, result);
+
+ // Now try fetching and setting fee manually
+ transactionData = new PaymentTransactionData(TestTransaction.generateBase(alice), chloe.getAddress(), 50000);
+ transactionData.setFee(new PaymentTransaction(null, null).getUnitFee(transactionData.getTimestamp()));
+ assertEquals(300000000L, transactionData.getFee().longValue());
+ transaction = Transaction.fromData(repository, transactionData);
+ transaction.sign(alice);
+ result = transaction.importAsUnconfirmed();
+ assertEquals("Transaction should be valid", ValidationResult.OK, result);
+ }
+ }
+
+ // test reading the fee schedule from blockchain.json / test-chain-v2.json
+ @Test
+ public void testFeeScheduleInTestchainData() throws Exception {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+
+ final long expectedFutureFeeIncreaseTimestamp = 9999999999999L; // 20 Nov 2286, as per test-chain-v2.json
+ final long expectedFutureFeeIncreaseValue = new AmountTypeAdapter().unmarshal("1");
+
+ assertEquals(expectedFutureFeeIncreaseValue, BlockChain.getInstance().getUnitFeeAtTimestamp(expectedFutureFeeIncreaseTimestamp));
+
+ // Validate unit fees pre and post timestamp
+ assertEquals(10000000, BlockChain.getInstance().getUnitFeeAtTimestamp(expectedFutureFeeIncreaseTimestamp - 1)); // 0.1 QORT
+ assertEquals(100000000, BlockChain.getInstance().getUnitFeeAtTimestamp(expectedFutureFeeIncreaseTimestamp)); // 1 QORT
+
+ // Payment
+ PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
+ PrivateKeyAccount bob = Common.getTestAccount(repository, "bob");
+
+ PaymentTransactionData transactionData = new PaymentTransactionData(TestTransaction.generateBase(alice), bob.getAddress(), 100000);
+ transactionData.setFee(new PaymentTransaction(null, null).getUnitFee(transactionData.getTimestamp()));
+ assertEquals(10000000L, transactionData.getFee().longValue());
+ TransactionUtils.signAndMint(repository, transactionData, alice);
+ }
+ }
+
}
diff --git a/src/test/resources/test-chain-v2-block-timestamps.json b/src/test/resources/test-chain-v2-block-timestamps.json
index 3b4de702..7059e035 100644
--- a/src/test/resources/test-chain-v2-block-timestamps.json
+++ b/src/test/resources/test-chain-v2-block-timestamps.json
@@ -4,8 +4,11 @@
"transactionExpiryPeriod": 86400000,
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
- "unitFee": "0.1",
+ "unitFees": [
+ { "timestamp": 0, "fee": "0.1" }
+ ],
"nameRegistrationUnitFees": [
+ { "timestamp": 0, "fee": "0.1" },
{ "timestamp": 1645372800000, "fee": "5" }
],
"requireGroupForApproval": false,
@@ -15,6 +18,7 @@
"onlineAccountSignaturesMinLifetime": 3600000,
"onlineAccountSignaturesMaxLifetime": 86400000,
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
+ "mempowTransactionUpdatesTimestamp": 0,
"rewardsByHeight": [
{ "height": 1, "reward": 100 },
{ "height": 11, "reward": 10 },
diff --git a/src/test/resources/test-chain-v2-disable-reference.json b/src/test/resources/test-chain-v2-disable-reference.json
index c93fbb78..1016bc17 100644
--- a/src/test/resources/test-chain-v2-disable-reference.json
+++ b/src/test/resources/test-chain-v2-disable-reference.json
@@ -4,8 +4,11 @@
"transactionExpiryPeriod": 86400000,
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
- "unitFee": "0.1",
+ "unitFees": [
+ { "timestamp": 0, "fee": "0.1" }
+ ],
"nameRegistrationUnitFees": [
+ { "timestamp": 0, "fee": "0.1" },
{ "timestamp": 1645372800000, "fee": "5" }
],
"requireGroupForApproval": false,
@@ -19,6 +22,7 @@
"onlineAccountSignaturesMinLifetime": 3600000,
"onlineAccountSignaturesMaxLifetime": 86400000,
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
+ "mempowTransactionUpdatesTimestamp": 0,
"rewardsByHeight": [
{ "height": 1, "reward": 100 },
{ "height": 11, "reward": 10 },
diff --git a/src/test/resources/test-chain-v2-founder-rewards.json b/src/test/resources/test-chain-v2-founder-rewards.json
index 1b068932..5f29bc97 100644
--- a/src/test/resources/test-chain-v2-founder-rewards.json
+++ b/src/test/resources/test-chain-v2-founder-rewards.json
@@ -4,8 +4,11 @@
"transactionExpiryPeriod": 86400000,
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
- "unitFee": "0.1",
+ "unitFees": [
+ { "timestamp": 0, "fee": "0.1" }
+ ],
"nameRegistrationUnitFees": [
+ { "timestamp": 0, "fee": "0.1" },
{ "timestamp": 1645372800000, "fee": "5" }
],
"requireGroupForApproval": false,
@@ -20,6 +23,7 @@
"onlineAccountSignaturesMaxLifetime": 86400000,
"onlineAccountsModulusV2Timestamp": 9999999999999,
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
+ "mempowTransactionUpdatesTimestamp": 0,
"rewardsByHeight": [
{ "height": 1, "reward": 100 },
{ "height": 11, "reward": 10 },
diff --git a/src/test/resources/test-chain-v2-leftover-reward.json b/src/test/resources/test-chain-v2-leftover-reward.json
index aef76cc2..86f2def1 100644
--- a/src/test/resources/test-chain-v2-leftover-reward.json
+++ b/src/test/resources/test-chain-v2-leftover-reward.json
@@ -4,8 +4,11 @@
"transactionExpiryPeriod": 86400000,
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
- "unitFee": "0.1",
+ "unitFees": [
+ { "timestamp": 0, "fee": "0.1" }
+ ],
"nameRegistrationUnitFees": [
+ { "timestamp": 0, "fee": "0.1" },
{ "timestamp": 1645372800000, "fee": "5" }
],
"requireGroupForApproval": false,
@@ -20,6 +23,7 @@
"onlineAccountSignaturesMaxLifetime": 86400000,
"onlineAccountsModulusV2Timestamp": 9999999999999,
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
+ "mempowTransactionUpdatesTimestamp": 0,
"rewardsByHeight": [
{ "height": 1, "reward": 100 },
{ "height": 11, "reward": 10 },
diff --git a/src/test/resources/test-chain-v2-minting.json b/src/test/resources/test-chain-v2-minting.json
index db6d8a0b..b2da6489 100644
--- a/src/test/resources/test-chain-v2-minting.json
+++ b/src/test/resources/test-chain-v2-minting.json
@@ -4,8 +4,11 @@
"transactionExpiryPeriod": 86400000,
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
- "unitFee": "0.1",
+ "unitFees": [
+ { "timestamp": 0, "fee": "0.1" }
+ ],
"nameRegistrationUnitFees": [
+ { "timestamp": 0, "fee": "0.1" },
{ "timestamp": 1645372800000, "fee": "5" }
],
"requireGroupForApproval": false,
@@ -20,6 +23,7 @@
"onlineAccountSignaturesMaxLifetime": 86400000,
"onlineAccountsModulusV2Timestamp": 9999999999999,
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
+ "mempowTransactionUpdatesTimestamp": 9999999999999,
"rewardsByHeight": [
{ "height": 1, "reward": 100 },
{ "height": 11, "reward": 10 },
diff --git a/src/test/resources/test-chain-v2-qora-holder-extremes.json b/src/test/resources/test-chain-v2-qora-holder-extremes.json
index 2452d4d2..2933a63d 100644
--- a/src/test/resources/test-chain-v2-qora-holder-extremes.json
+++ b/src/test/resources/test-chain-v2-qora-holder-extremes.json
@@ -4,8 +4,11 @@
"transactionExpiryPeriod": 86400000,
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
- "unitFee": "0.1",
+ "unitFees": [
+ { "timestamp": 0, "fee": "0.1" }
+ ],
"nameRegistrationUnitFees": [
+ { "timestamp": 0, "fee": "0.1" },
{ "timestamp": 1645372800000, "fee": "5" }
],
"requireGroupForApproval": false,
@@ -20,6 +23,7 @@
"onlineAccountSignaturesMaxLifetime": 86400000,
"onlineAccountsModulusV2Timestamp": 9999999999999,
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
+ "mempowTransactionUpdatesTimestamp": 0,
"rewardsByHeight": [
{ "height": 1, "reward": 100 },
{ "height": 11, "reward": 10 },
diff --git a/src/test/resources/test-chain-v2-qora-holder-reduction.json b/src/test/resources/test-chain-v2-qora-holder-reduction.json
index 23193729..40e40673 100644
--- a/src/test/resources/test-chain-v2-qora-holder-reduction.json
+++ b/src/test/resources/test-chain-v2-qora-holder-reduction.json
@@ -4,8 +4,11 @@
"transactionExpiryPeriod": 86400000,
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
- "unitFee": "0.1",
+ "unitFees": [
+ { "timestamp": 0, "fee": "0.1" }
+ ],
"nameRegistrationUnitFees": [
+ { "timestamp": 0, "fee": "0.1" },
{ "timestamp": 1645372800000, "fee": "5" }
],
"requireGroupForApproval": false,
@@ -20,6 +23,7 @@
"onlineAccountSignaturesMaxLifetime": 86400000,
"onlineAccountsModulusV2Timestamp": 9999999999999,
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
+ "mempowTransactionUpdatesTimestamp": 0,
"rewardsByHeight": [
{ "height": 1, "reward": 100 },
{ "height": 11, "reward": 10 },
diff --git a/src/test/resources/test-chain-v2-qora-holder.json b/src/test/resources/test-chain-v2-qora-holder.json
index 9d81632b..8ceafe63 100644
--- a/src/test/resources/test-chain-v2-qora-holder.json
+++ b/src/test/resources/test-chain-v2-qora-holder.json
@@ -4,8 +4,11 @@
"transactionExpiryPeriod": 86400000,
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
- "unitFee": "0.1",
+ "unitFees": [
+ { "timestamp": 0, "fee": "0.1" }
+ ],
"nameRegistrationUnitFees": [
+ { "timestamp": 0, "fee": "0.1" },
{ "timestamp": 1645372800000, "fee": "5" }
],
"requireGroupForApproval": false,
@@ -20,6 +23,7 @@
"onlineAccountSignaturesMaxLifetime": 86400000,
"onlineAccountsModulusV2Timestamp": 9999999999999,
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
+ "mempowTransactionUpdatesTimestamp": 0,
"rewardsByHeight": [
{ "height": 1, "reward": 100 },
{ "height": 11, "reward": 10 },
diff --git a/src/test/resources/test-chain-v2-reward-levels.json b/src/test/resources/test-chain-v2-reward-levels.json
index 81609595..68a79ed4 100644
--- a/src/test/resources/test-chain-v2-reward-levels.json
+++ b/src/test/resources/test-chain-v2-reward-levels.json
@@ -4,8 +4,11 @@
"transactionExpiryPeriod": 86400000,
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
- "unitFee": "0.1",
+ "unitFees": [
+ { "timestamp": 0, "fee": "0.1" }
+ ],
"nameRegistrationUnitFees": [
+ { "timestamp": 0, "fee": "0.1" },
{ "timestamp": 1645372800000, "fee": "5" }
],
"requireGroupForApproval": false,
@@ -20,6 +23,7 @@
"onlineAccountSignaturesMaxLifetime": 86400000,
"onlineAccountsModulusV2Timestamp": 9999999999999,
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
+ "mempowTransactionUpdatesTimestamp": 0,
"rewardsByHeight": [
{ "height": 1, "reward": 100 },
{ "height": 11, "reward": 10 },
diff --git a/src/test/resources/test-chain-v2-reward-scaling.json b/src/test/resources/test-chain-v2-reward-scaling.json
index 21a5b7a7..cc02a73e 100644
--- a/src/test/resources/test-chain-v2-reward-scaling.json
+++ b/src/test/resources/test-chain-v2-reward-scaling.json
@@ -4,8 +4,11 @@
"transactionExpiryPeriod": 86400000,
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
- "unitFee": "0.1",
+ "unitFees": [
+ { "timestamp": 0, "fee": "0.1" }
+ ],
"nameRegistrationUnitFees": [
+ { "timestamp": 0, "fee": "0.1" },
{ "timestamp": 1645372800000, "fee": "5" }
],
"requireGroupForApproval": false,
@@ -20,6 +23,7 @@
"onlineAccountSignaturesMaxLifetime": 86400000,
"onlineAccountsModulusV2Timestamp": 9999999999999,
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
+ "mempowTransactionUpdatesTimestamp": 0,
"rewardsByHeight": [
{ "height": 1, "reward": 100 },
{ "height": 11, "reward": 10 },
diff --git a/src/test/resources/test-chain-v2-reward-shares.json b/src/test/resources/test-chain-v2-reward-shares.json
index 6119ac48..5c508188 100644
--- a/src/test/resources/test-chain-v2-reward-shares.json
+++ b/src/test/resources/test-chain-v2-reward-shares.json
@@ -4,8 +4,11 @@
"transactionExpiryPeriod": 86400000,
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
- "unitFee": "0.1",
+ "unitFees": [
+ { "timestamp": 0, "fee": "0.1" }
+ ],
"nameRegistrationUnitFees": [
+ { "timestamp": 0, "fee": "0.1" },
{ "timestamp": 1645372800000, "fee": "5" }
],
"requireGroupForApproval": false,
@@ -19,6 +22,7 @@
"onlineAccountSignaturesMinLifetime": 3600000,
"onlineAccountSignaturesMaxLifetime": 86400000,
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
+ "mempowTransactionUpdatesTimestamp": 0,
"rewardsByHeight": [
{ "height": 1, "reward": 100 },
{ "height": 11, "reward": 10 },
diff --git a/src/test/resources/test-chain-v2-self-sponsorship-algo.json b/src/test/resources/test-chain-v2-self-sponsorship-algo.json
index dc5f3961..244d2491 100644
--- a/src/test/resources/test-chain-v2-self-sponsorship-algo.json
+++ b/src/test/resources/test-chain-v2-self-sponsorship-algo.json
@@ -4,8 +4,11 @@
"transactionExpiryPeriod": 86400000,
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 0,
- "unitFee": "0.00000001",
+ "unitFees": [
+ { "timestamp": 0, "fee": "0.00000001" }
+ ],
"nameRegistrationUnitFees": [
+ { "timestamp": 0, "fee": "0.00000001" },
{ "timestamp": 1645372800000, "fee": "5" }
],
"requireGroupForApproval": false,
@@ -20,6 +23,7 @@
"onlineAccountSignaturesMaxLifetime": 86400000,
"onlineAccountsModulusV2Timestamp": 9999999999999,
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
+ "mempowTransactionUpdatesTimestamp": 0,
"rewardsByHeight": [
{ "height": 1, "reward": 100 },
{ "height": 11, "reward": 10 },
diff --git a/src/test/resources/test-chain-v2.json b/src/test/resources/test-chain-v2.json
index d0c460df..9168a0de 100644
--- a/src/test/resources/test-chain-v2.json
+++ b/src/test/resources/test-chain-v2.json
@@ -4,9 +4,13 @@
"transactionExpiryPeriod": 86400000,
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
- "unitFee": "0.1",
+ "unitFees": [
+ { "timestamp": 0, "fee": "0.1" },
+ { "timestamp": 9999999999999, "fee": "1" }
+ ],
"nameRegistrationUnitFees": [
- { "timestamp": 1645372800000, "fee": "5" }
+ { "timestamp": 0, "fee": "0.1" },
+ { "timestamp": 9999999999999, "fee": "5" }
],
"requireGroupForApproval": false,
"minAccountLevelToRewardShare": 5,
@@ -20,6 +24,7 @@
"onlineAccountSignaturesMaxLifetime": 86400000,
"onlineAccountsModulusV2Timestamp": 9999999999999,
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
+ "mempowTransactionUpdatesTimestamp": 0,
"rewardsByHeight": [
{ "height": 1, "reward": 100 },
{ "height": 11, "reward": 10 },
diff --git a/testnet/testchain.json b/testnet/testchain.json
index aef9ed9a..089bd693 100644
--- a/testnet/testchain.json
+++ b/testnet/testchain.json
@@ -4,7 +4,9 @@
"transactionExpiryPeriod": 86400000,
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
- "unitFee": "0.001",
+ "unitFees": [
+ { "timestamp": 0, "fee": "0.001" }
+ ],
"nameRegistrationUnitFees": [
{ "timestamp": 0, "fee": "1.25" }
],
@@ -24,6 +26,7 @@
"onlineAccountSignaturesMaxLifetime": 86400000,
"onlineAccountsModulusV2Timestamp": 0,
"selfSponsorshipAlgoV1SnapshotTimestamp": 9999999999999,
+ "mempowTransactionUpdatesTimestamp": 1692554400000,
"rewardsByHeight": [
{ "height": 1, "reward": 5.00 },
{ "height": 259201, "reward": 4.75 },
diff --git a/tools/approve-auto-update.sh b/tools/approve-auto-update.sh
index cbfa280d..c34b6de7 100755
--- a/tools/approve-auto-update.sh
+++ b/tools/approve-auto-update.sh
@@ -50,7 +50,7 @@ tx_json=$( cat < 2;
-
-my $port = $opt{p} || 12391;
-my $privkey = shift @ARGV;
-my $commit_hash = shift @ARGV;
-
-my $git_dir = `git rev-parse --show-toplevel`;
-die("Cannot determine git top level dir\n") unless $git_dir;
-
-chomp $git_dir;
-chdir($git_dir) || die("Can't change directory to $git_dir: $!\n");
-
-open(POM, '<', 'pom.xml') || die ("Can't open 'pom.xml': $!\n");
-my $project;
-while () {
- if (m/(\w+)<.artifactId>/o) {
- $project = $1;
- last;
- }
-}
-close(POM);
-
-my $apikey = read_file('apikey.txt');
-
-# Do we need to determine commit hash?
-unless ($commit_hash) {
- # determine git branch
- my $branch_name = ` git symbolic-ref -q HEAD `;
- chomp $branch_name;
- $branch_name =~ s|^refs/heads/||; # ${branch_name##refs/heads/}
-
- # short-form commit hash on base branch (non-auto-update)
- $commit_hash ||= `git show --no-patch --format=%h`;
- die("Can't find commit hash\n") if ! defined $commit_hash;
- chomp $commit_hash;
- printf "Commit hash on '%s' branch: %s\n", $branch_name, $commit_hash;
-} else {
- printf "Using given commit hash: %s\n", $commit_hash;
-}
-
-# build timestamp / commit timestamp on base branch
-my $timestamp = `git show --no-patch --format=%ct ${commit_hash}`;
-die("Can't determine commit timestamp\n") if ! defined $timestamp;
-$timestamp *= 1000; # Convert to milliseconds
-
-# locate sha256 utility
-my $SHA256 = `which sha256sum || which sha256`;
-chomp $SHA256;
-die("Can't find sha256sum or sha256\n") unless length($SHA256) > 0;
-
-# SHA256 of actual update file
-my $sha256 = `git show auto-update-${commit_hash}:${project}.update | ${SHA256} | head -c 64`;
-die("Can't calculate SHA256 of ${project}.update\n") unless $sha256 =~ m/(\S{64})/;
-chomp $sha256;
-
-# long-form commit hash of HEAD on auto-update branch
-my $update_hash = `git rev-parse refs/heads/auto-update-${commit_hash}`;
-die("Can't find commit hash for HEAD on auto-update-${commit_hash} branch\n") if ! defined $update_hash;
-chomp $update_hash;
-
-printf "Build timestamp (ms): %d / 0x%016x\n", $timestamp, $timestamp;
-printf "Auto-update commit hash: %s\n", $update_hash;
-printf "SHA256 of ${project}.update: %s\n", $sha256;
-
-my $tx_type = 10;
-my $tx_timestamp = time() * 1000;
-my $tx_group_id = 1;
-my $service = 1;
-printf "\nARBITRARY(%d) transaction with timestamp %d, txGroupID %d and service %d\n", $tx_type, $tx_timestamp, $tx_group_id, $service;
-
-my $data_hex = sprintf "%016x%s%s", $timestamp, $update_hash, $sha256;
-printf "\nARBITRARY transaction data payload: %s\n", $data_hex;
-
-my $n_payments = 0;
-my $data_type = 1; # RAW_DATA
-my $data_length = length($data_hex) / 2; # two hex chars per byte
-my $fee = 0;
-my $nonce = 0;
-my $name_length = 0;
-my $identifier_length = 0;
-my $method = 0; # PUT
-my $secret_length = 0;
-my $compression = 0; # None
-my $metadata_hash_length = 0;
-
-die("Something's wrong: data length is not 60 bytes!\n") if $data_length != 60;
-
-my $pubkey = `curl --silent --url http://localhost:${port}/utils/publickey --data ${privkey}`;
-die("Can't convert private key to public key:\n$pubkey\n") unless $pubkey =~ m/^\w{44}$/;
-printf "\nPublic key: %s\n", $pubkey;
-
-my $pubkey_hex = `curl --silent --url http://localhost:${port}/utils/frombase58 --data ${pubkey}`;
-die("Can't convert base58 public key to hex:\n$pubkey_hex\n") unless $pubkey_hex =~ m/^[A-Za-z0-9]{64}$/;
-printf "Public key hex: %s\n", $pubkey_hex;
-
-my $address = `curl --silent --url http://localhost:${port}/addresses/convert/${pubkey}`;
-die("Can't convert base58 public key to address:\n$address\n") unless $address =~ m/^\w{33,34}$/;
-printf "Address: %s\n", $address;
-
-my $reference = `curl --silent --url http://localhost:${port}/addresses/lastreference/${address}`;
-die("Can't fetch last reference for $address:\n$reference\n") unless $reference =~ m/^\w{87,88}$/;
-printf "Last reference: %s\n", $reference;
-
-my $reference_hex = `curl --silent --url http://localhost:${port}/utils/frombase58 --data ${reference}`;
-die("Can't convert base58 reference to hex:\n$reference_hex\n") unless $reference_hex =~ m/^[A-Za-z0-9]{128}$/;
-printf "Last reference hex: %s\n", $reference_hex;
-
-my $raw_tx_hex = sprintf("%08x%016x%08x%s%s%08x%08x%08x%08x%08x%08x%08x%08x%02x%08x%s%08x%08x%016x", $tx_type, $tx_timestamp, $tx_group_id, $reference_hex, $pubkey_hex, $nonce, $name_length, $identifier_length, $method, $secret_length, $compression, $n_payments, $service, $data_type, $data_length, $data_hex, $data_length, $metadata_hash_length, $fee);
-printf "\nRaw transaction hex:\n%s\n", $raw_tx_hex;
-
-my $raw_tx = `curl --silent --url http://localhost:${port}/utils/tobase58/${raw_tx_hex}`;
-die("Can't convert raw transaction hex to base58:\n$raw_tx\n") unless $raw_tx =~ m/^\w{300,320}$/; # Roughly 305 to 320 base58 chars
-printf "\nRaw transaction (base58):\n%s\n", $raw_tx;
-
-my $computed_tx = `curl --silent -X POST --url http://localhost:${port}/arbitrary/compute -H "X-API-KEY: ${apikey}" -d "${raw_tx}"`;
-die("Can't compute nonce for transaction:\n$computed_tx\n") unless $computed_tx =~ m/^\w{300,320}$/; # Roughly 300 to 320 base58 chars
-printf "\nRaw computed transaction (base58):\n%s\n", $computed_tx;
-
-my $sign_data = qq|' { "privateKey": "${privkey}", "transactionBytes": "${computed_tx}" } '|;
-my $signed_tx = `curl --silent -H "accept: text/plain" -H "Content-Type: application/json" --url http://localhost:${port}/transactions/sign --data ${sign_data}`;
-die("Can't sign raw transaction:\n$signed_tx\n") unless $signed_tx =~ m/^\w{390,410}$/; # +90ish longer than $raw_tx
-printf "\nSigned transaction:\n%s\n", $signed_tx;
-
-# Check we can actually fetch update
-my $origin = `git remote get-url origin`;
-die("Unable to get github url for 'origin'?\n") unless $origin && $origin =~ m/:(.*)\.git$/;
-my $repo = $1;
-my $update_url = "https://github.com/${repo}/raw/${update_hash}/${project}.update";
-
-my $fetch_result = `curl --silent -o /dev/null --location --range 0-1 --head --write-out '%{http_code}' --url ${update_url}`;
-die("\nUnable to fetch update from ${update_url}\n") if $fetch_result ne '200';
-printf "\nUpdate fetchable from ${update_url}\n";
-
-# Flush STDOUT after every output
-$| = 1;
-print "\n";
-for (my $delay = 5; $delay > 0; --$delay) {
- printf "\rSubmitting transaction in %d second%s... CTRL-C to abort ", $delay, ($delay != 1 ? 's' : '');
- sleep 1;
-}
-
-printf "\rSubmitting transaction NOW... \n";
-my $result = `curl --silent --url http://localhost:${port}/transactions/process --data ${signed_tx}`;
-chomp $result;
-die("Transaction wasn't accepted:\n$result\n") unless $result eq 'true';
-
-my $decoded_tx = `curl --silent -H "Content-Type: application/json" --url http://localhost:${port}/transactions/decode --data ${signed_tx}`;
-printf "\nTransaction accepted:\n$decoded_tx\n";
diff --git a/tools/publish-auto-update.pl b/tools/publish-auto-update.pl
index ad43b2f4..9e6b885b 100755
--- a/tools/publish-auto-update.pl
+++ b/tools/publish-auto-update.pl
@@ -4,6 +4,7 @@ use strict;
use warnings;
use POSIX;
use Getopt::Std;
+use File::Slurp;
sub usage() {
die("usage: $0 [-p api-port] dev-private-key [short-commit-hash]\n");
@@ -34,6 +35,8 @@ while () {
}
close(POM);
+my $apikey = read_file('apikey.txt');
+
# Do we need to determine commit hash?
unless ($commit_hash) {
# determine git branch
@@ -84,9 +87,16 @@ my $data_hex = sprintf "%016x%s%s", $timestamp, $update_hash, $sha256;
printf "\nARBITRARY transaction data payload: %s\n", $data_hex;
my $n_payments = 0;
-my $is_raw = 1; # RAW_DATA
+my $data_type = 1; # RAW_DATA
my $data_length = length($data_hex) / 2; # two hex chars per byte
-my $fee = 0.001 * 1e8;
+my $fee = 0.01 * 1e8;
+my $nonce = 0;
+my $name_length = 0;
+my $identifier_length = 0;
+my $method = 0; # PUT
+my $secret_length = 0;
+my $compression = 0; # None
+my $metadata_hash_length = 0;
die("Something's wrong: data length is not 60 bytes!\n") if $data_length != 60;
@@ -110,16 +120,16 @@ my $reference_hex = `curl --silent --url http://localhost:${port}/utils/frombase
die("Can't convert base58 reference to hex:\n$reference_hex\n") unless $reference_hex =~ m/^[A-Za-z0-9]{128}$/;
printf "Last reference hex: %s\n", $reference_hex;
-my $raw_tx_hex = sprintf("%08x%016x%08x%s%s%08x%08x%02x%08x%s%016x", $tx_type, $tx_timestamp, $tx_group_id, $reference_hex, $pubkey_hex, $n_payments, $service, $is_raw, $data_length, $data_hex, $fee);
+my $raw_tx_hex = sprintf("%08x%016x%08x%s%s%08x%08x%08x%08x%08x%08x%08x%08x%02x%08x%s%08x%08x%016x", $tx_type, $tx_timestamp, $tx_group_id, $reference_hex, $pubkey_hex, $nonce, $name_length, $identifier_length, $method, $secret_length, $compression, $n_payments, $service, $data_type, $data_length, $data_hex, $data_length, $metadata_hash_length, $fee);
printf "\nRaw transaction hex:\n%s\n", $raw_tx_hex;
my $raw_tx = `curl --silent --url http://localhost:${port}/utils/tobase58/${raw_tx_hex}`;
-die("Can't convert raw transaction hex to base58:\n$raw_tx\n") unless $raw_tx =~ m/^\w{255,265}$/; # Roughly 255 to 265 base58 chars
+die("Can't convert raw transaction hex to base58:\n$raw_tx\n") unless $raw_tx =~ m/^\w{300,320}$/; # Roughly 305 to 320 base58 chars
printf "\nRaw transaction (base58):\n%s\n", $raw_tx;
my $sign_data = qq|' { "privateKey": "${privkey}", "transactionBytes": "${raw_tx}" } '|;
my $signed_tx = `curl --silent -H "accept: text/plain" -H "Content-Type: application/json" --url http://localhost:${port}/transactions/sign --data ${sign_data}`;
-die("Can't sign raw transaction:\n$signed_tx\n") unless $signed_tx =~ m/^\w{345,355}$/; # +90ish longer than $raw_tx
+die("Can't sign raw transaction:\n$signed_tx\n") unless $signed_tx =~ m/^\w{390,410}$/; # +90ish longer than $raw_tx
printf "\nSigned transaction:\n%s\n", $signed_tx;
# Check we can actually fetch update
diff --git a/tools/qdn b/tools/qdn
index ea52e3c9..b8f58141 100755
--- a/tools/qdn
+++ b/tools/qdn
@@ -5,10 +5,10 @@ host="localhost"
port=12391
if [ -z "$*" ]; then
- echo "Usage:"
+ echo "Usage:"
echo
echo "Host/update data:"
- echo "qdn POST [service] [name] PATH [dirpath] "
+ echo "qdn POST [service] [name] PATH [dirpath] "
echo "qdn POST [service] [name] STRING [data-string] "
echo
echo "Fetch data:"
@@ -22,6 +22,21 @@ if [ -z "$*" ]; then
exit
fi
+
+# Default ports for Qortal
+mainnet_port=12391
+testnet_port=62391
+
+# Check if the '-t' operator is passed, if so change to utilizing testnet.
+if [[ "$1" == "-t" ]]; then
+ # Use testnet port
+ port=$testnet_port
+ shift
+else
+ # Use mainnet port
+ port=$mainnet_port
+fi
+
script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
if [ -f "apikey.txt" ]; then
@@ -37,32 +52,46 @@ service=$2
name=$3
if [ -z "${method}" ]; then
- echo "Error: missing method"; exit
+ echo "Error: missing method"
+ exit 1
fi
if [ -z "${service}" ]; then
- echo "Error: missing service"; exit
+ echo "Error: missing service"
+ exit 1
fi
if [ -z "${name}" ]; then
- echo "Error: missing name"; exit
+ echo "Error: missing name"
+ exit 1
fi
-
if [[ "${method}" == "POST" ]]; then
type=$4
data=$5
identifier=$6
+ title=$7
+ description=$8
+ tags=$9
+ category=${10}
+ fee=${11}
+ preview=${12}
+
+
if [ -z "${data}" ]; then
if [[ "${type}" == "PATH" ]]; then
- echo "Error: missing directory"; exit
+ echo "Error: missing directory - please use a path to a directory with a SINGLE file wishing to be published"
+ exit 1
elif [[ "${type}" == "STRING" ]]; then
- echo "Error: missing data string"; exit
+ echo "Error: missing data string - please input the data string you wish to publish"
+ exit 1
else
- echo "Error: unrecognized type"; exit
+ echo "Error: unrecognized type"
+ exit 1
fi
fi
if [ -z "${QORTAL_PRIVKEY}" ]; then
- echo "Error: missing private key. Set it by running: export QORTAL_PRIVKEY=privkeyhere"; exit
+ echo "Error: missing private key. Set it by running: export QORTAL_PRIVKEY=privkeyhere"
+ exit 1
fi
if [ -z "${identifier}" ]; then
@@ -75,30 +104,88 @@ if [[ "${method}" == "POST" ]]; then
elif [[ "${type}" == "STRING" ]]; then
type_component="/string"
fi
+
+ # Create tags component in URL, comma-separated list of tags, will be added to the tags call.
+ tags_component=""
+ if [ -n "${tags}" ]; then
+ IFS=',' read -ra tag_array <<< "${tags}"
+ for tag in "${tag_array[@]}"; do
+ tags_component+="&tags=${tag}"
+ done
+ fi
+
+ if [ -z ${tags_component} ]; then
+ tags_component=""
+ echo "nothing in tags, using empty tags"
+ fi
+
+ #Create category component with pre-defined list of categories. Error if category is specified but not in list.
+ allowed_categories=("ART" "AUTOMOTIVE" "BEAUTY" "BOOKS" "BUSINESS" "COMMUNICATIONS" "CRYPTOCURRENCY" "CULTURE" "DATING" "DESIGN" "ENTERTAINMENT" "EVENTS" "FAITH" "FASHION" "FINANCE" "FOOD" "GAMING" "GEOGRAPHY" "HEALTH" "HISTORY" "HOME" "KNOWLEDGE" "LANGUAGE" "LIFESTYLE" "MANUFACTURING" "MAPS" "MUSIC" "NEWS" "OTHER" "PETS" "PHILOSOPHY" "PHOTOGRAPHY" "POLITICS" "PRODUCE" "PRODUCTIVITY" "PSYCHOLOGY" "QORTAL" "SCIENCE" "SELF_CARE" "SELF_SUFFICIENCY" "SHOPPING" "SOCIAL" "SOFTWARE" "SPIRITUALITY" "SPORTS" "STORYTELLING" "TECHNOLOGY" "TOOLS" "TRAVEL" "UNCATEGORIZED" "VIDEO" "WEATHER")
+
+ if [[ -n "$category" && ! " ${allowed_categories[@]} " =~ " $category " ]]; then
+ echo "Error: Invalid category. Allowed categories are: ${allowed_categories[*]} be sure to place your overall script inputs in the correct order"
+ exit 1
+ elif [ -z "$category" ]; then
+ category=""
+ echo "No category is being set"
+ fi
+
+ if [ -n "$fee" ]; then
+ if [[ "$fee" == "1" || "$fee" == ".01" ]]; then
+ fee="1000000"
+ elif [ -z "$fee" ]; then
+ fee=""
+ else
+ echo "Error: Invalid fee value. Expected '1', '.01' or no input."
+ exit 1
+ fi
+ final_fee="${fee}"
+ fi
+
+
+ # check that preview is true/false
+ if [[ -n "$preview" && ! ( "$preview" == "true" || "$preview" == "false" ) ]]; then
+ echo "Error: Invalid preview value. Expected 'true' or 'false'. Please retry with boolean as preview entry."
+ exit 1
+ elif [ -z "$preview" ]; then
+ preview=""
+ fi
+
+ # Build the API URL
+ api_url="http://${host}:${port}/arbitrary/${service}/${name}/${identifier}${type_component}"
+ api_url+="?title=${title}&description=${description}&tags=${tags_component}&category=${category}&fee=${final_fee}&preview=${preview}"
+
echo "Creating transaction - this can take a while..."
- tx_data=$(curl --silent --insecure -X ${method} "http://${host}:${port}/arbitrary/${service}/${name}/${identifier}${type_component}" -H "X-API-KEY: ${apikey}" -d "${data}")
+ tx_data=$(curl --silent --insecure -X ${method} "${api_url}" -H "accept: text/plain" -H "X-API-KEY: ${apikey}" -H "Content-Type: text/plain" -d "${data}")
if [[ "${tx_data}" == *"error"* || "${tx_data}" == *"ERROR"* ]]; then
- echo "${tx_data}"; exit
+ echo "Error creating transaction: ${tx_data}"
+ exit 1
elif [ -z "${tx_data}" ]; then
- echo "Error: no transaction data returned"; exit
+ echo "Error: no transaction data returned"
+ exit 1
fi
echo "Computing nonce..."
computed_tx_data=$(curl --silent --insecure -X POST "http://${host}:${port}/arbitrary/compute" -H "Content-Type: application/json" -H "X-API-KEY: ${apikey}" -d "${tx_data}")
+
if [[ "${computed_tx_data}" == *"error"* || "${computed_tx_data}" == *"ERROR"* ]]; then
- echo "${computed_tx_data}"; exit
+ echo "Error computing nonce: ${computed_tx_data}"
+ exit 1
fi
echo "Signing..."
signed_tx_data=$(curl --silent --insecure -X POST "http://${host}:${port}/transactions/sign" -H "Content-Type: application/json" -d "{\"privateKey\":\"${QORTAL_PRIVKEY}\",\"transactionBytes\":\"${computed_tx_data}\"}")
+
if [[ "${signed_tx_data}" == *"error"* || "${signed_tx_data}" == *"ERROR"* ]]; then
- echo "${signed_tx_data}"; exit
+ echo "Error signing transaction: ${signed_tx_data}"
+ exit 1
fi
echo "Broadcasting..."
success=$(curl --silent --insecure -X POST "http://${host}:${port}/transactions/process" -H "Content-Type: text/plain" -d "${signed_tx_data}")
+
if [[ "${success}" == "true" ]]; then
echo "Transaction broadcast successfully"
else
@@ -131,9 +218,10 @@ elif [[ "${method}" == "GET" ]]; then
echo "Empty response from ${host}:${port}"
fi
if [[ "${response}" == *"error"* || "${response}" == *"ERROR"* ]]; then
- echo "${response}"; exit
+ echo "${response}"
+ exit 1
fi
echo "${response}"
-
fi
+
diff --git a/tools/tx.pl b/tools/tx.pl
index 1cb3dd5b..7cdf444b 100755
--- a/tools/tx.pl
+++ b/tools/tx.pl
@@ -40,7 +40,7 @@ our %b58 = map { $b58[$_] => $_ } 0 .. 57;
our %reverseb58 = reverse %b58;
our $BASE_URL = $ENV{BASE_URL} || ($opt{t} ? 'http://localhost:62391' : 'http://localhost:12391');
-our $DEFAULT_FEE = 0.001;
+our $DEFAULT_FEE = 0.01;
our %TRANSACTION_TYPES = (
payment => {
@@ -92,7 +92,7 @@ our %TRANSACTION_TYPES = (
},
remove_group_admin => {
url => 'groups/removeadmin',
- required => [qw(groupId txGroupId member)],
+ required => [qw(groupId txGroupId admin)],
key_name => 'ownerPublicKey',
},
group_approval => {