# encoding: UTF-8 # frozen_string_literal: true describe API::V2::Admin::Blockchains, type: :request do let(:admin) { create(:member, :admin, :level_3, email: 'example@gmail.com', uid: 'ID73BF61C8H0') } let(:token) { jwt_for(admin) } let(:level_3_member) { create(:member, :level_3) } let(:level_3_member_token) { jwt_for(level_3_member) } describe 'GET /api/v2/admin/blockchains/:id' do let(:blockchain) { Blockchain.find_by(key: 'eth-rinkeby') } it 'returns information about specified blockchain' do api_get "/api/v2/admin/blockchains/#{blockchain.id}", token: token expect(response).to be_successful result = JSON.parse(response.body) expect(result.fetch('id')).to eq blockchain.id expect(result.fetch('name')).to eq blockchain.name end it 'returns error in case of invalid id' do api_get "/api/v2/admin/blockchains/#{Blockchain.last.id + 42}", token: token expect(response.code).to eq '404' expect(response).to include_api_error('record.not_found') end it 'return error in case of not permitted ability' do api_get "/api/v2/admin/blockchains/#{blockchain.id}", token: level_3_member_token expect(response.code).to eq '403' expect(response).to include_api_error('admin.ability.not_permitted') end end describe 'GET /api/v2/admin/blockchains/clients' do it 'get list of all available clients' do api_get '/api/v2/admin/blockchains/clients', token: token expect(JSON.parse(response.body)).to match_array Blockchain.clients.map &:to_s end end describe 'GET /api/v2/admin/blockchains/:id/latest_block' do let(:blockchain) { Blockchain.find_by(key: "eth-rinkeby") } it 'returns error in case of invalid id' do api_get "/api/v2/admin/blockchains/#{Blockchain.last.id + 42}/latest_block", token: token expect(response.code).to eq '422' expect(response).to include_api_error('admin.blockchain.latest_block') end it 'returns error in case of node inaccessibility' do api_get "/api/v2/admin/blockchains/#{blockchain.id}/latest_block", token: token expect(response.code).to eq '422' expect(response).to include_api_error('admin.blockchain.latest_block') end context 'get latest_block' do let(:blockchain) { Blockchain.find_by(key: "eth-rinkeby") } around do |example| WebMock.disable_net_connect! example.run WebMock.allow_net_connect! end let(:eth_blockchain) do Ethereum::Blockchain.new.tap { |b| b.configure(server: 'http://127.0.0.1:8545') } end it 'returns node latest block' do block_number = '0x16b916' stub_request(:post, 'http://127.0.0.1:8545') .with(body: { jsonrpc: '2.0', id: 1, method: :eth_blockNumber, params: [] }.to_json) .to_return(body: { result: block_number, error: nil, id: 1 }.to_json) api_get "/api/v2/admin/blockchains/#{blockchain.id}/latest_block", token: token expect(response.code).to eq '200' expect(response_body).to eq 1489174 end end end describe 'GET /api/v2/admin/blockchains' do it 'lists of blockchains' do api_get '/api/v2/admin/blockchains', token: token expect(response).to be_successful result = JSON.parse(response.body) expect(result.size).to eq 3 end it 'returns blockchains by ascending order' do api_get '/api/v2/admin/blockchains', params: { ordering: 'asc', order_by: 'client'}, token: token result = JSON.parse(response.body) expect(response).to be_successful expect(result.first['client']).to eq 'bitcoin' end it 'returns paginated blockchains' do api_get '/api/v2/admin/blockchains', params: { limit: 2, page: 1 }, token: token result = JSON.parse(response.body) expect(response).to be_successful expect(response.headers.fetch('Total')).to eq '3' expect(result.size).to eq 2 expect(result.first['key']).to eq 'eth-kovan' api_get '/api/v2/admin/blockchains', params: { limit: 1, page: 2 }, token: token result = JSON.parse(response.body) expect(response).to be_successful expect(response.headers.fetch('Total')).to eq '3' expect(result.size).to eq 1 expect(result.first['key']).to eq 'eth-rinkeby' end it 'returns blockchains filtered by key' do api_get '/api/v2/admin/blockchains', params: { key: "eth-kovan" }, token: token result = JSON.parse(response.body) expect(response).to be_successful expect(response.headers.fetch('Total')).to eq '1' expect(result.size).to eq 1 expect(result.first['key']).to eq 'eth-kovan' end it 'returns error in case invalid blockchain key' do api_get '/api/v2/admin/blockchains', params: { key: "inv" }, token: token expect(response.code).to eq '422' expect(response).to include_api_error('admin.blockchain.blockchain_key_doesnt_exist') end it 'returns blockchains filtered by client' do api_get '/api/v2/admin/blockchains', params: { client: "parity" }, token: token result = JSON.parse(response.body) expect(response).to be_successful expect(response.headers.fetch('Total')).to eq '1' expect(result.size).to eq 1 expect(result.first['name']).to eq 'Ethereum Kovan' end it 'returns error in case invalid blockchain client' do api_get '/api/v2/admin/blockchains', params: { client: "inv" }, token: token expect(response.code).to eq '422' expect(response).to include_api_error('admin.blockchain.blockchain_client_doesnt_exist') end it 'returns blockchains filtered by status' do api_get '/api/v2/admin/blockchains', params: { status: "active" }, token: token result = JSON.parse(response.body) expect(response).to be_successful expect(response.headers.fetch('Total')).to eq '3' expect(result.size).to eq 3 expect(result.map { |r| r["status"]}).to all eq "active" end it 'returns error in case invalid blockchain status' do api_get '/api/v2/admin/blockchains', params: { status: "inv" }, token: token expect(response.code).to eq '422' expect(response).to include_api_error('admin.blockchain.blockchain_status_doesnt_exist') end it 'returns blockchains filtered by name' do api_get '/api/v2/admin/blockchains', params: { name: "Ethereum Kovan" }, token: token result = JSON.parse(response.body) expect(response).to be_successful expect(response.headers.fetch('Total')).to eq '1' expect(result.size).to eq 1 expect(result.first['name']).to eq 'Ethereum Kovan' end it 'returns error in case invalid blockchain name' do api_get '/api/v2/admin/blockchains', params: { name: "inv" }, token: token expect(response.code).to eq '422' expect(response).to include_api_error('admin.blockchain.blockchain_name_doesnt_exist') end it 'return error in case of not permitted ability' do api_get "/api/v2/admin/blockchains", token: level_3_member_token expect(response.code).to eq '403' expect(response).to include_api_error('admin.ability.not_permitted') end end describe 'POST /api/v2/admin/blockchains/new' do it 'creates new blockchain' do api_post '/api/v2/admin/blockchains/new', token: token, params: { key: 'test-blockchain', name: 'Test', client: 'geth',server: 'http://127.0.0.1', height: 123333, explorer_transaction: 'test', explorer_address: 'test'} result = JSON.parse(response.body) expect(response).to be_successful expect(result['key']).to eq 'test-blockchain' end it 'long blockchain key' do api_post '/api/v2/admin/blockchains/new', token: token, params: { key: Faker::String.random(1024), name: 'Test', client: 'geth',server: 'http://127.0.0.1', height: 123333, explorer_transaction: 'test', explorer_address: 'test'} expect(response).not_to be_successful expect(response).to include_api_error('admin.blockchain.key_too_long') end it 'long blockchain name' do api_post '/api/v2/admin/blockchains/new', token: token, params: { key: Faker::String.random(24), name: Faker::String.random(1024), client: 'geth',server: 'http://127.0.0.1', height: 123333, explorer_transaction: 'test', explorer_address: 'test'} expect(response).not_to be_successful expect(response).to include_api_error('admin.blockchain.name_too_long') end it 'validate height param' do api_post '/api/v2/admin/blockchains/new', token: token, params: { key: 'test-blockchain', name: 'Test', client: 'geth',server: 'http://127.0.0.1', height: -123333, explorer_transaction: 'test', explorer_address: 'test', status: 'active', min_confirmations: 6, step: 2 } expect(response).to have_http_status 422 expect(response).to include_api_error('admin.blockchain.non_positive_height') end it 'validate min_confirmations param' do api_post '/api/v2/admin/blockchains/new', token: token, params: { key: 'test-blockchain', name: 'Test', client: 'geth',server: 'http://127.0.0.1', height: 123333, explorer_transaction: 'test', explorer_address: 'test', status: 'active', min_confirmations: -6, step: 2 } expect(response).to have_http_status 422 expect(response).to include_api_error('admin.blockchain.non_positive_min_confirmations') end it 'validate status param' do api_post '/api/v2/admin/blockchains/new', token: token, params: { key: 'test-blockchain', name: 'Test', client: 'geth',server: 'http://127.0.0.1', height: 123333, explorer_transaction: 'test', explorer_address: 'test', status: 'actived', min_confirmations: 6, step: 2 } expect(response).to have_http_status 422 expect(response).to include_api_error('admin.blockchain.invalid_status') end it 'validate client param' do api_post '/api/v2/admin/blockchains/new', token: token, params: { key: 'test-blockchain', name: 'Test', client: 'gezz',server: 'http://127.0.0.1', height: 123333, explorer_transaction: 'test', explorer_address: 'test', status: 'active', min_confirmations: 6, step: 2 } expect(response).to have_http_status 422 expect(response).to include_api_error('admin.blockchain.invalid_client') end it 'checked required params' do api_post '/api/v2/admin/blockchains/new', token: token, params: { } expect(response).to have_http_status 422 expect(response).to include_api_error('admin.blockchain.missing_key') expect(response).to include_api_error('admin.blockchain.missing_name') expect(response).to include_api_error('admin.blockchain.missing_client') expect(response).to include_api_error('admin.blockchain.missing_height') end it 'validates server' do api_post '/api/v2/admin/blockchains/new', token: token, params: { key: 'test-blockchain', name: 'Test', client: 'geth',server: 'not_a_url', height: 123333, explorer_transaction: 'test', explorer_address: 'test'} expect(response).to have_http_status 422 expect(response).to include_api_error('admin.blockchain.invalid_server') end it 'return error in case of not permitted ability' do api_post '/api/v2/admin/blockchains/new', token: level_3_member_token, params: { key: 'test-blockchain', name: 'Test', client: 'geth', server: 'http://127.0.0.1', height: 123333, explorer_transaction: 'test', explorer_address: 'test', status: 'active', min_confirmations: 6, step: 2 } expect(response.code).to eq '403' expect(response).to include_api_error('admin.ability.not_permitted') end it 'key already exists' do api_post '/api/v2/admin/blockchains/new', token: token, params: { key: Blockchain.first.key, name: 'Test', client: 'geth',server: 'http://127.0.0.1', height: 123333, explorer_transaction: 'test', explorer_address: 'test'} expect(response.status).to eq 422 end end describe 'POST /api/v2/admin/blockchains/update' do context 'permissions' do let(:support) { create(:member, :admin, :level_3, role: :support, email: 'example@gmail.com', uid: 'ID73BF61C8H1') } let(:support_token) { jwt_for(support) } it 'return error in case of not permitted ability' do api_post '/api/v2/admin/blockchains/update', params: { key: 'test-blockchain', id: Blockchain.first.id }, token: support_token expect(response.code).to eq '403' expect(response).to include_api_error('admin.ability.not_permitted') end it 'returns updated blockchain' do api_post '/api/v2/admin/blockchains/update', params: { name: 'Test Blockchain', id: Blockchain.first.id }, token: token result = JSON.parse(response.body) expect(response).to be_successful expect(result['name']).to eq 'Test Blockchain' end end it 'returns updated blockchain' do api_post '/api/v2/admin/blockchains/update', params: { key: 'test-blockchain', id: Blockchain.first.id }, token: token result = JSON.parse(response.body) expect(response).to be_successful expect(result['key']).to eq 'test-blockchain' end it 'returns updated blockchain' do api_post '/api/v2/admin/blockchains/update', token: token, params: { key: 'Test-blockchain ', id: Blockchain.first.id } result = JSON.parse(response.body) expect(response).to be_successful expect(result['key']).to eq 'test-blockchain' end it 'long blockchain key' do api_post '/api/v2/admin/blockchains/update', token: token, params: { key: Faker::String.random(1024) } expect(response).not_to be_successful expect(response).to include_api_error('admin.blockchain.key_too_long') end it 'long blockchain name' do api_post '/api/v2/admin/blockchains/update', token: token, params: { name: Faker::String.random(1024) } expect(response).not_to be_successful expect(response).to include_api_error('admin.blockchain.name_too_long') end it 'validate height param' do api_post '/api/v2/admin/blockchains/update', token: token, params: { height: -123333 } expect(response).to have_http_status 422 expect(response).to include_api_error('admin.blockchain.non_positive_height') end it 'checked required params' do api_post '/api/v2/admin/blockchains/update', token: level_3_member_token, params: { key: 'test-blockchain'} expect(response).to have_http_status 422 expect(response).to include_api_error('admin.blockchain.missing_id') end it 'return error in case of not permitted ability' do api_post '/api/v2/admin/blockchains/update', token: level_3_member_token, params: { id: Blockchain.first.id } expect(response.code).to eq '403' expect(response).to include_api_error('admin.ability.not_permitted') end end describe 'POST /api/v2/admin/blockchains/process_block' do context 'returns error' do it 'in case of not permitted ability' do api_post '/api/v2/admin/blockchains/process_block', token: level_3_member_token, params: { block_number: 1, id: Blockchain.last.id } expect(response.code).to eq '403' expect(response).to include_api_error('admin.ability.not_permitted') end it 'when blockchain doesnt exist' do api_post "/api/v2/admin/blockchains/process_block", params: { block_number: 1, id: Blockchain.last.id + 1 }, token: token expect(response.code).to eq '404' expect(response).to include_api_error('record.not_found') end it 'when blockchain is not accessible' do api_post "/api/v2/admin/blockchains/process_block", params: { block_number: 1, id: Blockchain.last.id }, token: token expect(response).to include_api_error('admin.blockchain.process_block') end end context 'successful' do let!(:blockchain) { Blockchain.find_by(key: 'btc-testnet') } let(:service) { BlockchainService.new(blockchain) } let!(:currency) { create(:currency, :btc, id: 'fake') } let(:block_number) { 3 } let!(:member) { create(:member) } let!(:fake_blockchain) { create(:blockchain, 'fake-testnet') } let!(:wallet) { create(:wallet, :fake_deposit) } before do Blockchain.any_instance.stubs(:blockchain_api).returns(service) service.stubs(:latest_block_number).returns(4) clear_redis PaymentAddress.create!(member: member, wallet: wallet, address: 'fake_address') end context 'deposit' do let(:transaction) { Peatio::Transaction.new(hash: 'fake_txid', from_addresses: ['fake_address'], to_address: 'fake_address', amount: 5, block_number: block_number, currency_id: 'fake', txout: 4, status: 'success') } let(:expected_block) { Peatio::Block.new(block_number, [transaction]) } before do service.adapter.stubs(:fetch_block!).returns(expected_block) end it 'detects in the block' do expect(Deposits::Coin.where(currency: currency).exists?).to be false api_post '/api/v2/admin/blockchains/process_block', token: token, params: { block_number: block_number, id: blockchain.id } result = JSON.parse(response.body) expect(response).to be_successful expect(Deposits::Coin.where(currency: currency).exists?).to be true end it 'doesn\'t update height of blockchain' do blockchain_height = blockchain.height expect(blockchain_height).not_to eq (block_number) api_post '/api/v2/admin/blockchains/process_block', token: token, params: { block_number: block_number, id: blockchain.id } result = JSON.parse(response.body) expect(response).to be_successful expect(result['height']).not_to eq block_number expect(result['height']).to eq blockchain_height end end context 'withdraw' do let!(:member_account) { member.get_account(:fake).tap { |ac| ac.update!(balance: 50, locked: 10) } } let!(:withdrawal) do Withdraw.create!(member: member, currency: currency, amount: 1, txid: "fake_hash", rid: 'fake_address', sum: 1, type: Withdraws::Coin, aasm_state: :confirming) end let!(:transaction) do Peatio::Transaction.new(hash: 'fake_hash', to_address: 'fake_address', amount: 1, block_number: block_number, currency_id: currency.id, txout: 10, status: 'pending') end let!(:succeed_transaction) do Peatio::Transaction.new(hash: 'fake_hash', to_address: 'fake_address', from_addresses: ['fake_address'], amount: 1, block_number: block_number, currency_id: currency.id, txout: 10, status: 'success') end let(:expected_block) { Peatio::Block.new(block_number, [transaction]) } before do service.adapter.stubs(:fetch_block!).returns(expected_block) service.adapter.stubs(:fetch_transaction).with(transaction).returns(succeed_transaction) end it 'detects successfuly in the block' do expect(Withdraws::Coin.find_by(currency: currency, txid: transaction.hash).succeed?).to be false api_post '/api/v2/admin/blockchains/process_block', token: token, params: { block_number: block_number, id: blockchain.id } result = JSON.parse(response.body) expect(response).to be_successful expect(Withdraws::Coin.find_by(currency: currency, txid: transaction.hash).succeed?).to be true end end end end end