Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Symbol SDK を使ってデリゲートハーベスティングするクソコードです。このコードでハーベスト成功しました。Symbol SDK のバージョンに応じて使用するファイルを変えてください。
/*
このプログラムは Symbol SDK v0.21.0 向けです
動かす前に
npm init
npm install symbol-sdk rxjs asn1js pkijs
を実行してください
VRF Key Link, Account Key Link, Node Key Link がすべて行われていない状態が前提のプログラムです。
一部又は全て Link されている場合は、プログラムを書き換えるか、一旦 Unlink してください。
*/
const symbol_sdk = require("symbol-sdk");
const tls = require("tls");
const pkijs = require("pkijs");
const asn1js = require("asn1js");
//ANNOUNCE_NODE に適当なノードのURLを入れます
const ANNOUNCE_NODE = "http://api-01.us-west-1.0.10.0.x.symboldev.network:3000";
const GENERATION_HASH = "6C1B92391CCB41C96478471C2634C111D9E989DECD66130C0430B5B8D20117CD";
const NETWORK_TYPE = symbol_sdk.NetworkType.TEST_NET;
/*
GET_NODE_PUBLICKEY_AUTOMATICALLY を true にして、DELEGATE_NODE に port (通常は 7900) と host (アドレスのみ、http:// や ポート番号は要りません) を入れると、
自動的にパブリックキーを取得してきて、nodeAccount としてそれを使用するようになります。
既にパブリックキーが分かってる場合は、下の nodePublicKey に入れた上で GET_NODE_PUBLICKEY_AUTOMATICALLY をfalse にしてください。
*/
const DELEGATE_NODE = {
port: 7900,
host: "ここに委任するノードのアドレス",
}
const GET_NODE_PUBLICKEY_AUTOMATICALLY = true;
/*
mainAccount にはメインの(importance が溜まってる)アカウントの秘密鍵を入れます。
announcerAccount には mainAccount と同じ秘密鍵か、それ以外の残高がある別のアカウントの秘密鍵のどちらかを入れます。
vrfAccount、remoteAccount は自動生成されます。既存の秘密鍵を使用する場合はコメントの部分を変更してください。
vrfAccount、remoteAccount をこのプログラムで生成した場合、秘密鍵が表示されるので一応書きとめておいてください。
nodePublicKey には https://github.com/nemtech/symbol-docs/issues/532 にアクセスして表示された中の、Node publicKey のどれかを入れます。上の GET_NODE_PUBLICKEY_AUTOMATICALLY を true にしていれば自動で取得するので空で構いません。
*/
const mainAccount = symbol_sdk.Account.createFromPrivateKey("ここに秘密鍵", NETWORK_TYPE);
const announcerAccount = symbol_sdk.Account.createFromPrivateKey("ここに秘密鍵", NETWORK_TYPE);
const vrfAccount = symbol_sdk.Account.generateNewAccount(NETWORK_TYPE);
//const vrfAccount = symbol_sdk.Account.createFromPrivateKey("ここに秘密鍵", NETWORK_TYPE);
const remoteAccount = symbol_sdk.Account.generateNewAccount(NETWORK_TYPE);
//const remoteAccount = symbol_sdk.Account.createFromPrivateKey("ここに秘密鍵", NETWORK_TYPE);
let nodePublicKey = "ここに公開鍵(自動取得なら空に)";
console.log("VRF account private key: ", vrfAccount.privateKey);
console.log("Remote account private key: ", remoteAccount.privateKey, "\n");
const sleep = msec => new Promise(resolve => setTimeout(resolve, msec));
const getNodePublicKey = (host, port) => {
return new Promise((resolve, reject) => {
const contextOptions = {
minVersion: "TLSv1.3",
sigalgs: "ed25519",
};
const connectionOptions = {
host,
port,
rejectUnauthorized: false,
secureContext: tls.createSecureContext(contextOptions),
checkServerIdentity: () => undefined,
};
const serverSocket = tls.connect(connectionOptions, () => {
const peerCertificate = serverSocket.getPeerCertificate();
const ber = new Uint8Array(peerCertificate.raw).buffer;
const asn1 = asn1js.fromBER(ber);
const certificate = new pkijs.Certificate({schema: asn1.result});
const hexPublicKey = Buffer.from(certificate.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHex).toString("hex");
serverSocket.end();
resolve(hexPublicKey);
});
serverSocket.on("error", err => {
reject(err);
}).once("close", () => {
serverSocket.destroy();
});
});
}
(async() => {
//API 叩く準備
const txHttp = new symbol_sdk.TransactionHttp(ANNOUNCE_NODE);
const txStatusHttp = new symbol_sdk.TransactionStatusHttp(ANNOUNCE_NODE);
if(GET_NODE_PUBLICKEY_AUTOMATICALLY){
nodePublicKey = await getNodePublicKey(DELEGATE_NODE.host, DELEGATE_NODE.port).catch(v => "");
}
if(!nodePublicKey){
console.log("Node public key が空です (自動取得の場合、取得失敗です)");
process.exit(1);
}
console.log("Node public key: ", nodePublicKey.toUpperCase(), "\n");
const nodeAccount = symbol_sdk.PublicAccount.createFromPublicKey(nodePublicKey, NETWORK_TYPE);
//VRF Key Link のトランザクションを生成
const vrfKeyLinkTx = symbol_sdk.VrfKeyLinkTransaction.create(
symbol_sdk.Deadline.create(),
vrfAccount.publicKey,
symbol_sdk.LinkAction.Link,
NETWORK_TYPE,
);
//Account Key Link のトランザクションを生成
const accountKeyLinkTx = symbol_sdk.AccountKeyLinkTransaction.create(
symbol_sdk.Deadline.create(),
remoteAccount.publicKey,
symbol_sdk.LinkAction.Link,
NETWORK_TYPE,
);
//Node Key Link のトランザクションを生成
const nodeKeyLinkTx = symbol_sdk.NodeKeyLinkTransaction.create(
symbol_sdk.Deadline.create(),
nodeAccount.publicKey,
symbol_sdk.LinkAction.Link,
NETWORK_TYPE,
);
//アグリゲートトランザクションの生成
const aggregateTransaction = symbol_sdk.AggregateTransaction.createComplete(
symbol_sdk.Deadline.create(),
[
vrfKeyLinkTx.toAggregate(mainAccount.publicAccount),
accountKeyLinkTx.toAggregate(mainAccount.publicAccount),
nodeKeyLinkTx.toAggregate(mainAccount.publicAccount),
],
NETWORK_TYPE,
[],
symbol_sdk.UInt64.fromUint(300000),
);
//トランザクションを main account で署名
const signedAggregateTx = mainAccount.sign(aggregateTransaction, GENERATION_HASH);
console.log("Key link トランザクションハッシュ: ", signedAggregateTx.hash);
//トランザクションをアナウンス
const res1 = await txHttp.announce(signedAggregateTx).toPromise().catch(v => false);
if(!res1){
console.log("Key link トランザクションのアナウンスに失敗しました。");
process.exit(1);
}
console.log("Key link トランザクションの承認を待っています・・・");
//ここから トランザクションの承認待ち
let i = 0;
let result = false;
do{
i++;
if(i > 24){
console.log("Key link トランザクションの承認を確認できませんでした。");
process.exit(1);
break;
}
console.log(`${i} 回目の確認`);
await sleep(5000);
const status = await txStatusHttp.getTransactionStatus(signedAggregateTx.hash).toPromise().catch(v => false);
if(!status)
continue;
if(status.group !== "confirmed")
continue;
result = true;
console.log("Key link の承認を確認しました。");
}while(!result);
//ここまで トランザクションの承認待ち
//Persistent delegation request のトランザクションを生成
const persistentDelegationRequestTx = symbol_sdk.PersistentDelegationRequestTransaction.createPersistentDelegationRequestTransaction(
symbol_sdk.Deadline.create(),
remoteAccount.privateKey,
vrfAccount.privateKey,
nodeAccount.publicKey,
NETWORK_TYPE,
symbol_sdk.UInt64.fromUint(100000),
);
//SDK が修正されるまでの暫定的な処置です。SDK が更新されたらこの行を削除してください。
persistentDelegationRequestTx.message.payload = persistentDelegationRequestTx.message.payload.replace(/^E201735761802AFE/i, "FE2A8061577301E2");
//Persistent delegation request のトランザクションを announcer account で署名
const signedPersistentDelegationRequestTx = announcerAccount.sign(persistentDelegationRequestTx, GENERATION_HASH);
console.log("Persistent delegation request トランザクションハッシュ: ", signedPersistentDelegationRequestTx.hash);
//Persistent delegation request のトランザクションをアナウンス
const res2 = await txHttp.announce(signedPersistentDelegationRequestTx).toPromise().catch(v => false);
if(!res2){
console.log("Persistent delegation request トランザクションのアナウンスに失敗しました。");
process.exit(1);
}
console.log("Persistent delegation request トランザクションの承認を待っています・・・");
//ここから トランザクションの承認待ち
i = 0;
result = false;
do{
i++;
if(i > 24){
console.log("Persistent delegation request トランザクションの承認を確認できませんでした。");
process.exit(1);
break;
}
console.log(`${i} 回目の確認`);
await sleep(5000);
const status = await txStatusHttp.getTransactionStatus(signedPersistentDelegationRequestTx.hash).toPromise().catch(v => false);
if(!status)
continue;
if(status.group !== "confirmed")
continue;
result = true;
console.log("Persistent delegation request の承認を確認しました。");
console.log("\n(多分)デリゲートハーベスティングが有効になりました。");
}while(!result);
//ここまで トランザクションの承認待ち
})();
/*
このプログラムは Symbol SDK v0.22.0 向けです
動かす前に
npm init
npm install symbol-sdk rxjs asn1js pkijs
を実行してください
VRF Key Link, Account Key Link, Node Key Link がすべて行われていない状態が前提のプログラムです。
一部又は全て Link されている場合は、プログラムを書き換えるか、一旦 Unlink してください。
*/
const symbol_sdk = require("symbol-sdk");
const tls = require("tls");
const pkijs = require("pkijs");
const asn1js = require("asn1js");
//ANNOUNCE_NODE に適当なノードのURLを入れます
const ANNOUNCE_NODE = "http://api-01.us-west-1.0.10.0.x.symboldev.network:3000";
const NETWORK_TYPE = symbol_sdk.NetworkType.TEST_NET;
/*
GET_NODE_PUBLICKEY_AUTOMATICALLY を true にして、DELEGATE_NODE に port (通常は 7900) と host (アドレスのみ、http:// や ポート番号は要りません) を入れると、
自動的にパブリックキーを取得してきて、nodeAccount としてそれを使用するようになります。
既にパブリックキーが分かってる場合は、下の nodePublicKey に入れた上で GET_NODE_PUBLICKEY_AUTOMATICALLY をfalse にしてください。
*/
const DELEGATE_NODE = {
port: 7900,
apiPort: 3000,
host: "ここに委任するノードのアドレス",
}
const GET_NODE_PUBLICKEY_AUTOMATICALLY = true;
/*
mainAccount にはメインの(importance が溜まってる)アカウントの秘密鍵を入れます。
announcerAccount には mainAccount と同じ秘密鍵か、それ以外の残高がある別のアカウントの秘密鍵のどちらかを入れます。
vrfAccount、remoteAccount は自動生成されます。既存の秘密鍵を使用する場合はコメントの部分を変更してください。
vrfAccount、remoteAccount をこのプログラムで生成した場合、秘密鍵が表示されるので一応書きとめておいてください。
nodePublicKey には https://github.com/nemtech/symbol-docs/issues/532 にアクセスして表示された中の、Node publicKey のどれかを入れます。上の GET_NODE_PUBLICKEY_AUTOMATICALLY を true にしていれば自動で取得するので空で構いません。
*/
const mainAccount = symbol_sdk.Account.createFromPrivateKey("ここに秘密鍵", NETWORK_TYPE);
const announcerAccount = symbol_sdk.Account.createFromPrivateKey("ここに秘密鍵", NETWORK_TYPE);
const vrfAccount = symbol_sdk.Account.generateNewAccount(NETWORK_TYPE);
//const vrfAccount = symbol_sdk.Account.createFromPrivateKey("ここに秘密鍵", NETWORK_TYPE);
const remoteAccount = symbol_sdk.Account.generateNewAccount(NETWORK_TYPE);
//const remoteAccount = symbol_sdk.Account.createFromPrivateKey("ここに秘密鍵", NETWORK_TYPE);
let nodePublicKey = "ここに公開鍵(自動取得なら空に)";
console.log("VRF account private key: ", vrfAccount.privateKey);
console.log("Remote account private key: ", remoteAccount.privateKey, "\n");
const sleep = msec => new Promise(resolve => setTimeout(resolve, msec));
const getNodePublicKey = (host, port) => {
return new Promise((resolve, reject) => {
const contextOptions = {
minVersion: "TLSv1.3",
sigalgs: "ed25519",
};
const connectionOptions = {
host,
port,
rejectUnauthorized: false,
secureContext: tls.createSecureContext(contextOptions),
checkServerIdentity: () => undefined,
};
const serverSocket = tls.connect(connectionOptions, () => {
const peerCertificate = serverSocket.getPeerCertificate();
const ber = new Uint8Array(peerCertificate.raw).buffer;
const asn1 = asn1js.fromBER(ber);
const certificate = new pkijs.Certificate({schema: asn1.result});
const hexPublicKey = Buffer.from(certificate.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHex).toString("hex");
serverSocket.end();
resolve(hexPublicKey);
});
serverSocket.on("error", err => {
reject(err);
}).once("close", () => {
serverSocket.destroy();
});
});
}
(async() => {
//API 叩く準備
const delegateNodeURL = ["http://", DELEGATE_NODE.host, ":", DELEGATE_NODE.apiPort].join("");
const repositoryFactoryAnnounceNode = new symbol_sdk.RepositoryFactoryHttp(ANNOUNCE_NODE, {
networkType: NETWORK_TYPE,
});
const repositoryFactoryDelegateNode = new symbol_sdk.RepositoryFactoryHttp(delegateNodeURL, {
networkType: NETWORK_TYPE,
});
const txHttp = repositoryFactoryAnnounceNode.createTransactionRepository();
const txStatusHttp = repositoryFactoryAnnounceNode.createTransactionStatusRepository();
const nodeHttp = repositoryFactoryDelegateNode.createNodeRepository();
//generation hash と epoch adjustment を取得
const generationHash = await repositoryFactoryAnnounceNode.getGenerationHash().toPromise();
const epochAdjustment = await repositoryFactoryAnnounceNode.getEpochAdjustment().toPromise();
//ノードパブリックキーの取得
if(GET_NODE_PUBLICKEY_AUTOMATICALLY){
nodePublicKey = await getNodePublicKey(DELEGATE_NODE.host, DELEGATE_NODE.port).catch(v => "");
}
if(!nodePublicKey){
console.log("Node public key が空です (自動取得の場合、取得失敗です)");
process.exit(1);
}
console.log("Node public key: ", nodePublicKey.toUpperCase(), "\n");
const nodeAccount = symbol_sdk.PublicAccount.createFromPublicKey(nodePublicKey, NETWORK_TYPE);
//VRF Key Link のトランザクションを生成
const vrfKeyLinkTx = symbol_sdk.VrfKeyLinkTransaction.create(
symbol_sdk.Deadline.create(epochAdjustment),
vrfAccount.publicKey,
symbol_sdk.LinkAction.Link,
NETWORK_TYPE,
);
//Account Key Link のトランザクションを生成
const accountKeyLinkTx = symbol_sdk.AccountKeyLinkTransaction.create(
symbol_sdk.Deadline.create(epochAdjustment),
remoteAccount.publicKey,
symbol_sdk.LinkAction.Link,
NETWORK_TYPE,
);
//Node Key Link のトランザクションを生成
const nodeKeyLinkTx = symbol_sdk.NodeKeyLinkTransaction.create(
symbol_sdk.Deadline.create(epochAdjustment),
nodeAccount.publicKey,
symbol_sdk.LinkAction.Link,
NETWORK_TYPE,
);
//アグリゲートトランザクションの生成
const aggregateTransaction = symbol_sdk.AggregateTransaction.createComplete(
symbol_sdk.Deadline.create(epochAdjustment),
[
vrfKeyLinkTx.toAggregate(mainAccount.publicAccount),
accountKeyLinkTx.toAggregate(mainAccount.publicAccount),
nodeKeyLinkTx.toAggregate(mainAccount.publicAccount),
],
NETWORK_TYPE,
[],
symbol_sdk.UInt64.fromUint(300000),
);
//トランザクションを main account で署名
const signedAggregateTx = mainAccount.sign(aggregateTransaction, generationHash);
console.log("Key link トランザクションハッシュ: ", signedAggregateTx.hash);
//トランザクションをアナウンス
const res1 = await txHttp.announce(signedAggregateTx).toPromise().catch(v => false);
if(!res1){
console.log("Key link トランザクションのアナウンスに失敗しました。");
process.exit(1);
}
console.log("Key link トランザクションの承認を待っています・・・");
//ここから トランザクションの承認待ち
let i = 0;
let result = false;
do{
i++;
if(i > 24){
console.log("Key link トランザクションの承認を確認できませんでした。");
process.exit(1);
break;
}
console.log(`${i} 回目の確認`);
await sleep(5000);
const status = await txStatusHttp.getTransactionStatus(signedAggregateTx.hash).toPromise().catch(v => false);
if(!status)
continue;
if(status.group !== "confirmed")
continue;
result = true;
console.log("Key link の承認を確認しました。");
}while(!result);
//ここまで トランザクションの承認待ち
//Persistent delegation request のトランザクションを生成
const persistentDelegationRequestTx = symbol_sdk.PersistentDelegationRequestTransaction.createPersistentDelegationRequestTransaction(
symbol_sdk.Deadline.create(epochAdjustment),
remoteAccount.privateKey,
vrfAccount.privateKey,
nodeAccount.publicKey,
NETWORK_TYPE,
symbol_sdk.UInt64.fromUint(100000),
);
//Persistent delegation request のトランザクションを announcer account で署名
const signedPersistentDelegationRequestTx = announcerAccount.sign(persistentDelegationRequestTx, generationHash);
console.log("Persistent delegation request トランザクションハッシュ: ", signedPersistentDelegationRequestTx.hash);
//Persistent delegation request のトランザクションをアナウンス
const res2 = await txHttp.announce(signedPersistentDelegationRequestTx).toPromise().catch(v => false);
if(!res2){
console.log("Persistent delegation request トランザクションのアナウンスに失敗しました。");
process.exit(1);
}
console.log("Persistent delegation request トランザクションの承認を待っています・・・");
//ここから トランザクションの承認待ち
i = 0;
result = false;
do{
i++;
if(i > 24){
console.log("Persistent delegation request トランザクションの承認を確認できませんでした。");
process.exit(1);
break;
}
console.log(`${i} 回目の確認`);
await sleep(5000);
const status = await txStatusHttp.getTransactionStatus(signedPersistentDelegationRequestTx.hash).toPromise().catch(v => false);
if(!status)
continue;
if(status.group !== "confirmed")
continue;
result = true;
console.log("Persistent delegation request の承認を確認しました。");
console.log("\n(多分)デリゲートハーベスティングが有効になりました。");
}while(!result);
//ここまで トランザクションの承認待ち
//ちゃんと委任出来てるかの確認(委任先の catapult-server バージョンが 0.10.0.4 以降で機能します)
const unlockedAccount = await nodeHttp.getUnlockedAccount().toPromise().catch(v => []);
if(unlockedAccount.includes(remoteAccount.publicKey)){
console.log("\nデリゲートハーベスティングが有効化されたことを確認しました。");
}else{
console.log("\nデリゲートハーベスティングが有効化されたことを確認できませんでした。(委任先のバージョンが 0.10.0.3 以下の場合はこの確認結果は正しくありません)");
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment