glueby 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/.ruby-gemset +1 -1
- data/.ruby-version +1 -1
- data/.travis.yml +3 -2
- data/README.md +27 -17
- data/glueby.gemspec +1 -1
- data/lib/generators/glueby/{initializer_generator.rb → contract/initializer_generator.rb} +0 -0
- data/lib/generators/glueby/contract/templates/initializer.rb.erb +3 -0
- data/lib/generators/glueby/contract/templates/key_table.rb.erb +15 -0
- data/lib/generators/glueby/{templates → contract/templates}/timestamp_table.rb.erb +2 -1
- data/lib/generators/glueby/contract/templates/utxo_table.rb.erb +15 -0
- data/lib/generators/glueby/contract/templates/wallet_table.rb.erb +10 -0
- data/lib/generators/glueby/contract/timestamp_generator.rb +26 -0
- data/lib/generators/glueby/contract/wallet_adapter_generator.rb +46 -0
- data/lib/glueby.rb +18 -1
- data/lib/glueby/contract.rb +3 -14
- data/lib/glueby/contract/active_record/timestamp.rb +8 -5
- data/lib/glueby/contract/errors.rb +6 -0
- data/lib/glueby/contract/payment.rb +54 -0
- data/lib/glueby/contract/timestamp.rb +39 -38
- data/lib/glueby/contract/token.rb +193 -0
- data/lib/glueby/contract/tx_builder.rb +197 -31
- data/lib/glueby/generator.rb +5 -0
- data/lib/glueby/generator/migrate_generator.rb +38 -0
- data/lib/glueby/internal.rb +6 -0
- data/lib/glueby/internal/rpc.rb +35 -0
- data/lib/glueby/internal/wallet.rb +122 -0
- data/lib/glueby/internal/wallet/abstract_wallet_adapter.rb +131 -0
- data/lib/glueby/internal/wallet/active_record.rb +15 -0
- data/lib/glueby/internal/wallet/active_record/key.rb +72 -0
- data/lib/glueby/internal/wallet/active_record/utxo.rb +50 -0
- data/lib/glueby/internal/wallet/active_record/wallet.rb +54 -0
- data/lib/glueby/internal/wallet/active_record_wallet_adapter.rb +133 -0
- data/lib/glueby/internal/wallet/errors.rb +11 -0
- data/lib/glueby/internal/wallet/tapyrus_core_wallet_adapter.rb +158 -0
- data/lib/glueby/version.rb +1 -1
- data/lib/glueby/wallet.rb +51 -0
- data/lib/tasks/glueby/contract/timestamp.rake +5 -5
- data/lib/tasks/glueby/contract/wallet_adapter.rake +42 -0
- metadata +30 -10
- data/lib/generators/glueby/templates/initializer.rb.erb +0 -4
- data/lib/generators/glueby/timestamp_generator.rb +0 -57
- data/lib/glueby/contract/rpc.rb +0 -15
@@ -0,0 +1,35 @@
|
|
1
|
+
module Glueby
|
2
|
+
module Internal
|
3
|
+
module RPC
|
4
|
+
module_function
|
5
|
+
|
6
|
+
def client
|
7
|
+
@rpc ||= Tapyrus::RPC::TapyrusCoreClient.new(@config)
|
8
|
+
end
|
9
|
+
|
10
|
+
def configure(config)
|
11
|
+
@config = config
|
12
|
+
end
|
13
|
+
|
14
|
+
# Perform RPC call on the specific wallet.
|
15
|
+
# This method needs block, and pass a client as as block argument. You can call RPCs on the wallet using the
|
16
|
+
# client object. See an example below.
|
17
|
+
# @param [string] wallet name on Tapyrus Core Wallet
|
18
|
+
# @return [Object] The return object of the block
|
19
|
+
#
|
20
|
+
# ## Example
|
21
|
+
# ```ruby
|
22
|
+
# perform_as('mywallet') do |client|
|
23
|
+
# client.getbalance
|
24
|
+
# end
|
25
|
+
# ```
|
26
|
+
def perform_as(wallet)
|
27
|
+
before = client.config[:wallet]
|
28
|
+
client.config[:wallet] = wallet
|
29
|
+
yield(client)
|
30
|
+
ensure
|
31
|
+
client.config[:wallet] = before
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module Glueby
|
2
|
+
module Internal
|
3
|
+
# # Glueby::Internal::Wallet
|
4
|
+
#
|
5
|
+
# This module provides the way to deal about wallet that includes key management, address management, getting UTXOs.
|
6
|
+
#
|
7
|
+
# ## How to use
|
8
|
+
#
|
9
|
+
# First, you need to configure which wallet implementation is used in Glueby::Internal::Wallet. For now, below wallets are
|
10
|
+
# supported.
|
11
|
+
#
|
12
|
+
# * [Tapyrus Core](https://github.com/chaintope/tapyrus-core)
|
13
|
+
#
|
14
|
+
# Here shows an example to use Tapyrus Core wallet.
|
15
|
+
#
|
16
|
+
# ```ruby
|
17
|
+
# # Setup Tapyrus Core RPC connection
|
18
|
+
# config = {schema: 'http', host: '127.0.0.1', port: 12381, user: 'user', password: 'pass'}
|
19
|
+
# Glueby::Internal::RPC.configure(config)
|
20
|
+
#
|
21
|
+
# # Setup wallet adapter
|
22
|
+
# Glueby::Internal::Wallet.wallet_adapter = Glueby::Internal::Wallet::TapyrusCoreWalletAdapter.new
|
23
|
+
#
|
24
|
+
# # Create wallet
|
25
|
+
# wallet = Glueby::Internal::Wallet.create
|
26
|
+
# wallet.balance # => 0
|
27
|
+
# wallet.list_unspent
|
28
|
+
# ```
|
29
|
+
class Wallet
|
30
|
+
autoload :AbstractWalletAdapter, 'glueby/internal/wallet/abstract_wallet_adapter'
|
31
|
+
autoload :AR, 'glueby/internal/wallet/active_record'
|
32
|
+
autoload :TapyrusCoreWalletAdapter, 'glueby/internal/wallet/tapyrus_core_wallet_adapter'
|
33
|
+
autoload :ActiveRecordWalletAdapter, 'glueby/internal/wallet/active_record_wallet_adapter'
|
34
|
+
autoload :Errors, 'glueby/internal/wallet/errors'
|
35
|
+
|
36
|
+
class << self
|
37
|
+
attr_writer :wallet_adapter
|
38
|
+
|
39
|
+
def create
|
40
|
+
new(wallet_adapter.create_wallet)
|
41
|
+
end
|
42
|
+
|
43
|
+
def load(wallet_id)
|
44
|
+
begin
|
45
|
+
wallet_adapter.load_wallet(wallet_id)
|
46
|
+
rescue Errors::WalletAlreadyLoaded => _
|
47
|
+
# Ignore when wallet is already loaded.
|
48
|
+
end
|
49
|
+
new(wallet_id)
|
50
|
+
end
|
51
|
+
|
52
|
+
def wallets
|
53
|
+
wallet_adapter.wallets.map { |id| new(id) }
|
54
|
+
end
|
55
|
+
|
56
|
+
def wallet_adapter
|
57
|
+
@wallet_adapter or
|
58
|
+
raise Errors::ShouldInitializeWalletAdapter, 'You should initialize wallet adapter using `Glueby::Internal::Wallet.wallet_adapter = some wallet adapter instance`.'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
attr_reader :id
|
63
|
+
|
64
|
+
def initialize(wallet_id)
|
65
|
+
@id = wallet_id
|
66
|
+
end
|
67
|
+
|
68
|
+
def balance(only_finalized = true)
|
69
|
+
wallet_adapter.balance(id, only_finalized)
|
70
|
+
end
|
71
|
+
|
72
|
+
def list_unspent(only_finalized = true)
|
73
|
+
wallet_adapter.list_unspent(id, only_finalized)
|
74
|
+
end
|
75
|
+
|
76
|
+
def delete
|
77
|
+
wallet_adapter.delete_wallet(id)
|
78
|
+
end
|
79
|
+
|
80
|
+
def sign_tx(tx, prev_txs = [])
|
81
|
+
wallet_adapter.sign_tx(id, tx, prev_txs)
|
82
|
+
end
|
83
|
+
|
84
|
+
def broadcast(tx)
|
85
|
+
wallet_adapter.broadcast(id, tx)
|
86
|
+
end
|
87
|
+
|
88
|
+
def receive_address
|
89
|
+
wallet_adapter.receive_address(id)
|
90
|
+
end
|
91
|
+
|
92
|
+
def change_address
|
93
|
+
wallet_adapter.change_address(id)
|
94
|
+
end
|
95
|
+
|
96
|
+
def create_pubkey
|
97
|
+
wallet_adapter.create_pubkey(id)
|
98
|
+
end
|
99
|
+
|
100
|
+
def collect_uncolored_outputs(amount)
|
101
|
+
utxos = list_unspent
|
102
|
+
|
103
|
+
utxos.inject([0, []]) do |sum, output|
|
104
|
+
next sum if output[:color_id]
|
105
|
+
|
106
|
+
new_sum = sum[0] + output[:amount]
|
107
|
+
new_outputs = sum[1] << output
|
108
|
+
return [new_sum, new_outputs] if new_sum >= amount
|
109
|
+
|
110
|
+
[new_sum, new_outputs]
|
111
|
+
end
|
112
|
+
raise Glueby::Contract::Errors::InsufficientFunds
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def wallet_adapter
|
118
|
+
self.class.wallet_adapter
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module Glueby
|
2
|
+
module Internal
|
3
|
+
class Wallet
|
4
|
+
# This is an abstract class for wallet adapters. If you want to use a wallet
|
5
|
+
# component with Glueby modules, you can use it by adding subclass of this abstract
|
6
|
+
# class for the wallet component.
|
7
|
+
class AbstractWalletAdapter
|
8
|
+
# Creates a new wallet inside the wallet component and returns `wallet_id`. The created
|
9
|
+
# wallet is loaded from at first.
|
10
|
+
# @return [String] wallet_id
|
11
|
+
def create_wallet
|
12
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
13
|
+
end
|
14
|
+
|
15
|
+
# Delete the wallet inside the wallet component.
|
16
|
+
# This method expect that the wallet will be removed completely
|
17
|
+
# in the wallet and it cannot restore. It is assumed that the main use-case of
|
18
|
+
# this method is for testing.
|
19
|
+
#
|
20
|
+
# @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
|
21
|
+
# @return [Boolean] The boolean value whether the deletion was success.
|
22
|
+
def delete_wallet(wallet_id)
|
23
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Load the wallet inside the wallet component.
|
27
|
+
# This method makes the wallet to possible to use on other methods like `balance`,
|
28
|
+
# `list_unspent`, etc. All the methods that gets `wallet_id` as a argument needs to
|
29
|
+
# load the wallet before use it.
|
30
|
+
# If the wallet component doesn't need such as load operation to prepare to use the
|
31
|
+
# wallet, `load_wallet` can be empty method.
|
32
|
+
#
|
33
|
+
# @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
|
34
|
+
# @raise [Glueby::Internal::Wallet::Errors::WalletAlreadyLoaded] when the specified wallet has been already loaded.
|
35
|
+
def load_wallet(wallet_id)
|
36
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Unload the wallet inside the wallet component.
|
40
|
+
#
|
41
|
+
# @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
|
42
|
+
# @return [Boolean] The boolean value whether the unloading was success.
|
43
|
+
def unload_wallet(wallet_id)
|
44
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns list of loaded wallet_id.
|
48
|
+
#
|
49
|
+
# @return [Array of String] - Array of wallet_id
|
50
|
+
def wallets
|
51
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns amount of tapyrus that the wallet has.
|
55
|
+
#
|
56
|
+
# @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
|
57
|
+
# @param [Boolean] only_finalized - The balance includes only finalized UTXO value if it
|
58
|
+
# is true. Default is true.
|
59
|
+
# @return [Integer] The balance of the wallet. The unit is 'tapyrus'.
|
60
|
+
# 1 TPC equals to 10^8 'tapyrus'.
|
61
|
+
def balance(wallet_id, only_finalized = true)
|
62
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns all the UTXOs that the wallet has.
|
66
|
+
#
|
67
|
+
# @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
|
68
|
+
# @param [Boolean] only_finalized - The UTXOs includes only finalized UTXO value if it
|
69
|
+
# is true. Default is true.
|
70
|
+
# @return [Array of UTXO]
|
71
|
+
#
|
72
|
+
# ## The UTXO structure
|
73
|
+
#
|
74
|
+
# - txid: [String] Transaction id
|
75
|
+
# - vout: [Integer] Output index
|
76
|
+
# - amount: [Integer] Amount of the UTXO as tapyrus unit
|
77
|
+
# - finalized: [Boolean] Whether the UTXO is finalized
|
78
|
+
def list_unspent(wallet_id, only_finalized = true)
|
79
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
80
|
+
end
|
81
|
+
|
82
|
+
# Sign to the transaction with a key in the wallet.
|
83
|
+
#
|
84
|
+
# @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
|
85
|
+
# @param [Tapyrus::Tx] tx - The transaction will be signed.
|
86
|
+
# @param [Array] prevtxs array of hash that represents unbroadcasted transaction outputs used by signing tx.
|
87
|
+
# Each hash has `txid`, `vout`, `scriptPubKey`, `amount` fields.
|
88
|
+
# @return [Tapyrus::Tx]
|
89
|
+
def sign_tx(wallet_id, tx, prevtxs = [])
|
90
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
91
|
+
end
|
92
|
+
|
93
|
+
# Broadcast the transaction to the Tapyrus Network.
|
94
|
+
#
|
95
|
+
# @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
|
96
|
+
# @param [Tapyrus::Tx] tx - The transaction to be broadcasterd.
|
97
|
+
# @return [String] txid
|
98
|
+
def broadcast(wallet_id, tx)
|
99
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns an address to receive coin.
|
103
|
+
#
|
104
|
+
# @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
|
105
|
+
# @return [String] P2PKH address
|
106
|
+
def receive_address(wallet_id)
|
107
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
108
|
+
end
|
109
|
+
|
110
|
+
# Returns an address to change coin.
|
111
|
+
#
|
112
|
+
# @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
|
113
|
+
# @return [String] P2PKH address
|
114
|
+
def change_address(wallet_id)
|
115
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns a new public key.
|
119
|
+
#
|
120
|
+
# This method is expected to returns a new public key. The key would generate internally. This key is provided
|
121
|
+
# for contracts that need public key such as multi sig transaction.
|
122
|
+
#
|
123
|
+
# @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
|
124
|
+
# @return [Tapyrus::Key]
|
125
|
+
def create_pubkey(wallet_id)
|
126
|
+
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_record'
|
4
|
+
|
5
|
+
module Glueby
|
6
|
+
module Internal
|
7
|
+
class Wallet
|
8
|
+
module AR
|
9
|
+
autoload :Key, 'glueby/internal/wallet/active_record/key'
|
10
|
+
autoload :Utxo, 'glueby/internal/wallet/active_record/utxo'
|
11
|
+
autoload :Wallet, 'glueby/internal/wallet/active_record/wallet'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Glueby
|
4
|
+
module Internal
|
5
|
+
class Wallet
|
6
|
+
module AR
|
7
|
+
class Key < ::ActiveRecord::Base
|
8
|
+
before_create :generate_key
|
9
|
+
before_create :set_script_pubkey
|
10
|
+
|
11
|
+
belongs_to :wallet
|
12
|
+
|
13
|
+
enum purpose: { receive: 0, change: 1 }
|
14
|
+
|
15
|
+
validates :purpose, presence: true
|
16
|
+
validates :private_key, uniqueness: { case_sensitive: false }
|
17
|
+
|
18
|
+
def to_p2pkh
|
19
|
+
Tapyrus::Script.to_p2pkh(Tapyrus.hash160(public_key))
|
20
|
+
end
|
21
|
+
|
22
|
+
def sign(data)
|
23
|
+
Tapyrus::Key.new(priv_key: self.private_key).sign(data, algo: :schnorr)
|
24
|
+
end
|
25
|
+
|
26
|
+
def address
|
27
|
+
to_p2pkh.addresses.first
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return Glueby::Internal::Wallet::AR::Key object for output.
|
31
|
+
# If output is colored output, key is found by corresponding `uncolored` script.
|
32
|
+
#
|
33
|
+
# @param [Tapyrus::TxOut] output
|
34
|
+
# @return [Glueby::Internal::Wallet::AR::Key] key for output
|
35
|
+
def self.key_for_output(output)
|
36
|
+
key_for_script(output.script_pubkey)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Return Glueby::Internal::Wallet::AR::Key object for script.
|
40
|
+
# If script is colored, key is found by corresponding `uncolored` script.
|
41
|
+
#
|
42
|
+
# @param [Tapyrus::Script] script_pubkey
|
43
|
+
# @return [Glueby::Internal::Wallet::AR::Key] key for input
|
44
|
+
def self.key_for_script(script_pubkey)
|
45
|
+
script_pubkey = if script_pubkey.colored?
|
46
|
+
script_pubkey.remove_color.to_hex
|
47
|
+
else
|
48
|
+
script_pubkey.to_hex
|
49
|
+
end
|
50
|
+
find_by(script_pubkey: script_pubkey)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def generate_key
|
56
|
+
key = if private_key
|
57
|
+
Tapyrus::Key.new(priv_key: private_key)
|
58
|
+
else
|
59
|
+
Tapyrus::Key.generate
|
60
|
+
end
|
61
|
+
self.private_key ||= key.priv_key
|
62
|
+
self.public_key ||= key.pubkey
|
63
|
+
end
|
64
|
+
|
65
|
+
def set_script_pubkey
|
66
|
+
self.script_pubkey = to_p2pkh.to_hex
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Glueby
|
4
|
+
module Internal
|
5
|
+
class Wallet
|
6
|
+
module AR
|
7
|
+
class Utxo < ::ActiveRecord::Base
|
8
|
+
belongs_to :key
|
9
|
+
|
10
|
+
validates :txid, uniqueness: { scope: :index, case_sensitive: false }
|
11
|
+
|
12
|
+
enum status: { init: 0, broadcasted: 1, finalized: 2 }
|
13
|
+
|
14
|
+
def color_id
|
15
|
+
script = Tapyrus::Script.parse_from_payload(script_pubkey.htb)
|
16
|
+
script.color_id&.to_hex
|
17
|
+
end
|
18
|
+
|
19
|
+
# Delete utxo spent by specified tx.
|
20
|
+
#
|
21
|
+
# @param [Tapyrus::Tx] the spending tx
|
22
|
+
def self.destroy_for_inputs(tx)
|
23
|
+
tx.inputs.each do |input|
|
24
|
+
Utxo.destroy_by(txid: input.out_point.txid, index: input.out_point.index)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Create utxo or update utxo for tx outputs
|
29
|
+
# if there is no key for script pubkey in an output, utxo for the output is not created.
|
30
|
+
#
|
31
|
+
# @param [Tapyrus::Tx] tx
|
32
|
+
def self.create_or_update_for_outputs(tx, status: :finalized)
|
33
|
+
tx.outputs.each.with_index do |output, index|
|
34
|
+
key = Key.key_for_output(output)
|
35
|
+
next unless key
|
36
|
+
|
37
|
+
utxo = Utxo.find_or_initialize_by(txid: tx.txid, index: index)
|
38
|
+
utxo.update!(
|
39
|
+
script_pubkey: output.script_pubkey.to_hex,
|
40
|
+
value: output.value,
|
41
|
+
status: status,
|
42
|
+
key: key
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_record'
|
4
|
+
|
5
|
+
module Glueby
|
6
|
+
module Internal
|
7
|
+
class Wallet
|
8
|
+
module AR
|
9
|
+
class Wallet < ::ActiveRecord::Base
|
10
|
+
has_many :keys
|
11
|
+
|
12
|
+
validates :wallet_id, uniqueness: { case_sensitive: false }
|
13
|
+
|
14
|
+
# @param [Tapyrus::Tx] tx
|
15
|
+
# @param [Array] prevtxs array of outputs
|
16
|
+
def sign(tx, prevtxs = [])
|
17
|
+
tx.inputs.each.with_index do |input, index|
|
18
|
+
script_pubkey = script_for_input(input, prevtxs)
|
19
|
+
next unless script_pubkey
|
20
|
+
key = Key.key_for_script(script_pubkey)
|
21
|
+
next unless key
|
22
|
+
sign_tx_for_p2pkh(tx, index, key, script_pubkey)
|
23
|
+
end
|
24
|
+
tx
|
25
|
+
end
|
26
|
+
|
27
|
+
def utxos
|
28
|
+
Glueby::Internal::Wallet::AR::Utxo.where(key: keys)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def sign_tx_for_p2pkh(tx, index, key, script_pubkey)
|
34
|
+
sighash = tx.sighash_for_input(index, script_pubkey)
|
35
|
+
sig = key.sign(sighash) + [Tapyrus::SIGHASH_TYPE[:all]].pack('C')
|
36
|
+
script_sig = Tapyrus::Script.parse_from_payload(Tapyrus::Script.pack_pushdata(sig) + Tapyrus::Script.pack_pushdata(key.public_key.htb))
|
37
|
+
tx.inputs[index].script_sig = script_sig
|
38
|
+
end
|
39
|
+
|
40
|
+
def script_for_input(input, prevtxs = [])
|
41
|
+
out_point = input.out_point
|
42
|
+
utxo = Utxo.find_by(txid: out_point.txid, index: out_point.index)
|
43
|
+
if utxo
|
44
|
+
Tapyrus::Script.parse_from_payload(utxo.script_pubkey.htb)
|
45
|
+
else
|
46
|
+
output = prevtxs.select { |output| output[:txid] == out_point.txid && output[:vout] == out_point.index }.first
|
47
|
+
Tapyrus::Script.parse_from_payload(output[:scriptPubKey].htb) if output
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|