sigma_rb 0.1.3

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.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +9 -0
  4. data/ext/Rakefile +7 -0
  5. data/ext/csigma.c +5 -0
  6. data/lib/sigma/address.rb +73 -0
  7. data/lib/sigma/block_header.rb +197 -0
  8. data/lib/sigma/box_selection.rb +105 -0
  9. data/lib/sigma/byte_array.rb +96 -0
  10. data/lib/sigma/constant.rb +117 -0
  11. data/lib/sigma/context_extension.rb +47 -0
  12. data/lib/sigma/contract.rb +70 -0
  13. data/lib/sigma/data_input.rb +100 -0
  14. data/lib/sigma/enums.rb +35 -0
  15. data/lib/sigma/ergo_box.rb +556 -0
  16. data/lib/sigma/ergo_box_candidate_builder.rb +123 -0
  17. data/lib/sigma/ergo_state_context.rb +41 -0
  18. data/lib/sigma/ergo_tree.rb +111 -0
  19. data/lib/sigma/input.rb +249 -0
  20. data/lib/sigma/merkle_proof.rb +79 -0
  21. data/lib/sigma/nipopow.rb +155 -0
  22. data/lib/sigma/pre_header.rb +38 -0
  23. data/lib/sigma/reduced_transaction.rb +88 -0
  24. data/lib/sigma/secret_key.rb +119 -0
  25. data/lib/sigma/structs.rb +31 -0
  26. data/lib/sigma/token.rb +225 -0
  27. data/lib/sigma/transaction.rb +365 -0
  28. data/lib/sigma/tx_builder.rb +116 -0
  29. data/lib/sigma/util.rb +27 -0
  30. data/lib/sigma/wallet.rb +97 -0
  31. data/lib/sigma.rb +32 -0
  32. data/sigma.gemspec +20 -0
  33. data/tests/all.rb +44 -0
  34. data/tests/sigma/address_test.rb +45 -0
  35. data/tests/sigma/block_header_test.rb +35 -0
  36. data/tests/sigma/box_selection_test.rb +78 -0
  37. data/tests/sigma/constant_test.rb +57 -0
  38. data/tests/sigma/context_extension_test.rb +16 -0
  39. data/tests/sigma/contract_test.rb +39 -0
  40. data/tests/sigma/data_input_test.rb +38 -0
  41. data/tests/sigma/ergo_box_candidate_builder_test.rb +76 -0
  42. data/tests/sigma/ergo_box_test.rb +219 -0
  43. data/tests/sigma/ergo_state_context_test.rb +26 -0
  44. data/tests/sigma/ergo_tree_test.rb +66 -0
  45. data/tests/sigma/input_test.rb +41 -0
  46. data/tests/sigma/merkle_proof_test.rb +34 -0
  47. data/tests/sigma/nipopow_test.rb +100 -0
  48. data/tests/sigma/secret_key_test.rb +31 -0
  49. data/tests/sigma/token_test.rb +87 -0
  50. data/tests/sigma/transaction_test.rb +438 -0
  51. data/tests/sigma_test.rb +18 -0
  52. data/tests/test_seeds.rb +35 -0
  53. data/tests/test_utils.rb +11 -0
  54. metadata +174 -0
@@ -0,0 +1,438 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'test/unit'
4
+ require 'json'
5
+
6
+ require_relative '../../lib/sigma.rb'
7
+ require_relative '../test_utils.rb'
8
+ include Sigma
9
+
10
+ class Transaction::Test < Test::Unit::TestCase
11
+ def test_tx_id
12
+ str = '93d344aa527e18e5a221db060ea1a868f46b61e4537e6e5f69ecc40334c15e38'
13
+ tx_id = TxId.with_string(str)
14
+ assert_equal(str, tx_id.to_s)
15
+ end
16
+
17
+ def test_tx_builder
18
+ tn_address = '3WvsT2Gm4EpsM9Pg18PdY6XyhNNMqXDsvJTbbf6ihLvAmSb7u5RN'
19
+ box_id_str = "e56847ed19b3dc6b72828fcfb992fdf7310828cf291221269b7ffc72fd66706e"
20
+ box_value_int = 67500000000
21
+ ergo_tree_encoded_str = "100204a00b08cd021dde34603426402615658f1d970cfa7c7bd92ac81a8b16eeebff264d59ce4604ea02d192a39a8cc7a70173007301"
22
+ creation_height = 284761
23
+ tx_id_str = "9148408c04c2e38a6402a7950d6157730fa7d49e9ab3b9cadec481d7769918e9"
24
+ index = 1
25
+ payload = {
26
+ 'boxId' => box_id_str,
27
+ 'value' => box_value_int,
28
+ 'ergoTree' => ergo_tree_encoded_str,
29
+ 'assets' => [],
30
+ 'creationHeight' => creation_height,
31
+ 'additionalRegisters' => {},
32
+ 'transactionId' => tx_id_str,
33
+ 'index' => index
34
+ }
35
+ json_str = payload.to_json
36
+
37
+ recipient = Address.with_testnet_address(tn_address)
38
+ unspent_boxes = ErgoBoxes.from_json([json_str])
39
+ contract = Contract.pay_to_address(recipient)
40
+ outbox_value = BoxValue.safe_user_min
41
+ outbox = ErgoBoxCandidateBuilder.create(box_value: outbox_value, contract: contract, creation_height: 0).build
42
+ tx_outputs = ErgoBoxCandidates.create
43
+ tx_outputs.add(outbox)
44
+ fee = TxBuilder.suggested_tx_fee
45
+ change_address = Address.with_testnet_address(tn_address)
46
+ min_change_value = BoxValue.safe_user_min
47
+ data_inputs = DataInputs.create
48
+ box_selector = SimpleBoxSelector.create
49
+ target_balance = BoxValue.sum_of(outbox_value, fee)
50
+ tokens = Tokens.create
51
+ box_selection = box_selector.select(inputs: unspent_boxes, target_balance: target_balance, target_tokens: tokens)
52
+ tx_builder = TxBuilder.create(
53
+ box_selection: box_selection,
54
+ output_candidates: tx_outputs,
55
+ current_height: 0,
56
+ fee_amount: fee,
57
+ change_address: change_address,
58
+ min_change_value: min_change_value
59
+ )
60
+ tx_builder.set_data_inputs(data_inputs)
61
+ tx = tx_builder.build
62
+ assert_nothing_raised do
63
+ tx.to_json_eip12
64
+ end
65
+ end
66
+
67
+ def test_sign_transaction
68
+ sk = SecretKey.create
69
+ input_contract = Contract.pay_to_address(sk.get_address)
70
+ str = "93d344aa527e18e5a221db060ea1a868f46b61e4537e6e5f69ecc40334c15e38"
71
+ tx_id = TxId.with_string(str)
72
+ bv = BoxValue.from_i64(1000000000)
73
+ creation_height = 0
74
+ index = 0
75
+ tokens = Tokens.create
76
+ input_box = ErgoBox.create(box_value: bv, creation_height: creation_height, contract: input_contract, tx_id: tx_id, index: index, tokens: tokens)
77
+
78
+ # Create transaction that spends the 'simulated' box
79
+ tn_address_str = "3WvsT2Gm4EpsM9Pg18PdY6XyhNNMqXDsvJTbbf6ihLvAmSb7u5RN"
80
+ recipient = Address.with_testnet_address(tn_address_str)
81
+ unspent_boxes = ErgoBoxes.create
82
+ unspent_boxes.add(input_box)
83
+ contract = Contract.pay_to_address(recipient)
84
+ outbox_value = BoxValue.safe_user_min
85
+ outbox = ErgoBoxCandidateBuilder.create(box_value: outbox_value, contract: contract, creation_height: creation_height).build
86
+ tx_outputs = ErgoBoxCandidates.create
87
+ tx_outputs.add(outbox)
88
+ fee = TxBuilder.suggested_tx_fee
89
+ change_address = Address.with_testnet_address(tn_address_str)
90
+ min_change_value = BoxValue.safe_user_min
91
+ data_inputs = DataInputs.create
92
+ box_selector = SimpleBoxSelector.create
93
+ target_balance = BoxValue.sum_of(outbox_value, fee)
94
+ empty_tokens = Tokens.create
95
+ box_selection = box_selector.select(inputs: unspent_boxes, target_balance: target_balance, target_tokens: empty_tokens)
96
+ tx_builder = TxBuilder.create(
97
+ box_selection: box_selection,
98
+ output_candidates: tx_outputs,
99
+ current_height: creation_height,
100
+ fee_amount: fee,
101
+ change_address: change_address,
102
+ min_change_value: min_change_value)
103
+ tx_builder.set_data_inputs(data_inputs)
104
+ tx = tx_builder.build
105
+ assert_nothing_raised do
106
+ tx.to_json_eip12
107
+ end
108
+ tx_data_inputs = ErgoBoxes.from_json([])
109
+ block_headers = TestSeeds.block_headers_from_json
110
+ pre_header = PreHeader.with_block_header(block_headers.get(0))
111
+ ctx = ErgoStateContext.create(pre_header: pre_header, headers: block_headers)
112
+ secret_keys = SecretKeys.create
113
+ secret_keys.add(sk)
114
+ wallet = Wallet.create_from_secrets(secret_keys)
115
+ signed_tx = wallet.sign_transaction(state_context: ctx, unsigned_tx: tx, boxes_to_spend: unspent_boxes, data_boxes: tx_data_inputs)
116
+ assert_nothing_raised do
117
+ signed_tx.to_json_eip12
118
+ end
119
+ end
120
+
121
+ def test_mint_token
122
+ tn_addr = "3WvsT2Gm4EpsM9Pg18PdY6XyhNNMqXDsvJTbbf6ihLvAmSb7u5RN"
123
+ recipient = Address.with_testnet_address(tn_addr)
124
+ eb_json = {
125
+ boxId: "e56847ed19b3dc6b72828fcfb992fdf7310828cf291221269b7ffc72fd66706e",
126
+ value: 67500000000,
127
+ ergoTree: "100204a00b08cd021dde34603426402615658f1d970cfa7c7bd92ac81a8b16eeebff264d59ce4604ea02d192a39a8cc7a70173007301",
128
+ assets: [],
129
+ creationHeight: 284761,
130
+ additionalRegisters: {},
131
+ transactionId: "9148408c04c2e38a6402a7950d6157730fa7d49e9ab3b9cadec481d7769918e9",
132
+ index: 1
133
+ }.to_json
134
+ unspent_boxes = ErgoBoxes.from_json([eb_json])
135
+ contract = Contract.pay_to_address(recipient)
136
+ outbox_value = BoxValue.safe_user_min
137
+ fee = TxBuilder.suggested_tx_fee
138
+ box_selector = SimpleBoxSelector.create
139
+ target_balance = BoxValue.sum_of(outbox_value, fee)
140
+ box_selection = box_selector.select(inputs: unspent_boxes, target_balance: target_balance, target_tokens: Tokens.create)
141
+ # mint token
142
+ token_id = TokenId.with_box_id(box_selection.get_boxes.get(0).get_box_id)
143
+ token = Token.create(token_id: token_id, token_amount: TokenAmount.with_i64(1))
144
+ box_builder = ErgoBoxCandidateBuilder.create(box_value: outbox_value, contract: contract, creation_height: 0).mint_token(token: token, name: "TKN", description: "token desc", num_decimals: 2)
145
+ outbox = box_builder.build
146
+ tx_outputs = ErgoBoxCandidates.create
147
+ tx_outputs.add(outbox)
148
+ change_address = Address.with_testnet_address(tn_addr)
149
+ min_change_value = BoxValue.safe_user_min
150
+ data_inputs = DataInputs.create
151
+ tx_builder = TxBuilder.create(box_selection: box_selection, output_candidates: tx_outputs, current_height: 0, fee_amount: fee, change_address: change_address, min_change_value: min_change_value)
152
+ tx_builder.set_data_inputs(data_inputs)
153
+ tx = tx_builder.build
154
+ assert_nothing_raised do
155
+ tx.to_json_eip12
156
+ end
157
+ end
158
+
159
+ def test_burn_token
160
+ eb_json =
161
+ {
162
+ boxId: "0cf7b9e71961cc473242de389c8e594a4e5d630ddd2e4e590083fb0afb386341",
163
+ value: 11491500000,
164
+ ergoTree: "100f040005c801056404000e2019719268d230fd9093e4db0e2e42a07883ffe976e77c7419efc1bb218a05d4ba04000500043c040204c096b10204020101040205c096b1020400d805d601b2a5730000d602e4c6a70405d6039c9d720273017302d604b5db6501fed9010463ededed93e4c67204050ec5a7938cb2db6308720473030001730492e4c672040605997202720390e4c6720406059a72027203d605b17204ea02d1edededededed93cbc27201e4c6a7060e917205730593db63087201db6308a793e4c6720104059db072047306d9010641639a8c720601e4c68c72060206057e72050593e4c6720105049ae4c6a70504730792c1720199c1a77e9c9a720573087309058cb072048602730a730bd901063c400163d802d6088c720601d6098c72080186029a7209730ceded8c72080293c2b2a5720900d0cde4c68c720602040792c1b2a5720900730d02b2ad7204d9010663cde4c672060407730e00",
165
+ assets: [
166
+ {
167
+ tokenId: "19475d9a78377ff0f36e9826cec439727bea522f6ffa3bda32e20d2f8b3103ac",
168
+ amount: 1
169
+ }
170
+ ],
171
+ creationHeight: 348198,
172
+ additionalRegisters: {
173
+ R4: "059acd9109",
174
+ R5: "04f2c02a",
175
+ R6: "0e20277c78751ff6f68d4dcd082eeea9506324911a875b6b9cd4d177d4fcab061327"
176
+ },
177
+ transactionId: "5ed0e572a8c097b053965519a696f413f7be02754345e8ed650377e29a6dedb3",
178
+ index: 0
179
+ }.to_json
180
+ unspent_boxes = ErgoBoxes.from_json([eb_json])
181
+ tn_address = "3WvsT2Gm4EpsM9Pg18PdY6XyhNNMqXDsvJTbbf6ihLvAmSb7u5RN"
182
+ recipient = Address.with_testnet_address(tn_address)
183
+ token_id = TokenId.from_base16_encoded_string("19475d9a78377ff0f36e9826cec439727bea522f6ffa3bda32e20d2f8b3103ac")
184
+ token = Token.create(token_id: token_id, token_amount: TokenAmount.with_i64(1))
185
+ box_selector = SimpleBoxSelector.create
186
+ tokens = Tokens.create
187
+ tokens.add(token)
188
+ outbox_value = BoxValue.safe_user_min
189
+ fee = TxBuilder.suggested_tx_fee
190
+ target_balance = BoxValue.sum_of(outbox_value, fee)
191
+ box_selection = box_selector.select(inputs: unspent_boxes, target_balance: target_balance, target_tokens: tokens)
192
+ # Select tokens from inputs
193
+ contract = Contract.pay_to_address(recipient)
194
+ # but don't put selected tokens in the output box (burn them)
195
+ box_builder = ErgoBoxCandidateBuilder.create(box_value: outbox_value, contract: contract, creation_height: 0)
196
+ outbox = box_builder.build
197
+ tx_outputs = ErgoBoxCandidates.create
198
+ tx_outputs.add(outbox)
199
+ change_address = Address.with_testnet_address(tn_address)
200
+ min_change_value = BoxValue.safe_user_min
201
+ data_inputs = DataInputs.create
202
+ tx_builder = TxBuilder.create(box_selection: box_selection, output_candidates: tx_outputs, current_height: 0, fee_amount: fee, change_address: change_address, min_change_value: min_change_value)
203
+ tx_builder.set_data_inputs(data_inputs)
204
+ assert_nothing_raised do
205
+ tx_builder.build()
206
+ end
207
+ end
208
+
209
+ def test_using_signed_tx_as_input_in_new_tx
210
+ sk = SecretKey.create
211
+ input_contract = Contract.pay_to_address(sk.get_address)
212
+ str = "0000000000000000000000000000000000000000000000000000000000000000"
213
+ tx_id = TxId.with_string(str)
214
+ input_box_bv = BoxValue.from_i64(100000000000)
215
+ input_box_tokens = Tokens.create
216
+ input_box = ErgoBox.create(box_value: input_box_bv, creation_height: 0, contract: input_contract, tx_id: tx_id, index: 0, tokens: input_box_tokens)
217
+ # Create transaction that spends the 'simulated' box
218
+ tn_address = "3WvsT2Gm4EpsM9Pg18PdY6XyhNNMqXDsvJTbbf6ihLvAmSb7u5RN"
219
+ recipient = Address.with_testnet_address(tn_address)
220
+ unspent_boxes = ErgoBoxes.create
221
+ unspent_boxes.add(input_box)
222
+ contract = Contract.pay_to_address(recipient)
223
+ outbox_value = BoxValue.from_i64(10000000000)
224
+ outbox = ErgoBoxCandidateBuilder.create(box_value: outbox_value, contract: contract, creation_height: 0).build
225
+ tx_outputs = ErgoBoxCandidates.create
226
+ tx_outputs.add(outbox)
227
+ fee = TxBuilder.suggested_tx_fee
228
+ change_address = Address.with_testnet_address(tn_address)
229
+ min_change_value = BoxValue.safe_user_min
230
+ data_inputs = DataInputs.create
231
+ box_selector = SimpleBoxSelector.create
232
+ target_balance = BoxValue.sum_of(outbox_value, fee)
233
+ target_tokens = Tokens.create
234
+ box_selection = box_selector.select(inputs: unspent_boxes, target_balance: target_balance, target_tokens: target_tokens)
235
+ tx_builder = TxBuilder.create(box_selection: box_selection, output_candidates: tx_outputs, current_height: 0, fee_amount: fee, change_address: change_address, min_change_value: min_change_value)
236
+ tx_builder.set_data_inputs(data_inputs)
237
+ tx = tx_builder.build
238
+ assert_nothing_raised do
239
+ tx.to_json_eip12
240
+ end
241
+ tx_data_inputs = ErgoBoxes.from_json([])
242
+ block_headers = TestSeeds.block_headers_from_json
243
+ pre_header = PreHeader.with_block_header(block_headers.get(0))
244
+ ctx = ErgoStateContext.create(pre_header: pre_header, headers: block_headers)
245
+ secret_keys = SecretKeys.create
246
+ secret_keys.add(sk)
247
+ wallet = Wallet.create_from_secrets(secret_keys)
248
+ signed_tx = wallet.sign_transaction(state_context: ctx, unsigned_tx: tx, boxes_to_spend: unspent_boxes, data_boxes: tx_data_inputs)
249
+ assert_equal(10000000000, signed_tx.get_outputs.get(0).get_box_value.to_i64)
250
+ assert_nothing_raised do
251
+ signed_tx.to_json_eip12
252
+ end
253
+ # New tx
254
+ new_outbox_value = BoxValue.from_i64(1000000000)
255
+ new_outbox = ErgoBoxCandidateBuilder.create(box_value: new_outbox_value, contract: contract, creation_height: 0).build
256
+ new_tx_outputs = ErgoBoxCandidates.create
257
+ new_tx_outputs.add(new_outbox)
258
+ new_box_selector = SimpleBoxSelector.create
259
+ new_target_balance = BoxValue.sum_of(new_outbox_value, fee)
260
+ new_box_selection = new_box_selector.select(inputs: signed_tx.get_outputs, target_balance: new_target_balance, target_tokens: Tokens.create)
261
+ new_tx_builder = TxBuilder.create(
262
+ box_selection: new_box_selection,
263
+ output_candidates: new_tx_outputs,
264
+ current_height: 0,
265
+ fee_amount: fee,
266
+ change_address: change_address,
267
+ min_change_value: min_change_value
268
+ )
269
+
270
+ assert_nothing_raised do
271
+ new_tx_builder.build
272
+ end
273
+ end
274
+
275
+ def test_tx_from_unsigned_tx
276
+ tn_address = "3WvsT2Gm4EpsM9Pg18PdY6XyhNNMqXDsvJTbbf6ihLvAmSb7u5RN"
277
+ recipient = Address.with_testnet_address(tn_address)
278
+ eb_json =
279
+ {
280
+ boxId: "e56847ed19b3dc6b72828fcfb992fdf7310828cf291221269b7ffc72fd66706e",
281
+ value: 67500000000,
282
+ ergoTree: "100204a00b08cd021dde34603426402615658f1d970cfa7c7bd92ac81a8b16eeebff264d59ce4604ea02d192a39a8cc7a70173007301",
283
+ assets: [],
284
+ creationHeight: 284761,
285
+ additionalRegisters: {},
286
+ transactionId: "9148408c04c2e38a6402a7950d6157730fa7d49e9ab3b9cadec481d7769918e9",
287
+ index: 1
288
+ }.to_json
289
+ unspent_boxes = ErgoBoxes.from_json([eb_json])
290
+ contract = Contract.pay_to_address(recipient)
291
+ outbox_value = BoxValue.safe_user_min
292
+ outbox = ErgoBoxCandidateBuilder.create(box_value: outbox_value, contract: contract, creation_height: 0).build
293
+ tx_outputs = ErgoBoxCandidates.create
294
+ tx_outputs.add(outbox)
295
+ fee = TxBuilder.suggested_tx_fee
296
+ change_address = Address.with_testnet_address(tn_address)
297
+ min_change_value = BoxValue.safe_user_min
298
+ data_inputs = DataInputs.create
299
+ box_selector = SimpleBoxSelector.create
300
+ target_balance = BoxValue.sum_of(outbox_value, fee)
301
+ box_selection = box_selector.select(inputs: unspent_boxes, target_balance: target_balance, target_tokens: Tokens.create)
302
+ tx_builder = TxBuilder.create(
303
+ box_selection: box_selection,
304
+ output_candidates: tx_outputs,
305
+ current_height: 0,
306
+ fee_amount: fee,
307
+ change_address: change_address,
308
+ min_change_value: min_change_value
309
+ )
310
+ tx_builder.set_data_inputs(data_inputs)
311
+ tx = tx_builder.build
312
+ bytes = [1, 1, 2, 255]
313
+ proof = ByteArray.from_bytes(bytes)
314
+ proofs = ByteArrays.create
315
+ proofs.add(proof)
316
+ signed_tx = Transaction.create_from_unsigned_tx(unsigned_tx: tx, proofs: proofs)
317
+ assert_equal(bytes, signed_tx.get_inputs.get(0).get_spending_proof.to_bytes)
318
+ end
319
+
320
+ def test_wallet_mnemonic
321
+ phrase = "change me do not use me change me do not use me"
322
+ pass = "password1234"
323
+ assert_nothing_raised do
324
+ Wallet.create_from_mnemonic(phrase, pass)
325
+ end
326
+ end
327
+
328
+ def test_multi_sig_tx
329
+ alice_base16_str = "e726ad60a073a49f7851f4d11a83de6c9c7f99e17314fcce560f00a51a8a3d18"
330
+ alice_bytes = TestUtils.base16_string_to_bytes(alice_base16_str)
331
+ alice_secret = SecretKey.from_bytes(alice_bytes)
332
+ alice_pk_str = "cd03c8e1527efae4be9868cea6767157fcccac66489842738efed0a302e4f81710d0"
333
+ alice_pk_bytes = TestUtils.base16_string_to_bytes(alice_pk_str)
334
+
335
+ bob_base16_str = "9e6616b4e44818d21b8dfdd5ea87eb822480e7856ab910d00f5834dc64db79b3"
336
+ bob_bytes = TestUtils.base16_string_to_bytes(bob_base16_str)
337
+ bob_secret = SecretKey.from_bytes(bob_bytes)
338
+
339
+ # Pay 2 Script address of a multi_sig contract with contract { alicePK && bobPK }
340
+ p2k_addr = "JryiCXrc7x5D8AhS9DYX1TDzW5C5mT6QyTMQaptF76EQkM15cetxtYKq3u6LymLZLVCyjtgbTKFcfuuX9LLi49Ec5m2p6cwsg5NyEsCQ7na83yEPN"
341
+ multi_sig_address = Address.with_testnet_address(p2k_addr)
342
+ input_contract = Contract.pay_to_address(multi_sig_address)
343
+ tx_id = TxId.with_string("0000000000000000000000000000000000000000000000000000000000000000")
344
+ input_box = ErgoBox.create(
345
+ box_value: BoxValue.from_i64(1000000000),
346
+ creation_height: 0,
347
+ contract: input_contract,
348
+ tx_id: tx_id,
349
+ index: 0,
350
+ tokens: Tokens.create
351
+ )
352
+ # create a transaction that spends the "simulated" box
353
+ tn_address = "3WvsT2Gm4EpsM9Pg18PdY6XyhNNMqXDsvJTbbf6ihLvAmSb7u5RN"
354
+ recipient = Address.with_testnet_address(tn_address)
355
+ unspent_boxes = ErgoBoxes.create
356
+ unspent_boxes.add(input_box)
357
+ contract = Contract.pay_to_address(recipient)
358
+ outbox_value = BoxValue.safe_user_min
359
+ outbox = ErgoBoxCandidateBuilder.create(
360
+ box_value: outbox_value,
361
+ contract: contract,
362
+ creation_height: 0
363
+ ).build
364
+ tx_outputs = ErgoBoxCandidates.create
365
+ tx_outputs.add(outbox)
366
+ fee = TxBuilder.suggested_tx_fee
367
+ change_address = Address.with_testnet_address(tn_address)
368
+ min_change_value = BoxValue.safe_user_min
369
+ box_selector = SimpleBoxSelector.create
370
+ target_balance = BoxValue.sum_of(outbox_value, fee)
371
+ box_selection = box_selector.select(
372
+ inputs: unspent_boxes,
373
+ target_balance: target_balance,
374
+ target_tokens: Tokens.create
375
+ )
376
+ tx_builder = TxBuilder.create(
377
+ box_selection: box_selection,
378
+ output_candidates: tx_outputs,
379
+ current_height: 0,
380
+ fee_amount: fee,
381
+ change_address: change_address,
382
+ min_change_value: min_change_value
383
+ )
384
+ tx = tx_builder.build
385
+ tx_data_inputs = ErgoBoxes.from_json([])
386
+ block_headers = TestSeeds.block_headers_from_json
387
+ pre_header = PreHeader.with_block_header(block_headers.get(0))
388
+ ctx = ErgoStateContext.create(pre_header: pre_header, headers: block_headers)
389
+ sks_alice = SecretKeys.create
390
+ sks_alice.add(alice_secret)
391
+ wallet_alice = Wallet.create_from_secrets(sks_alice)
392
+ sks_bob = SecretKeys.create
393
+ sks_bob.add(bob_secret)
394
+ wallet_bob = Wallet.create_from_secrets(sks_bob)
395
+ bob_hints = wallet_bob.generate_commitments(
396
+ state_context: ctx,
397
+ unsigned_tx: tx,
398
+ boxes_to_spend: unspent_boxes,
399
+ data_boxes: tx_data_inputs
400
+ ).all_hints_for_input(0)
401
+ bob_known = bob_hints.get_commitment_hint(0)
402
+ bob_own = bob_hints.get_commitment_hint(1)
403
+ hints_bag = HintsBag.create
404
+ hints_bag.add_commitment_hint(bob_known)
405
+ alice_tx_hints_bag = TransactionHintsBag.create
406
+ alice_tx_hints_bag.add_hints_for_input(index: 0, hints_bag: hints_bag)
407
+ partial_signed = wallet_alice.sign_transaction_multi(
408
+ state_context: ctx,
409
+ unsigned_tx: tx,
410
+ boxes_to_spend: unspent_boxes,
411
+ data_boxes: tx_data_inputs,
412
+ tx_hints: alice_tx_hints_bag
413
+ )
414
+ real_propositions = Propositions.create
415
+ simulated_propositions = Propositions.create
416
+ real_propositions.add_proposition(alice_pk_bytes)
417
+ bob_hints_bag = TransactionHintsBag.extract_hints_from_signed_transaction(
418
+ transaction: partial_signed,
419
+ state_context: ctx,
420
+ boxes_to_spend: unspent_boxes,
421
+ data_boxes: tx_data_inputs,
422
+ real_propositions: real_propositions,
423
+ simulated_propositions: simulated_propositions
424
+ ).all_hints_for_input(0)
425
+ bob_hints_bag.add_commitment_hint(bob_own)
426
+ bob_tx_hints_bag = TransactionHintsBag.create
427
+ bob_tx_hints_bag.add_hints_for_input(index: 0, hints_bag: bob_hints_bag)
428
+ assert_nothing_raised do
429
+ stx = wallet_bob.sign_transaction_multi(
430
+ state_context: ctx,
431
+ unsigned_tx: tx,
432
+ boxes_to_spend: unspent_boxes,
433
+ data_boxes: tx_data_inputs,
434
+ tx_hints: bob_tx_hints_bag
435
+ )
436
+ end
437
+ end
438
+ end
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'test/unit'
4
+
5
+ require_relative '../lib/sigma.rb'
6
+ require_relative 'test_utils.rb'
7
+
8
+ class Sigma::Test < Test::Unit::TestCase
9
+ def test_non_mandator_register_ids
10
+ assert_equal(Sigma::REGISTER_ID_ENUM[:r4], 4)
11
+ assert_equal(Sigma::REGISTER_ID_ENUM[:r5], 5)
12
+ assert_equal(Sigma::REGISTER_ID_ENUM[:r6], 6)
13
+ assert_equal(Sigma::REGISTER_ID_ENUM[:r7], 7)
14
+ assert_equal(Sigma::REGISTER_ID_ENUM[:r8], 8)
15
+ assert_equal(Sigma::REGISTER_ID_ENUM[:r9], 9)
16
+ end
17
+ end
18
+
@@ -0,0 +1,35 @@
1
+ require_relative '../lib/sigma'
2
+
3
+ module TestSeeds
4
+ def self.block_header_json
5
+ header = {
6
+ extensionId: "d16f25b14457186df4c5f6355579cc769261ce1aebc8209949ca6feadbac5a3f",
7
+ difficulty: "626412390187008",
8
+ votes: "040000",
9
+ timestamp: 1618929697400,
10
+ size: 221,
11
+ stateRoot: "8ad868627ea4f7de6e2a2fe3f98fafe57f914e0f2ef3331c006def36c697f92713",
12
+ height: 471746,
13
+ nBits: 117586360,
14
+ version: 2,
15
+ id: "4caa17e62fe66ba7bd69597afdc996ae35b1ff12e0ba90c22ff288a4de10e91b",
16
+ adProofsRoot: "d882aaf42e0a95eb95fcce5c3705adf758e591532f733efe790ac3c404730c39",
17
+ transactionsRoot: "63eaa9aff76a1de3d71c81e4b2d92e8d97ae572a8e9ab9e66599ed0912dd2f8b",
18
+ extensionHash: "3f91f3c680beb26615fdec251aee3f81aaf5a02740806c167c0f3c929471df44",
19
+ powSolutions: {
20
+ pk: "02b3a06d6eaa8671431ba1db4dd427a77f75a5c2acbd71bfb725d38adc2b55f669",
21
+ w: "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
22
+ n: "5939ecfee6b0d7f4",
23
+ d: 0
24
+ },
25
+ adProofsId: "86eaa41f328bee598e33e52c9e515952ad3b7874102f762847f17318a776a7ae",
26
+ transactionsId: "ac80245714f25aa2fafe5494ad02a26d46e7955b8f5709f3659f1b9440797b3e",
27
+ parentId: "6481752bace5fa5acba5d5ef7124d48826664742d46c974c98a2d60ace229a34"
28
+ }.to_json
29
+ end
30
+
31
+ def self.block_headers_from_json
32
+ headers = Array.new(10) { block_header_json }
33
+ Sigma::BlockHeaders.from_json(headers)
34
+ end
35
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'test_seeds'
2
+
3
+ module TestUtils
4
+ def self.base16_string_to_bytes(str)
5
+ bytes = []
6
+ str.split(//).each_slice(2) do |p|
7
+ bytes << p.join.to_i(16)
8
+ end
9
+ bytes
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,174 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sigma_rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ platform: ruby
6
+ authors:
7
+ - Dark Lord of Programming
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-05-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ffi-compiler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.0.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ffi
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.15.5
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.15.5
55
+ - !ruby/object:Gem::Dependency
56
+ name: test-unit
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.5'
69
+ description: Ruby bindings for the Ergo-Lib crate of Sigma-Rust. Specifically for
70
+ chain types and abstractions, json serialization, box selection for tx inputs, tx
71
+ creation, and signing.
72
+ email: thedlop@sent.com
73
+ executables: []
74
+ extensions:
75
+ - ext/Rakefile
76
+ extra_rdoc_files: []
77
+ files:
78
+ - LICENSE
79
+ - README.md
80
+ - ext/Rakefile
81
+ - ext/csigma.c
82
+ - lib/sigma.rb
83
+ - lib/sigma/address.rb
84
+ - lib/sigma/block_header.rb
85
+ - lib/sigma/box_selection.rb
86
+ - lib/sigma/byte_array.rb
87
+ - lib/sigma/constant.rb
88
+ - lib/sigma/context_extension.rb
89
+ - lib/sigma/contract.rb
90
+ - lib/sigma/data_input.rb
91
+ - lib/sigma/enums.rb
92
+ - lib/sigma/ergo_box.rb
93
+ - lib/sigma/ergo_box_candidate_builder.rb
94
+ - lib/sigma/ergo_state_context.rb
95
+ - lib/sigma/ergo_tree.rb
96
+ - lib/sigma/input.rb
97
+ - lib/sigma/merkle_proof.rb
98
+ - lib/sigma/nipopow.rb
99
+ - lib/sigma/pre_header.rb
100
+ - lib/sigma/reduced_transaction.rb
101
+ - lib/sigma/secret_key.rb
102
+ - lib/sigma/structs.rb
103
+ - lib/sigma/token.rb
104
+ - lib/sigma/transaction.rb
105
+ - lib/sigma/tx_builder.rb
106
+ - lib/sigma/util.rb
107
+ - lib/sigma/wallet.rb
108
+ - sigma.gemspec
109
+ - tests/all.rb
110
+ - tests/sigma/address_test.rb
111
+ - tests/sigma/block_header_test.rb
112
+ - tests/sigma/box_selection_test.rb
113
+ - tests/sigma/constant_test.rb
114
+ - tests/sigma/context_extension_test.rb
115
+ - tests/sigma/contract_test.rb
116
+ - tests/sigma/data_input_test.rb
117
+ - tests/sigma/ergo_box_candidate_builder_test.rb
118
+ - tests/sigma/ergo_box_test.rb
119
+ - tests/sigma/ergo_state_context_test.rb
120
+ - tests/sigma/ergo_tree_test.rb
121
+ - tests/sigma/input_test.rb
122
+ - tests/sigma/merkle_proof_test.rb
123
+ - tests/sigma/nipopow_test.rb
124
+ - tests/sigma/secret_key_test.rb
125
+ - tests/sigma/token_test.rb
126
+ - tests/sigma/transaction_test.rb
127
+ - tests/sigma_test.rb
128
+ - tests/test_seeds.rb
129
+ - tests/test_utils.rb
130
+ homepage: https://github.com/thedlop/sigma_rb
131
+ licenses:
132
+ - MIT
133
+ metadata: {}
134
+ post_install_message:
135
+ rdoc_options: []
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: 3.0.1
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ requirements: []
149
+ rubygems_version: 3.2.33
150
+ signing_key:
151
+ specification_version: 4
152
+ summary: Ruby bindings for Ergo types, abstractions, and interfaces provided by Sigma-Rust.
153
+ test_files:
154
+ - tests/all.rb
155
+ - tests/sigma/address_test.rb
156
+ - tests/sigma/block_header_test.rb
157
+ - tests/sigma/box_selection_test.rb
158
+ - tests/sigma/constant_test.rb
159
+ - tests/sigma/context_extension_test.rb
160
+ - tests/sigma/contract_test.rb
161
+ - tests/sigma/data_input_test.rb
162
+ - tests/sigma/ergo_box_candidate_builder_test.rb
163
+ - tests/sigma/ergo_box_test.rb
164
+ - tests/sigma/ergo_state_context_test.rb
165
+ - tests/sigma/ergo_tree_test.rb
166
+ - tests/sigma/input_test.rb
167
+ - tests/sigma/merkle_proof_test.rb
168
+ - tests/sigma/nipopow_test.rb
169
+ - tests/sigma/secret_key_test.rb
170
+ - tests/sigma/token_test.rb
171
+ - tests/sigma/transaction_test.rb
172
+ - tests/sigma_test.rb
173
+ - tests/test_seeds.rb
174
+ - tests/test_utils.rb