zilliqa 0.1.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.
@@ -0,0 +1,61 @@
1
+ require 'secp256k1'
2
+ require 'digest'
3
+
4
+ module Zilliqa
5
+ module Crypto
6
+ class KeyTool
7
+ include Secp256k1
8
+ def initialize(private_key)
9
+ is_raw = private_key.length == 32
10
+
11
+ @pk = PrivateKey.new(privkey: private_key, raw: is_raw)
12
+ end
13
+
14
+ def self.generate_private_key
15
+ Util.encode_hex KeyTool.generate_random_bytes(32)
16
+ end
17
+
18
+ def self.generate_random_bytes(size)
19
+ SecureRandom.random_bytes(size)
20
+ end
21
+
22
+ # getPubKeyFromPrivateKey
23
+ #
24
+ # takes a hex-encoded string (private key) and returns its corresponding
25
+ # hex-encoded 33-byte public key.
26
+ #
27
+ # @param {string} privateKey
28
+ # @returns {string}
29
+ def self.get_public_key_from_private_key(private_key, is_compressed = true)
30
+ is_raw = private_key.length == 32
31
+
32
+ pk = PrivateKey.new(privkey: private_key, raw: is_raw)
33
+
34
+ (Util.encode_hex pk.pubkey.serialize(compressed: is_compressed)).downcase
35
+ end
36
+
37
+ # getAddressFromPrivateKey
38
+ #
39
+ # takes a hex-encoded string (private key) and returns its corresponding
40
+ # 20-byte hex-encoded address.
41
+ #
42
+ # @param {string} privateKey
43
+ # @returns {string}
44
+ def self.get_address_from_private_key(private_key)
45
+ public_key = KeyTool.get_public_key_from_private_key(private_key)
46
+ KeyTool.get_address_from_public_key(public_key)
47
+ end
48
+
49
+ # getAddressFromPublicKey
50
+ #
51
+ # takes hex-encoded string and returns the corresponding address
52
+ #
53
+ # @param {string} public_key
54
+ # @returns {string}
55
+ def self.get_address_from_public_key(public_key)
56
+ orig_address = Digest::SHA256.hexdigest Util.decode_hex public_key
57
+ Util::Bech32.to_bech32(orig_address[24..-1].downcase)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,147 @@
1
+ require 'secp256k1'
2
+ require 'digest'
3
+ require 'openssl'
4
+
5
+ module Zilliqa
6
+ module Crypto
7
+ class Schnorr
8
+ include Secp256k1
9
+
10
+ N = OpenSSL::BN.new('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16)
11
+ G = OpenSSL::BN.new('79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798', 16)
12
+
13
+ def initialize
14
+ end
15
+
16
+ # sign
17
+ #
18
+ # @param {String} msg
19
+ # @param {String} key
20
+ def self.sign(message, private_key, public_key)
21
+ sig = nil
22
+ until sig
23
+ k = Util.encode_hex SecureRandom.random_bytes(32)
24
+ k_bn = OpenSSL::BN.new(k, 16)
25
+
26
+ sig = try_sign(message, private_key, k_bn, public_key)
27
+ sig = Zilliqa::Util::Validator.signature?(sig.to_s) ? sig : nil
28
+ end
29
+
30
+ sig
31
+ end
32
+
33
+ # trySign
34
+ #
35
+ # @param {String} message - the message to sign over
36
+ # @param {String} privateKey - the private key
37
+ # @param {BN} k_bn - output of the HMAC-DRBG
38
+ #
39
+ # @returns {Signature | null =>}
40
+ def self.try_sign(message, private_key, k_bn, public_key)
41
+ group = OpenSSL::PKey::EC::Group.new('secp256k1')
42
+
43
+ prikey_bn = OpenSSL::BN.new(private_key, 16)
44
+
45
+ pubkey_bn = OpenSSL::BN.new(public_key, 16)
46
+ pubkey_point = OpenSSL::PKey::EC::Point.new(group, pubkey_bn)
47
+
48
+ throw 'Bad private key.' if prikey_bn.zero? || prikey_bn >= N
49
+
50
+ # 1a. check that k is not 0
51
+ return nil if k_bn.zero?
52
+
53
+ # 1b. check that k is < the order of the group
54
+ return nil if k_bn >= N
55
+
56
+ # 2. Compute commitment Q = kG, where g is the base point
57
+ q_point = pubkey_point.mul(0, k_bn)
58
+
59
+ # 3. Compute the challenge r = H(Q || pubKey || msg)
60
+ # mod reduce the r value by the order of secp256k1, n
61
+ r_bn = hash(q_point, pubkey_point, message) % N
62
+
63
+ return nil if r_bn.zero?
64
+
65
+ # 4. Compute s = k - r * prv
66
+ # 4a. Compute r * prv
67
+ s_bn = r_bn * prikey_bn % N
68
+ # 4b. Compute s = k - r * prv mod n
69
+ s_bn = k_bn.mod_sub(s_bn, N)
70
+
71
+ return nil if s_bn.zero?
72
+
73
+ Signature.new(r_bn.to_s(16), s_bn.to_s(16))
74
+ end
75
+
76
+
77
+ # Verify signature.
78
+ #
79
+ # @param {Buffer} message
80
+ # @param {Buffer} sig
81
+ # @param {Buffer} public_key
82
+ #
83
+ # @returns {boolean}
84
+ #
85
+ # 1. Check if r,s is in [1, ..., order-1]
86
+ # 2. Compute Q = sG + r*kpub
87
+ # 3. If Q = O (the neutral point), return 0;
88
+ # 4. r' = H(Q, kpub, m)
89
+ # 5. return r' == r
90
+ def self.verify(message, sig, public_key)
91
+ pubkey = PublicKey.new
92
+ pubkey.deserialize Util.decode_hex(public_key)
93
+
94
+ r = sig.r
95
+ r_bn = OpenSSL::BN.new(r, 16)
96
+
97
+ s = sig.s
98
+ s_bn = OpenSSL::BN.new(s, 16)
99
+
100
+ throw 'Invalid signature' if (s_bn.zero? || r_bn.zero?)
101
+
102
+ throw 'Invalid signature' if (s_bn.negative? || r_bn.negative?)
103
+
104
+ throw 'Invalid signature' if (s_bn >= N || r_bn >= N)
105
+
106
+ group = OpenSSL::PKey::EC::Group.new('secp256k1')
107
+ pubkey_bn = OpenSSL::BN.new(public_key, 16)
108
+ pubkey_point = OpenSSL::PKey::EC::Point.new(group, pubkey_bn)
109
+
110
+ throw 'Invalid public key' unless pubkey_point.on_curve?
111
+
112
+ q_point = pubkey_point.mul(r_bn, s_bn)
113
+
114
+ throw 'Invalid intermediate point.' if q_point.infinity?
115
+
116
+ h_bn = self.hash(q_point, pubkey_point, message) % N
117
+
118
+ throw 'Invalid hash.' if (h_bn.zero?)
119
+
120
+ h_bn.eql?(r_bn)
121
+ end
122
+
123
+
124
+ # Hash (r | M).
125
+ def self.hash(q_point, pubkey_point, message)
126
+ sha256 = Digest::SHA256.new
127
+ sha256 << q_point.to_octet_string(:compressed)
128
+ sha256 << pubkey_point.to_octet_string(:compressed)
129
+ sha256 << Util.decode_hex(message)
130
+
131
+ OpenSSL::BN.new(sha256.hexdigest, 16)
132
+ end
133
+ end
134
+
135
+ class Signature
136
+ attr_reader :r, :s
137
+ def initialize(r, s)
138
+ @r = r
139
+ @s = s
140
+ end
141
+
142
+ def to_s
143
+ "#{@r}#{@s}"
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,31 @@
1
+ require 'jsonrpc-client'
2
+
3
+ module JSONRPC
4
+ class Base
5
+ def self.make_id
6
+ "1"
7
+ end
8
+ end
9
+ end
10
+
11
+ module Zilliqa
12
+ module Jsonrpc
13
+ class Provider
14
+ def initialize(endpoint)
15
+ conn = Faraday.new { |connection|
16
+ connection.adapter Faraday.default_adapter
17
+ }
18
+ @client = JSONRPC::Client.new(endpoint, { connection: conn })
19
+ @endpoint = endpoint
20
+ end
21
+
22
+ def method_missing(sym, *args)
23
+ @client.invoke(sym.to_s, args)
24
+ end
25
+
26
+ def testnet?
27
+ @endpoint && !@endpoint.match('dev').nil?
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,44 @@
1
+ syntax = "proto2";
2
+
3
+ package laksa.proto;
4
+
5
+ // ============================================================================
6
+ // Primitives
7
+ // ============================================================================
8
+
9
+ message ByteArray
10
+ {
11
+ required bytes data = 1;
12
+ }
13
+
14
+ message ProtoTransactionCoreInfo
15
+ {
16
+ optional uint32 version = 1;
17
+ optional uint64 nonce = 2;
18
+ optional bytes toaddr = 3;
19
+ optional ByteArray senderpubkey = 4;
20
+ optional ByteArray amount = 5;
21
+ optional ByteArray gasprice = 6;
22
+ optional uint64 gaslimit = 7;
23
+ optional bytes code = 8;
24
+ optional bytes data = 9;
25
+ }
26
+
27
+ message ProtoTransaction
28
+ {
29
+ optional bytes tranid = 1;
30
+ optional ProtoTransactionCoreInfo info = 2;
31
+ optional ByteArray signature = 3;
32
+ }
33
+
34
+ message ProtoTransactionReceipt
35
+ {
36
+ optional bytes receipt = 1;
37
+ optional uint64 cumgas = 2;
38
+ }
39
+
40
+ message ProtoTransactionWithReceipt
41
+ {
42
+ optional ProtoTransaction transaction = 1;
43
+ optional ProtoTransactionReceipt receipt = 2;
44
+ }
@@ -0,0 +1,46 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # source: message.proto
3
+
4
+ require 'google/protobuf'
5
+
6
+ Google::Protobuf::DescriptorPool.generated_pool.build do
7
+ add_file("message.proto", :syntax => :proto2) do
8
+ add_message "laksa.proto.ByteArray" do
9
+ required :data, :bytes, 1
10
+ end
11
+ add_message "laksa.proto.ProtoTransactionCoreInfo" do
12
+ optional :version, :uint32, 1
13
+ optional :nonce, :uint64, 2
14
+ optional :toaddr, :bytes, 3
15
+ optional :senderpubkey, :message, 4, "laksa.proto.ByteArray"
16
+ optional :amount, :message, 5, "laksa.proto.ByteArray"
17
+ optional :gasprice, :message, 6, "laksa.proto.ByteArray"
18
+ optional :gaslimit, :uint64, 7
19
+ optional :code, :bytes, 8
20
+ optional :data, :bytes, 9
21
+ end
22
+ add_message "laksa.proto.ProtoTransaction" do
23
+ optional :tranid, :bytes, 1
24
+ optional :info, :message, 2, "laksa.proto.ProtoTransactionCoreInfo"
25
+ optional :signature, :message, 3, "laksa.proto.ByteArray"
26
+ end
27
+ add_message "laksa.proto.ProtoTransactionReceipt" do
28
+ optional :receipt, :bytes, 1
29
+ optional :cumgas, :uint64, 2
30
+ end
31
+ add_message "laksa.proto.ProtoTransactionWithReceipt" do
32
+ optional :transaction, :message, 1, "laksa.proto.ProtoTransaction"
33
+ optional :receipt, :message, 2, "laksa.proto.ProtoTransactionReceipt"
34
+ end
35
+ end
36
+ end
37
+
38
+ module Zilliqa
39
+ module Proto
40
+ ByteArray = Google::Protobuf::DescriptorPool.generated_pool.lookup("laksa.proto.ByteArray").msgclass
41
+ ProtoTransactionCoreInfo = Google::Protobuf::DescriptorPool.generated_pool.lookup("laksa.proto.ProtoTransactionCoreInfo").msgclass
42
+ ProtoTransaction = Google::Protobuf::DescriptorPool.generated_pool.lookup("laksa.proto.ProtoTransaction").msgclass
43
+ ProtoTransactionReceipt = Google::Protobuf::DescriptorPool.generated_pool.lookup("laksa.proto.ProtoTransactionReceipt").msgclass
44
+ ProtoTransactionWithReceipt = Google::Protobuf::DescriptorPool.generated_pool.lookup("laksa.proto.ProtoTransactionWithReceipt").msgclass
45
+ end
46
+ end
@@ -0,0 +1,28 @@
1
+ require 'bitcoin'
2
+
3
+ module Zilliqa
4
+ module Util
5
+ class Bech32
6
+
7
+ def self.to_bech32(address)
8
+ raise 'Invalid address format.' unless Validator.address?(address)
9
+
10
+ address = address.sub('0x','')
11
+
12
+ ret = Bitcoin::Bech32.convert_bits(Util.decode_hex(address).bytes, from_bits: 8, to_bits: 5, pad: false)
13
+
14
+ Bitcoin::Bech32.encode('zil', ret);
15
+ end
16
+
17
+ def self.from_bech32(address)
18
+ data = Bitcoin::Bech32.decode(address)
19
+
20
+ raise 'Expected hrp to be zil' unless data[0] == 'zil'
21
+
22
+ ret = Bitcoin::Bech32.convert_bits(data[1], from_bits: 5, to_bits: 8, pad: false)
23
+
24
+ Zilliqa::Account::Wallet.to_checksum_address(Util.encode_hex(ret.pack('c*'))).sub('0x', '')
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,37 @@
1
+ module Zilliqa
2
+ module Util
3
+ class Unit
4
+ ZIL = 'zil'
5
+ LI = 'li'
6
+ QA = 'qa'
7
+
8
+ def self.from_qa(qa, unit, is_pack = false)
9
+ ret = case unit
10
+ when ZIL
11
+ qa / 1000000000000.0
12
+ when LI
13
+ qa / 1000000.0
14
+ when QA
15
+ qa
16
+ end
17
+
18
+ if is_pack
19
+ ret.round
20
+ else
21
+ ret
22
+ end
23
+ end
24
+
25
+ def self.to_qa(qa, unit)
26
+ case unit
27
+ when ZIL
28
+ qa * 1000000000000
29
+ when LI
30
+ qa * 1000000
31
+ when QA
32
+ qa
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,17 @@
1
+ module Zilliqa
2
+ module Util
3
+ extend self
4
+
5
+ def pack(a, b)
6
+ a << 16 + b
7
+ end
8
+
9
+ def encode_hex(b)
10
+ b.unpack('H*').first
11
+ end
12
+
13
+ def decode_hex(s)
14
+ [s].pack('H*')
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,41 @@
1
+ module Zilliqa
2
+ module Util
3
+ class Validator
4
+ def self.public_key?(public_key)
5
+ m = /(0x)?\h{66}/ =~ public_key
6
+ m != nil
7
+ end
8
+
9
+ def self.private_key?(private_key)
10
+ m = /(0x)?\h{64}/ =~ private_key
11
+ m != nil
12
+ end
13
+
14
+ def self.address?(address)
15
+ return true if bech32?(address)
16
+ m = /(0x)?\h{40}/ =~ address
17
+ m != nil
18
+ end
19
+
20
+ def self.signature?(signature)
21
+ m = /(0x)?\h{128}/ =~ signature
22
+ m != nil
23
+ end
24
+
25
+ # checksum_address?
26
+ #
27
+ # takes hex-encoded string and returns boolean if address is checksumed
28
+ #
29
+ # @param {string} address
30
+ # @returns {boolean}
31
+ def self.checksum_address?(address)
32
+ self.address?(address) && Zilliqa::Account::Wallet::to_checksum_address(address) == address
33
+ end
34
+
35
+ def self.bech32?(address)
36
+ m = /^zil1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{38}/ =~ address
37
+ m != nil
38
+ end
39
+ end
40
+ end
41
+ end