neb 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +62 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +15 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +21 -0
- data/README.md +123 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/neb/account.rb +55 -0
- data/lib/neb/address.rb +90 -0
- data/lib/neb/base_convert.rb +57 -0
- data/lib/neb/client/admin.rb +95 -0
- data/lib/neb/client/api.rb +104 -0
- data/lib/neb/client/request.rb +43 -0
- data/lib/neb/client/response.rb +36 -0
- data/lib/neb/client.rb +19 -0
- data/lib/neb/configuration.rb +38 -0
- data/lib/neb/constant.rb +35 -0
- data/lib/neb/core_ext.rb +45 -0
- data/lib/neb/exceptions.rb +29 -0
- data/lib/neb/key.rb +133 -0
- data/lib/neb/private_key.rb +91 -0
- data/lib/neb/proto/transaction.proto +40 -0
- data/lib/neb/proto/transaction_pb.rb +30 -0
- data/lib/neb/public_key.rb +87 -0
- data/lib/neb/secp256k1.rb +66 -0
- data/lib/neb/transaction.rb +120 -0
- data/lib/neb/utils.rb +128 -0
- data/lib/neb/version.rb +6 -0
- data/lib/neb.rb +71 -0
- data/neb.gemspec +40 -0
- data/tmp/.keep +0 -0
- metadata +288 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative "./request"
|
5
|
+
require_relative "./response"
|
6
|
+
|
7
|
+
module Neb
|
8
|
+
class Client
|
9
|
+
class API
|
10
|
+
attr_reader :host, :api_version, :endpoint
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@host = CONFIG[:host]
|
14
|
+
@api_version = CONFIG[:api_version]
|
15
|
+
@endpoint = CONFIG[:api_endpoint]
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_neb_state
|
19
|
+
send_request(:get, "/nebstate")
|
20
|
+
end
|
21
|
+
|
22
|
+
def latest_irreversible_block
|
23
|
+
send_request(:get, "/lib")
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_account_state(address: , height: 0)
|
27
|
+
send_request(:post, "/accountstate", address: address, height: height)
|
28
|
+
end
|
29
|
+
|
30
|
+
def call(from:, to:, value:, nonce:, gas_price: 1_000_000, gas_limit: 20_000, contract: nil)
|
31
|
+
params = {
|
32
|
+
from: from,
|
33
|
+
to: to,
|
34
|
+
value: value.to_i,
|
35
|
+
nonce: nonce.to_i,
|
36
|
+
gas_price: gas_price.to_i,
|
37
|
+
gas_limit: gas_limit.to_i,
|
38
|
+
contract: contract
|
39
|
+
}
|
40
|
+
|
41
|
+
send_request(:post, "/call", params)
|
42
|
+
end
|
43
|
+
|
44
|
+
def send_raw_transaction(data:)
|
45
|
+
send_request(:post, "/rawtransaction", data: data)
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_block_by_hash(hash:, is_full: true)
|
49
|
+
send_request(:post, "/getBlockByHash", hash: hash, full_fill_transaction: is_full)
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_block_by_height(height:, is_full: true)
|
53
|
+
send_request(:post, "/getBlockByHeight", height: height, full_fill_transaction: is_full)
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_transaction_receipt(hash:)
|
57
|
+
send_request(:post, "/getTransactionReceipt", hash: hash)
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_transaction_by_contract(address:)
|
61
|
+
send_request(:post, "/getTransactionByContract", address: address)
|
62
|
+
end
|
63
|
+
|
64
|
+
def subscribe(topics: [])
|
65
|
+
send_request(:post, "/subscribe", topics: topics)
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_gas_price
|
69
|
+
send_request(:get, "/getGasPrice")
|
70
|
+
end
|
71
|
+
alias_method :gas_price, :get_gas_price
|
72
|
+
|
73
|
+
def estimate_gas(from:, to:, value:, nonce:, gas_price: 1_000_000, gas_limit: 20_000, contract: nil, binary: nil)
|
74
|
+
params = {
|
75
|
+
from: from,
|
76
|
+
to: to,
|
77
|
+
value: value.to_i,
|
78
|
+
nonce: nonce.to_i,
|
79
|
+
gas_price: gas_price.to_i,
|
80
|
+
gas_limit: gas_limit.to_i,
|
81
|
+
contract: contract,
|
82
|
+
binary: binary
|
83
|
+
}
|
84
|
+
|
85
|
+
send_request(:post, "/estimateGas", params)
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_events_by_hash(hash:)
|
89
|
+
send_request(:post, "/getEventsByHash", hash: hash)
|
90
|
+
end
|
91
|
+
|
92
|
+
def get_dynasty(height:)
|
93
|
+
send_request(:post, "/dynasty", height: height)
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def send_request(action, url, payload = {})
|
99
|
+
request_url = host + api_version + endpoint + url
|
100
|
+
Request.new(action, request_url, payload).execute
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Neb
|
5
|
+
class Client
|
6
|
+
class Request
|
7
|
+
attr_reader :action, :url, :payload
|
8
|
+
|
9
|
+
def initialize(action, url, payload = {}, headers = {})
|
10
|
+
@action = action
|
11
|
+
@url = url
|
12
|
+
@payload = payload
|
13
|
+
@headers = headers.blank? ? default_headers : headers
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
"#{self.class}{action=#{action}, url=#{url}, payload=#{payload}}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def request_args
|
21
|
+
{
|
22
|
+
method: action,
|
23
|
+
url: url,
|
24
|
+
payload: payload.deep_camelize_keys(:lower).to_json,
|
25
|
+
headers: { content_type: :json, accept: :json }
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def execute
|
30
|
+
Neb.logger.debug(self.to_s)
|
31
|
+
::RestClient::Request.execute(request_args) do |resp, _, _|
|
32
|
+
Response.new(resp)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def default_headers
|
39
|
+
{ content_type: :json, accept: :json }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Neb
|
5
|
+
class Client
|
6
|
+
class Response
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
attr_reader :response
|
10
|
+
def_delegators :@response, :code, :body
|
11
|
+
|
12
|
+
SUCCESS_CODE = 200
|
13
|
+
|
14
|
+
def initialize(response)
|
15
|
+
@response = response
|
16
|
+
Neb.logger.debug(self.to_s)
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"#{self.class}{code=#{code}, body=#{body}}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def result
|
24
|
+
JSON.parse(body, symbolize_names: true)[:result] if success?
|
25
|
+
end
|
26
|
+
|
27
|
+
def error
|
28
|
+
JSON.parse(body, symbolize_names: true)[:error] if !success?
|
29
|
+
end
|
30
|
+
|
31
|
+
def success?
|
32
|
+
code == SUCCESS_CODE
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/neb/client.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative "./client/admin"
|
5
|
+
require_relative "./client/api"
|
6
|
+
|
7
|
+
module Neb
|
8
|
+
class Client
|
9
|
+
attr_reader :api, :admin
|
10
|
+
|
11
|
+
def initialize(config = {})
|
12
|
+
Neb.configure(config) unless Neb.configured?
|
13
|
+
|
14
|
+
@api = API.new
|
15
|
+
@admin = Admin.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Neb
|
5
|
+
class Configuration
|
6
|
+
extend Forwardable
|
7
|
+
def_delegators :@hash, :to_hash, :[], :[]=, :==, :fetch, :delete, :has_key?
|
8
|
+
|
9
|
+
DEFAULTS = {
|
10
|
+
host: "http://localhost:8685",
|
11
|
+
timeout: "0",
|
12
|
+
api_version: "/v1",
|
13
|
+
log: "log/neb.log",
|
14
|
+
api_endpoint: "/user",
|
15
|
+
admin_endpoint: "/admin"
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
clear
|
20
|
+
end
|
21
|
+
|
22
|
+
def clear
|
23
|
+
@hash = DEFAULTS.dup
|
24
|
+
end
|
25
|
+
|
26
|
+
def merge!(hash)
|
27
|
+
hash = hash.dup
|
28
|
+
@hash = @hash.deep_merge(hash)
|
29
|
+
end
|
30
|
+
|
31
|
+
def merge(hash)
|
32
|
+
instance = self.class.new
|
33
|
+
instance.merge!(to_hash)
|
34
|
+
instance.merge!(hash)
|
35
|
+
instance
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/neb/constant.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Neb
|
5
|
+
module Constant
|
6
|
+
|
7
|
+
BYTE_EMPTY = "".freeze
|
8
|
+
BYTE_ZERO = "\x00".freeze
|
9
|
+
BYTE_ONE = "\x01".freeze
|
10
|
+
|
11
|
+
TT32 = 2**32
|
12
|
+
TT40 = 2**40
|
13
|
+
TT160 = 2**160
|
14
|
+
TT256 = 2**256
|
15
|
+
TT64M1 = 2**64 - 1
|
16
|
+
|
17
|
+
UINT_MAX = 2**256 - 1
|
18
|
+
UINT_MIN = 0
|
19
|
+
INT_MAX = 2**255 - 1
|
20
|
+
INT_MIN = -2**255
|
21
|
+
|
22
|
+
HASH_ZERO = ("\x00"*32).freeze
|
23
|
+
|
24
|
+
PUBKEY_ZERO = ("\x00"*32).freeze
|
25
|
+
PRIVKEY_ZERO = ("\x00"*32).freeze
|
26
|
+
PRIVKEY_ZERO_HEX = ('0'*64).freeze
|
27
|
+
|
28
|
+
CONTRACT_CODE_SIZE_LIMIT = 0x6000
|
29
|
+
|
30
|
+
ADDRESS_LENGTH = 26
|
31
|
+
ADDRESS_PREFIX = 25
|
32
|
+
NORMAL_TYPE = 87
|
33
|
+
CONTRACT_TYPE = 88
|
34
|
+
end
|
35
|
+
end
|
data/lib/neb/core_ext.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
class Hash
|
5
|
+
# Returns a new hash with all keys converted to UpperCamelCase strings.If th
|
6
|
+
# is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
|
7
|
+
#
|
8
|
+
# hash = { my_name: 'Rob', my_age: '28' }
|
9
|
+
#
|
10
|
+
# hash.camelize_keys
|
11
|
+
# # => {"MyName"=>"Rob", "MyAge"=>"28"}
|
12
|
+
#
|
13
|
+
# hash.camelize_keys(:lower)
|
14
|
+
# # => {"myName"=>"Rob", "myAge"=>"28"}
|
15
|
+
def camelize_keys(first_letter = :upper)
|
16
|
+
transform_keys { |key| key.to_s.camelize(first_letter) rescue key }
|
17
|
+
end
|
18
|
+
|
19
|
+
# Destructively converts all keys to strings. Same as
|
20
|
+
# +camelize_keys+, but modifies +self+.
|
21
|
+
def camelize_keys!(first_letter = :upper)
|
22
|
+
transform_keys! { |key| key.to_s.camelize(first_letter) rescue key }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns a new hash with all keys converted to UpperCamelCase strings.If th
|
26
|
+
# is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
|
27
|
+
# This includes the keys from the root hash
|
28
|
+
# and from all nested hashes and arrays.
|
29
|
+
#
|
30
|
+
# hash = { my_name: 'Rob', my_age: '28', my_friend: { his_name: "bob" } }
|
31
|
+
#
|
32
|
+
# hash.deep_camelize_keys
|
33
|
+
# # => {"MyName"=>"Rob", "MyAge"=>"28", "MyFriend"=>{"HisName"=>"bob"}}
|
34
|
+
def deep_camelize_keys(first_letter = :upper)
|
35
|
+
deep_transform_keys { |key| key.to_s.camelize(first_letter) rescue key }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Destructively converts all keys to strings. Same as
|
39
|
+
# +camelize_keys+, but modifies +self+.
|
40
|
+
# This includes the keys from the root hash and from all
|
41
|
+
# nested hashes and arrays.
|
42
|
+
def deep_camelize_keys!(first_letter = :upper)
|
43
|
+
deep_transform_keys! { |key| key.to_s.camelize(first_letter) rescue key }
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Neb
|
5
|
+
class DeprecatedError < StandardError; end
|
6
|
+
class InvalidJSONKeyError < StandardError; end
|
7
|
+
class ChecksumError < StandardError; end
|
8
|
+
class FormatError < StandardError; end
|
9
|
+
class ValidationError < StandardError; end
|
10
|
+
class ValueError < StandardError; end
|
11
|
+
class AssertError < StandardError; end
|
12
|
+
class UnsignError < StandardError; end
|
13
|
+
|
14
|
+
class UnknownParentError < StandardError; end
|
15
|
+
class InvalidBlock < ValidationError; end
|
16
|
+
class InvalidUncles < ValidationError; end
|
17
|
+
|
18
|
+
class InvalidTransaction < ValidationError; end
|
19
|
+
class UnsignedTransactionError < InvalidTransaction; end
|
20
|
+
class InvalidNonce < InvalidTransaction; end
|
21
|
+
class InsufficientStartGas < InvalidTransaction; end
|
22
|
+
class InsufficientBalance < InvalidTransaction; end
|
23
|
+
class BlockGasLimitReached < InvalidTransaction; end
|
24
|
+
|
25
|
+
class InvalidSPVProof < ValidationError; end
|
26
|
+
|
27
|
+
class ContractCreationFailed < StandardError; end
|
28
|
+
class TransactionFailed < StandardError; end
|
29
|
+
end
|
data/lib/neb/key.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Neb
|
5
|
+
class Key
|
6
|
+
KDF = "scrypt".freeze
|
7
|
+
CIPHER_NAME = "aes-128-ctr".freeze
|
8
|
+
MACHASH = "sha3256".freeze
|
9
|
+
|
10
|
+
KDF_N = 1 << 12
|
11
|
+
KDF_R = 8
|
12
|
+
KDF_P = 1
|
13
|
+
DKLEN = 32
|
14
|
+
|
15
|
+
KEY_CURRENT_VERSION = 4
|
16
|
+
KEY_VERSION_3 = 3
|
17
|
+
|
18
|
+
attr_accessor :address, :private_key, :password, :salt, :iv, :mac
|
19
|
+
|
20
|
+
def initialize(address: nil, private_key: nil, password: nil, salt: nil, iv: nil)
|
21
|
+
@address = Address.new(address) if address
|
22
|
+
@private_key = PrivateKey.new(private_key) if private_key
|
23
|
+
@password = password
|
24
|
+
@salt = self.class.convert_salt(salt || Utils.random_bytes(32))
|
25
|
+
@iv = self.class.convert_iv(iv || Utils.random_bytes(16))
|
26
|
+
end
|
27
|
+
|
28
|
+
def encrypt
|
29
|
+
derived_key = Utils.scrypt(password, salt, KDF_N, KDF_R, KDF_P, DKLEN)
|
30
|
+
ciphertext_bin = Utils.aes_encrypt(private_key.encode(:bin), derived_key[0, 16], iv)
|
31
|
+
mac_bin = Utils.keccak256([derived_key[16, 16], ciphertext_bin, iv, CIPHER_NAME].join)
|
32
|
+
|
33
|
+
{
|
34
|
+
version: KEY_CURRENT_VERSION,
|
35
|
+
id: Utils.uuid,
|
36
|
+
address: address.to_s,
|
37
|
+
crypto: {
|
38
|
+
ciphertext: Utils.bin_to_hex(ciphertext_bin),
|
39
|
+
cipherparams: {
|
40
|
+
iv: Utils.bin_to_hex(iv)
|
41
|
+
},
|
42
|
+
cipher: CIPHER_NAME,
|
43
|
+
kdf: KDF,
|
44
|
+
kdfparams: {
|
45
|
+
dklen: DKLEN,
|
46
|
+
salt: Utils.bin_to_hex(salt),
|
47
|
+
n: KDF_N,
|
48
|
+
r: KDF_R,
|
49
|
+
p: KDF_P
|
50
|
+
},
|
51
|
+
mac: Utils.bin_to_hex(mac_bin),
|
52
|
+
machash: MACHASH
|
53
|
+
}
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
class << self
|
58
|
+
|
59
|
+
def encrypt(address, private_key, password)
|
60
|
+
new(address: address, private_key: private_key, password: password).encrypt
|
61
|
+
end
|
62
|
+
|
63
|
+
def decrypt(key_data, password)
|
64
|
+
key_data = Utils.from_json(key_data) if key_data.is_a?(String)
|
65
|
+
key_data = key_data.deep_symbolize_keys!
|
66
|
+
|
67
|
+
raise InvalidJSONKeyError("key data validate failed") if !validate?(key_data)
|
68
|
+
|
69
|
+
version = key_data[:version]
|
70
|
+
address = key_data[:address]
|
71
|
+
crypto = key_data[:crypto]
|
72
|
+
|
73
|
+
cipher = crypto[:cipher]
|
74
|
+
salt = convert_salt(crypto[:kdfparams][:salt])
|
75
|
+
dklen = crypto[:kdfparams][:dklen]
|
76
|
+
kdf_n = crypto[:kdfparams][:n]
|
77
|
+
kdf_r = crypto[:kdfparams][:r]
|
78
|
+
kdf_p = crypto[:kdfparams][:p]
|
79
|
+
iv = convert_iv(crypto[:cipherparams][:iv])
|
80
|
+
|
81
|
+
derived_key = Utils.scrypt(password, salt, kdf_n, kdf_r, kdf_p, dklen)
|
82
|
+
ciphertext_bin = Utils.hex_to_bin(crypto[:ciphertext])
|
83
|
+
|
84
|
+
if version == KEY_CURRENT_VERSION
|
85
|
+
mac = Utils.keccak256([derived_key[16, 16], ciphertext_bin, iv, cipher].join)
|
86
|
+
else
|
87
|
+
mac = Utils.keccak256([derived_key[16, 16], ciphertext_bin].join) # KeyVersion3
|
88
|
+
end
|
89
|
+
|
90
|
+
raise InvalidJSONKeyError.new("mac is wrong") if Utils.bin_to_hex(mac) != crypto[:mac]
|
91
|
+
|
92
|
+
private_key = Utils.aes_decrypt(ciphertext_bin, derived_key[0, 16], iv)
|
93
|
+
Utils.bin_to_hex(private_key)
|
94
|
+
end
|
95
|
+
|
96
|
+
def validate?(key_data)
|
97
|
+
key_data = Utils.from_json(key_data) if key_data.is_a?(String)
|
98
|
+
|
99
|
+
[:version, :id, :address, :crypto].each do |k|
|
100
|
+
return false if !key_data.keys.include?(k)
|
101
|
+
end
|
102
|
+
|
103
|
+
return false if key_data[:version] != KEY_CURRENT_VERSION && key_data[:version] != KEY_VERSION_3
|
104
|
+
|
105
|
+
[:ciphertext, :cipherparams, :cipher, :kdf, :kdfparams, :mac, :machash].each do |k|
|
106
|
+
return false if !key_data[:crypto].keys.include?(k)
|
107
|
+
end
|
108
|
+
|
109
|
+
return false if !key_data[:crypto][:cipherparams].keys.include?(:iv)
|
110
|
+
|
111
|
+
[:dklen, :salt, :n, :r, :p].each do |k|
|
112
|
+
return false if !key_data[:crypto][:kdfparams].keys.include?(k)
|
113
|
+
end
|
114
|
+
|
115
|
+
true
|
116
|
+
end
|
117
|
+
|
118
|
+
def convert_salt(salt)
|
119
|
+
return salt if salt.length == 32
|
120
|
+
return Utils.hex_to_bin(salt) if salt.length == 64
|
121
|
+
|
122
|
+
raise ArgumentError.new("salt must be 32 bytes")
|
123
|
+
end
|
124
|
+
|
125
|
+
def convert_iv(iv)
|
126
|
+
return iv if iv.length == 16
|
127
|
+
return Utils.hex_to_bin(iv) if iv.length == 32
|
128
|
+
|
129
|
+
raise ArgumentError.new("iv must be 16 bytes")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Neb
|
5
|
+
class PrivateKey
|
6
|
+
attr_reader :raw
|
7
|
+
|
8
|
+
def initialize(raw)
|
9
|
+
@raw = raw
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
encode(:hex)
|
14
|
+
end
|
15
|
+
|
16
|
+
def encode(fmt)
|
17
|
+
return self.class.new(value).encode(fmt) unless raw.is_a?(Numeric)
|
18
|
+
|
19
|
+
case fmt
|
20
|
+
when :decimal
|
21
|
+
raw
|
22
|
+
when :bin
|
23
|
+
BaseConvert.encode(raw, 256, 32)
|
24
|
+
when :bin_compressed
|
25
|
+
"#{BaseConvert.encode(raw, 256, 32)}\x01"
|
26
|
+
when :hex
|
27
|
+
BaseConvert.encode(raw, 16, 64)
|
28
|
+
when :hex_compressed
|
29
|
+
"#{BaseConvert.encode(raw, 16, 64)}01"
|
30
|
+
else
|
31
|
+
raise ArgumentError, "invalid format: #{fmt}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def decode(fmt = nil)
|
36
|
+
fmt ||= format
|
37
|
+
|
38
|
+
case fmt
|
39
|
+
when :decimal
|
40
|
+
raw
|
41
|
+
when :bin
|
42
|
+
BaseConvert.decode(raw, 256)
|
43
|
+
when :bin_compressed
|
44
|
+
BaseConvert.decode(raw[0, 32], 256)
|
45
|
+
when :hex
|
46
|
+
BaseConvert.decode(raw, 16)
|
47
|
+
when :hex_compressed
|
48
|
+
BaseConvert.decode(raw[0, 64], 16)
|
49
|
+
else
|
50
|
+
raise ArgumentError, "WIF does not represent privkey"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def value
|
55
|
+
@value ||= decode
|
56
|
+
end
|
57
|
+
|
58
|
+
def format
|
59
|
+
return :decimal if raw.is_a?(Numeric)
|
60
|
+
return :bin if raw.size == 32
|
61
|
+
return :bin_compressed if raw.size == 33
|
62
|
+
return :hex if raw.size == 64
|
63
|
+
return :hex_compressed if raw.size == 66
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_pubkey
|
67
|
+
to_pubkey_obj.to_s
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_pubkey_obj
|
71
|
+
raise ValidationError, "Invalid private key" if value >= Secp256k1::N
|
72
|
+
PublicKey.new(Secp256k1.priv_to_pub(encode(:bin)))
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_address_obj
|
76
|
+
PublicKey.new(to_pubkey_obj.encode(:bin)).to_address
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_address
|
80
|
+
to_address_obj.to_s
|
81
|
+
end
|
82
|
+
|
83
|
+
class << self
|
84
|
+
|
85
|
+
def random
|
86
|
+
new(Utils.random_bytes)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
// Copyright (C) 2017 go-nebulas authors
|
2
|
+
//
|
3
|
+
// This file is part of the go-nebulas library.
|
4
|
+
//
|
5
|
+
// the go-nebulas library is free software: you can redistribute it and/or modify
|
6
|
+
// it under the terms of the GNU General Public License as published by
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
8
|
+
// (at your option) any later version.
|
9
|
+
//
|
10
|
+
// the go-nebulas library is distributed in the hope that it will be useful,
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
// GNU General Public License for more details.
|
14
|
+
//
|
15
|
+
// You should have received a copy of the GNU General Public License
|
16
|
+
// along with the go-nebulas library. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
//
|
18
|
+
syntax = "proto3";
|
19
|
+
package corepb;
|
20
|
+
|
21
|
+
message Data {
|
22
|
+
string type = 1;
|
23
|
+
bytes payload = 2;
|
24
|
+
}
|
25
|
+
|
26
|
+
message Transaction {
|
27
|
+
bytes hash = 1;
|
28
|
+
bytes from = 2;
|
29
|
+
bytes to = 3;
|
30
|
+
bytes value = 4;
|
31
|
+
uint64 nonce = 5;
|
32
|
+
int64 timestamp = 6;
|
33
|
+
Data data = 7;
|
34
|
+
uint32 chain_id = 8;
|
35
|
+
bytes gas_price = 9;
|
36
|
+
bytes gas_limit = 10;
|
37
|
+
|
38
|
+
uint32 alg = 11;
|
39
|
+
bytes sign = 12;
|
40
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
2
|
+
# source: lib/neb/proto/transaction.proto
|
3
|
+
|
4
|
+
require 'google/protobuf'
|
5
|
+
|
6
|
+
Google::Protobuf::DescriptorPool.generated_pool.build do
|
7
|
+
add_message "corepb.Data" do
|
8
|
+
optional :type, :string, 1
|
9
|
+
optional :payload, :bytes, 2
|
10
|
+
end
|
11
|
+
add_message "corepb.Transaction" do
|
12
|
+
optional :hash, :bytes, 1
|
13
|
+
optional :from, :bytes, 2
|
14
|
+
optional :to, :bytes, 3
|
15
|
+
optional :value, :bytes, 4
|
16
|
+
optional :nonce, :uint64, 5
|
17
|
+
optional :timestamp, :int64, 6
|
18
|
+
optional :data, :message, 7, "corepb.Data"
|
19
|
+
optional :chain_id, :uint32, 8
|
20
|
+
optional :gas_price, :bytes, 9
|
21
|
+
optional :gas_limit, :bytes, 10
|
22
|
+
optional :alg, :uint32, 11
|
23
|
+
optional :sign, :bytes, 12
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Corepb
|
28
|
+
Data = Google::Protobuf::DescriptorPool.generated_pool.lookup("corepb.Data").msgclass
|
29
|
+
Transaction = Google::Protobuf::DescriptorPool.generated_pool.lookup("corepb.Transaction").msgclass
|
30
|
+
end
|