アカウントにカスタムデータを追加
Bob は Symbol パブリックブロックチェーン上の公証アカウントを扱うデジタル公証人として働いています。顧客がドキュメントを公証するために Bob の元へ訪れると、彼は顧客のドキュメントの認証をチェックし、デジタル化ドキュメントをメタデータとして顧客アカウントにタグ付けします。
最近 Alice は学校を卒業し、教育証明を彼女の Symbol アカウントに認定することで、学位証明を繰り返して提供しなければならない面倒事を避けたいと思っています。そのため彼女は Bob を訪れ、学位を彼に提供します。Alice が手数料を支払うと Bob は真正性を検証し Alice のアカウントに学位を表すメタデータをスタンプします。
このチュートリアルでは、 Bob のタグアカウントに メタデータトランザクション の発行を許可する実装を行います。
symbol-cli account generate --save
Enter network type (MAIN_NET, TEST_NET): TEST_NET
Do you want to save it? [y/n]: y
Enter a Symbol Node URL. (Example: http://localhost:3000): <NODE_URL>
Insert profile name: alice
1. Bob has to pick a key to store Alice's certificate.
Imagine that CERT
is a common key to store university degrees.
Define this key as a new variable.
// replace with key
const key = KeyGenerator.generateUInt64Key('CERT');
// replace with key
const key = symbol_sdk_1.KeyGenerator.generateUInt64Key('CERT');
2. Alice's degree brings the identifier 123456
for her certificate.
Help Bob to assign this value to the key defined in the previous step.
To achieve so, define an AccountMetadataTransaction linking Alice account, the key (CERT), and the associated value (123456).
// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with public key
const alicePublicKey =
'D04AB232742BB4AB3A1368BD4615E4E6D0224AB71A016BAF8520A332C9778737';
const alicePublicAccount = PublicAccount.createFromPublicKey(
alicePublicKey,
networkType,
);
// replace with value
const value = '123456';
const accountMetadataTransaction = AccountMetadataTransaction.create(
Deadline.create(epochAdjustment),
alicePublicAccount.address,
key,
value.length,
value,
networkType,
);
// replace with network type
const networkType = symbol_sdk_1.NetworkType.TEST_NET;
// replace with public key
const alicePublicKey =
'D04AB232742BB4AB3A1368BD4615E4E6D0224AB71A016BAF8520A332C9778737';
const alicePublicAccount = symbol_sdk_1.PublicAccount.createFromPublicKey(
alicePublicKey,
networkType,
);
// replace with value
const value = '123456';
const accountMetadataTransaction = symbol_sdk_1.AccountMetadataTransaction.create(
symbol_sdk_1.Deadline.create(epochAdjustment),
alicePublicAccount.address,
key,
value.length,
value,
networkType,
);
3. To avoid spamming the account with invalid metadata keys, all metadata is attached only with the consent of the account owner through Aggregate Transactions. Thus, Alice will have to opt-in if she wants the metadata entry assigned to its account. Wrap the AccountMetadataTransaction inside an AggregateBondedTransaction and sign the transaction using Bob's account.
// replace with bob private key
const bobPrivateKey =
'0000000000000000000000000000000000000000000000000000000000000000';
const bobAccount = Account.createFromPrivateKey(bobPrivateKey, networkType);
const aggregateTransaction = AggregateTransaction.createBonded(
Deadline.create(epochAdjustment),
[accountMetadataTransaction.toAggregate(bobAccount.publicAccount)],
networkType,
[],
UInt64.fromUint(2000000),
);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
'1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = bobAccount.sign(
aggregateTransaction,
networkGenerationHash,
);
console.log(signedTransaction.hash);
// replace with bob private key
const bobPrivateKey =
'0000000000000000000000000000000000000000000000000000000000000000';
const bobAccount = symbol_sdk_1.Account.createFromPrivateKey(
bobPrivateKey,
networkType,
);
const aggregateTransaction = symbol_sdk_1.AggregateTransaction.createBonded(
symbol_sdk_1.Deadline.create(epochAdjustment),
[accountMetadataTransaction.toAggregate(bobAccount.publicAccount)],
networkType,
[],
symbol_sdk_1.UInt64.fromUint(2000000),
);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
'1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = bobAccount.sign(
aggregateTransaction,
networkGenerationHash,
);
console.log(signedTransaction.hash);
4. Before sending an aggregate transaction to the network, Bob has to lock 10
symbol.xym
.
Define a new HashLockTransaction and sign it with Bob's account.
// replace with symbol.xym id
const networkCurrencyMosaicId = new MosaicId('5E62990DCAC5BE8A');
// replace with network currency divisibility
const networkCurrencyDivisibility = 6;
const hashLockTransaction = HashLockTransaction.create(
Deadline.create(epochAdjustment),
new Mosaic(
networkCurrencyMosaicId,
UInt64.fromUint(10 * Math.pow(10, networkCurrencyDivisibility)),
),
UInt64.fromUint(480),
signedTransaction,
networkType,
UInt64.fromUint(2000000),
);
const signedHashLockTransaction = bobAccount.sign(
hashLockTransaction,
networkGenerationHash,
);
// replace with symbol.xym id
const networkCurrencyMosaicId = new symbol_sdk_1.MosaicId('5E62990DCAC5BE8A');
// replace with network currency divisibility
const networkCurrencyDivisibility = 6;
const hashLockTransaction = symbol_sdk_1.HashLockTransaction.create(
symbol_sdk_1.Deadline.create(epochAdjustment),
new symbol_sdk_1.Mosaic(
networkCurrencyMosaicId,
symbol_sdk_1.UInt64.fromUint(
10 * Math.pow(10, networkCurrencyDivisibility),
),
),
symbol_sdk_1.UInt64.fromUint(480),
signedTransaction,
networkType,
symbol_sdk_1.UInt64.fromUint(2000000),
);
const signedHashLockTransaction = bobAccount.sign(
hashLockTransaction,
networkGenerationHash,
);
注釈
Alice が次の 480
ブロック中にアグリゲートに署名すると、 Bob はロックされた資産を取り戻します。
5. Announce the HashLockTransaction. Monitor the network until the transaction gets confirmed, and then announce the AggregateTransaction containing the AccountMetadataTransaction.
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const listener = repositoryFactory.createListener();
const receiptHttp = repositoryFactory.createReceiptRepository();
const transactionHttp = repositoryFactory.createTransactionRepository();
const transactionService = new TransactionService(transactionHttp, receiptHttp);
listener.open().then(() => {
transactionService
.announceHashLockAggregateBonded(
signedHashLockTransaction,
signedTransaction,
listener,
)
.subscribe(
(x) => console.log(x),
(err) => console.log(err),
() => listener.close(),
);
});
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
const listener = repositoryFactory.createListener();
const receiptHttp = repositoryFactory.createReceiptRepository();
const transactionHttp = repositoryFactory.createTransactionRepository();
const transactionService = new symbol_sdk_1.TransactionService(
transactionHttp,
receiptHttp,
);
listener.open().then(() => {
transactionService
.announceHashLockAggregateBonded(
signedHashLockTransaction,
signedTransaction,
listener,
)
.subscribe(
(x) => console.log(x),
(err) => console.log(err),
() => listener.close(),
);
});
トランザクションが承認されたら、Alice のプロファイルを使用して、4番目のステップで取得したハッシュに署名します。
symbol-cli transaction cosign --hash <transaction-hash> --profile alice
すべてがうまくいくと、Alice には メタデータ値 {bobPublicKey, CERT, 123456}
が割り当てられます。これは "Alice アカウントは証明書番号 123456 を持っており、Bob によって検証されています" と読むことができます。