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
         
     |