From 98506a038bf2ee604ad0b562bdcefe7caaf00317 Mon Sep 17 00:00:00 2001 From: catbref Date: Tue, 14 Apr 2020 17:19:44 +0100 Subject: [PATCH] Loads of work on CIYAM AT support, including BTC-QORT cross-chain trading. We require AT v1.3.4 now! Updated AT-related logging. Added "isInitial" flag to AT state data so that state data created at deployment is not added to serialized block data. Updated BTC-QORT AT code and tests to cover various scenarios. Added missing 'testNtpOffset' to various test versions of 'settings.json'. Added missing 'ciyamAtSettings' to various test blockchain configs. Loads of AT-related additions/fixes/etc. to core code, e.g Block --- pom.xml | 5 +- src/main/java/org/qortal/at/AT.java | 80 +- src/main/java/org/qortal/at/QortalATAPI.java | 106 +- .../java/org/qortal/at/QortalATLogger.java | 26 - .../java/org/qortal/at/QortalAtLogger.java | 2182 +++++++++++++++++ .../org/qortal/at/QortalAtLoggerFactory.java | 24 + .../org/qortal/at/QortalFunctionCode.java | 28 +- src/main/java/org/qortal/block/Block.java | 47 +- .../java/org/qortal/block/BlockChain.java | 2 +- .../java/org/qortal/block/BlockMinter.java | 10 +- .../java/org/qortal/crosschain/BTCACCT.java | 265 +- src/main/java/org/qortal/crypto/Crypto.java | 12 + .../java/org/qortal/data/at/ATStateData.java | 20 +- .../repository/hsqldb/HSQLDBATRepository.java | 76 +- .../hsqldb/HSQLDBDatabaseUpdates.java | 5 + .../org/qortal/transaction/AtTransaction.java | 4 + .../transaction/DeployAtTransaction.java | 4 +- .../org/qortal/transaction/Transaction.java | 7 +- .../java/org/qortal/test/apps/orphan.java | 18 +- .../java/org/qortal/test/btcacct/AtTests.java | 403 +++ .../org/qortal/test/btcacct/BuildP2SH.java | 2 +- .../org/qortal/test/btcacct/CheckP2SH.java | 2 +- .../org/qortal/test/btcacct/DeployAT.java | 32 +- .../java/org/qortal/test/btcacct/Redeem.java | 2 +- .../java/org/qortal/test/btcacct/Refund.java | 2 +- .../org/qortal/test/common/BlockUtils.java | 4 +- src/test/resources/test-chain-old-asset.json | 6 + src/test/resources/test-chain-v1.json | 6 + .../test-chain-v2-founder-rewards.json | 6 + src/test/resources/test-chain-v2-minting.json | 6 + .../resources/test-chain-v2-qora-holder.json | 6 + src/test/resources/test-chain-v2.json | 6 + .../resources/test-settings-old-asset.json | 1 + src/test/resources/test-settings-v1.json | 1 + .../test-settings-v2-founder-rewards.json | 1 + .../resources/test-settings-v2-minting.json | 1 + .../test-settings-v2-qora-holder.json | 1 + src/test/resources/test-settings-v2.json | 1 + 38 files changed, 3144 insertions(+), 266 deletions(-) delete mode 100644 src/main/java/org/qortal/at/QortalATLogger.java create mode 100644 src/main/java/org/qortal/at/QortalAtLogger.java create mode 100644 src/main/java/org/qortal/at/QortalAtLoggerFactory.java create mode 100644 src/test/java/org/qortal/test/btcacct/AtTests.java diff --git a/pom.xml b/pom.xml index 83cb4b4d..a866afd6 100644 --- a/pom.xml +++ b/pom.xml @@ -9,6 +9,7 @@ 0.15.5 1.64 ${maven.build.timestamp} + 1.3.4 3.6 1.8 1.2.2 @@ -405,8 +406,8 @@ org.ciyam - at - 1.3.2 + AT + ${ciyam-at.version} diff --git a/src/main/java/org/qortal/at/AT.java b/src/main/java/org/qortal/at/AT.java index 23bf02cd..1ba5d8e2 100644 --- a/src/main/java/org/qortal/at/AT.java +++ b/src/main/java/org/qortal/at/AT.java @@ -54,17 +54,18 @@ public class AT { long blockTimestamp = Timestamp.toLong(height, 0); QortalATAPI api = new QortalATAPI(repository, skeletonAtData, blockTimestamp); + QortalAtLoggerFactory loggerFactory = QortalAtLoggerFactory.getInstance(); - MachineState machineState = new MachineState(api, deployATTransactionData.getCreationBytes()); + MachineState machineState = new MachineState(api, loggerFactory, deployATTransactionData.getCreationBytes()); this.atData = new ATData(atAddress, creatorPublicKey, creation, machineState.version, assetId, machineState.getCodeBytes(), - machineState.getIsSleeping(), machineState.getSleepUntilHeight(), machineState.getIsFinished(), machineState.getHadFatalError(), - machineState.getIsFrozen(), machineState.getFrozenBalance()); + machineState.isSleeping(), machineState.getSleepUntilHeight(), machineState.isFinished(), machineState.hadFatalError(), + machineState.isFrozen(), machineState.getFrozenBalance()); byte[] stateData = machineState.toBytes(); byte[] stateHash = Crypto.digest(stateData); - this.atStateData = new ATStateData(atAddress, height, creation, stateData, stateHash, BigDecimal.ZERO.setScale(8)); + this.atStateData = new ATStateData(atAddress, height, creation, stateData, stateHash, BigDecimal.ZERO.setScale(8), true); } else { // Legacy v1 AT // We would deploy these in 'dead' state as they will never be run on Qortal @@ -107,7 +108,7 @@ public class AT { this.atData = new ATData(atAddress, creatorPublicKey, creation, version, Asset.QORT, codeBytes, isSleeping, sleepUntilHeight, isFinished, hadFatalError, isFrozen, frozenBalance); - this.atStateData = new ATStateData(atAddress, height, creation, null, null, BigDecimal.ZERO.setScale(8)); + this.atStateData = new ATStateData(atAddress, height, creation, null, null, BigDecimal.ZERO.setScale(8), true); } } @@ -133,34 +134,87 @@ public class AT { this.repository.getATRepository().delete(this.atData.getATAddress()); } - public List run(long blockTimestamp) throws DataException { + public List run(int blockHeight, long blockTimestamp) throws DataException { String atAddress = this.atData.getATAddress(); QortalATAPI api = new QortalATAPI(repository, this.atData, blockTimestamp); - QortalATLogger logger = new QortalATLogger(); + QortalAtLoggerFactory loggerFactory = QortalAtLoggerFactory.getInstance(); byte[] codeBytes = this.atData.getCodeBytes(); - // Fetch latest ATStateData for this AT (if any) + // Fetch latest ATStateData for this AT ATStateData latestAtStateData = this.repository.getATRepository().getLatestATState(atAddress); - // There should be at least initial AT state data + // There should be at least initial deployment AT state data if (latestAtStateData == null) - throw new IllegalStateException("No initial AT state data found"); + throw new IllegalStateException("No previous AT state data found"); // [Re]create AT machine state using AT state data or from scratch as applicable - MachineState state = MachineState.fromBytes(api, logger, latestAtStateData.getStateData(), codeBytes); + MachineState state = MachineState.fromBytes(api, loggerFactory, latestAtStateData.getStateData(), codeBytes); state.execute(); - int height = this.repository.getBlockRepository().getBlockchainHeight() + 1; long creation = this.atData.getCreation(); byte[] stateData = state.toBytes(); byte[] stateHash = Crypto.digest(stateData); BigDecimal atFees = api.calcFinalFees(state); - this.atStateData = new ATStateData(atAddress, height, creation, stateData, stateHash, atFees); + this.atStateData = new ATStateData(atAddress, blockHeight, creation, stateData, stateHash, atFees, false); return api.getTransactions(); } + public void update(int blockHeight, long blockTimestamp) throws DataException { + // [Re]create AT machine state using AT state data or from scratch as applicable + QortalATAPI api = new QortalATAPI(repository, this.atData, blockTimestamp); + QortalAtLoggerFactory loggerFactory = QortalAtLoggerFactory.getInstance(); + + byte[] codeBytes = this.atData.getCodeBytes(); + MachineState state = MachineState.fromBytes(api, loggerFactory, this.atStateData.getStateData(), codeBytes); + + // Save latest AT state data + this.repository.getATRepository().save(this.atStateData); + + // Update AT info in repository too + this.atData.setIsSleeping(state.isSleeping()); + this.atData.setSleepUntilHeight(state.getSleepUntilHeight()); + this.atData.setIsFinished(state.isFinished()); + this.atData.setHadFatalError(state.hadFatalError()); + this.atData.setIsFrozen(state.isFrozen()); + Long frozenBalance = state.getFrozenBalance(); + this.atData.setFrozenBalance(frozenBalance != null ? BigDecimal.valueOf(frozenBalance, 8) : null); + this.repository.getATRepository().save(this.atData); + } + + public void revert(int blockHeight, long blockTimestamp) throws DataException { + String atAddress = this.atData.getATAddress(); + + // Delete old AT state data from repository + this.repository.getATRepository().delete(atAddress, blockHeight); + + if (this.atStateData.isInitial()) + return; + + // Load previous state data + ATStateData previousStateData = this.repository.getATRepository().getLatestATState(atAddress); + if (previousStateData == null) + throw new DataException("Can't find previous AT state data for " + atAddress); + + // [Re]create AT machine state using AT state data or from scratch as applicable + QortalATAPI api = new QortalATAPI(repository, this.atData, blockTimestamp); + QortalAtLoggerFactory loggerFactory = QortalAtLoggerFactory.getInstance(); + + byte[] codeBytes = this.atData.getCodeBytes(); + MachineState state = MachineState.fromBytes(api, loggerFactory, previousStateData.getStateData(), codeBytes); + + // Update AT info in repository + this.atData.setIsSleeping(state.isSleeping()); + this.atData.setSleepUntilHeight(state.getSleepUntilHeight()); + this.atData.setIsFinished(state.isFinished()); + this.atData.setHadFatalError(state.hadFatalError()); + this.atData.setIsFrozen(state.isFrozen()); + Long frozenBalance = state.getFrozenBalance(); + this.atData.setFrozenBalance(frozenBalance != null ? BigDecimal.valueOf(frozenBalance, 8) : null); + this.repository.getATRepository().save(this.atData); + } + } diff --git a/src/main/java/org/qortal/at/QortalATAPI.java b/src/main/java/org/qortal/at/QortalATAPI.java index 27f67ac0..9455e59e 100644 --- a/src/main/java/org/qortal/at/QortalATAPI.java +++ b/src/main/java/org/qortal/at/QortalATAPI.java @@ -1,12 +1,13 @@ package org.qortal.at; import java.math.BigDecimal; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.ciyam.at.API; import org.ciyam.at.ExecutionException; import org.ciyam.at.FunctionData; @@ -44,6 +45,7 @@ import com.google.common.primitives.Bytes; public class QortalATAPI extends API { private static final byte[] ADDRESS_PADDING = new byte[32 - Account.ADDRESS_LENGTH]; + private static final Logger LOGGER = LogManager.getLogger(QortalATAPI.class); // Properties private Repository repository; @@ -128,13 +130,14 @@ public class QortalATAPI extends API { throw new RuntimeException("AT API unable to fetch previous block hash?"); // Block's signature is 128 bytes so we need to reduce this to 4 longs (32 bytes) - // To be able to use hash to look up block, save height (8 bytes) and rehash with SHA192 (24 bytes) + // To be able to use hash to look up block, save height (8 bytes) and partial signature (24 bytes) this.setA1(state, previousBlockHeight); - byte[] sigHash192 = sha192(blockSummaries.get(0).getSignature()); - this.setA2(state, fromBytes(sigHash192, 0)); - this.setA3(state, fromBytes(sigHash192, 8)); - this.setA4(state, fromBytes(sigHash192, 16)); + byte[] signature = blockSummaries.get(0).getSignature(); + // Save some of minter's signature and transactions signature, so middle 24 bytes of the full 128 byte signature. + this.setA2(state, fromBytes(signature, 52)); + this.setA3(state, fromBytes(signature, 60)); + this.setA4(state, fromBytes(signature, 68)); } catch (DataException e) { throw new RuntimeException("AT API unable to fetch previous block?", e); } @@ -143,7 +146,7 @@ public class QortalATAPI extends API { @Override public void putTransactionAfterTimestampIntoA(Timestamp timestamp, MachineState state) { // Recipient is this AT - String recipient = this.atData.getATAddress(); + String atAddress = this.atData.getATAddress(); int height = timestamp.blockHeight; int sequence = timestamp.transactionSequence + 1; @@ -151,39 +154,44 @@ public class QortalATAPI extends API { BlockRepository blockRepository = this.getRepository().getBlockRepository(); try { - Account recipientAccount = new Account(this.getRepository(), recipient); int currentHeight = blockRepository.getBlockchainHeight(); + List blockTransactions = null; while (height <= currentHeight) { - BlockData blockData = blockRepository.fromHeight(height); + if (blockTransactions == null) { + BlockData blockData = blockRepository.fromHeight(height); - if (blockData == null) - throw new DataException("Unable to fetch block " + height + " from repository?"); + if (blockData == null) + throw new DataException("Unable to fetch block " + height + " from repository?"); - Block block = new Block(this.getRepository(), blockData); + Block block = new Block(this.getRepository(), blockData); - List blockTransactions = block.getTransactions(); + blockTransactions = block.getTransactions(); + } // No more transactions in this block? Try next block if (sequence >= blockTransactions.size()) { ++height; sequence = 0; + blockTransactions = null; continue; } Transaction transaction = blockTransactions.get(sequence); // Transaction needs to be sent to specified recipient - if (transaction.getRecipientAccounts().contains(recipientAccount)) { + List recipientAccounts = transaction.getRecipientAccounts(); + List recipientAddresses = recipientAccounts.stream().map(Account::getAddress).collect(Collectors.toList()); + if (recipientAddresses.contains(atAddress)) { // Found a transaction this.setA1(state, new Timestamp(height, timestamp.blockchainId, sequence).longValue()); - // Hash transaction's signature into other three A fields for future verification that it's the same transaction - byte[] sigHash192 = sha192(transaction.getTransactionData().getSignature()); - this.setA2(state, fromBytes(sigHash192, 0)); - this.setA3(state, fromBytes(sigHash192, 8)); - this.setA4(state, fromBytes(sigHash192, 16)); + // Copy transaction's partial signature into the other three A fields for future verification that it's the same transaction + byte[] signature = transaction.getTransactionData().getSignature(); + this.setA2(state, fromBytes(signature, 8)); + this.setA3(state, fromBytes(signature, 16)); + this.setA4(state, fromBytes(signature, 24)); return; } @@ -245,7 +253,7 @@ public class QortalATAPI extends API { @Override public long getTimestampFromTransactionInA(MachineState state) { // Transaction's "timestamp" already stored in A1 - Timestamp timestamp = new Timestamp(state.getA1()); + Timestamp timestamp = new Timestamp(this.getA1(state)); return timestamp.longValue(); } @@ -340,11 +348,10 @@ public class QortalATAPI extends API { @Override public void putCreatorAddressIntoB(MachineState state) { + // Simply use raw public key byte[] publicKey = atData.getCreatorPublicKey(); - String address = Crypto.toAddress(publicKey); - byte[] addressBytes = Bytes.ensureCapacity(address.getBytes(), 32, 0); - this.setB(state, addressBytes); + this.setB(state, publicKey); } @Override @@ -377,7 +384,7 @@ public class QortalATAPI extends API { @Override public void messageAToB(MachineState state) { - byte[] message = state.getA(); + byte[] message = this.getA(state); Account recipient = getAccountFromB(state); long timestamp = this.getNextTransactionTimestamp(); @@ -404,6 +411,9 @@ public class QortalATAPI extends API { @Override public void onFinished(long finalBalance, MachineState state) { + if (finalBalance <= 0) + return; + // Refund remaining balance (if any) to AT's creator Account creator = this.getCreator(); long timestamp = this.getNextTransactionTimestamp(); @@ -421,7 +431,7 @@ public class QortalATAPI extends API { @Override public void onFatalError(MachineState state, ExecutionException e) { - state.getLogger().error("AT " + this.atData.getATAddress() + " suffered fatal error: " + e.getMessage()); + LOGGER.error("AT " + this.atData.getATAddress() + " suffered fatal error: " + e.getMessage()); } @Override @@ -432,7 +442,7 @@ public class QortalATAPI extends API { if (qortalFunctionCode == null) throw new IllegalFunctionCodeException("Unknown Qortal function code 0x" + String.format("%04x", rawFunctionCode) + " encountered"); - qortalFunctionCode.preExecuteCheck(2, true, state, rawFunctionCode); + qortalFunctionCode.preExecuteCheck(paramCount, returnValueExpected, rawFunctionCode); } @Override @@ -450,29 +460,23 @@ public class QortalATAPI extends API { | (bytes[start + 4] & 0xffL) << 32 | (bytes[start + 5] & 0xffL) << 40 | (bytes[start + 6] & 0xffL) << 48 | (bytes[start + 7] & 0xffL) << 56; } - /** Returns SHA2-192 digest of input - used to verify transaction signatures */ - public static byte[] sha192(byte[] input) { - try { - // SHA2-192 - MessageDigest sha192 = MessageDigest.getInstance("SHA-192"); - return sha192.digest(input); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("SHA-192 not available"); - } + /** Returns partial transaction signature, used to verify we're operating on the same transaction and not naively using block height & sequence. */ + public static byte[] partialSignature(byte[] fullSignature) { + return Arrays.copyOfRange(fullSignature, 8, 32); } - /** Verify transaction's SHA2-192 hashed signature matches A2 thru A4 */ - private static void verifyTransaction(TransactionData transactionData, MachineState state) { - // Compare SHA2-192 of transaction's signature against A2 thru A4 - byte[] hash = sha192(transactionData.getSignature()); + /** Verify transaction's partial signature matches A2 thru A4 */ + private void verifyTransaction(TransactionData transactionData, MachineState state) { + // Compare end of transaction's signature against A2 thru A4 + byte[] sig = transactionData.getSignature(); - if (state.getA2() != fromBytes(hash, 0) || state.getA3() != fromBytes(hash, 8) || state.getA4() != fromBytes(hash, 16)) + if (this.getA2(state) != fromBytes(sig, 8) || this.getA3(state) != fromBytes(sig, 16) || this.getA4(state) != fromBytes(sig, 24)) throw new IllegalStateException("Transaction signature in A no longer matches signature from repository"); } /** Returns transaction data from repository using block height & sequence from A1, checking the transaction signatures match too */ /* package */ TransactionData getTransactionFromA(MachineState state) { - Timestamp timestamp = new Timestamp(state.getA1()); + Timestamp timestamp = new Timestamp(this.getA1(state)); try { TransactionData transactionData = this.repository.getTransactionRepository().fromHeightAndSequence(timestamp.blockHeight, @@ -503,11 +507,11 @@ public class QortalATAPI extends API { /** Returns the timestamp to use for next AT Transaction */ private long getNextTransactionTimestamp() { /* - * Timestamp is block's timestamp + position in AT-Transactions list. + * Use block's timestamp. * - * We need increasing timestamps to preserve transaction order and hence a correct signature-reference chain when the block is processed. + * This is OK because AT transactions are always generated locally and order is preserved in Transaction.getDataComparator(). */ - return this.blockTimestamp + this.transactions.size(); + return this.blockTimestamp; } /** Returns AT account's lastReference, taking newly generated ATTransactions into account */ @@ -535,7 +539,7 @@ public class QortalATAPI extends API { * Otherwise, assume B is a public key. */ private Account getAccountFromB(MachineState state) { - byte[] bBytes = state.getB(); + byte[] bBytes = this.getB(state); if ((bBytes[0] == Crypto.ADDRESS_VERSION || bBytes[0] == Crypto.AT_ADDRESS_VERSION) && Arrays.mismatch(bBytes, Account.ADDRESS_LENGTH, 32, ADDRESS_PADDING, 0, ADDRESS_PADDING.length) == -1) { @@ -550,4 +554,14 @@ public class QortalATAPI extends API { return new PublicKeyAccount(this.repository, bBytes); } + /* Convenience methods to allow QortalFunctionCode package-visibility access to A/B-get/set methods. */ + + protected byte[] getB(MachineState state) { + return super.getB(state); + } + + protected void setB(MachineState state, byte[] bBytes) { + super.setB(state, bBytes); + } + } diff --git a/src/main/java/org/qortal/at/QortalATLogger.java b/src/main/java/org/qortal/at/QortalATLogger.java deleted file mode 100644 index df01247a..00000000 --- a/src/main/java/org/qortal/at/QortalATLogger.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.qortal.at; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -public class QortalATLogger implements org.ciyam.at.LoggerInterface { - - // NOTE: We're logging on behalf of org.qortal.at.AT, not ourselves! - private static final Logger LOGGER = LogManager.getLogger(AT.class); - - @Override - public void error(String message) { - LOGGER.error(message); - } - - @Override - public void debug(String message) { - LOGGER.debug(message); - } - - @Override - public void echo(String message) { - LOGGER.info(message); - } - -} diff --git a/src/main/java/org/qortal/at/QortalAtLogger.java b/src/main/java/org/qortal/at/QortalAtLogger.java new file mode 100644 index 00000000..703972a6 --- /dev/null +++ b/src/main/java/org/qortal/at/QortalAtLogger.java @@ -0,0 +1,2182 @@ +package org.qortal.at; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.message.Message; +import org.apache.logging.log4j.message.MessageFactory; +import org.apache.logging.log4j.spi.AbstractLogger; +import org.apache.logging.log4j.spi.ExtendedLoggerWrapper; +import org.apache.logging.log4j.util.MessageSupplier; +import org.apache.logging.log4j.util.Supplier; + +/** + * Extended Logger interface with convenience methods for + * the ERROR, DEBUG and ECHO custom log levels. + *

Compatible with Log4j 2.6 or higher.

+ */ +public final class QortalAtLogger extends ExtendedLoggerWrapper implements org.ciyam.at.AtLogger { + private static final long serialVersionUID = 4740841485167L; + private final ExtendedLoggerWrapper logger; + + private static final String FQCN = QortalAtLogger.class.getName(); + private static final Level ERROR = Level.forName("ERROR", 200); + private static final Level DEBUG = Level.forName("DEBUG", 500); + private static final Level ECHO = Level.forName("ECHO", 400); + + private QortalAtLogger(final Logger logger) { + super((AbstractLogger) logger, logger.getName(), logger.getMessageFactory()); + this.logger = this; + } + + /** + * Returns a custom Logger with the name of the calling class. + * + * @return The custom Logger for the calling class. + */ + public static QortalAtLogger create() { + final Logger wrapped = LogManager.getLogger(); + return new QortalAtLogger(wrapped); + } + + /** + * Returns a custom Logger using the fully qualified name of the Class as + * the Logger name. + * + * @param loggerName The Class whose name should be used as the Logger name. + * If null it will default to the calling class. + * @return The custom Logger. + */ + public static QortalAtLogger create(final Class loggerName) { + final Logger wrapped = LogManager.getLogger(loggerName); + return new QortalAtLogger(wrapped); + } + + /** + * Returns a custom Logger using the fully qualified name of the Class as + * the Logger name. + * + * @param loggerName The Class whose name should be used as the Logger name. + * If null it will default to the calling class. + * @param messageFactory The message factory is used only when creating a + * logger, subsequent use does not change the logger but will log + * a warning if mismatched. + * @return The custom Logger. + */ + public static QortalAtLogger create(final Class loggerName, final MessageFactory messageFactory) { + final Logger wrapped = LogManager.getLogger(loggerName, messageFactory); + return new QortalAtLogger(wrapped); + } + + /** + * Returns a custom Logger using the fully qualified class name of the value + * as the Logger name. + * + * @param value The value whose class name should be used as the Logger + * name. If null the name of the calling class will be used as + * the logger name. + * @return The custom Logger. + */ + public static QortalAtLogger create(final Object value) { + final Logger wrapped = LogManager.getLogger(value); + return new QortalAtLogger(wrapped); + } + + /** + * Returns a custom Logger using the fully qualified class name of the value + * as the Logger name. + * + * @param value The value whose class name should be used as the Logger + * name. If null the name of the calling class will be used as + * the logger name. + * @param messageFactory The message factory is used only when creating a + * logger, subsequent use does not change the logger but will log + * a warning if mismatched. + * @return The custom Logger. + */ + public static QortalAtLogger create(final Object value, final MessageFactory messageFactory) { + final Logger wrapped = LogManager.getLogger(value, messageFactory); + return new QortalAtLogger(wrapped); + } + + /** + * Returns a custom Logger with the specified name. + * + * @param name The logger name. If null the name of the calling class will + * be used. + * @return The custom Logger. + */ + public static QortalAtLogger create(final String name) { + final Logger wrapped = LogManager.getLogger(name); + return new QortalAtLogger(wrapped); + } + + /** + * Returns a custom Logger with the specified name. + * + * @param name The logger name. If null the name of the calling class will + * be used. + * @param messageFactory The message factory is used only when creating a + * logger, subsequent use does not change the logger but will log + * a warning if mismatched. + * @return The custom Logger. + */ + public static QortalAtLogger create(final String name, final MessageFactory messageFactory) { + final Logger wrapped = LogManager.getLogger(name, messageFactory); + return new QortalAtLogger(wrapped); + } + + /** + * Logs a message with the specific Marker at the {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param msg the message string to be logged + */ + public void error(final Marker marker, final Message msg) { + logger.logIfEnabled(FQCN, ERROR, marker, msg, (Throwable) null); + } + + /** + * Logs a message with the specific Marker at the {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param msg the message string to be logged + * @param t A Throwable or null. + */ + public void error(final Marker marker, final Message msg, final Throwable t) { + logger.logIfEnabled(FQCN, ERROR, marker, msg, t); + } + + /** + * Logs a message object with the {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param message the message object to log. + */ + public void error(final Marker marker, final Object message) { + logger.logIfEnabled(FQCN, ERROR, marker, message, (Throwable) null); + } + + /** + * Logs a message CharSequence with the {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param message the message CharSequence to log. + * @since Log4j-2.6 + */ + public void error(final Marker marker, final CharSequence message) { + logger.logIfEnabled(FQCN, ERROR, marker, message, (Throwable) null); + } + + /** + * Logs a message at the {@code ERROR} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param marker the marker data specific to this log statement + * @param message the message to log. + * @param t the exception to log, including its stack trace. + */ + public void error(final Marker marker, final Object message, final Throwable t) { + logger.logIfEnabled(FQCN, ERROR, marker, message, t); + } + + /** + * Logs a message at the {@code ERROR} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param marker the marker data specific to this log statement + * @param message the CharSequence to log. + * @param t the exception to log, including its stack trace. + * @since Log4j-2.6 + */ + public void error(final Marker marker, final CharSequence message, final Throwable t) { + logger.logIfEnabled(FQCN, ERROR, marker, message, t); + } + + /** + * Logs a message object with the {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param message the message object to log. + */ + public void error(final Marker marker, final String message) { + logger.logIfEnabled(FQCN, ERROR, marker, message, (Throwable) null); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param params parameters to the message. + * @see #getMessageFactory() + */ + public void error(final Marker marker, final String message, final Object... params) { + logger.logIfEnabled(FQCN, ERROR, marker, message, params); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final Marker marker, final String message, final Object p0) { + logger.logIfEnabled(FQCN, ERROR, marker, message, p0); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final Marker marker, final String message, final Object p0, final Object p1) { + logger.logIfEnabled(FQCN, ERROR, marker, message, p0, p1); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final Marker marker, final String message, final Object p0, final Object p1, final Object p2) { + logger.logIfEnabled(FQCN, ERROR, marker, message, p0, p1, p2); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3) { + logger.logIfEnabled(FQCN, ERROR, marker, message, p0, p1, p2, p3); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4) { + logger.logIfEnabled(FQCN, ERROR, marker, message, p0, p1, p2, p3, p4); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5) { + logger.logIfEnabled(FQCN, ERROR, marker, message, p0, p1, p2, p3, p4, p5); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6) { + logger.logIfEnabled(FQCN, ERROR, marker, message, p0, p1, p2, p3, p4, p5, p6); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7) { + logger.logIfEnabled(FQCN, ERROR, marker, message, p0, p1, p2, p3, p4, p5, p6, p7); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @param p8 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7, final Object p8) { + logger.logIfEnabled(FQCN, ERROR, marker, message, p0, p1, p2, p3, p4, p5, p6, p7, p8); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @param p8 parameter to the message. + * @param p9 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7, final Object p8, final Object p9) { + logger.logIfEnabled(FQCN, ERROR, marker, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); + } + + /** + * Logs a message at the {@code ERROR} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param marker the marker data specific to this log statement + * @param message the message to log. + * @param t the exception to log, including its stack trace. + */ + public void error(final Marker marker, final String message, final Throwable t) { + logger.logIfEnabled(FQCN, ERROR, marker, message, t); + } + + /** + * Logs the specified Message at the {@code ERROR} level. + * + * @param msg the message string to be logged + */ + public void error(final Message msg) { + logger.logIfEnabled(FQCN, ERROR, null, msg, (Throwable) null); + } + + /** + * Logs the specified Message at the {@code ERROR} level. + * + * @param msg the message string to be logged + * @param t A Throwable or null. + */ + public void error(final Message msg, final Throwable t) { + logger.logIfEnabled(FQCN, ERROR, null, msg, t); + } + + /** + * Logs a message object with the {@code ERROR} level. + * + * @param message the message object to log. + */ + public void error(final Object message) { + logger.logIfEnabled(FQCN, ERROR, null, message, (Throwable) null); + } + + /** + * Logs a message at the {@code ERROR} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param message the message to log. + * @param t the exception to log, including its stack trace. + */ + public void error(final Object message, final Throwable t) { + logger.logIfEnabled(FQCN, ERROR, null, message, t); + } + + /** + * Logs a message CharSequence with the {@code ERROR} level. + * + * @param message the message CharSequence to log. + * @since Log4j-2.6 + */ + public void error(final CharSequence message) { + logger.logIfEnabled(FQCN, ERROR, null, message, (Throwable) null); + } + + /** + * Logs a CharSequence at the {@code ERROR} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param message the CharSequence to log. + * @param t the exception to log, including its stack trace. + * @since Log4j-2.6 + */ + public void error(final CharSequence message, final Throwable t) { + logger.logIfEnabled(FQCN, ERROR, null, message, t); + } + + /** + * Logs a message object with the {@code ERROR} level. + * + * @param message the message object to log. + */ + public void error(final String message) { + logger.logIfEnabled(FQCN, ERROR, null, message, (Throwable) null); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param message the message to log; the format depends on the message factory. + * @param params parameters to the message. + * @see #getMessageFactory() + */ + public void error(final String message, final Object... params) { + logger.logIfEnabled(FQCN, ERROR, null, message, params); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final String message, final Object p0) { + logger.logIfEnabled(FQCN, ERROR, null, message, p0); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final String message, final Object p0, final Object p1) { + logger.logIfEnabled(FQCN, ERROR, null, message, p0, p1); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final String message, final Object p0, final Object p1, final Object p2) { + logger.logIfEnabled(FQCN, ERROR, null, message, p0, p1, p2); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final String message, final Object p0, final Object p1, final Object p2, + final Object p3) { + logger.logIfEnabled(FQCN, ERROR, null, message, p0, p1, p2, p3); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4) { + logger.logIfEnabled(FQCN, ERROR, null, message, p0, p1, p2, p3, p4); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5) { + logger.logIfEnabled(FQCN, ERROR, null, message, p0, p1, p2, p3, p4, p5); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6) { + logger.logIfEnabled(FQCN, ERROR, null, message, p0, p1, p2, p3, p4, p5, p6); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7) { + logger.logIfEnabled(FQCN, ERROR, null, message, p0, p1, p2, p3, p4, p5, p6, p7); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @param p8 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7, final Object p8) { + logger.logIfEnabled(FQCN, ERROR, null, message, p0, p1, p2, p3, p4, p5, p6, p7, p8); + } + + /** + * Logs a message with parameters at the {@code ERROR} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @param p8 parameter to the message. + * @param p9 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void error(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7, final Object p8, final Object p9) { + logger.logIfEnabled(FQCN, ERROR, null, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); + } + + /** + * Logs a message at the {@code ERROR} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param message the message to log. + * @param t the exception to log, including its stack trace. + */ + public void error(final String message, final Throwable t) { + logger.logIfEnabled(FQCN, ERROR, null, message, t); + } + + /** + * Logs a message which is only to be constructed if the logging level is the {@code ERROR}level. + * + * @param msgSupplier A function, which when called, produces the desired log message; + * the format depends on the message factory. + * @since Log4j-2.4 + */ + public void error(final Supplier msgSupplier) { + logger.logIfEnabled(FQCN, ERROR, null, msgSupplier, (Throwable) null); + } + + /** + * Logs a message (only to be constructed if the logging level is the {@code ERROR} + * level) including the stack trace of the {@link Throwable} t passed as parameter. + * + * @param msgSupplier A function, which when called, produces the desired log message; + * the format depends on the message factory. + * @param t the exception to log, including its stack trace. + * @since Log4j-2.4 + */ + public void error(final Supplier msgSupplier, final Throwable t) { + logger.logIfEnabled(FQCN, ERROR, null, msgSupplier, t); + } + + /** + * Logs a message which is only to be constructed if the logging level is the + * {@code ERROR} level with the specified Marker. + * + * @param marker the marker data specific to this log statement + * @param msgSupplier A function, which when called, produces the desired log message; + * the format depends on the message factory. + * @since Log4j-2.4 + */ + public void error(final Marker marker, final Supplier msgSupplier) { + logger.logIfEnabled(FQCN, ERROR, marker, msgSupplier, (Throwable) null); + } + + /** + * Logs a message with parameters which are only to be constructed if the logging level is the + * {@code ERROR} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param paramSuppliers An array of functions, which when called, produce the desired log message parameters. + * @since Log4j-2.4 + */ + public void error(final Marker marker, final String message, final Supplier... paramSuppliers) { + logger.logIfEnabled(FQCN, ERROR, marker, message, paramSuppliers); + } + + /** + * Logs a message (only to be constructed if the logging level is the {@code ERROR} + * level) with the specified Marker and including the stack trace of the {@link Throwable} + * t passed as parameter. + * + * @param marker the marker data specific to this log statement + * @param msgSupplier A function, which when called, produces the desired log message; + * the format depends on the message factory. + * @param t A Throwable or null. + * @since Log4j-2.4 + */ + public void error(final Marker marker, final Supplier msgSupplier, final Throwable t) { + logger.logIfEnabled(FQCN, ERROR, marker, msgSupplier, t); + } + + /** + * Logs a message with parameters which are only to be constructed if the logging level is + * the {@code ERROR} level. + * + * @param message the message to log; the format depends on the message factory. + * @param paramSuppliers An array of functions, which when called, produce the desired log message parameters. + * @since Log4j-2.4 + */ + public void error(final String message, final Supplier... paramSuppliers) { + logger.logIfEnabled(FQCN, ERROR, null, message, paramSuppliers); + } + + /** + * Logs a message which is only to be constructed if the logging level is the + * {@code ERROR} level with the specified Marker. The {@code MessageSupplier} may or may + * not use the {@link MessageFactory} to construct the {@code Message}. + * + * @param marker the marker data specific to this log statement + * @param msgSupplier A function, which when called, produces the desired log message. + * @since Log4j-2.4 + */ + public void error(final Marker marker, final MessageSupplier msgSupplier) { + logger.logIfEnabled(FQCN, ERROR, marker, msgSupplier, (Throwable) null); + } + + /** + * Logs a message (only to be constructed if the logging level is the {@code ERROR} + * level) with the specified Marker and including the stack trace of the {@link Throwable} + * t passed as parameter. The {@code MessageSupplier} may or may not use the + * {@link MessageFactory} to construct the {@code Message}. + * + * @param marker the marker data specific to this log statement + * @param msgSupplier A function, which when called, produces the desired log message. + * @param t A Throwable or null. + * @since Log4j-2.4 + */ + public void error(final Marker marker, final MessageSupplier msgSupplier, final Throwable t) { + logger.logIfEnabled(FQCN, ERROR, marker, msgSupplier, t); + } + + /** + * Logs a message which is only to be constructed if the logging level is the + * {@code ERROR} level. The {@code MessageSupplier} may or may not use the + * {@link MessageFactory} to construct the {@code Message}. + * + * @param msgSupplier A function, which when called, produces the desired log message. + * @since Log4j-2.4 + */ + public void error(final MessageSupplier msgSupplier) { + logger.logIfEnabled(FQCN, ERROR, null, msgSupplier, (Throwable) null); + } + + /** + * Logs a message (only to be constructed if the logging level is the {@code ERROR} + * level) including the stack trace of the {@link Throwable} t passed as parameter. + * The {@code MessageSupplier} may or may not use the {@link MessageFactory} to construct the + * {@code Message}. + * + * @param msgSupplier A function, which when called, produces the desired log message. + * @param t the exception to log, including its stack trace. + * @since Log4j-2.4 + */ + public void error(final MessageSupplier msgSupplier, final Throwable t) { + logger.logIfEnabled(FQCN, ERROR, null, msgSupplier, t); + } + + /** + * Logs a message with the specific Marker at the {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param msg the message string to be logged + */ + public void debug(final Marker marker, final Message msg) { + logger.logIfEnabled(FQCN, DEBUG, marker, msg, (Throwable) null); + } + + /** + * Logs a message with the specific Marker at the {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param msg the message string to be logged + * @param t A Throwable or null. + */ + public void debug(final Marker marker, final Message msg, final Throwable t) { + logger.logIfEnabled(FQCN, DEBUG, marker, msg, t); + } + + /** + * Logs a message object with the {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param message the message object to log. + */ + public void debug(final Marker marker, final Object message) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, (Throwable) null); + } + + /** + * Logs a message CharSequence with the {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param message the message CharSequence to log. + * @since Log4j-2.6 + */ + public void debug(final Marker marker, final CharSequence message) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, (Throwable) null); + } + + /** + * Logs a message at the {@code DEBUG} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param marker the marker data specific to this log statement + * @param message the message to log. + * @param t the exception to log, including its stack trace. + */ + public void debug(final Marker marker, final Object message, final Throwable t) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, t); + } + + /** + * Logs a message at the {@code DEBUG} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param marker the marker data specific to this log statement + * @param message the CharSequence to log. + * @param t the exception to log, including its stack trace. + * @since Log4j-2.6 + */ + public void debug(final Marker marker, final CharSequence message, final Throwable t) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, t); + } + + /** + * Logs a message object with the {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param message the message object to log. + */ + public void debug(final Marker marker, final String message) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, (Throwable) null); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param params parameters to the message. + * @see #getMessageFactory() + */ + public void debug(final Marker marker, final String message, final Object... params) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, params); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final Marker marker, final String message, final Object p0) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, p0); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final Marker marker, final String message, final Object p0, final Object p1) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, p0, p1); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final Marker marker, final String message, final Object p0, final Object p1, final Object p2) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, p0, p1, p2); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, p0, p1, p2, p3); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, p0, p1, p2, p3, p4); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, p0, p1, p2, p3, p4, p5); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, p0, p1, p2, p3, p4, p5, p6); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, p0, p1, p2, p3, p4, p5, p6, p7); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @param p8 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7, final Object p8) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, p0, p1, p2, p3, p4, p5, p6, p7, p8); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @param p8 parameter to the message. + * @param p9 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7, final Object p8, final Object p9) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); + } + + /** + * Logs a message at the {@code DEBUG} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param marker the marker data specific to this log statement + * @param message the message to log. + * @param t the exception to log, including its stack trace. + */ + public void debug(final Marker marker, final String message, final Throwable t) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, t); + } + + /** + * Logs the specified Message at the {@code DEBUG} level. + * + * @param msg the message string to be logged + */ + public void debug(final Message msg) { + logger.logIfEnabled(FQCN, DEBUG, null, msg, (Throwable) null); + } + + /** + * Logs the specified Message at the {@code DEBUG} level. + * + * @param msg the message string to be logged + * @param t A Throwable or null. + */ + public void debug(final Message msg, final Throwable t) { + logger.logIfEnabled(FQCN, DEBUG, null, msg, t); + } + + /** + * Logs a message object with the {@code DEBUG} level. + * + * @param message the message object to log. + */ + public void debug(final Object message) { + logger.logIfEnabled(FQCN, DEBUG, null, message, (Throwable) null); + } + + /** + * Logs a message at the {@code DEBUG} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param message the message to log. + * @param t the exception to log, including its stack trace. + */ + public void debug(final Object message, final Throwable t) { + logger.logIfEnabled(FQCN, DEBUG, null, message, t); + } + + /** + * Logs a message CharSequence with the {@code DEBUG} level. + * + * @param message the message CharSequence to log. + * @since Log4j-2.6 + */ + public void debug(final CharSequence message) { + logger.logIfEnabled(FQCN, DEBUG, null, message, (Throwable) null); + } + + /** + * Logs a CharSequence at the {@code DEBUG} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param message the CharSequence to log. + * @param t the exception to log, including its stack trace. + * @since Log4j-2.6 + */ + public void debug(final CharSequence message, final Throwable t) { + logger.logIfEnabled(FQCN, DEBUG, null, message, t); + } + + /** + * Logs a message object with the {@code DEBUG} level. + * + * @param message the message object to log. + */ + public void debug(final String message) { + logger.logIfEnabled(FQCN, DEBUG, null, message, (Throwable) null); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param message the message to log; the format depends on the message factory. + * @param params parameters to the message. + * @see #getMessageFactory() + */ + public void debug(final String message, final Object... params) { + logger.logIfEnabled(FQCN, DEBUG, null, message, params); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final String message, final Object p0) { + logger.logIfEnabled(FQCN, DEBUG, null, message, p0); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final String message, final Object p0, final Object p1) { + logger.logIfEnabled(FQCN, DEBUG, null, message, p0, p1); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final String message, final Object p0, final Object p1, final Object p2) { + logger.logIfEnabled(FQCN, DEBUG, null, message, p0, p1, p2); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final String message, final Object p0, final Object p1, final Object p2, + final Object p3) { + logger.logIfEnabled(FQCN, DEBUG, null, message, p0, p1, p2, p3); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4) { + logger.logIfEnabled(FQCN, DEBUG, null, message, p0, p1, p2, p3, p4); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5) { + logger.logIfEnabled(FQCN, DEBUG, null, message, p0, p1, p2, p3, p4, p5); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6) { + logger.logIfEnabled(FQCN, DEBUG, null, message, p0, p1, p2, p3, p4, p5, p6); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7) { + logger.logIfEnabled(FQCN, DEBUG, null, message, p0, p1, p2, p3, p4, p5, p6, p7); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @param p8 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7, final Object p8) { + logger.logIfEnabled(FQCN, DEBUG, null, message, p0, p1, p2, p3, p4, p5, p6, p7, p8); + } + + /** + * Logs a message with parameters at the {@code DEBUG} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @param p8 parameter to the message. + * @param p9 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void debug(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7, final Object p8, final Object p9) { + logger.logIfEnabled(FQCN, DEBUG, null, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); + } + + /** + * Logs a message at the {@code DEBUG} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param message the message to log. + * @param t the exception to log, including its stack trace. + */ + public void debug(final String message, final Throwable t) { + logger.logIfEnabled(FQCN, DEBUG, null, message, t); + } + + /** + * Logs a message which is only to be constructed if the logging level is the {@code DEBUG}level. + * + * @param msgSupplier A function, which when called, produces the desired log message; + * the format depends on the message factory. + * @since Log4j-2.4 + */ + public void debug(final Supplier msgSupplier) { + logger.logIfEnabled(FQCN, DEBUG, null, msgSupplier, (Throwable) null); + } + + /** + * Logs a message (only to be constructed if the logging level is the {@code DEBUG} + * level) including the stack trace of the {@link Throwable} t passed as parameter. + * + * @param msgSupplier A function, which when called, produces the desired log message; + * the format depends on the message factory. + * @param t the exception to log, including its stack trace. + * @since Log4j-2.4 + */ + public void debug(final Supplier msgSupplier, final Throwable t) { + logger.logIfEnabled(FQCN, DEBUG, null, msgSupplier, t); + } + + /** + * Logs a message which is only to be constructed if the logging level is the + * {@code DEBUG} level with the specified Marker. + * + * @param marker the marker data specific to this log statement + * @param msgSupplier A function, which when called, produces the desired log message; + * the format depends on the message factory. + * @since Log4j-2.4 + */ + public void debug(final Marker marker, final Supplier msgSupplier) { + logger.logIfEnabled(FQCN, DEBUG, marker, msgSupplier, (Throwable) null); + } + + /** + * Logs a message with parameters which are only to be constructed if the logging level is the + * {@code DEBUG} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param paramSuppliers An array of functions, which when called, produce the desired log message parameters. + * @since Log4j-2.4 + */ + public void debug(final Marker marker, final String message, final Supplier... paramSuppliers) { + logger.logIfEnabled(FQCN, DEBUG, marker, message, paramSuppliers); + } + + /** + * Logs a message (only to be constructed if the logging level is the {@code DEBUG} + * level) with the specified Marker and including the stack trace of the {@link Throwable} + * t passed as parameter. + * + * @param marker the marker data specific to this log statement + * @param msgSupplier A function, which when called, produces the desired log message; + * the format depends on the message factory. + * @param t A Throwable or null. + * @since Log4j-2.4 + */ + public void debug(final Marker marker, final Supplier msgSupplier, final Throwable t) { + logger.logIfEnabled(FQCN, DEBUG, marker, msgSupplier, t); + } + + /** + * Logs a message with parameters which are only to be constructed if the logging level is + * the {@code DEBUG} level. + * + * @param message the message to log; the format depends on the message factory. + * @param paramSuppliers An array of functions, which when called, produce the desired log message parameters. + * @since Log4j-2.4 + */ + public void debug(final String message, final Supplier... paramSuppliers) { + logger.logIfEnabled(FQCN, DEBUG, null, message, paramSuppliers); + } + + /** + * Logs a message which is only to be constructed if the logging level is the + * {@code DEBUG} level with the specified Marker. The {@code MessageSupplier} may or may + * not use the {@link MessageFactory} to construct the {@code Message}. + * + * @param marker the marker data specific to this log statement + * @param msgSupplier A function, which when called, produces the desired log message. + * @since Log4j-2.4 + */ + public void debug(final Marker marker, final MessageSupplier msgSupplier) { + logger.logIfEnabled(FQCN, DEBUG, marker, msgSupplier, (Throwable) null); + } + + /** + * Logs a message (only to be constructed if the logging level is the {@code DEBUG} + * level) with the specified Marker and including the stack trace of the {@link Throwable} + * t passed as parameter. The {@code MessageSupplier} may or may not use the + * {@link MessageFactory} to construct the {@code Message}. + * + * @param marker the marker data specific to this log statement + * @param msgSupplier A function, which when called, produces the desired log message. + * @param t A Throwable or null. + * @since Log4j-2.4 + */ + public void debug(final Marker marker, final MessageSupplier msgSupplier, final Throwable t) { + logger.logIfEnabled(FQCN, DEBUG, marker, msgSupplier, t); + } + + /** + * Logs a message which is only to be constructed if the logging level is the + * {@code DEBUG} level. The {@code MessageSupplier} may or may not use the + * {@link MessageFactory} to construct the {@code Message}. + * + * @param msgSupplier A function, which when called, produces the desired log message. + * @since Log4j-2.4 + */ + public void debug(final MessageSupplier msgSupplier) { + logger.logIfEnabled(FQCN, DEBUG, null, msgSupplier, (Throwable) null); + } + + /** + * Logs a message (only to be constructed if the logging level is the {@code DEBUG} + * level) including the stack trace of the {@link Throwable} t passed as parameter. + * The {@code MessageSupplier} may or may not use the {@link MessageFactory} to construct the + * {@code Message}. + * + * @param msgSupplier A function, which when called, produces the desired log message. + * @param t the exception to log, including its stack trace. + * @since Log4j-2.4 + */ + public void debug(final MessageSupplier msgSupplier, final Throwable t) { + logger.logIfEnabled(FQCN, DEBUG, null, msgSupplier, t); + } + + /** + * Logs a message with the specific Marker at the {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param msg the message string to be logged + */ + public void echo(final Marker marker, final Message msg) { + logger.logIfEnabled(FQCN, ECHO, marker, msg, (Throwable) null); + } + + /** + * Logs a message with the specific Marker at the {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param msg the message string to be logged + * @param t A Throwable or null. + */ + public void echo(final Marker marker, final Message msg, final Throwable t) { + logger.logIfEnabled(FQCN, ECHO, marker, msg, t); + } + + /** + * Logs a message object with the {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param message the message object to log. + */ + public void echo(final Marker marker, final Object message) { + logger.logIfEnabled(FQCN, ECHO, marker, message, (Throwable) null); + } + + /** + * Logs a message CharSequence with the {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param message the message CharSequence to log. + * @since Log4j-2.6 + */ + public void echo(final Marker marker, final CharSequence message) { + logger.logIfEnabled(FQCN, ECHO, marker, message, (Throwable) null); + } + + /** + * Logs a message at the {@code ECHO} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param marker the marker data specific to this log statement + * @param message the message to log. + * @param t the exception to log, including its stack trace. + */ + public void echo(final Marker marker, final Object message, final Throwable t) { + logger.logIfEnabled(FQCN, ECHO, marker, message, t); + } + + /** + * Logs a message at the {@code ECHO} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param marker the marker data specific to this log statement + * @param message the CharSequence to log. + * @param t the exception to log, including its stack trace. + * @since Log4j-2.6 + */ + public void echo(final Marker marker, final CharSequence message, final Throwable t) { + logger.logIfEnabled(FQCN, ECHO, marker, message, t); + } + + /** + * Logs a message object with the {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param message the message object to log. + */ + public void echo(final Marker marker, final String message) { + logger.logIfEnabled(FQCN, ECHO, marker, message, (Throwable) null); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param params parameters to the message. + * @see #getMessageFactory() + */ + public void echo(final Marker marker, final String message, final Object... params) { + logger.logIfEnabled(FQCN, ECHO, marker, message, params); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final Marker marker, final String message, final Object p0) { + logger.logIfEnabled(FQCN, ECHO, marker, message, p0); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final Marker marker, final String message, final Object p0, final Object p1) { + logger.logIfEnabled(FQCN, ECHO, marker, message, p0, p1); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final Marker marker, final String message, final Object p0, final Object p1, final Object p2) { + logger.logIfEnabled(FQCN, ECHO, marker, message, p0, p1, p2); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3) { + logger.logIfEnabled(FQCN, ECHO, marker, message, p0, p1, p2, p3); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4) { + logger.logIfEnabled(FQCN, ECHO, marker, message, p0, p1, p2, p3, p4); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5) { + logger.logIfEnabled(FQCN, ECHO, marker, message, p0, p1, p2, p3, p4, p5); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6) { + logger.logIfEnabled(FQCN, ECHO, marker, message, p0, p1, p2, p3, p4, p5, p6); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7) { + logger.logIfEnabled(FQCN, ECHO, marker, message, p0, p1, p2, p3, p4, p5, p6, p7); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @param p8 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7, final Object p8) { + logger.logIfEnabled(FQCN, ECHO, marker, message, p0, p1, p2, p3, p4, p5, p6, p7, p8); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @param p8 parameter to the message. + * @param p9 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final Marker marker, final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7, final Object p8, final Object p9) { + logger.logIfEnabled(FQCN, ECHO, marker, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); + } + + /** + * Logs a message at the {@code ECHO} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param marker the marker data specific to this log statement + * @param message the message to log. + * @param t the exception to log, including its stack trace. + */ + public void echo(final Marker marker, final String message, final Throwable t) { + logger.logIfEnabled(FQCN, ECHO, marker, message, t); + } + + /** + * Logs the specified Message at the {@code ECHO} level. + * + * @param msg the message string to be logged + */ + public void echo(final Message msg) { + logger.logIfEnabled(FQCN, ECHO, null, msg, (Throwable) null); + } + + /** + * Logs the specified Message at the {@code ECHO} level. + * + * @param msg the message string to be logged + * @param t A Throwable or null. + */ + public void echo(final Message msg, final Throwable t) { + logger.logIfEnabled(FQCN, ECHO, null, msg, t); + } + + /** + * Logs a message object with the {@code ECHO} level. + * + * @param message the message object to log. + */ + public void echo(final Object message) { + logger.logIfEnabled(FQCN, ECHO, null, message, (Throwable) null); + } + + /** + * Logs a message at the {@code ECHO} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param message the message to log. + * @param t the exception to log, including its stack trace. + */ + public void echo(final Object message, final Throwable t) { + logger.logIfEnabled(FQCN, ECHO, null, message, t); + } + + /** + * Logs a message CharSequence with the {@code ECHO} level. + * + * @param message the message CharSequence to log. + * @since Log4j-2.6 + */ + public void echo(final CharSequence message) { + logger.logIfEnabled(FQCN, ECHO, null, message, (Throwable) null); + } + + /** + * Logs a CharSequence at the {@code ECHO} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param message the CharSequence to log. + * @param t the exception to log, including its stack trace. + * @since Log4j-2.6 + */ + public void echo(final CharSequence message, final Throwable t) { + logger.logIfEnabled(FQCN, ECHO, null, message, t); + } + + /** + * Logs a message object with the {@code ECHO} level. + * + * @param message the message object to log. + */ + public void echo(final String message) { + logger.logIfEnabled(FQCN, ECHO, null, message, (Throwable) null); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param message the message to log; the format depends on the message factory. + * @param params parameters to the message. + * @see #getMessageFactory() + */ + public void echo(final String message, final Object... params) { + logger.logIfEnabled(FQCN, ECHO, null, message, params); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final String message, final Object p0) { + logger.logIfEnabled(FQCN, ECHO, null, message, p0); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final String message, final Object p0, final Object p1) { + logger.logIfEnabled(FQCN, ECHO, null, message, p0, p1); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final String message, final Object p0, final Object p1, final Object p2) { + logger.logIfEnabled(FQCN, ECHO, null, message, p0, p1, p2); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final String message, final Object p0, final Object p1, final Object p2, + final Object p3) { + logger.logIfEnabled(FQCN, ECHO, null, message, p0, p1, p2, p3); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4) { + logger.logIfEnabled(FQCN, ECHO, null, message, p0, p1, p2, p3, p4); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5) { + logger.logIfEnabled(FQCN, ECHO, null, message, p0, p1, p2, p3, p4, p5); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6) { + logger.logIfEnabled(FQCN, ECHO, null, message, p0, p1, p2, p3, p4, p5, p6); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7) { + logger.logIfEnabled(FQCN, ECHO, null, message, p0, p1, p2, p3, p4, p5, p6, p7); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @param p8 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7, final Object p8) { + logger.logIfEnabled(FQCN, ECHO, null, message, p0, p1, p2, p3, p4, p5, p6, p7, p8); + } + + /** + * Logs a message with parameters at the {@code ECHO} level. + * + * @param message the message to log; the format depends on the message factory. + * @param p0 parameter to the message. + * @param p1 parameter to the message. + * @param p2 parameter to the message. + * @param p3 parameter to the message. + * @param p4 parameter to the message. + * @param p5 parameter to the message. + * @param p6 parameter to the message. + * @param p7 parameter to the message. + * @param p8 parameter to the message. + * @param p9 parameter to the message. + * @see #getMessageFactory() + * @since Log4j-2.6 + */ + public void echo(final String message, final Object p0, final Object p1, final Object p2, + final Object p3, final Object p4, final Object p5, final Object p6, + final Object p7, final Object p8, final Object p9) { + logger.logIfEnabled(FQCN, ECHO, null, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); + } + + /** + * Logs a message at the {@code ECHO} level including the stack trace of + * the {@link Throwable} {@code t} passed as parameter. + * + * @param message the message to log. + * @param t the exception to log, including its stack trace. + */ + public void echo(final String message, final Throwable t) { + logger.logIfEnabled(FQCN, ECHO, null, message, t); + } + + /** + * Logs a message which is only to be constructed if the logging level is the {@code ECHO}level. + * + * @param msgSupplier A function, which when called, produces the desired log message; + * the format depends on the message factory. + * @since Log4j-2.4 + */ + public void echo(final Supplier msgSupplier) { + logger.logIfEnabled(FQCN, ECHO, null, msgSupplier, (Throwable) null); + } + + /** + * Logs a message (only to be constructed if the logging level is the {@code ECHO} + * level) including the stack trace of the {@link Throwable} t passed as parameter. + * + * @param msgSupplier A function, which when called, produces the desired log message; + * the format depends on the message factory. + * @param t the exception to log, including its stack trace. + * @since Log4j-2.4 + */ + public void echo(final Supplier msgSupplier, final Throwable t) { + logger.logIfEnabled(FQCN, ECHO, null, msgSupplier, t); + } + + /** + * Logs a message which is only to be constructed if the logging level is the + * {@code ECHO} level with the specified Marker. + * + * @param marker the marker data specific to this log statement + * @param msgSupplier A function, which when called, produces the desired log message; + * the format depends on the message factory. + * @since Log4j-2.4 + */ + public void echo(final Marker marker, final Supplier msgSupplier) { + logger.logIfEnabled(FQCN, ECHO, marker, msgSupplier, (Throwable) null); + } + + /** + * Logs a message with parameters which are only to be constructed if the logging level is the + * {@code ECHO} level. + * + * @param marker the marker data specific to this log statement + * @param message the message to log; the format depends on the message factory. + * @param paramSuppliers An array of functions, which when called, produce the desired log message parameters. + * @since Log4j-2.4 + */ + public void echo(final Marker marker, final String message, final Supplier... paramSuppliers) { + logger.logIfEnabled(FQCN, ECHO, marker, message, paramSuppliers); + } + + /** + * Logs a message (only to be constructed if the logging level is the {@code ECHO} + * level) with the specified Marker and including the stack trace of the {@link Throwable} + * t passed as parameter. + * + * @param marker the marker data specific to this log statement + * @param msgSupplier A function, which when called, produces the desired log message; + * the format depends on the message factory. + * @param t A Throwable or null. + * @since Log4j-2.4 + */ + public void echo(final Marker marker, final Supplier msgSupplier, final Throwable t) { + logger.logIfEnabled(FQCN, ECHO, marker, msgSupplier, t); + } + + /** + * Logs a message with parameters which are only to be constructed if the logging level is + * the {@code ECHO} level. + * + * @param message the message to log; the format depends on the message factory. + * @param paramSuppliers An array of functions, which when called, produce the desired log message parameters. + * @since Log4j-2.4 + */ + public void echo(final String message, final Supplier... paramSuppliers) { + logger.logIfEnabled(FQCN, ECHO, null, message, paramSuppliers); + } + + /** + * Logs a message which is only to be constructed if the logging level is the + * {@code ECHO} level with the specified Marker. The {@code MessageSupplier} may or may + * not use the {@link MessageFactory} to construct the {@code Message}. + * + * @param marker the marker data specific to this log statement + * @param msgSupplier A function, which when called, produces the desired log message. + * @since Log4j-2.4 + */ + public void echo(final Marker marker, final MessageSupplier msgSupplier) { + logger.logIfEnabled(FQCN, ECHO, marker, msgSupplier, (Throwable) null); + } + + /** + * Logs a message (only to be constructed if the logging level is the {@code ECHO} + * level) with the specified Marker and including the stack trace of the {@link Throwable} + * t passed as parameter. The {@code MessageSupplier} may or may not use the + * {@link MessageFactory} to construct the {@code Message}. + * + * @param marker the marker data specific to this log statement + * @param msgSupplier A function, which when called, produces the desired log message. + * @param t A Throwable or null. + * @since Log4j-2.4 + */ + public void echo(final Marker marker, final MessageSupplier msgSupplier, final Throwable t) { + logger.logIfEnabled(FQCN, ECHO, marker, msgSupplier, t); + } + + /** + * Logs a message which is only to be constructed if the logging level is the + * {@code ECHO} level. The {@code MessageSupplier} may or may not use the + * {@link MessageFactory} to construct the {@code Message}. + * + * @param msgSupplier A function, which when called, produces the desired log message. + * @since Log4j-2.4 + */ + public void echo(final MessageSupplier msgSupplier) { + logger.logIfEnabled(FQCN, ECHO, null, msgSupplier, (Throwable) null); + } + + /** + * Logs a message (only to be constructed if the logging level is the {@code ECHO} + * level) including the stack trace of the {@link Throwable} t passed as parameter. + * The {@code MessageSupplier} may or may not use the {@link MessageFactory} to construct the + * {@code Message}. + * + * @param msgSupplier A function, which when called, produces the desired log message. + * @param t the exception to log, including its stack trace. + * @since Log4j-2.4 + */ + public void echo(final MessageSupplier msgSupplier, final Throwable t) { + logger.logIfEnabled(FQCN, ECHO, null, msgSupplier, t); + } +} + diff --git a/src/main/java/org/qortal/at/QortalAtLoggerFactory.java b/src/main/java/org/qortal/at/QortalAtLoggerFactory.java new file mode 100644 index 00000000..19cbb3d9 --- /dev/null +++ b/src/main/java/org/qortal/at/QortalAtLoggerFactory.java @@ -0,0 +1,24 @@ +package org.qortal.at; + +import org.ciyam.at.AtLogger; + +public class QortalAtLoggerFactory implements org.ciyam.at.AtLoggerFactory { + + private static QortalAtLoggerFactory instance; + + private QortalAtLoggerFactory() { + } + + public static synchronized QortalAtLoggerFactory getInstance() { + if (instance == null) + instance = new QortalAtLoggerFactory(); + + return instance; + } + + @Override + public AtLogger create(final Class loggerName) { + return QortalAtLogger.create(loggerName); + } + +} diff --git a/src/main/java/org/qortal/at/QortalFunctionCode.java b/src/main/java/org/qortal/at/QortalFunctionCode.java index 0ca9f5d6..cf6b1cfd 100644 --- a/src/main/java/org/qortal/at/QortalFunctionCode.java +++ b/src/main/java/org/qortal/at/QortalFunctionCode.java @@ -4,6 +4,8 @@ import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.ciyam.at.ExecutionException; import org.ciyam.at.FunctionData; import org.ciyam.at.IllegalFunctionCodeException; @@ -30,9 +32,9 @@ public enum QortalFunctionCode { byte[] pkh = new byte[32]; // Copy PKH part of B to last 20 bytes - System.arraycopy(state.getB(), 32 - 20 - 4, pkh, 32 - 20, 20); + System.arraycopy(getB(state), 32 - 20 - 4, pkh, 32 - 20, 20); - state.getAPI().setB(state, pkh); + setB(state, pkh); } }, /** @@ -64,6 +66,8 @@ public enum QortalFunctionCode { public final int paramCount; public final boolean returnsValue; + private static final Logger LOGGER = LogManager.getLogger(QortalFunctionCode.class); + private static final Map map = Arrays.stream(QortalFunctionCode.values()) .collect(Collectors.toMap(functionCode -> functionCode.value, functionCode -> functionCode)); @@ -77,7 +81,7 @@ public enum QortalFunctionCode { return map.get((short) value); } - public void preExecuteCheck(int paramCount, boolean returnValueExpected, MachineState state, short rawFunctionCode) throws IllegalFunctionCodeException { + public void preExecuteCheck(int paramCount, boolean returnValueExpected, short rawFunctionCode) throws IllegalFunctionCodeException { if (paramCount != this.paramCount) throw new IllegalFunctionCodeException( "Passed paramCount (" + paramCount + ") does not match function's required paramCount (" + this.paramCount + ")"); @@ -100,7 +104,7 @@ public enum QortalFunctionCode { */ public void execute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException { // Check passed functionData against requirements of this function - preExecuteCheck(functionData.paramCount, functionData.returnValueExpected, state, rawFunctionCode); + preExecuteCheck(functionData.paramCount, functionData.returnValueExpected, rawFunctionCode); if (functionData.paramCount >= 1 && functionData.value1 == null) throw new IllegalFunctionCodeException("Passed value1 is null but function has paramCount of (" + this.paramCount + ")"); @@ -108,7 +112,7 @@ public enum QortalFunctionCode { if (functionData.paramCount == 2 && functionData.value2 == null) throw new IllegalFunctionCodeException("Passed value2 is null but function has paramCount of (" + this.paramCount + ")"); - state.getLogger().debug("Function \"" + this.name() + "\""); + LOGGER.debug(() -> String.format("Function \"%s\"", this.name())); postCheckExecute(functionData, state, rawFunctionCode); } @@ -119,7 +123,7 @@ public enum QortalFunctionCode { private static void convertAddressInB(byte addressPrefix, MachineState state) { byte[] addressNoChecksum = new byte[1 + 20]; addressNoChecksum[0] = addressPrefix; - System.arraycopy(state.getB(), 0, addressNoChecksum, 1, 20); + System.arraycopy(getB(state), 0, addressNoChecksum, 1, 20); byte[] checksum = Crypto.doubleDigest(addressNoChecksum); @@ -128,7 +132,17 @@ public enum QortalFunctionCode { System.arraycopy(addressNoChecksum, 0, address, 32 - 1 - 20 - 4, addressNoChecksum.length); System.arraycopy(checksum, 0, address, 32 - 4, 4); - state.getAPI().setB(state, address); + setB(state, address); + } + + private static byte[] getB(MachineState state) { + QortalATAPI api = (QortalATAPI) state.getAPI(); + return api.getB(state); + } + + private static void setB(MachineState state, byte[] bBytes) { + QortalATAPI api = (QortalATAPI) state.getAPI(); + api.setB(state, bBytes); } } diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index 4614c81c..37ae1bf1 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -506,8 +506,10 @@ public class Block { // Allocate cache for results List transactionsData = this.repository.getBlockRepository().getTransactionsFromSignature(this.blockData.getSignature()); - // The number of transactions fetched from repository should correspond with Block's transactionCount - if (transactionsData.size() != this.blockData.getTransactionCount()) + long nonAtTransactionCount = transactionsData.stream().filter(transactionData -> transactionData.getType() != TransactionType.AT).count(); + + // The number of non-AT transactions fetched from repository should correspond with Block's transactionCount + if (nonAtTransactionCount != this.blockData.getTransactionCount()) throw new IllegalStateException("Block's transactions from repository do not match block's transaction count"); this.transactions = new ArrayList<>(); @@ -540,8 +542,10 @@ public class Block { // Allocate cache for results List atStateData = this.repository.getATRepository().getBlockATStatesAtHeight(this.blockData.getHeight()); - // The number of AT states fetched from repository should correspond with Block's atCount - if (atStateData.size() != this.blockData.getATCount()) + // The number of non-initial AT states fetched from repository should correspond with Block's atCount. + // We exclude initial AT states created by processing DEPLOY_AT transactions as they are never serialized and so not included in block's AT count. + int nonInitialCount = (int) atStateData.stream().filter(atState -> !atState.isInitial()).count(); + if (nonInitialCount != this.blockData.getATCount()) throw new IllegalStateException("Block's AT states from repository do not match block's AT count"); this.atStates = atStateData; @@ -1182,7 +1186,7 @@ public class Block { // Run each AT, appends AT-Transactions and corresponding AT states, to our lists for (ATData atData : executableATs) { AT at = new AT(this.repository, atData); - List atTransactions = at.run(this.blockData.getTimestamp()); + List atTransactions = at.run(this.blockData.getHeight(), this.blockData.getTimestamp()); allAtTransactions.addAll(atTransactions); @@ -1192,14 +1196,16 @@ public class Block { this.ourAtFees = this.ourAtFees.add(atStateData.getFees()); } + // AT Transactions never need approval + allAtTransactions.forEach(transaction -> transaction.getTransactionData().setApprovalStatus(ApprovalStatus.NOT_REQUIRED)); + // Prepend our entire AT-Transactions/states to block's transactions this.transactions.addAll(0, allAtTransactions); // Re-sort this.transactions.sort(Transaction.getComparator()); - // Update transaction count - this.blockData.setTransactionCount(this.blockData.getTransactionCount() + 1); + // AT Transactions do not affect block's transaction count // We've added transactions, so recalculate transactions signature calcTransactionsSignature(); @@ -1408,13 +1414,17 @@ public class Block { protected void processAtFeesAndStates() throws DataException { ATRepository atRepository = this.repository.getATRepository(); - for (ATStateData atState : this.getATStates()) { - Account atAccount = new Account(this.repository, atState.getATAddress()); + for (ATStateData atStateData : this.getATStates()) { + Account atAccount = new Account(this.repository, atStateData.getATAddress()); // Subtract AT-generated fees from AT accounts - atAccount.setConfirmedBalance(Asset.QORT, atAccount.getConfirmedBalance(Asset.QORT).subtract(atState.getFees())); + atAccount.setConfirmedBalance(Asset.QORT, atAccount.getConfirmedBalance(Asset.QORT).subtract(atStateData.getFees())); - atRepository.save(atState); + // Update AT info with latest state + ATData atData = atRepository.fromATAddress(atStateData.getATAddress()); + + AT at = new AT(repository, atData, atStateData); + at.update(this.blockData.getHeight(), this.blockData.getTimestamp()); } } @@ -1568,15 +1578,18 @@ public class Block { protected void orphanAtFeesAndStates() throws DataException { ATRepository atRepository = this.repository.getATRepository(); - for (ATStateData atState : this.getATStates()) { - Account atAccount = new Account(this.repository, atState.getATAddress()); + for (ATStateData atStateData : this.getATStates()) { + Account atAccount = new Account(this.repository, atStateData.getATAddress()); // Return AT-generated fees to AT accounts - atAccount.setConfirmedBalance(Asset.QORT, atAccount.getConfirmedBalance(Asset.QORT).add(atState.getFees())); - } + atAccount.setConfirmedBalance(Asset.QORT, atAccount.getConfirmedBalance(Asset.QORT).add(atStateData.getFees())); - // Delete ATStateData for this height - atRepository.deleteATStates(this.blockData.getHeight()); + // Revert AT info to prior values + ATData atData = atRepository.fromATAddress(atStateData.getATAddress()); + + AT at = new AT(repository, atData, atStateData); + at.revert(this.blockData.getHeight(), this.blockData.getTimestamp()); + } } protected void decreaseAccountLevels() throws DataException { diff --git a/src/main/java/org/qortal/block/BlockChain.java b/src/main/java/org/qortal/block/BlockChain.java index 5d4c50fc..bc9909d9 100644 --- a/src/main/java/org/qortal/block/BlockChain.java +++ b/src/main/java/org/qortal/block/BlockChain.java @@ -471,7 +471,7 @@ public class BlockChain { this.unitFee = this.unitFee.setScale(8); this.minFeePerByte = this.unitFee.divide(this.maxBytesPerUnitFee, MathContext.DECIMAL32); - this.ciyamAtSettings.feePerStep.setScale(8); + this.ciyamAtSettings.feePerStep = this.ciyamAtSettings.feePerStep.setScale(8); // Pre-calculate cumulative blocks required for each level int cumulativeBlocks = 0; diff --git a/src/main/java/org/qortal/block/BlockMinter.java b/src/main/java/org/qortal/block/BlockMinter.java index 3adbef3d..2026f5f7 100644 --- a/src/main/java/org/qortal/block/BlockMinter.java +++ b/src/main/java/org/qortal/block/BlockMinter.java @@ -337,11 +337,9 @@ public class BlockMinter extends Thread { this.interrupt(); } - public static void mintTestingBlock(Repository repository, PrivateKeyAccount... mintingAndOnlineAccounts) throws DataException { - if (!BlockChain.getInstance().isTestChain()) { - LOGGER.warn("Ignoring attempt to mint testing block for non-test chain!"); - return; - } + public static Block mintTestingBlock(Repository repository, PrivateKeyAccount... mintingAndOnlineAccounts) throws DataException { + if (!BlockChain.getInstance().isTestChain()) + throw new DataException("Ignoring attempt to mint testing block for non-test chain!"); // Ensure mintingAccount is 'online' so blocks can be minted Controller.getInstance().ensureTestingAccountsOnline(mintingAndOnlineAccounts); @@ -372,6 +370,8 @@ public class BlockMinter extends Thread { LOGGER.info(String.format("Minted new test block: %d", newBlock.getBlockData().getHeight())); repository.saveChanges(); + + return newBlock; } finally { blockchainLock.unlock(); } diff --git a/src/main/java/org/qortal/crosschain/BTCACCT.java b/src/main/java/org/qortal/crosschain/BTCACCT.java index 1ee78f2c..2789fb3d 100644 --- a/src/main/java/org/qortal/crosschain/BTCACCT.java +++ b/src/main/java/org/qortal/crosschain/BTCACCT.java @@ -1,7 +1,10 @@ package org.qortal.crosschain; +import static org.ciyam.at.OpCode.calcOffset; + import java.math.BigDecimal; import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.function.Function; import org.bitcoinj.core.Coin; @@ -17,9 +20,11 @@ import org.bitcoinj.script.ScriptBuilder; import org.bitcoinj.script.ScriptChunk; import org.bitcoinj.script.ScriptOpCodes; import org.ciyam.at.API; +import org.ciyam.at.CompilationException; import org.ciyam.at.FunctionCode; import org.ciyam.at.MachineState; import org.ciyam.at.OpCode; +import org.qortal.account.Account; import org.qortal.utils.Base58; import org.qortal.utils.BitTwiddling; @@ -29,6 +34,23 @@ import com.google.common.primitives.Bytes; public class BTCACCT { public static final Coin DEFAULT_BTC_FEE = Coin.valueOf(1000L); // 0.00001000 BTC + public static final byte[] CODE_BYTES_HASH = HashCode.fromString("750012c7ae79d85a97e64e94c467c7791dd76cf3050b864f3166635a21d767c6").asBytes(); // SHA256 of AT code bytes + + public static class AtConstants { + public final byte[] secretHash; + public final BigDecimal initialPayout; + public final BigDecimal redeemPayout; + public final String recipient; + public final int refundMinutes; + + public AtConstants(byte[] secretHash, BigDecimal initialPayout, BigDecimal redeemPayout, String recipient, int refundMinutes) { + this.secretHash = secretHash; + this.initialPayout = initialPayout; + this.redeemPayout = redeemPayout; + this.recipient = recipient; + this.refundMinutes = refundMinutes; + } + } /* * OP_TUCK (to copy public key to before signature) @@ -172,7 +194,8 @@ public class BTCACCT { return buildP2shTransaction(redeemAmount, redeemKey, fundingOutput, redeemScriptBytes, null, redeemSigScriptBuilder); } - public static byte[] buildQortalAT(byte[] secretHash, String recipientQortalAddress, long refundMinutes, BigDecimal initialPayout) { + @SuppressWarnings("unused") + public static byte[] buildQortalAT(byte[] secretHash, String recipientQortalAddress, int refundMinutes, BigDecimal initialPayout, BigDecimal redeemPayout) { // Labels for data segment addresses int addrCounter = 0; // Constants (with corresponding dataByteBuffer.put*() calls below) @@ -185,10 +208,15 @@ public class BTCACCT { final int addrAddressPart3 = addrCounter++; final int addrAddressPart4 = addrCounter++; final int addrRefundMinutes = addrCounter++; + final int addrInitialPayoutAmount = addrCounter++; + final int addrRedeemPayoutAmount = addrCounter++; + final int addrExpectedTxType = addrCounter++; + final int addrHashIndex = addrCounter++; + final int addrAddressIndex = addrCounter++; + final int addrAddressTempIndex = addrCounter++; final int addrHashTempIndex = addrCounter++; final int addrHashTempLength = addrCounter++; - final int addrInitialPayoutAmount = addrCounter++; - final int addrExpectedTxType = addrCounter++; + final int addrEndOfConstants = addrCounter; // Variables final int addrRefundTimestamp = addrCounter++; final int addrLastTimestamp = addrCounter++; @@ -220,136 +248,175 @@ public class BTCACCT { assert dataByteBuffer.position() == addrRefundMinutes * MachineState.VALUE_SIZE : "addrRefundMinutes incorrect"; dataByteBuffer.putLong(refundMinutes); + // Initial payout amount + assert dataByteBuffer.position() == addrInitialPayoutAmount * MachineState.VALUE_SIZE : "addrInitialPayoutAmount incorrect"; + dataByteBuffer.putLong(initialPayout.unscaledValue().longValue()); + + // Redeem payout amount + assert dataByteBuffer.position() == addrRedeemPayoutAmount * MachineState.VALUE_SIZE : "addrRedeemPayoutAmount incorrect"; + dataByteBuffer.putLong(redeemPayout.unscaledValue().longValue()); + + // We're only interested in MESSAGE transactions + assert dataByteBuffer.position() == addrExpectedTxType * MachineState.VALUE_SIZE : "addrExpectedTxType incorrect"; + dataByteBuffer.putLong(API.ATTransactionType.MESSAGE.value); + + // Index into data segment of hash, used by GET_B_IND + assert dataByteBuffer.position() == addrHashIndex * MachineState.VALUE_SIZE : "addrHashIndex incorrect"; + dataByteBuffer.putLong(addrHashPart1); + + // Index into data segment of recipient address, used by SET_B_IND + assert dataByteBuffer.position() == addrAddressIndex * MachineState.VALUE_SIZE : "addrAddressIndex incorrect"; + dataByteBuffer.putLong(addrAddressPart1); + + // Index into data segment of (temporary) transaction's sender's address, used by GET_B_IND + assert dataByteBuffer.position() == addrAddressTempIndex * MachineState.VALUE_SIZE : "addrAddressTempIndex incorrect"; + dataByteBuffer.putLong(addrAddressTemp1); + // Source location and length for hashing any passed secret assert dataByteBuffer.position() == addrHashTempIndex * MachineState.VALUE_SIZE : "addrHashTempIndex incorrect"; dataByteBuffer.putLong(addrHashTemp1); assert dataByteBuffer.position() == addrHashTempLength * MachineState.VALUE_SIZE : "addrHashTempLength incorrect"; dataByteBuffer.putLong(32L); - // Initial payout amount - assert dataByteBuffer.position() == addrInitialPayoutAmount * MachineState.VALUE_SIZE : "addrInitialPayoutAmount incorrect"; - dataByteBuffer.putLong(initialPayout.unscaledValue().longValue()); - - // We're only interested in MESSAGE transactions - assert dataByteBuffer.position() == addrExpectedTxType * MachineState.VALUE_SIZE : "addrExpectedTxType incorrect"; - dataByteBuffer.putLong(API.ATTransactionType.MESSAGE.value); + assert dataByteBuffer.position() == addrEndOfConstants * MachineState.VALUE_SIZE : "dataByteBuffer position not at end of constants"; // Code labels - final int addrTxLoop = 0x0036; - final int addrCheckTx = 0x004b; - final int addrRefund = 0x00c6; - final int addrEndOfCode = 0x00cd; + Integer labelTxLoop = null; + Integer labelRefund = null; + Integer labelCheckTx = null; - int tempPC; - ByteBuffer codeByteBuffer = ByteBuffer.allocate(addrEndOfCode * 1); + ByteBuffer codeByteBuffer = ByteBuffer.allocate(512); - /* Initialization */ + // Two-pass version + for (int pass = 0; pass < 2; ++pass) { + codeByteBuffer.clear(); - // Use AT creation 'timestamp' as starting point for finding transactions sent to AT - codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_CREATION_TIMESTAMP.value).putInt(addrLastTimestamp); - // Calculate refund 'timestamp' by adding minutes to above 'timestamp', then save into addrRefundTimestamp - codeByteBuffer.put(OpCode.EXT_FUN_RET_DAT_2.value).putShort(FunctionCode.ADD_MINUTES_TO_TIMESTAMP.value).putInt(addrRefundTimestamp) - .putInt(addrLastTimestamp).putInt(addrRefundMinutes); + try { + /* Initialization */ - // Load recipient's address into B register - codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B_IND.value).putInt(addrAddressPart1); - // Send initial payment to recipient so they have enough funds to message AT if all goes well - codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.PAY_TO_ADDRESS_IN_B.value).putInt(addrInitialPayoutAmount); + // Use AT creation 'timestamp' as starting point for finding transactions sent to AT + codeByteBuffer.put(OpCode.EXT_FUN_RET.compile(FunctionCode.GET_CREATION_TIMESTAMP, addrLastTimestamp)); + // Calculate refund 'timestamp' by adding minutes to above 'timestamp', then save into addrRefundTimestamp + codeByteBuffer.put(OpCode.EXT_FUN_RET_DAT_2.compile(FunctionCode.ADD_MINUTES_TO_TIMESTAMP, addrRefundTimestamp, addrLastTimestamp, addrRefundMinutes)); - // Set restart position to after this opcode - codeByteBuffer.put(OpCode.SET_PCS.value); + // Load recipient's address into B register + codeByteBuffer.put(OpCode.EXT_FUN_DAT.compile(FunctionCode.SET_B_IND, addrAddressIndex)); + // Send initial payment to recipient so they have enough funds to message AT if all goes well + codeByteBuffer.put(OpCode.EXT_FUN_DAT.compile(FunctionCode.PAY_TO_ADDRESS_IN_B, addrInitialPayoutAmount)); - /* Main loop */ + // Set restart position to after this opcode + codeByteBuffer.put(OpCode.SET_PCS.compile()); - // Fetch current block 'timestamp' - codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_BLOCK_TIMESTAMP.value).putInt(addrBlockTimestamp); - // If we're past refund 'timestamp' then go refund everything back to AT creator - tempPC = codeByteBuffer.position(); - codeByteBuffer.put(OpCode.BGE_DAT.value).putInt(addrBlockTimestamp).putInt(addrRefundTimestamp).put((byte) (addrRefund - tempPC)); + /* Main loop */ - /* Transaction processing loop */ - assert codeByteBuffer.position() == addrTxLoop : "addrTxLoop incorrect"; + // Fetch current block 'timestamp' + codeByteBuffer.put(OpCode.EXT_FUN_RET.compile(FunctionCode.GET_BLOCK_TIMESTAMP, addrBlockTimestamp)); + // If we're not past refund 'timestamp' then look for next transaction + codeByteBuffer.put(OpCode.BLT_DAT.compile(addrBlockTimestamp, addrRefundTimestamp, calcOffset(codeByteBuffer, labelTxLoop))); + // We're past refund 'timestamp' so go refund everything back to AT creator + codeByteBuffer.put(OpCode.JMP_ADR.compile(labelRefund == null ? 0 : labelRefund)); - // Find next transaction to this AT since the last one (if any) - codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.PUT_TX_AFTER_TIMESTAMP_INTO_A.value).putInt(addrLastTimestamp); - // If no transaction found, A will be zero. If A is zero, set addrComparator to 1, otherwise 0. - codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.CHECK_A_IS_ZERO.value).putInt(addrComparator); - // If addrComparator is zero (i.e. A is non-zero, transaction was found) then branch to addrCheckTx - tempPC = codeByteBuffer.position(); - codeByteBuffer.put(OpCode.BZR_DAT.value).putInt(addrComparator).put((byte) (addrCheckTx - tempPC)); - // Stop and wait for next block - codeByteBuffer.put(OpCode.STP_IMD.value); + /* Transaction processing loop */ + labelTxLoop = codeByteBuffer.position(); - /* Check transaction */ - assert codeByteBuffer.position() == addrCheckTx : "addrCheckTx incorrect"; + // Find next transaction to this AT since the last one (if any) + codeByteBuffer.put(OpCode.EXT_FUN_DAT.compile(FunctionCode.PUT_TX_AFTER_TIMESTAMP_INTO_A, addrLastTimestamp)); + // If no transaction found, A will be zero. If A is zero, set addrComparator to 1, otherwise 0. + codeByteBuffer.put(OpCode.EXT_FUN_RET.compile(FunctionCode.CHECK_A_IS_ZERO, addrComparator)); + // If addrComparator is zero (i.e. A is non-zero, transaction was found) then go check transaction + codeByteBuffer.put(OpCode.BZR_DAT.compile(addrComparator, calcOffset(codeByteBuffer, labelCheckTx))); + // Stop and wait for next block + codeByteBuffer.put(OpCode.STP_IMD.compile()); - // Update our 'last found transaction's timestamp' using 'timestamp' from transaction - codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_TIMESTAMP_FROM_TX_IN_A.value).putInt(addrLastTimestamp); - // Extract transaction type (message/payment) from transaction and save type in addrTxType - codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_TYPE_FROM_TX_IN_A.value).putInt(addrTxType); - // If transaction type is not MESSAGE type then go look for another transaction - tempPC = codeByteBuffer.position(); - codeByteBuffer.put(OpCode.BNE_DAT.value).putInt(addrTxType).putInt(addrExpectedTxType).put((byte) (addrTxLoop - tempPC)); + /* Check transaction */ + labelCheckTx = codeByteBuffer.position(); - /* Check transaction's sender */ + // Update our 'last found transaction's timestamp' using 'timestamp' from transaction + codeByteBuffer.put(OpCode.EXT_FUN_RET.compile(FunctionCode.GET_TIMESTAMP_FROM_TX_IN_A, addrLastTimestamp)); + // Extract transaction type (message/payment) from transaction and save type in addrTxType + codeByteBuffer.put(OpCode.EXT_FUN_RET.compile(FunctionCode.GET_TYPE_FROM_TX_IN_A, addrTxType)); + // If transaction type is not MESSAGE type then go look for another transaction + codeByteBuffer.put(OpCode.BNE_DAT.compile(addrTxType, addrExpectedTxType, calcOffset(codeByteBuffer, labelTxLoop))); - // Extract sender address from transaction into B register - codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PUT_ADDRESS_FROM_TX_IN_A_INTO_B.value); - // Save B register into data segment starting at addrAddressTemp1 - codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_B_IND.value).putInt(addrAddressTemp1); - // Compare each part of transaction's sender's address with expected address. If they don't match, look for another transaction. - tempPC = codeByteBuffer.position(); - codeByteBuffer.put(OpCode.BNE_DAT.value).putInt(addrAddressTemp1).putInt(addrAddressPart1).put((byte) (addrTxLoop - tempPC)); - tempPC = codeByteBuffer.position(); - codeByteBuffer.put(OpCode.BNE_DAT.value).putInt(addrAddressTemp2).putInt(addrAddressPart2).put((byte) (addrTxLoop - tempPC)); - tempPC = codeByteBuffer.position(); - codeByteBuffer.put(OpCode.BNE_DAT.value).putInt(addrAddressTemp3).putInt(addrAddressPart3).put((byte) (addrTxLoop - tempPC)); - tempPC = codeByteBuffer.position(); - codeByteBuffer.put(OpCode.BNE_DAT.value).putInt(addrAddressTemp4).putInt(addrAddressPart4).put((byte) (addrTxLoop - tempPC)); + /* Check transaction's sender */ - /* Check 'secret' in transaction's message */ + // Extract sender address from transaction into B register + codeByteBuffer.put(OpCode.EXT_FUN.compile(FunctionCode.PUT_ADDRESS_FROM_TX_IN_A_INTO_B)); + // Save B register into data segment starting at addrAddressTemp1 (as pointed to by addrAddressTempIndex) + codeByteBuffer.put(OpCode.EXT_FUN_DAT.compile(FunctionCode.GET_B_IND, addrAddressTempIndex)); + // Compare each part of transaction's sender's address with expected address. If they don't match, look for another transaction. + codeByteBuffer.put(OpCode.BNE_DAT.compile(addrAddressTemp1, addrAddressPart1, calcOffset(codeByteBuffer, labelTxLoop))); + codeByteBuffer.put(OpCode.BNE_DAT.compile(addrAddressTemp2, addrAddressPart2, calcOffset(codeByteBuffer, labelTxLoop))); + codeByteBuffer.put(OpCode.BNE_DAT.compile(addrAddressTemp3, addrAddressPart3, calcOffset(codeByteBuffer, labelTxLoop))); + codeByteBuffer.put(OpCode.BNE_DAT.compile(addrAddressTemp4, addrAddressPart4, calcOffset(codeByteBuffer, labelTxLoop))); - // Extract message from transaction into B register - codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PUT_MESSAGE_FROM_TX_IN_A_INTO_B.value); - // Save B register into data segment starting at addrHashTemp1 - codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_B_IND.value).putInt(addrHashTemp1); - // Load B register with expected hash result - codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B_IND.value).putInt(addrHashPart1); - // Perform HASH160 using source data at addrHashTemp1 through addrHashTemp4. (Location and length specified via addrHashTempIndex and addrHashTemplength). - // Save the equality result (1 if they match, 0 otherwise) into addrComparator. - codeByteBuffer.put(OpCode.EXT_FUN_RET_DAT_2.value).putShort(FunctionCode.CHECK_HASH160_WITH_B.value).putInt(addrComparator).putInt(addrHashTempIndex).putInt(addrHashTempLength); - // If hashes don't match, addrComparator will be zero so go find another transaction - tempPC = codeByteBuffer.position(); - codeByteBuffer.put(OpCode.BZR_DAT.value).putInt(addrComparator).put((byte) (addrTxLoop - tempPC)); + /* Check 'secret' in transaction's message */ - /* Success! Pay balance to intended recipient */ + // Extract message from transaction into B register + codeByteBuffer.put(OpCode.EXT_FUN.compile(FunctionCode.PUT_MESSAGE_FROM_TX_IN_A_INTO_B)); + // Save B register into data segment starting at addrHashTemp1 (as pointed to by addrHashTempIndex) + codeByteBuffer.put(OpCode.EXT_FUN_DAT.compile(FunctionCode.GET_B_IND, addrHashTempIndex)); + // Load B register with expected hash result (as pointed to by addrHashIndex) + codeByteBuffer.put(OpCode.EXT_FUN_DAT.compile(FunctionCode.SET_B_IND, addrHashIndex)); + // Perform HASH160 using source data at addrHashTemp1 through addrHashTemp4. (Location and length specified via addrHashTempIndex and addrHashTemplength). + // Save the equality result (1 if they match, 0 otherwise) into addrComparator. + codeByteBuffer.put(OpCode.EXT_FUN_RET_DAT_2.compile(FunctionCode.CHECK_HASH160_WITH_B, addrComparator, addrHashTempIndex, addrHashTempLength)); + // If hashes don't match, addrComparator will be zero so go find another transaction + codeByteBuffer.put(OpCode.BZR_DAT.compile(addrComparator, calcOffset(codeByteBuffer, labelTxLoop))); - // Load B register with intended recipient address. - codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B_IND.value).putInt(addrAddressPart1); - // Pay AT's balance to recipient - codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PAY_ALL_TO_ADDRESS_IN_B.value); - // We're finished forever - codeByteBuffer.put(OpCode.FIN_IMD.value); + /* Success! Pay arranged amount to intended recipient */ - /* Refund balance back to AT creator */ - assert codeByteBuffer.position() == addrRefund : "addrRefund incorrect"; + // Load B register with intended recipient address (as pointed to by addrAddressIndex) + codeByteBuffer.put(OpCode.EXT_FUN_DAT.compile(FunctionCode.SET_B_IND, addrAddressIndex)); + // Pay AT's balance to recipient + codeByteBuffer.put(OpCode.EXT_FUN_DAT.compile(FunctionCode.PAY_TO_ADDRESS_IN_B, addrRedeemPayoutAmount)); + // We're finished forever + codeByteBuffer.put(OpCode.FIN_IMD.compile()); - // Load B register with AT creator's address. - codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PUT_CREATOR_INTO_B.value); - // Pay AT's balance back to AT's creator. - codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PAY_ALL_TO_ADDRESS_IN_B.value); - // We're finished forever - codeByteBuffer.put(OpCode.FIN_IMD.value); + /* Refund balance back to AT creator */ + labelRefund = codeByteBuffer.position(); - // end-of-code - assert codeByteBuffer.position() == addrEndOfCode : "addrEndOfCode incorrect"; + // Load B register with AT creator's address. + codeByteBuffer.put(OpCode.EXT_FUN.compile(FunctionCode.PUT_CREATOR_INTO_B)); + // Pay AT's balance back to AT's creator. + codeByteBuffer.put(OpCode.EXT_FUN.compile(FunctionCode.PAY_ALL_TO_ADDRESS_IN_B)); + // We're finished forever + codeByteBuffer.put(OpCode.FIN_IMD.compile()); + } catch (CompilationException e) { + throw new IllegalStateException("Unable to compile BTC-QORT ACCT?", e); + } + } + + codeByteBuffer.flip(); + + byte[] codeBytes = new byte[codeByteBuffer.limit()]; + codeByteBuffer.get(codeBytes); final short ciyamAtVersion = 2; final short numCallStackPages = 0; final short numUserStackPages = 0; final long minActivationAmount = 0L; - return MachineState.toCreationBytes(ciyamAtVersion, codeByteBuffer.array(), dataByteBuffer.array(), numCallStackPages, numUserStackPages, minActivationAmount); + return MachineState.toCreationBytes(ciyamAtVersion, codeBytes, dataByteBuffer.array(), numCallStackPages, numUserStackPages, minActivationAmount); + } + + public static AtConstants extractAtConstants(byte[] dataBytes) { + ByteBuffer dataByteBuffer = ByteBuffer.wrap(dataBytes); + + byte[] secretHash = new byte[32]; + dataByteBuffer.get(secretHash); + + byte[] addressBytes = new byte[32]; + dataByteBuffer.get(addressBytes); + String recipient = Base58.encode(Arrays.copyOf(addressBytes, Account.ADDRESS_LENGTH)); + + int refundMinutes = (int) dataByteBuffer.getLong(); + + BigDecimal initialPayout = BigDecimal.valueOf(dataByteBuffer.getLong(), 8); + + BigDecimal redeemPayout = BigDecimal.valueOf(dataByteBuffer.getLong(), 8); + + return new AtConstants(secretHash, initialPayout, redeemPayout, recipient, refundMinutes); } } diff --git a/src/main/java/org/qortal/crypto/Crypto.java b/src/main/java/org/qortal/crypto/Crypto.java index c940c6ab..6c1e7ba9 100644 --- a/src/main/java/org/qortal/crypto/Crypto.java +++ b/src/main/java/org/qortal/crypto/Crypto.java @@ -59,6 +59,18 @@ public class Crypto { return Bytes.concat(digest, digest); } + /** Returns RMD160(SHA256(data)) */ + public static byte[] hash160(byte[] data) { + byte[] interim = digest(data); + + try { + MessageDigest md160 = MessageDigest.getInstance("RIPEMD160"); + return md160.digest(interim); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("RIPEMD160 message digest not available"); + } + } + @SuppressWarnings("deprecation") private static String toAddress(byte addressVersion, byte[] input) { // SHA2-256 input to create new data and of known size diff --git a/src/main/java/org/qortal/data/at/ATStateData.java b/src/main/java/org/qortal/data/at/ATStateData.java index aa8cf4cd..1ea9e527 100644 --- a/src/main/java/org/qortal/data/at/ATStateData.java +++ b/src/main/java/org/qortal/data/at/ATStateData.java @@ -11,32 +11,38 @@ public class ATStateData { private byte[] stateData; private byte[] stateHash; private BigDecimal fees; + private boolean isInitial; // Constructors /** Create new ATStateData */ - public ATStateData(String ATAddress, Integer height, Long creation, byte[] stateData, byte[] stateHash, BigDecimal fees) { + public ATStateData(String ATAddress, Integer height, Long creation, byte[] stateData, byte[] stateHash, BigDecimal fees, boolean isInitial) { this.ATAddress = ATAddress; this.height = height; this.creation = creation; this.stateData = stateData; this.stateHash = stateHash; this.fees = fees; + this.isInitial = isInitial; } /** For recreating per-block ATStateData from repository where not all info is needed */ - public ATStateData(String ATAddress, int height, byte[] stateHash, BigDecimal fees) { - this(ATAddress, height, null, null, stateHash, fees); + public ATStateData(String ATAddress, int height, byte[] stateHash, BigDecimal fees, boolean isInitial) { + this(ATAddress, height, null, null, stateHash, fees, isInitial); } /** For creating ATStateData from serialized bytes when we don't have all the info */ public ATStateData(String ATAddress, byte[] stateHash) { - this(ATAddress, null, null, null, stateHash, null); + // This won't ever be initial AT state from deployment as that's never serialized over the network, + // but generated when the DeployAtTransaction is processed locally. + this(ATAddress, null, null, null, stateHash, null, false); } /** For creating ATStateData from serialized bytes when we don't have all the info */ public ATStateData(String ATAddress, byte[] stateHash, BigDecimal fees) { - this(ATAddress, null, null, null, stateHash, fees); + // This won't ever be initial AT state from deployment as that's never serialized over the network, + // but generated when the DeployAtTransaction is processed locally. + this(ATAddress, null, null, null, stateHash, fees, false); } // Getters / setters @@ -70,4 +76,8 @@ public class ATStateData { return this.fees; } + public boolean isInitial() { + return this.isInitial; + } + } diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBATRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBATRepository.java index 59955bb3..dd85a241 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBATRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBATRepository.java @@ -1,11 +1,12 @@ package org.qortal.repository.hsqldb; +import static org.qortal.repository.hsqldb.HSQLDBRepository.getZonedTimestampMilli; +import static org.qortal.repository.hsqldb.HSQLDBRepository.toOffsetDateTime; + import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Timestamp; import java.util.ArrayList; -import java.util.Calendar; import java.util.List; import org.qortal.data.at.ATData; @@ -25,14 +26,18 @@ public class HSQLDBATRepository implements ATRepository { @Override public ATData fromATAddress(String atAddress) throws DataException { - final String sql = "SELECT creator, creation, version, asset_id, code_bytes, is_sleeping, sleep_until_height, is_finished, had_fatal_error, is_frozen, frozen_balance FROM ATs WHERE AT_address = ?"; + String sql = "SELECT creator, creation, version, asset_id, code_bytes, " + + "is_sleeping, sleep_until_height, is_finished, had_fatal_error, " + + "is_frozen, frozen_balance " + + "FROM ATs " + + "WHERE AT_address = ? LIMIT 1"; try (ResultSet resultSet = this.repository.checkedExecute(sql, atAddress)) { if (resultSet == null) return null; byte[] creatorPublicKey = resultSet.getBytes(1); - long creation = resultSet.getTimestamp(2, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); + long creation = getZonedTimestampMilli(resultSet, 2); int version = resultSet.getInt(3); long assetId = resultSet.getLong(4); byte[] codeBytes = resultSet.getBytes(5); // Actually BLOB @@ -66,9 +71,14 @@ public class HSQLDBATRepository implements ATRepository { @Override public List getAllExecutableATs() throws DataException { - final String sql = "SELECT AT_address, creator, creation, version, asset_id, code_bytes, is_sleeping, sleep_until_height, had_fatal_error, is_frozen, frozen_balance FROM ATs WHERE is_finished = false ORDER BY creation ASC"; + String sql = "SELECT AT_address, creator, creation, version, asset_id, code_bytes, " + + "is_sleeping, sleep_until_height, had_fatal_error, " + + "is_frozen, frozen_balance " + + "FROM ATs " + + "WHERE is_finished = false " + + "ORDER BY creation ASC"; - List executableATs = new ArrayList(); + List executableATs = new ArrayList<>(); try (ResultSet resultSet = this.repository.checkedExecute(sql)) { if (resultSet == null) @@ -79,7 +89,7 @@ public class HSQLDBATRepository implements ATRepository { do { String atAddress = resultSet.getString(1); byte[] creatorPublicKey = resultSet.getBytes(2); - long creation = resultSet.getTimestamp(3, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); + long creation = getZonedTimestampMilli(resultSet, 3); int version = resultSet.getInt(4); long assetId = resultSet.getLong(5); byte[] codeBytes = resultSet.getBytes(6); // Actually BLOB @@ -108,7 +118,12 @@ public class HSQLDBATRepository implements ATRepository { @Override public Integer getATCreationBlockHeight(String atAddress) throws DataException { - final String sql = "SELECT height from DeployATTransactions JOIN BlockTransactions ON transaction_signature = signature JOIN Blocks ON Blocks.signature = block_signature WHERE AT_address = ?"; + String sql = "SELECT height " + + "FROM DeployATTransactions " + + "JOIN BlockTransactions ON transaction_signature = signature " + + "JOIN Blocks ON Blocks.signature = block_signature " + + "WHERE AT_address = ? " + + "LIMIT 1"; try (ResultSet resultSet = this.repository.checkedExecute(sql, atAddress)) { if (resultSet == null) @@ -124,7 +139,7 @@ public class HSQLDBATRepository implements ATRepository { public void save(ATData atData) throws DataException { HSQLDBSaver saveHelper = new HSQLDBSaver("ATs"); - saveHelper.bind("AT_address", atData.getATAddress()).bind("creator", atData.getCreatorPublicKey()).bind("creation", new Timestamp(atData.getCreation())) + saveHelper.bind("AT_address", atData.getATAddress()).bind("creator", atData.getCreatorPublicKey()).bind("creation", toOffsetDateTime(atData.getCreation())) .bind("version", atData.getVersion()).bind("asset_id", atData.getAssetId()).bind("code_bytes", atData.getCodeBytes()) .bind("is_sleeping", atData.getIsSleeping()).bind("sleep_until_height", atData.getSleepUntilHeight()) .bind("is_finished", atData.getIsFinished()).bind("had_fatal_error", atData.getHadFatalError()).bind("is_frozen", atData.getIsFrozen()) @@ -151,17 +166,22 @@ public class HSQLDBATRepository implements ATRepository { @Override public ATStateData getATStateAtHeight(String atAddress, int height) throws DataException { - try (ResultSet resultSet = this.repository - .checkedExecute("SELECT creation, state_data, state_hash, fees FROM ATStates WHERE AT_address = ? AND height = ?", atAddress, height)) { + String sql = "SELECT creation, state_data, state_hash, fees, is_initial " + + "FROM ATStates " + + "WHERE AT_address = ? AND height = ? " + + "LIMIT 1"; + + try (ResultSet resultSet = this.repository.checkedExecute(sql, atAddress, height)) { if (resultSet == null) return null; - long creation = resultSet.getTimestamp(1, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); + long creation = getZonedTimestampMilli(resultSet, 1); byte[] stateData = resultSet.getBytes(2); // Actually BLOB byte[] stateHash = resultSet.getBytes(3); BigDecimal fees = resultSet.getBigDecimal(4); + boolean isInitial = resultSet.getBoolean(5); - return new ATStateData(atAddress, height, creation, stateData, stateHash, fees); + return new ATStateData(atAddress, height, creation, stateData, stateHash, fees, isInitial); } catch (SQLException e) { throw new DataException("Unable to fetch AT state from repository", e); } @@ -169,18 +189,24 @@ public class HSQLDBATRepository implements ATRepository { @Override public ATStateData getLatestATState(String atAddress) throws DataException { - try (ResultSet resultSet = this.repository - .checkedExecute("SELECT height, creation, state_data, state_hash, fees FROM ATStates WHERE AT_address = ? ORDER BY height DESC", atAddress)) { + String sql = "SELECT height, creation, state_data, state_hash, fees, is_initial " + + "FROM ATStates " + + "WHERE AT_address = ? " + + "ORDER BY height DESC " + + "LIMIT 1"; + + try (ResultSet resultSet = this.repository.checkedExecute(sql, atAddress)) { if (resultSet == null) return null; int height = resultSet.getInt(1); - long creation = resultSet.getTimestamp(2, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); + long creation = getZonedTimestampMilli(resultSet, 2); byte[] stateData = resultSet.getBytes(3); // Actually BLOB byte[] stateHash = resultSet.getBytes(4); BigDecimal fees = resultSet.getBigDecimal(5); + boolean isInitial = resultSet.getBoolean(6); - return new ATStateData(atAddress, height, creation, stateData, stateHash, fees); + return new ATStateData(atAddress, height, creation, stateData, stateHash, fees, isInitial); } catch (SQLException e) { throw new DataException("Unable to fetch latest AT state from repository", e); } @@ -188,10 +214,14 @@ public class HSQLDBATRepository implements ATRepository { @Override public List getBlockATStatesAtHeight(int height) throws DataException { + String sql = "SELECT AT_address, state_hash, fees, is_initial " + + "FROM ATStates " + + "WHERE height = ? " + + "ORDER BY creation ASC"; + List atStates = new ArrayList<>(); - try (ResultSet resultSet = this.repository.checkedExecute("SELECT AT_address, state_hash, fees FROM ATStates WHERE height = ? ORDER BY creation ASC", - height)) { + try (ResultSet resultSet = this.repository.checkedExecute(sql, height)) { if (resultSet == null) return atStates; // No atStates in this block @@ -200,8 +230,9 @@ public class HSQLDBATRepository implements ATRepository { String atAddress = resultSet.getString(1); byte[] stateHash = resultSet.getBytes(2); BigDecimal fees = resultSet.getBigDecimal(3); + boolean isInitial = resultSet.getBoolean(4); - ATStateData atStateData = new ATStateData(atAddress, height, stateHash, fees); + ATStateData atStateData = new ATStateData(atAddress, height, stateHash, fees, isInitial); atStates.add(atStateData); } while (resultSet.next()); } catch (SQLException e) { @@ -220,8 +251,9 @@ public class HSQLDBATRepository implements ATRepository { HSQLDBSaver saveHelper = new HSQLDBSaver("ATStates"); saveHelper.bind("AT_address", atStateData.getATAddress()).bind("height", atStateData.getHeight()) - .bind("creation", new Timestamp(atStateData.getCreation())).bind("state_data", atStateData.getStateData()) - .bind("state_hash", atStateData.getStateHash()).bind("fees", atStateData.getFees()); + .bind("creation", toOffsetDateTime(atStateData.getCreation())).bind("state_data", atStateData.getStateData()) + .bind("state_hash", atStateData.getStateHash()).bind("fees", atStateData.getFees()) + .bind("is_initial", atStateData.isInitial()); try { saveHelper.execute(this.repository); diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java index 4f88842b..562f5030 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java @@ -975,6 +975,11 @@ public class HSQLDBDatabaseUpdates { stmt.execute("CHECKPOINT DEFRAG"); break; + case 71: + // Add flag to AT state data to indicate 'initial deployment state' + stmt.execute("ALTER TABLE ATStates ADD COLUMN is_initial BOOLEAN NOT NULL DEFAULT TRUE"); + break; + default: // nothing to do return false; diff --git a/src/main/java/org/qortal/transaction/AtTransaction.java b/src/main/java/org/qortal/transaction/AtTransaction.java index 15ae12e3..f55dfaf8 100644 --- a/src/main/java/org/qortal/transaction/AtTransaction.java +++ b/src/main/java/org/qortal/transaction/AtTransaction.java @@ -178,6 +178,8 @@ public class AtTransaction extends Transaction { @Override public void processReferencesAndFees() throws DataException { + getATAccount().setLastReference(this.atTransactionData.getSignature()); + if (this.atTransactionData.getAmount() != null) { Account recipient = getRecipient(); long assetId = this.atTransactionData.getAssetId(); @@ -211,6 +213,8 @@ public class AtTransaction extends Transaction { @Override public void orphanReferencesAndFees() throws DataException { + getATAccount().setLastReference(this.atTransactionData.getReference()); + if (this.atTransactionData.getAmount() != null) { Account recipient = getRecipient(); diff --git a/src/main/java/org/qortal/transaction/DeployAtTransaction.java b/src/main/java/org/qortal/transaction/DeployAtTransaction.java index 8995b626..75d51dc0 100644 --- a/src/main/java/org/qortal/transaction/DeployAtTransaction.java +++ b/src/main/java/org/qortal/transaction/DeployAtTransaction.java @@ -13,6 +13,7 @@ import org.qortal.account.Account; import org.qortal.asset.Asset; import org.qortal.at.AT; import org.qortal.at.QortalATAPI; +import org.qortal.at.QortalAtLoggerFactory; import org.qortal.block.BlockChain; import org.qortal.crypto.Crypto; import org.qortal.data.asset.AssetData; @@ -212,9 +213,10 @@ public class DeployAtTransaction extends Transaction { long blockTimestamp = Timestamp.toLong(height, 0); QortalATAPI api = new QortalATAPI(repository, skeletonAtData, blockTimestamp); + QortalAtLoggerFactory loggerFactory = QortalAtLoggerFactory.getInstance(); try { - new MachineState(api, deployATTransactionData.getCreationBytes()); + new MachineState(api, loggerFactory, deployATTransactionData.getCreationBytes()); } catch (IllegalArgumentException e) { // Not valid return ValidationResult.INVALID_CREATION_BYTES; diff --git a/src/main/java/org/qortal/transaction/Transaction.java b/src/main/java/org/qortal/transaction/Transaction.java index bc8974f1..6329e486 100644 --- a/src/main/java/org/qortal/transaction/Transaction.java +++ b/src/main/java/org/qortal/transaction/Transaction.java @@ -1020,11 +1020,16 @@ public abstract class Transaction { // AT transactions come before non-AT transactions if (td1.getType() == TransactionType.AT && td2.getType() != TransactionType.AT) return -1; + // Non-AT transactions come after AT transactions if (td1.getType() != TransactionType.AT && td2.getType() == TransactionType.AT) return 1; - // Both transactions are either AT or non-AT so compare timestamps + // If both transactions are AT type, then preserve existing ordering. + if (td1.getType() == TransactionType.AT) + return 0; + + // Both transactions are non-AT so compare timestamps int result = Long.compare(td1.getTimestamp(), td2.getTimestamp()); if (result == 0) diff --git a/src/test/java/org/qortal/test/apps/orphan.java b/src/test/java/org/qortal/test/apps/orphan.java index 58853e55..f847f98d 100644 --- a/src/test/java/org/qortal/test/apps/orphan.java +++ b/src/test/java/org/qortal/test/apps/orphan.java @@ -13,17 +13,23 @@ import org.qortal.settings.Settings; public class orphan { public static void main(String[] args) { - if (args.length == 0) { - System.err.println("usage: orphan "); + if (args.length < 1 || args.length > 2) { + System.err.println("usage: orphan [] "); System.exit(1); } - int targetHeight = Integer.parseInt(args[0]); - Security.insertProviderAt(new BouncyCastleProvider(), 0); - // Load/check settings, which potentially sets up blockchain config, etc. - Settings.getInstance(); + int argIndex = 0; + + if (args.length > 1) { + Settings.fileInstance(args[argIndex++]); + } else { + // Load/check settings, which potentially sets up blockchain config, etc. + Settings.getInstance(); + } + + int targetHeight = Integer.parseInt(args[argIndex]); try { RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Controller.getRepositoryUrl()); diff --git a/src/test/java/org/qortal/test/btcacct/AtTests.java b/src/test/java/org/qortal/test/btcacct/AtTests.java new file mode 100644 index 00000000..67968af6 --- /dev/null +++ b/src/test/java/org/qortal/test/btcacct/AtTests.java @@ -0,0 +1,403 @@ +package org.qortal.test.btcacct; + +import static org.junit.Assert.assertTrue; + +import java.math.BigDecimal; +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 org.ciyam.at.MachineState; +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.at.QortalAtLoggerFactory; +import org.qortal.crosschain.BTCACCT; +import org.qortal.crypto.Crypto; +import org.qortal.data.at.ATData; +import org.qortal.data.at.ATStateData; +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 com.google.common.hash.HashCode; + +public class AtTests extends Common { + + public static final byte[] secret = "This string is exactly 32 bytes!".getBytes(); + public static final byte[] secretHash = Crypto.hash160(secret); // daf59884b4d1aec8c1b17102530909ee43c0151a + public static final int refundTimeout = 10; // blocks + public static final BigDecimal initialPayout = new BigDecimal("0.001").setScale(8); + public static final BigDecimal redeemAmount = new BigDecimal("80.4020").setScale(8); + public static final BigDecimal fundingAmount = new BigDecimal("123.456").setScale(8); + + @Before + public void beforeTest() throws DataException { + Common.useDefaultSettings(); + } + + @Test + public void testCompile() { + String redeemAddress = Common.getTestAccount(null, "chloe").getAddress(); + + byte[] creationBytes = BTCACCT.buildQortalAT(secretHash, redeemAddress, refundTimeout, initialPayout, redeemAmount); + System.out.println("CIYAM 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 recipient = Common.getTestAccount(repository, "dilbert"); + + BigDecimal deployersInitialBalance = deployer.getBalance(Asset.QORT); + BigDecimal recipientsInitialBalance = recipient.getBalance(Asset.QORT); + + DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, recipient); + + BigDecimal expectedBalance = deployersInitialBalance.subtract(fundingAmount).subtract(deployAtTransaction.getTransactionData().getFee()); + BigDecimal actualBalance = deployer.getBalance(Asset.QORT); + + Common.assertEqualBigDecimals("Deployer's post-deployment balance incorrect", expectedBalance, actualBalance); + + expectedBalance = fundingAmount; + actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT); + + Common.assertEqualBigDecimals("AT's post-deployment balance incorrect", expectedBalance, actualBalance); + + expectedBalance = recipientsInitialBalance; + actualBalance = recipient.getBalance(Asset.QORT); + + Common.assertEqualBigDecimals("Recipient's post-deployment balance incorrect", expectedBalance, actualBalance); + + // Test orphaning + BlockUtils.orphanLastBlock(repository); + + expectedBalance = deployersInitialBalance; + actualBalance = deployer.getBalance(Asset.QORT); + + Common.assertEqualBigDecimals("Deployer's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance); + + expectedBalance = BigDecimal.ZERO; + actualBalance = deployAtTransaction.getATAccount().getConfirmedBalance(Asset.QORT); + + Common.assertEqualBigDecimals("AT's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance); + + expectedBalance = recipientsInitialBalance; + actualBalance = recipient.getBalance(Asset.QORT); + + Common.assertEqualBigDecimals("Recipient's post-orphan/pre-deployment balance incorrect", expectedBalance, actualBalance); + } + } + + @SuppressWarnings("unused") + @Test + public void testInitialPayment() throws DataException { + try (final Repository repository = RepositoryManager.getRepository()) { + PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe"); + PrivateKeyAccount recipient = Common.getTestAccount(repository, "dilbert"); + + BigDecimal deployersInitialBalance = deployer.getBalance(Asset.QORT); + BigDecimal recipientsInitialBalance = recipient.getBalance(Asset.QORT); + + DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, recipient); + + // Initial payment should happen 1st block after deployment + BlockUtils.mintBlock(repository); + + BigDecimal expectedBalance = recipientsInitialBalance.add(initialPayout); + BigDecimal actualBalance = recipient.getConfirmedBalance(Asset.QORT); + + Common.assertEqualBigDecimals("Recipient's post-initial-payout balance incorrect", expectedBalance, actualBalance); + + // Test orphaning + BlockUtils.orphanLastBlock(repository); + + expectedBalance = recipientsInitialBalance; + actualBalance = recipient.getBalance(Asset.QORT); + + Common.assertEqualBigDecimals("Recipient's pre-initial-payout balance incorrect", expectedBalance, actualBalance); + } + } + + @SuppressWarnings("unused") + @Test + public void testAutomaticRefund() throws DataException { + try (final Repository repository = RepositoryManager.getRepository()) { + PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe"); + PrivateKeyAccount recipient = Common.getTestAccount(repository, "dilbert"); + + BigDecimal deployersInitialBalance = deployer.getBalance(Asset.QORT); + BigDecimal recipientsInitialBalance = recipient.getBalance(Asset.QORT); + + DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, recipient); + + BigDecimal deployAtFee = deployAtTransaction.getTransactionData().getFee(); + BigDecimal deployersPostDeploymentBalance = deployersInitialBalance.subtract(fundingAmount).subtract(deployAtFee); + + checkAtRefund(repository, deployer, deployersInitialBalance, deployAtFee); + + // Test orphaning + BlockUtils.orphanLastBlock(repository); + + BigDecimal expectedBalance = deployersPostDeploymentBalance; + BigDecimal actualBalance = deployer.getBalance(Asset.QORT); + + Common.assertEqualBigDecimals("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 recipient = Common.getTestAccount(repository, "dilbert"); + + BigDecimal deployersInitialBalance = deployer.getBalance(Asset.QORT); + BigDecimal recipientsInitialBalance = recipient.getBalance(Asset.QORT); + + DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, recipient); + Account at = deployAtTransaction.getATAccount(); + String atAddress = at.getAddress(); + + // Send correct secret to AT + MessageTransaction messageTransaction = sendSecret(repository, recipient, secret, atAddress); + + // AT should send funds in the next block + ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress); + BlockUtils.mintBlock(repository); + + BigDecimal expectedBalance = recipientsInitialBalance.add(initialPayout).subtract(messageTransaction.getTransactionData().getFee()).add(redeemAmount); + BigDecimal actualBalance = recipient.getConfirmedBalance(Asset.QORT); + + Common.assertEqualBigDecimals("Recipent's post-redeem balance incorrect", expectedBalance, actualBalance); + + // Orphan redeem + BlockUtils.orphanLastBlock(repository); + + expectedBalance = recipientsInitialBalance.add(initialPayout).subtract(messageTransaction.getTransactionData().getFee()); + actualBalance = recipient.getBalance(Asset.QORT); + + Common.assertEqualBigDecimals("Recipent'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 recipient = Common.getTestAccount(repository, "dilbert"); + PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob"); + + BigDecimal deployersInitialBalance = deployer.getBalance(Asset.QORT); + BigDecimal recipientsInitialBalance = recipient.getBalance(Asset.QORT); + + DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, recipient); + BigDecimal deployAtFee = deployAtTransaction.getTransactionData().getFee(); + + Account at = deployAtTransaction.getATAccount(); + String atAddress = at.getAddress(); + + // Send correct secret to AT, but from wrong account + MessageTransaction messageTransaction = sendSecret(repository, bystander, secret, atAddress); + + // AT should NOT send funds in the next block + ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress); + BlockUtils.mintBlock(repository); + + BigDecimal expectedBalance = recipientsInitialBalance.add(initialPayout); + BigDecimal actualBalance = recipient.getConfirmedBalance(Asset.QORT); + + Common.assertEqualBigDecimals("Recipent's balance incorrect", expectedBalance, actualBalance); + + checkAtRefund(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 recipient = Common.getTestAccount(repository, "dilbert"); + + BigDecimal deployersInitialBalance = deployer.getBalance(Asset.QORT); + BigDecimal recipientsInitialBalance = recipient.getBalance(Asset.QORT); + + DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, recipient); + BigDecimal deployAtFee = deployAtTransaction.getTransactionData().getFee(); + + Account at = deployAtTransaction.getATAccount(); + String atAddress = at.getAddress(); + + // Send correct secret to AT, but from wrong account + byte[] wrongSecret = Crypto.digest(secret); + MessageTransaction messageTransaction = sendSecret(repository, recipient, wrongSecret, atAddress); + + // AT should NOT send funds in the next block + ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress); + BlockUtils.mintBlock(repository); + + BigDecimal expectedBalance = recipientsInitialBalance.add(initialPayout).subtract(messageTransaction.getTransactionData().getFee()); + BigDecimal actualBalance = recipient.getConfirmedBalance(Asset.QORT); + + Common.assertEqualBigDecimals("Recipent's balance incorrect", expectedBalance, actualBalance); + + checkAtRefund(repository, deployer, deployersInitialBalance, deployAtFee); + } + } + + @SuppressWarnings("unused") + @Test + public void testDescribeDeployed() throws DataException { + try (final Repository repository = RepositoryManager.getRepository()) { + PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe"); + PrivateKeyAccount recipient = Common.getTestAccount(repository, "dilbert"); + + BigDecimal deployersInitialBalance = deployer.getBalance(Asset.QORT); + BigDecimal recipientsInitialBalance = recipient.getBalance(Asset.QORT); + + DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, recipient); + + 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, BTCACCT.CODE_BYTES_HASH)) + continue; + + ATStateData atStateData = repository.getATRepository().getLatestATState(atAddress); + byte[] stateData = atStateData.getStateData(); + + QortalAtLoggerFactory loggerFactory = QortalAtLoggerFactory.getInstance(); + byte[] dataBytes = MachineState.extractDataBytes(loggerFactory, stateData); + + BTCACCT.AtConstants atConstants = BTCACCT.extractAtConstants(dataBytes); + + long autoRefundTimestamp = atData.getCreation() + atConstants.refundMinutes * 60 * 1000L; + + String autoRefundString = LocalDateTime.ofInstant(Instant.ofEpochMilli(autoRefundTimestamp), ZoneOffset.UTC).format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)); + System.out.println(String.format("%s:\n" + + "\tcreator: %s,\n" + + "\tHASH160 of secret: %s,\n" + + "\trecipient: %s,\n" + + "\tinitial payout: %s QORT,\n" + + "\tredeem payout: %s QORT,\n" + + "\tauto-refund at: %s (local time)", + atAddress, + Crypto.toAddress(atData.getCreatorPublicKey()), + HashCode.fromBytes(atConstants.secretHash).toString().substring(0, 40), + atConstants.recipient, + atConstants.initialPayout.toPlainString(), + atConstants.redeemPayout.toPlainString(), + autoRefundString)); + } + } + } + + private DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer, Account recipient) throws DataException { + byte[] creationBytes = BTCACCT.buildQortalAT(secretHash, recipient.getAddress(), refundTimeout, initialPayout, redeemAmount); + + 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); + } + + BigDecimal fee = BigDecimal.ZERO; + String name = "QORT-BTC cross-chain trade"; + String description = String.format("Qortal-Bitcoin cross-chain trade between %s and %s", deployer.getAddress(), recipient.getAddress()); + 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 sendSecret(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); + } + + BigDecimal fee = BigDecimal.ZERO; + BigDecimal amount = BigDecimal.ZERO; + + BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, sender.getPublicKey(), fee, null); + TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, 4, recipient, Asset.QORT, amount, data, false, false); + + MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData); + + fee = messageTransaction.calcRecommendedFee(); + messageTransactionData.setFee(fee); + + TransactionUtils.signAndMint(repository, messageTransactionData, sender); + + return messageTransaction; + } + + private void checkAtRefund(Repository repository, Account deployer, BigDecimal deployersInitialBalance, BigDecimal deployAtFee) throws DataException { + BigDecimal deployersPostDeploymentBalance = deployersInitialBalance.subtract(fundingAmount).subtract(deployAtFee); + + // 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 + BigDecimal expectedMinimumBalance = deployersPostDeploymentBalance; + BigDecimal expectedMaximumBalance = deployersInitialBalance.subtract(deployAtFee).subtract(initialPayout); + + BigDecimal actualBalance = deployer.getConfirmedBalance(Asset.QORT); + + assertTrue(String.format("Deployer's balance %s should be above minimum %s", actualBalance.toPlainString(), expectedMinimumBalance.toPlainString()), actualBalance.compareTo(expectedMinimumBalance) > 0); + assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance.toPlainString(), expectedMaximumBalance.toPlainString()), actualBalance.compareTo(expectedMaximumBalance) < 0); + } + +} diff --git a/src/test/java/org/qortal/test/btcacct/BuildP2SH.java b/src/test/java/org/qortal/test/btcacct/BuildP2SH.java index 23c7ab7c..f03fb8b5 100644 --- a/src/test/java/org/qortal/test/btcacct/BuildP2SH.java +++ b/src/test/java/org/qortal/test/btcacct/BuildP2SH.java @@ -34,7 +34,7 @@ public class BuildP2SH { + "mrTDPdM15cFWJC4g223BXX5snicfVJBx6M \\\n" + "\t0.00008642 \\\n" + "\tn2N5VKrzq39nmuefZwp3wBiF4icdXX2B6o \\\n" - + "\td1b64100879ad93ceaa3c15929b6fe8550f54967 \\\n" + + "\tdaf59884b4d1aec8c1b17102530909ee43c0151a \\\n" + "\t1585920000")); System.exit(1); } diff --git a/src/test/java/org/qortal/test/btcacct/CheckP2SH.java b/src/test/java/org/qortal/test/btcacct/CheckP2SH.java index befa1963..e61a031e 100644 --- a/src/test/java/org/qortal/test/btcacct/CheckP2SH.java +++ b/src/test/java/org/qortal/test/btcacct/CheckP2SH.java @@ -37,7 +37,7 @@ public class CheckP2SH { + "mrTDPdM15cFWJC4g223BXX5snicfVJBx6M \\\n" + "\t0.00008642 \\\n" + "\tn2N5VKrzq39nmuefZwp3wBiF4icdXX2B6o \\\n" - + "\td1b64100879ad93ceaa3c15929b6fe8550f54967 \\\n" + + "\tdaf59884b4d1aec8c1b17102530909ee43c0151a \\\n" + "\t1585920000")); System.exit(1); } diff --git a/src/test/java/org/qortal/test/btcacct/DeployAT.java b/src/test/java/org/qortal/test/btcacct/DeployAT.java index 98e2daa7..488de43f 100644 --- a/src/test/java/org/qortal/test/btcacct/DeployAT.java +++ b/src/test/java/org/qortal/test/btcacct/DeployAT.java @@ -32,34 +32,37 @@ import com.google.common.hash.HashCode; public class DeployAT { + public static final BigDecimal atFundingExtra = new BigDecimal("0.2").setScale(8); + private static void usage(String error) { if (error != null) System.err.println(error); - System.err.println(String.format("usage: DeployAT ()")); + System.err.println(String.format("usage: DeployAT [ []]")); System.err.println(String.format("example: DeployAT " + "AdTd9SUEYSdTW8mgK3Gu72K97bCHGdUwi2VvLNjUohot \\\n" + "\t3.1415 \\\n" + "\tQgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v \\\n" - + "\td1b64100879ad93ceaa3c15929b6fe8550f54967 \\\n" + + "\tdaf59884b4d1aec8c1b17102530909ee43c0151a \\\n" + "\t1585920000 \\\n" + "\t0.0001")); System.exit(1); } public static void main(String[] args) { - if (args.length < 5 || args.length > 6) + if (args.length < 5 || args.length > 7) usage(null); Security.insertProviderAt(new BouncyCastleProvider(), 0); Settings.fileInstance("settings-test.json"); byte[] refundPrivateKey = null; - BigDecimal qortAmount = null; + BigDecimal redeemAmount = null; String redeemAddress = null; byte[] secretHash = null; int lockTime = 0; BigDecimal initialPayout = BigDecimal.ZERO.setScale(8); + BigDecimal fundingAmount = null; int argIndex = 0; try { @@ -67,8 +70,8 @@ public class DeployAT { if (refundPrivateKey.length != 32) usage("Refund private key must be 32 bytes"); - qortAmount = new BigDecimal(args[argIndex++]); - if (qortAmount.signum() <= 0) + redeemAmount = new BigDecimal(args[argIndex++]).setScale(8); + if (redeemAmount.signum() <= 0) usage("QORT amount must be positive"); redeemAddress = args[argIndex++]; @@ -83,6 +86,13 @@ public class DeployAT { if (args.length > argIndex) initialPayout = new BigDecimal(args[argIndex++]).setScale(8); + + if (args.length > argIndex) { + fundingAmount = new BigDecimal(args[argIndex++]).setScale(8); + + if (fundingAmount.compareTo(redeemAmount) <= 0) + usage("AT funding amount must be greater than QORT redeem amount"); + } } catch (IllegalArgumentException e) { usage(String.format("Invalid argument %d: %s", argIndex, e.getMessage())); } @@ -100,7 +110,11 @@ public class DeployAT { PrivateKeyAccount refundAccount = new PrivateKeyAccount(repository, refundPrivateKey); System.out.println(String.format("Refund Qortal address: %s", refundAccount.getAddress())); - System.out.println(String.format("QORT amount (INCLUDING FEES): %s", qortAmount.toPlainString())); + System.out.println(String.format("QORT redeem amount: %s", redeemAmount.toPlainString())); + + if (fundingAmount == null) + fundingAmount = redeemAmount.add(atFundingExtra); + System.out.println(String.format("AT funding amount: %s", fundingAmount.toPlainString())); System.out.println(String.format("HASH160 of secret: %s", HashCode.fromBytes(secretHash))); @@ -117,7 +131,7 @@ public class DeployAT { final int BLOCK_TIME = 60; // seconds final int refundTimeout = (lockTime - (int) (System.currentTimeMillis() / 1000L)) / BLOCK_TIME; - byte[] creationBytes = BTCACCT.buildQortalAT(secretHash, redeemAddress, refundTimeout, initialPayout); + byte[] creationBytes = BTCACCT.buildQortalAT(secretHash, redeemAddress, refundTimeout, initialPayout, fundingAmount); System.out.println("CIYAM AT creation bytes: " + HashCode.fromBytes(creationBytes).toString()); long txTimestamp = System.currentTimeMillis(); @@ -135,7 +149,7 @@ public class DeployAT { String tags = "QORT-BTC ACCT"; BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, refundAccount.getPublicKey(), fee, null); - TransactionData deployAtTransactionData = new DeployAtTransactionData(baseTransactionData, name, description, atType, tags, creationBytes, qortAmount, Asset.QORT); + TransactionData deployAtTransactionData = new DeployAtTransactionData(baseTransactionData, name, description, atType, tags, creationBytes, redeemAmount, Asset.QORT); Transaction deployAtTransaction = new DeployAtTransaction(repository, deployAtTransactionData); diff --git a/src/test/java/org/qortal/test/btcacct/Redeem.java b/src/test/java/org/qortal/test/btcacct/Redeem.java index a4907813..ad403cd2 100644 --- a/src/test/java/org/qortal/test/btcacct/Redeem.java +++ b/src/test/java/org/qortal/test/btcacct/Redeem.java @@ -44,7 +44,7 @@ public class Redeem { + "2NEZboTLhBDPPQciR7sExBhy3TsDi7wV3Cv \\\n" + "\tmrTDPdM15cFWJC4g223BXX5snicfVJBx6M \\\n" + "\tec199a4abc9d3bf024349e397535dfee9d287e174aeabae94237eb03a0118c03 \\\n" - + "\t736563726574 \\\n" + + "\t5468697320737472696e672069732065786163746c7920333220627974657321 \\\n" + "\t1585920000")); System.exit(1); } diff --git a/src/test/java/org/qortal/test/btcacct/Refund.java b/src/test/java/org/qortal/test/btcacct/Refund.java index f22a436a..14752d8d 100644 --- a/src/test/java/org/qortal/test/btcacct/Refund.java +++ b/src/test/java/org/qortal/test/btcacct/Refund.java @@ -44,7 +44,7 @@ public class Refund { + "2NEZboTLhBDPPQciR7sExBhy3TsDi7wV3Cv \\\n" + "\tef027fb5828c5e201eaf6de4cd3b0b340d16a191ef848cd691f35ef8f727358c9c01b576fb7e \\\n" + "\tn2N5VKrzq39nmuefZwp3wBiF4icdXX2B6o \\\n" - + "\td1b64100879ad93ceaa3c15929b6fe8550f54967 \\\n" + + "\tdaf59884b4d1aec8c1b17102530909ee43c0151a \\\n" + "\t1585920000")); System.exit(1); } diff --git a/src/test/java/org/qortal/test/common/BlockUtils.java b/src/test/java/org/qortal/test/common/BlockUtils.java index 2bc02931..40f31083 100644 --- a/src/test/java/org/qortal/test/common/BlockUtils.java +++ b/src/test/java/org/qortal/test/common/BlockUtils.java @@ -17,9 +17,9 @@ public class BlockUtils { private static final Logger LOGGER = LogManager.getLogger(BlockUtils.class); /** Mints a new block using "alice-reward-share" test account. */ - public static void mintBlock(Repository repository) throws DataException { + public static Block mintBlock(Repository repository) throws DataException { PrivateKeyAccount mintingAccount = Common.getTestAccount(repository, "alice-reward-share"); - BlockMinter.mintTestingBlock(repository, mintingAccount); + return BlockMinter.mintTestingBlock(repository, mintingAccount); } public static BigDecimal getNextBlockReward(Repository repository) throws DataException { diff --git a/src/test/resources/test-chain-old-asset.json b/src/test/resources/test-chain-old-asset.json index 08bb6a6a..6c9598f2 100644 --- a/src/test/resources/test-chain-old-asset.json +++ b/src/test/resources/test-chain-old-asset.json @@ -29,6 +29,12 @@ "blockTimingsByHeight": [ { "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 } ], + "ciyamAtSettings": { + "feePerStep": "0.0001", + "maxStepsPerRound": 500, + "stepsPerFunctionCall": 10, + "minutesPerBlock": 1 + }, "featureTriggers": { "messageHeight": 0, "atHeight": 0, diff --git a/src/test/resources/test-chain-v1.json b/src/test/resources/test-chain-v1.json index f3685db1..71d5bcff 100644 --- a/src/test/resources/test-chain-v1.json +++ b/src/test/resources/test-chain-v1.json @@ -29,6 +29,12 @@ "blockTimingsByHeight": [ { "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 } ], + "ciyamAtSettings": { + "feePerStep": "0.0001", + "maxStepsPerRound": 500, + "stepsPerFunctionCall": 10, + "minutesPerBlock": 1 + }, "featureTriggers": { "messageHeight": 0, "atHeight": 0, diff --git a/src/test/resources/test-chain-v2-founder-rewards.json b/src/test/resources/test-chain-v2-founder-rewards.json index 2c315c79..5e71b4d9 100644 --- a/src/test/resources/test-chain-v2-founder-rewards.json +++ b/src/test/resources/test-chain-v2-founder-rewards.json @@ -29,6 +29,12 @@ "blockTimingsByHeight": [ { "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 } ], + "ciyamAtSettings": { + "feePerStep": "0.0001", + "maxStepsPerRound": 500, + "stepsPerFunctionCall": 10, + "minutesPerBlock": 1 + }, "featureTriggers": { "messageHeight": 0, "atHeight": 0, diff --git a/src/test/resources/test-chain-v2-minting.json b/src/test/resources/test-chain-v2-minting.json index 625c225a..9bcc4a95 100644 --- a/src/test/resources/test-chain-v2-minting.json +++ b/src/test/resources/test-chain-v2-minting.json @@ -29,6 +29,12 @@ "blockTimingsByHeight": [ { "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 } ], + "ciyamAtSettings": { + "feePerStep": "0.0001", + "maxStepsPerRound": 500, + "stepsPerFunctionCall": 10, + "minutesPerBlock": 1 + }, "featureTriggers": { "messageHeight": 0, "atHeight": 0, diff --git a/src/test/resources/test-chain-v2-qora-holder.json b/src/test/resources/test-chain-v2-qora-holder.json index 088f7d44..81c6760a 100644 --- a/src/test/resources/test-chain-v2-qora-holder.json +++ b/src/test/resources/test-chain-v2-qora-holder.json @@ -29,6 +29,12 @@ "blockTimingsByHeight": [ { "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 } ], + "ciyamAtSettings": { + "feePerStep": "0.0001", + "maxStepsPerRound": 500, + "stepsPerFunctionCall": 10, + "minutesPerBlock": 1 + }, "featureTriggers": { "messageHeight": 0, "atHeight": 0, diff --git a/src/test/resources/test-chain-v2.json b/src/test/resources/test-chain-v2.json index 1ea8bb57..09dbcab1 100644 --- a/src/test/resources/test-chain-v2.json +++ b/src/test/resources/test-chain-v2.json @@ -29,6 +29,12 @@ "blockTimingsByHeight": [ { "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 } ], + "ciyamAtSettings": { + "feePerStep": "0.0001", + "maxStepsPerRound": 500, + "stepsPerFunctionCall": 10, + "minutesPerBlock": 1 + }, "featureTriggers": { "messageHeight": 0, "atHeight": 0, diff --git a/src/test/resources/test-settings-old-asset.json b/src/test/resources/test-settings-old-asset.json index 587a880c..694bcd11 100644 --- a/src/test/resources/test-settings-old-asset.json +++ b/src/test/resources/test-settings-old-asset.json @@ -2,5 +2,6 @@ "restrictedApi": false, "blockchainConfig": "src/test/resources/test-chain-old-asset.json", "wipeUnconfirmedOnStart": false, + "testNtpOffset": 0, "minPeers": 0 } diff --git a/src/test/resources/test-settings-v1.json b/src/test/resources/test-settings-v1.json index 675d0c51..069ab82f 100644 --- a/src/test/resources/test-settings-v1.json +++ b/src/test/resources/test-settings-v1.json @@ -2,5 +2,6 @@ "restrictedApi": false, "blockchainConfig": "src/test/resources/test-chain-v1.json", "wipeUnconfirmedOnStart": false, + "testNtpOffset": 0, "minPeers": 0 } diff --git a/src/test/resources/test-settings-v2-founder-rewards.json b/src/test/resources/test-settings-v2-founder-rewards.json index 1751cf7d..c89df187 100644 --- a/src/test/resources/test-settings-v2-founder-rewards.json +++ b/src/test/resources/test-settings-v2-founder-rewards.json @@ -2,5 +2,6 @@ "restrictedApi": false, "blockchainConfig": "src/test/resources/test-chain-v2-founder-rewards.json", "wipeUnconfirmedOnStart": false, + "testNtpOffset": 0, "minPeers": 0 } diff --git a/src/test/resources/test-settings-v2-minting.json b/src/test/resources/test-settings-v2-minting.json index eba9a655..9c72c375 100644 --- a/src/test/resources/test-settings-v2-minting.json +++ b/src/test/resources/test-settings-v2-minting.json @@ -2,5 +2,6 @@ "restrictedApi": false, "blockchainConfig": "src/test/resources/test-chain-v2-minting.json", "wipeUnconfirmedOnStart": false, + "testNtpOffset": 0, "minPeers": 0 } diff --git a/src/test/resources/test-settings-v2-qora-holder.json b/src/test/resources/test-settings-v2-qora-holder.json index 0248c867..83b23287 100644 --- a/src/test/resources/test-settings-v2-qora-holder.json +++ b/src/test/resources/test-settings-v2-qora-holder.json @@ -2,5 +2,6 @@ "restrictedApi": false, "blockchainConfig": "src/test/resources/test-chain-v2-qora-holder.json", "wipeUnconfirmedOnStart": false, + "testNtpOffset": 0, "minPeers": 0 } diff --git a/src/test/resources/test-settings-v2.json b/src/test/resources/test-settings-v2.json index 31fc2672..57eb22a5 100644 --- a/src/test/resources/test-settings-v2.json +++ b/src/test/resources/test-settings-v2.json @@ -2,5 +2,6 @@ "restrictedApi": false, "blockchainConfig": "src/test/resources/test-chain-v2.json", "wipeUnconfirmedOnStart": false, + "testNtpOffset": 0, "minPeers": 0 }