Qucik Learning Symbol Section 8
Chapter 8 Lock
The Symbol blockchain has two types of LockTransactions: Hash Lock Transaction and Secret Lock Transaction.
8.1 Hash Lock
Hash Lock Transactions enable a transaction to be be announced later. The transaction is stored in every node's partial cache with a hash value until the transaction is announced. The transaction is locked and not processed on the API node until it is signed by all cosignatories. It does not lock the tokens owned by the account but a 10 XYM deposit is paid by the initiator of the transaction. The locked funds will be refunded to the initiating account when the Hash Lock transaction is fully signed. The maximum validity period of a Hash Lock Transaction is approximately 48 hours, if the transaction is not completed within this time period then the 10 XYM deposit is lost.
8.1.1 Creation of an Aggregate Bonded Transaction.
$bobKey = $facade->createAccount(new PrivateKey("ED949592C90CA58A16CB5BEC303DB011A48373063DDB0C4CFD6DFD01F********"));
$bobAddress = $bobKey->address;
$namespaceIds = IdGenerator::generateNamespacePath('symbol.xym');
$namespaceId = new NamespaceId($namespaceIds[count($namespaceIds) - 1]);
// アグリゲートTxに含めるTxを作成
$tx1 = new EmbeddedTransferTransactionV1(
signerPublicKey: $aliceKey->publicKey,
recipientAddress: $bobAddress,
mosaics: [
new UnresolvedMosaic(
mosaicId: new UnresolvedMosaicId($namespaceId), // モザイクID
amount: new Amount(1000000) // 金額(1XYM)
)
],
message: "", //メッセージなし
);
$tx2 = new EmbeddedTransferTransactionV1(
signerPublicKey: $bobKey->publicKey,
recipientAddress: $aliceKey->address,
message: "\0thank you!",
);
// マークルハッシュの算出
$embeddedTransactions = [$tx1, $tx2];
$merkleHash = $facade->hashEmbeddedTransactions($embeddedTransactions);
// アグリゲートボンデットTx作成
$aggregateTx = new AggregateBondedTransactionV2(
network: new NetworkType(NetworkType::TESTNET),
signerPublicKey: $aliceKey->publicKey, // 署名者公開鍵
deadline: new Timestamp($facade->now()->addHours(2)),
transactionsHash: $merkleHash,
transactions: $embeddedTransactions
);
$facade->setMaxFee($aggregateTx, 100, 1); // 手数料
// 署名
$sig = $aliceKey->signTransaction($aggregateTx);
$payload = $facade->attachSignature($aggregateTx, $sig);
echo 'アグリゲートボンデットTxHash' . PHP_EOL;
echo $facade->hashTransaction($aggregateTx) . PHP_EOL;
/**
* ハッシュロック
*/
$hashLockTx = new HashLockTransactionV1(
signerPublicKey: $aliceKey->publicKey, // 署名者公開鍵
network: new NetworkType(NetworkType::TESTNET),
deadline: new Timestamp($facade->now()->addHours(2)), // 有効期限
duration: new BlockDuration(480), // 有効期限
hash: new Hash256($facade->hashTransaction($aggregateTx)), // ペイロードのハッシュ
mosaic: new UnresolvedMosaic(
mosaicId: new UnresolvedMosaicId($namespaceId), // モザイクID
amount: new Amount(10 * 1000000) // 金額(10XYM)
)
);
$facade->setMaxFee($hashLockTx, 100); // 手数料
// 署名
$hashLockSig = $aliceKey->signTransaction($hashLockTx);
$hashLockJsonPayload = $facade->attachSignature($hashLockTx, $hashLockSig);
Specify the public key of the sender's account when two transactions, tx1 and tx2, are arrayed in AggregateArray. Get the public key in advance via the API with reference to the Account chapter. Arrayed transactions are verified for integrity in this order during block approval.
For example, it is possible to send an NFT from Alice to Bob in tx1 and then from Bob to Carol in tx2, but changing the order of the Aggregate Transaction to tx2,tx1 will result in an error. In addition, if there is even one inconsistent transaction in the Aggregate transaction, the entire Aggregate transaction will fail and will not be approved into the chain.
8.1.2 Creation, signing and announcement of Hash Lock Transaction
//ハッシュロックTX作成
$hashLockTx = new HashLockTransactionV1(
signerPublicKey: $aliceKey->publicKey, // 署名者公開鍵
network: new NetworkType(NetworkType::TESTNET),
deadline: new Timestamp($facade->now()->addHours(2)), // 有効期限
duration: new BlockDuration(480), // 有効期限
hash: new Hash256($facade->hashTransaction($aggregateTx)), // ペイロードのハッシュ
mosaic: new UnresolvedMosaic(
mosaicId: new UnresolvedMosaicId($namespaceId), // モザイクID
amount: new Amount(10 * 1000000) // 金額(10XYM)
)
);
$facade->setMaxFee($hashLockTx, 100); // 手数料
// 署名
$hashLockSig = $aliceKey->signTransaction($hashLockTx);
$hashLockJsonPayload = $facade->attachSignature($hashLockTx, $hashLockSig);
/**
* ハッシュロックをアナウンス
*/
$config = new Configuration();
$config->setHost($NODE_URL);
$client = new GuzzleHttp\Client();
$apiInstance = new TransactionRoutesApi($client, $config);
try {
$result = $apiInstance->announceTransaction($hashLockJsonPayload);
echo $result . PHP_EOL;
} catch (Exception $e) {
echo 'Exception when calling TransactionRoutesApi->announceTransaction: ', $e->getMessage(), PHP_EOL;
}
8.1.3 Announcement of Aggregate Bonded Transaction
After checking with e.g. Explorer, announce the Bonded Transaction to the network.
try {
$result = $apiInstance->announcePartialTransaction($payload);
echo $result . PHP_EOL;
} catch (Exception $e) {
echo 'Exception when calling TransactionRoutesApi->announceTransaction: ', $e->getMessage(), PHP_EOL;
}
8.1.4 Co-signature
Co-sign the locked transaction from the specified account (Bob).
$txInfo = $apiInstance->getPartialTransaction($facade->hashTransaction($aggregateTx));
// 連署者の連署
$txInfo = $apiInstance->getPartialTransaction($facade->hashTransaction($aggregateTx));
// // 連署者の連署
$signTxHash = new Hash256($txInfo->getMeta()->getHash());
$signature = new Signature($bobKey->keyPair->sign($signTxHash->binaryData));
$body = [
'parentHash' => $signTxHash->__toString(),
'signature' => $signature->__toString(),
'signerPublicKey' => $bobKey->publicKey->__toString(),
'version' => '0'
];
print_r($body);
//アナウンス
try {
$result = $apiInstance->announceCosignatureTransaction($body);
echo $result . PHP_EOL;
} catch (Exception $e) {
echo 'Exception when calling TransactionRoutesApi->announceTransaction: ', $e->getMessage(), PHP_EOL;
}
8.1.5 Note
Hash Lock Transactions can be created and announced by anyone, not just the account that initially creates and signs the transaction. But make sure that the Aggregate Transaction includes a transaction for whom the account is the signer. Dummy transactions with no mosaic transmission and no message are valid.
8.2 Secret Lock・Secret Proof
The secret lock creates a common password in advance and locks the designated mosaic. This allows the recipient to receive the locked mosaic if they can prove that they possess the password before the lock expiry date.
This section describes how Alice locks 1XYM and Bob unlocks the transaction to receive the funds.
First, create a Bob account to interact with Alice. Bob needs to announce the transaction to unlock the transaction, so please request 10XYM from the faucet.
//FAUCET URL出力
echo "https://testnet.symbol.tools/?recipient=" . $bobAddress . "&amount=10";
8.2.1 Secret Lock
Create a common pass for locking and unlocking.PHP installs a library that provides the SHA3-256 hash function.
composer require symfony/polyfill-php70
require 'vendor/autoload.php';
use Symfony\Polyfill\Php70\Php70;
$proof = random_bytes(20); // 解除用キーワード
$secret = hash('sha3-256', $proof, true); // ロック用キーワード
echo "secret: " . bin2hex($secret) . PHP_EOL;
echo "proof: " . bin2hex($proof) . PHP_EOL;
Sample output
secret: 40eb770de739fa22617e8f2212b1fe821107e97f32a2c288264e0ccb644610f1
proof: 1f62d5bdfbffb657d5c4a67d5d2f5617aec14c43
Creating, signing and announcing transaction
$lockTx = new SecretLockTransactionV1(
signerPublicKey: $aliceKey->publicKey, // 署名者公開鍵
deadline: new Timestamp($facade->now()->addHours(2)), // 有効期限
network: new NetworkType(NetworkType::TESTNET),
mosaic: new UnresolvedMosaic(
mosaicId: new UnresolvedMosaicId($namespaceId), // モザイクID
amount: new Amount(1000000) // ロックするモザイク
),
duration: new BlockDuration(480), //ロック期間
hashAlgorithm: new LockHashAlgorithm(LockHashAlgorithm::SHA3_256), // ハッシュアルゴリズム
secret: new Hash256($secret), // ロック用キーワード
recipientAddress: $bobAddress, // 解除時の転送先:Bob
);
$facade->setMaxFee($lockTx, 100); // 手数料
// 署名
$lockSig = $aliceKey->signTransaction($lockTx);
$payload = $facade->attachSignature($lockTx, $lockSig);
try {
$result = $apiInstance->announceTransaction($payload);
echo $result . PHP_EOL;
} catch (Exception $e) {
echo 'Exception when calling TransactionRoutesApi->announceTransaction: ', $e->getMessage(), PHP_EOL;
}
The LockHashAlgorithm is as follows
{0:'Op_Sha3_256', 1:'Op_Hash_160', 2:'Op_Hash_256'}
At the time of locking, the unlock destination is specified by Bob, thus the destination account (Bob) cannot be changed even if an account other than Bob unlocks the transaction.
The maximum lock period is 365 days (counting number of blocks in days).
Check the approved transactions.
$secretAipInstance = new SecretLockRoutesApi($client, $config);
$resutl = $secretAipInstance->searchSecretLock(secret: $secret);
Sample output
{
"data": [
{
"id": "66A4247084E82060AFC6705C",
"lock": {
"version": 1,
"ownerAddress": "98E521BD0F024F58E670A023BF3A14F3BECAF0280396BED0",
"mosaicId": "72C0212E67A08BCE",
"amount": "1000000",
"endHeight": "1607702",
"status": 0,
"hashAlgorithm": 0,
"secret": "A8E4F52ADDA0AFCD413D86A51589711CF045F144EEE56FC9CE96095D6AB79E9E",
"recipientAddress": "98665DDAC8CCF6EE40B1D50800DC8C6C27B314988A1FDB26",
"compositeHash": "57541680B0E2DFAE504A6937AAB6A65A7008C25F905FCA68A24C21165314023C"
}
}
],
"pagination": {
"pageNumber": 1,
"pageSize": 10
}
}
It shows that Alice who locked the transaction is recorded in ownerAddress and the Bob is recorded in recipientAddress. The information about the secret is published and Bob informs the network of the corresponding proof.
8.2.2 Secret Proof
Unlock the transaction using the secret proof. Bob must have obtained the secret proof in advance.
$proofTx = new SecretProofTransactionV1(
signerPublicKey: $bobKey->publicKey, // 署名者公開鍵
deadline: new Timestamp($facade->now()->addHours(2)), // 有効期限
network: new NetworkType(NetworkType::TESTNET),
hashAlgorithm: new LockHashAlgorithm(LockHashAlgorithm::SHA3_256), // ハッシュアルゴリズム
secret: new Hash256($secret), // ロック用キーワード
recipientAddress: $bobAddress, // 解除時の転送先:Alice
proof: $proof, // 解除用キーワード
);
$facade->setMaxFee($proofTx, 100); // 手数料
// 署名
$proofSig = $bobKey->signTransaction($proofTx);
$payload = $facade->attachSignature($proofTx, $proofSig);
try {
$result = $apiInstance->announceTransaction($payload);
echo $result . PHP_EOL;
} catch (Exception $e) {
echo 'Exception when calling TransactionRoutesApi->announceTransaction: ', $e->getMessage(), PHP_EOL;
}
Confirm the approval result.
$txInfo = $apiInstance->getConfirmedTransaction($facade->hashTransaction($proofTx));
echo $txInfo;
Sample output
{
"id": "66A429D1527B051AC20AE9B3",
"meta": {
"height": "1607263",
"hash": "91387B92117ACE7A6BB5596720DAEC6FEA89E42076BD40320381CE1A86C0D57D",
"merkleComponentHash": "91387B92117ACE7A6BB5596720DAEC6FEA89E42076BD40320381CE1A86C0D57D",
"index": 1,
"timestamp": "54784174665",
"feeMultiplier": 100
},
"transaction": {
"size": 207,
"signature": "52CFCE339A361AED998B12EC7B4976542F5F413694BD6E9395DB37C980977839FE2ABEBF577C78CFCA065F0E9C2D72228859B830B75FEBC64551396FAE1D7B00",
"signerPublicKey": "D47E477DA7CAE6127779523270F91BD000D7D0E06DA56192FE911460DC39081C",
"version": 1,
"network": 152,
"type": 16978,
"maxFee": "20700",
"deadline": "54791351985",
"recipientAddress": "98665DDAC8CCF6EE40B1D50800DC8C6C27B314988A1FDB26",
"secret": "2F63B181A3C0A9C549F13492D9A8A3D6851B911F813A4C4D70539A0BE55D277D",
"hashAlgorithm": 0,
"proof": "86D75E3321D5EE227E14C3CFD67E378EB3F3FD64"
}
}
The SecretProofTransaction does not contain information about the amount of any mosaics received. Check the amount in the receipt created when the block is generated. Search for receipts addressed to Bob with receipt type:LockHash_Completed.
$receiptApiInstance = new ReceiptRoutesApi($client, $config);
$result = $receiptApiInstance->searchReceipts(
receipt_type: new ReceiptType(ReceiptType::LOCK_SECRET_COMPLETED),
target_address:$bobAddress
);
echo 'レシート' . PHP_EOL;
echo $result . PHP_EOL;
ReceiptTypes are as follows:
{ 4685: 'Mosaic_Rental_Fee', 4942: 'Namespace_Rental_Fee', 8515: 'Harvest_Fee', 8776: 'LockHash_Completed', 8786: 'LockSecret_Completed', 9032: 'LockHash_Expired', 9042: 'LockSecret_Expired', 12616: 'LockHash_Created', 12626: 'LockSecret_Created', 16717: 'Mosaic_Expired', 16718: 'Namespace_Expired', 16974: 'Namespace_Deleted', 20803: 'Inflation', 57667: 'Transaction_Group', 61763: 'Address_Alias_Resolution' 62019: 'Mosaic_Alias_Resolution', } 8786: 'LockSecret_Completed' :LockSecret is completed 9042: 'LockSecret_Expired' :LockSecret is expired
8.3 Tips for use
8.3.1 Paying the transaction fee instead
Generally blockchains require transaction fees for sending transactions. Therefore, users who want to use blockchains need to obtain the native currency of the chain to pay fees (e.g. Symbol's native currency XYM) from the exchange in advance. If the user is a company, the way it is managed might be an issue from an operational point of view. If using Aggregate Transactions, service providers can cover hash lock and transaction fees on behalf of users.
8.3.2 Scheduled transactions
Secret locks are refunded to the account that created the transaction after a specified number of blocks. When the service provider charges the cost of the lock for the Secret Lock account, the amount of tokens owned by the user for the lock will increase after the expiry date has passed. On the other hand, announcing a secret proof transaction before the deadline has passed is treated as a cancellation as the transaction is completed and the funds are returned to the service provider.
8.3.3 Atomic swaps
Secret locks can be used to exchange mosaics (tokens) with other chains. Please note that other chains refer to this as a hash time lock contract (HTLC) not to be mistaken for a Symbol Hash Lock.