btcruby 0.0.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +18 -0
- data/.travis.yml +7 -0
- data/FAQ.md +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +18 -0
- data/HOWTO.md +17 -0
- data/LICENSE +19 -0
- data/README.md +59 -0
- data/Rakefile +6 -0
- data/TODO.txt +40 -0
- data/bin/console +19 -0
- data/btcruby.gemspec +20 -0
- data/documentation/address.md +73 -0
- data/documentation/base58.md +52 -0
- data/documentation/block.md +127 -0
- data/documentation/block_header.md +120 -0
- data/documentation/constants.md +88 -0
- data/documentation/data.md +54 -0
- data/documentation/diagnostics.md +90 -0
- data/documentation/extensions.md +76 -0
- data/documentation/hash_functions.md +58 -0
- data/documentation/hash_id.md +22 -0
- data/documentation/index.md +230 -0
- data/documentation/key.md +177 -0
- data/documentation/keychain.md +180 -0
- data/documentation/network.md +75 -0
- data/documentation/opcode.md +220 -0
- data/documentation/openssl.md +7 -0
- data/documentation/p2pkh.md +71 -0
- data/documentation/p2sh.md +64 -0
- data/documentation/proof_of_work.md +84 -0
- data/documentation/script.md +280 -0
- data/documentation/signature.md +71 -0
- data/documentation/transaction.md +213 -0
- data/documentation/transaction_builder.md +188 -0
- data/documentation/transaction_input.md +133 -0
- data/documentation/transaction_output.md +130 -0
- data/documentation/wif.md +72 -0
- data/documentation/wire_format.md +70 -0
- data/lib/btcruby/address.rb +296 -0
- data/lib/btcruby/base58.rb +108 -0
- data/lib/btcruby/big_number.rb +47 -0
- data/lib/btcruby/block.rb +170 -0
- data/lib/btcruby/block_header.rb +231 -0
- data/lib/btcruby/constants.rb +59 -0
- data/lib/btcruby/currency_formatter.rb +64 -0
- data/lib/btcruby/data.rb +98 -0
- data/lib/btcruby/diagnostics.rb +92 -0
- data/lib/btcruby/errors.rb +8 -0
- data/lib/btcruby/extensions.rb +65 -0
- data/lib/btcruby/hash_functions.rb +54 -0
- data/lib/btcruby/hash_id.rb +18 -0
- data/lib/btcruby/key.rb +517 -0
- data/lib/btcruby/keychain.rb +464 -0
- data/lib/btcruby/network.rb +73 -0
- data/lib/btcruby/opcode.rb +197 -0
- data/lib/btcruby/open_assets/asset.rb +35 -0
- data/lib/btcruby/open_assets/asset_address.rb +49 -0
- data/lib/btcruby/open_assets/asset_definition.rb +75 -0
- data/lib/btcruby/open_assets/asset_id.rb +24 -0
- data/lib/btcruby/open_assets/asset_marker.rb +94 -0
- data/lib/btcruby/open_assets/asset_processor.rb +377 -0
- data/lib/btcruby/open_assets/asset_transaction.rb +184 -0
- data/lib/btcruby/open_assets/asset_transaction_builder/errors.rb +15 -0
- data/lib/btcruby/open_assets/asset_transaction_builder/provider.rb +32 -0
- data/lib/btcruby/open_assets/asset_transaction_builder/result.rb +47 -0
- data/lib/btcruby/open_assets/asset_transaction_builder.rb +418 -0
- data/lib/btcruby/open_assets/asset_transaction_input.rb +64 -0
- data/lib/btcruby/open_assets/asset_transaction_output.rb +140 -0
- data/lib/btcruby/open_assets.rb +26 -0
- data/lib/btcruby/openssl.rb +536 -0
- data/lib/btcruby/proof_of_work.rb +110 -0
- data/lib/btcruby/safety.rb +26 -0
- data/lib/btcruby/script.rb +733 -0
- data/lib/btcruby/signature_hashtype.rb +37 -0
- data/lib/btcruby/transaction.rb +511 -0
- data/lib/btcruby/transaction_builder/errors.rb +15 -0
- data/lib/btcruby/transaction_builder/provider.rb +54 -0
- data/lib/btcruby/transaction_builder/result.rb +73 -0
- data/lib/btcruby/transaction_builder/signer.rb +28 -0
- data/lib/btcruby/transaction_builder.rb +520 -0
- data/lib/btcruby/transaction_input.rb +298 -0
- data/lib/btcruby/transaction_outpoint.rb +30 -0
- data/lib/btcruby/transaction_output.rb +315 -0
- data/lib/btcruby/version.rb +3 -0
- data/lib/btcruby/wif.rb +118 -0
- data/lib/btcruby/wire_format.rb +362 -0
- data/lib/btcruby.rb +44 -2
- data/sample_code/creating_a_p2sh_multisig_address.rb +21 -0
- data/sample_code/creating_a_transaction_manually.rb +44 -0
- data/sample_code/generating_an_address.rb +20 -0
- data/sample_code/using_transaction_builder.rb +49 -0
- data/spec/address_spec.rb +206 -0
- data/spec/all.rb +6 -0
- data/spec/base58_spec.rb +83 -0
- data/spec/block_header_spec.rb +18 -0
- data/spec/block_spec.rb +18 -0
- data/spec/currency_formatter_spec.rb +46 -0
- data/spec/data_spec.rb +50 -0
- data/spec/diagnostics_spec.rb +41 -0
- data/spec/key_spec.rb +205 -0
- data/spec/keychain_spec.rb +261 -0
- data/spec/network_spec.rb +48 -0
- data/spec/open_assets/asset_address_spec.rb +33 -0
- data/spec/open_assets/asset_id_spec.rb +15 -0
- data/spec/open_assets/asset_marker_spec.rb +47 -0
- data/spec/open_assets/asset_processor_spec.rb +567 -0
- data/spec/open_assets/asset_transaction_builder_spec.rb +273 -0
- data/spec/open_assets/asset_transaction_spec.rb +70 -0
- data/spec/proof_of_work_spec.rb +53 -0
- data/spec/script_spec.rb +66 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/transaction_builder_spec.rb +338 -0
- data/spec/transaction_spec.rb +162 -0
- data/spec/wire_format_spec.rb +283 -0
- metadata +141 -7
@@ -0,0 +1,197 @@
|
|
1
|
+
# Bitcoin script consists of opcodes ("operation codes") and raw binary data.
|
2
|
+
# Opcodes define how the data is added, removed and modified on the stack.
|
3
|
+
module BTC
|
4
|
+
module Opcode
|
5
|
+
extend self
|
6
|
+
|
7
|
+
# Returns a human-readable name for a given opcode byte value.
|
8
|
+
def name_for_opcode(opcode)
|
9
|
+
OPCODE_VALUE_TO_NAME[opcode] || "OP_UNKNOWN"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns an opcode byte value for a given name ("OP_...").
|
13
|
+
def opcode_for_name(name)
|
14
|
+
OPCODE_NAME_TO_VALUE[name] || OP_INVALIDOPCODE
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns OP_1NEGATE, OP_0 .. OP_16 for ints from -1 to 16.
|
18
|
+
# Returns OP_INVALIDOPCODE for other ints.
|
19
|
+
def opcode_for_small_integer(small_int)
|
20
|
+
raise ArgumentError, "small_int must not be nil" if !small_int
|
21
|
+
return OP_0 if small_int == 0
|
22
|
+
return OP_1NEGATE if small_int == -1
|
23
|
+
if small_int >= 1 && small_int <= 16
|
24
|
+
return OP_1 + (small_int - 1)
|
25
|
+
end
|
26
|
+
return OP_INVALIDOPCODE
|
27
|
+
end
|
28
|
+
|
29
|
+
# Converts opcode OP_<N> or OP_1NEGATE to an integer value.
|
30
|
+
# If incorrect opcode is given, nil is returned.
|
31
|
+
def small_integer_from_opcode(opcode)
|
32
|
+
return 0 if opcode == "".b # OP_O
|
33
|
+
return 0 if opcode == OP_0
|
34
|
+
return -1 if opcode == OP_1NEGATE
|
35
|
+
if opcode >= OP_1 && opcode <= OP_16
|
36
|
+
return opcode - (OP_1 - 1)
|
37
|
+
end
|
38
|
+
return nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# 1. Operators pushing data on stack.
|
43
|
+
|
44
|
+
# Push 1 byte 0x00 on the stack
|
45
|
+
OP_FALSE = 0x00
|
46
|
+
OP_0 = 0x00
|
47
|
+
|
48
|
+
# Any opcode with value < PUSHDATA1 is a length of the string to be pushed on the stack.
|
49
|
+
# So opcode 0x01 is followed by 1 byte of data, 0x09 by 9 bytes and so on up to 0x4b (75 bytes)
|
50
|
+
|
51
|
+
# PUSHDATA<N> opcode is followed by N-byte length of the string that follows.
|
52
|
+
OP_PUSHDATA1 = 0x4c # followed by a 1-byte length of the string to push (allows pushing 0..255 bytes).
|
53
|
+
OP_PUSHDATA2 = 0x4d # followed by a 2-byte length of the string to push (allows pushing 0..65535 bytes).
|
54
|
+
OP_PUSHDATA4 = 0x4e # followed by a 4-byte length of the string to push (allows pushing 0..4294967295 bytes).
|
55
|
+
OP_1NEGATE = 0x4f # pushes -1 number on the stack
|
56
|
+
OP_RESERVED = 0x50 # Not assigned. If executed, transaction is invalid.
|
57
|
+
|
58
|
+
# OP_<N> pushes number <N> on the stack
|
59
|
+
OP_TRUE = 0x51
|
60
|
+
OP_1 = 0x51
|
61
|
+
OP_2 = 0x52
|
62
|
+
OP_3 = 0x53
|
63
|
+
OP_4 = 0x54
|
64
|
+
OP_5 = 0x55
|
65
|
+
OP_6 = 0x56
|
66
|
+
OP_7 = 0x57
|
67
|
+
OP_8 = 0x58
|
68
|
+
OP_9 = 0x59
|
69
|
+
OP_10 = 0x5a
|
70
|
+
OP_11 = 0x5b
|
71
|
+
OP_12 = 0x5c
|
72
|
+
OP_13 = 0x5d
|
73
|
+
OP_14 = 0x5e
|
74
|
+
OP_15 = 0x5f
|
75
|
+
OP_16 = 0x60
|
76
|
+
|
77
|
+
# 2. Control flow operators
|
78
|
+
|
79
|
+
OP_NOP = 0x61 # Does nothing
|
80
|
+
OP_VER = 0x62 # Not assigned. If executed, transaction is invalid.
|
81
|
+
|
82
|
+
# BitcoinQT executes all operators from OP_IF to OP_ENDIF even inside "non-executed" branch (to keep track of nesting).
|
83
|
+
# Since OP_VERIF and OP_VERNOTIF are not assigned, even inside a non-executed branch they will fall in "default:" switch case
|
84
|
+
# and cause the script to fail. Some other ops like OP_VER can be present inside non-executed branch because they'll be skipped.
|
85
|
+
OP_IF = 0x63 # If the top stack value is not 0, the statements are executed. The top stack value is removed.
|
86
|
+
OP_NOTIF = 0x64 # If the top stack value is 0, the statements are executed. The top stack value is removed.
|
87
|
+
OP_VERIF = 0x65 # Not assigned. Script is invalid with that opcode (even if inside non-executed branch).
|
88
|
+
OP_VERNOTIF = 0x66 # Not assigned. Script is invalid with that opcode (even if inside non-executed branch).
|
89
|
+
OP_ELSE = 0x67 # Executes code if the previous OP_IF or OP_NOTIF was not executed.
|
90
|
+
OP_ENDIF = 0x68 # Finishes if/else block
|
91
|
+
|
92
|
+
OP_VERIFY = 0x69 # Removes item from the stack if it's not 0x00 or 0x80 (negative zero). Otherwise, marks script as invalid.
|
93
|
+
OP_RETURN = 0x6a # Marks transaction as invalid.
|
94
|
+
|
95
|
+
# Stack ops
|
96
|
+
OP_TOALTSTACK = 0x6b # Moves item from the stack to altstack
|
97
|
+
OP_FROMALTSTACK = 0x6c # Moves item from the altstack to stack
|
98
|
+
OP_2DROP = 0x6d
|
99
|
+
OP_2DUP = 0x6e
|
100
|
+
OP_3DUP = 0x6f
|
101
|
+
OP_2OVER = 0x70
|
102
|
+
OP_2ROT = 0x71
|
103
|
+
OP_2SWAP = 0x72
|
104
|
+
OP_IFDUP = 0x73
|
105
|
+
OP_DEPTH = 0x74
|
106
|
+
OP_DROP = 0x75
|
107
|
+
OP_DUP = 0x76
|
108
|
+
OP_NIP = 0x77
|
109
|
+
OP_OVER = 0x78
|
110
|
+
OP_PICK = 0x79
|
111
|
+
OP_ROLL = 0x7a
|
112
|
+
OP_ROT = 0x7b
|
113
|
+
OP_SWAP = 0x7c
|
114
|
+
OP_TUCK = 0x7d
|
115
|
+
|
116
|
+
# Splice ops
|
117
|
+
OP_CAT = 0x7e # Disabled opcode. If executed, transaction is invalid.
|
118
|
+
OP_SUBSTR = 0x7f # Disabled opcode. If executed, transaction is invalid.
|
119
|
+
OP_LEFT = 0x80 # Disabled opcode. If executed, transaction is invalid.
|
120
|
+
OP_RIGHT = 0x81 # Disabled opcode. If executed, transaction is invalid.
|
121
|
+
OP_SIZE = 0x82
|
122
|
+
|
123
|
+
# Bit logic
|
124
|
+
OP_INVERT = 0x83 # Disabled opcode. If executed, transaction is invalid.
|
125
|
+
OP_AND = 0x84 # Disabled opcode. If executed, transaction is invalid.
|
126
|
+
OP_OR = 0x85 # Disabled opcode. If executed, transaction is invalid.
|
127
|
+
OP_XOR = 0x86 # Disabled opcode. If executed, transaction is invalid.
|
128
|
+
|
129
|
+
OP_EQUAL = 0x87 # Last two items are removed from the stack and compared. Result (true or false) is pushed to the stack.
|
130
|
+
OP_EQUALVERIFY = 0x88 # Same as OP_EQUAL, but removes the result from the stack if it's true or marks script as invalid.
|
131
|
+
|
132
|
+
OP_RESERVED1 = 0x89 # Disabled opcode. If executed, transaction is invalid.
|
133
|
+
OP_RESERVED2 = 0x8a # Disabled opcode. If executed, transaction is invalid.
|
134
|
+
|
135
|
+
# Numeric
|
136
|
+
OP_1ADD = 0x8b # adds 1 to last item, pops it from stack and pushes result.
|
137
|
+
OP_1SUB = 0x8c # substracts 1 to last item, pops it from stack and pushes result.
|
138
|
+
OP_2MUL = 0x8d # Disabled opcode. If executed, transaction is invalid.
|
139
|
+
OP_2DIV = 0x8e # Disabled opcode. If executed, transaction is invalid.
|
140
|
+
OP_NEGATE = 0x8f # negates the number, pops it from stack and pushes result.
|
141
|
+
OP_ABS = 0x90 # replaces number with its absolute value
|
142
|
+
OP_NOT = 0x91 # replaces number with True if it's zero, False otherwise.
|
143
|
+
OP_0NOTEQUAL = 0x92 # replaces number with True if it's not zero, False otherwise.
|
144
|
+
|
145
|
+
OP_ADD = 0x93 # (x y -- x+y)
|
146
|
+
OP_SUB = 0x94 # (x y -- x-y)
|
147
|
+
OP_MUL = 0x95 # Disabled opcode. If executed, transaction is invalid.
|
148
|
+
OP_DIV = 0x96 # Disabled opcode. If executed, transaction is invalid.
|
149
|
+
OP_MOD = 0x97 # Disabled opcode. If executed, transaction is invalid.
|
150
|
+
OP_LSHIFT = 0x98 # Disabled opcode. If executed, transaction is invalid.
|
151
|
+
OP_RSHIFT = 0x99 # Disabled opcode. If executed, transaction is invalid.
|
152
|
+
|
153
|
+
OP_BOOLAND = 0x9a
|
154
|
+
OP_BOOLOR = 0x9b
|
155
|
+
OP_NUMEQUAL = 0x9c
|
156
|
+
OP_NUMEQUALVERIFY = 0x9d
|
157
|
+
OP_NUMNOTEQUAL = 0x9e
|
158
|
+
OP_LESSTHAN = 0x9f
|
159
|
+
OP_GREATERTHAN = 0xa0
|
160
|
+
OP_LESSTHANOREQUAL = 0xa1
|
161
|
+
OP_GREATERTHANOREQUAL = 0xa2
|
162
|
+
OP_MIN = 0xa3
|
163
|
+
OP_MAX = 0xa4
|
164
|
+
|
165
|
+
OP_WITHIN = 0xa5
|
166
|
+
|
167
|
+
# Crypto
|
168
|
+
OP_RIPEMD160 = 0xa6
|
169
|
+
OP_SHA1 = 0xa7
|
170
|
+
OP_SHA256 = 0xa8
|
171
|
+
OP_HASH160 = 0xa9
|
172
|
+
OP_HASH256 = 0xaa
|
173
|
+
OP_CODESEPARATOR = 0xab # This opcode is rarely used because it's useless, but we need to support it anyway.
|
174
|
+
OP_CHECKSIG = 0xac
|
175
|
+
OP_CHECKSIGVERIFY = 0xad
|
176
|
+
OP_CHECKMULTISIG = 0xae
|
177
|
+
OP_CHECKMULTISIGVERIFY = 0xaf
|
178
|
+
|
179
|
+
# Expansion
|
180
|
+
OP_NOP1 = 0xb0
|
181
|
+
OP_NOP2 = 0xb1
|
182
|
+
OP_NOP3 = 0xb2
|
183
|
+
OP_NOP4 = 0xb3
|
184
|
+
OP_NOP5 = 0xb4
|
185
|
+
OP_NOP6 = 0xb5
|
186
|
+
OP_NOP7 = 0xb6
|
187
|
+
OP_NOP8 = 0xb7
|
188
|
+
OP_NOP9 = 0xb8
|
189
|
+
OP_NOP10 = 0xb9
|
190
|
+
|
191
|
+
OP_INVALIDOPCODE = 0xff
|
192
|
+
|
193
|
+
OPCODE_NAME_TO_VALUE = Hash[*constants.grep(/^OP_/).map{|c| [c.to_s, const_get(c)] }.flatten]
|
194
|
+
OPCODE_VALUE_TO_NAME = Hash[*constants.grep(/^OP_/).map{|c| [const_get(c), c.to_s] }.flatten]
|
195
|
+
|
196
|
+
end
|
197
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module BTC
|
2
|
+
# Implementation of OpenAssets protocol.
|
3
|
+
# https://github.com/OpenAssets/open-assets-protocol/blob/master/specification.mediawiki
|
4
|
+
class Asset
|
5
|
+
|
6
|
+
# BTC::AssetID instance identifying this asset.
|
7
|
+
attr_accessor :asset_id
|
8
|
+
|
9
|
+
# Binary 160-bit identifier of the asset (same as `asset_id.hash`)
|
10
|
+
attr_accessor :identifier_binary
|
11
|
+
|
12
|
+
# Optional attributes, set either by initializer when possible, or externally.
|
13
|
+
attr_accessor :script
|
14
|
+
attr_accessor :output
|
15
|
+
|
16
|
+
# Initializes assets with one of the following:
|
17
|
+
# * script - a BTC::Script instance.
|
18
|
+
# * output - a BTC::TransactionOutput instance that issues the asset.
|
19
|
+
# * asset_id - a Base58 identifier of the asset or BTC::AssetID instance.
|
20
|
+
def initialize(asset_id: nil, script: nil, output: nil, network: nil)
|
21
|
+
if script || output
|
22
|
+
script ||= output.script
|
23
|
+
@output = output
|
24
|
+
@script = script
|
25
|
+
@identifier_binary = BTC::Data.hash160(script.data)
|
26
|
+
@asset_id = AssetID.new(hash: @identifier_binary, network: network)
|
27
|
+
elsif asset_id
|
28
|
+
@asset_id = Address.parse(asset_id)
|
29
|
+
@identifier_binary = @asset_id.hash
|
30
|
+
else
|
31
|
+
raise ArgumentError, "Either asset_id, script or output must be specified."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module BTC
|
2
|
+
# Represents an Asset Address, where the assets can be sent.
|
3
|
+
class AssetAddress < BTC::Address
|
4
|
+
NAMESPACE = 0x13
|
5
|
+
|
6
|
+
def self.mainnet_version
|
7
|
+
NAMESPACE
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.testnet_version
|
11
|
+
NAMESPACE
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :bitcoin_address
|
15
|
+
|
16
|
+
def initialize(string: nil, bitcoin_address: nil, _raw_data: nil)
|
17
|
+
if string
|
18
|
+
_raw_data ||= Base58.data_from_base58check(string)
|
19
|
+
raise FormatError, "Too short AssetAddress" if _raw_data.bytesize < 2
|
20
|
+
raise FormatError, "Invalid namespace for AssetAddress" if _raw_data.bytes[0] != NAMESPACE
|
21
|
+
@bitcoin_address = Address.parse_raw_data(_raw_data[1..-1])
|
22
|
+
@base58check_string = string
|
23
|
+
elsif bitcoin_address
|
24
|
+
@base58check_string = nil
|
25
|
+
@bitcoin_address = BTC::Address.parse(bitcoin_address)
|
26
|
+
else
|
27
|
+
raise ArgumentError, "Either data or string must be provided"
|
28
|
+
end
|
29
|
+
# If someone accidentally supplied AssetAddress as a bitcoin address,
|
30
|
+
# simply unwrap the bitcoin address from it.
|
31
|
+
while @bitcoin_address.is_a?(self.class)
|
32
|
+
@bitcoin_address = @bitcoin_address.bitcoin_address
|
33
|
+
@base58check_string = nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def network
|
38
|
+
@bitcoin_address.network
|
39
|
+
end
|
40
|
+
|
41
|
+
def script
|
42
|
+
@bitcoin_address.script
|
43
|
+
end
|
44
|
+
|
45
|
+
def data_for_base58check_encoding
|
46
|
+
BTC::Data.data_from_bytes([NAMESPACE]) + @bitcoin_address.data_for_base58check_encoding
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Implementation of OpenAssets Asset Definition Format
|
2
|
+
# https://github.com/OpenAssets/open-assets-protocol/blob/master/asset-definition-protocol.mediawiki
|
3
|
+
require 'json' # in Ruby 2 stdlib
|
4
|
+
module BTC
|
5
|
+
class AssetDefinition
|
6
|
+
|
7
|
+
DEFAULT_VERSION = "1.0".freeze
|
8
|
+
|
9
|
+
attr_accessor :asset_ids
|
10
|
+
attr_accessor :name_short
|
11
|
+
attr_accessor :name
|
12
|
+
attr_accessor :contract_url
|
13
|
+
attr_accessor :issuer
|
14
|
+
attr_accessor :description
|
15
|
+
attr_accessor :description_mime
|
16
|
+
attr_accessor :type
|
17
|
+
attr_accessor :divisibility
|
18
|
+
attr_accessor :link_to_website
|
19
|
+
attr_accessor :icon_url
|
20
|
+
attr_accessor :image_url
|
21
|
+
attr_accessor :version
|
22
|
+
|
23
|
+
def initialize(dictionary: nil,
|
24
|
+
json: nil,
|
25
|
+
asset_ids: [],
|
26
|
+
name: nil,
|
27
|
+
name_short: nil,
|
28
|
+
issuer: nil,
|
29
|
+
type: nil,
|
30
|
+
divisibility: 0)
|
31
|
+
|
32
|
+
@json = json
|
33
|
+
dictionary ||= (json ? JSON.parse(json) : nil)
|
34
|
+
if dictionary
|
35
|
+
@asset_ids = dictionary["asset_ids"].map{|aid| AssetID.parse(aid) }
|
36
|
+
else
|
37
|
+
@asset_ids = asset_ids
|
38
|
+
@name = name
|
39
|
+
@name_short = name_short
|
40
|
+
@issuer = issuer
|
41
|
+
@type = type
|
42
|
+
@divisibility = divisibility
|
43
|
+
end
|
44
|
+
@version ||= DEFAULT_VERSION
|
45
|
+
@asset_ids ||= []
|
46
|
+
@divisibility ||= 0
|
47
|
+
end
|
48
|
+
|
49
|
+
def dictionary
|
50
|
+
dict = {}
|
51
|
+
dict["asset_ids"] = self.asset_ids.map{|aid| aid.to_s}
|
52
|
+
dict["name_short"] = self.name_short || ""
|
53
|
+
dict["name"] = self.name || ""
|
54
|
+
dict["contract_url"] = self.contract_url if self.contract_url
|
55
|
+
dict["issuer"] = self.issuer if self.issuer
|
56
|
+
dict["description"] = self.description if self.description
|
57
|
+
dict["description_mime"] = self.description_mime if self.description_mime
|
58
|
+
dict["type"] = self.type if self.type
|
59
|
+
dict["divisibility"] = self.divisibility if self.divisibility
|
60
|
+
dict["link_to_website"] = self.link_to_website if self.link_to_website
|
61
|
+
dict["icon_url"] = self.icon_url if self.icon_url
|
62
|
+
dict["image_url"] = self.image_url if self.image_url
|
63
|
+
dict["version"] = self.version if self.version
|
64
|
+
dict
|
65
|
+
end
|
66
|
+
|
67
|
+
def json
|
68
|
+
@json ||= JSON.generate(dictionary)
|
69
|
+
end
|
70
|
+
|
71
|
+
def sha256
|
72
|
+
BTC::Data.hex_from_data(BTC.sha256(json))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module BTC
|
2
|
+
# Represents an Asset ID.
|
3
|
+
class AssetID < BTC::Hash160Address
|
4
|
+
|
5
|
+
def self.mainnet_version
|
6
|
+
23 # "A" prefix
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.testnet_version
|
10
|
+
115
|
11
|
+
end
|
12
|
+
|
13
|
+
# Instantiates AssetID with output, output script or raw hash.
|
14
|
+
# To compute an Asset ID for the Asset Definition file, use `trim_script_prefix: true`.
|
15
|
+
def initialize(string: nil, hash: nil, network: nil, _raw_data: nil, script: nil, trim_script_prefix: false)
|
16
|
+
if script
|
17
|
+
script = script.without_dropped_prefix_data if trim_script_prefix
|
18
|
+
super(hash: BTC.hash160(script.data), network: network)
|
19
|
+
else
|
20
|
+
super(string: string, hash: hash, network: network, _raw_data: _raw_data)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module BTC
|
2
|
+
class AssetMarker
|
3
|
+
|
4
|
+
MAGIC = "\x4f\x41".freeze
|
5
|
+
VERSION1 = "\x01\x00".freeze
|
6
|
+
PREFIX_V1 = MAGIC + VERSION1
|
7
|
+
|
8
|
+
attr_accessor :quantities
|
9
|
+
attr_accessor :metadata
|
10
|
+
|
11
|
+
attr_reader :data
|
12
|
+
attr_reader :output
|
13
|
+
attr_reader :script
|
14
|
+
|
15
|
+
def initialize(output: nil, script: nil, data: nil, quantities: nil, metadata: nil)
|
16
|
+
if output || script
|
17
|
+
script ||= output.script
|
18
|
+
raise ArgumentError, "Script is not an OP_RETURN script" if !script.op_return_script?
|
19
|
+
data = script.op_return_data
|
20
|
+
raise ArgumentError, "No pushdata found in script" if !data || data.bytesize == 0
|
21
|
+
@output = output
|
22
|
+
@script = script
|
23
|
+
end
|
24
|
+
if data
|
25
|
+
data = BTC::Data.ensure_binary_encoding(data)
|
26
|
+
|
27
|
+
raise ArgumentError, "Data must be at least 6 bytes long (4 bytes prefix and 2 bytes for varints)" if data.bytesize < 6
|
28
|
+
raise ArgumentError, "Prefix is invalid. Expected #{BTC::Data.hex_from_data(PREFIX_V1)}" if data[0, PREFIX_V1.bytesize] != PREFIX_V1
|
29
|
+
|
30
|
+
offset = PREFIX_V1.bytesize
|
31
|
+
count, bytesread = WireFormat.read_varint(data: data, offset: offset)
|
32
|
+
raise ArgumentError, "Cannot read Asset Quantity Count varint" if !count
|
33
|
+
offset = bytesread
|
34
|
+
@quantities = []
|
35
|
+
count.times do
|
36
|
+
qty, bytesread = WireFormat.read_uleb128(data: data, offset: offset)
|
37
|
+
raise ArgumentError, "Cannot read Asset Quantity LEB128 unsigned integer" if !qty
|
38
|
+
raise ArgumentError, "Open Assets limit LEB128 encoding for quantities to 9 bytes" if (bytesread - offset) > 9
|
39
|
+
@quantities << qty
|
40
|
+
offset = bytesread
|
41
|
+
end
|
42
|
+
metadata, bytesread = WireFormat.read_string(data: data, offset: offset)
|
43
|
+
raise ArgumentError, "Cannot read Asset Metadata" if !metadata
|
44
|
+
@metadata = metadata
|
45
|
+
@data = data[0, bytesread]
|
46
|
+
else
|
47
|
+
# Initialize with optional attributes
|
48
|
+
@quantities = quantities
|
49
|
+
@metadata = metadata
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def quantities
|
54
|
+
@quantities ||= []
|
55
|
+
end
|
56
|
+
|
57
|
+
def quantities=(qs)
|
58
|
+
@quantities = qs
|
59
|
+
@data = nil
|
60
|
+
@script = nil
|
61
|
+
@output = nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def metadata
|
65
|
+
@metadata ||= "".b
|
66
|
+
end
|
67
|
+
|
68
|
+
def metadata=(md)
|
69
|
+
@metadata = md
|
70
|
+
@data = nil
|
71
|
+
@script = nil
|
72
|
+
@output = nil
|
73
|
+
end
|
74
|
+
|
75
|
+
def data
|
76
|
+
@data ||= begin
|
77
|
+
PREFIX_V1 +
|
78
|
+
WireFormat.encode_varint(self.quantities.size) +
|
79
|
+
self.quantities.inject("".b){|buf, qty| buf << WireFormat.encode_uleb128(qty) } +
|
80
|
+
WireFormat.encode_string(self.metadata)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def script
|
85
|
+
@script ||= begin
|
86
|
+
Script.new << OP_RETURN << self.data
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def output
|
91
|
+
@output ||= TransactionOutput.new(value: 0, script: self.script)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|