sigma_rb 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +9 -0
- data/ext/Rakefile +7 -0
- data/ext/csigma.c +5 -0
- data/lib/sigma/address.rb +73 -0
- data/lib/sigma/block_header.rb +197 -0
- data/lib/sigma/box_selection.rb +105 -0
- data/lib/sigma/byte_array.rb +96 -0
- data/lib/sigma/constant.rb +117 -0
- data/lib/sigma/context_extension.rb +47 -0
- data/lib/sigma/contract.rb +70 -0
- data/lib/sigma/data_input.rb +100 -0
- data/lib/sigma/enums.rb +35 -0
- data/lib/sigma/ergo_box.rb +556 -0
- data/lib/sigma/ergo_box_candidate_builder.rb +123 -0
- data/lib/sigma/ergo_state_context.rb +41 -0
- data/lib/sigma/ergo_tree.rb +111 -0
- data/lib/sigma/input.rb +249 -0
- data/lib/sigma/merkle_proof.rb +79 -0
- data/lib/sigma/nipopow.rb +155 -0
- data/lib/sigma/pre_header.rb +38 -0
- data/lib/sigma/reduced_transaction.rb +88 -0
- data/lib/sigma/secret_key.rb +119 -0
- data/lib/sigma/structs.rb +31 -0
- data/lib/sigma/token.rb +225 -0
- data/lib/sigma/transaction.rb +365 -0
- data/lib/sigma/tx_builder.rb +116 -0
- data/lib/sigma/util.rb +27 -0
- data/lib/sigma/wallet.rb +97 -0
- data/lib/sigma.rb +32 -0
- data/sigma.gemspec +20 -0
- data/tests/all.rb +44 -0
- data/tests/sigma/address_test.rb +45 -0
- data/tests/sigma/block_header_test.rb +35 -0
- data/tests/sigma/box_selection_test.rb +78 -0
- data/tests/sigma/constant_test.rb +57 -0
- data/tests/sigma/context_extension_test.rb +16 -0
- data/tests/sigma/contract_test.rb +39 -0
- data/tests/sigma/data_input_test.rb +38 -0
- data/tests/sigma/ergo_box_candidate_builder_test.rb +76 -0
- data/tests/sigma/ergo_box_test.rb +219 -0
- data/tests/sigma/ergo_state_context_test.rb +26 -0
- data/tests/sigma/ergo_tree_test.rb +66 -0
- data/tests/sigma/input_test.rb +41 -0
- data/tests/sigma/merkle_proof_test.rb +34 -0
- data/tests/sigma/nipopow_test.rb +100 -0
- data/tests/sigma/secret_key_test.rb +31 -0
- data/tests/sigma/token_test.rb +87 -0
- data/tests/sigma/transaction_test.rb +438 -0
- data/tests/sigma_test.rb +18 -0
- data/tests/test_seeds.rb +35 -0
- data/tests/test_utils.rb +11 -0
- 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
|
data/tests/sigma_test.rb
ADDED
@@ -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
|
+
|
data/tests/test_seeds.rb
ADDED
@@ -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
|
data/tests/test_utils.rb
ADDED
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
|