21
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

nemAdvent Calendar 2021

Day 9

Symbolを強制ガチホする

Last updated at Posted at 2021-12-08

#この記事を書こうと思ったきっかけ
私はチャートを見ると一喜一憂してしまうタイプの人間です.ガチホが正義とは言われておりますが,チャートを見るたびに暴落の恐怖で売ってしまいそうになってしまいます(同じような悩みを持っている方もいらっしゃるのではないでしょうか...!)
ガチホを固く決めたはずの私ですが12月の頭頃の下落でも売ろうか一瞬まよってしまいました.そこで,強制的にガチホする方法を考えました.

#強制ガチホするためには
私がぱっと思いついたのは次の4つでした
・他人に預かってもらう
・マルチシグで連署し,秘密鍵を物理的に分散させることでハードルをあげる
・ペーパーウォレットに入れて物理的に遠い貸金庫に預ける
・他人とマルチシグで管理する

しかしこれらの方法では
・他人に預かってもらう
=>他人が持ち逃げ,勝手に売却する可能性がある

・マルチシグで連署し,秘密鍵を物理的に分散させることでハードルをあげる
・ペーパーウォレットに入れて物理的に遠い貸金庫に預ける
=>その気になれば売却できる

・他人とマルチシグで管理する
=>意見の相違で何かあったら大変

これらの問題があります.仕方なく候補から外しました.
しかし,先日トークン交換の方法をまとめた記事を書いていた際,シークレットロックの機能を見てガチホに転用できそうだと考えました.

#シークレットロックについて
シークレットロックではアナウンスの際に指定されたシークレットとペアになっているプルーフを公開するまでXYMをロックし,送金を保留することができるといった特徴があります.この際,ロックの期限が切れるブロック数を最大365日(1051200ブロック)まで指定することができます.
ロックされている間はチェーンの仕組み上プルーフを公開しない限り絶対に取り出すことができません.この仕組みを利用します.以下に1万XYMを1年ガチホするスクリプトを書いてみました.

##免責
このコードを利用して発生した損害は一切責任を負いません.ご自身の判断でご利用ください.

const xym = require('symbol-sdk');
const crypto = require('crypto');
const sha3_256 = require('js-sha3').sha3_256;
var networkGenerationHash ='';
var epochAdjustment = '';
var node = "";

var repo;
var transactionHttp;
var currencyMosaicId;
var networkType;
var feemultiplier;

console.log("***************************************************************");
console.log(Date());
console.log("***************************************************************");
var networkType = xym.NetworkType.TEST_NET;

const a_account = xym.Account.createFromPrivateKey(
    '******',
    networkType,
);

const a_publicAccount = xym.PublicAccount.createFromPublicKey(
    'D77668C77A26F6979ED2D0C5EF99E0A1FFA7A988601E40E82B9A25861A6B2D12',
    networkType
);



(async()=>{

    node= 'http://tomato.xym-node.com:3000';
    repo = new xym.RepositoryFactoryHttp(node);
    transactionHttp = repo.createTransactionRepository();
    await getInfo();
    await makeSecretLock(10000);

})();
async function makeSecretLock(locks){

    const xymMosaic = new xym.Mosaic(
        currencyMosaicId,
        xym.UInt64.fromUint(locks * 1000000)//送金量*可分性(xymは6)
    );

    const random = crypto.randomBytes(20);
    const proof = random.toString('hex');
    const hash = sha3_256.create();
    const secret = hash.update(random).hex().toUpperCase();
    console.log('Secret:', secret);

    const gachiho = xym.SecretLockTransaction.create(
        xym.Deadline.create(epochAdjustment),
        xymMosaic,
        xym.UInt64.fromUint(24*60*60/30*365),
        xym.LockHashAlgorithm.Op_Sha3_256,
        secret,
        a_publicAccount.address,
        networkType,
    ).setMaxFee(feemultiplier);
    const signed = a_account.sign(
    gachiho,
    networkGenerationHash,
    );
    await anounceTX(signed);
}


async function getInfo(){
  feemultiplier = 10;
  epochAdjustment = await repo.getEpochAdjustment().toPromise();
  networkGenerationHash = await repo.getGenerationHash().toPromise();
  currencyMosaicId = new xym.MosaicId((await repo.createNetworkRepository().getNetworkProperties().toPromise()).chain.currencyMosaicId.replace(/0x/,"").replace(/'/g,""));

}


async function anounceTX(signed) {

    console.log("送信トランザクションハッシュ: " + signed.hash);
    console.log(node + '/transactionStatus/' + signed.hash);
    transactionHttp.announce(signed).subscribe(
        (x) => console.log(x),
        (err) => console.error(err),
    );

}

プルーフは出力されないようになっているため,このトランザクションをアナウンスした時点で10000XYMは365日待たないと返還されないことが確定します.

#更に縛りを強くする
プルーフをどうしてもコンソールに出力してしまう方もいらっしゃるかもしれません.そんな方でも絶対にプルーフをアナウンスしたくなくなるようにしてみました.

const xym = require('symbol-sdk');
const crypto = require('crypto');
const sha3_256 = require('js-sha3').sha3_256;
var networkGenerationHash ='';
var epochAdjustment = '';
var node = "";

var repo;
var transactionHttp;
var currencyMosaicId;
var networkType;
var feemultiplier;


console.log("***************************************************************");
console.log(Date());
console.log("***************************************************************");
var networkType = xym.NetworkType.TEST_NET;
const a_account = xym.Account.createFromPrivateKey(
    '*****',
    networkType,
);

const randomAccount = xym.Account.generateNewAccount(networkType);



(async()=>{

    node= 'http://tomato.xym-node.com:3000';
    repo = new xym.RepositoryFactoryHttp(node);
    transactionHttp = repo.createTransactionRepository();
    await getInfo();
    await makeSecretLock(100);

})();
async function makeSecretLock(locks){

    const xymMosaic = new xym.Mosaic(
        currencyMosaicId,
        xym.UInt64.fromUint(locks * 1000000)//送金量*可分性(xymは6)
    );

    const random = crypto.randomBytes(20);
    const proof = random.toString('hex');
    const hash = sha3_256.create();
    const secret = hash.update(random).hex().toUpperCase();
    console.log('proof:', proof);
    console.log('Secret:', secret);

    const gachiho = xym.SecretLockTransaction.create(
        xym.Deadline.create(epochAdjustment),
        xymMosaic,
        xym.UInt64.fromUint(24*60*60/30*365),
        xym.LockHashAlgorithm.Op_Sha3_256,
        secret,
        randomAccount.address,
        networkType,
    ).setMaxFee(feemultiplier);
    const signed = a_account.sign(
    gachiho,
    networkGenerationHash,
    );
    await anounceTX(signed);
}


async function getInfo(){
  feemultiplier = 10;
  epochAdjustment = await repo.getEpochAdjustment().toPromise();
  networkGenerationHash = await repo.getGenerationHash().toPromise();
  currencyMosaicId = new xym.MosaicId((await repo.createNetworkRepository().getNetworkProperties().toPromise()).chain.currencyMosaicId.replace(/0x/,"").replace(/'/g,""));

}


async function anounceTX(signed) {

    console.log("送信トランザクションハッシュ: " + signed.hash);
    console.log(node + '/transactionStatus/' + signed.hash);
    transactionHttp.announce(signed).subscribe(
        (x) => console.log(x),
        (err) => console.error(err),
    );

}

このTXは適当に生成したアドレスに対してシークレットロックを行っています.つまり,プルーフをアナウンスするとGOXします.絶対にアナウンスできません.

#更に更に縛りを強くする
先ほどのトランザクションではプルーフをアナウンスすると強制GOXさせる仕組みとなっておりましたが,プログラム内で秘密鍵からGOXアドレスを作成しているので秘密鍵を出力さえしてしまえばプルーフをアナウンスし,回収できてしまいます.そこで秘密鍵の分からないアドレスに送りつけることで縛りをさらに強力かつ安全なものにします.ついでにシークレットもプルーフから生成しないように変更し,プルーフも絶対に分からないようにしてしまいます.
Symbolのアドレスはチェックデジットを含んでいるため,単純にNAAAAAAAAAA....のようにしても無効なアドレスになってしまいます.そこでランダム生成した公開鍵からアドレスを導出してあげます.

function generateGoxAddress(networktype){
    return xym.Address.createFromPublicKey((crypto.randomBytes(64)).toString('hex'), networktype);
}

これでGOXアドレスを作成することができました.ではやってみましょう

const xym = require('symbol-sdk');
const crypto = require('crypto');
const sha3_256 = require('js-sha3').sha3_256;
var networkGenerationHash ='';
var epochAdjustment = '';
var node = "";

var repo;
var transactionHttp;
var currencyMosaicId;
var networkType;
var feemultiplier;

console.log("***************************************************************");
console.log(Date());
console.log("***************************************************************");
var networkType = xym.NetworkType.TEST_NET;
const privateKey = '******';
const a_account = xym.Account.createFromPrivateKey(
    privateKey,
    networkType,
);


function generateGoxAddress(networktype){
    return xym.Address.createFromPublicKey((crypto.randomBytes(64)).toString('hex'), networktype);
}


(async()=>{

    node= 'http://tomato.xym-node.com:3000';
    repo = new xym.RepositoryFactoryHttp(node);
    transactionHttp = repo.createTransactionRepository();
    await getInfo();
    await makeSecretLock(100);

})();
async function makeSecretLock(locks){

    const xymMosaic = new xym.Mosaic(
        currencyMosaicId,
        xym.UInt64.fromUint(locks * 1000000)//送金量*可分性(xymは6)
    );

    const secret = crypto.randomBytes(32).toString('hex').toUpperCase();

    const gachiho = xym.SecretLockTransaction.create(
        xym.Deadline.create(epochAdjustment),
        xymMosaic,
        xym.UInt64.fromUint(24*60*60/30*365),
        xym.LockHashAlgorithm.Op_Sha3_256,
        secret,
        generateGoxAddress(networkType),
        networkType,
    ).setMaxFee(feemultiplier);
    const signed = a_account.sign(
    gachiho,
    networkGenerationHash,
    );
    await anounceTX(signed);
}


async function getInfo(){
  feemultiplier = 10;
  epochAdjustment = await repo.getEpochAdjustment().toPromise();
  networkGenerationHash = await repo.getGenerationHash().toPromise();
  currencyMosaicId = new xym.MosaicId((await repo.createNetworkRepository().getNetworkProperties().toPromise()).chain.currencyMosaicId.replace(/0x/,"").replace(/'/g,""));

}


async function anounceTX(signed) {

    console.log("送信トランザクションハッシュ: " + signed.hash);
    console.log(node + '/transactionStatus/' + signed.hash);
    transactionHttp.announce(signed).subscribe(
        (x) => console.log(x),
        (err) => console.error(err),
    );

}

無事,プルーフが分からず,分かったとしても絶対にアナウンスできないため1051200ブロック(約1年間)強制ガチホせざるをえないトランザクションが完成しました.
aa.JPG

完成後に思ったのですが,プルーフが絶対分からず,(現在の暗号技術では)安全なトランザクションになっているのでGOXアドレスは必要ないですね...
プルーフは現在のコンピューターの能力では解析できないのでこのハッシュロックが成立する可能性は無いのですが,万が一を考えるとハッシュロックの受け取りアドレスを自分のものにして良いと思います.

#この方法のメリット,デメリット
###メリット
・システム的にロックすることができるため解除ができない
・メンタルに左右されなくなる
###デメリット
・暴騰しても暴落しても売却は不可能
・ハーベストできない!
#その他
未来のdeadlineのトランザクションを生成して他人に署名済みのトランザクションを渡しておくことで,更に長期間のガチホもできるかもしれません

#最後に
無理せず余剰資金だけを投資して仮想通貨を楽しみましょう!

21
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?