//NETWORK
node = window.origin;
json = await api("/network/properties");
generationHash = json.network.generationHashSeed;
networkId = json.network.identifier;
xymhex = json.chain.currencyMosaicId.split("'").join('').slice(2);
xymid = BigInt(`0x${xymhex}`);
epochAdjustment = Number(json.network.epochAdjustment.replace("s",""));
json = await api("/network/fees/transaction");
feeMultiplier = json.medianFeeMultiplier;
add2Hours = 60 * 60 * 2;
netstat();
//SDK
bundle = await import("https://www.unpkg.com/symbol-sdk@3.2.2/dist/bundle.web.js");
core = bundle.core;
sym = bundle.symbol;
chain = new sym.SymbolFacade(networkId);
m = sym.models;
d = sym.descriptors;
u = core.utils;
async function api(path,method,body){
if(method === "PUT" || method === "POST"){
const res = await fetch(
node + path,
{
method: method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
}
);
return await res.json();
}else{
return await (await fetch(node + path)).json();
}
}
//ログ出力 console log
function clog(signedHash){
console.log(node + "/transactionStatus/" + signedHash);
console.log(node + "/transactions/confirmed/" + signedHash);
console.log("https://symbol.fyi/transactions/" + signedHash);
console.log("https://testnet.symbol.fyi/transactions/" + signedHash);
}
//トランザクション ステータス transaction status
function txstat(tx){
console.log(`== TRANSACTION STATUS ==`);
console.log(`[ 名称: ${tx.constructor.name} ]`);
console.log(`[ 有効期限: ${new Date(epochAdjustment * 1000 + Number(tx.deadline))} ]`);
const useFee = tx.size / 1000000 * 100;
console.log(`[ 手数料係数: 100, データサイズ: ${tx.size}Byte, 想定手数料: ${useFee}XYM ]`);
console.log(`[ 署名アドレス: ${chain.network.publicKeyToAddress(tx.signerPublicKey)} ]`);
}
//ネットワーク ステータス network status
function netstat(){
console.log(`== NETWORK STATUS ==`);
console.log(`[ node: ${node} ]`);
console.log(`[ generationHash: ${generationHash} ]`);
console.log(`[ networkId: ${networkId} ]`);
console.log(`[ xymid: ${xymhex} ]`);
console.log(`[ epochAdjustment: ${epochAdjustment} ]`);
console.log(`[ feeMultiplier: ${feeMultiplier} ]`);
}
//アカウント ステータス account status
async function acntstat(account){
console.log(`== ACCOUNT STATUS ==`);
console.log(`[アドレス: ${account.address} ]`);
console.log(`[公開鍵: ${account.publicKey} ]`);
try{
json = await api(`/accounts/${account.address.toString()}`);
console.log(`[所有モザイク: ID, 数量]`);
for(mosaic of json.account.mosaics){
console.log(`[ ${mosaic.id}, ${mosaic.amount} ]`);
}
}catch{
console.log("ResourceNotFound")
}
}
//アカウント新規作成 new account
function newacnt(){
const prikey = core.PrivateKey.random()
return chain.createAccount(prikey)
}
//アカウント復元 account from private key string
function acnt(prikeyString){
const prikey = new core.PrivateKey(prikeyString)
return chain.createAccount(prikey)
}
function decadr(hexEncodedAddress){
return sym.Address.fromDecodedAddressHexString(hexEncodedAddress).toString()
}
//転送トランザクション transfer transaction
function trftx(address,mosaics,message){
messageData = new Uint8Array([
0x00, //現行アプリケーション対応 00:平文, 01:暗号化
...new TextEncoder().encode(message),
]);
return new sym.descriptors.TransferTransactionV1Descriptor(address,mosaics,messageData);
}
//署名&通知 sign and announce
async function sigan(desc,signer){
const tx = chain.createTransactionFromTypedDescriptor(desc,signer.publicKey,feeMultiplier,add2Hours);
txstat(tx);
const signature = signer.signTransaction(tx);
const requestBody = sym.SymbolTransactionFactory.attachSignature(tx, signature);
const hash = chain.hashTransaction(tx).toString();
const res = await fetch(
new URL('/transactions', node),
{
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: requestBody,
}
);
console.log(res);
return hash;
}
//埋め込みトランザクション
function embed(tx,pubkey){
return chain.createEmbeddedTransactionFromTypedDescriptor(tx,pubkey);
}
//アグリゲートコンプリートトランザクション aggregate complete transaction
function aggcptx(transactions,initPublicKey,cosignatureCount){
const transactionsHash = sym.SymbolFacade.hashEmbeddedTransactions(transactions);
const desc = new sym.descriptors.AggregateCompleteTransactionV2Descriptor(transactionsHash,transactions,[]);
const tx = chain.createTransactionFromTypedDescriptor(desc,initPublicKey,feeMultiplier,add2Hours,cosignatureCount);
return tx;
}
//署名&連署&通知 sign and cosign and announce
async function sigcosan(tx,signer,cosigners){
//署名
const signature = signer.signTransaction(tx);
sym.SymbolTransactionFactory.attachSignature(tx, signature);
//連署
for(cosigner of cosigners){
const cosignature = cosigner.cosignTransaction(tx);
tx.cosignatures.push(cosignature);
}
txstat(tx);
//通知
const requestBody = sym.SymbolTransactionFactory.attachSignature(tx, tx.signature);
const hash = chain.hashTransaction(tx).toString();
const res = await fetch(
new URL('/transactions', node),
{
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: requestBody,
}
);
console.log(res);
return hash;
}
//モザイクトークン(hexID,数量)
function mosaic(mosaicId,amount){
const mosaicNumber = typeof value === 'string' ? BigInt("0x" + mosaicId) : mosaicId;
return new sym.descriptors.UnresolvedMosaicDescriptor(
new sym.models.UnresolvedMosaicId(mosaicNumber),
new sym.models.Amount(amount)
)
}
//アカウント新規作成 new account
function newacnt(){
const prikey = core.PrivateKey.random()
return chain.createAccount(prikey)
}
//アカウント復元 account from private key string
function acnt(prikeyString){
const prikey = new core.PrivateKey(prikeyString)
return chain.createAccount(prikey)
}
//テキスト暗号化 encode to hex from text string
function enchex(sendPrikey,recvPubkey,message){
return core.utils.uint8ToHex(sendPrikey.messageEncoder().encode(recvPubkey,new TextEncoder().encode(message)))
}
//テキスト復号化 decode from hex to text string
function dechex(recvPrikey,sendPubkey,hex){
msgDecoded = recvPrikey.messageEncoder().tryDecode(
sendPubkey,
core.utils.hexToUint8(hex)
)
const textDecoder = new TextDecoder();
return textDecoder.decode(msgDecoded.message);
}
async function generateSalt(length) {
const array = new Uint8Array(length);
window.crypto.getRandomValues(array);
return array;
}
async function deriveKey(password, salt, iterations, keyLength) {
const enc = new TextEncoder();
const keyMaterial = await window.crypto.subtle.importKey(
'raw',
enc.encode(password),
{ name: 'PBKDF2' },
false,
['deriveKey']
);
return window.crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt: salt,
iterations: iterations,
hash: 'SHA-512'
},
keyMaterial,
{ name: 'AES-GCM', length: keyLength },
false,
['encrypt', 'decrypt']
);
}
function generateIV() {
return window.crypto.getRandomValues(new Uint8Array(12)); // AES-GCM では 12 バイトの IV を使用
}
async function enckey(account, password) {
const secretKey = account.keyPair.privateKey
const salt = await generateSalt(16);
const iterations = 310000; // 推奨される最低回数
const keyLength = 256; // 256ビット
const derivedKey = await deriveKey(password, salt, iterations, keyLength);
const iv = generateIV();
const enc = new TextEncoder();
const encrypted = await window.crypto.subtle.encrypt(
{
name: 'AES-GCM',
iv: iv
},
derivedKey,
enc.encode(secretKey)
);
return {
salt: core.utils.uint8ToHex(salt),
iv: core.utils.uint8ToHex(iv),
ciphertext: core.utils.uint8ToHex(new Uint8Array(encrypted))
};
}
async function deckey(hexSalt, hexIV, hexCiphertext, password) {
const salt = core.utils.hexToUint8(hexSalt)
const iv = core.utils.hexToUint8(hexIV)
const ciphertext = core.utils.hexToUint8(hexCiphertext)
const iterations = 310000; // 推奨される最低回数
const keyLength = 256; // 256ビット
const derivedKey = await deriveKey(password, salt, iterations, keyLength);
try {
const decrypted = await window.crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: iv
},
derivedKey,
ciphertext
);
const dec = new TextDecoder();
return dec.decode(decrypted);
} catch (error) {
console.error("Decryption failed:", error);
throw new Error("Decryption failed");
}
}
//転送トランザクション transfer transaction
function trftx(address,mosaics,message){
messageData = new Uint8Array([
0x00, //現行アプリケーション対応 00:平文, 01:暗号化
...new TextEncoder().encode(message),
]);
return new sym.descriptors.TransferTransactionV1Descriptor(address,mosaics,messageData);
}
//署名&通知 sign and announce
async function sigan(desc,signer){
const tx = chain.createTransactionFromTypedDescriptor(desc,signer.publicKey,feeMultiplier,add2Hours);
txstat(tx);
const signature = signer.signTransaction(tx);
const requestBody = sym.SymbolTransactionFactory.attachSignature(tx, signature);
const hash = chain.hashTransaction(tx).toString();
const res = await fetch(
new URL('/transactions', node),
{
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: requestBody,
}
);
console.log(res);
return hash;
}
//埋め込みトランザクション
function embed(tx,pubkey){
return chain.createEmbeddedTransactionFromTypedDescriptor(tx,pubkey);
}
//アグリゲートコンプリートトランザクション aggregate complete transaction
function aggcptx(transactions,initPublicKey,cosignatureCount){
const transactionsHash = sym.SymbolFacade.hashEmbeddedTransactions(transactions);
const desc = new sym.descriptors.AggregateCompleteTransactionV2Descriptor(transactionsHash,transactions,[]);
const tx = chain.createTransactionFromTypedDescriptor(desc,initPublicKey,feeMultiplier,add2Hours,cosignatureCount);
return tx;
}
//署名&連署&通知 sign and cosign and announce
async function sigcosan(tx,signer,cosigners){
//署名
const signature = signer.signTransaction(tx);
sym.SymbolTransactionFactory.attachSignature(tx, signature);
//連署
for(cosigner of cosigners){
const cosignature = cosigner.cosignTransaction(tx);
tx.cosignatures.push(cosignature);
}
txstat(tx);
//通知
const requestBody = sym.SymbolTransactionFactory.attachSignature(tx, tx.signature);
const hash = chain.hashTransaction(tx).toString();
const res = await fetch(
new URL('/transactions', node),
{
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: requestBody,
}
);
console.log(res);
return hash;
}
//モザイクトークン(hexID,数量)
function mosaic(mosaicId,amount){
const mosaicNumber = typeof value === 'string' ? BigInt("0x" + mosaicId) : mosaicId;
return new sym.descriptors.UnresolvedMosaicDescriptor(
new sym.models.UnresolvedMosaicId(mosaicNumber),
new sym.models.Amount(amount)
)
}
function nonce() {
const buffer = new ArrayBuffer(sym.models.MosaicNonce.SIZE);
const view = new DataView(buffer);
crypto.getRandomValues(new Uint8Array(buffer));
return view.getUint32(0, true);
}
//MosaicDefinitionTransaction
function mosdftx(mosaicId,duration,mosaicNonce,mosaicFlags,divisibility){
const desc = new sym.descriptors.MosaicDefinitionTransactionV1Descriptor(
new sym.models.MosaicId(mosaicId),
new sym.models.BlockDuration(duration),
new sym.models.MosaicNonce(mosaicNonce),
new sym.models.MosaicFlags(mosaicFlags),
divisibility
)
return desc;
}
//MosaicSupplyChangeTransaction
function mossctx(mosaicId,amount,mosaicSupplyChangeAction){
const desc = new sym.descriptors.MosaicSupplyChangeTransactionV1Descriptor(
new sym.models.UnresolvedMosaicId(mosaicId),
new sym.models.Amount(amount),
mosaicSupplyChangeAction
)
return desc
}
//NamespaceRegistrationTransaction(ROOT)
function nsregtx(name,duration){
const descriptor = new sym.descriptors.NamespaceRegistrationTransactionV1Descriptor(
new sym.models.NamespaceId(sym.generateNamespaceId(name)),
sym.models.NamespaceRegistrationType.ROOT,
new sym.models.BlockDuration(duration),
undefined,
name
)
return descriptor
}
//NamespaceRegistrationTransaction(CHILD)
function subnsregtx(name,parentName){
const parentNamespaceNumber = sym.generateNamespacePath(parentName).pop()
const descriptor = new sym.descriptors.NamespaceRegistrationTransactionV1Descriptor(
new sym.models.NamespaceId(sym.generateNamespaceId(name)),
sym.models.NamespaceRegistrationType.CHILD,
undefined,
new sym.models.NamespaceId(parentNamespaceNumber),
name
)
return descriptor
}
//AddressAliasTransaction
function adralitx(hexNamespaceId,address,aliasAction){
const descriptor = new sym.descriptors.AddressAliasTransactionV1Descriptor(
new sym.models.NamespaceId(BigInt(`0x${hexNamespaceId}`)),
address,
aliasAction
)
return descriptor
}
//MosaicAliasTransaction
function mosalitx(hexNamespaceId,hexMosaicId,aliasAction){
const descriptor = new sym.descriptors.MosaicAliasTransactionV1Descriptor(
new sym.models.NamespaceId(BigInt(`0x${hexNamespaceId}`)),
new sym.models.MosaicId(BigInt(`0x${hexMosaicId}`)),
aliasAction
)
return descriptor
}
//ネームスペースをアドレスに変換
function nstoadr(name){
const hex = nstohexadr(name)
return sym.Address.fromDecodedAddressHexString(hex)
}
//ネームスペースを16進数アドレス文字列に変換
function nstohexadr(name){
console.log("name")
const namespaceNumber = sym.generateNamespacePath(name).pop()
const address = sym.Address.fromNamespaceId(
new sym.models.NamespaceId(namespaceNumber),
chain.network.identifier
)
return core.utils.uint8ToHex(address.bytes);
}
//ネームスペースを16進数ネームスペース文字列に変換
function nstohex(name){
const namespaceNumber = sym.generateNamespacePath(name).pop()
return namespaceNumber.toString(16).toUpperCase()
}
//AccountMetadataTransaction
function acntmetatx(targetAddress,key,sizeDeltaAndValueMap){
const descriptor = new sym.descriptors.AccountMetadataTransactionV1Descriptor(
targetAddress, // ターゲットアドレス
key, // キー
sizeDeltaAndValueMap.delta, // サイズ差分
sizeDeltaAndValueMap.value // 値
)
return descriptor
}
//MosaicMetadataTransaction
function mosmetatx(targetAddress,key,targetMosaic,sizeDeltaAndValueMap){
const descriptor = new sym.descriptors.MosaicMetadataTransactionV1Descriptor(
targetAddress, // ターゲットアドレス
key, // キー
new sym.models.UnresolvedMosaicId(BigInt(`0x${targetMosaic}`)), // メタデータ記録先モザイク
sizeDeltaAndValueMap.delta, // サイズ差分
sizeDeltaAndValueMap.value // 値
)
return descriptor
}
//NamespaceMetadataTransaction
function nsmetatx(targetAddress,key,targetNamespace,sizeDeltaAndValueMap){
console.log("nsmetatx")
const descriptor = new sym.descriptors.NamespaceMetadataTransactionV1Descriptor(
targetAddress, // ターゲットアドレス
key, // キー
new sym.models.NamespaceId(BigInt(`0x${targetNamespace}`)), // メタデータ記録先モザイク
sizeDeltaAndValueMap.delta, // サイズ差分
sizeDeltaAndValueMap.value // 値
)
return descriptor
}
function updvalue(info,value){
const encodedValue = new TextEncoder().encode(value)
const delta = encodedValue.length
const res = {}
if (info.data.length > 0) {
res.delta = delta - info.data[0].metadataEntry.valueSize
res.value = sym.metadataUpdateValue(core.utils.hexToUint8(info.data[0].metadataEntry.value), encodedValue )
}else{
res.delta = delta
res.value = encodedValue
}
return res
}
function metaquery(addressOrId,sourceAddress,scopedMetadataKey,metadataType){
let query;
if(metadataType === 0){
//Address
query = new URLSearchParams({
"targetAddress": addressOrId.toString(),
"sourceAddress": sourceAddress.toString(),
"scopedMetadataKey": scopedMetadataKey.toString(16).toUpperCase(),
"metadataType": metadataType
});
}else{
//Mosaic or Namespace
query = new URLSearchParams({
"targetId": addressOrId.toString(16).toUpperCase(),
"sourceAddress": sourceAddress.toString(),
"scopedMetadataKey": scopedMetadataKey.toString(16).toUpperCase(),
"metadataType": metadataType
});
}
return query;
}
function mosaic(mosaicId,amount){
const mosaicNumber = typeof mosaicId === 'string' ? BigInt("0x" + mosaicId) : mosaicId;
return new sym.descriptors.UnresolvedMosaicDescriptor(
new sym.models.UnresolvedMosaicId(mosaicNumber),
new sym.models.Amount(amount)
)
}
//アグリゲートボンデッドトランザクション aggregate bobded transaction
function aggbdtx(transactions,initPublicKey,cosignatureCount){
const transactionsHash = sym.SymbolFacade.hashEmbeddedTransactions(transactions);
const desc = new sym.descriptors.AggregateBondedTransactionV2Descriptor(transactionsHash,transactions,[]);
const tx = chain.createTransactionFromTypedDescriptor(desc,initPublicKey,feeMultiplier,add2Hours,cosignatureCount);
return tx;
}
function hlocktx(hash){
const descriptor = new sym.descriptors.HashLockTransactionV1Descriptor( // Txタイプ:ハッシュロックTx
new sym.descriptors.UnresolvedMosaicDescriptor(
new sym.models.UnresolvedMosaicId(xymid), // UnresolvedMosaic:未解決モザイク
new sym.models.Amount(10n * 1000000n) // 10xym固定値
),
new sym.models.BlockDuration(480n), // ロック有効期限
core.utils.hexToUint8(hash) // アグリゲートトランザクションのハッシュ値を登録
);
return descriptor
}
function slocktx(address,secret,mosaic){
descriptor = new sym.descriptors.SecretLockTransactionV1Descriptor( // Txタイプ:シークレットロックTx
address, // 解除時の転送先:Bob
secret, // ロック用キーワード
mosaic,
new sym.models.BlockDuration(480n), // ロック期間(ブロック数)
sym.models.LockHashAlgorithm.SHA3_256 // ロックキーワード生成に使用したアルゴリズム
)
return descriptor
}
function prooftx(address,secret,proof){
descriptor = new sym.descriptors.SecretProofTransactionV1Descriptor( // Txタイプ:シークレットプルーフTx
address, // 解除アカウント(受信アカウント)
secret, // ロックキーワード
sym.models.LockHashAlgorithm.SHA3_256, // ロックキーワード生成に使用したアルゴリズム
proof // 解除用キーワード
);
return descriptor
}
function sig(aggregateTx,signer){
const signature = signer.signTransaction(aggregateTx);
const requestBody = sym.SymbolTransactionFactory.attachSignature(aggregateTx, signature);
const hash = chain.hashTransaction(aggregateTx).toString();
return {request:JSON.parse(requestBody),hash:hash}
}
async function cosan(aggregateTx,cosigner){
const cosignature = cosigner.cosignTransaction(aggregateTx, true);
const body= {
"parentHash": cosignature.parentHash.toString(),
"signature": cosignature.signature.toString(),
"signerPublicKey": cosignature.signerPublicKey.toString(),
"version": cosignature.version.toString()
};
const res = await api('/transactions/cosignature',"PUT",body)
console.log(res)
return cosignature.parentHash.toString();
}
function msigtx(minRemoval,minApproval,addressAdditions,addressDeletions){
descriptor = new sym.descriptors.MultisigAccountModificationTransactionV1Descriptor( // Txタイプ:マルチシグ設定Tx
minRemoval, // minRemoval:除名のために必要な最小署名者数増分
minApproval, // minApproval:承認のために必要な最小署名者数増分
addressAdditions,
addressDeletions // 除名対象アドレスリスト
);
return descriptor
}
async function cosan(aggregateTx,cosigner){
const cosignature = cosigner.cosignTransaction(aggregateTx, true);
const body= {
"parentHash": cosignature.parentHash.toString(),
"signature": cosignature.signature.toString(),
"signerPublicKey": cosignature.signerPublicKey.toString(),
"version": cosignature.version.toString()
};
const res = await api('/transactions/cosignature',"PUT",body)
console.log(res)
return cosignature.parentHash.toString();
}
uid = "";
funcMap = {};
function addCallback(channel, callback){
if (!funcMap.hasOwnProperty(channel)) {
funcMap[channel] = [];
}
funcMap[channel].push(callback);
};
function connectWebSocket(targetNode) {
return new Promise((resolve, reject) => {
const wsEndpoint = targetNode.replace("http", "ws") + "/ws";
const socket = new WebSocket(wsEndpoint);
socket.onmessage = function (event) {
const response = JSON.parse(event.data);
if(response.uid){
uid = response.uid
resolve({socket:socket,uid:response.uid});
}else{
if (funcMap.hasOwnProperty(response.topic)) {
funcMap[response.topic].forEach(f => {
f(response.data);
});
}
}
};
socket.onclose = async function () {
uid = "";
funcMap = {};
console.log("WebSocket connection closed.");
};
socket.onopen = function () {console.log("WebSocket connected");};
socket.onerror = function () {reject(new Error("Failed to connect to the WebSocket"));};
});
}
function acntrestx(restrictionFlags,restrictionAdditions,restrictionDeletions){
descriptor = new sym.descriptors.AccountAddressRestrictionTransactionV1Descriptor( // Txタイプ:アドレス制限設定Tx
flags,restrictionAdditions,restrictionDeletions
);
return descriptor
}
function mosrestx(restrictionFlags,restrictionAdditions,restrictionDeletions){
descriptor = new sym.descriptors.AccountMosaicRestrictionTransactionV1Descriptor( // Txタイプ:アドレス制限設定Tx
flags,restrictionAdditions,restrictionDeletions
);
return descriptor
}
function operestx(flags,restrictionAdditions,restrictionDeletions){
descriptor = new sym.descriptors.AccountOperationRestrictionTransactionV1Descriptor(
flags,restrictionAdditions,restrictionDeletions
);
return descriptor
}
function mosglorestx(
mosaicId,referenceMosaicId,restrictionKey,
previousRestrictionValue,newRestrictionValue,
previousRestrictionType,newRestrictionType
){
descriptor = new sym.descriptors.MosaicGlobalRestrictionTransactionV1Descriptor(
mosaicId,referenceMosaicId,restrictionKey,
previousRestrictionValue,newRestrictionValue,
previousRestrictionType,newRestrictionType
)
return descriptor
}
function mosadrrestx(
mosaicId,restrictionKey,previousRestrictionValue,newRestrictionValue,targetAddress
){
descriptor = new sym.descriptors.MosaicAddressRestrictionTransactionV1Descriptor(
mosaicId,restrictionKey,previousRestrictionValue,newRestrictionValue,targetAddress
)
return descriptor
}
function moshex(moshex){
return new sym.models.UnresolvedMosaicId(BigInt("0x" + moshex))
}
negkey = 0xFFFFFFFFFFFFFFFFn
sha3_256 = (await import('https://cdn.skypack.dev/@noble/hashes/sha3')).sha3_256;
function sig(aggregateTx,signer){
const signature = signer.signTransaction(aggregateTx);
const requestBody = sym.SymbolTransactionFactory.attachSignature(aggregateTx, signature);
const hash = chain.hashTransaction(aggregateTx).toString();
return {request:JSON.parse(requestBody),hash:hash}
}
bob = newacnt()
console.log(`bob address: ${bob.address}`)
console.log(`bob private key: ${bob.keyPair.privateKey}`)
function hexToUint8(hex) {
if (hex.length % 2 !== 0) {
throw new Error('Hex string must have an even length');
}
const byteArray = new Uint8Array(hex.length / 2);
for (let i = 0; i < hex.length; i += 2) {
byteArray[i / 2] = parseInt(hex.slice(i, i + 2), 16);
}
return byteArray;
}
// Uint8ArrayをHEX文字列に変換するヘルパー関数
function uint8ToHex(uint8Array) {
return Array.from(uint8Array)
.map(byte => byte.toString(16).padStart(2, '0'))
.join('').toUpperCase();
}
sha3_256 = (await import('https://cdn.skypack.dev/@noble/hashes/sha3')).sha3_256;
//葉のハッシュ値取得関数
function getLeafHash(encodedPath, leafValue){
const hasher = sha3_256.create();
return uint8ToHex(hasher.update(hexToUint8(encodedPath + leafValue)).digest());
}
//枝のハッシュ値取得関数
function getBranchHash(encodedPath, links){
const branchLinks = Array(16).fill(uint8ToHex(new Uint8Array(32)));
links.forEach((link) => {
branchLinks[parseInt(`0x${link.bit}`, 16)] = link.link;
});
const hasher = sha3_256.create();
const bHash = uint8ToHex(hasher.update(hexToUint8(encodedPath + branchLinks.join(''))).digest());
return bHash;
}
//ワールドステートの検証
function checkState(stateProof,stateHash,pathHash,rootHash){
merkleLeaf = undefined;
merkleBranches = [];
stateProof.tree.forEach(n => {
if (n.type === 255) {
merkleLeaf = n;
} else {
merkleBranches.push(n);
}
});
merkleBranches.reverse();
const leafHash = getLeafHash(merkleLeaf.encodedPath,stateHash);
let linkHash = leafHash; //最初のlinkHashはleafHash
let bit="";
for(let i = 0; i < merkleBranches.length; i++){
const branch = merkleBranches[i];
const branchLink = branch.links.find(x=>x.link === linkHash)
linkHash = getBranchHash(branch.encodedPath,branch.links);
bit = merkleBranches[i].path.slice(0,merkleBranches[i].nibbleCount) + branchLink.bit + bit ;
}
const treeRootHash = linkHash; //最後のlinkHashはrootHash
let treePathHash = bit + merkleLeaf.path;
if(treePathHash.length % 2 == 1){
treePathHash = treePathHash.slice( 0, -1 );
}
//検証
console.log(treeRootHash === rootHash);
console.log(treePathHash === pathHash);
}
alice = acnt("C9E741092833FEA79D7DB7DC839506BBEB6704631DB9B4D59C60A02BF6B0200C");
console.log("Good Luck!");