Last active
September 28, 2020 09:03
-
-
Save metalefty/f4f1d644cecc75f28f16f759f202e726 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'open-uri' | |
require 'fileutils' | |
require 'optparse' | |
require 'securerandom' | |
@wrkdir='/tmp/tapyrus-bootstrap' | |
@tmpdir="" | |
def bootstrap | |
begin | |
Dir.mkdir(@wrkdir) | |
rescue Errno::EEXIST | |
ensure | |
@tmpdir=%x(mktemp --tmpdir=#{@wrkdir} --directory).strip | |
Dir.chdir(@tmpdir) | |
end | |
end | |
def signer_setup(*args) | |
%x(tapyrus-signer.setup #{args.join(' ')}) | |
end | |
def core_genesis(*args) | |
%x(tapyrus-core.genesis #{args.join(' ')}) | |
end | |
def bx(*args) | |
begin | |
Dir.mkdir(@wrkdir) | |
rescue Errno::EEXIST | |
end | |
unless File.exist?("#{@wrkdir}/bx") | |
bxurl = 'https://github.com/libbitcoin/libbitcoin-explorer/releases/download/v3.2.0/bx-linux-x64-qrcode' | |
open("#{@wrkdir}/bx", 'wb') do |output| | |
URI.open(bxurl) do |f| | |
output.write(f.read) | |
end | |
end | |
FileUtils.chmod(0500, "#{@wrkdir}/bx") | |
end | |
%x(#{@wrkdir}/bx #{args.join(' ')}) | |
end | |
def key_pairs(n) | |
(1..n).map { |i| signer_setup('createkey').split(' ').reverse }.to_h | |
end | |
def vss_map(command, key_pairs, t) | |
vsss = key_pairs.values.map do |priv| | |
result = signer_setup("#{command} --private-key=#{priv} --threshold=#{t.to_s}" + | |
key_pairs.keys.map { |pub| " --public-key=#{pub}" }.join) | |
result.split(/\R/).map { |line| line.split(':') }.to_h | |
end | |
key_pairs.keys.map { |pub| [pub, vsss.map { |vss| vss[pub] }] }.to_h | |
end | |
def aggregates(key_pairs, vss_map) | |
aggregates_pub = '' | |
key_pairs.map do |pub, priv| | |
result = signer_setup("aggregate --private-key=#{priv}" + vss_map[pub].map { |vss| " --vss=#{vss}" }.join) | |
aggregates_pub = result.split(' ')[0].split(/\R/)[0] | |
[pub, result.split(' ')[1].split(/\R/)[0]] | |
end.to_h.merge({ pub: aggregates_pub }) | |
end | |
def address() | |
address_key = bx("ec-new #{bx('seed')}") | |
public = bx("ec-to-public #{address_key}") | |
address = bx("ec-to-address #{public}").split(/\R/)[0] | |
{ address_key: address_key, address: address } | |
end | |
def genesis_block(aggregate_pub) | |
address = address() | |
genesis_block = core_genesis("-time=1563342688 -address=#{address[:address]} -signblockpubkey=#{aggregate_pub}").split(/\R/)[0] | |
address.merge({ genesis_block: genesis_block }) | |
end | |
def signs(key_pairs, vss_map, aggregates, block, t) | |
key_pairs.map do |pub, priv| | |
signer_setup("sign --aggregated-public-key=#{aggregates[:pub]} --node-secret-share=#{aggregates[pub]}" + | |
" --private-key=#{priv} --block=#{block} --threshold=#{t}" + | |
vss_map[pub].map { |vss| " --block-vss=#{vss}" }.join).split(/\R/)[0] | |
end | |
end | |
def computesig(key_pairs, sigs, block_vsss, node_vsss, aggregates, block, t) | |
key_pairs.map do |pub, priv| | |
signer_setup("computesig --aggregated-public-key=#{aggregates[:pub]} --node-secret-share=#{aggregates[pub]}" + | |
" --private-key=#{priv} --block=#{block} --threshold=#{t}" + | |
block_vsss[pub].map { |vss| " --block-vss=#{vss}" }.join + | |
node_vsss[pub].map { |vss| " --node-vss=#{vss}" }.join + | |
sigs.map { |sig| " --sig=#{sig}" }.join) | |
end | |
end | |
def federations_toml(key_pairs, t, aggregates, node_vss_map) | |
key_pairs.map do |pub, priv| | |
"[[federation]]\n" + | |
"block-height = 0\n" + | |
"threshold = #{t}\n" + | |
"aggregated-public-key = '#{aggregates[:pub]}'\n" + | |
"node-vss = [\n" + | |
node_vss_map[pub].map { |vss| " '#{vss}'" }.join(",\n") + | |
"\n]" | |
end | |
end | |
def signer_toml(key_pairs, addrs, rpc_ips, opts) | |
key_pairs.map do |pub, priv| | |
<<"EOS" | |
[signer] | |
to-address = '#{addrs[pub][:address]}' | |
public-key = '#{pub}' | |
federations-file = '/root/snap/tapyrus-signer/common/federations.toml' | |
[rpc] | |
rpc-endpoint-host = '#{rpc_ips[pub]}' | |
rpc-endpoint-port = 2377 | |
rpc-endpoint-user = '#{opts[:rpc_user]}' | |
rpc-endpoint-pass = '#{opts[:rpc_pass]}' | |
[redis] | |
redis-host = '#{opts[:redis]}' | |
redis-port = 6379 | |
[general] | |
round-duration = #{opts[:round_duration]} | |
log-quiet = true | |
log-level = 'info' | |
log-file = '/root/snap/tapyrus-signer/common/log/tapyrus-signer.log' | |
skip-waiting-ibd = true | |
EOS | |
end | |
end | |
def tapyrus_conf(opts) | |
<<"EOS" | |
networkid=#{opts[:networkid]} | |
txindex=1 | |
server=1 | |
rest=1 | |
rpcuser=#{opts[:rpc_user]} | |
rpcpassword=#{opts[:rpc_pass]} | |
rpcbind=0.0.0.0 | |
rpcallowip=0.0.0.0/0 | |
addseeder=#{opts[:seeder]} | |
EOS | |
end | |
def main(opts) | |
# def main(n, t, network_id, seeder, redis_id, rpc_ips) | |
key_pairs = key_pairs(opts[:signers]) | |
node_vss_map = vss_map('createnodevss', key_pairs, opts[:min_signers]) | |
aggregates = aggregates(key_pairs, node_vss_map) | |
block_vss_map = vss_map('createblockvss', key_pairs, opts[:min_signers]) | |
address_and_genesis_block = genesis_block(aggregates[:pub]) | |
sigs = signs(key_pairs, block_vss_map, aggregates, address_and_genesis_block[:genesis_block], opts[:min_signers]) | |
block_with_signature = computesig(key_pairs, sigs, block_vss_map, node_vss_map, aggregates, address_and_genesis_block[:genesis_block], opts[:min_signers]) | |
federations_toml = federations_toml(key_pairs, opts[:min_signers], aggregates, node_vss_map) | |
addrs = key_pairs.keys.map { |pub| [pub, address()] }.to_h | |
rpc_ips_m = key_pairs.keys.map.with_index { |pub, i| [pub, opts[:rpcendpoint][i % opts[:rpcendpoint].size]] }.to_h | |
signer_toml = signer_toml(key_pairs, addrs, rpc_ips_m, opts) | |
tapyrus_conf = tapyrus_conf(opts) | |
{ key_pairs: key_pairs, node_vss_map: node_vss_map, aggregates: aggregates, | |
block_vss_map: block_vss_map, sigs: sigs, block_with_signature: block_with_signature, | |
federations_toml: federations_toml, signer_toml: signer_toml, addrs: addrs, tapyrus_conf: tapyrus_conf }.merge(address_and_genesis_block) | |
end | |
def optparse | |
options = {} | |
opt = OptionParser.new | |
opt.on('--signers=VALUE', 'number of signers') {|v| options[:signers] = v.to_i} | |
opt.on('--min-signers=VALUE', 'minimum number of signers to create block') {|v| options[:min_signers] = v.to_i} | |
opt.on('--networkid=[VALUE]', 'tapyrus network id (random if empty)') {|v| options[:networkid] = v.to_i} | |
opt.on('--seeder=VALUE', 'seeder hostname') {|v| options[:seeder] = v} | |
opt.on('--redis=VALUE', 'hostname of Redis server') {|v| options[:redis] = v} | |
opt.on('--rpc-endpoint=VALUE', 'comma separated') {|v| options[:rpcendpoint] = v.split(',') } | |
opt.on('--rpc-user=VALUE', 'rpc username') {|v| options[:rpc_user] = v} | |
opt.on('--rpc-pass=VALUE', 'rpc password') {|v| options[:rpc_pass] = v} | |
opt.on('--round-duration=[VALUE]', 'round duration (in seconds, 600 if empty)') {|v| options[:round_duration] = v.to_i } | |
opt.parse(%w(-h)) if ARGV.empty? | |
opt.parse(ARGV) | |
# raise error if mandatory arguments are not given | |
raise ArgumentError if options[:signers].nil? | |
raise ArgumentError if options[:min_signers].nil? | |
raise ArgumentError if options[:seeder].nil? | |
raise ArgumentError if options[:redis].nil? | |
raise ArgumentError if options[:rpcendpoint].nil? | |
raise ArgumentError if options[:rpc_user].nil? | |
raise ArgumentError if options[:rpc_pass].nil? | |
options[:networkid] ||= SecureRandom.random_number(2**32-1) - 33550335 | |
options[:round_duration] ||= 600 # 10 minutes | |
options | |
end | |
opts = optparse | |
bootstrap | |
result = main(opts) | |
opts[:signers].times.with_index {|i| Dir.mkdir("#{@tmpdir}/#{i}")} | |
result[:addrs].values.each.with_index { |addr, i| `echo "#{addr[:address_key]}" > #{i}/address_key`; `echo "#{addr[:address]}" > #{i}/address` } | |
result[:key_pairs].keys.each.with_index { |pub, i| `echo "#{pub}" > #{i}/pub_key` } | |
result[:key_pairs].values.each.with_index { |priv, i| `echo "#{priv}" > #{i}/priv_key` } | |
result[:federations_toml].each.with_index { |toml, i| `echo "#{toml}" > #{i}/federations.toml` } | |
result[:signer_toml].each.with_index { |toml, i| `echo "#{toml}" > #{i}/signer.toml` } | |
`echo "#{result[:block_with_signature].first}" > genesis.#{opts[:networkid]}` | |
`echo "#{result[:tapyrus_conf]}" > tapyrus.conf` | |
$stderr.puts "configurations has been dumped into #{@tmpdir}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment