はじめに
NEMのカタパルト移行を考えたときに、NIS1のどこかのブロック高でスナップショットをとって、それをカタパルトのネメシスブロックに詰め込むやり方が考えられます。ひとつのやり方です。
というわけで、なんとなくやってみよう。
今回は、ネームスペースとかモザイクとかマルチシグは無視して、XEMの残高だけをやってみる。
データ取得
まずは、NIS1のデータを取得する。NIS1のノードはh2というDBを使っている。
テストネットのDBをとってきて、のぞいてみる。
参考手順:https://qiita.com/heiuchi23/items/1f126c9b68b15b3746ad
java -Dh2.bindAddress=0.0.0.0 -cp ./h2db/h2/bin/h2-1.4.197.jar org.h2.tools.Server -baseDir ~/h2db -webAllowOthers -tcpAllowOthers
ACCOUNTSというテーブルにすべての出現したことのあるアドレスが書かれていそうだ。
アカウントリスト
ACCOUNTSテーブルをすべて取得します。
java -cp ./h2db/h2/bin/h2-1.4.197.jar org.h2.tools.RunScript -url "jdbc:h2:./h2db/nis5_testnet" -script "./h2db/sql.txt" -showResults > ./h2db/result.txt
select printablekey from accounts;
select printablekey from accounts;
--> TBULEAUG2CZQISUR442HWA6UAKGWIXHDABJVIPS4
--> TBONKWCOWBZYZB2I5JD3LSDBQVBYHB757VN3SKPP
--> TBGIMRE4SBFRUJXMH7DVF2IBY36L2EDWZ37GVSC4
--> TBLOODPLWOWMZ2TARX4RFPOSOWLULHXMROBN2WXI
--> TBALNEMNEMKIMWLF65HTUWMQVX5G55EBBIWS4WQC
--> TAOPATMADWFEPME6GHOJL477SI7D3UT6NFJN4LGB
--> TBMKRYST2J3GEZRWHS3MICWFIBSKVHH7F5FA6FH3
--> TBGJAGUAQY47BULYL4GRYBJLOI6XKXPJUXU25JRJ
--> TB7YGASZY2ROWNAVZO3DYH6IVYGMD3LZYTHIESWP
--> TDECM3D4JX4M2EIHMWP6PBWV4EBZY3TMAKOTO26J
--> TBSANAAURPWQDBZQSSQ5ZP3NYINUBSQY4RKYI6I7
--> TAFMINM2IMWQ3WXJLHGOGOGOBB7C46EQ3UFIREOA
--> TBGXMN6QEKTV32MGBJQK22E7GZKOEAN2FY2JUSAN
--> TB5FO7AWGNE7VEBWOCXBKWX6VTJVWDNMVEKHACHI
--> TD2IWIJ3EUNIJUICXCFDSPO6IDXSNKMPTMX4HMPJ
--> TCSGMTAJNWH7G6CLEVRTJC7WSDKQKOO6VP6SFNZU
--> TALICEBMLRCCYHAYBKUVAPLB2DMZIBZFFGIMJHSC
--> TALICEYUFXRQKTIONRNMS6LBJN3T5RCE2O2GVTJO
...
さて、残高はどこにあるのかというと、そう簡単には取得できそうにありませんでした。残高は、トランザクションの履歴から、すべての出金と入金を足し合わせた値でしか取得できそうにありません。
残高
今回、残高を計算することは本筋ではないので、NISのAPIに問い合わせで残高を取得します。取得中にトランザクションが発生して残高が変わってしまうかもしれないが、目をつぶるということで。
result.txt
は、整形して1行に1アドレスが記載されるようにしています。あとは、全アカウントの残高合計値も取得しておきます。
process.env.NODE_URL = 'http://192.168.11.77:7890'
const fs = require('fs');
const axios = require('axios');
const text = fs.readFileSync('./result.txt', { encoding: 'utf8'});
const addresses = text.split('\n');
let totalSupply = 0;
function addressConvert(addressStinrg) {
// 後述
}
async function sleep(millis) {
return new Promise((resolve, reject) => {
setTimeout(resolve, millis);
});
}
async function exec() {
for (let i = 0; i < addresses.length; i++) {
const address = addresses[i];
const data = await axios.get(`${process.env.NODE_URL}/account/get?address=${address}`);
const balance = data.data.account.balance;
const line = `${addressConvert(address)} = ${balance.toLocaleString()}`;
console.log(i, line);
if (balance !== 0) {
totalSupply += balance;
}
fs.appendFileSync('nemesis.txt', line + "\n", 'utf8');
await sleep(100);
}
console.log("totalSupply", totalSupply);
}
exec();
アドレス変換
2019/05/18時点では、NIS1はハッシュ関数にKeccakを使っており、カタパルトはSHA3を使っています。
アドレスにはチェックサムがついており、チェックサムの導出にはハッシュ関数を使います。
なので、NIS1のアドレスをそのまま使うことができません。
そこで、NIS1のアドレスから公開鍵ハッシュを計算し、それをもとにカタパルトのアドレスを計算することにします。また、バージョンプリフィックスもMIJIN_TEST用に変更します。
(なお、Ed25519の場合、ハッシュ関数が異なると、同じ秘密鍵を使ったとしても、異なる公開鍵が導出されます。)
(この導出されたアドレスの秘密鍵は誰も知らないことになりますが、この件については無視します。)
function addressConvert(addressStinrg) {
const addressDecoded = nem2lib.address.stringToAddress(addressStinrg);
const pubKeyHash = addressDecoded.subarray(1,20);
const addressConverted = pubKeyHashToAddress(pubKeyHash);
return nem2lib.address.addressToString(addressConverted);
}
// copy from nem2-library corders/address.js, coders/array.js
function pubKeyHashToAddress(ripemdHash) {
const decodedAddress = new Uint8Array(25);
decodedAddress[0] = 0x90;
copy(decodedAddress, ripemdHash, 20, 1);
const hash = sha3_256.arrayBuffer(decodedAddress.subarray(0, 21));
copy(decodedAddress, uint8View(hash), 4, 21);
return decodedAddress;
}
function copy(dest, src, numElementsToCopy, destOffset = 0, srcOffset = 0) {
const length = undefined === numElementsToCopy ? dest.length : numElementsToCopy;
for (let i = 0; i < length; ++i)
dest[destOffset + i] = src[srcOffset + i];
}
function uint8View(input) {
if (ArrayBuffer === input.constructor)
return new Uint8Array(input); // note that wrapping an ArrayBuffer in an Uint8Array does not make a copy
else if (Uint8Array === input.constructor)
return input;
throw Error('unsupported type passed to uint8View');
}
結果
SBULEAUG2CZQISUR442HWA6UAKGWIXHDACFRTZLK = 323,942,608
SBONKWCOWBZYZB2I5JD3LSDBQVBYHB75ACUVP7AF = 49,897,001,750,000
SBGIMRE4SBFRUJXMH7DVF2IBY36L2EDWACGDIB6V = 44,640,294,909,864
SBLOODPLWOWMZ2TARX4RFPOSOWLULHXMABZIGNGL = 50,026,293,690,123
SBALNEMNEMKIMWLF65HTUWMQVX5G55EBAAJUYB4T = 50,012,323,000,000
SAOPATMADWFEPME6GHOJL477SI7D3UT6ADULGNFU = 50,000,000,000,006
SBMKRYST2J3GEZRWHS3MICWFIBSKVHH7ABAHI6VO = 50,000,000,000,000
SBGJAGUAQY47BULYL4GRYBJLOI6XKXPJAD53HFDX = 50,000,234,000,000
SB7YGASZY2ROWNAVZO3DYH6IVYGMD3LZAARS7SHD = 50,008,505,000,000
SDECM3D4JX4M2EIHMWP6PBWV4EBZY3TMAC7DRXI3 = 50,001,800,000,000
SBSANAAURPWQDBZQSSQ5ZP3NYINUBSQYAAZAO4JX = 50,000,000,000,000
SAFMINM2IMWQ3WXJLHGOGOGOBB7C46EQACV7NGPF = 51,546,772,865,817
SBGXMN6QEKTV32MGBJQK22E7GZKOEAN2AC25PDPG = 50,000,000,000,000
SB5FO7AWGNE7VEBWOCXBKWX6VTJVWDNMADOI622S = 50,000,000,000,000
SD2IWIJ3EUNIJUICXCFDSPO6IDXSNKMPACXXZBJU = 50,000,000,000,000
...
totalSupply 7999999990950000
起動してみる
ネメシスブロック用のファイルを作ります。アカウントの残高にある,
を'
に変換します。supply
を変更します。
...
[mosaic>cat:currency]
divisibility = 6
duration = 0
supply = 7'999'999'990'950'000
isTransferable = true
isSupplyMutable = false
isLevyMutable = false
[distribution>cat:currency]
SBULEAUG2CZQISUR442HWA6UAKGWIXHDACFRTZLK = 323'942'608
SBONKWCOWBZYZB2I5JD3LSDBQVBYHB75ACUVP7AF = 49'897'001'750'000
SBGIMRE4SBFRUJXMH7DVF2IBY36L2EDWACGDIB6V = 44'640'294'909'864
SBLOODPLWOWMZ2TARX4RFPOSOWLULHXMABZIGNGL = 50'026'293'690'123
SBALNEMNEMKIMWLF65HTUWMQVX5G55EBAAJUYB4T = 50'012'323'000'000
SAOPATMADWFEPME6GHOJL477SI7D3UT6ADULGNFU = 50'000'000'000'006
SBMKRYST2J3GEZRWHS3MICWFIBSKVHH7ABAHI6VO = 50'000'000'000'000
SBGJAGUAQY47BULYL4GRYBJLOI6XKXPJAD53HFDX = 50'000'234'000'000
SB7YGASZY2ROWNAVZO3DYH6IVYGMD3LZAARS7SHD = 50'008'505'000'000
...
起動します。
ふつうに起動しました。
おわりに
ネメシスブロックに39,000アカウントを入れることができた。
今の設定で、1ブロック120,000トランザクションが最大なんだが、ネメシスブロックはこれを超えることはできるのだろうか。
モザイクの分もネメシスブロックに入れると、きっと120,000を超えるので、やる気があればやってみる。