mirror of
https://github.com/Qortal/qortal.git
synced 2025-04-21 10:27:52 +00:00
Added more global parameters to /admin/unused API endpoint (formally /admin/dud) and also managed to remove /admin/unused from API documentation UI. Added results slicing to /assets/all Added /assets/orderbook API call that returns open asset orders Added /assets/trades that returns successful asset trades Added POST /assets/issue stub Unified HSQLDB connectionUrl to public variable inside Controller class. Can't deploy v1 ATs with isFinished=true flag as that prevents later transactions sending messages (during import of v1 chain). Some future hard-fork code will need to set all v1 ATs to "finished". Changed DB's "TransactionRecipients" to "TransactionParticipants" to properly support API call to find all transactions 'involving' a specific address. Support code needed in Block and Transaction with some transaction-specific overrides for Genesis and AT transactions. Removed old, deprecated calls from Transaction/TransactionRepository Moved HSQLDB database properties from connection URL to explicit SQL statements in HSQLDBDatabaseUpdates. They didn't work in connection URL during DB creation anyway. Retrofitted HSQLDB Accounts table with public_key column instead of rebuilding it later. Fixed incorrect comments in IssueAssetTransactionTransformer regarding v1 serialization for signing. Re-imported v1 chain to test latest changes.
161 lines
5.4 KiB
Java
161 lines
5.4 KiB
Java
package qora.at;
|
|
|
|
import java.math.BigDecimal;
|
|
import java.nio.ByteBuffer;
|
|
import java.util.List;
|
|
|
|
import org.ciyam.at.MachineState;
|
|
|
|
import data.at.ATData;
|
|
import data.at.ATStateData;
|
|
import data.transaction.DeployATTransactionData;
|
|
import qora.assets.Asset;
|
|
import qora.crypto.Crypto;
|
|
import qora.transaction.ATTransaction;
|
|
import repository.ATRepository;
|
|
import repository.DataException;
|
|
import repository.Repository;
|
|
|
|
public class AT {
|
|
|
|
// Properties
|
|
private Repository repository;
|
|
private ATData atData;
|
|
private ATStateData atStateData;
|
|
|
|
// Constructors
|
|
|
|
public AT(Repository repository, ATData atData, ATStateData atStateData) {
|
|
this.repository = repository;
|
|
this.atData = atData;
|
|
this.atStateData = atStateData;
|
|
}
|
|
|
|
public AT(Repository repository, ATData atData) {
|
|
this(repository, atData, null);
|
|
}
|
|
|
|
/** Constructs AT-handling object when deploying AT */
|
|
public AT(Repository repository, DeployATTransactionData deployATTransactionData) throws DataException {
|
|
this.repository = repository;
|
|
|
|
String atAddress = deployATTransactionData.getATAddress();
|
|
int height = this.repository.getBlockRepository().getBlockchainHeight() + 1;
|
|
byte[] creatorPublicKey = deployATTransactionData.getCreatorPublicKey();
|
|
long creation = deployATTransactionData.getTimestamp();
|
|
|
|
byte[] creationBytes = deployATTransactionData.getCreationBytes();
|
|
long assetId = deployATTransactionData.getAssetId();
|
|
short version = (short) (creationBytes[0] | (creationBytes[1] << 8)); // Little-endian
|
|
|
|
if (version >= 2) {
|
|
MachineState machineState = new MachineState(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());
|
|
|
|
byte[] stateData = machineState.toBytes();
|
|
byte[] stateHash = Crypto.digest(stateData);
|
|
|
|
this.atStateData = new ATStateData(atAddress, height, creation, stateData, stateHash, BigDecimal.ZERO.setScale(8));
|
|
} else {
|
|
// Legacy v1 AT
|
|
// We would deploy these in 'dead' state as they will never be run on Qora2
|
|
// but this breaks import from Qora1 so something else will have to mark them dead at hard-fork
|
|
|
|
// Extract code bytes length
|
|
ByteBuffer byteBuffer = ByteBuffer.wrap(deployATTransactionData.getCreationBytes());
|
|
|
|
// v1 AT header is: version, reserved, code-pages, data-pages, call-stack-pages, user-stack-pages (all shorts)
|
|
|
|
// Number of code pages
|
|
short numCodePages = byteBuffer.get(2 + 2);
|
|
|
|
// Skip header and also "minimum activation amount" (long)
|
|
byteBuffer.position(6 * 2 + 8);
|
|
|
|
int codeLen = 0;
|
|
|
|
// Extract actual code length, stored in minimal-size form (byte, short or int)
|
|
if (numCodePages * 256 < 257) {
|
|
codeLen = (int) (byteBuffer.get() & 0xff);
|
|
} else if (numCodePages * 256 < Short.MAX_VALUE + 1) {
|
|
codeLen = byteBuffer.getShort() & 0xffff;
|
|
} else if (numCodePages * 256 <= Integer.MAX_VALUE) {
|
|
codeLen = byteBuffer.getInt();
|
|
}
|
|
|
|
// Extract code bytes
|
|
byte[] codeBytes = new byte[codeLen];
|
|
byteBuffer.get(codeBytes);
|
|
|
|
// Create AT
|
|
boolean isSleeping = false;
|
|
Integer sleepUntilHeight = null;
|
|
boolean isFinished = false;
|
|
boolean hadFatalError = false;
|
|
boolean isFrozen = false;
|
|
Long frozenBalance = null;
|
|
|
|
this.atData = new ATData(atAddress, creatorPublicKey, creation, version, Asset.QORA, codeBytes, isSleeping, sleepUntilHeight, isFinished,
|
|
hadFatalError, isFrozen, frozenBalance);
|
|
|
|
this.atStateData = new ATStateData(atAddress, height, creation, null, null, BigDecimal.ZERO.setScale(8));
|
|
}
|
|
}
|
|
|
|
// Getters / setters
|
|
|
|
public ATStateData getATStateData() {
|
|
return this.atStateData;
|
|
}
|
|
|
|
// Processing
|
|
|
|
public void deploy() throws DataException {
|
|
ATRepository atRepository = this.repository.getATRepository();
|
|
atRepository.save(this.atData);
|
|
|
|
// For version 2+ we also store initial AT state data
|
|
if (this.atData.getVersion() >= 2)
|
|
atRepository.save(this.atStateData);
|
|
}
|
|
|
|
public void undeploy() throws DataException {
|
|
// AT states deleted implicitly by repository
|
|
this.repository.getATRepository().delete(this.atData.getATAddress());
|
|
}
|
|
|
|
public List<ATTransaction> run(long blockTimestamp) throws DataException {
|
|
String atAddress = this.atData.getATAddress();
|
|
|
|
QoraATAPI api = new QoraATAPI(repository, this.atData, blockTimestamp);
|
|
QoraATLogger logger = new QoraATLogger();
|
|
|
|
byte[] codeBytes = this.atData.getCodeBytes();
|
|
|
|
// Fetch latest ATStateData for this AT (if any)
|
|
ATStateData atStateData = this.repository.getATRepository().getLatestATState(atAddress);
|
|
|
|
// There should be at least initial AT state data
|
|
if (atStateData == null)
|
|
throw new IllegalStateException("No initial AT state data found");
|
|
|
|
// [Re]create AT machine state using AT state data or from scratch as applicable
|
|
MachineState state = MachineState.fromBytes(api, logger, atStateData.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);
|
|
|
|
return api.getTransactions();
|
|
}
|
|
|
|
}
|