appchain.rb 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3cf165b17b0de81dc3dcc848600339523ad2e7920be7a4f620bdcca802d3525b
4
- data.tar.gz: 4067ce5f69b9e1b76c7143117ddd6d032348802b478e5afc953f53da722384b1
3
+ metadata.gz: dd176c97f662397947072cd9b6b08cb967cbc1ea171b44e5e2c9d4b348cd5e4a
4
+ data.tar.gz: 354f3c9e6dbad28a6d7e8a4c9053b942d11c889615e60903145d03daa879e1bb
5
5
  SHA512:
6
- metadata.gz: 839817784fd7e9351b8050e86d7c9353078a971c0d00bc7316f09fb928461ddc3359f05f082d420441f48a8034d559688d3c245424f4fe50cb0059256d6ab9bd
7
- data.tar.gz: 6fd4a13f92717af89484f62c05f2816e9e19d4521706ddfe4e1ed17540572c375b5358f4d4cce296f1d56ca5a5dc076503ca4038367652dbed49b3b99c494e04
6
+ metadata.gz: 57c287fc34a72b7b0fa5d51a859a11dcb37a69fa81455a659b75327ec42e0a0e5eebcddce7a90c9b256eaf7f29f3cf0fe9417d04013fccbb042296adb7c390df
7
+ data.tar.gz: 78cd56fc25a1ed429be686b1db3ae2b958b585e8aa3207b2ee40b95bf81b5fb4a679082c98dc4b8b0a4fa740450db7151a1d2f3d8a8363f892dbeba99416c2c3
@@ -3,9 +3,9 @@ sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
- - 2.4.4
7
- - 2.5.1
8
- before_install: gem install bundler -v 1.16.4
6
+ - 2.4.5
7
+ - 2.5.3
8
+ before_install: gem install bundler -v 1.16.6
9
9
  install:
10
10
  - cd secp256k1 && ./autogen.sh && ./configure --enable-module-recovery --enable-experimental --enable-module-ecdh && make && sudo make install && cd ..
11
11
  - bundle install
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- appchain.rb (0.1.0)
4
+ appchain.rb (0.2.0)
5
5
  activesupport (~> 5.2.1)
6
6
  ciri-crypto (= 0.1.1)
7
7
  faraday (~> 0.15.3)
@@ -33,7 +33,7 @@ GEM
33
33
  multipart-post (>= 1.2, < 3)
34
34
  ffi (1.9.25)
35
35
  google-protobuf (3.6.1-universal-darwin)
36
- i18n (1.1.0)
36
+ i18n (1.1.1)
37
37
  concurrent-ruby (~> 1.0)
38
38
  jaro_winkler (1.5.1)
39
39
  method_source (0.9.0)
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # AppChain.rb
2
2
 
3
3
  [![Build Status](https://travis-ci.org/cryptape/appchain.rb.svg?branch=master)](https://travis-ci.org/cryptape/appchain.rb)
4
+ [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](https://www.rubydoc.info/github/cryptape/appchain.rb/master)
4
5
 
5
6
  Nervos AppChain Ruby SDK
6
7
 
@@ -44,8 +45,8 @@ transaction = AppChain::Transaction.new(
44
45
  valid_until_block: 1882078,
45
46
  data: "",
46
47
  value: "0x3e8",
47
- chain_id: 1,
48
- version: 0
48
+ chain_id: "1",
49
+ version: 1
49
50
  )
50
51
 
51
52
  # sign transaction with your private key
@@ -74,6 +75,18 @@ response = contract.call_func(method: :symbol)
74
75
  response = contract.send_func(tx: tx, private_key: private_key, method: :transfer, params: [address, tokens])
75
76
  ```
76
77
 
78
+ ## CHANGELOG
79
+
80
+ ### v0.1
81
+
82
+ * supports CITA 0.19
83
+
84
+ ### 0.2
85
+
86
+ * supports CITA 0.20 and 0.19
87
+ * set default transaction version to 1
88
+ * update TransactionSinger.decode output type to hash with symbol keys
89
+ * parse TransactionSinger.decode hash value to hex string if it's bytes.
77
90
 
78
91
  ## Contributing
79
92
 
@@ -40,7 +40,7 @@ module AppChain
40
40
  # @param tx [Hash] see rpc `call` doc for more info
41
41
  #
42
42
  # @return [any]
43
- def call_func(method:, params: [], tx: {})
43
+ def call_func(method:, params: [], tx: {}) # rubocop:disable Naming/UncommunicativeMethodParamName
44
44
  data, output_types = function_data_with_ot(method, *params)
45
45
  resp = @rpc.call_rpc(:call, params: [tx.merge(data: data, to: address), "latest"])
46
46
  result = resp["result"]
@@ -60,7 +60,7 @@ module AppChain
60
60
  # @param *params [Array] your params
61
61
  #
62
62
  # @return [nil | Hash] {hash: "", status: ""}, sendRawTransactionResult
63
- def send_func(tx:, private_key:, method:, params: [])
63
+ def send_func(tx:, private_key:, method:, params: []) # rubocop:disable Naming/UncommunicativeMethodParamName
64
64
  data, _output_types = function_data_with_ot(method, *params)
65
65
  transaction = if tx.is_a?(Hash)
66
66
  Transaction.from_hash(tx)
@@ -15,8 +15,8 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
15
15
  optional :state_root, :bytes, 4
16
16
  optional :transactions_root, :bytes, 5
17
17
  optional :receipts_root, :bytes, 6
18
- optional :gas_used, :uint64, 7
19
- optional :gas_limit, :uint64, 8
18
+ optional :quota_used, :uint64, 7
19
+ optional :quota_limit, :uint64, 8
20
20
  optional :proof, :message, 9, "Proof"
21
21
  optional :proposer, :bytes, 10
22
22
  end
@@ -25,8 +25,8 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
25
25
  optional :height, :uint64, 2
26
26
  end
27
27
  add_message "AccountGasLimit" do
28
- optional :common_gas_limit, :uint64, 1
29
- map :specific_gas_limit, :string, :uint64, 2
28
+ optional :common_quota_limit, :uint64, 1
29
+ map :specific_quota_limit, :string, :uint64, 2
30
30
  end
31
31
  add_message "RichStatus" do
32
32
  optional :hash, :bytes, 1
@@ -34,6 +34,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
34
34
  repeated :nodes, :bytes, 3
35
35
  optional :interval, :uint64, 4
36
36
  optional :version, :uint32, 5
37
+ repeated :validators, :bytes, 6
37
38
  end
38
39
  add_message "Transaction" do
39
40
  optional :to, :string, 1
@@ -44,6 +45,8 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
44
45
  optional :value, :bytes, 6
45
46
  optional :chain_id, :uint32, 7
46
47
  optional :version, :uint32, 8
48
+ optional :to_v1, :bytes, 9
49
+ optional :chain_id_v1, :bytes, 10
47
50
  end
48
51
  add_message "UnverifiedTransaction" do
49
52
  optional :transaction, :message, 1, "Transaction"
@@ -107,4 +110,3 @@ module AppChain::Protos
107
110
  ProofType = Google::Protobuf::DescriptorPool.generated_pool.lookup("ProofType").enummodule
108
111
  Crypto = Google::Protobuf::DescriptorPool.generated_pool.lookup("Crypto").enummodule
109
112
  end
110
-
@@ -70,8 +70,14 @@ module AppChain
70
70
  # @return [Hash]
71
71
  def transfer(to:, private_key:, value:, quota: 30_000)
72
72
  valid_until_block = block_number["result"].hex + 88
73
- chain_id = get_meta_data("latest").dig "result", "chainId"
74
- transaction = Transaction.new(nonce: Utils.nonce, valid_until_block: valid_until_block, chain_id: chain_id, to: to, value: value, quota: quota)
73
+ meta_data = get_meta_data("latest")["result"]
74
+ version = meta_data["version"]
75
+ chain_id = if version.zero?
76
+ meta_data["chainId"]
77
+ elsif version == 1
78
+ meta_data["chainIdV1"]
79
+ end
80
+ transaction = Transaction.new(nonce: Utils.nonce, valid_until_block: valid_until_block, chain_id: chain_id, to: to, value: value, quota: quota, version: version)
75
81
  send_transaction(transaction, private_key)
76
82
  end
77
83
  end
@@ -1,12 +1,16 @@
1
1
  # frozen_string_literal: true
2
+ require "active_support/core_ext/string"
2
3
 
3
4
  module AppChain
4
5
  class Transaction
6
+ class VersionError < StandardError
7
+ end
8
+
5
9
  attr_accessor :to, :nonce, :quota, :valid_until_block, :data, :value, :chain_id, :version
6
10
 
7
- # @param nonce [String]
11
+ # @param nonce [String] default is SecureRandom.hex; if you provide with nil or empty string, it will be assigned a random string.
8
12
  # @param valid_until_block [Integer]
9
- # @param chain_id [Integer]
13
+ # @param chain_id [Integer | String] hex string if version == 1
10
14
  # @param version [Integer]
11
15
  # @param to [String]
12
16
  # @param data [String]
@@ -14,13 +18,19 @@ module AppChain
14
18
  # @param quota [Integer]
15
19
  #
16
20
  # @return [void]
17
- def initialize(nonce:, valid_until_block:, chain_id:, version: 0, to: nil, data: nil, value: "0", quota: 1_000_000)
21
+ def initialize(valid_until_block:, chain_id:, nonce: nil, version: 1, to: nil, data: nil, value: "0", quota: 1_000_000) # rubocop:disable Metrics/ParameterLists
22
+ raise VersionError, "transaction version error, expected 0 or 1, got #{version}" unless [0, 1].include?(version)
23
+
18
24
  @to = to
19
- @nonce = nonce
25
+ @nonce = nonce.blank? ? SecureRandom.hex : nonce
20
26
  @quota = quota
21
27
  @valid_until_block = valid_until_block
22
28
  @data = data
23
- @chain_id = chain_id
29
+ @chain_id = if chain_id.is_a?(String)
30
+ chain_id.delete("-")
31
+ else
32
+ chain_id
33
+ end
24
34
  @version = version
25
35
  @value = if value.is_a?(Integer)
26
36
  Utils.to_hex(value)
@@ -38,7 +48,7 @@ module AppChain
38
48
  chain_id: h[:chain_id],
39
49
  to: h[:to],
40
50
  data: h[:data],
41
- version: h[:version] || 0,
51
+ version: h[:version] || 1,
42
52
  value: h[:value] || "0",
43
53
  quota: h[:quota] || 1_000_000
44
54
  )
@@ -13,16 +13,23 @@ module AppChain
13
13
  def encode(transaction, private_key)
14
14
  tx = AppChain::Protos::Transaction.new
15
15
 
16
- tx.nonce = transaction.nonce
17
16
  to = AppChain::Utils.remove_hex_prefix(transaction.to)&.downcase
18
- tx.to = to unless to.nil?
17
+
18
+ tx.nonce = transaction.nonce
19
19
  tx.quota = transaction.quota
20
20
  tx.data = AppChain::Utils.to_bytes(transaction.data)
21
+ tx.value = hex_to_bytes(transaction.value)
21
22
  tx.version = transaction.version
22
- tx.value = process_value(transaction.value)
23
- tx.chain_id = transaction.chain_id
24
23
  tx.valid_until_block = transaction.valid_until_block
25
24
 
25
+ if transaction.version.zero?
26
+ tx.to = to unless to.nil?
27
+ tx.chain_id = transaction.chain_id
28
+ elsif transaction.version == 1
29
+ tx.to_v1 = Utils.to_bytes(to) unless to.nil?
30
+ tx.chain_id_v1 = hex_to_bytes(transaction.chain_id)
31
+ end
32
+
26
33
  encoded_tx = Protos::Transaction.encode(tx)
27
34
 
28
35
  private_key_bytes = AppChain::Utils.to_bytes(private_key)
@@ -41,7 +48,7 @@ module AppChain
41
48
  # unsign transaction
42
49
  #
43
50
  # @param tx_content [String] hex string
44
- def decode(tx_content)
51
+ def simple_decode(tx_content)
45
52
  content_bytes = AppChain::Utils.to_bytes(tx_content)
46
53
  unverified_transaction = Protos::UnverifiedTransaction.decode(content_bytes)
47
54
 
@@ -57,22 +64,73 @@ module AppChain
57
64
  from_address_hex = Utils.from_bytes(from_address)
58
65
 
59
66
  sender = {
60
- "address" => from_address_hex,
61
- "public_key" => pubkey_hex
67
+ address: from_address_hex,
68
+ public_key: pubkey_hex
62
69
  }
63
70
 
64
71
  {
65
- "unverified_transaction" => unverified_transaction,
66
- "sender" => sender
72
+ unverified_transaction: unverified_transaction.to_h,
73
+ sender: sender
67
74
  }
68
75
  end
69
76
 
77
+ # decode and support forks
78
+ #
79
+ # @param tx_content [String] hex string
80
+ # @return [Hash]
81
+ def original_decode(tx_content)
82
+ data = simple_decode(tx_content)
83
+ utx = data[:unverified_transaction]
84
+ tx = utx[:transaction]
85
+ version = tx[:version]
86
+
87
+ if version == 0 # rubocop:disable Style/NumericPredicate
88
+ tx.delete(:to_v1)
89
+ tx.delete(:chain_id_v1)
90
+ elsif version == 1
91
+ tx[:to] = tx.delete(:to_v1)
92
+ tx[:chain_id] = tx.delete(:chain_id_v1)
93
+ else
94
+ raise Transaction::VersionError, "transaction version error, expected 0 or 1, got #{version}"
95
+ end
96
+
97
+ data
98
+ end
99
+
100
+ # decode and parse bytes to hex string
101
+ #
102
+ # @param tx_content [String] hex string
103
+ # @return [Hash]
104
+ def decode(tx_content)
105
+ data = original_decode(tx_content)
106
+ utx = data[:unverified_transaction]
107
+ tx = utx[:transaction]
108
+ version = tx[:version]
109
+
110
+ tx[:value] = Utils.from_bytes(tx[:value])
111
+ tx[:data] = Utils.from_bytes(tx[:data])
112
+ tx[:nonce] = Utils.add_prefix_for_not_blank(tx[:nonce])
113
+ utx[:signature] = Utils.from_bytes(utx[:signature])
114
+
115
+ if version == 0 # rubocop:disable Style/NumericPredicate
116
+ tx.delete(:to_v1)
117
+ tx.delete(:chain_id_v1)
118
+ tx[:to] = Utils.add_prefix_for_not_blank(tx[:to])
119
+ elsif version == 1
120
+ tx[:to] = Utils.from_bytes(tx[:to])
121
+ tx[:chain_id] = Utils.from_bytes(tx[:chain_id])
122
+ end
123
+
124
+ data
125
+ end
126
+
70
127
  private
71
128
 
72
129
  # @param value [String] hex string with or without `0x` prefix
130
+ # @param length [Integer] length, default is 64
73
131
  # @return [String] byte code string
74
- def process_value(value)
75
- AppChain::Utils.to_bytes(AppChain::Utils.remove_hex_prefix(value).rjust(64, "0"))
132
+ def hex_to_bytes(value, length = 64)
133
+ AppChain::Utils.to_bytes(AppChain::Utils.remove_hex_prefix(value).rjust(length, "0"))
76
134
  end
77
135
  end
78
136
  end
@@ -15,6 +15,15 @@ module AppChain
15
15
  HEX_PREFIX + hex
16
16
  end
17
17
 
18
+ # add `0x` prefix to not blank hex string
19
+ #
20
+ # @param hex [String]
21
+ def add_prefix_for_not_blank(hex)
22
+ return add_hex_prefix(hex) unless hex.blank?
23
+
24
+ hex
25
+ end
26
+
18
27
  # remove `0x` prefix from hex string
19
28
  #
20
29
  # @param hex [String]
@@ -55,7 +64,10 @@ module AppChain
55
64
  # @param bytes_str [String] byte code string
56
65
  # @return [String] normal string
57
66
  def from_bytes(bytes_str)
58
- AppChain::Utils.add_hex_prefix(bytes_str.unpack1("H*"))
67
+ hex = bytes_str.unpack1("H*")
68
+ return AppChain::Utils.add_hex_prefix(hex) unless hex.blank?
69
+
70
+ hex
59
71
  end
60
72
 
61
73
  # keccak 256 hash
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppChain
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appchain.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - classicalliu
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-10-26 00:00:00.000000000 Z
11
+ date: 2018-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -219,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
219
219
  version: '0'
220
220
  requirements: []
221
221
  rubyforge_project:
222
- rubygems_version: 2.7.6
222
+ rubygems_version: 2.7.7
223
223
  signing_key:
224
224
  specification_version: 4
225
225
  summary: Ruby Nervos AppChain SDK