sigma_rb 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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