Skip to content

Commit

Permalink
Merge pull request #368 from shaojunda/shaojunda-release-dao-withdraw…
Browse files Browse the repository at this point in the history
…-fee

[ᚬmaster] release dao withdraw fee
  • Loading branch information
shaojunda authored Aug 21, 2019
2 parents 1cb2721 + c8a399c commit 08a97b3
Show file tree
Hide file tree
Showing 16 changed files with 540 additions and 9 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ BLOCK_SAFETY_INTERVAL="10"
AUTHENTICSYNC_LOOP_INTERVAL="10"
INAUTHENTICSYNC_LOOP_INTERVAL="1"
CODE_HASH="0x54811ce986d5c3e57eaafab22cdd080e32209e39590e204a99b32935f835a13c"
DAO_CODE_HASH="0x0450e0eb0ad787135c1243e5730f72fa3bc8837e232e928c7f4b1efdd6692582"
INITIAL_BLOCK_REWARD="5_000_000_000_000"
HASH_RATE_STATISTICAL_INTERVAL="500"
PROPOSAL_WINDOW = "10"
Expand Down
375 changes: 375 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion app/models/address.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ class Address < ApplicationRecord
has_many :ckb_transactions, through: :account_books
validates :balance, :cell_consumed, :ckb_transactions_count, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true

attribute :lock_hash, :ckb_hash
after_commit :flush_cache

def lock_script
Expand Down
2 changes: 2 additions & 0 deletions app/models/cell_output.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
class CellOutput < ApplicationRecord
enum status: { live: 0, dead: 1 }
enum cell_type: { normal: 0, dao: 1 }

belongs_to :ckb_transaction
belongs_to :generated_by, class_name: "CkbTransaction"
Expand Down Expand Up @@ -41,6 +42,7 @@ def node_output
# cell_index :integer
# generated_by_id :decimal(30, )
# consumed_by_id :decimal(30, )
# cell_type :integer default("normal")
#
# Indexes
#
Expand Down
2 changes: 1 addition & 1 deletion app/models/ckb_sync/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module CkbSync
class Api
include Singleton

METHOD_NAMES = %w(system_script_out_point dry_run_transaction set_system_script_cell system_script_cell system_script_cell_hash genesis_block get_block_by_number genesis_block_hash get_block_hash get_block get_tip_header get_tip_block_number get_cells_by_lock_hash get_transaction get_live_cell local_node_info get_current_epoch get_epoch_by_number get_peers tx_pool_info get_blockchain_info get_peers_state compute_transaction_hash get_cellbase_output_capacity_details).freeze
METHOD_NAMES = %w(system_script_out_point dry_run_transaction set_system_script_cell system_script_cell system_script_cell_hash genesis_block get_block_by_number genesis_block_hash get_block_hash get_block get_tip_header get_tip_block_number get_cells_by_lock_hash get_transaction get_live_cell local_node_info get_current_epoch get_epoch_by_number get_peers tx_pool_info get_blockchain_info get_peers_state compute_transaction_hash get_cellbase_output_capacity_details calculate_dao_maximum_withdraw).freeze

def initialize
@api = CKB::API.new(host: ENV["CKB_NODE_URL"])
Expand Down
9 changes: 8 additions & 1 deletion app/models/ckb_sync/node_data_processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,12 @@ def build_cell_outputs(node_outputs, ckb_transaction, addresses)
end
end

def cell_type(type_script)
return "normal" if type_script.blank?

type_script.code_hash == ENV["DAO_CODE_HASH"] ? "dao" : "normal"
end

def build_cell_output(ckb_transaction, output, address, cell_index)
ckb_transaction.cell_outputs.build(
capacity: output.capacity,
Expand All @@ -242,7 +248,8 @@ def build_cell_output(ckb_transaction, output, address, cell_index)
block: ckb_transaction.block,
tx_hash: ckb_transaction.tx_hash,
cell_index: cell_index,
generated_by: ckb_transaction
generated_by: ckb_transaction,
cell_type: cell_type(output.type)
)
end

Expand Down
2 changes: 1 addition & 1 deletion app/models/ckb_transaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ def display_outputs
# block_timestamp :decimal(30, )
# transaction_fee :decimal(30, )
# version :integer
# witnesses :string is an Array
# created_at :datetime not null
# updated_at :datetime not null
# is_cellbase :boolean default(FALSE)
# witnesses :jsonb
#
# Indexes
#
Expand Down
27 changes: 26 additions & 1 deletion app/utils/ckb_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ def self.get_epoch_info(epoch)
end

def self.ckb_transaction_fee(ckb_transaction)
ckb_transaction.inputs.sum(:capacity) - ckb_transaction.outputs.sum(:capacity)
if ckb_transaction.inputs.dao.present?
dao_withdraw_tx_fee(ckb_transaction)
else
normal_tx_fee(ckb_transaction)
end
end

def self.get_unspent_cells(address_hash)
Expand Down Expand Up @@ -143,4 +147,25 @@ def self.update_target_block_miner_address_pending_rewards(current_block)
miner_address = target_block.miner_address
Address.decrement_counter(:pending_reward_blocks_count, miner_address.id, touch: true) if miner_address.present?
end

def self.normal_tx_fee(ckb_transaction)
ckb_transaction.inputs.sum(:capacity) - ckb_transaction.outputs.sum(:capacity)
end

def self.dao_withdraw_tx_fee(ckb_transaction)
dao_cells = ckb_transaction.inputs.dao
witnesses = ckb_transaction.witnesses
deps = ckb_transaction.deps
interests =
dao_cells.reduce(0) do |memo, dao_cell|
witness = witnesses[dao_cell.cell_index]
dep = deps[witness["data"].last.hex]
out_point = CKB::Types::OutPoint.new(cell: CKB::Types::CellOutPoint.new(tx_hash: dao_cell.tx_hash, index: dao_cell.cell_index))
memo + CkbSync::Api.instance.calculate_dao_maximum_withdraw(out_point, dep["block_hash"]).to_i - dao_cell.capacity.to_i
end

ckb_transaction.inputs.sum(:capacity) + interests - ckb_transaction.outputs.sum(:capacity)
rescue CKB::RPCError
0
end
end
5 changes: 5 additions & 0 deletions db/migrate/20190809023059_add_cell_type_to_cell_outputs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddCellTypeToCellOutputs < ActiveRecord::Migration[5.2]
def change
add_column :cell_outputs, :cell_type, :integer, default: 0
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class ChangeWitnessesToJsonbInCkbTransactions < ActiveRecord::Migration[5.2]
def change
remove_column :ckb_transactions, :witnesses, :jsonb
add_column :ckb_transactions, :witnesses, :jsonb
end
end
5 changes: 3 additions & 2 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2019_08_10_071407) do
ActiveRecord::Schema.define(version: 2019_08_19_090938) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -104,6 +104,7 @@
t.integer "cell_index"
t.decimal "generated_by_id", precision: 30
t.decimal "consumed_by_id", precision: 30
t.integer "cell_type", default: 0
t.index ["address_id", "status"], name: "index_cell_outputs_on_address_id_and_status"
t.index ["block_id"], name: "index_cell_outputs_on_block_id"
t.index ["ckb_transaction_id"], name: "index_cell_outputs_on_ckb_transaction_id"
Expand All @@ -120,10 +121,10 @@
t.decimal "block_timestamp", precision: 30
t.decimal "transaction_fee", precision: 30
t.integer "version"
t.string "witnesses", array: true
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "is_cellbase", default: false
t.jsonb "witnesses"
t.index ["block_id", "block_timestamp"], name: "index_ckb_transactions_on_block_id_and_block_timestamp"
t.index ["is_cellbase"], name: "index_ckb_transactions_on_is_cellbase"
t.index ["tx_hash", "block_id"], name: "index_ckb_transactions_on_tx_hash_and_block_id", unique: true
Expand Down
1 change: 1 addition & 0 deletions test/factories/cell_output.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

capacity { 10**8 * 8 }
data {}
cell_type { "normal" }

trait :with_full_transaction do
before(:create) do |cell_output, _evaluator|
Expand Down
7 changes: 7 additions & 0 deletions test/models/cell_output_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,11 @@ class CellOutputTest < ActiveSupport::TestCase
should validate_numericality_of(:capacity).
is_greater_than_or_equal_to(0)
end

test "should have cell_type column" do
block = create(:block)
cell_output = create(:cell_output, :with_full_transaction, block: block)

assert_equal "normal", cell_output.cell_type
end
end
39 changes: 39 additions & 0 deletions test/models/ckb_sync/node_data_processor_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,26 @@ class NodeDataProcessorTest < ActiveSupport::TestCase
end
end

test "#process_block created cell_outputs's cell_type should be equal to normal when cell is not dao cell" do
VCR.use_cassette("blocks/#{DEFAULT_NODE_BLOCK_NUMBER}") do
node_block = CkbSync::Api.instance.get_block_by_number(DEFAULT_NODE_BLOCK_NUMBER)
local_block = node_data_processor.process_block(node_block)

assert_equal ["normal"], local_block.cell_outputs.pluck(:cell_type).uniq
end
end

test "#process_block created cell_outputs's cell_type should be equal to dao when cell is dao cell" do
VCR.use_cassette("blocks/#{DEFAULT_NODE_BLOCK_NUMBER}") do
node_block = CkbSync::Api.instance.get_block_by_number(DEFAULT_NODE_BLOCK_NUMBER)
node_output = node_block.transactions.first.outputs.first
node_output.type = CKB::Types::Script.new(code_hash: ENV["DAO_CODE_HASH"], args: [])
local_block = node_data_processor.process_block(node_block)

assert_equal ["dao"], local_block.cell_outputs.pluck(:cell_type).uniq
end
end

test "#process_block should create addresses for cell_output" do
VCR.use_cassette("blocks/#{DEFAULT_NODE_BLOCK_NUMBER}") do
node_block = CkbSync::Api.instance.get_block_by_number(DEFAULT_NODE_BLOCK_NUMBER)
Expand Down Expand Up @@ -386,6 +406,25 @@ class NodeDataProcessorTest < ActiveSupport::TestCase
end
end

test "#process_block should update block's contained addresses's info even if raise RPCError " do
CkbSync::Api.any_instance.stubs(:calculate_dao_maximum_withdraw).raises(CKB::RPCError)
node_block = fake_node_block("0x3307186493c5da8b91917924253a5ffd35231151649d0c7e2941aa8801815063")
VCR.use_cassette("blocks/#{DEFAULT_NODE_BLOCK_NUMBER}") do
block = create(:block, :with_block_hash)
ckb_transaction1 = create(:ckb_transaction, tx_hash: "0x498315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", block: block)
ckb_transaction2 = create(:ckb_transaction, tx_hash: "0x598315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", block: block)
create(:cell_output, ckb_transaction: ckb_transaction1, cell_index: 1, tx_hash: "0x498315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", generated_by: ckb_transaction2, block: block, cell_type: "dao")
create(:cell_output, ckb_transaction: ckb_transaction2, cell_index: 0, tx_hash: "0x598315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", generated_by: ckb_transaction1, block: block)
tx = node_block.transactions.last
tx.deps = [CKB::Types::OutPoint.new(cell: nil, block_hash: "0x0b3e980e4e5e59b7d478287e21cd89ffdc3ff5916ee26cf2aa87910c6a504d61")]
tx.witnesses = [CKB::Types::Witness.new(data: %w(0x8ae8061ec879d66c0f3996ab60d7c2a21094b8739817beddaea1e28d3620a70a21497a692581ca352631a67f3f6659a7c47d9a0c6c2def79d3e39440918a66fef00 0x0000000000000000)), CKB::Types::Witness.new(data: %w(0x8ae8061ec879d66c0f3996ab60d7c2a21094b8739817beddaea1e28d360a70a21497a692581ca352631a67f3f6659a7c47d9a0c6c2def79d3e39440918a66fef00 0x0000000000000000))]

local_block = node_data_processor.process_block(node_block)

assert_equal 4, local_block.contained_addresses.map(&:ckb_transactions).flatten.count
end
end

test "#process_block should update cell status" do
VCR.use_cassette("blocks/#{DEFAULT_NODE_BLOCK_NUMBER}") do
node_block = fake_node_block("0x3307186493c5da8b91917924253a5ffd35231151649d0c7e2941aa8801815063")
Expand Down
2 changes: 1 addition & 1 deletion test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def format_node_block(node_block)

def format_node_block_commit_transaction(commit_transaction)
tx = commit_transaction.instance_values.reject { |key, _value| key.in?(%w(inputs outputs)) }
tx["witnesses"] = tx["witnesses"].map(&:to_h).map(&:to_s)
tx["witnesses"] = JSON.parse(tx["witnesses"].map(&:to_h).to_json)

tx
end
Expand Down
65 changes: 64 additions & 1 deletion test/utils/ckb_utils_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ class CkbUtilsTest < ActiveSupport::TestCase
end
end


test "#block_cell_consumed generated block's cell_consumed should equal to the sum of transactions output occupied capacity" do
VCR.use_cassette("blocks/#{DEFAULT_NODE_BLOCK_NUMBER}") do
node_block = CkbSync::Api.instance.get_block_by_number(DEFAULT_NODE_BLOCK_NUMBER)
Expand All @@ -69,6 +68,70 @@ class CkbUtilsTest < ActiveSupport::TestCase
end
end

test ".ckb_transaction_fee should return right tx_fee when tx is not dao withdraw tx" do
node_block = fake_node_block("0x3307186493c5da8b91917924253a5ffd35231151649d0c7e2941aa8801815063")
VCR.use_cassette("blocks/#{DEFAULT_NODE_BLOCK_NUMBER}") do
block = create(:block, :with_block_hash)
ckb_transaction1 = create(:ckb_transaction, tx_hash: "0x498315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", block: block)
ckb_transaction2 = create(:ckb_transaction, tx_hash: "0x598315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", block: block)
create(:cell_output, ckb_transaction: ckb_transaction1, cell_index: 1, tx_hash: "0x498315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", generated_by: ckb_transaction2, block: block)
create(:cell_output, ckb_transaction: ckb_transaction2, cell_index: 0, tx_hash: "0x598315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", generated_by: ckb_transaction1, block: block)
node_data_processor.process_block(node_block)
node_tx = node_block.transactions.last
ckb_transaction = CkbTransaction.find_by(tx_hash: node_tx.hash)

assert_equal 10**8 * 3, CkbUtils.ckb_transaction_fee(ckb_transaction)
end
end

test ".ckb_transaction_fee should return right tx_fee when tx is dao withdraw tx" do
CkbSync::Api.any_instance.stubs(:calculate_dao_maximum_withdraw).returns("800001000")
node_block = fake_node_block("0x3307186493c5da8b91917924253a5ffd35231151649d0c7e2941aa8801815063")
VCR.use_cassette("blocks/#{DEFAULT_NODE_BLOCK_NUMBER}") do
block = create(:block, :with_block_hash)
ckb_transaction1 = create(:ckb_transaction, tx_hash: "0x498315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", block: block)
ckb_transaction2 = create(:ckb_transaction, tx_hash: "0x598315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", block: block)
create(:cell_output, ckb_transaction: ckb_transaction1, cell_index: 1, tx_hash: "0x498315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", generated_by: ckb_transaction2, block: block, cell_type: "dao")
create(:cell_output, ckb_transaction: ckb_transaction2, cell_index: 0, tx_hash: "0x598315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", generated_by: ckb_transaction1, block: block)
tx = node_block.transactions.last
tx.deps = [CKB::Types::OutPoint.new(cell: nil, block_hash: "0x0b3e980e4e5e59b7d478287e21cd89ffdc3ff5916ee26cf2aa87910c6a504d61")]
tx.witnesses = [CKB::Types::Witness.new(data: %w(0x8ae8061ec879d66c0f3996ab60d7c2a21094b8739817beddaea1e28d3620a70a21497a692581ca352631a67f3f6659a7c47d9a0c6c2def79d3e39440918a66fef00 0x0000000000000000)), CKB::Types::Witness.new(data: %w(0x8ae8061ec879d66c0f3996ab60d7c2a21094b8739817beddaea1e28d360a70a21497a692581ca352631a67f3f6659a7c47d9a0c6c2def79d3e39440918a66fef00 0x0000000000000000))]

node_data_processor.process_block(node_block)

node_tx = node_block.transactions.last
ckb_transaction = CkbTransaction.find_by(tx_hash: node_tx.hash)

assert_equal 10**8 * 3 + 1000, CkbUtils.ckb_transaction_fee(ckb_transaction)
end
end

test ".ckb_transaction_fee should return right tx_fee when tx is dao withdraw tx and have multiple dao cell" do
CkbSync::Api.any_instance.stubs(:calculate_dao_maximum_withdraw).returns("800001000")
node_block = fake_node_block("0x3307186493c5da8b91917924253a5ffd35231151649d0c7e2941aa8801815063")
VCR.use_cassette("blocks/#{DEFAULT_NODE_BLOCK_NUMBER}") do
block = create(:block, :with_block_hash)
ckb_transaction1 = create(:ckb_transaction, tx_hash: "0x498315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", block: block)
ckb_transaction2 = create(:ckb_transaction, tx_hash: "0x598315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", block: block)
create(:cell_output, ckb_transaction: ckb_transaction1, cell_index: 0, tx_hash: "0x498315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", generated_by: ckb_transaction1, block: block, cell_type: "dao")
create(:cell_output, ckb_transaction: ckb_transaction1, cell_index: 1, tx_hash: "0x498315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", generated_by: ckb_transaction2, block: block, cell_type: "dao")
create(:cell_output, ckb_transaction: ckb_transaction2, cell_index: 0, tx_hash: "0x598315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", generated_by: ckb_transaction1, block: block)
tx = node_block.transactions.last
input = CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(cell: CKB::Types::CellOutPoint.new(tx_hash: "0x498315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", index: 0)))
tx.inputs.unshift(input)
tx.deps = [CKB::Types::OutPoint.new(cell: nil, block_hash: "0x0b3e980e4e5e59b7d478287e21cd89ffdc3ff5916ee26cf2aa87910c6a504d61")]
tx.witnesses = [CKB::Types::Witness.new(data: %w(0x8ae8061ec879d66c0f3996ab60d7c2a21094b8739817beddaea1e28d3620a70a21497a692581ca352631a67f3f6659a7c47d9a0c6c2def79d3e39440918a66fef00 0x0000000000000000)), CKB::Types::Witness.new(data: %w(0x8ae8061ec879d66c0f3996ab60d7c2a21094b8739817beddaea1e28d360a70a21497a692581ca352631a67f3f6659a7c47d9a0c6c2def79d3e39440918a66fef00 0x0000000000000000))]

node_data_processor.process_block(node_block)

node_tx = node_block.transactions.last
ckb_transaction = CkbTransaction.find_by(tx_hash: node_tx.hash)
expected_tx_fee = 10**8 * 16 + 1000 * 2 - 10**8 * 5

assert_equal expected_tx_fee, CkbUtils.ckb_transaction_fee(ckb_transaction)
end
end

private

def node_data_processor
Expand Down

0 comments on commit 08a97b3

Please sign in to comment.