neb 0.1.0
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 +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
|