Files
Dena/spec/services/blockchain_service_spec.rb
2026-08-13 19:50:53 +03:30

439 lines
17 KiB
Ruby

# encoding: UTF-8
# frozen_string_literal: true
class FakeBlockchain < Peatio::Blockchain::Abstract
def initialize
@features = {cash_addr_format: false, case_sensitive: true}
end
def configure(settings = {}); end
end
describe BlockchainService do
let!(:blockchain) { create(:blockchain, 'fake-testnet') }
let(:block_number) { 100 }
let(:fake_adapter) { FakeBlockchain.new }
let(:service) { BlockchainService.new(blockchain) }
let!(:fake_currency) { create(:currency, :fake) }
let!(:fake_currency1) { create(:currency, :fake, id: 'fake1') }
let!(:fake_currency2) { create(:currency, :fake, id: 'fake2') }
let!(:wallet) { create(:wallet, :fake_deposit) }
let!(:member) { create(:member) }
let(:transaction) { Peatio::Transaction.new(hash: 'fake_txid', to_address: 'fake_address', from_addresses: ['fake_address'], amount: 5, block_number: 3, currency_id: 'fake1', txout: 4, status: 'success') }
let(:expected_transactions) do
[
{ hash: 'fake_hash1', to_address: 'fake_address', amount: 1, block_number: 2, currency_id: 'fake1', txout: 1, from_addresses: ['fake_address'], status: 'success' },
{ hash: 'fake_hash2', to_address: 'fake_address1', amount: 2, block_number: 2, currency_id: 'fake1', txout: 2, from_addresses: ['fake_address'], status: 'success' },
{ hash: 'fake_hash3', to_address: 'fake_address2', amount: 3, block_number: 2, currency_id: 'fake2', txout: 3, from_addresses: ['fake_address'], status: 'success' }
].map { |t| Peatio::Transaction.new(t) }
end
let(:expected_block) { Peatio::Block.new(block_number, expected_transactions) }
before do
wallet.currencies << [fake_currency1, fake_currency2]
Peatio::Blockchain.registry.expects(:[])
.with(:fake)
.returns(fake_adapter.class)
.at_least_once
service.stubs(:latest_block_number).returns(4)
service.adapter.stubs(:latest_block_number).never
end
# Deposit context: (mock fetch_block)
# * Single deposit in block which should be saved.
# * Multiple deposits in single block (one saved one updated).
# * Multiple deposits for 2 currencies in single block.
# * Multiple deposits in single transaction (different txout).
describe 'Filter Deposits' do
context 'single fake deposit was created during block processing' do
before do
PaymentAddress.create!(member: member,
wallet: wallet,
address: 'fake_address')
service.adapter.stubs(:fetch_block!).returns(expected_block)
service.process_block(block_number)
end
subject { Deposits::Coin.where(currency: fake_currency1) }
it { expect(subject.exists?).to be true }
context 'creates deposit with correct attributes' do
before do
service.adapter.stubs(:fetch_block!).returns(Peatio::Block.new(block_number, [transaction]))
service.process_block(block_number)
end
it { expect(subject.where(txid: transaction.hash,
amount: transaction.amount,
address: transaction.to_address,
block_number: transaction.block_number,
txout: transaction.txout,
from_addresses: transaction.from_addresses).exists?).to be true }
end
context 'collect deposit after processing block' do
before do
service.stubs(:latest_block_number).returns(100)
service.adapter.stubs(:fetch_block!).returns(expected_block)
AMQP::Queue.expects(:enqueue).with(:events_processor, is_a(Hash))
end
it { service.process_block(block_number) }
end
context 'process data one more time' do
before do
service.adapter.stubs(:fetch_block!).returns(expected_block)
end
it { expect { service.process_block(block_number) }.not_to change { subject } }
end
end
context 'two fake deposits for one currency were created during block processing' do
before do
PaymentAddress.create!(member: member,
wallet: wallet,
address: 'fake_address')
PaymentAddress.create!(member: member,
wallet: wallet,
address: 'fake_address1')
service.adapter.stubs(:fetch_block!).returns(expected_block)
service.process_block(block_number)
end
subject { Deposits::Coin.where(currency: fake_currency1) }
it { expect(subject.count).to eq 2 }
context 'one deposit was updated' do
let!(:deposit) do
Deposit.create!(currency: fake_currency1,
member: member,
amount: 5,
address: 'fake_address',
txid: 'fake_txid',
block_number: 0,
txout: 4,
type: Deposits::Coin)
end
before do
service.adapter.stubs(:fetch_block!).returns(Peatio::Block.new(block_number, [transaction]))
service.process_block(block_number)
end
it { expect(Deposits::Coin.find_by(txid: transaction.hash).block_number).to eq(transaction.block_number) }
end
end
context 'two fake deposits for two currency were created during block processing' do
before do
PaymentAddress.create!(member: member,
wallet: wallet,
address: 'fake_address')
PaymentAddress.create!(member: member,
wallet: wallet,
address: 'fake_address2')
service.adapter.stubs(:fetch_block!).returns(expected_block)
service.process_block(block_number)
end
subject { Deposits::Coin.where(currency: [fake_currency1, fake_currency2]) }
it do
expect(subject.count).to eq 2
expect(Deposits::Coin.where(currency: fake_currency1).exists?).to be true
expect(Deposits::Coin.where(currency: fake_currency2).exists?).to be true
end
end
context 'skip deposit collection fee transaction' do
let!(:transaction) { create(:transaction, txid: 'fake_hash1') }
before do
PaymentAddress.create!(member: member,
wallet: wallet,
address: 'fake_address')
service.adapter.stubs(:fetch_block!).returns(expected_block)
service.process_block(block_number)
end
subject { Deposits::Coin.where(currency: [fake_currency1, fake_currency2]) }
it do
expect(subject.count).to eq 1
end
end
end
# Withdraw context: (mock fetch_block)
# * Single withdrawal.
# * Multiple withdrawals for single currency.
# * Multiple withdrawals for 2 currencies.
describe 'Filter Withdrawals' do
context 'single fake withdrawal was updated during block processing' do
let!(:fake_account) { member.get_account(:fake1).tap { |ac| ac.update!(balance: 50, locked: 5) } }
let!(:withdrawal) do
Withdraw.create!(member: member,
currency: fake_currency1,
amount: 1,
txid: 'fake_hash1',
rid: 'fake_address',
sum: 1,
type: Withdraws::Coin,
aasm_state: :confirming)
end
before do
service.adapter.stubs(:fetch_block!).returns(expected_block)
service.process_block(block_number)
end
it { expect(withdrawal.reload.block_number).to eq(expected_transactions.first.block_number) }
context 'single withdrawal was succeed during block processing' do
before do
service.stubs(:latest_block_number).returns(100)
service.adapter.stubs(:fetch_block!).returns(expected_block)
service.process_block(block_number)
end
it { expect(withdrawal.reload.succeed?).to be true }
end
end
end
context 'two fake withdrawals were updated during block processing' do
let!(:fake_account1) { member.get_account(:fake1).tap { |ac| ac.update!(balance: 50, locked: 10) } }
let!(:withdrawals) do
%w[fake_hash1 fake_hash2].each do |t|
Withdraw.create!(member: member,
currency: fake_currency1,
amount: 1,
txid: t,
rid: 'fake_address',
sum: 1,
type: Withdraws::Coin,
aasm_state: :confirming)
end
end
before do
service.adapter.stubs(:fetch_block!).returns(expected_block)
service.process_block(block_number)
end
subject { Withdraws::Coin.where(currency: fake_currency1) }
it do
expect(subject.find_by(txid: expected_transactions.first.hash).block_number).to eq(expected_transactions.first.block_number)
expect(subject.find_by(txid: expected_transactions.second.hash).block_number).to eq(expected_transactions.second.block_number)
end
end
context 'two fake withdrawals for two currency were updated during block processing' do
let!(:fake_account1) { member.get_account(:fake1).tap { |ac| ac.update!(balance: 50, locked: 10) } }
let!(:fake_account2) { member.get_account(:fake2).tap { |ac| ac.update!(balance: 50, locked: 10) } }
let!(:withdrawal1) do
Withdraw.create!(member: member,
currency: fake_currency1,
amount: 1,
txid: "fake_hash1",
rid: 'fake_address',
sum: 1,
type: Withdraws::Coin,
aasm_state: :confirming)
end
let!(:withdrawal2) do
Withdraw.create!(member: member,
currency: fake_currency2,
amount: 1,
txid: "fake_hash3",
rid: 'fake_address',
sum: 1,
type: Withdraws::Coin,
aasm_state: :confirming)
end
before do
service.adapter.stubs(:fetch_block!).returns(expected_block)
service.process_block(block_number)
end
subject { Withdraws::Coin.where(currency: [fake_currency1, fake_currency2]) }
it do
expect(subject.find_by(txid: expected_transactions.first.hash).block_number).to eq(expected_transactions.first.block_number)
expect(subject.find_by(txid: expected_transactions.third.hash).block_number).to eq(expected_transactions.third.block_number)
end
context 'fail withdrawal if transaction has status :fail' do
let!(:fake_account1) { member.get_account(:fake1).tap { |ac| ac.update!(balance: 50, locked: 10) } }
let!(:withdrawal) do
Withdraw.create!(member: member,
currency: fake_currency1,
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: 3, currency_id: fake_currency1.id, txout: 10, status: 'failed')
end
before do
service.adapter.stubs(:fetch_block!).returns(Peatio::Block.new(block_number, [transaction]))
service.process_block(block_number)
end
subject { Withdraws::Coin.find_by(currency: fake_currency1, txid: transaction.hash) }
it do
expect(subject.failed?).to be true
end
end
context 'fail withdrawal if transaction has status :fail' do
let!(:fake_account1) { member.get_account(:fake1).tap { |ac| ac.update!(balance: 50, locked: 10) } }
let!(:withdrawal) do
Withdraw.create!(member: member,
currency: fake_currency1,
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: 3, currency_id: fake_currency1.id, txout: 10, status: 'pending')
end
let!(:failed_transaction) do
Peatio::Transaction.new(hash: 'fake_hash', to_address: 'fake_address', amount: 1, block_number: 3, currency_id: fake_currency1.id, txout: 10, status: 'failed')
end
before do
service.adapter.stubs(:respond_to?).returns(true)
service.adapter.stubs(:fetch_block!).returns(Peatio::Block.new(block_number, [transaction]))
service.adapter.stubs(:fetch_transaction).with(transaction).returns(failed_transaction)
service.process_block(block_number)
end
subject { Withdraws::Coin.find_by(currency: fake_currency1, txid: transaction.hash) }
it do
expect(subject.failed?).to be true
end
end
context 'succeed withdrawal if transaction has status :success' do
let!(:fake_account1) { member.get_account(:fake1).tap { |ac| ac.update!(balance: 50, locked: 10) } }
let!(:withdrawal) do
Withdraw.create!(member: member,
currency: fake_currency1,
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: 3, currency_id: fake_currency1.id, txout: 10, status: 'pending')
end
let!(:succeed_transaction) do
Peatio::Transaction.new(hash: 'fake_hash', to_address: 'fake_address', amount: 1, block_number: 3, currency_id: fake_currency1.id, txout: 10, status: 'success')
end
before do
service.adapter.stubs(:respond_to?).returns(true)
service.adapter.stubs(:fetch_block!).returns(Peatio::Block.new(block_number, [transaction]))
service.stubs(:latest_block_number).returns(10)
service.adapter.stubs(:fetch_transaction).with(transaction).returns(succeed_transaction)
service.process_block(block_number)
end
subject { Withdraws::Coin.find_by(currency: fake_currency1, txid: transaction.hash) }
it do
expect(subject.succeed?).to be true
end
end
end
describe 'Several blocks' do
let(:expected_transactions1) do
[
{ hash: 'fake_hash4', to_address: 'fake_address4', amount: 1, block_number: 3, currency_id: 'fake1', txout: 1, status: 'success' },
{ hash: 'fake_hash5', to_address: 'fake_address4', amount: 2, block_number: 3, currency_id: 'fake1', txout: 2, status: 'success' },
{ hash: 'fake_hash6', to_address: 'fake_address4', amount: 3, block_number: 3, currency_id: 'fake2', txout: 1, status: 'success' }
].map { |t| Peatio::Transaction.new(t) }
end
let(:expected_block1) { Peatio::Block.new(block_number, expected_transactions1) }
let!(:fake_account1) { member.get_account(:fake1) }
let!(:fake_account2) { member.get_account(:fake2) }
before do
service.stubs(:latest_block_number).returns(100)
PaymentAddress.create!(member: member,
wallet: wallet,
address: 'fake_address')
PaymentAddress.create!(member: member,
wallet: wallet,
address: 'fake_address2')
service.adapter.stubs(:fetch_block!).returns(expected_block, expected_block1)
end
it 'creates deposits and updates withdrawals' do
service.process_block(block_number)
expect(Deposits::Coin.where(currency: fake_currency1).exists?).to be true
expect(Deposits::Coin.where(currency: fake_currency2).exists?).to be true
Deposit.find_each { |d| d.dispatch! }
[fake_account1, fake_account2].map { |a| a.reload }
withdraw1 = Withdraw.create!(member: member, currency: fake_currency1, amount: 1, txid: "fake_hash5",
rid: 'fake_address4', sum: 1, type: Withdraws::Coin)
withdraw1.accept!
withdraw1.process!
withdraw1.dispatch!
withdraw2 = Withdraw.create!(member: member, currency: fake_currency2, amount: 3, txid: "fake_hash6",
rid: 'fake_address4', sum: 3, type: Withdraws::Coin)
withdraw2.accept!
withdraw2.process!
withdraw2.dispatch!
service.process_block(block_number)
expect(withdraw1.reload.succeed?).to be true
expect(withdraw2.reload.succeed?).to be true
end
end
end