axentro-rb-util 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d00fc176356de2e02ba47caa16f5459c41c0d9a2ce022b7e22aa380f7fab9cde
4
+ data.tar.gz: 673c47711f9fbd2ff8d401c3c070f85c6421a4e4f8b3cdffe85553b7eed5857b
5
+ SHA512:
6
+ metadata.gz: a81510252a00f751a1e242f50b1401e4acdc1204d511ac7265e6a92a54c162db3d790c19ef61b767452cfc171b73dac63d95b295fa1c96905fc0b2b288c821eb
7
+ data.tar.gz: 5c0ac4441f07ce2e59d72aa7bdc946a7f0dce57f1e5bd15149df05c638fefd9e2d8b20fb6c3c6cc82723066e1592bf5c06f7d900108db3583a24cb0f12fdff18
@@ -0,0 +1,17 @@
1
+ require "json"
2
+ require "securerandom"
3
+ require 'bigdecimal'
4
+ require "ed25519-hd"
5
+ require "faraday"
6
+ require "crypto/crypto"
7
+ require "crypto/hashes"
8
+ require "crypto/keys/address"
9
+ require "crypto/keys/key_utils"
10
+ require "crypto/keys/private_key"
11
+ require "crypto/keys/public_key"
12
+ require "crypto/keys/wif"
13
+ require "crypto/key_ring"
14
+ require "crypto/transaction"
15
+
16
+ include Crypto
17
+ include Keys
@@ -0,0 +1,2 @@
1
+ module Crypto
2
+ end
@@ -0,0 +1,13 @@
1
+ require "digest"
2
+
3
+ module Crypto::Hashes
4
+ extend self
5
+
6
+ def sha256(base)
7
+ Digest::SHA256.hexdigest(base)
8
+ end
9
+
10
+ def ripemd160(base)
11
+ Digest::RMD160.hexdigest(base)
12
+ end
13
+ end
@@ -0,0 +1,53 @@
1
+ module Crypto::Keys
2
+ include Crypto::Hashes
3
+
4
+ MAINNET = { prefix: "M0", name: "mainnet" }
5
+ TESTNET = { prefix: "T0", name: "testnet" }
6
+
7
+ class KeyRing
8
+ attr_reader :private_key
9
+ attr_reader :public_key
10
+ attr_reader :wif
11
+ attr_reader :address
12
+ attr_reader :address
13
+ attr_reader :seed
14
+
15
+ def initialize(private_key, public_key, wif, address, seed = nil)
16
+ @private_key = private_key
17
+ @public_key = public_key
18
+ @wif = wif
19
+ @address = address
20
+ @seed = seed
21
+ end
22
+
23
+ def self.generate(network = MAINNET)
24
+ key_pair = KeyUtils.create_new_keypair
25
+ private_key = PrivateKey.new(key_pair[:hex_private_key], network)
26
+ public_key = PublicKey.new(key_pair[:hex_public_key], network)
27
+ KeyRing.new(private_key, public_key, private_key.wif, public_key.address)
28
+ end
29
+
30
+ def self.generate_hd(seed = nil, derivation = nil, network = MAINNET)
31
+ _seed = seed.nil? ? SecureRandom.hex(64) : seed
32
+ keys = (derivation.nil? || derivation.nil? && derivation == "m") ?
33
+ HDKEY::KeyRing.get_master_key_from_seed(_seed) : HDKEY::KeyRing.derive_path(derivation, _seed, HDKEY::HARDENED_AXENTRO)
34
+
35
+ private_key = PrivateKey.new(keys.private_key, network)
36
+ _public_key = HDKEY::KeyRing.get_public_key(keys.private_key)[2..-1]
37
+ public_key = PublicKey.new(_public_key, network)
38
+ KeyRing.new(private_key, public_key, private_key.wif, public_key.address, _seed)
39
+ end
40
+
41
+ def self.is_valid?(public_key, wif, address)
42
+ address = Address.from(address)
43
+ wif = Wif.new(wif)
44
+
45
+ raise AxentroError, "network mismatch between address and wif" if address.network != wif.network
46
+
47
+ public_key = PublicKey.from_hex(public_key, address.network)
48
+ raise AxentroError, "public key mismatch between public key and wif" if public_key.as_hex != wif.public_key.as_hex
49
+
50
+ true
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,66 @@
1
+ require "base64"
2
+
3
+ class AxentroError < StandardError
4
+ end
5
+
6
+ module Crypto::Keys
7
+ class Address
8
+ attr_reader :network
9
+
10
+ def initialize(hex_address, network = MAINNET, name = "generic")
11
+ @hex_address = hex_address
12
+ @network = network
13
+ @name = name
14
+ unless is_valid?
15
+ raise AxentroError, "invalid '#{@name}' address checksum for: '#{@hex_address}'"
16
+ end
17
+ end
18
+
19
+ def as_hex
20
+ @hex_address
21
+ end
22
+
23
+ def self.from(hex_address, name = "")
24
+ network = get_network_from_address(hex_address)
25
+ Address.new(hex_address, network, name)
26
+ end
27
+
28
+ def is_valid?
29
+ Address.is_valid?(@hex_address)
30
+ end
31
+
32
+ def self.is_valid?(hex_address)
33
+ begin
34
+ decoded_address = Base64.strict_decode64(hex_address)
35
+ return false unless decoded_address.size == 48
36
+ version_address = decoded_address[0..-7]
37
+ hashed_address = Crypto::Hashes.sha256(Crypto::Hashes.sha256(version_address))
38
+ checksum = decoded_address[-6..-1]
39
+ checksum == hashed_address[0..5]
40
+ rescue AxentroError => e
41
+ false
42
+ end
43
+ end
44
+
45
+ def self.get_network_from_address(hex_address)
46
+ begin
47
+ decoded_address = Base64.strict_decode64(hex_address)
48
+ rescue => e
49
+ raise AxentroError, "invalid address: #{e}"
50
+ end
51
+
52
+ case decoded_address[0..1]
53
+ when MAINNET[:prefix]
54
+ MAINNET
55
+ when TESTNET[:prefix]
56
+ TESTNET
57
+ else
58
+ raise AxentroError, "invalid network: #{decoded_address[0..1]} for address: #{hex_address}"
59
+ end
60
+ end
61
+
62
+ def to_s
63
+ as_hex
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,61 @@
1
+ module Crypto::Keys
2
+ class KeyUtils
3
+ def self.verify_signature(message, signature_hex, hex_public_key)
4
+ begin
5
+ verify_key = Ed25519::VerifyKey.new([hex_public_key].pack("H*"))
6
+ verify_key.verify([signature_hex].pack("H*"), message)
7
+ rescue => e
8
+ raise AxentroError, "Verify fail: #{e.message}"
9
+ end
10
+ end
11
+
12
+ def self.sign(hex_private_key, message)
13
+ signing_key = Ed25519::SigningKey.new([hex_private_key].pack("H*"))
14
+ signing_key.sign(message).unpack("H*").first
15
+ end
16
+
17
+ def self.create_new_keypair
18
+ signing_key = Ed25519::SigningKey.generate
19
+ private_key = signing_key.keypair.unpack("H*").first[0..63]
20
+ public_key = signing_key.keypair.unpack("H*").first[64..-1]
21
+ {
22
+ hex_private_key: private_key,
23
+ hex_public_key: public_key
24
+ }
25
+ end
26
+
27
+ def self.to_hex(bytes)
28
+ bytes.pack("c*").unpack("H*").first
29
+ end
30
+
31
+ def self.to_bytes(hex)
32
+ [hex].pack('H*').bytes.to_a
33
+ end
34
+
35
+ def self.get_address_from_public_key(public_key)
36
+ hashed_address = Crypto::Hashes.ripemd160(Crypto::Hashes.sha256(public_key.as_hex))
37
+ network_address = public_key.network[:prefix] + hashed_address
38
+ hashed_address_again = Crypto::Hashes.sha256(Crypto::Hashes.sha256(network_address))
39
+ checksum = hashed_address_again[0..5]
40
+ Base64.strict_encode64(network_address + checksum)
41
+ end
42
+
43
+ def self.to_wif(key, network)
44
+ private_key = key.as_hex
45
+ network_key = network[:prefix] + private_key
46
+ hashed_key = Crypto::Hashes.sha256(Crypto::Hashes.sha256(network_key))
47
+ checksum = hashed_key[0..5]
48
+ encoded_key = Base64.strict_encode64(network_key + checksum)
49
+ Wif.new(encoded_key)
50
+ end
51
+
52
+ def self.from_wif(wif)
53
+ decoded_wif = Base64.strict_decode64(wif.as_hex)
54
+ network_prefix = decoded_wif[0..1]
55
+ network = network_prefix == "M0" ? MAINNET : TESTNET
56
+ private_key_hex = decoded_wif[2..-7]
57
+ private_key = PrivateKey.from_hex(private_key_hex)
58
+ {private_key: private_key, network: network}
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,45 @@
1
+ module Crypto::Keys
2
+ class PrivateKey
3
+ attr_reader :network
4
+
5
+ def initialize(private_key_hex, network = MAINNET)
6
+ @private_key_hex = private_key_hex
7
+ @network = network
8
+ raise AxentroError, "invalid private key: '#{@private_key_hex}'" unless is_valid?
9
+ end
10
+
11
+ def self.from_hex(hex, network = MAINNET)
12
+ PrivateKey.new(hex, network)
13
+ end
14
+
15
+ def self.from_bytes(bytes, network = MAINNET)
16
+ PrivateKey.new(KeyUtils.to_hex(bytes), network)
17
+ end
18
+
19
+ def as_hex
20
+ @private_key_hex
21
+ end
22
+
23
+ def as_bytes
24
+ KeyUtils.to_bytes(@private_key_hex)
25
+ end
26
+
27
+ def wif
28
+ Wif.from(self, @network)
29
+ end
30
+
31
+ def public_key
32
+ signing_key = Ed25519::SigningKey.new([@private_key_hex].pack("H*"))
33
+ hex_public_key = signing_key.keypair.unpack("H*").first[64..-1]
34
+ PublicKey.new(hex_public_key, @network)
35
+ end
36
+
37
+ def address
38
+ Address.new(KeyUtils.get_address_from_public_key(self.public_key), @network)
39
+ end
40
+
41
+ def is_valid?
42
+ !@private_key_hex.nil? && @private_key_hex.size == 64
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,35 @@
1
+ module Crypto::Keys
2
+ class PublicKey
3
+ attr_reader :network
4
+
5
+ def initialize(public_key_hex, network = MAINNET)
6
+ @public_key_hex = public_key_hex
7
+ @network = network
8
+ raise AxentroError, "invalid public key: #{@public_key_hex}" unless is_valid?
9
+ end
10
+
11
+ def self.from_hex(hex, network = MAINNET)
12
+ PublicKey.new(hex, network)
13
+ end
14
+
15
+ def self.from_bytes(bytes, network = MAINNET)
16
+ PublicKey.new(KeyUtils.to_hex(bytes), network)
17
+ end
18
+
19
+ def as_hex
20
+ @public_key_hex
21
+ end
22
+
23
+ def as_bytes
24
+ KeyUtils.to_bytes(@public_key_hex)
25
+ end
26
+
27
+ def address
28
+ Address.new(KeyUtils.get_address_from_public_key(self), @network)
29
+ end
30
+
31
+ def is_valid?
32
+ !@public_key_hex.nil? && @public_key_hex.size == 64
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,49 @@
1
+ module Crypto::Keys
2
+ class Wif
3
+ def initialize(wif_hex)
4
+ @wif_hex = wif_hex
5
+ raise AxentroError, "invalid wif: #{@wif_hex}" unless is_valid?
6
+ end
7
+
8
+ def as_hex
9
+ @wif_hex
10
+ end
11
+
12
+ def self.from(private_key, network = MAINNET)
13
+ wif = KeyUtils.to_wif(private_key, network)
14
+ raise AxentroError, "invalid wif: #{wif.as_hex}" unless wif.is_valid?
15
+ wif
16
+ end
17
+
18
+ def private_key
19
+ KeyUtils.from_wif(self)[:private_key]
20
+ end
21
+
22
+ def public_key
23
+ KeyUtils.from_wif(self)[:private_key].public_key
24
+ end
25
+
26
+ def network
27
+ KeyUtils.from_wif(self)[:network]
28
+ end
29
+
30
+ def address
31
+ res = KeyUtils.from_wif(self)
32
+ public_key = res[:private_key].public_key
33
+ network = res[:network]
34
+ Address.new(KeyUtils.get_address_from_public_key(public_key), network)
35
+ end
36
+
37
+ def is_valid?
38
+ begin
39
+ decoded_wif = Base64.strict_decode64(@wif_hex)
40
+ network_key = decoded_wif[0..-7]
41
+ hashed_key = Crypto::Hashes.sha256(Crypto::Hashes.sha256(network_key))
42
+ checksum = hashed_key[0..5]
43
+ checksum == decoded_wif[-6..-1]
44
+ rescue
45
+ false
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,57 @@
1
+ module Crypto::Transaction
2
+ extend self
3
+ SCALE_DECIMAL = 100000000
4
+
5
+ def create_signed_send_transaction(from_address, from_public_key, wif, to_address, amount, fee = "0.0001", speed = "FAST")
6
+ from_private_key = __get_private_key_from_wif(wif)
7
+ __create_signed_send_token_transaction(from_address, from_public_key, from_private_key, to_address, amount, fee, speed)
8
+ end
9
+
10
+ def post_transaction(transaction_json, url = "https://mainnet.axentro.io")
11
+ begin
12
+ response = Faraday.post("#{url}/api/v1/transaction", transaction_json, "Content-Type" => "application/json")
13
+ raise "status code was: #{response.status}" if response.status != 200
14
+ json_response = JSON.parse(response.body)
15
+ json_response["result"]["id"].to_s
16
+ rescue => e
17
+ puts "Error sending transaction: #{e}"
18
+ end
19
+ end
20
+
21
+ def __create_signed_send_token_transaction(from_address, from_public_key, from_private_key, to_address, amount, fee = "0.0001", speed = "FAST")
22
+ transaction_id = __create_id
23
+ timestamp = __timestamp
24
+ scaled_amount = __scale_i64(amount)
25
+ scaled_fee = __scale_i64(fee)
26
+
27
+ unsigned_transaction = %Q{{"id":"#{transaction_id}","action":"send","senders":[{"address":"#{from_address}","public_key":"#{from_public_key}","amount":#{scaled_amount},"fee":#{scaled_fee},"signature":"0"}],"recipients":[{"address":"#{to_address}","amount":#{scaled_amount}}],"message":"","token":"AXNT","prev_hash":"0","timestamp":#{timestamp},"scaled":1,"kind":"#{speed}","version":"V1"}}
28
+
29
+ payload_hash = Hashes.sha256(unsigned_transaction)
30
+ signature = KeyUtils.sign(from_private_key, payload_hash)
31
+ signed_transaction = __to_signed(unsigned_transaction, signature)
32
+
33
+ %Q{{"transaction": #{signed_transaction}}}
34
+ end
35
+
36
+ def __create_id
37
+ tmp_id = SecureRandom.hex(32)
38
+ return __create_id if tmp_id[0] == "0"
39
+ tmp_id
40
+ end
41
+
42
+ def __timestamp
43
+ Time.now.to_i * 1000
44
+ end
45
+
46
+ def __to_signed(unsigned_transaction, signature)
47
+ unsigned_transaction.gsub(%Q{"signature":"0"}, %Q{"signature":"#{signature}"})
48
+ end
49
+
50
+ def __get_private_key_from_wif(wif)
51
+ Base64.strict_decode64(wif)[2..-7]
52
+ end
53
+
54
+ def __scale_i64(value)
55
+ (BigDecimal(value) * SCALE_DECIMAL).to_i
56
+ end
57
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: axentro-rb-util
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Kingsley Hendrickse
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-04-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ed25519
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.2'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.2.4
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.2'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.2.4
33
+ - !ruby/object:Gem::Dependency
34
+ name: ed25519-hd-rb
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 0.0.1
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 0.0.1
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: 0.0.1
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 0.0.1
53
+ - !ruby/object:Gem::Dependency
54
+ name: faraday
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '1.0'
60
+ type: :runtime
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '1.0'
67
+ - !ruby/object:Gem::Dependency
68
+ name: bundler
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '1.3'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '1.3'
81
+ - !ruby/object:Gem::Dependency
82
+ name: rake
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: rspec
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ description: Sending transactions
110
+ email: kingsley@axentro.io
111
+ executables: []
112
+ extensions: []
113
+ extra_rdoc_files: []
114
+ files:
115
+ - lib/axentro-rb-util.rb
116
+ - lib/crypto/crypto.rb
117
+ - lib/crypto/hashes.rb
118
+ - lib/crypto/key_ring.rb
119
+ - lib/crypto/keys/address.rb
120
+ - lib/crypto/keys/key_utils.rb
121
+ - lib/crypto/keys/private_key.rb
122
+ - lib/crypto/keys/public_key.rb
123
+ - lib/crypto/keys/wif.rb
124
+ - lib/crypto/transaction.rb
125
+ homepage: https://rubygems.org/gems/axentro-rb-util
126
+ licenses:
127
+ - MIT
128
+ metadata: {}
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubygems_version: 3.1.4
145
+ signing_key:
146
+ specification_version: 4
147
+ summary: Tools for Axentro blockchain crypto in Ruby
148
+ test_files: []