qortal/src/qora/at/AT.java
catbref cfd8b53fc1 API, HSQLDB
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.
2018-12-12 12:13:06 +00:00

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