nis-ruby 0.0.13 → 0.0.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +17 -12
- data/Rakefile +3 -0
- data/examples/transactions/transfer_mosaic.rb +48 -0
- data/lib/nis/fee/importance_transfer.rb +1 -1
- data/lib/nis/fee/mosaic_definition_creation.rb +1 -1
- data/lib/nis/fee/mosaic_supply_change_transfer.rb +1 -1
- data/lib/nis/fee/multisig.rb +1 -1
- data/lib/nis/fee/multisig_aggregation_modification.rb +2 -6
- data/lib/nis/fee/provision_namespace.rb +2 -2
- data/lib/nis/fee/transfer.rb +34 -17
- data/lib/nis/keypair.rb +4 -0
- data/lib/nis/request/announce.rb +7 -2
- data/lib/nis/request/prepare_announce.rb +7 -4
- data/lib/nis/struct/mosaic_attachment.rb +21 -0
- data/lib/nis/struct/mosaic_definition.rb +5 -1
- data/lib/nis/struct/mosaic_properties.rb +11 -1
- data/lib/nis/transaction/mosaic_definition_creation.rb +1 -1
- data/lib/nis/transaction/provision_namespace.rb +2 -2
- data/lib/nis/transaction/transfer.rb +10 -5
- data/lib/nis/unit/address.rb +1 -1
- data/lib/nis/util.rb +4 -0
- data/lib/nis/util/deserializer.rb +46 -0
- data/lib/nis/util/ed25519.rb +77 -83
- data/lib/nis/util/serializer.rb +3 -3
- data/lib/nis/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 681592f0869e7c2ff1956e2b04ad32ffcd07f002
|
4
|
+
data.tar.gz: d3f7c8660d39d09d9186c3df607e5b00682ca5d1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f7c4b08efb1324125b792de4df01fa6dc7aaacc355358a65e10ce337dbad2dce308a830103aa23bebb954488b673cd00ab086036c41cef5dfbdc9522bd6aa5ee
|
7
|
+
data.tar.gz: 05e7ed7f141eb73a948b3690376dc5d5b4a43ebdd343d6a68942470d46f6d5dd089b396026567f6fcf579d996332e6c9ec28bc5d62593fb4893c68a247f66bdd
|
data/README.md
CHANGED
@@ -5,9 +5,9 @@
|
|
5
5
|
[![Code Climate](https://codeclimate.com/github/44uk/nis-ruby/badges/gpa.svg)](https://codeclimate.com/github/44uk/nis-ruby)
|
6
6
|
[![Join the chat at https://gitter.im/44uk/nis-ruby](https://badges.gitter.im/44uk/nis-ruby.svg)](https://gitter.im/44uk/nis-ruby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
7
7
|
|
8
|
-
<img src="https://cloud.githubusercontent.com/assets/370508/24320282/a332d238-1175-11e7-96dc-75bc30e562d2.png" width="
|
8
|
+
<img src="https://cloud.githubusercontent.com/assets/370508/24320282/a332d238-1175-11e7-96dc-75bc30e562d2.png" width="280" height="280" alt="nem" align="right" />
|
9
9
|
|
10
|
-
Ruby client library for the NEM Infrastructure Server API
|
10
|
+
Ruby client library for the NEM Infrastructure Server(NIS) API.
|
11
11
|
|
12
12
|
- [NEM \- Distributed Ledger Technology \(Blockchain\)](https://www.nem.io/)
|
13
13
|
- [NEM NIS API Documentation](https://nemproject.github.io/)
|
@@ -15,6 +15,9 @@ Ruby client library for the NEM Infrastructure Server API
|
|
15
15
|
|
16
16
|
*The gem is under development. Incompatible changes can be made.*
|
17
17
|
|
18
|
+
*Do a thorough test on testnet before you use this gem on production.*
|
19
|
+
|
20
|
+
- [NEM Testnet Faucet \- You can get Testnet XEM for development / testing.](http://test-nem-faucet.44uk.net/)
|
18
21
|
|
19
22
|
## Installation
|
20
23
|
|
@@ -30,6 +33,8 @@ gem 'nis-ruby'
|
|
30
33
|
|
31
34
|
## Usage
|
32
35
|
|
36
|
+
### Examples
|
37
|
+
|
33
38
|
More specific example codes are in **examples/** directory.
|
34
39
|
|
35
40
|
### Methods
|
@@ -79,7 +84,6 @@ nis.request(:post, '/account/unlock',
|
|
79
84
|
# See https://nemproject.github.io/#locking-and-unlocking-accounts
|
80
85
|
```
|
81
86
|
|
82
|
-
|
83
87
|
## Commandline
|
84
88
|
|
85
89
|
```bash
|
@@ -96,11 +100,9 @@ $ nis request get account/harvests --params=address:TALICELCD3XPH4FFI5STGGNSNSWP
|
|
96
100
|
# => [Array <HervestInfo structure>]
|
97
101
|
```
|
98
102
|
|
99
|
-
|
100
103
|
## Connection
|
101
104
|
|
102
|
-
You can find nodes here.
|
103
|
-
- [NEM Node Rewards](https://supernodes.nem.io/)
|
105
|
+
You can find nodes here. [NEM Node Rewards](https://supernodes.nem.io/)
|
104
106
|
|
105
107
|
### Hash
|
106
108
|
|
@@ -121,26 +123,29 @@ $ nis heartbeat # => {"code":1,"type":2,"message":"ok"}
|
|
121
123
|
|
122
124
|
Environment variable used as default value.
|
123
125
|
|
126
|
+
## For More Information
|
127
|
+
|
128
|
+
* [Documentation for nis-ruby - rubydoc.info](http://www.rubydoc.info/gems/nis-ruby)
|
124
129
|
|
125
130
|
## TODO
|
126
131
|
|
127
132
|
* Do more improvements
|
128
|
-
* Mosaic transferring
|
129
133
|
* Encryption message
|
134
|
+
* Failover connection
|
130
135
|
* Be more easy to use
|
131
136
|
|
137
|
+
## Contact
|
132
138
|
|
133
|
-
|
134
|
-
|
135
|
-
Available at [rubydoc.info](http://www.rubydoc.info/gems/nis-ruby).
|
139
|
+
Feel free to ask me if you have any questions.
|
136
140
|
|
141
|
+
* [@44uk_i3 - Twitter](https://twitter.com/44uk_i3)
|
142
|
+
* [44uk/nis-ruby - gitter](https://gitter.im/44uk/nis-ruby)
|
137
143
|
|
138
144
|
## Contributing
|
139
145
|
|
140
146
|
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/nis-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
141
147
|
|
142
|
-
|
143
148
|
## License
|
144
149
|
|
145
|
-
The gem is available as open source under the terms of the [MIT License](
|
150
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE).
|
146
151
|
|
data/Rakefile
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'nis'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
# sender
|
5
|
+
A_PRIVATE_KEY = '260206d683962350532408e8774fd14870a173b7fba17f6b504da3dbc5f1cc9f'
|
6
|
+
|
7
|
+
# receiver
|
8
|
+
B_ADDRESS = 'TAWKJTUP4DWKLDKKS534TYP6G324CBNMXKBA4X7B'
|
9
|
+
|
10
|
+
kp = Nis::Keypair.new(A_PRIVATE_KEY)
|
11
|
+
|
12
|
+
# fetch mosaic information
|
13
|
+
nis = Nis.new(host: '104.128.226.60')
|
14
|
+
mo_dmdps = nis.namespace_mosaic_definition_page(namespace: 'sushi')
|
15
|
+
mo_def = mo_dmdps.first.mosaic
|
16
|
+
|
17
|
+
# require 'pry'; binding.pry
|
18
|
+
|
19
|
+
# mosaic_id = Nis::Struct::MosaicId.new(
|
20
|
+
# namespaceId: 'sushi',
|
21
|
+
# name: 'anago'
|
22
|
+
# )
|
23
|
+
|
24
|
+
# properties = Nis::Struct::MosaicProperties.new(
|
25
|
+
# divisibility: 0,
|
26
|
+
# initialSupply: 10_000,
|
27
|
+
# supplyMutable: true,
|
28
|
+
# transferable: true
|
29
|
+
# )
|
30
|
+
|
31
|
+
# mo_def = Nis::Struct::MosaicDefinition.new(
|
32
|
+
# id: mosaic_id,
|
33
|
+
# properties: properties,
|
34
|
+
# )
|
35
|
+
|
36
|
+
tx = Nis::Transaction::Transfer.new(B_ADDRESS, 1_000_000, 'Good luck!')
|
37
|
+
tx.mosaics << Nis::Struct::MosaicAttachment.new(mo_def, 1_000_000)
|
38
|
+
|
39
|
+
# pp tx.to_hash
|
40
|
+
|
41
|
+
nis = Nis.new
|
42
|
+
req = Nis::Request::PrepareAnnounce.new(tx, kp)
|
43
|
+
pp req.to_hash
|
44
|
+
# res = nis.transaction_prepare_announce(req)
|
45
|
+
|
46
|
+
#
|
47
|
+
# puts "Message: #{res.message}"
|
48
|
+
# puts "TransactionHash: #{res.transaction_hash}"
|
data/lib/nis/fee/multisig.rb
CHANGED
@@ -7,13 +7,9 @@ class Nis::Fee
|
|
7
7
|
# @return [Integer] fee in micro XEM
|
8
8
|
def value
|
9
9
|
if @transaction.minCosignatories == 0
|
10
|
-
|
11
|
-
0.5 * 1_000_000 :
|
12
|
-
(10 + 6 * @transaction.modifications.length) * 1_000_000
|
10
|
+
0.5 * 1_000_000
|
13
11
|
else
|
14
|
-
|
15
|
-
0.5 * 1_000_000 :
|
16
|
-
(10 + 6 * @transaction.modifications.length + 6) * 1_000_000
|
12
|
+
0.5 * 1_000_000
|
17
13
|
end
|
18
14
|
end
|
19
15
|
|
data/lib/nis/fee/transfer.rb
CHANGED
@@ -1,23 +1,19 @@
|
|
1
1
|
class Nis::Fee
|
2
2
|
class Transfer
|
3
|
+
FEE_FACTOR = 0.05
|
4
|
+
|
3
5
|
def initialize(transaction)
|
4
6
|
@transaction = transaction
|
5
7
|
end
|
6
8
|
|
7
9
|
# @return [Integer] fee in micro XEM
|
8
10
|
def value
|
9
|
-
tmp =
|
10
|
-
|
11
|
-
if @transaction.mosaics.empty?
|
12
|
-
tmp += min_fee
|
11
|
+
tmp = if @transaction.has_mosaics?
|
12
|
+
mosaics_fee
|
13
13
|
else
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
if @transaction.message.bytesize > 0
|
18
|
-
tmp += message_fee
|
14
|
+
FEE_FACTOR * minimum_fee(@transaction.amount / 1_000_000)
|
19
15
|
end
|
20
|
-
|
16
|
+
tmp += message_fee if @transaction.has_message?
|
21
17
|
tmp * 1_000_000
|
22
18
|
end
|
23
19
|
|
@@ -33,19 +29,40 @@ class Nis::Fee
|
|
33
29
|
|
34
30
|
private
|
35
31
|
|
36
|
-
def
|
37
|
-
tmp = [1,
|
38
|
-
tmp
|
39
|
-
testnet? ? 0.05 * tmp : tmp
|
32
|
+
def minimum_fee(base)
|
33
|
+
tmp = [1, base / 10_000].max
|
34
|
+
tmp > 25 ? 25 : tmp
|
40
35
|
end
|
41
36
|
|
42
37
|
def message_fee
|
43
|
-
|
44
|
-
testnet? ? 0.05 * tmp : tmp
|
38
|
+
FEE_FACTOR * [1, (@transaction.message.bytesize / 2 / 32) + 1].max
|
45
39
|
end
|
46
40
|
|
47
41
|
def mosaics_fee
|
48
|
-
|
42
|
+
FEE_FACTOR * @transaction.mosaics.inject(0) do |sum, mo_attachment|
|
43
|
+
mo = mo_attachment.mosaic_definition
|
44
|
+
quantity = mo_attachment.quantity
|
45
|
+
tmp_fee = 0
|
46
|
+
if mo.divisibility == 0 && mo.initial_supply <= 10_000
|
47
|
+
# It is called *Small Business Mosaic Fee*
|
48
|
+
supply_related_adjustment = 0
|
49
|
+
tmp_fee = 1
|
50
|
+
else
|
51
|
+
# custom mosaic fee, Max is 1.25.
|
52
|
+
max_mosaic_quantity = 9_000_000_000_000_000
|
53
|
+
total_mosaic_quantity = mo.initial_supply * (10**mo.divisibility)
|
54
|
+
supply_related_adjustment = (0.8 * (Math.log(max_mosaic_quantity / total_mosaic_quantity))).floor
|
55
|
+
|
56
|
+
num_nem = if mo.initial_supply == 0
|
57
|
+
0
|
58
|
+
else
|
59
|
+
8_999_999_999.to_f * quantity * 1_000_000 / mo.initial_supply / (10**(mo.divisibility + 6))
|
60
|
+
end
|
61
|
+
tmp_fee = minimum_fee(num_nem.ceil)
|
62
|
+
end
|
63
|
+
|
64
|
+
sum + [1, tmp_fee - supply_related_adjustment].max
|
65
|
+
end
|
49
66
|
end
|
50
67
|
end
|
51
68
|
end
|
data/lib/nis/keypair.rb
CHANGED
@@ -2,11 +2,15 @@ class Nis
|
|
2
2
|
class Keypair
|
3
3
|
attr_reader :private, :public
|
4
4
|
|
5
|
+
# @param [String] Private Key
|
6
|
+
# @option options [Strung] :public_key Public Key
|
5
7
|
def initialize(private_key, public_key: nil)
|
6
8
|
@private = private_key
|
7
9
|
@public = public_key || calc_public_key
|
8
10
|
end
|
9
11
|
|
12
|
+
# @param [String] Hex string
|
13
|
+
# @return [String] Signed hex string
|
10
14
|
def sign(data)
|
11
15
|
bin_data = data.scan(/../).map(&:hex).pack('C*')
|
12
16
|
bin_signed = Nis::Util::Ed25519.signature_hash_unsafe(bin_data, @bin_secret, @bin_public)
|
data/lib/nis/request/announce.rb
CHANGED
@@ -14,6 +14,7 @@ class Nis::Request
|
|
14
14
|
@transaction = transaction
|
15
15
|
end
|
16
16
|
|
17
|
+
# @return [Hash] Attribute and value pairs
|
17
18
|
def to_hash
|
18
19
|
if @transaction.respond_to?(:other_trans)
|
19
20
|
other_trans(@transaction)
|
@@ -22,7 +23,7 @@ class Nis::Request
|
|
22
23
|
@transaction.tap do |tx|
|
23
24
|
tx.timeStamp = Nis::Util.timestamp
|
24
25
|
tx.deadline = Nis::Util.deadline(DEADLINE)
|
25
|
-
tx.version = Nis::Util.parse_version(tx.network,
|
26
|
+
tx.version = Nis::Util.parse_version(tx.network, version(tx))
|
26
27
|
tx.signer = @keypair.public
|
27
28
|
end
|
28
29
|
|
@@ -47,9 +48,13 @@ class Nis::Request
|
|
47
48
|
transaction.other_trans.tap do |tx|
|
48
49
|
tx.timeStamp = Nis::Util.timestamp
|
49
50
|
tx.deadline = Nis::Util.deadline(DEADLINE)
|
50
|
-
tx.version = Nis::Util.parse_version(tx.network,
|
51
|
+
tx.version = Nis::Util.parse_version(tx.network, version(tx))
|
51
52
|
tx.signer = transaction.signer
|
52
53
|
end
|
53
54
|
end
|
55
|
+
|
56
|
+
def version(transaction)
|
57
|
+
transaction.respond_to?(:has_mosaics?) && transaction.has_mosaics? ? 2 : 1
|
58
|
+
end
|
54
59
|
end
|
55
60
|
end
|
@@ -14,6 +14,7 @@ class Nis::Request
|
|
14
14
|
@transaction = transaction
|
15
15
|
end
|
16
16
|
|
17
|
+
# @return [Hash] Attribute and value pairs
|
17
18
|
def to_hash
|
18
19
|
if @transaction.respond_to?(:other_trans)
|
19
20
|
other_trans(@transaction)
|
@@ -22,7 +23,7 @@ class Nis::Request
|
|
22
23
|
@transaction.tap do |tx|
|
23
24
|
tx.timeStamp = Nis::Util.timestamp
|
24
25
|
tx.deadline = Nis::Util.deadline(DEADLINE)
|
25
|
-
tx.version = Nis::Util.parse_version(tx.network,
|
26
|
+
tx.version = Nis::Util.parse_version(tx.network, version(tx))
|
26
27
|
tx.signer = @keypair.public
|
27
28
|
end
|
28
29
|
|
@@ -36,13 +37,15 @@ class Nis::Request
|
|
36
37
|
transaction.other_trans.tap do |tx|
|
37
38
|
tx.timeStamp = Nis::Util.timestamp
|
38
39
|
tx.deadline = Nis::Util.deadline(DEADLINE)
|
39
|
-
tx.version = Nis::Util.parse_version(tx.network,
|
40
|
-
|
41
|
-
# tx.signer = @keypair.public
|
40
|
+
tx.version = Nis::Util.parse_version(tx.network, version(tx))
|
42
41
|
|
43
42
|
# multisig transfer
|
44
43
|
tx.signer = transaction.signer
|
45
44
|
end
|
46
45
|
end
|
46
|
+
|
47
|
+
def version(transaction)
|
48
|
+
transaction.respond_to?(:has_mosaics?) && transaction.has_mosaics? ? 2 : 1
|
49
|
+
end
|
47
50
|
end
|
48
51
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Nis::Struct
|
2
|
+
# @attr [String] fqn ex) nem.xem
|
3
|
+
# @attr [Integer] quantity
|
4
|
+
# @see https://nemproject.github.io/#version-2-transfer-transactions
|
5
|
+
class MosaicAttachment
|
6
|
+
extend Forwardable
|
7
|
+
def_delegators :@mosaic_definition, :initial_supply, :divisibility
|
8
|
+
|
9
|
+
attr_reader :mosaic_definition, :quantity
|
10
|
+
|
11
|
+
def initialize(mo_def, quantity)
|
12
|
+
@mosaic_definition = mo_def
|
13
|
+
@quantity = quantity
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_hash
|
17
|
+
{ mosaicId: @mosaic_definition.id.to_hash,
|
18
|
+
quantity: @quantity }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -9,9 +9,13 @@ class Nis::Struct
|
|
9
9
|
include Nis::Util::Assignable
|
10
10
|
attr_accessor :creator, :id, :description, :properties, :levy
|
11
11
|
|
12
|
+
extend Forwardable
|
13
|
+
def_delegators :@properties, :divisibility, :initialSupply, :supplyMutable, :transferable,
|
14
|
+
:initial_supply, :supply_mutable
|
15
|
+
|
12
16
|
def self.build(attrs)
|
13
17
|
attrs[:id] = MosaicId.build(attrs[:id])
|
14
|
-
attrs[:properties] = attrs[:properties]
|
18
|
+
attrs[:properties] = MosaicProperties.build(attrs[:properties])
|
15
19
|
attrs[:levy] = MosaicLevy.build(attrs[:levy]) if attrs[:levy]
|
16
20
|
new(attrs)
|
17
21
|
end
|
@@ -13,7 +13,17 @@ class Nis::Struct
|
|
13
13
|
alias supply_mutable supplyMutable
|
14
14
|
alias supply_mutable= supplyMutable=
|
15
15
|
|
16
|
-
def self.build(
|
16
|
+
def self.build(props)
|
17
|
+
attrs = props.inject({}) do |hash, prop|
|
18
|
+
hash[prop[:name]] = case prop[:name]
|
19
|
+
when 'divisibility' then prop[:value].to_i
|
20
|
+
when 'initialSupply' then prop[:value].to_i
|
21
|
+
when 'supplyMutable' then prop[:value] == 'true' ? true : false
|
22
|
+
when 'transferable' then prop[:value] == 'true' ? true : false
|
23
|
+
else prop[:value]
|
24
|
+
end
|
25
|
+
hash
|
26
|
+
end
|
17
27
|
new(attrs)
|
18
28
|
end
|
19
29
|
|
@@ -64,10 +64,10 @@ class Nis::Transaction
|
|
64
64
|
end
|
65
65
|
else
|
66
66
|
if root?
|
67
|
-
{ fee:
|
67
|
+
{ fee: 100 * 1_000_000 ,
|
68
68
|
sink: 'NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA' }
|
69
69
|
else
|
70
|
-
{ fee:
|
70
|
+
{ fee: 10 * 1_000_000 ,
|
71
71
|
sink: 'NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA' }
|
72
72
|
end
|
73
73
|
end
|
@@ -20,13 +20,14 @@ class Nis::Transaction
|
|
20
20
|
attr_reader :type, :fee
|
21
21
|
attr_accessor :recipient, :amount, :message,
|
22
22
|
:deadline, :timeStamp, :version, :signer,
|
23
|
-
:network
|
23
|
+
:network,
|
24
|
+
:mosaics
|
24
25
|
|
25
26
|
alias timestamp timeStamp
|
26
27
|
|
27
28
|
TYPE = 0x0101 # 257 (transfer transaction)
|
28
29
|
|
29
|
-
def initialize(recipient, amount, message = '', network: :testnet)
|
30
|
+
def initialize(recipient, amount, message = '', mosaics: [], network: :testnet)
|
30
31
|
@type = TYPE
|
31
32
|
@network = network
|
32
33
|
|
@@ -34,11 +35,15 @@ class Nis::Transaction
|
|
34
35
|
@amount = amount
|
35
36
|
@message = Nis::Struct::Message.new(message)
|
36
37
|
@fee = Nis::Fee::Transfer.new(self)
|
38
|
+
@mosaics = mosaics
|
37
39
|
end
|
38
40
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
41
|
+
def has_message?
|
42
|
+
@message.bytesize > 0
|
43
|
+
end
|
44
|
+
|
45
|
+
def has_mosaics?
|
46
|
+
@mosaics.size > 0
|
42
47
|
end
|
43
48
|
end
|
44
49
|
end
|
data/lib/nis/unit/address.rb
CHANGED
@@ -48,7 +48,7 @@ module Nis::Unit
|
|
48
48
|
ripe = OpenSSL::Digest::RIPEMD160.digest(public_key_hash)
|
49
49
|
|
50
50
|
if network == :testnet
|
51
|
-
version = "\x98".force_encoding(
|
51
|
+
version = "\x98".force_encoding('ASCII-8BIT') + ripe
|
52
52
|
else
|
53
53
|
version = "\x68" + ripe
|
54
54
|
end
|
data/lib/nis/util.rb
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
module Nis::Util
|
2
|
+
module Deserializer
|
3
|
+
# Deserialize a transaction object
|
4
|
+
# @param [String] serialized
|
5
|
+
# @return [Hash]
|
6
|
+
def self.deserialize_transaction(serialized)
|
7
|
+
s = Nis::Util::Convert.hex2ua(serialized)
|
8
|
+
tx = {}
|
9
|
+
tx[:type] = deserialize_int(s[0, 4])
|
10
|
+
tx[:version] = deserialize_int(s[4, 4])
|
11
|
+
tx[:timeStamp] = deserialize_int(s[8, 4])
|
12
|
+
# s[12,4] # length of public key
|
13
|
+
tx[:signer] = deserialize_hex(s[16, 32])
|
14
|
+
tx[:fee] = deserialize_int(s[48, 8])
|
15
|
+
tx[:deadline] = deserialize_int(s[56, 4])
|
16
|
+
# s[60,4] # length of address
|
17
|
+
tx[:recipient] = deserialize_a(s[64, 40])
|
18
|
+
tx[:amount] = deserialize_int(s[104, 8])
|
19
|
+
|
20
|
+
tx[:message] = {}
|
21
|
+
message_len = deserialize_int(s[112, 4])
|
22
|
+
if message_len > 0
|
23
|
+
tx[:message][:type] = deserialize_int(s[116, 4])
|
24
|
+
# s[120, 4] # length of payload
|
25
|
+
tx[:message][:payload] = deserialize_hex(s[124, s.size])
|
26
|
+
else
|
27
|
+
tx[:message] = { type: 1, payload: '' }
|
28
|
+
end
|
29
|
+
tx
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def self.deserialize_int(ua)
|
35
|
+
Nis::Util::Convert.ua2hex(ua.reverse).to_i(16)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.deserialize_hex(ua)
|
39
|
+
Nis::Util::Convert.ua2hex(ua)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.deserialize_a(ua)
|
43
|
+
Nis::Util::Convert.hex2a(deserialize_hex(ua))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/nis/util/ed25519.rb
CHANGED
@@ -14,7 +14,7 @@ module Nis::Util
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def intlist2bytes(l)
|
17
|
-
l.map {|c| c.chr }.join
|
17
|
+
l.map { |c| c.chr }.join
|
18
18
|
end
|
19
19
|
|
20
20
|
# standard implement
|
@@ -27,36 +27,34 @@ module Nis::Util
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def pow2(x, p)
|
30
|
-
# """== pow(x, 2**p, q)"""
|
31
30
|
while p > 0 do
|
32
|
-
x = x * x %
|
31
|
+
x = x * x % @@q
|
33
32
|
p -= 1
|
34
33
|
end
|
35
34
|
x
|
36
35
|
end
|
37
36
|
|
38
37
|
def inv(z)
|
39
|
-
# """$= z^{-1} \mod q$, for z != 0"""
|
40
38
|
# Adapted from curve25519_athlon.c in djb's Curve25519.
|
41
|
-
z2 = z * z %
|
42
|
-
z9 = pow2(z2, 2) * z %
|
43
|
-
z11 = z9 * z2 %
|
44
|
-
z2_5_0 = (z11 * z11) %
|
45
|
-
z2_10_0 = pow2(z2_5_0, 5) * z2_5_0 %
|
46
|
-
z2_20_0 = pow2(z2_10_0, 10) * z2_10_0 %
|
47
|
-
z2_40_0 = pow2(z2_20_0, 20) * z2_20_0 %
|
48
|
-
z2_50_0 = pow2(z2_40_0, 10) * z2_10_0 %
|
49
|
-
z2_100_0 = pow2(z2_50_0, 50) * z2_50_0 %
|
50
|
-
z2_200_0 = pow2(z2_100_0, 100) * z2_100_0 %
|
51
|
-
z2_250_0 = pow2(z2_200_0, 50) * z2_50_0 %
|
52
|
-
pow2(z2_250_0, 5) * z11 %
|
39
|
+
z2 = z * z % @@q # 2
|
40
|
+
z9 = pow2(z2, 2) * z % @@q # 9
|
41
|
+
z11 = z9 * z2 % @@q # 11
|
42
|
+
z2_5_0 = (z11 * z11) % @@q * z9 % @@q # 31 == 2^5 - 2^0
|
43
|
+
z2_10_0 = pow2(z2_5_0, 5) * z2_5_0 % @@q # 2^10 - 2^0
|
44
|
+
z2_20_0 = pow2(z2_10_0, 10) * z2_10_0 % @@q # ...
|
45
|
+
z2_40_0 = pow2(z2_20_0, 20) * z2_20_0 % @@q
|
46
|
+
z2_50_0 = pow2(z2_40_0, 10) * z2_10_0 % @@q
|
47
|
+
z2_100_0 = pow2(z2_50_0, 50) * z2_50_0 % @@q
|
48
|
+
z2_200_0 = pow2(z2_100_0, 100) * z2_100_0 % @@q
|
49
|
+
z2_250_0 = pow2(z2_200_0, 50) * z2_50_0 % @@q # 2^250 - 2^0
|
50
|
+
pow2(z2_250_0, 5) * z11 % @@q # 2^255 - 2^5 + 11 = q - 2
|
53
51
|
end
|
54
52
|
|
55
53
|
def xrecover(y)
|
56
|
-
xx = (y * y - 1) * inv(
|
57
|
-
x = xx.to_bn.mod_exp((
|
58
|
-
x = (x *
|
59
|
-
x =
|
54
|
+
xx = (y * y - 1) * inv(@@d * y * y + 1)
|
55
|
+
x = xx.to_bn.mod_exp((@@q + 3) / 8, @@q)
|
56
|
+
x = (x * @@I) % @@q if (x * x - xx) % @@q != 0
|
57
|
+
x = @@q - x if x % 2 != 0
|
60
58
|
x
|
61
59
|
end
|
62
60
|
|
@@ -66,10 +64,10 @@ module Nis::Util
|
|
66
64
|
x1, y1, z1, t1 = _P
|
67
65
|
x2, y2, z2, t2 = _Q
|
68
66
|
|
69
|
-
a = (y1 - x1) * (y2 - x2) %
|
70
|
-
b = (y1 + x1) * (y2 + x2) %
|
71
|
-
c = t1 * 2 *
|
72
|
-
dd = z1 * 2 * z2 %
|
67
|
+
a = (y1 - x1) * (y2 - x2) % @@q
|
68
|
+
b = (y1 + x1) * (y2 + x2) % @@q
|
69
|
+
c = t1 * 2 * @@d * t2 % @@q
|
70
|
+
dd = z1 * 2 * z2 % @@q
|
73
71
|
e = b - a
|
74
72
|
f = dd - c
|
75
73
|
g = dd + c
|
@@ -79,7 +77,7 @@ module Nis::Util
|
|
79
77
|
t3 = e * h
|
80
78
|
z3 = f * g
|
81
79
|
|
82
|
-
[x3 %
|
80
|
+
[x3 % @@q, y3 % @@q, z3 % @@q, t3 % @@q]
|
83
81
|
end
|
84
82
|
|
85
83
|
def edwards_double(_P)
|
@@ -87,11 +85,11 @@ module Nis::Util
|
|
87
85
|
# http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
|
88
86
|
x1, y1, z1, _t1 = _P
|
89
87
|
|
90
|
-
a = x1 * x1 %
|
91
|
-
b = y1 * y1 %
|
92
|
-
c = 2 * z1 * z1 %
|
88
|
+
a = x1 * x1 % @@q
|
89
|
+
b = y1 * y1 % @@q
|
90
|
+
c = 2 * z1 * z1 % @@q
|
93
91
|
# dd = -a
|
94
|
-
e = ((x1 + y1) * (x1 + y1) - a - b) %
|
92
|
+
e = ((x1 + y1) * (x1 + y1) - a - b) % @@q
|
95
93
|
g = -a + b # dd + b
|
96
94
|
f = g - c
|
97
95
|
h = -a - b # dd - b
|
@@ -100,11 +98,11 @@ module Nis::Util
|
|
100
98
|
t3 = e * h
|
101
99
|
z3 = f * g
|
102
100
|
|
103
|
-
|
101
|
+
[x3 % @@q, y3 % @@q, z3 % @@q, t3 % @@q]
|
104
102
|
end
|
105
103
|
|
106
104
|
def scalarmult(_P, e)
|
107
|
-
|
105
|
+
@@ident if e == 0
|
108
106
|
_Q = scalarmult(_P, e / 2)
|
109
107
|
_Q = edwards_double(_Q)
|
110
108
|
_Q = edwards_add(_Q, _P) if e & 1
|
@@ -112,9 +110,9 @@ module Nis::Util
|
|
112
110
|
end
|
113
111
|
|
114
112
|
def make_Bpow
|
115
|
-
_P =
|
113
|
+
_P = @@B
|
116
114
|
(0...253).each do |_|
|
117
|
-
|
115
|
+
@@Bpow << _P
|
118
116
|
_P = edwards_double(_P)
|
119
117
|
end
|
120
118
|
end
|
@@ -122,27 +120,27 @@ module Nis::Util
|
|
122
120
|
# Implements scalarmult(B, e) more efficiently.
|
123
121
|
def scalarmult_B(e)
|
124
122
|
# scalarmult(B, l) is the identity
|
125
|
-
e = e %
|
126
|
-
_P =
|
123
|
+
e = e % @@l
|
124
|
+
_P = @@ident
|
127
125
|
(0...253).each do |i|
|
128
|
-
_P = edwards_add(_P,
|
126
|
+
_P = edwards_add(_P, @@Bpow[i]) if e & 1 == 1
|
129
127
|
e = e / 2
|
130
128
|
end
|
131
129
|
_P
|
132
130
|
end
|
133
131
|
|
134
132
|
def encodeint(y)
|
135
|
-
bits = (0
|
136
|
-
(0
|
133
|
+
bits = (0...@@b).map { |i| (y >> i) & 1 }
|
134
|
+
(0...@@b / 8).map { |i| int2byte((0...8).inject(0) { |sum, j| sum + (bits[i * 8 + j] << j) }) }.join
|
137
135
|
end
|
138
136
|
|
139
137
|
def encodepoint(_P)
|
140
138
|
x, y, z, _t = _P
|
141
139
|
zi = inv(z)
|
142
|
-
x = (x * zi) %
|
143
|
-
y = (y * zi) %
|
144
|
-
bits = (0
|
145
|
-
(0
|
140
|
+
x = (x * zi) % @@q
|
141
|
+
y = (y * zi) % @@q
|
142
|
+
bits = (0...@@b - 1).map { |i| (y >> i) & 1 } + [x & 1]
|
143
|
+
(0...@@b / 8).map { |i| int2byte((0...8).inject(0) { |sum, j| sum + (bits[i * 8 + j] << j) }) }.join
|
146
144
|
end
|
147
145
|
|
148
146
|
def bit(h, i)
|
@@ -151,70 +149,69 @@ module Nis::Util
|
|
151
149
|
|
152
150
|
def publickey_unsafe(sk)
|
153
151
|
h = H(sk)
|
154
|
-
a = 2
|
152
|
+
a = 2**(@@b - 2) + (3...@@b - 2).inject(0) { |sum, i| sum + 2**i * bit(h, i) }
|
155
153
|
_A = scalarmult_B(a)
|
156
|
-
|
154
|
+
codepoint(_A)
|
157
155
|
end
|
158
156
|
|
159
157
|
def publickey_hash_unsafe(sk)
|
160
158
|
h = HH(sk)
|
161
|
-
a = 2
|
159
|
+
a = 2**(@@b - 2) + (3...@@b - 2).inject(0) { |sum, i| sum + 2**i * bit(h, i) }
|
162
160
|
_A = scalarmult_B(a)
|
163
|
-
|
161
|
+
encodepoint(_A)
|
164
162
|
end
|
165
163
|
|
166
164
|
def Hint(m)
|
167
165
|
h = H(m)
|
168
|
-
(0...2
|
166
|
+
(0...2 * @@b).inject(0) { |sum, i| sum + 2**i * bit(h, i) }
|
169
167
|
end
|
170
168
|
|
171
169
|
def Hint_hash(m)
|
172
170
|
h = HH(m)
|
173
|
-
(0...2
|
171
|
+
(0...2 * @@b).inject(0) { |sum, i| sum + 2**i * bit(h, i) }
|
174
172
|
end
|
175
173
|
|
176
174
|
def signature_unsafe(m, sk, pk)
|
177
175
|
h = H(sk)
|
178
|
-
a = 2
|
176
|
+
a = 2**(@@b - 2) + (3...@@b - 2).inject(0) { |sum, i| sum + 2**i * bit(h, i) }
|
179
177
|
r = Hint(
|
180
|
-
intlist2bytes((
|
178
|
+
intlist2bytes((@@b / 8...@@b / 4).map { |j| indexbytes(h, j) }) + m
|
181
179
|
)
|
182
180
|
_R = scalarmult_B(r)
|
183
|
-
_S = (r + Hint(encodepoint(_R) + pk + m) * a) %
|
181
|
+
_S = (r + Hint(encodepoint(_R) + pk + m) * a) % @@l
|
184
182
|
encodepoint(_R) + encodeint(_S)
|
185
183
|
end
|
186
184
|
|
187
185
|
# Not safe to use with secret keys or secret data.
|
188
|
-
#
|
189
|
-
# See module docstring. This function should be used for testing only.
|
186
|
+
# This function should be used for testing only.
|
190
187
|
def signature_hash_unsafe(m, sk, pk)
|
191
188
|
h = HH(sk)
|
192
|
-
a = 2
|
189
|
+
a = 2**(@@b - 2) + (3...@@b - 2).inject(0) { |sum, i| sum + 2**i * bit(h, i) }
|
193
190
|
r = Hint_hash(
|
194
|
-
intlist2bytes((
|
191
|
+
intlist2bytes((@@b / 8...@@b / 4).map { |j| indexbytes(h, j) }) + m
|
195
192
|
)
|
196
193
|
_R = scalarmult_B(r)
|
197
|
-
_S = (r + Hint_hash(encodepoint(_R) + pk + m) * a) %
|
194
|
+
_S = (r + Hint_hash(encodepoint(_R) + pk + m) * a) % @@l
|
198
195
|
encodepoint(_R) + encodeint(_S)
|
199
196
|
end
|
200
197
|
|
201
198
|
def isoncurve(_P)
|
202
199
|
x, y, z, t = _P
|
203
|
-
(z %
|
204
|
-
x * y %
|
205
|
-
(y * y - x * x - z * z -
|
200
|
+
(z % @@q != (0) &&
|
201
|
+
x * y % @@q == (z * t % @@q) &&
|
202
|
+
(y * y - x * x - z * z - @@d * t * t) % @@q == (0))
|
206
203
|
end
|
207
204
|
|
208
205
|
def decodeint(s)
|
209
|
-
(0
|
206
|
+
(0...@@b).inject(0) { |sum, i| sum + 2**i * bit(s, i) }
|
210
207
|
end
|
211
208
|
|
212
209
|
def decodepoint(s)
|
213
|
-
y = (0
|
210
|
+
y = (0...@@b - 1).inject(0) { |sum, i| 2**i * bit(s, i) }
|
214
211
|
x = xrecover(y)
|
215
|
-
x =
|
216
|
-
_P = [x, y, 1, (x * y) %
|
217
|
-
raise
|
212
|
+
x = @@q - x if x & 1 != bit(s, @@b - 1)
|
213
|
+
_P = [x, y, 1, (x * y) % @@q]
|
214
|
+
raise 'decoding point that is not on curve' unless isoncurve(_P)
|
218
215
|
_P
|
219
216
|
end
|
220
217
|
|
@@ -222,42 +219,39 @@ module Nis::Util
|
|
222
219
|
end
|
223
220
|
|
224
221
|
# Not safe to use when any argument is secret.
|
225
|
-
#
|
226
|
-
# See module docstring. This function should be used only for
|
222
|
+
# This function should be used only for
|
227
223
|
# verifying public signatures of public messages.
|
228
224
|
def checkvalid(s, m, pk)
|
229
|
-
raise
|
230
|
-
raise
|
225
|
+
raise 'signature length is wrong' if s.size != @@b / 4
|
226
|
+
raise 'public-key length is wrong' if pk.size != @@b / 8
|
231
227
|
|
232
|
-
|
233
|
-
_R = decodepoint(s[0...$b/8])
|
228
|
+
_R = decodepoint(s[0...@@b / 8])
|
234
229
|
_A = decodepoint(pk)
|
235
|
-
_S = decodeint(s[
|
230
|
+
_S = decodeint(s[@@b / 8...@@b / 4])
|
236
231
|
h = Hint(encodepoint(_R) + pk + m)
|
237
232
|
|
238
233
|
x1, y1, z1, _t1 = _P = scalarmult_B(_S)
|
239
234
|
x2, y2, z2, _t2 = _Q = edwards_add(_R, scalarmult(_A, h))
|
240
235
|
|
241
236
|
if (!isoncurve(_P) || !isoncurve(_Q) || (x1 * z2 - x2 * z1) % q != 0 || (y1 * z2 - y2 * z1) % q != 0)
|
242
|
-
raise SignatureMismatch(
|
237
|
+
raise SignatureMismatch('signature does not pass verification')
|
243
238
|
end
|
244
239
|
end
|
245
240
|
end
|
246
241
|
|
247
|
-
|
248
|
-
|
249
|
-
|
242
|
+
@@b = 256
|
243
|
+
@@q = 2**255 - 19
|
244
|
+
@@l = 2**252 + 27742317777372353535851937790883648493
|
250
245
|
|
251
|
-
|
252
|
-
|
246
|
+
@@d = -121665 * self.inv(121666) % @@q
|
247
|
+
@@I = 2.to_bn.mod_exp((@@q - 1) / 4, @@q)
|
253
248
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
249
|
+
@@By = 4 * self.inv(5)
|
250
|
+
@@Bx = self.xrecover(@@By)
|
251
|
+
@@B = [@@Bx % @@q, @@By % @@q, 1, (@@Bx * @@By) % @@q]
|
252
|
+
@@ident = [0, 1, 1, 0]
|
258
253
|
|
259
|
-
|
260
|
-
$Bpow = []
|
254
|
+
@@Bpow = []
|
261
255
|
self.make_Bpow
|
262
256
|
end
|
263
257
|
end
|
data/lib/nis/util/serializer.rb
CHANGED
@@ -5,8 +5,8 @@ module Nis::Util
|
|
5
5
|
# @return [Array]
|
6
6
|
def self.serialize_transaction(entity)
|
7
7
|
method = case entity[:type]
|
8
|
-
|
9
|
-
|
8
|
+
when 257 then method(:serialize_transfer)
|
9
|
+
when 4100 then method(:serialize_multisig_transfer)
|
10
10
|
else raise "Not implemented entity type: #{entity[:type]}"
|
11
11
|
end
|
12
12
|
method.call(entity)
|
@@ -54,7 +54,7 @@ module Nis::Util
|
|
54
54
|
|
55
55
|
temp = hex2ua(entity[:message][:payload])
|
56
56
|
if temp.size == 0
|
57
|
-
a += [0,0,0,0]
|
57
|
+
a += [0, 0, 0, 0]
|
58
58
|
else
|
59
59
|
a += serialize_int(temp.size + 8)
|
60
60
|
a += serialize_int(entity[:message][:type])
|
data/lib/nis/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nis-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yoshiyuki Ieyama
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-08-
|
11
|
+
date: 2017-08-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -213,6 +213,7 @@ files:
|
|
213
213
|
- examples/transactions/multisig_signature.rb
|
214
214
|
- examples/transactions/provision_namespace.rb
|
215
215
|
- examples/transactions/transfer.rb
|
216
|
+
- examples/transactions/transfer_mosaic.rb
|
216
217
|
- examples/transactions/transfer_remote.rb
|
217
218
|
- lib/nis.rb
|
218
219
|
- lib/nis/client.rb
|
@@ -295,6 +296,7 @@ files:
|
|
295
296
|
- lib/nis/struct/key_pair_view_model.rb
|
296
297
|
- lib/nis/struct/message.rb
|
297
298
|
- lib/nis/struct/mosaic.rb
|
299
|
+
- lib/nis/struct/mosaic_attachment.rb
|
298
300
|
- lib/nis/struct/mosaic_definition.rb
|
299
301
|
- lib/nis/struct/mosaic_definition_meta_data.rb
|
300
302
|
- lib/nis/struct/mosaic_definition_meta_data_pair.rb
|
@@ -340,6 +342,7 @@ files:
|
|
340
342
|
- lib/nis/util.rb
|
341
343
|
- lib/nis/util/assignable.rb
|
342
344
|
- lib/nis/util/convert.rb
|
345
|
+
- lib/nis/util/deserializer.rb
|
343
346
|
- lib/nis/util/ed25519.rb
|
344
347
|
- lib/nis/util/serializer.rb
|
345
348
|
- lib/nis/version.rb
|