mnam 0.1.2
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/.DS_Store +0 -0
- data/.env.example +9 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +40 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/mnam/client/account.rb +33 -0
- data/lib/mnam/client/block.rb +49 -0
- data/lib/mnam/client/delegate.rb +81 -0
- data/lib/mnam/client/loader.rb +17 -0
- data/lib/mnam/client/multisignature.rb +27 -0
- data/lib/mnam/client/peer.rb +17 -0
- data/lib/mnam/client/signature.rb +20 -0
- data/lib/mnam/client/transaction.rb +31 -0
- data/lib/mnam/client.rb +38 -0
- data/lib/mnam/configurable.rb +44 -0
- data/lib/mnam/connection.rb +61 -0
- data/lib/mnam/transaction_builder.rb +260 -0
- data/lib/mnam/util/crypto.rb +25 -0
- data/lib/mnam/version.rb +13 -0
- data/lib/mnam.rb +12 -0
- data/mnam.gemspec +34 -0
- metadata +197 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 17f0e333bbd7aa2e5b18cff5d588b2834f152e0ca9827dec57d503c36d547881
|
4
|
+
data.tar.gz: 8cec3a4dd57f09d896fb878311cd2c9d6c054a19bf15a3f7690b24ff3ee4711d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 711aaf9d237c5f7e3f7c2a7e4d8161aa5c199a5fe9ad9def6ba835a997040774b6f4bb9f48fd0d019b96188f05a5c761ab868896b81072ad1689e33be4320384
|
7
|
+
data.tar.gz: dc99ea517ed5f160dd3b11182ba94ccd3f7b008f08ef71dd8201333b1959ecd37a39af5eaf03f501de0afd958af32478377ca892415f77ec02c8178b84091519
|
data/.DS_Store
ADDED
Binary file
|
data/.env.example
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) Mnam Ecosystem <info@mnam.io>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# MNAM Ruby
|
2
|
+
|
3
|
+
> An [MNAM](https://github.com/ArkEcosystem/mnam-node) bridge for Ruby.
|
4
|
+
|
5
|
+
# Installation
|
6
|
+
|
7
|
+
## Bundler
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'mnam'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install mnam
|
22
|
+
|
23
|
+
## Development
|
24
|
+
|
25
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, copy and edit the contents of the `.env.example` file into a file called `.env`. This file represents your network and account details. You can then run the tests using `rake spec`. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
26
|
+
|
27
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
28
|
+
|
29
|
+
## Security
|
30
|
+
|
31
|
+
If you discover a security vulnerability within this package, please send an e-mail to security@mnam.io. All security vulnerabilities will be promptly addressed.
|
32
|
+
## Credits
|
33
|
+
|
34
|
+
- [Wu Minzhe](https://github.com/wuminzhe)
|
35
|
+
- [Brian Faust](https://github.com/faustbrian)
|
36
|
+
- [All Contributors](../../contributors)
|
37
|
+
|
38
|
+
## License
|
39
|
+
|
40
|
+
[MIT](LICENSE) © [Mnam Ecosystem](https://mnam.io)
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "mnam"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Mnam
|
2
|
+
class Client
|
3
|
+
module Account
|
4
|
+
def account_balance(address)
|
5
|
+
get('api/accounts/getBalance', {address: address})
|
6
|
+
end
|
7
|
+
|
8
|
+
def account_publickey(address)
|
9
|
+
get('api/accounts/getPublickey', {address: address})
|
10
|
+
end
|
11
|
+
|
12
|
+
def account_delegates(address)
|
13
|
+
get('api/accounts/delegates', {address: address})
|
14
|
+
end
|
15
|
+
|
16
|
+
def account_delegates_fee(address)
|
17
|
+
get('api/accounts/delegates/fee', {address: address})
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_account_delegates(secret, public_key, second_secret)
|
21
|
+
put('api/accounts/delegates', {
|
22
|
+
:secret => secret,
|
23
|
+
:publicKey => public_key,
|
24
|
+
:secondSecret => second_secret
|
25
|
+
})
|
26
|
+
end
|
27
|
+
|
28
|
+
def account(address)
|
29
|
+
get('api/accounts', {address: address})
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Mnam
|
2
|
+
class Client
|
3
|
+
module Block
|
4
|
+
def block(id)
|
5
|
+
get('api/blocks/get', {:id => id})
|
6
|
+
end
|
7
|
+
|
8
|
+
def blocks(parameters = {})
|
9
|
+
get('api/blocks', parameters)
|
10
|
+
end
|
11
|
+
|
12
|
+
def block_epoch
|
13
|
+
get('api/blocks/getEpoch')
|
14
|
+
end
|
15
|
+
|
16
|
+
def block_height
|
17
|
+
get('api/blocks/getHeight')
|
18
|
+
end
|
19
|
+
|
20
|
+
def block_nethash
|
21
|
+
get('api/blocks/getNethash')
|
22
|
+
end
|
23
|
+
|
24
|
+
def block_fee
|
25
|
+
get('api/blocks/getFee')
|
26
|
+
end
|
27
|
+
|
28
|
+
def block_fees
|
29
|
+
get('api/blocks/getFees')
|
30
|
+
end
|
31
|
+
|
32
|
+
def block_milestone
|
33
|
+
get('api/blocks/getMilestone')
|
34
|
+
end
|
35
|
+
|
36
|
+
def block_reward
|
37
|
+
get('api/blocks/getReward')
|
38
|
+
end
|
39
|
+
|
40
|
+
def block_supply
|
41
|
+
get('api/blocks/getSupply')
|
42
|
+
end
|
43
|
+
|
44
|
+
def block_status
|
45
|
+
get('api/blocks/getStatus')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Mnam
|
2
|
+
class Client
|
3
|
+
module Delegate
|
4
|
+
def count_delegates
|
5
|
+
get('api/delegates/count')
|
6
|
+
end
|
7
|
+
|
8
|
+
def search_delegates(q, parameters = {})
|
9
|
+
get('api/delegates/search', {q: q}.merge(parameters))
|
10
|
+
end
|
11
|
+
|
12
|
+
def delegate_voters(public_key, parameters = {})
|
13
|
+
get('api/delegates/voters', {publicKey: public_key}.merge(parameters))
|
14
|
+
end
|
15
|
+
|
16
|
+
def delegate(parameters = {})
|
17
|
+
get('api/delegates/get', parameters)
|
18
|
+
end
|
19
|
+
|
20
|
+
def delegates(parameters = {})
|
21
|
+
get('api/delegates', parameters)
|
22
|
+
end
|
23
|
+
|
24
|
+
def delegate_fee
|
25
|
+
get('api/delegates/fee')
|
26
|
+
end
|
27
|
+
|
28
|
+
def forged_by_account(generator_public_key)
|
29
|
+
get('api/delegates/forging/getForgedByAccount', {generatorPublicKey: generator_public_key})
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_delegate(username, secret, second_secret = nil)
|
33
|
+
params = {
|
34
|
+
:transactions => [
|
35
|
+
Mnam::TransactionBuilder.new.create_delegate(username, secret, second_secret).to_params
|
36
|
+
]
|
37
|
+
}
|
38
|
+
|
39
|
+
post('peer/transactions', params)
|
40
|
+
end
|
41
|
+
|
42
|
+
def vote_for_delegate(delegates, secret, second_secret = nil)
|
43
|
+
delegates = Array(delegates).map { |d| d[0] == '+' ? d : "+#{d}" }
|
44
|
+
params = {
|
45
|
+
:transactions => [
|
46
|
+
Mnam::TransactionBuilder.new.create_vote(delegates, secret, second_secret, network_address).to_params
|
47
|
+
]
|
48
|
+
}
|
49
|
+
|
50
|
+
post('peer/transactions', params)
|
51
|
+
end
|
52
|
+
|
53
|
+
def remove_vote_for_delegate(delegates, secret, second_secret = nil)
|
54
|
+
delegates = Array(delegates).map { |d| d[0] == '-' ? d : "-#{d}" }
|
55
|
+
params = {
|
56
|
+
:transactions => [
|
57
|
+
Mnam::TransactionBuilder.new.create_vote(delegates, secret, second_secret, network_address).to_params
|
58
|
+
]
|
59
|
+
}
|
60
|
+
|
61
|
+
post('peer/transactions', params)
|
62
|
+
end
|
63
|
+
|
64
|
+
def next_forgers
|
65
|
+
get('api/delegates/getNextForgers')
|
66
|
+
end
|
67
|
+
|
68
|
+
def enable_forging(secret, parameters = {})
|
69
|
+
post('api/delegates/forging/enable', {:secret => secret}.merge(parameters))
|
70
|
+
end
|
71
|
+
|
72
|
+
def disable_forging(secret, parameters = {})
|
73
|
+
post('api/delegates/forging/disable', {:secret => secret}.merge(parameters))
|
74
|
+
end
|
75
|
+
|
76
|
+
def forging_status(publicKey, parameters = {})
|
77
|
+
post('api/delegates/forging/disable', {:publicKey => publicKey}.merge(parameters))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Mnam
|
2
|
+
class Client
|
3
|
+
module MultiSignature
|
4
|
+
def pending_multi_signatures(public_key)
|
5
|
+
get('api/multisignatures/pending', {:publicKey => public_key})
|
6
|
+
end
|
7
|
+
|
8
|
+
def multi_signature_sign(transaction_id, secret, parameters = {})
|
9
|
+
post('api/multisignatures/sign', {:transactionId => transaction_id, :secret => secret}.merge(parameters))
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_multi_signature(secret, second_secret, keysgroup, lifetime, min)
|
13
|
+
params = {
|
14
|
+
:transactions => [
|
15
|
+
Mnam::TransactionBuilder.new.create_multisignature(secret, second_secret, keysgroup, lifetime, min).to_params
|
16
|
+
]
|
17
|
+
}
|
18
|
+
|
19
|
+
post('peer/transactions', params)
|
20
|
+
end
|
21
|
+
|
22
|
+
def multi_signature_accounts(public_key)
|
23
|
+
get('api/multisignatures/accounts', {:publicKey => public_key})
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Mnam
|
2
|
+
class Client
|
3
|
+
module Peer
|
4
|
+
def peer(ip, port)
|
5
|
+
get('api/peers/get', {:ip => ip, :port => port})
|
6
|
+
end
|
7
|
+
|
8
|
+
def peers(parameters = {})
|
9
|
+
get('api/peers', parameters)
|
10
|
+
end
|
11
|
+
|
12
|
+
def version
|
13
|
+
get('api/peers/version')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Mnam
|
2
|
+
class Client
|
3
|
+
module Signature
|
4
|
+
def signature_fee
|
5
|
+
get('api/signatures/fee')
|
6
|
+
end
|
7
|
+
|
8
|
+
#rename method
|
9
|
+
def create_signature(secret, second_secret)
|
10
|
+
params = {
|
11
|
+
:transactions => [
|
12
|
+
Mnam::TransactionBuilder.new.create_second_signature(second_secret, secret).to_params
|
13
|
+
]
|
14
|
+
}
|
15
|
+
|
16
|
+
post('peer/transactions', params)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Mnam
|
2
|
+
class Client
|
3
|
+
module Transaction
|
4
|
+
def transaction(id)
|
5
|
+
get('api/transactions/get', {:id => id})
|
6
|
+
end
|
7
|
+
|
8
|
+
def transactions(parameters = {})
|
9
|
+
get('api/transactions', parameters)
|
10
|
+
end
|
11
|
+
|
12
|
+
def unconfirmed_transaction(id)
|
13
|
+
get('api/transactions/unconfirmed/get', {:id => id})
|
14
|
+
end
|
15
|
+
|
16
|
+
def unconfirmed_transactions(parameters = {})
|
17
|
+
get('api/transactions/unconfirmed', parameters)
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_transaction(recipient_id, amount, vendor_field, secret, second_secret=nil)
|
21
|
+
params = {
|
22
|
+
:transactions => [
|
23
|
+
Mnam::TransactionBuilder.new.create_transfer(recipient_id, amount, vendor_field, secret, second_secret).to_params
|
24
|
+
]
|
25
|
+
}
|
26
|
+
|
27
|
+
post('peer/transactions', params)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/mnam/client.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'mnam/connection'
|
2
|
+
require 'mnam/configurable'
|
3
|
+
require 'mnam/transaction_builder'
|
4
|
+
|
5
|
+
require 'mnam/client/account'
|
6
|
+
require 'mnam/client/block'
|
7
|
+
require 'mnam/client/delegate'
|
8
|
+
require 'mnam/client/loader'
|
9
|
+
require 'mnam/client/multisignature'
|
10
|
+
require 'mnam/client/peer'
|
11
|
+
require 'mnam/client/signature'
|
12
|
+
require 'mnam/client/transaction'
|
13
|
+
|
14
|
+
module Mnam
|
15
|
+
MAIN_NETWORK_ADDRESS = '17'
|
16
|
+
DEV_NETWORK_ADDRESS = '1e'
|
17
|
+
|
18
|
+
class Client
|
19
|
+
include Mnam::Configurable
|
20
|
+
include Mnam::Connection
|
21
|
+
include Mnam::Client::Account
|
22
|
+
include Mnam::Client::Block
|
23
|
+
include Mnam::Client::Delegate
|
24
|
+
include Mnam::Client::Loader
|
25
|
+
include Mnam::Client::MultiSignature
|
26
|
+
include Mnam::Client::Peer
|
27
|
+
include Mnam::Client::Signature
|
28
|
+
include Mnam::Client::Transaction
|
29
|
+
|
30
|
+
def initialize(options = {})
|
31
|
+
Mnam::Configurable.keys.each do |key|
|
32
|
+
instance_variable_set(:"@#{key}", options[key] || Mnam.instance_variable_get(:"@#{key}"))
|
33
|
+
end
|
34
|
+
|
35
|
+
@network_address ||= Mnam::MAIN_NETWORK_ADDRESS
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Mnam
|
2
|
+
module Configurable
|
3
|
+
attr_accessor :host, :nethash, :version, :network_address
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def keys
|
7
|
+
@keys ||= [
|
8
|
+
:host,
|
9
|
+
:nethash,
|
10
|
+
:version,
|
11
|
+
:network_address
|
12
|
+
]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Set configuration options using a block
|
17
|
+
def configure
|
18
|
+
yield self
|
19
|
+
end
|
20
|
+
|
21
|
+
# Reset configuration options to default values
|
22
|
+
def reset!
|
23
|
+
Mnam::Configurable.keys.each do |key|
|
24
|
+
instance_variable_set(:"@#{key}", Mnam::Default.options[key])
|
25
|
+
end
|
26
|
+
self
|
27
|
+
end
|
28
|
+
alias setup reset!
|
29
|
+
|
30
|
+
# Compares client options to a Hash of requested options
|
31
|
+
#
|
32
|
+
# @param opts [Hash] Options to compare with current client options
|
33
|
+
# @return [Boolean]
|
34
|
+
def same_options?(opts)
|
35
|
+
opts.hash == options.hash
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def options
|
41
|
+
Hash[Mnam::Configurable.keys.map{|key| [key, instance_variable_get(:"@#{key}")]}]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
|
4
|
+
module Mnam
|
5
|
+
module Connection
|
6
|
+
def get(url, options = {})
|
7
|
+
request :get, url, options
|
8
|
+
end
|
9
|
+
|
10
|
+
def post(url, options = {})
|
11
|
+
request :post, url, options
|
12
|
+
end
|
13
|
+
|
14
|
+
def put(url, options = {})
|
15
|
+
request :put, url, options
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete(url, options = {})
|
19
|
+
request :delete, url, options
|
20
|
+
end
|
21
|
+
|
22
|
+
def root
|
23
|
+
"#{@host}/"
|
24
|
+
end
|
25
|
+
|
26
|
+
def last_response
|
27
|
+
@last_response if defined? @last_response
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def request(method, path, data)
|
33
|
+
request = http.send(method, path, data)
|
34
|
+
|
35
|
+
@last_response = response = JSON.parse request.body
|
36
|
+
|
37
|
+
if response['success'] == false
|
38
|
+
raise response['error']
|
39
|
+
end
|
40
|
+
|
41
|
+
if response['status'] == false
|
42
|
+
raise response['error']
|
43
|
+
end
|
44
|
+
|
45
|
+
response
|
46
|
+
end
|
47
|
+
|
48
|
+
def http
|
49
|
+
connection = Faraday.new root do |conn|
|
50
|
+
conn.headers['Content-Type'] = 'application/json'
|
51
|
+
conn.headers[:nethash] = @nethash
|
52
|
+
conn.headers[:version] = @version
|
53
|
+
conn.headers[:port] = "1"
|
54
|
+
|
55
|
+
conn.request :json
|
56
|
+
|
57
|
+
conn.adapter Faraday.default_adapter
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,260 @@
|
|
1
|
+
require_relative 'util/crypto'
|
2
|
+
require 'btcruby/base58'
|
3
|
+
require 'deep_hash_transform'
|
4
|
+
|
5
|
+
module Mnam
|
6
|
+
class TransactionBuilder
|
7
|
+
|
8
|
+
class Transaction
|
9
|
+
class Type
|
10
|
+
TRANSFER = 0
|
11
|
+
SECOND_SIGNATURE = 1
|
12
|
+
DELEGATE = 2
|
13
|
+
VOTE = 3
|
14
|
+
MULTISIGNATURE = 4
|
15
|
+
end
|
16
|
+
|
17
|
+
class Fee
|
18
|
+
TRANSFER = 10000000;
|
19
|
+
SECOND_SIGNATURE = 500000000;
|
20
|
+
DELEGATE = 2500000000;
|
21
|
+
VOTE = 100000000;
|
22
|
+
MULTISIGNATURE_BASE = 500000000;
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader *%i(
|
26
|
+
amount
|
27
|
+
asset
|
28
|
+
fee
|
29
|
+
id
|
30
|
+
recipient_id
|
31
|
+
sender_public_key
|
32
|
+
sign_signature
|
33
|
+
signature
|
34
|
+
timestamp
|
35
|
+
type
|
36
|
+
vendor_field
|
37
|
+
)
|
38
|
+
|
39
|
+
def initialize(
|
40
|
+
type:,
|
41
|
+
fee:,
|
42
|
+
sender_public_key: nil,
|
43
|
+
recipient_id: nil,
|
44
|
+
amount: nil,
|
45
|
+
vendor_field: nil,
|
46
|
+
asset: {}
|
47
|
+
)
|
48
|
+
@type = type
|
49
|
+
@fee = fee
|
50
|
+
@sender_public_key = sender_public_key
|
51
|
+
@recipient_id = recipient_id
|
52
|
+
@amount = amount
|
53
|
+
@vendor_field = vendor_field
|
54
|
+
@timestamp = seconds_after_epoch
|
55
|
+
@asset = asset
|
56
|
+
end
|
57
|
+
|
58
|
+
def sign_and_create_id(key, second_key = nil)
|
59
|
+
transaction_bytes = to_bytes
|
60
|
+
@signature = key.ecdsa_signature(Digest::SHA256.digest(transaction_bytes)).unpack('H*').first
|
61
|
+
|
62
|
+
hashed_transaction_bytes_with_sig = Digest::SHA256.digest(to_bytes(false))
|
63
|
+
|
64
|
+
@sign_signature = second_key.ecdsa_signature(hashed_transaction_bytes_with_sig).unpack('H*').first if second_key
|
65
|
+
@id = Digest::SHA256.digest(to_bytes(false, false)).unpack('H*').first
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_bytes(skip_signature = true, skip_second_signature = true)
|
69
|
+
out = ''
|
70
|
+
out << [type].pack('c')
|
71
|
+
out << [timestamp].pack("V")
|
72
|
+
out << [sender_public_key].pack('H*')
|
73
|
+
if recipient_id
|
74
|
+
out << BTC::Base58.data_from_base58check(recipient_id)
|
75
|
+
else
|
76
|
+
out << [].pack('x21')
|
77
|
+
end
|
78
|
+
|
79
|
+
if vendor_field
|
80
|
+
out << vendor_field
|
81
|
+
if vendor_field.size < 64
|
82
|
+
out << [].pack("x#{64 - vendor_field.size}")
|
83
|
+
end
|
84
|
+
else
|
85
|
+
out << [].pack("x64")
|
86
|
+
end
|
87
|
+
|
88
|
+
out << [amount].pack('Q<')
|
89
|
+
out << [fee].pack('Q<')
|
90
|
+
|
91
|
+
case type
|
92
|
+
when Type::SECOND_SIGNATURE
|
93
|
+
asset_signature_public_key = asset[:signature][:public_key]
|
94
|
+
out << [asset_signature_public_key].pack('H*')
|
95
|
+
when Type::DELEGATE
|
96
|
+
out << asset[:delegate][:username]
|
97
|
+
when Type::VOTE
|
98
|
+
out << asset[:votes].join('')
|
99
|
+
when Type::MULTISIGNATURE
|
100
|
+
ms_asset = asset[:multisignature]
|
101
|
+
out << [ms_asset[:min]].pack('C')
|
102
|
+
out << [ms_asset[:lifetime]].pack('C')
|
103
|
+
out << ms_asset[:keysgroup].join('')
|
104
|
+
end
|
105
|
+
|
106
|
+
if !skip_signature && signature
|
107
|
+
out << [signature].pack('H*')
|
108
|
+
end
|
109
|
+
|
110
|
+
if !skip_second_signature && sign_signature
|
111
|
+
out << [sign_signature].pack('H*')
|
112
|
+
end
|
113
|
+
|
114
|
+
out
|
115
|
+
end
|
116
|
+
|
117
|
+
def to_params
|
118
|
+
{
|
119
|
+
:type => type,
|
120
|
+
:amount => amount,
|
121
|
+
:fee => fee,
|
122
|
+
:vendorField => vendor_field,
|
123
|
+
:timestamp => timestamp,
|
124
|
+
:recipientId => recipient_id,
|
125
|
+
:senderPublicKey => sender_public_key,
|
126
|
+
:signature => signature,
|
127
|
+
:id => id
|
128
|
+
}.tap do |h|
|
129
|
+
h[:asset] = asset.deep_transform_keys {|key| snake_case_to_camel_case(key)} if asset.any?
|
130
|
+
h[:signSignature] = sign_signature if sign_signature
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
def seconds_after_epoch
|
137
|
+
(Time.now.utc - Time.utc(2017, 3, 21, 13, 00, 00)).to_i
|
138
|
+
end
|
139
|
+
|
140
|
+
def snake_case_to_camel_case(string)
|
141
|
+
string.to_s.split('_').enum_for(:each_with_index).collect do |s, index|
|
142
|
+
index == 0 ? s : s.capitalize
|
143
|
+
end.join
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
class TransactionType
|
148
|
+
TRANSFER = 0
|
149
|
+
end
|
150
|
+
|
151
|
+
class TransactionFee
|
152
|
+
TRANSFER = 10000000
|
153
|
+
SECONDSIGNATURE = 500000000
|
154
|
+
DELEGATE = 2500000000
|
155
|
+
VOTE = 100000000
|
156
|
+
MULTISIGNATURE_BASE = 500000000
|
157
|
+
end
|
158
|
+
|
159
|
+
def create_transfer(recipient_id, amount, vendor_field, secret, second_secret = nil)
|
160
|
+
key = Mnam::Util::Crypto.get_key(secret)
|
161
|
+
second_key = Mnam::Util::Crypto.get_key(second_secret) if second_secret
|
162
|
+
|
163
|
+
transaction = Transaction.new(
|
164
|
+
:type => Transaction::Type::TRANSFER,
|
165
|
+
:fee => Transaction::Fee::TRANSFER,
|
166
|
+
:sender_public_key => key.public_key.unpack('H*').first,
|
167
|
+
:recipient_id => recipient_id,
|
168
|
+
:amount => amount,
|
169
|
+
:vendor_field => vendor_field,
|
170
|
+
)
|
171
|
+
transaction.sign_and_create_id(key, second_key)
|
172
|
+
|
173
|
+
transaction
|
174
|
+
end
|
175
|
+
|
176
|
+
def create_second_signature(second_secret, first_secret)
|
177
|
+
key = Mnam::Util::Crypto.get_key(first_secret)
|
178
|
+
second_key = Mnam::Util::Crypto.get_key(second_secret) if second_secret
|
179
|
+
|
180
|
+
transaction = Transaction.new(
|
181
|
+
:type => Transaction::Type::SECOND_SIGNATURE,
|
182
|
+
:fee => Transaction::Fee::SECOND_SIGNATURE,
|
183
|
+
:sender_public_key => key.public_key.unpack('H*').first,
|
184
|
+
:amount => 0,
|
185
|
+
:asset => {
|
186
|
+
:signature => {
|
187
|
+
:public_key => second_key.public_key.unpack('H*').first
|
188
|
+
}
|
189
|
+
}
|
190
|
+
)
|
191
|
+
|
192
|
+
transaction.sign_and_create_id(key)
|
193
|
+
|
194
|
+
return transaction
|
195
|
+
end
|
196
|
+
|
197
|
+
def create_vote(votes, secret, second_secret, network_address)
|
198
|
+
key = Mnam::Util::Crypto.get_key(secret)
|
199
|
+
second_key = Mnam::Util::Crypto.get_key(second_secret) if second_secret
|
200
|
+
|
201
|
+
transaction = Transaction.new(
|
202
|
+
:type => Transaction::Type::VOTE,
|
203
|
+
:fee => Transaction::Fee::VOTE,
|
204
|
+
:sender_public_key => key.public_key.unpack('H*').first,
|
205
|
+
:recipient_id => Mnam::Util::Crypto.get_address(key, network_address),
|
206
|
+
:amount => 0,
|
207
|
+
:asset => {:votes => votes}
|
208
|
+
)
|
209
|
+
|
210
|
+
transaction.sign_and_create_id(key, second_key)
|
211
|
+
|
212
|
+
transaction
|
213
|
+
end
|
214
|
+
|
215
|
+
def create_delegate(username, secret, second_secret)
|
216
|
+
key = Mnam::Util::Crypto.get_key(secret)
|
217
|
+
second_key = Mnam::Util::Crypto.get_key(second_secret) if second_secret
|
218
|
+
|
219
|
+
transaction = Transaction.new(
|
220
|
+
:type => Transaction::Type::DELEGATE,
|
221
|
+
:fee => Transaction::Fee::DELEGATE,
|
222
|
+
:sender_public_key => key.public_key.unpack('H*').first,
|
223
|
+
:amount => 0,
|
224
|
+
:asset => {
|
225
|
+
:delegate => {
|
226
|
+
:username => username,
|
227
|
+
:public_key => key.public_key.unpack('H*').first
|
228
|
+
}
|
229
|
+
}
|
230
|
+
)
|
231
|
+
|
232
|
+
transaction.sign_and_create_id(key, second_key)
|
233
|
+
|
234
|
+
transaction
|
235
|
+
end
|
236
|
+
|
237
|
+
def create_multisignature(secret, second_secret, keysgroup, lifetime, min)
|
238
|
+
key = Mnam::Util::Crypto.get_key(secret)
|
239
|
+
second_key = Mnam::Util::Crypto.get_key(second_secret) if second_secret
|
240
|
+
|
241
|
+
transaction = Transaction.new(
|
242
|
+
:type => Transaction::Type::MULTISIGNATURE,
|
243
|
+
:fee => (keysgroup.size + 1) * Transaction::Fee::MULTISIGNATURE_BASE,
|
244
|
+
:sender_public_key => key.public_key.unpack('H*').first,
|
245
|
+
:amount => 0,
|
246
|
+
:asset => {
|
247
|
+
:multisignature => {
|
248
|
+
:min => min,
|
249
|
+
:lifetime => lifetime,
|
250
|
+
:keysgroup => keysgroup
|
251
|
+
}
|
252
|
+
}
|
253
|
+
)
|
254
|
+
|
255
|
+
transaction.sign_and_create_id(key, second_key)
|
256
|
+
|
257
|
+
transaction
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'btcruby'
|
2
|
+
|
3
|
+
module Mnam
|
4
|
+
module Util
|
5
|
+
module Crypto
|
6
|
+
def self.get_key(secret)
|
7
|
+
BTC::Key.new(:private_key => Digest::SHA256.digest(secret), :public_key_compressed => true)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.verify(transaction)
|
11
|
+
public_only_key = BTC::Key.new(:public_key => [transaction.sender_public_key].pack('H*'))
|
12
|
+
public_only_key.verify_ecdsa_signature([transaction.signature].pack('H*'), Digest::SHA256.digest(transaction.to_bytes))
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.second_verify(transaction, second_public_key_hex)
|
16
|
+
public_only_key = BTC::Key.new(:public_key => [second_public_key_hex].pack('H*'))
|
17
|
+
public_only_key.verify_ecdsa_signature([transaction.sign_signature].pack('H*'), Digest::SHA256.digest(transaction.to_bytes(false)))
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.get_address(key, network_address = '17')
|
21
|
+
BTC::Base58.base58check_from_data([network_address.to_i(16)].pack('c') + Digest::RMD160.digest(key.public_key))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/mnam/version.rb
ADDED
data/lib/mnam.rb
ADDED
data/mnam.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "mnam/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "mnam"
|
8
|
+
spec.version = Mnam::VERSION
|
9
|
+
spec.authors = ["Brian Faust"]
|
10
|
+
spec.email = ["hello@brianfaust.me"]
|
11
|
+
|
12
|
+
spec.summary = "An MNAM bridge for Ruby."
|
13
|
+
spec.description = "An MNAM bridge for Ruby."
|
14
|
+
spec.homepage = "https://github.com/ArkEcosystem/Mnam-Ruby"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.15"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
27
|
+
spec.add_development_dependency "webmock", "3.0.1"
|
28
|
+
spec.add_development_dependency "dotenv", "2.2.1"
|
29
|
+
|
30
|
+
spec.add_dependency 'btcruby'
|
31
|
+
spec.add_dependency 'deep_hash_transform'
|
32
|
+
spec.add_dependency 'faraday', "~> 0.12.2"
|
33
|
+
spec.add_dependency 'faraday_middleware', "~> 0.12.2"
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mnam
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brian Faust
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-06-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.15'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.15'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: webmock
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.0.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 3.0.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: dotenv
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 2.2.1
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 2.2.1
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: btcruby
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: deep_hash_transform
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: faraday
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.12.2
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 0.12.2
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: faraday_middleware
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 0.12.2
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 0.12.2
|
139
|
+
description: An MNAM bridge for Ruby.
|
140
|
+
email:
|
141
|
+
- hello@brianfaust.me
|
142
|
+
executables: []
|
143
|
+
extensions: []
|
144
|
+
extra_rdoc_files: []
|
145
|
+
files:
|
146
|
+
- ".DS_Store"
|
147
|
+
- ".env.example"
|
148
|
+
- ".gitignore"
|
149
|
+
- ".rspec"
|
150
|
+
- ".travis.yml"
|
151
|
+
- Gemfile
|
152
|
+
- LICENSE
|
153
|
+
- README.md
|
154
|
+
- Rakefile
|
155
|
+
- bin/console
|
156
|
+
- bin/setup
|
157
|
+
- lib/mnam.rb
|
158
|
+
- lib/mnam/client.rb
|
159
|
+
- lib/mnam/client/account.rb
|
160
|
+
- lib/mnam/client/block.rb
|
161
|
+
- lib/mnam/client/delegate.rb
|
162
|
+
- lib/mnam/client/loader.rb
|
163
|
+
- lib/mnam/client/multisignature.rb
|
164
|
+
- lib/mnam/client/peer.rb
|
165
|
+
- lib/mnam/client/signature.rb
|
166
|
+
- lib/mnam/client/transaction.rb
|
167
|
+
- lib/mnam/configurable.rb
|
168
|
+
- lib/mnam/connection.rb
|
169
|
+
- lib/mnam/transaction_builder.rb
|
170
|
+
- lib/mnam/util/crypto.rb
|
171
|
+
- lib/mnam/version.rb
|
172
|
+
- mnam.gemspec
|
173
|
+
homepage: https://github.com/ArkEcosystem/Mnam-Ruby
|
174
|
+
licenses:
|
175
|
+
- MIT
|
176
|
+
metadata: {}
|
177
|
+
post_install_message:
|
178
|
+
rdoc_options: []
|
179
|
+
require_paths:
|
180
|
+
- lib
|
181
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
182
|
+
requirements:
|
183
|
+
- - ">="
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: '0'
|
186
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
187
|
+
requirements:
|
188
|
+
- - ">="
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: '0'
|
191
|
+
requirements: []
|
192
|
+
rubyforge_project:
|
193
|
+
rubygems_version: 2.7.6
|
194
|
+
signing_key:
|
195
|
+
specification_version: 4
|
196
|
+
summary: An MNAM bridge for Ruby.
|
197
|
+
test_files: []
|