xrbp 0.1.3 → 0.1.4
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/examples/crawl_nodes.rb +1 -1
 - data/examples/nodestore1.rb +14 -0
 - data/examples/nodestore2.rb +42 -0
 - data/examples/p2p.rb +14 -0
 - data/lib/xrbp.rb +3 -0
 - data/lib/xrbp/common.rb +6 -0
 - data/lib/xrbp/core_ext.rb +22 -0
 - data/lib/xrbp/crypto.rb +3 -0
 - data/lib/xrbp/crypto/account.rb +33 -0
 - data/lib/xrbp/crypto/key.rb +93 -0
 - data/lib/xrbp/crypto/node.rb +27 -0
 - data/lib/xrbp/model/node.rb +2 -1
 - data/lib/xrbp/nodestore.rb +7 -0
 - data/lib/xrbp/nodestore/backends/nudb.rb +1 -0
 - data/lib/xrbp/nodestore/backends/rocksdb.rb +43 -0
 - data/lib/xrbp/nodestore/db.rb +324 -0
 - data/lib/xrbp/nodestore/format.rb +297 -0
 - data/lib/xrbp/overlay.rb +3 -0
 - data/lib/xrbp/overlay/connection.rb +86 -0
 - data/lib/xrbp/overlay/frame.rb +51 -0
 - data/lib/xrbp/overlay/handshake.rb +78 -0
 - data/lib/xrbp/overlay/ripple.proto.rb +306 -0
 - data/lib/xrbp/version.rb +1 -1
 - data/lib/xrbp/webclient/connection.rb +6 -1
 - metadata +61 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: e0ce0d6d6704d00d7fe16278ad96bfd68514f73e7b76486894ae479dee90eef8
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 149a45a103104f1e5cef4da27d6870ba26f98dcb96a4f7e6e0e1bc18038d7367
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: ed3630f368f3706c6c87e7031d25a6c3136489d6de8e3c83120c8a638a9e198d376615ed0c2fe7f5dab1f897f6aff6502a0296b0d3e03ea4ec27813d9d62b027
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: f43f8de4dc245cd2b3fe5996fd8a33f5b009ba60ea08858d0e1af9b008a4bfe71855172e734699d32c1b1dc97d853e94285efb4f11a94feac4f2c5b0087754eb
         
     | 
    
        data/examples/crawl_nodes.rb
    CHANGED
    
    
| 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            $: << File.expand_path('../../lib', __FILE__)
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'xrbp'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'xrbp/nodestore/backends/rocksdb'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            db = XRBP::NodeStore::Backends::RocksDB.new "/var/lib/rippled/rocksdb/rippledb.0899"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            #ledger = "B506ADD630CB707044B4BFFCD943C1395966692A13DD618E5BD0978A006B43BD"
         
     | 
| 
      
 8 
     | 
    
         
            +
            #ledger = [ledger].pack("H*")
         
     | 
| 
      
 9 
     | 
    
         
            +
            #puts db.ledger(ledger)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            #account = "0001bf7468341666f1f47a95e0f4d88e68b5fc7d20d77437cb22954fbbfe6127"
         
     | 
| 
      
 12 
     | 
    
         
            +
            account = "02c46b3a4130d0a329c47f0da61b829aa5d1ae53c5817e475bcd794e5107be44"
         
     | 
| 
      
 13 
     | 
    
         
            +
            account = [account].pack("H*")
         
     | 
| 
      
 14 
     | 
    
         
            +
            puts db.account(account)
         
     | 
| 
         @@ -0,0 +1,42 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            $: << File.expand_path('../../lib', __FILE__)
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'xrbp'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'xrbp/nodestore/backends/rocksdb'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            db = XRBP::NodeStore::Backends::RocksDB.new "/var/lib/rippled/rocksdb/rippledb.0899"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            db.on :unknown do |hash, node|
         
     | 
| 
      
 8 
     | 
    
         
            +
              puts "Unknown #{hash}: #{node}"
         
     | 
| 
      
 9 
     | 
    
         
            +
            end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            db.on :inner_node do |hash, node|
         
     | 
| 
      
 12 
     | 
    
         
            +
              #puts "Inner Node #{hash}"
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            db.on :ledger do |hash, ledger|
         
     | 
| 
      
 16 
     | 
    
         
            +
              #puts "Ledger #{ledger['index']}"
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            db.on :tx do |hash, tx|
         
     | 
| 
      
 20 
     | 
    
         
            +
              puts "Tx #{tx}"
         
     | 
| 
      
 21 
     | 
    
         
            +
            end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            db.on :account do |hash, account|
         
     | 
| 
      
 24 
     | 
    
         
            +
              #puts "Account #{account}"
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            ###
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            tallys = {}
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            # object iterator invokes event emitters
         
     | 
| 
      
 32 
     | 
    
         
            +
            db.each do |node|
         
     | 
| 
      
 33 
     | 
    
         
            +
                      obj = XRBP::NodeStore::Format::TYPE_INFER.decode(node.value)
         
     | 
| 
      
 34 
     | 
    
         
            +
                node_type = XRBP::NodeStore::Format::NODE_TYPES[obj["node_type"]]
         
     | 
| 
      
 35 
     | 
    
         
            +
              hash_prefix = XRBP::NodeStore::Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
              type = node_type.to_s + "/" + hash_prefix.to_s
         
     | 
| 
      
 38 
     | 
    
         
            +
              tallys[type] ||= 0
         
     | 
| 
      
 39 
     | 
    
         
            +
              tallys[type]  += 1
         
     | 
| 
      
 40 
     | 
    
         
            +
            end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            puts tallys
         
     | 
    
        data/examples/p2p.rb
    ADDED
    
    | 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            gem 'openssl', '2.1.3'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            $: << File.expand_path('../../lib', __FILE__)
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'xrbp'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            overlay = XRBP::Overlay::Connection.new "127.0.0.1", 51235
         
     | 
| 
      
 7 
     | 
    
         
            +
            overlay.connect
         
     | 
| 
      
 8 
     | 
    
         
            +
            puts overlay.handshake.response
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            overlay.read_frames do |frame|
         
     | 
| 
      
 11 
     | 
    
         
            +
              puts "Message: #{frame.type_name} (#{frame.size} bytes)"
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            overlay.close
         
     | 
    
        data/lib/xrbp.rb
    CHANGED
    
    
    
        data/lib/xrbp/common.rb
    CHANGED
    
    | 
         @@ -7,4 +7,10 @@ module XRBP 
     | 
|
| 
       7 
7 
     | 
    
         
             
              # Created on 2013-01-01
         
     | 
| 
       8 
8 
     | 
    
         
             
              # https://data.ripple.com/v2/ledgers/32570
         
     | 
| 
       9 
9 
     | 
    
         
             
              GENESIS_TIME = DateTime.new(2013, 1, 1, 0, 0, 0)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              # Convert XRP Ledger time to local time
         
     | 
| 
      
 12 
     | 
    
         
            +
              def self.from_xrp_time(xrp_time)
         
     | 
| 
      
 13 
     | 
    
         
            +
                return nil if xrp_time.nil?
         
     | 
| 
      
 14 
     | 
    
         
            +
                Time.at(xrp_time + 946684800)
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
       10 
16 
     | 
    
         
             
            end # module XRBP
         
     | 
    
        data/lib/xrbp/core_ext.rb
    CHANGED
    
    | 
         @@ -22,3 +22,25 @@ class Queue 
     | 
|
| 
       22 
22 
     | 
    
         
             
                end
         
     | 
| 
       23 
23 
     | 
    
         
             
              end
         
     | 
| 
       24 
24 
     | 
    
         
             
            end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            # @private
         
     | 
| 
      
 27 
     | 
    
         
            +
            class String
         
     | 
| 
      
 28 
     | 
    
         
            +
              # return bignum corresponding to string
         
     | 
| 
      
 29 
     | 
    
         
            +
              def to_bn
         
     | 
| 
      
 30 
     | 
    
         
            +
                bytes.inject(0) { |bn, b| (bn << 8) | b }
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            # @private
         
     | 
| 
      
 35 
     | 
    
         
            +
            class Integer
         
     | 
| 
      
 36 
     | 
    
         
            +
              # return bytes
         
     | 
| 
      
 37 
     | 
    
         
            +
              def bytes
         
     | 
| 
      
 38 
     | 
    
         
            +
                i = dup
         
     | 
| 
      
 39 
     | 
    
         
            +
                b = []
         
     | 
| 
      
 40 
     | 
    
         
            +
                until i == 0
         
     | 
| 
      
 41 
     | 
    
         
            +
                  b << (i & 0xFF)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  i  = i >> 8
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
                b
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/xrbp/crypto.rb
    ADDED
    
    
| 
         @@ -0,0 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'base58'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module XRBP
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Crypto
         
     | 
| 
      
 5 
     | 
    
         
            +
                def self.account(key=nil)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  pub = nil
         
     | 
| 
      
 7 
     | 
    
         
            +
                  if key == :secp256k1 || key.nil?
         
     | 
| 
      
 8 
     | 
    
         
            +
                    key = Key::secp256k1
         
     | 
| 
      
 9 
     | 
    
         
            +
                    key[:type] = :secp256k1
         
     | 
| 
      
 10 
     | 
    
         
            +
                    pub = key[:public]
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  elsif key == :ed25519
         
     | 
| 
      
 13 
     | 
    
         
            +
                    key = Key::ed25519
         
     | 
| 
      
 14 
     | 
    
         
            +
                    key[:type] = :ed25519
         
     | 
| 
      
 15 
     | 
    
         
            +
                    pub = "\xED" + key[:public]
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  elsif key.is_a?(Hash)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    pub = key[:public]
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  else
         
     | 
| 
      
 21 
     | 
    
         
            +
                    pub = key
         
     | 
| 
      
 22 
     | 
    
         
            +
                    key = {:public => pub}
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                      sha256 = OpenSSL::Digest::SHA256.new
         
     | 
| 
      
 26 
     | 
    
         
            +
                   ripemd160 = OpenSSL::Digest::RIPEMD160.new
         
     | 
| 
      
 27 
     | 
    
         
            +
                  account_id = [Key::TOKEN_TYPES[:account_id]].pack("C") + ripemd160.digest(sha256.digest([pub].pack("H*")))
         
     | 
| 
      
 28 
     | 
    
         
            +
                      chksum = sha256.digest(sha256.digest(account_id))[0..3]
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  { :account => Base58.binary_to_base58(account_id  + chksum, :ripple) }.merge(key)
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
              end # module Crypto
         
     | 
| 
      
 33 
     | 
    
         
            +
            end # module XRBP
         
     | 
| 
         @@ -0,0 +1,93 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'securerandom'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module XRBP
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Crypto
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Key
         
     | 
| 
      
 6 
     | 
    
         
            +
                  TOKEN_TYPES = {
         
     | 
| 
      
 7 
     | 
    
         
            +
                    :none             =>  1, # unused
         
     | 
| 
      
 8 
     | 
    
         
            +
                    :node_public      => 28,
         
     | 
| 
      
 9 
     | 
    
         
            +
                    :node_private     => 32,
         
     | 
| 
      
 10 
     | 
    
         
            +
                    :account_id       =>  0,
         
     | 
| 
      
 11 
     | 
    
         
            +
                    :account_public   => 35,
         
     | 
| 
      
 12 
     | 
    
         
            +
                    :account_secret   => 34,
         
     | 
| 
      
 13 
     | 
    
         
            +
                    :family_generator => 41,
         
     | 
| 
      
 14 
     | 
    
         
            +
                    :family_seed      => 33
         
     | 
| 
      
 15 
     | 
    
         
            +
                  }
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  ###
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  def self.priv
         
     | 
| 
      
 20 
     | 
    
         
            +
                    seed = SecureRandom.random_bytes(32)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    OpenSSL::Digest::SHA256.new.digest(seed)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def self.secp256k1
         
     | 
| 
      
 25 
     | 
    
         
            +
                      # XXX: the bitcoin secp256k1 implementation (which rippled pulls in / vendors)
         
     | 
| 
      
 26 
     | 
    
         
            +
                      #      has alot of nuances which require special configuration in openssl. For
         
     | 
| 
      
 27 
     | 
    
         
            +
                      #      the time being, mitigate this by pulling in & using the ruby
         
     | 
| 
      
 28 
     | 
    
         
            +
                      #      btc-secp256k1 bindings:
         
     | 
| 
      
 29 
     | 
    
         
            +
                      #        https://github.com/cryptape/ruby-bitcoin-secp256k1
         
     | 
| 
      
 30 
     | 
    
         
            +
                      #
         
     | 
| 
      
 31 
     | 
    
         
            +
                      #      Perhaps at some point, we can look into implementing this logic in pure-ruby:
         
     | 
| 
      
 32 
     | 
    
         
            +
                      #        https://medium.com/coinmonks/introduction-to-blockchains-bedrock-the-elliptic-curve-secp256k1-e4bd3bc17d
         
     | 
| 
      
 33 
     | 
    
         
            +
                      require 'secp256k1'
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                      spk = Secp256k1::PrivateKey.new
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                      # XXX: I'd like to generate the private key first & set,
         
     | 
| 
      
 38 
     | 
    
         
            +
                      #      but for some reason this doesn't work. When the
         
     | 
| 
      
 39 
     | 
    
         
            +
                      #      keys are loaded for signing/verification later
         
     | 
| 
      
 40 
     | 
    
         
            +
                      #      the public key is not able to verify the signature
         
     | 
| 
      
 41 
     | 
    
         
            +
                      #      generated from the private key set in this way.
         
     | 
| 
      
 42 
     | 
    
         
            +
                      # TODO: Investigate
         
     | 
| 
      
 43 
     | 
    
         
            +
                       #pk = priv
         
     | 
| 
      
 44 
     | 
    
         
            +
                      #spk.set_raw_privkey [pk].pack("H*")
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                      {  :public => spk.pubkey.serialize.unpack("H*").first,
         
     | 
| 
      
 47 
     | 
    
         
            +
                        :private => spk.send(:serialize) }
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  def self.ed25519
         
     | 
| 
      
 51 
     | 
    
         
            +
                    # XXX openssl 1.1.1 needed for EdDSA support:
         
     | 
| 
      
 52 
     | 
    
         
            +
                    #     https://www.openssl.org/blog/blog/2018/09/11/release111/
         
     | 
| 
      
 53 
     | 
    
         
            +
                    #     Until then use this:
         
     | 
| 
      
 54 
     | 
    
         
            +
                    require "ed25519"
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                    # FIXME: this works for now (eg generates valid keys),
         
     | 
| 
      
 57 
     | 
    
         
            +
                    #        but we should do this in the same way rippled does:
         
     | 
| 
      
 58 
     | 
    
         
            +
                    #        Generate private key, then generate corresponding
         
     | 
| 
      
 59 
     | 
    
         
            +
                    #        Ed25519 public key
         
     | 
| 
      
 60 
     | 
    
         
            +
                    key = Ed25519::SigningKey.generate
         
     | 
| 
      
 61 
     | 
    
         
            +
                    {  :public => key.to_bytes.unpack("H*").first.upcase,
         
     | 
| 
      
 62 
     | 
    
         
            +
                      :private => key.verify_key.to_bytes.unpack("H*").first.upcase }
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  ###
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  def self.sign_digest(key, data)
         
     | 
| 
      
 68 
     | 
    
         
            +
                    raise "unknown key" unless key.is_a?(Hash) && key[:type] && key[:private]
         
     | 
| 
      
 69 
     | 
    
         
            +
                    raise "invalid data" unless data.length == 32
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    if key[:type] == :secp256k1
         
     | 
| 
      
 73 
     | 
    
         
            +
                      # XXX: see note about this library above
         
     | 
| 
      
 74 
     | 
    
         
            +
                      require 'secp256k1'
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                      pk = Secp256k1::PrivateKey.new
         
     | 
| 
      
 77 
     | 
    
         
            +
                      pk.set_raw_privkey [key[:private]].pack("H*")
         
     | 
| 
      
 78 
     | 
    
         
            +
                      #pk.pubkey.deserialize [key[:public]].pack("H*")
         
     | 
| 
      
 79 
     | 
    
         
            +
                      sig_raw = pk.ecdsa_sign data, raw: true
         
     | 
| 
      
 80 
     | 
    
         
            +
                      return pk.ecdsa_serialize sig_raw
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                    #elsif key[:type] == :ed25519
         
     | 
| 
      
 83 
     | 
    
         
            +
                      # TODO
         
     | 
| 
      
 84 
     | 
    
         
            +
                    end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                    raise "unknown key type"
         
     | 
| 
      
 87 
     | 
    
         
            +
                  end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                  def self.verify(key, data, expected)
         
     | 
| 
      
 90 
     | 
    
         
            +
                  end
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
              end # module Crypto
         
     | 
| 
      
 93 
     | 
    
         
            +
            end # module XRBP
         
     | 
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'base58'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module XRBP
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Crypto
         
     | 
| 
      
 5 
     | 
    
         
            +
                def self.node(key=nil)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  pub = nil
         
     | 
| 
      
 7 
     | 
    
         
            +
                  if key == :secp256k1 || key.nil?
         
     | 
| 
      
 8 
     | 
    
         
            +
                    key = Key::secp256k1
         
     | 
| 
      
 9 
     | 
    
         
            +
                    key[:type] = :secp256k1
         
     | 
| 
      
 10 
     | 
    
         
            +
                    pub = key[:public]
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  elsif key.is_a?(Hash)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    pub = key[:public]
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  else
         
     | 
| 
      
 16 
     | 
    
         
            +
                    pub = key
         
     | 
| 
      
 17 
     | 
    
         
            +
                    key = {:public => pub}
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                   sha256 = OpenSSL::Digest::SHA256.new
         
     | 
| 
      
 21 
     | 
    
         
            +
                  node_id = [Key::TOKEN_TYPES[:node_public]].pack("C") + [pub].pack("H*")
         
     | 
| 
      
 22 
     | 
    
         
            +
                   chksum = sha256.digest(sha256.digest(node_id))[0..3]
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  { :node => Base58.binary_to_base58(node_id + chksum, :ripple) }.merge(key)
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
              end # module Crypto
         
     | 
| 
      
 27 
     | 
    
         
            +
            end # module XRBP
         
     | 
    
        data/lib/xrbp/model/node.rb
    CHANGED
    
    | 
         @@ -11,7 +11,7 @@ module XRBP 
     | 
|
| 
       11 
11 
     | 
    
         
             
                  DEFAULT_CRAWL_PORT = 51235
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
13 
     | 
    
         
             
                  attr_accessor :ip, :port
         
     | 
| 
       14 
     | 
    
         
            -
                  attr_accessor :addr, :version, :uptime, :type
         
     | 
| 
      
 14 
     | 
    
         
            +
                  attr_accessor :addr, :version, :uptime, :type, :ledgers
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
16 
     | 
    
         
             
                  # Return unique node id
         
     | 
| 
       17 
17 
     | 
    
         
             
                  def id
         
     | 
| 
         @@ -71,6 +71,7 @@ module XRBP 
     | 
|
| 
       71 
71 
     | 
    
         
             
                    n.version = p["version"].split("-").last
         
     | 
| 
       72 
72 
     | 
    
         
             
                    n.uptime  = p["uptime"]
         
     | 
| 
       73 
73 
     | 
    
         
             
                    n.type    = p["type"]
         
     | 
| 
      
 74 
     | 
    
         
            +
                    n.ledgers = p["complete_ledgers"]
         
     | 
| 
       74 
75 
     | 
    
         | 
| 
       75 
76 
     | 
    
         
             
                    n
         
     | 
| 
       76 
77 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # TODO
         
     | 
| 
         @@ -0,0 +1,43 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "rocksdb"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module XRBP
         
     | 
| 
      
 4 
     | 
    
         
            +
              module NodeStore
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Backends
         
     | 
| 
      
 6 
     | 
    
         
            +
                  class RocksDB < DB
         
     | 
| 
      
 7 
     | 
    
         
            +
                    # cap max open files for performance
         
     | 
| 
      
 8 
     | 
    
         
            +
                    MAX_OPEN_FILES = 200
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                    def initialize(path)
         
     | 
| 
      
 11 
     | 
    
         
            +
                      @db = ::RocksDB::DB.new path,
         
     | 
| 
      
 12 
     | 
    
         
            +
                              {:readonly => true,
         
     | 
| 
      
 13 
     | 
    
         
            +
                         :max_open_files => MAX_OPEN_FILES}
         
     | 
| 
      
 14 
     | 
    
         
            +
                    end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    def [](key)
         
     | 
| 
      
 17 
     | 
    
         
            +
                      @db[key]
         
     | 
| 
      
 18 
     | 
    
         
            +
                    end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                    def each
         
     | 
| 
      
 21 
     | 
    
         
            +
                      iterator = @db.new_iterator
         
     | 
| 
      
 22 
     | 
    
         
            +
                      iterator.seek_to_first
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                      while(iterator.valid)
         
     | 
| 
      
 25 
     | 
    
         
            +
                        type, obj = infer_type(iterator.value)
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                        if type
         
     | 
| 
      
 28 
     | 
    
         
            +
                          emit type, iterator.key, obj
         
     | 
| 
      
 29 
     | 
    
         
            +
                        else
         
     | 
| 
      
 30 
     | 
    
         
            +
                          emit :unknown, iterator.key,
         
     | 
| 
      
 31 
     | 
    
         
            +
                                         iterator.value
         
     | 
| 
      
 32 
     | 
    
         
            +
                        end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                        yield iterator
         
     | 
| 
      
 35 
     | 
    
         
            +
                        iterator.next
         
     | 
| 
      
 36 
     | 
    
         
            +
                      end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                      iterator.close
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end # class RocksDB
         
     | 
| 
      
 41 
     | 
    
         
            +
                end # module Backends
         
     | 
| 
      
 42 
     | 
    
         
            +
              end # module NodeStore
         
     | 
| 
      
 43 
     | 
    
         
            +
            end # module XRBP
         
     | 
| 
         @@ -0,0 +1,324 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'base58'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'openssl'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module XRBP
         
     | 
| 
      
 5 
     | 
    
         
            +
              module NodeStore
         
     | 
| 
      
 6 
     | 
    
         
            +
                class DB
         
     | 
| 
      
 7 
     | 
    
         
            +
                  include Enumerable
         
     | 
| 
      
 8 
     | 
    
         
            +
                  include EventEmitter
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def ledger(hash)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    parse_ledger(self[hash])
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def account(hash)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    parse_ledger_entry(self[hash])
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  def tx(hash)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    parse_tx(self[hash])
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  def inner_node(hash)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    parse_inner_node(self[hash])
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  ###
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  private
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  def parse_ledger(ledger)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    obj = Format::LEDGER.decode(ledger)
         
     | 
| 
      
 32 
     | 
    
         
            +
                           obj['close_time'] = XRBP::from_xrp_time(obj['close_time']).utc
         
     | 
| 
      
 33 
     | 
    
         
            +
                    obj['parent_close_time'] = XRBP::from_xrp_time(obj['parent_close_time']).utc
         
     | 
| 
      
 34 
     | 
    
         
            +
                    obj['parent_hash'].upcase!
         
     | 
| 
      
 35 
     | 
    
         
            +
                    obj['tx_hash'].upcase!
         
     | 
| 
      
 36 
     | 
    
         
            +
                    obj['account_hash'].upcase!
         
     | 
| 
      
 37 
     | 
    
         
            +
                    obj
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  def parse_encoding(encoding)
         
     | 
| 
      
 41 
     | 
    
         
            +
                        enc = encoding.unpack("C").first
         
     | 
| 
      
 42 
     | 
    
         
            +
                       type = enc >> 4
         
     | 
| 
      
 43 
     | 
    
         
            +
                      field = enc  & 0xF
         
     | 
| 
      
 44 
     | 
    
         
            +
                    encoding = encoding[1..-1]
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    if type == 0
         
     | 
| 
      
 47 
     | 
    
         
            +
                      type = encoding.unpack("C").first
         
     | 
| 
      
 48 
     | 
    
         
            +
                      encoding = encoding[1..-1]
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    if field == 0
         
     | 
| 
      
 52 
     | 
    
         
            +
                      field = encoding.unpack("C").first
         
     | 
| 
      
 53 
     | 
    
         
            +
                      encoding = encoding[1..-1]
         
     | 
| 
      
 54 
     | 
    
         
            +
                    end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                    type = Format::SERIALIZED_TYPES[type]
         
     | 
| 
      
 57 
     | 
    
         
            +
                    [[type, field], encoding]
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  def parse_ledger_entry(ledger_entry)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    # validate parsability
         
     | 
| 
      
 62 
     | 
    
         
            +
                            obj = Format::TYPE_INFER.decode(ledger_entry)
         
     | 
| 
      
 63 
     | 
    
         
            +
                      node_type = Format::NODE_TYPES[obj["node_type"]]
         
     | 
| 
      
 64 
     | 
    
         
            +
                    hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
         
     | 
| 
      
 65 
     | 
    
         
            +
                    raise unless   node_type == :account_node &&
         
     | 
| 
      
 66 
     | 
    
         
            +
                                 hash_prefix == :leaf_node
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                    # discard node type, and hash prefix
         
     | 
| 
      
 69 
     | 
    
         
            +
                    ledger_entry = ledger_entry[13..-1]
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                    # verify encoding
         
     | 
| 
      
 72 
     | 
    
         
            +
                    encoding, ledger_entry = parse_encoding(ledger_entry)
         
     | 
| 
      
 73 
     | 
    
         
            +
                    raise "Invalid Ledger Entry" unless Format::ENCODINGS[encoding] == :ledger_entry_type
         
     | 
| 
      
 74 
     | 
    
         
            +
                    ledger_entry = ledger_entry.bytes
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                    # first byte after encoding is ledger entry type prefix
         
     | 
| 
      
 77 
     | 
    
         
            +
                    prefix = ledger_entry[0..1].pack("C*")
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                    # last 32 bytes is entry index
         
     | 
| 
      
 80 
     | 
    
         
            +
                    index  = ledger_entry[-32..-1].pack("C*")
         
     | 
| 
      
 81 
     | 
    
         
            +
                                                  .unpack("H*")
         
     | 
| 
      
 82 
     | 
    
         
            +
                                                  .first
         
     | 
| 
      
 83 
     | 
    
         
            +
                                                  .upcase
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    # remaining bytes are serialized object
         
     | 
| 
      
 86 
     | 
    
         
            +
                    fields = parse_fields(ledger_entry[2...-32].pack("C*"))
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                    # TODO instantiate class corresponding to prefix &
         
     | 
| 
      
 89 
     | 
    
         
            +
                    #      populate attributes w/ fields
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                    { :prefix => prefix,
         
     | 
| 
      
 92 
     | 
    
         
            +
                      :index  => index,
         
     | 
| 
      
 93 
     | 
    
         
            +
                      :fields => fields }
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                  ###
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                  def parse_fields(fields)
         
     | 
| 
      
 99 
     | 
    
         
            +
                    parsed = {}
         
     | 
| 
      
 100 
     | 
    
         
            +
                    until fields == "" || fields.nil?
         
     | 
| 
      
 101 
     | 
    
         
            +
                      encoding, fields = parse_encoding(fields)
         
     | 
| 
      
 102 
     | 
    
         
            +
                      return parsed if encoding.first.nil?
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                      e = Format::ENCODINGS[encoding]
         
     | 
| 
      
 105 
     | 
    
         
            +
                      value, fields = parse_field(fields, encoding)
         
     | 
| 
      
 106 
     | 
    
         
            +
                      break unless value
         
     | 
| 
      
 107 
     | 
    
         
            +
                      parsed[e] = value
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                    return parsed
         
     | 
| 
      
 111 
     | 
    
         
            +
                  end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                  def parse_field(data, encoding)
         
     | 
| 
      
 114 
     | 
    
         
            +
                    length = encoding.first
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                    case length
         
     | 
| 
      
 117 
     | 
    
         
            +
                    when :uint8
         
     | 
| 
      
 118 
     | 
    
         
            +
                      return data.unpack("C").first, data[1..-1]
         
     | 
| 
      
 119 
     | 
    
         
            +
                    when :uint16
         
     | 
| 
      
 120 
     | 
    
         
            +
                      return data.unpack("S").first, data[2..-1]
         
     | 
| 
      
 121 
     | 
    
         
            +
                    when :uint32
         
     | 
| 
      
 122 
     | 
    
         
            +
                      return data.unpack("L").first, data[4..-1]
         
     | 
| 
      
 123 
     | 
    
         
            +
                    when :uint64
         
     | 
| 
      
 124 
     | 
    
         
            +
                      return data.unpack("Q").first, data[8..-1]
         
     | 
| 
      
 125 
     | 
    
         
            +
                    when :hash128
         
     | 
| 
      
 126 
     | 
    
         
            +
                      return data.unpack("H32").first, data[16..-1]
         
     | 
| 
      
 127 
     | 
    
         
            +
                    when :hash160
         
     | 
| 
      
 128 
     | 
    
         
            +
                      return data.unpack("H40").first, data[20..-1]
         
     | 
| 
      
 129 
     | 
    
         
            +
                    when :hash256
         
     | 
| 
      
 130 
     | 
    
         
            +
                      return data.unpack("H64").first, data[32..-1]
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                    when :amount
         
     | 
| 
      
 133 
     | 
    
         
            +
                      amount = data[0..7].unpack("Q>").first
         
     | 
| 
      
 134 
     | 
    
         
            +
                         xrp = amount < 0x8000000000000000
         
     | 
| 
      
 135 
     | 
    
         
            +
                      return  (amount & 0x3FFFFFFFFFFFFFFF), data[8..-1] if xrp
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                      sign = (amount & 0x4000000000000000) >> 62 # 0 = neg / 1 = pos
         
     | 
| 
      
 138 
     | 
    
         
            +
                       exp = (amount & 0x3FC0000000000000) >> 54
         
     | 
| 
      
 139 
     | 
    
         
            +
                      mant = (amount & 0x003FFFFFFFFFFFFF)
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                      data = data[8..-1]
         
     | 
| 
      
 142 
     | 
    
         
            +
                      currency = Format::CURRENCY_CODE.decode(data)
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                      data = data[Format::CURRENCY_CODE.size..-1]
         
     | 
| 
      
 145 
     | 
    
         
            +
                      issuer, data = parse_account(data, 20)
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
      
 147 
     | 
    
         
            +
                      # TODO calculate value
         
     | 
| 
      
 148 
     | 
    
         
            +
                      return { :sign => sign,
         
     | 
| 
      
 149 
     | 
    
         
            +
                                :exp => exp,
         
     | 
| 
      
 150 
     | 
    
         
            +
                           :mantissa => mant,
         
     | 
| 
      
 151 
     | 
    
         
            +
                           :currency => currency,
         
     | 
| 
      
 152 
     | 
    
         
            +
                             :issuer => issuer }, data
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                    when :vl
         
     | 
| 
      
 155 
     | 
    
         
            +
                      vl, offset = parse_vl(data)
         
     | 
| 
      
 156 
     | 
    
         
            +
                      return data[offset..vl+offset-1], data[vl+offset..-1]
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                    when :account
         
     | 
| 
      
 159 
     | 
    
         
            +
                      return parse_account(data)
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                    when :array
         
     | 
| 
      
 162 
     | 
    
         
            +
                      e = Format::ENCODINGS[encoding]
         
     | 
| 
      
 163 
     | 
    
         
            +
                      return nil, data if e == :end_of_array
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                      array = []
         
     | 
| 
      
 166 
     | 
    
         
            +
                      until data == "" || data.nil?
         
     | 
| 
      
 167 
     | 
    
         
            +
                        aencoding, data = parse_encoding(data)
         
     | 
| 
      
 168 
     | 
    
         
            +
                        break if aencoding.first.nil?
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
                        e = Format::ENCODINGS[aencoding]
         
     | 
| 
      
 171 
     | 
    
         
            +
                        break if e == :end_of_array
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
                        value, data = parse_field(data, aencoding)
         
     | 
| 
      
 174 
     | 
    
         
            +
                        break unless value
         
     | 
| 
      
 175 
     | 
    
         
            +
                        array << value
         
     | 
| 
      
 176 
     | 
    
         
            +
                      end
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                      return array, data
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
      
 180 
     | 
    
         
            +
                    when :object
         
     | 
| 
      
 181 
     | 
    
         
            +
                      e = Format::ENCODINGS[encoding]
         
     | 
| 
      
 182 
     | 
    
         
            +
                      case e
         
     | 
| 
      
 183 
     | 
    
         
            +
                      when :end_of_object
         
     | 
| 
      
 184 
     | 
    
         
            +
                        return nil, data
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
                      when :signer,   :signer_entry,
         
     | 
| 
      
 187 
     | 
    
         
            +
                           :majority, :memo,
         
     | 
| 
      
 188 
     | 
    
         
            +
                           :modified_node, :created_node, :deleted_node,
         
     | 
| 
      
 189 
     | 
    
         
            +
                           :previous_fields, :final_fields, :new_fields
         
     | 
| 
      
 190 
     | 
    
         
            +
                        # TODO instantiate corresponding classes
         
     | 
| 
      
 191 
     | 
    
         
            +
                        return parse_fields(data)
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
                      #else:
         
     | 
| 
      
 194 
     | 
    
         
            +
                      end
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
                    when :pathset
         
     | 
| 
      
 197 
     | 
    
         
            +
                      pathset = []
         
     | 
| 
      
 198 
     | 
    
         
            +
                      until data == "" || data.nil?
         
     | 
| 
      
 199 
     | 
    
         
            +
                        segment = data.unpack("C").first
         
     | 
| 
      
 200 
     | 
    
         
            +
                        data = data[1..-1]
         
     | 
| 
      
 201 
     | 
    
         
            +
                        return pathset, data if segment == 0x00 # end of path
         
     | 
| 
      
 202 
     | 
    
         
            +
             
     | 
| 
      
 203 
     | 
    
         
            +
                        if segment == 0xFF # path boundry
         
     | 
| 
      
 204 
     | 
    
         
            +
                          pathset << []
         
     | 
| 
      
 205 
     | 
    
         
            +
                        else
         
     | 
| 
      
 206 
     | 
    
         
            +
                          if segment & 0x01 # path account
         
     | 
| 
      
 207 
     | 
    
         
            +
                            issuer, data = parse_account(data, 20)
         
     | 
| 
      
 208 
     | 
    
         
            +
                          end
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
                          if segment & 0x02 # path currency
         
     | 
| 
      
 211 
     | 
    
         
            +
                            currency = Format::CURRENCY_CODE.decode(data)
         
     | 
| 
      
 212 
     | 
    
         
            +
                            data = data[Format::CURRENCY_CODE.size..-1]
         
     | 
| 
      
 213 
     | 
    
         
            +
                          end
         
     | 
| 
      
 214 
     | 
    
         
            +
             
     | 
| 
      
 215 
     | 
    
         
            +
                          if segment & 0x03 # path issuer
         
     | 
| 
      
 216 
     | 
    
         
            +
                            issuer, data = parse_account(data, 20)
         
     | 
| 
      
 217 
     | 
    
         
            +
                          end
         
     | 
| 
      
 218 
     | 
    
         
            +
                        end
         
     | 
| 
      
 219 
     | 
    
         
            +
                      end
         
     | 
| 
      
 220 
     | 
    
         
            +
             
     | 
| 
      
 221 
     | 
    
         
            +
                      return pathset, data
         
     | 
| 
      
 222 
     | 
    
         
            +
             
     | 
| 
      
 223 
     | 
    
         
            +
                    when :vector256
         
     | 
| 
      
 224 
     | 
    
         
            +
                      vl, offset = parse_vl(data)
         
     | 
| 
      
 225 
     | 
    
         
            +
                      return data[offset..vl+offset-1], data[vl+offset..-1]
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
                    end
         
     | 
| 
      
 228 
     | 
    
         
            +
             
     | 
| 
      
 229 
     | 
    
         
            +
                    raise
         
     | 
| 
      
 230 
     | 
    
         
            +
                  end
         
     | 
| 
      
 231 
     | 
    
         
            +
             
     | 
| 
      
 232 
     | 
    
         
            +
                  def parse_vl(data)
         
     | 
| 
      
 233 
     | 
    
         
            +
                     data = data.bytes
         
     | 
| 
      
 234 
     | 
    
         
            +
                    first = data.first.to_i
         
     | 
| 
      
 235 
     | 
    
         
            +
                    return first, 1 if first <= 192
         
     | 
| 
      
 236 
     | 
    
         
            +
             
     | 
| 
      
 237 
     | 
    
         
            +
                    data = data[1..-1]
         
     | 
| 
      
 238 
     | 
    
         
            +
                    second = data.first.to_i
         
     | 
| 
      
 239 
     | 
    
         
            +
                    if first <= 240
         
     | 
| 
      
 240 
     | 
    
         
            +
                      return (193+(first-193)*256+second), 2
         
     | 
| 
      
 241 
     | 
    
         
            +
             
     | 
| 
      
 242 
     | 
    
         
            +
                    elsif first <= 254
         
     | 
| 
      
 243 
     | 
    
         
            +
                      data = data[1..-1]
         
     | 
| 
      
 244 
     | 
    
         
            +
                      third = data.first.to_i
         
     | 
| 
      
 245 
     | 
    
         
            +
                      return (12481 + (first-241)*65536 + second*256 + third), 3
         
     | 
| 
      
 246 
     | 
    
         
            +
                    end
         
     | 
| 
      
 247 
     | 
    
         
            +
             
     | 
| 
      
 248 
     | 
    
         
            +
                    raise
         
     | 
| 
      
 249 
     | 
    
         
            +
                  end
         
     | 
| 
      
 250 
     | 
    
         
            +
             
     | 
| 
      
 251 
     | 
    
         
            +
                  def parse_account(data, vl=nil)
         
     | 
| 
      
 252 
     | 
    
         
            +
                    unless vl
         
     | 
| 
      
 253 
     | 
    
         
            +
                      vl,offset = parse_vl(data)
         
     | 
| 
      
 254 
     | 
    
         
            +
                      data = data[offset..-1]
         
     | 
| 
      
 255 
     | 
    
         
            +
                    end
         
     | 
| 
      
 256 
     | 
    
         
            +
             
     | 
| 
      
 257 
     | 
    
         
            +
                      acct = "\0" + data[0..vl-1]
         
     | 
| 
      
 258 
     | 
    
         
            +
                    sha256 = OpenSSL::Digest::SHA256.new
         
     | 
| 
      
 259 
     | 
    
         
            +
                    digest = sha256.digest(sha256.digest(acct))[0..3]
         
     | 
| 
      
 260 
     | 
    
         
            +
                    acct  += digest
         
     | 
| 
      
 261 
     | 
    
         
            +
                    return Base58.binary_to_base58(acct, :ripple), data[vl..-1]
         
     | 
| 
      
 262 
     | 
    
         
            +
                  end
         
     | 
| 
      
 263 
     | 
    
         
            +
             
     | 
| 
      
 264 
     | 
    
         
            +
                  ###
         
     | 
| 
      
 265 
     | 
    
         
            +
             
     | 
| 
      
 266 
     | 
    
         
            +
                  def parse_tx(tx)
         
     | 
| 
      
 267 
     | 
    
         
            +
                            obj = Format::TYPE_INFER.decode(tx)
         
     | 
| 
      
 268 
     | 
    
         
            +
                      node_type = Format::NODE_TYPES[obj["node_type"]]
         
     | 
| 
      
 269 
     | 
    
         
            +
                    hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
         
     | 
| 
      
 270 
     | 
    
         
            +
                    raise unless   node_type == :tx_node &&
         
     | 
| 
      
 271 
     | 
    
         
            +
                                 hash_prefix == :tx_node
         
     | 
| 
      
 272 
     | 
    
         
            +
             
     | 
| 
      
 273 
     | 
    
         
            +
                    # discard node type, and hash prefix
         
     | 
| 
      
 274 
     | 
    
         
            +
                    tx = tx[13..-1]
         
     | 
| 
      
 275 
     | 
    
         
            +
             
     | 
| 
      
 276 
     | 
    
         
            +
                    # get node length
         
     | 
| 
      
 277 
     | 
    
         
            +
                    vl, offset = parse_vl(tx)
         
     | 
| 
      
 278 
     | 
    
         
            +
                    node, _tx = tx.bytes[offset..vl+offset-1], tx.bytes[vl+offset..-1]
         
     | 
| 
      
 279 
     | 
    
         
            +
                    node = parse_fields(node.pack("C*"))
         
     | 
| 
      
 280 
     | 
    
         
            +
             
     | 
| 
      
 281 
     | 
    
         
            +
                    # get meta length
         
     | 
| 
      
 282 
     | 
    
         
            +
                    vl, offset = parse_vl(_tx.pack("C*"))
         
     | 
| 
      
 283 
     | 
    
         
            +
                    meta, index = _tx[offset..vl+offset-1], _tx[vl+offset..-1]
         
     | 
| 
      
 284 
     | 
    
         
            +
                    meta = parse_fields(meta.pack("C*"))
         
     | 
| 
      
 285 
     | 
    
         
            +
             
     | 
| 
      
 286 
     | 
    
         
            +
                    {  :node =>  node,
         
     | 
| 
      
 287 
     | 
    
         
            +
                       :meta =>  meta,
         
     | 
| 
      
 288 
     | 
    
         
            +
                      :index => index.pack("C*").unpack("H*").first.upcase }
         
     | 
| 
      
 289 
     | 
    
         
            +
                  end
         
     | 
| 
      
 290 
     | 
    
         
            +
             
     | 
| 
      
 291 
     | 
    
         
            +
                  def parse_inner_node(node)
         
     | 
| 
      
 292 
     | 
    
         
            +
                    # verify parsability
         
     | 
| 
      
 293 
     | 
    
         
            +
                            obj = Format::TYPE_INFER.decode(node)
         
     | 
| 
      
 294 
     | 
    
         
            +
                    hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
         
     | 
| 
      
 295 
     | 
    
         
            +
                    raise unless hash_prefix == :inner_node
         
     | 
| 
      
 296 
     | 
    
         
            +
             
     | 
| 
      
 297 
     | 
    
         
            +
                    Format::INNER_NODE.decode(node)
         
     | 
| 
      
 298 
     | 
    
         
            +
                  end
         
     | 
| 
      
 299 
     | 
    
         
            +
             
     | 
| 
      
 300 
     | 
    
         
            +
                  protected
         
     | 
| 
      
 301 
     | 
    
         
            +
             
     | 
| 
      
 302 
     | 
    
         
            +
                  def infer_type(value)
         
     | 
| 
      
 303 
     | 
    
         
            +
                            obj = Format::TYPE_INFER.decode(value)
         
     | 
| 
      
 304 
     | 
    
         
            +
                      node_type = Format::NODE_TYPES[obj["node_type"]]
         
     | 
| 
      
 305 
     | 
    
         
            +
                    hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
         
     | 
| 
      
 306 
     | 
    
         
            +
             
     | 
| 
      
 307 
     | 
    
         
            +
                    if hash_prefix == :inner_node
         
     | 
| 
      
 308 
     | 
    
         
            +
                      return :inner_node, parse_inner_node(value)
         
     | 
| 
      
 309 
     | 
    
         
            +
             
     | 
| 
      
 310 
     | 
    
         
            +
                    elsif node_type == :account_node
         
     | 
| 
      
 311 
     | 
    
         
            +
                      return :account, parse_ledger_entry(value)
         
     | 
| 
      
 312 
     | 
    
         
            +
             
     | 
| 
      
 313 
     | 
    
         
            +
                    elsif node_type == :tx_node
         
     | 
| 
      
 314 
     | 
    
         
            +
                      return :tx, parse_tx(value)
         
     | 
| 
      
 315 
     | 
    
         
            +
             
     | 
| 
      
 316 
     | 
    
         
            +
                    elsif node_type == :ledger
         
     | 
| 
      
 317 
     | 
    
         
            +
                      return :ledger, parse_ledger(value)
         
     | 
| 
      
 318 
     | 
    
         
            +
                    end
         
     | 
| 
      
 319 
     | 
    
         
            +
             
     | 
| 
      
 320 
     | 
    
         
            +
                    return nil
         
     | 
| 
      
 321 
     | 
    
         
            +
                  end
         
     | 
| 
      
 322 
     | 
    
         
            +
                end # class DB
         
     | 
| 
      
 323 
     | 
    
         
            +
              end # module NodeStore
         
     | 
| 
      
 324 
     | 
    
         
            +
            end # module XRBP
         
     |