axentro-rb-util 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []