Transaction
is a Signable
subclass and is used to comunicate with TRINCI blockchain network. Nonetheless they're not directly interchangeable as Transaction
class has a specific format and some redefined methods. Although one could recreate it manually using Signable
, it is much faster and simpler to use Transaction
instead. All transactions have some common fields the same basic functionality:
- A transaction allows its submitter to call any (existing) method of a specific (registered) smart contract in the context of a specific account.
- Arguments can be passed to the called method as an array of bytes. Data transcoding is contyract-specific. No checks are performed on those data other than that of the size.
- If, on submission, TRINCI core returns a ticket, this means that the transaction has been built correctly and core will try to add it to a new block. However this doesn't mean that the transaction will be executed sucessfully.
- On execution (when unconfirmed tx is inserted into a new valid block) transaction produces a receipt, which has 2 major properties:
success
, boolean - Iftrue
, then everything is OK. Iffalse
, any changes made to the database during the smart contract execution will be dropped. The transaction itself, however, remains in the blockchain with it's receipt and can be viewed at any time using itsticket
.result
, binary data - Other than modifying database, a smart contract can also return some data. In case transaction was successful (success
field istrue
) the encoding is contract-specific. In case of a transaction faillure (success
field isfalse
) then the core is expecting an error string as direct Unicode string bytes.
Transactions in TRINCI blockchain can be of different types (UnitaryTransaction
being the most common). More on each transaction type down below.
Keep in mind that Transaction
class is a base class which can manage all types of transactions. It can be use to decode a transaction of an uncertain type. Even if it can be used to create every other type of transaction, it is much simpler and faster to use ad-hoc classes to create transactions of a specific types.
const tx = new t2lib.Transaction();
const uTx = new t2lib.UnitaryTransaction();
const bTx = new t2lib.BulkTransaction();
const brTx = new t2lib.BulkRootTransaction();
const bnTx = new t2lib.BulkNodeTransaction();
Below are methods and setters/getters common to all types of transactions.
Each transaction produces a ticket on submission. The ticket is, basically, transaction hash, which can be calculated without submitting it using the following method:
const txTicket = await tx.getTicket();
Each transaction can only be executed once. If TRINCI blockchain receives a transaction which hash (ticket) is already present in blockchain, the transaction will be rejected.
To allow users to perform the same operation multiple times.
// automatic nonce generation.
tx.data.genNonce();
// nonce can also be set manually using either bytes of hex string
// following examples have both the same effect
tx.data.nonce = new Uint8Array([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]);
tx.data.nonceHex = '0001020304050607';
Each time a smart contract is executed, it happens in the context of an account. This means that even if two accounts share the same smart contract code, two instances of that smart contract can be completely isolated (it all depends on the actual code of the smart contract).
tx.data.accountId = '<transactionTargetAccountString>';
This is the most common and simplest transaction type.
const tx = new T2lib.UnitaryTransaction();
tx.accountId = 'Qme9QcADwLnBSL...';
This is the transaction's target account ID. In other words the account by which the transaction is going to be executed.
tx.nonce = new Uint8Array(/* values */);
tx.nonceHex = 'f39d77a9b0043ee2';
tx.genNonce();
This is the transaction's anti-replay value. It is 8 bytes long and can be set as a bytearray, hex string or generated automatically.
tx.networkName = 'skynet';
Name of the transaction's target network. A TRINCI network won't accept transactions built for another network. Network name can be set in TRINCI core's settings.
tx.smartContractHash = new Uint8Array(/* values */);
tx.smartContractHashHex = '12208ebac7c5bbf519aba12b58e9e53f69189c827f0bf0f091f54d7bba1dfe303b30';
Hash of the smart contract you want to invoke on target account.
This field is optional and can be used only when you want to associate a smart contract to previously "clean" account. Once target account is initialized with a smart contract, this field is not needed anymore as that smart contract will be executed automatically. An account cannot be initialized with a different smart contract.
tx.smartContractMethod = 'transfer';
Method you want to invoke on target's smart contract.
tx.smartContractMethodArgs = {
from: 'QmZ6LUiEL64P...',
to: 'QmSiRYNCz8vn...',
amount: 10,
};
tx.smartContractMethodArgsBytes = new Uint8Array(/* values */);
tx.smartContractMethodArgsHex = 'a604f82355fae3...';
Those are arguments specific to the smart contract's method. They can be set in three ways:
- As a plain JS object. It will be automatically serialized using MessagePack library.
- As an array of bytes, if you use your own encoding (e.g. encrypting data before sending them to invoked method).
- Hex string representing abovementioned array of bytes.
tx.signerPublicKey = myAccount.keyPair.publicKey;
Public key used to verify transaction's signature. Even if yu can set it manually, it is not necessary, as it will be automatically derived from private key during transaction's signing.
tx.sign(myAccount.keyPair.privateKey).then(/* ... */);
This method is used to sign a transaction with your private key. During this procedure a public key is automatically derived from passed private key and appended to the transaction as signerPublicKey.
tx.verify().then(/* ... */);
Method used to verify Transactions's signature. It workks just like Signable.verifySignature()
(You can also use that, as Transaction
inherits Signable
's methods). The only difference is that public key used for verification is automatically picked from the transaction itself.
Transaction's import and export methods are the same you will find in Signable
class. They behave in the same exact manner and no further explaining is needed.
// export
tx.toObject().then(/* ... */);
tx.toBytes().then(/* ... */);
tx.toBase58().then(/* ... */);
// import
tx.fromObject().then(/* ... */);
tx.fromBytes().then(/* ... */);
tx.fromBase58().then(/* ... */);