glueby 0.2.0 → 0.4.2
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/.github/workflows/ruby.yml +35 -0
- data/Gemfile +1 -0
- data/README.md +224 -16
- data/glueby.gemspec +2 -2
- data/lib/generators/glueby/contract/block_syncer_generator.rb +26 -0
- data/lib/generators/glueby/contract/reissuable_token_generator.rb +26 -0
- data/lib/generators/glueby/contract/templates/initializer.rb.erb +4 -2
- data/lib/generators/glueby/contract/templates/key_table.rb.erb +4 -3
- data/lib/generators/glueby/contract/templates/reissuable_token_table.rb.erb +10 -0
- data/lib/generators/glueby/contract/templates/system_information_table.rb.erb +12 -0
- data/lib/generators/glueby/contract/templates/timestamp_table.rb.erb +1 -1
- data/lib/generators/glueby/contract/templates/utxo_table.rb.erb +3 -2
- data/lib/generators/glueby/contract/templates/wallet_table.rb.erb +2 -2
- data/lib/glueby.rb +28 -11
- data/lib/glueby/active_record.rb +8 -0
- data/lib/glueby/active_record/system_information.rb +15 -0
- data/lib/glueby/block_syncer.rb +98 -0
- data/lib/glueby/configuration.rb +62 -0
- data/lib/glueby/contract.rb +2 -2
- data/lib/glueby/contract/active_record.rb +1 -0
- data/lib/glueby/contract/active_record/reissuable_token.rb +26 -0
- data/lib/glueby/contract/fee_estimator.rb +38 -0
- data/lib/glueby/contract/payment.rb +10 -6
- data/lib/glueby/contract/timestamp.rb +8 -6
- data/lib/glueby/contract/timestamp/syncer.rb +13 -0
- data/lib/glueby/contract/token.rb +78 -26
- data/lib/glueby/contract/tx_builder.rb +23 -20
- data/lib/glueby/fee_provider.rb +73 -0
- data/lib/glueby/fee_provider/tasks.rb +141 -0
- data/lib/glueby/generator/migrate_generator.rb +1 -1
- data/lib/glueby/internal/wallet.rb +56 -13
- data/lib/glueby/internal/wallet/abstract_wallet_adapter.rb +25 -6
- data/lib/glueby/internal/wallet/active_record/utxo.rb +1 -0
- data/lib/glueby/internal/wallet/active_record/wallet.rb +15 -5
- data/lib/glueby/internal/wallet/active_record_wallet_adapter.rb +25 -8
- data/lib/glueby/internal/wallet/active_record_wallet_adapter/syncer.rb +14 -0
- data/lib/glueby/internal/wallet/errors.rb +3 -0
- data/lib/glueby/internal/wallet/tapyrus_core_wallet_adapter.rb +42 -14
- data/lib/glueby/railtie.rb +14 -0
- data/lib/glueby/version.rb +1 -1
- data/lib/glueby/wallet.rb +3 -2
- data/lib/tasks/glueby/block_syncer.rake +29 -0
- data/lib/tasks/glueby/contract/timestamp.rake +4 -26
- data/lib/tasks/glueby/fee_provider.rake +18 -0
- metadata +26 -11
- data/.travis.yml +0 -7
- data/lib/glueby/contract/fee_provider.rb +0 -21
- data/lib/tasks/glueby/contract/wallet_adapter.rake +0 -42
| @@ -34,10 +34,13 @@ module Glueby | |
| 34 34 | 
             
                  autoload :Errors, 'glueby/internal/wallet/errors'
         | 
| 35 35 |  | 
| 36 36 | 
             
                  class << self
         | 
| 37 | 
            -
                     | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
                       | 
| 37 | 
            +
                    def create(wallet_id = nil)
         | 
| 38 | 
            +
                      begin
         | 
| 39 | 
            +
                        wallet_id = wallet_adapter.create_wallet(wallet_id)
         | 
| 40 | 
            +
                      rescue Errors::WalletAlreadyCreated => _
         | 
| 41 | 
            +
                        # Ignore when wallet is already created.
         | 
| 42 | 
            +
                      end
         | 
| 43 | 
            +
                      new(wallet_id)
         | 
| 41 44 | 
             
                    end
         | 
| 42 45 |  | 
| 43 46 | 
             
                    def load(wallet_id)
         | 
| @@ -53,6 +56,16 @@ module Glueby | |
| 53 56 | 
             
                      wallet_adapter.wallets.map { |id| new(id) }
         | 
| 54 57 | 
             
                    end
         | 
| 55 58 |  | 
| 59 | 
            +
                    def wallet_adapter=(adapter)
         | 
| 60 | 
            +
                      if adapter.is_a?(ActiveRecordWalletAdapter)
         | 
| 61 | 
            +
                        BlockSyncer.register_syncer(ActiveRecordWalletAdapter::Syncer)
         | 
| 62 | 
            +
                      else
         | 
| 63 | 
            +
                        BlockSyncer.unregister_syncer(ActiveRecordWalletAdapter::Syncer)
         | 
| 64 | 
            +
                      end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                      @wallet_adapter = adapter
         | 
| 67 | 
            +
                    end
         | 
| 68 | 
            +
             | 
| 56 69 | 
             
                    def wallet_adapter
         | 
| 57 70 | 
             
                      @wallet_adapter or
         | 
| 58 71 | 
             
                        raise Errors::ShouldInitializeWalletAdapter, 'You should initialize wallet adapter using `Glueby::Internal::Wallet.wallet_adapter = some wallet adapter instance`.'
         | 
| @@ -69,24 +82,50 @@ module Glueby | |
| 69 82 | 
             
                    wallet_adapter.balance(id, only_finalized)
         | 
| 70 83 | 
             
                  end
         | 
| 71 84 |  | 
| 72 | 
            -
                   | 
| 73 | 
            -
             | 
| 85 | 
            +
                  # @param only_finalized [Boolean] The flag to get a UTXO with status only finalized
         | 
| 86 | 
            +
                  # @param label [String] This label is used to filtered the UTXOs with labeled if a key or Utxo is labeled.
         | 
| 87 | 
            +
                  #                    - If label is not specified (label=nil), all UTXOs will be returned.
         | 
| 88 | 
            +
                  #                    - If label=:unlabeled, only unlabeled UTXOs will be returned.
         | 
| 89 | 
            +
                  def list_unspent(only_finalized = true, label = nil)
         | 
| 90 | 
            +
                    wallet_adapter.list_unspent(id, only_finalized, label)
         | 
| 74 91 | 
             
                  end
         | 
| 75 92 |  | 
| 76 93 | 
             
                  def delete
         | 
| 77 94 | 
             
                    wallet_adapter.delete_wallet(id)
         | 
| 78 95 | 
             
                  end
         | 
| 79 96 |  | 
| 80 | 
            -
                   | 
| 81 | 
            -
             | 
| 97 | 
            +
                  # @param [Tapyrus::Tx] tx The tx that is signed
         | 
| 98 | 
            +
                  # @param [Array<Hash>] prev_txs An array of hash that represents unbroadcasted transaction outputs used by signing tx
         | 
| 99 | 
            +
                  #   @option prev_txs [String] :txid
         | 
| 100 | 
            +
                  #   @option prev_txs [Integer] :vout
         | 
| 101 | 
            +
                  #   @option prev_txs [String] :scriptPubkey
         | 
| 102 | 
            +
                  #   @option prev_txs [Integer] :amount
         | 
| 103 | 
            +
                  # @param [Boolean] for_fee_provider_input The flag to notify whether the caller is FeeProvider and called for signing a input that is by FeeProvider.
         | 
| 104 | 
            +
                  def sign_tx(tx, prev_txs = [], for_fee_provider_input: false)
         | 
| 105 | 
            +
                    sighashtype = Tapyrus::SIGHASH_TYPE[:all]
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                    if !for_fee_provider_input && Glueby.configuration.fee_provider_bears?
         | 
| 108 | 
            +
                      sighashtype |= Tapyrus::SIGHASH_TYPE[:anyonecanpay]
         | 
| 109 | 
            +
                    end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                    wallet_adapter.sign_tx(id, tx, prev_txs, sighashtype: sighashtype)
         | 
| 82 112 | 
             
                  end
         | 
| 83 113 |  | 
| 84 | 
            -
                   | 
| 114 | 
            +
                  # Broadcast a transaction via Tapyrus Core RPC
         | 
| 115 | 
            +
                  # @param [Tapyrus::Tx] tx The tx that would be broadcasted
         | 
| 116 | 
            +
                  # @option [Boolean] without_fee_provider The flag to avoid to use FeeProvider temporary.
         | 
| 117 | 
            +
                  # @param [Proc] block The block that is called before broadcasting. It can be used to handle tx that is modified by FeeProvider.
         | 
| 118 | 
            +
                  def broadcast(tx, without_fee_provider: false, &block)
         | 
| 119 | 
            +
                    tx = FeeProvider.provide(tx) if !without_fee_provider && Glueby.configuration.fee_provider_bears?
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                    block.call(tx) if block
         | 
| 122 | 
            +
             | 
| 85 123 | 
             
                    wallet_adapter.broadcast(id, tx)
         | 
| 124 | 
            +
                    tx
         | 
| 86 125 | 
             
                  end
         | 
| 87 126 |  | 
| 88 | 
            -
                  def receive_address
         | 
| 89 | 
            -
                    wallet_adapter.receive_address(id)
         | 
| 127 | 
            +
                  def receive_address(label = nil)
         | 
| 128 | 
            +
                    wallet_adapter.receive_address(id, label)
         | 
| 90 129 | 
             
                  end
         | 
| 91 130 |  | 
| 92 131 | 
             
                  def change_address
         | 
| @@ -97,8 +136,8 @@ module Glueby | |
| 97 136 | 
             
                    wallet_adapter.create_pubkey(id)
         | 
| 98 137 | 
             
                  end
         | 
| 99 138 |  | 
| 100 | 
            -
                  def collect_uncolored_outputs(amount)
         | 
| 101 | 
            -
                    utxos = list_unspent
         | 
| 139 | 
            +
                  def collect_uncolored_outputs(amount, label = nil)
         | 
| 140 | 
            +
                    utxos = list_unspent(true, label)
         | 
| 102 141 |  | 
| 103 142 | 
             
                    utxos.inject([0, []]) do |sum, output|
         | 
| 104 143 | 
             
                      next sum if output[:color_id]
         | 
| @@ -112,6 +151,10 @@ module Glueby | |
| 112 151 | 
             
                    raise Glueby::Contract::Errors::InsufficientFunds
         | 
| 113 152 | 
             
                  end
         | 
| 114 153 |  | 
| 154 | 
            +
                  def get_addresses(label = nil)
         | 
| 155 | 
            +
                    wallet_adapter.get_addresses(id, label)
         | 
| 156 | 
            +
                  end
         | 
| 157 | 
            +
             | 
| 115 158 | 
             
                  private
         | 
| 116 159 |  | 
| 117 160 | 
             
                  def wallet_adapter
         | 
| @@ -7,8 +7,10 @@ module Glueby | |
| 7 7 | 
             
                  class AbstractWalletAdapter
         | 
| 8 8 | 
             
                    # Creates a new wallet inside the wallet component and returns `wallet_id`. The created
         | 
| 9 9 | 
             
                    # wallet is loaded from at first.
         | 
| 10 | 
            +
                    # @params [String] wallet_id - Option. The wallet id that if for the wallet to be created. If this is nil, wallet adapter generates it.
         | 
| 10 11 | 
             
                    # @return [String] wallet_id
         | 
| 11 | 
            -
                     | 
| 12 | 
            +
                    # @raise [Glueby::Internal::Wallet::Errors::WalletAlreadyCreated] when the specified wallet has been already created.
         | 
| 13 | 
            +
                    def create_wallet(wallet_id = nil)
         | 
| 12 14 | 
             
                      raise NotImplementedError, "You must implement #{self.class}##{__method__}"
         | 
| 13 15 | 
             
                    end
         | 
| 14 16 |  | 
| @@ -32,6 +34,7 @@ module Glueby | |
| 32 34 | 
             
                    #
         | 
| 33 35 | 
             
                    # @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
         | 
| 34 36 | 
             
                    # @raise [Glueby::Internal::Wallet::Errors::WalletAlreadyLoaded] when the specified wallet has been already loaded.
         | 
| 37 | 
            +
                    # @raise [Glueby::Internal::Wallet::Errors::WalletNotFound] when the specified wallet is not found.
         | 
| 35 38 | 
             
                    def load_wallet(wallet_id)
         | 
| 36 39 | 
             
                      raise NotImplementedError, "You must implement #{self.class}##{__method__}"
         | 
| 37 40 | 
             
                    end
         | 
| @@ -62,11 +65,13 @@ module Glueby | |
| 62 65 | 
             
                      raise NotImplementedError, "You must implement #{self.class}##{__method__}"
         | 
| 63 66 | 
             
                    end
         | 
| 64 67 |  | 
| 65 | 
            -
                    # Returns  | 
| 68 | 
            +
                    # Returns the UTXOs that the wallet has.
         | 
| 69 | 
            +
                    # If label is specified, return UTXOs filtered with label
         | 
| 66 70 | 
             
                    #
         | 
| 67 71 | 
             
                    # @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
         | 
| 68 72 | 
             
                    # @param [Boolean] only_finalized - The UTXOs includes only finalized UTXO value if it
         | 
| 69 73 | 
             
                    #                                   is true. Default is true.
         | 
| 74 | 
            +
                    # @param [String] label - Label for filtering UTXOs
         | 
| 70 75 | 
             
                    # @return [Array of UTXO]
         | 
| 71 76 | 
             
                    #
         | 
| 72 77 | 
             
                    # ## The UTXO structure
         | 
| @@ -75,7 +80,7 @@ module Glueby | |
| 75 80 | 
             
                    # - vout: [Integer] Output index
         | 
| 76 81 | 
             
                    # - amount: [Integer] Amount of the UTXO as tapyrus unit
         | 
| 77 82 | 
             
                    # - finalized: [Boolean] Whether the UTXO is finalized
         | 
| 78 | 
            -
                    def list_unspent(wallet_id, only_finalized = true)
         | 
| 83 | 
            +
                    def list_unspent(wallet_id, only_finalized = true, label = nil)
         | 
| 79 84 | 
             
                      raise NotImplementedError, "You must implement #{self.class}##{__method__}"
         | 
| 80 85 | 
             
                    end
         | 
| 81 86 |  | 
| @@ -83,10 +88,12 @@ module Glueby | |
| 83 88 | 
             
                    #
         | 
| 84 89 | 
             
                    # @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
         | 
| 85 90 | 
             
                    # @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. | 
| 91 | 
            +
                    # @param [Array] prevtxs - array of hash that represents unbroadcasted transaction outputs used by signing tx.
         | 
| 87 92 | 
             
                    #                Each hash has `txid`, `vout`, `scriptPubKey`, `amount` fields.
         | 
| 93 | 
            +
                    # @param [Integer] sighashtype - The sighash flag for each signature that would be produced here.
         | 
| 88 94 | 
             
                    # @return [Tapyrus::Tx]
         | 
| 89 | 
            -
                     | 
| 95 | 
            +
                    # @raise [Glueby::Internal::Wallet::Errors::InvalidSighashType] when the specified sighashtype is invalid
         | 
| 96 | 
            +
                    def sign_tx(wallet_id, tx, prevtxs = [], sighashtype: Tapyrus::SIGHASH_TYPE[:all])
         | 
| 90 97 | 
             
                      raise NotImplementedError, "You must implement #{self.class}##{__method__}"
         | 
| 91 98 | 
             
                    end
         | 
| 92 99 |  | 
| @@ -102,8 +109,9 @@ module Glueby | |
| 102 109 | 
             
                    # Returns an address to receive coin.
         | 
| 103 110 | 
             
                    #
         | 
| 104 111 | 
             
                    # @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
         | 
| 112 | 
            +
                    # @param [String] label The label associated with this address.
         | 
| 105 113 | 
             
                    # @return [String] P2PKH address
         | 
| 106 | 
            -
                    def receive_address(wallet_id)
         | 
| 114 | 
            +
                    def receive_address(wallet_id, label = nil)
         | 
| 107 115 | 
             
                      raise NotImplementedError, "You must implement #{self.class}##{__method__}"
         | 
| 108 116 | 
             
                    end
         | 
| 109 117 |  | 
| @@ -125,6 +133,17 @@ module Glueby | |
| 125 133 | 
             
                    def create_pubkey(wallet_id)
         | 
| 126 134 | 
             
                      raise NotImplementedError, "You must implement #{self.class}##{__method__}"
         | 
| 127 135 | 
             
                    end
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                    # Returns an array of addresses
         | 
| 138 | 
            +
                    #
         | 
| 139 | 
            +
                    # This method is expected to return the list of addresses that wallet has.
         | 
| 140 | 
            +
                    #
         | 
| 141 | 
            +
                    # @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
         | 
| 142 | 
            +
                    # @param [String] label The label to filter the addresses.
         | 
| 143 | 
            +
                    # @return [Array<String>] array of P2PKH address
         | 
| 144 | 
            +
                    def get_addresses(wallet_id, label = nil)
         | 
| 145 | 
            +
                      raise NotImplementedError, "You must implement #{self.class}##{__method__}"
         | 
| 146 | 
            +
                    end
         | 
| 128 147 | 
             
                  end
         | 
| 129 148 | 
             
                end
         | 
| 130 149 | 
             
              end
         | 
| @@ -13,13 +13,16 @@ module Glueby | |
| 13 13 |  | 
| 14 14 | 
             
                      # @param [Tapyrus::Tx] tx
         | 
| 15 15 | 
             
                      # @param [Array] prevtxs array of outputs
         | 
| 16 | 
            -
                       | 
| 16 | 
            +
                      # @param [Integer] sighashtype
         | 
| 17 | 
            +
                      def sign(tx, prevtxs = [], sighashtype: Tapyrus::SIGHASH_TYPE[:all])
         | 
| 18 | 
            +
                        validate_sighashtype!(sighashtype)
         | 
| 19 | 
            +
             | 
| 17 20 | 
             
                        tx.inputs.each.with_index do |input, index|
         | 
| 18 21 | 
             
                          script_pubkey = script_for_input(input, prevtxs)
         | 
| 19 22 | 
             
                          next unless script_pubkey
         | 
| 20 23 | 
             
                          key = Key.key_for_script(script_pubkey)
         | 
| 21 24 | 
             
                          next unless key
         | 
| 22 | 
            -
                          sign_tx_for_p2pkh(tx, index, key, script_pubkey)
         | 
| 25 | 
            +
                          sign_tx_for_p2pkh(tx, index, key, script_pubkey, sighashtype)
         | 
| 23 26 | 
             
                        end
         | 
| 24 27 | 
             
                        tx
         | 
| 25 28 | 
             
                      end
         | 
| @@ -30,9 +33,9 @@ module Glueby | |
| 30 33 |  | 
| 31 34 | 
             
                      private
         | 
| 32 35 |  | 
| 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) + [ | 
| 36 | 
            +
                      def sign_tx_for_p2pkh(tx, index, key, script_pubkey, sighashtype)
         | 
| 37 | 
            +
                        sighash = tx.sighash_for_input(index, script_pubkey, hash_type: sighashtype)
         | 
| 38 | 
            +
                        sig = key.sign(sighash) + [sighashtype].pack('C')
         | 
| 36 39 | 
             
                        script_sig = Tapyrus::Script.parse_from_payload(Tapyrus::Script.pack_pushdata(sig) + Tapyrus::Script.pack_pushdata(key.public_key.htb))
         | 
| 37 40 | 
             
                        tx.inputs[index].script_sig = script_sig
         | 
| 38 41 | 
             
                      end
         | 
| @@ -47,6 +50,13 @@ module Glueby | |
| 47 50 | 
             
                          Tapyrus::Script.parse_from_payload(output[:scriptPubKey].htb) if output
         | 
| 48 51 | 
             
                        end
         | 
| 49 52 | 
             
                      end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                      def validate_sighashtype!(sighashtype)
         | 
| 55 | 
            +
                        hash_type = sighashtype & (~(Tapyrus::SIGHASH_TYPE[:anyonecanpay]))
         | 
| 56 | 
            +
                        if hash_type < Tapyrus::SIGHASH_TYPE[:all] || hash_type > Tapyrus::SIGHASH_TYPE[:single]
         | 
| 57 | 
            +
                          raise Errors::InvalidSighashType, "Invalid sighash type '#{sighashtype}'"
         | 
| 58 | 
            +
                        end
         | 
| 59 | 
            +
                      end
         | 
| 50 60 | 
             
                    end
         | 
| 51 61 | 
             
                  end
         | 
| 52 62 | 
             
                end
         | 
| @@ -54,9 +54,16 @@ module Glueby | |
| 54 54 | 
             
                  # alice_wallet.balances
         | 
| 55 55 | 
             
                  # ```
         | 
| 56 56 | 
             
                  class ActiveRecordWalletAdapter < AbstractWalletAdapter
         | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 57 | 
            +
             | 
| 58 | 
            +
                    autoload :Syncer, 'glueby/internal/wallet/active_record_wallet_adapter/syncer'
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    def create_wallet(wallet_id = nil)
         | 
| 61 | 
            +
                      wallet_id = SecureRandom.hex(16) unless wallet_id
         | 
| 62 | 
            +
                      begin
         | 
| 63 | 
            +
                        AR::Wallet.create!(wallet_id: wallet_id)
         | 
| 64 | 
            +
                      rescue ActiveRecord::RecordInvalid => _
         | 
| 65 | 
            +
                        raise Errors::WalletAlreadyCreated, "wallet_id '#{wallet_id}' is already exists"
         | 
| 66 | 
            +
                      end
         | 
| 60 67 | 
             
                      wallet_id
         | 
| 61 68 | 
             
                    end
         | 
| 62 69 |  | 
| @@ -65,6 +72,7 @@ module Glueby | |
| 65 72 | 
             
                    end
         | 
| 66 73 |  | 
| 67 74 | 
             
                    def load_wallet(wallet_id)
         | 
| 75 | 
            +
                      raise Errors::WalletNotFound, "Wallet #{wallet_id} does not found" unless AR::Wallet.where(wallet_id: wallet_id).exists?
         | 
| 68 76 | 
             
                    end
         | 
| 69 77 |  | 
| 70 78 | 
             
                    def unload_wallet(wallet_id)
         | 
| @@ -81,10 +89,12 @@ module Glueby | |
| 81 89 | 
             
                      utxos.sum(&:value)
         | 
| 82 90 | 
             
                    end
         | 
| 83 91 |  | 
| 84 | 
            -
                    def list_unspent(wallet_id, only_finalized = true)
         | 
| 92 | 
            +
                    def list_unspent(wallet_id, only_finalized = true, label = nil)
         | 
| 85 93 | 
             
                      wallet = AR::Wallet.find_by(wallet_id: wallet_id)
         | 
| 86 94 | 
             
                      utxos = wallet.utxos
         | 
| 87 95 | 
             
                      utxos = utxos.where(status: :finalized) if only_finalized
         | 
| 96 | 
            +
                      utxos = utxos.where(label: label) if label && (label != :unlabeled)
         | 
| 97 | 
            +
                      utxos = utxos.where(label: nil) if label == :unlabeled
         | 
| 88 98 | 
             
                      utxos.map do |utxo|
         | 
| 89 99 | 
             
                        {
         | 
| 90 100 | 
             
                          txid: utxo.txid,
         | 
| @@ -97,9 +107,9 @@ module Glueby | |
| 97 107 | 
             
                      end
         | 
| 98 108 | 
             
                    end
         | 
| 99 109 |  | 
| 100 | 
            -
                    def sign_tx(wallet_id, tx, prevtxs = [])
         | 
| 110 | 
            +
                    def sign_tx(wallet_id, tx, prevtxs = [], sighashtype: Tapyrus::SIGHASH_TYPE[:all])
         | 
| 101 111 | 
             
                      wallet = AR::Wallet.find_by(wallet_id: wallet_id)
         | 
| 102 | 
            -
                      wallet.sign(tx, prevtxs)
         | 
| 112 | 
            +
                      wallet.sign(tx, prevtxs, sighashtype: sighashtype)
         | 
| 103 113 | 
             
                    end
         | 
| 104 114 |  | 
| 105 115 | 
             
                    def broadcast(wallet_id, tx)
         | 
| @@ -110,9 +120,9 @@ module Glueby | |
| 110 120 | 
             
                      end
         | 
| 111 121 | 
             
                    end
         | 
| 112 122 |  | 
| 113 | 
            -
                    def receive_address(wallet_id)
         | 
| 123 | 
            +
                    def receive_address(wallet_id, label = nil)
         | 
| 114 124 | 
             
                      wallet = AR::Wallet.find_by(wallet_id: wallet_id)
         | 
| 115 | 
            -
                      key = wallet.keys.create(purpose: :receive)
         | 
| 125 | 
            +
                      key = wallet.keys.create(purpose: :receive, label: label)
         | 
| 116 126 | 
             
                      key.address
         | 
| 117 127 | 
             
                    end
         | 
| 118 128 |  | 
| @@ -127,6 +137,13 @@ module Glueby | |
| 127 137 | 
             
                      key = wallet.keys.create(purpose: :receive)
         | 
| 128 138 | 
             
                      Tapyrus::Key.new(pubkey: key.public_key)
         | 
| 129 139 | 
             
                    end
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                    def get_addresses(wallet_id, label = nil)
         | 
| 142 | 
            +
                      wallet = AR::Wallet.find_by(wallet_id: wallet_id)
         | 
| 143 | 
            +
                      keys = wallet.keys
         | 
| 144 | 
            +
                      keys = keys.where(label: label) if label
         | 
| 145 | 
            +
                      keys.map(&:address)
         | 
| 146 | 
            +
                    end
         | 
| 130 147 | 
             
                  end
         | 
| 131 148 | 
             
                end
         | 
| 132 149 | 
             
              end
         | 
| @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            module Glueby
         | 
| 2 | 
            +
              module Internal
         | 
| 3 | 
            +
                class Wallet
         | 
| 4 | 
            +
                  class ActiveRecordWalletAdapter
         | 
| 5 | 
            +
                    class Syncer
         | 
| 6 | 
            +
                      def tx_sync(tx)
         | 
| 7 | 
            +
                        Glueby::Internal::Wallet::AR::Utxo.destroy_for_inputs(tx)
         | 
| 8 | 
            +
                        Glueby::Internal::Wallet::AR::Utxo.create_or_update_for_outputs(tx)
         | 
| 9 | 
            +
                      end
         | 
| 10 | 
            +
                    end
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
            end
         | 
| @@ -5,6 +5,9 @@ module Glueby | |
| 5 5 | 
             
                    class ShouldInitializeWalletAdapter < StandardError; end
         | 
| 6 6 | 
             
                    class WalletUnloaded < StandardError; end
         | 
| 7 7 | 
             
                    class WalletAlreadyLoaded < StandardError; end
         | 
| 8 | 
            +
                    class WalletAlreadyCreated < StandardError; end
         | 
| 9 | 
            +
                    class WalletNotFound < StandardError; end
         | 
| 10 | 
            +
                    class InvalidSighashType < StandardError; end
         | 
| 8 11 | 
             
                  end
         | 
| 9 12 | 
             
                end
         | 
| 10 13 | 
             
              end
         | 
| @@ -28,9 +28,18 @@ module Glueby | |
| 28 28 | 
             
                    RPC_WALLET_ERROR_ERROR_CODE = -4 # Unspecified problem with wallet (key not found etc.)
         | 
| 29 29 | 
             
                    RPC_WALLET_NOT_FOUND_ERROR_CODE = -18 # Invalid wallet specified
         | 
| 30 30 |  | 
| 31 | 
            -
                    def create_wallet
         | 
| 32 | 
            -
                      wallet_id = SecureRandom.hex(16)
         | 
| 33 | 
            -
                       | 
| 31 | 
            +
                    def create_wallet(wallet_id = nil)
         | 
| 32 | 
            +
                      wallet_id = SecureRandom.hex(16) unless wallet_id
         | 
| 33 | 
            +
                      begin
         | 
| 34 | 
            +
                        RPC.client.createwallet(wallet_name(wallet_id))
         | 
| 35 | 
            +
                      rescue Tapyrus::RPC::Error => ex
         | 
| 36 | 
            +
                        if ex.rpc_error && ex.rpc_error['code'] == RPC_WALLET_ERROR_ERROR_CODE && /Wallet wallet-#{wallet_id} already exists\./ =~ ex.rpc_error['message']
         | 
| 37 | 
            +
                          raise Errors::WalletAlreadyCreated, "Wallet #{wallet_id} has been already created."
         | 
| 38 | 
            +
                        else
         | 
| 39 | 
            +
                          raise ex
         | 
| 40 | 
            +
                        end
         | 
| 41 | 
            +
                      end
         | 
| 42 | 
            +
             | 
| 34 43 | 
             
                      wallet_id
         | 
| 35 44 | 
             
                    end
         | 
| 36 45 |  | 
| @@ -42,10 +51,11 @@ module Glueby | |
| 42 51 |  | 
| 43 52 | 
             
                    def load_wallet(wallet_id)
         | 
| 44 53 | 
             
                      RPC.client.loadwallet(wallet_name(wallet_id))
         | 
| 45 | 
            -
                    rescue  | 
| 46 | 
            -
                       | 
| 47 | 
            -
                      if json.is_a?(Hash) && json['code'] == RPC_WALLET_ERROR_ERROR_CODE && /Duplicate -wallet filename specified/ =~ ex.message
         | 
| 54 | 
            +
                    rescue Tapyrus::RPC::Error => ex
         | 
| 55 | 
            +
                      if ex.rpc_error && ex.rpc_error['code'] == RPC_WALLET_ERROR_ERROR_CODE && /Duplicate -wallet filename specified/ =~ ex.rpc_error['message']
         | 
| 48 56 | 
             
                        raise Errors::WalletAlreadyLoaded, "Wallet #{wallet_id} has been already loaded."
         | 
| 57 | 
            +
                      elsif ex.rpc_error && ex.rpc_error['code'] == RPC_WALLET_NOT_FOUND_ERROR_CODE
         | 
| 58 | 
            +
                        raise Errors::WalletNotFound, "Wallet #{wallet_id} does not found"
         | 
| 49 59 | 
             
                      else
         | 
| 50 60 | 
             
                        raise ex
         | 
| 51 61 | 
             
                      end
         | 
| @@ -73,11 +83,14 @@ module Glueby | |
| 73 83 | 
             
                      end
         | 
| 74 84 | 
             
                    end
         | 
| 75 85 |  | 
| 76 | 
            -
                    def list_unspent(wallet_id, only_finalized = true)
         | 
| 86 | 
            +
                    def list_unspent(wallet_id, only_finalized = true, label = nil)
         | 
| 77 87 | 
             
                      perform_as(wallet_id) do |client|
         | 
| 78 88 | 
             
                        min_conf = only_finalized ? 1 : 0
         | 
| 79 89 | 
             
                        res = client.listunspent(min_conf)
         | 
| 80 90 |  | 
| 91 | 
            +
                        res = res.filter { |i| i['label'] == label } if label && (label != :unlabeled)
         | 
| 92 | 
            +
                        res = res.filter { |i| i['label'] == "" } if label == :unlabeled
         | 
| 93 | 
            +
             
         | 
| 81 94 | 
             
                        res.map do |i|
         | 
| 82 95 | 
             
                          script = Tapyrus::Script.parse_from_payload(i['scriptPubKey'].htb)
         | 
| 83 96 | 
             
                          color_id = if script.cp2pkh? || script.cp2sh?
         | 
| @@ -95,9 +108,9 @@ module Glueby | |
| 95 108 | 
             
                      end
         | 
| 96 109 | 
             
                    end
         | 
| 97 110 |  | 
| 98 | 
            -
                    def sign_tx(wallet_id, tx, prevtxs = [])
         | 
| 111 | 
            +
                    def sign_tx(wallet_id, tx, prevtxs = [], sighashtype: Tapyrus::SIGHASH_TYPE[:all])
         | 
| 99 112 | 
             
                      perform_as(wallet_id) do |client|
         | 
| 100 | 
            -
                        res = client.signrawtransactionwithwallet(tx.to_hex, prevtxs)
         | 
| 113 | 
            +
                        res = client.signrawtransactionwithwallet(tx.to_hex, prevtxs, encode_sighashtype(sighashtype))
         | 
| 101 114 | 
             
                        if res['complete']
         | 
| 102 115 | 
             
                          Tapyrus::Tx.parse_from_payload(res['hex'].htb)
         | 
| 103 116 | 
             
                        else
         | 
| @@ -112,9 +125,9 @@ module Glueby | |
| 112 125 | 
             
                      end
         | 
| 113 126 | 
             
                    end
         | 
| 114 127 |  | 
| 115 | 
            -
                    def receive_address(wallet_id)
         | 
| 128 | 
            +
                    def receive_address(wallet_id, label = nil)
         | 
| 116 129 | 
             
                      perform_as(wallet_id) do |client|
         | 
| 117 | 
            -
                        client.getnewaddress('', ADDRESS_TYPE)
         | 
| 130 | 
            +
                        client.getnewaddress(label || '', ADDRESS_TYPE)
         | 
| 118 131 | 
             
                      end
         | 
| 119 132 | 
             
                    end
         | 
| 120 133 |  | 
| @@ -138,9 +151,8 @@ module Glueby | |
| 138 151 | 
             
                      RPC.perform_as(wallet_name(wallet_id)) do |client|
         | 
| 139 152 | 
             
                        begin
         | 
| 140 153 | 
             
                          yield(client)
         | 
| 141 | 
            -
                        rescue  | 
| 142 | 
            -
                           | 
| 143 | 
            -
                          if json.is_a?(Hash) && json['code'] == RPC_WALLET_NOT_FOUND_ERROR_CODE
         | 
| 154 | 
            +
                        rescue Tapyrus::RPC::Error => ex
         | 
| 155 | 
            +
                          if ex.rpc_error && ex.rpc_error['code'] == RPC_WALLET_NOT_FOUND_ERROR_CODE
         | 
| 144 156 | 
             
                            raise Errors::WalletUnloaded, "The wallet #{wallet_id} is unloaded. You should load before use it."
         | 
| 145 157 | 
             
                          else
         | 
| 146 158 | 
             
                            raise ex
         | 
| @@ -152,6 +164,22 @@ module Glueby | |
| 152 164 | 
             
                    def wallet_name(wallet_id)
         | 
| 153 165 | 
             
                      "#{WALLET_PREFIX}#{wallet_id}"
         | 
| 154 166 | 
             
                    end
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                    def encode_sighashtype(sighashtype)
         | 
| 169 | 
            +
                      type = case sighashtype & (~(Tapyrus::SIGHASH_TYPE[:anyonecanpay]))
         | 
| 170 | 
            +
                             when Tapyrus::SIGHASH_TYPE[:all] then 'ALL'
         | 
| 171 | 
            +
                             when Tapyrus::SIGHASH_TYPE[:none] then 'NONE'
         | 
| 172 | 
            +
                             when Tapyrus::SIGHASH_TYPE[:single] then 'SIGNLE'
         | 
| 173 | 
            +
                             else
         | 
| 174 | 
            +
                               raise Errors::InvalidSighashType, "Invalid sighash type '#{sighashtype}'"
         | 
| 175 | 
            +
                             end
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                      if sighashtype & Tapyrus::SIGHASH_TYPE[:anyonecanpay] == 0x80
         | 
| 178 | 
            +
                        type += '|ANYONECANPAY'
         | 
| 179 | 
            +
                      end
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                      type
         | 
| 182 | 
            +
                    end
         | 
| 155 183 | 
             
                  end
         | 
| 156 184 | 
             
                end
         | 
| 157 185 | 
             
              end
         |