5
3

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 1 year has passed since last update.

nemAdvent Calendar 2021

Day 24

RubyでSymbolブロックチェーンのトランザクションを送信する

Last updated at Posted at 2022-06-21

この記事は「自分の得意なプログラミング言語でSymbolブロックチェーンを動かす方法」をRubyで実践したものです。

Rubyでさまざまなトランザクションを作成する方法はこちらをご参考ください。

Gemfile

source "https://rubygems.org"

gem 'base32', '~> 0.3.4'
gem 'ed25519', '~> 1.3'
gem 'digest', '~> 3.1'
gem 'sha3', '~> 1.0', '>= 1.0.4'

require

require "ed25519"
require "digest"
require "sha3"
require "base32"
require "json"
require "net/http"

アカウント作成

signing_key = Ed25519::SigningKey.generate
verify_key = signing_key.verify_key

private_key = signing_key.to_bytes
public_key = verify_key.to_bytes

puts private_key.unpack('H*')
puts public_key.unpack('H*')

アカウント復元

alice_priavte_key = Ed25519::SigningKey.new(
	"94ee0f4d7fe388ac4b04a6a6ae2ba969617879b83616e4d25710d688a89d80c7" \
	.scan(/../).map{ |b| b.to_i(16) }.pack('C*')
)
alice_public_key = alice_priavte_key.verify_key

#公開鍵出力
puts alice_public_key.to_bytes.unpack('H*')

public_key_hash = SHA3::Digest.hexdigest(:sha256, alice_public_key.to_bytes)
address_body = Digest::RMD160.hexdigest public_key_hash.scan(/../).map{ |b| b.to_i(16) }.pack('C*')
sum_hash = SHA3::Digest.hexdigest(:sha256, ("98" + address_body).scan(/../).map{ |b| b.to_i(16) }.pack('C*'))
alice_address = Base32.encode(("98" + address_body + sum_hash.slice(0..5)).scan(/../).map{ |b| b.to_i(16) }.pack('C*')) 
alice_address = alice_address.slice(0..alice_address.length-2)

#アドレス出力
puts alice_address

トランザクション構築

version = [1].pack("C").unpack('H*')[0]
network_type = [152].pack("C").unpack('H*')[0]
transaction_type = [16724].pack("v").unpack('H*')[0]
fee = [1000000].pack("Q").unpack('H*')[0]
second_later_7200 = ((Time.now.to_i + 7200) - 1637848847) * 1000;
deadline = [second_later_7200].pack("Q").unpack('H*')[0]
mosaic_count = [1].pack("C").unpack('H*')[0]
mosaic_id = [0x3A8416DB2D53B6C8].pack("Q").unpack('H*')[0]
recipient_address = Base32.decode("TBIL6D6RURP45YQRWV6Q7YVWIIPLQGLZQFHWFEQ").unpack('H*')[0]
mosaic_amount = [100].pack("Q").unpack('H*')[0]
message = ('Hello Ruby! Welcome to Symbol world!').unpack('H*')[0]
message_size = [message.length / 2 + 1].pack("v").unpack('H*')[0]

トランザクション署名

verifiable_body = version \
	+ network_type \
	+ transaction_type \
	+ fee \
	+ deadline \
	+ recipient_address \
	+ message_size \
	+ mosaic_count \
	+ "00" + "00000000" \
	+ mosaic_id \
	+ mosaic_amount \
	+ "00" + message


verifiable_string = "7fccd304802016bebbcd342a332f91ff1f3bb5e902988b352697be245f48e836" \
		+ verifiable_body;

verifiable_buffer = verifiable_string.scan(/../).map{ |b| b.to_i(16) }.pack('C*')
signature = alice_priavte_key.sign(verifiable_buffer).unpack('H*')[0]

トランザクションの通知

transaction_size = [verifiable_body.length / 2 + 108].pack("L").unpack('H*')[0]

payload_string = transaction_size \
	+ "00000000" \
	+ signature \
	+ alice_public_key.to_bytes.unpack('H*')[0] \
	+ "00000000" \
	+ verifiable_body

payload = { "payload" => payload_string}
uri = URI.parse('https://sym-test-02.opening-line.jp:3001/transactions')
req = Net::HTTP::Put.new(uri.request_uri)
req["Content-Type"] = "application/json" 
req.body = payload.to_json

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
response = http.request(req)
puts response

確認

hashable_string = signature \
	+ alice_public_key.to_bytes.unpack('H*')[0] \
	+ verifiable_string

puts hashable_string
transactionHash  = SHA3::Digest.hexdigest(:sha256, hashable_string.scan(/../).map{ |b| b.to_i(16) }.pack('C*'))

puts "transactionStatus: https://sym-test-02.opening-line.jp:3001/transactionStatus/" + transactionHash;
puts "confirmed: https://sym-test-02.opening-line.jp:3001/transactions/confirmed/" + transactionHash;
puts "explorer: https://testnet.symbol.fyi/transactions/" +  transactionHash;


検証スクリプト

require "ed25519"
require 'digest'
require 'sha3'
require "base32"
require 'json'
require "net/http"

signing_key = Ed25519::SigningKey.generate
verify_key = signing_key.verify_key

private_key = signing_key.to_bytes
public_key = verify_key.to_bytes

puts private_key.unpack('H*')
puts public_key.unpack('H*')


#アカウント復元
alice_priavte_key = Ed25519::SigningKey.new(
	"94ee0f4d7fe388ac4b04a6a6ae2ba969617879b83616e4d25710d688a89d80c7" \
	.scan(/../).map{ |b| b.to_i(16) }.pack('C*')
)
alice_public_key = alice_priavte_key.verify_key
puts alice_public_key.to_bytes.unpack('H*')

public_key_hash = SHA3::Digest.hexdigest(:sha256, alice_public_key.to_bytes)
address_body = Digest::RMD160.hexdigest public_key_hash.scan(/../).map{ |b| b.to_i(16) }.pack('C*')
sum_hash = SHA3::Digest.hexdigest(:sha256, ("98" + address_body).scan(/../).map{ |b| b.to_i(16) }.pack('C*'))
alice_address = Base32.encode(("98" + address_body + sum_hash.slice(0..5)).scan(/../).map{ |b| b.to_i(16) }.pack('C*')) 
alice_address = alice_address.slice(0..alice_address.length-2)
puts alice_address


version = [1].pack("C").unpack('H*')[0]
network_type = [152].pack("C").unpack('H*')[0]
transaction_type = [16724].pack("v").unpack('H*')[0]
fee = [1000000].pack("Q").unpack('H*')[0]
second_later_7200 = ((Time.now.to_i + 7200) - 1637848847) * 1000;
deadline = [second_later_7200].pack("Q").unpack('H*')[0]
mosaic_count = [1].pack("C").unpack('H*')[0]
mosaic_id = [0x3A8416DB2D53B6C8].pack("Q").unpack('H*')[0]
recipient_address = Base32.decode("TBIL6D6RURP45YQRWV6Q7YVWIIPLQGLZQFHWFEQ").unpack('H*')[0]
mosaic_amount = [100].pack("Q").unpack('H*')[0]
message = ('Hello Ruby! Welcome to Symbol world!').unpack('H*')[0]
message_size = [message.length / 2 + 1].pack("v").unpack('H*')[0]

verifiable_body = version \
	+ network_type \
	+ transaction_type \
	+ fee \
	+ deadline \
	+ recipient_address \
	+ message_size \
	+ mosaic_count \
	+ "00" + "00000000" \
	+ mosaic_id \
	+ mosaic_amount \
	+ "00" + message


verifiable_string = "7fccd304802016bebbcd342a332f91ff1f3bb5e902988b352697be245f48e836" \
		+ verifiable_body;

verifiable_buffer = verifiable_string.scan(/../).map{ |b| b.to_i(16) }.pack('C*')
signature = alice_priavte_key.sign(verifiable_buffer).unpack('H*')[0]
transaction_size = [verifiable_body.length / 2 + 108].pack("L").unpack('H*')[0]

payload_string = transaction_size \
	+ "00000000" \
	+ signature \
	+ alice_public_key.to_bytes.unpack('H*')[0] \
	+ "00000000" \
	+ verifiable_body

puts payload_string

payload = { "payload" => payload_string}
uri = URI.parse('https://sym-test-02.opening-line.jp:3001/transactions')
req = Net::HTTP::Put.new(uri.request_uri)
req["Content-Type"] = "application/json" 
req.body = payload.to_json

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
response = http.request(req)
puts response

hashable_string = signature \
	+ alice_public_key.to_bytes.unpack('H*')[0] \
	+ verifiable_string

puts hashable_string
transactionHash  = SHA3::Digest.hexdigest(:sha256, hashable_string.scan(/../).map{ |b| b.to_i(16) }.pack('C*'))

puts "transactionStatus: https://sym-test-02.opening-line.jp:3001/transactionStatus/" + transactionHash;
puts "confirmed: https://sym-test-02.opening-line.jp:3001/transactions/confirmed/" + transactionHash;
puts "explorer: https://testnet.symbol.fyi/transactions/" +  transactionHash;

5
3
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
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?