From 1d89ca5b0292971af7bc2e80ce9f6fc7e5349b60 Mon Sep 17 00:00:00 2001 From: NanZhang Date: Fri, 15 Sep 2023 14:00:51 +0800 Subject: [PATCH 1/9] feat: scripts general_info returns type_id (#1451) --- app/controllers/api/v2/scripts_controller.rb | 9 ++++++++- config/settings.mainnet.yml | 3 +++ config/settings.testnet.yml | 3 +++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/v2/scripts_controller.rb b/app/controllers/api/v2/scripts_controller.rb index a04d0724f..0378d3872 100644 --- a/app/controllers/api/v2/scripts_controller.rb +++ b/app/controllers/api/v2/scripts_controller.rb @@ -48,8 +48,15 @@ def get_script_content deployed_cells = @contract&.deployed_cell_outputs&.live transactions = @contract&.cell_dependencies + if deployed_cells.present? + deployed_type_script = deployed_cells[0].type_script + if deployed_type_script.code_hash == Settings.type_id_code_hash + type_id = deployed_type_script.script_hash + end + end + { - id: @script.id, + id: type_id, code_hash: @script.code_hash, hash_type: @script.hash_type, script_type: @script.class.to_s, diff --git a/config/settings.mainnet.yml b/config/settings.mainnet.yml index 30ecb2e97..31f771387 100644 --- a/config/settings.mainnet.yml +++ b/config/settings.mainnet.yml @@ -58,6 +58,9 @@ code_hash: "0x973bdb373cbb1d752b4ac006e2bb5bdcb63431ed2b6e394b22721c8906a2ad72" secp_cell_type_hash: "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8" secp_multisig_cell_code_hash: "0xc1fb0ae6915d3d4eded3498aedf5faddd8c5f6bd8921e0f8bfabd5ebcbf259bc" +# type id code hash +type_id_code_hash: "0x00000000000000000000000000000000000000000000000000545950455f4944" + homepage_transactions_records_count: 15 homepage_block_records_count: 15 proposal_window: 10 diff --git a/config/settings.testnet.yml b/config/settings.testnet.yml index e1113c30e..0ed0f21f1 100644 --- a/config/settings.testnet.yml +++ b/config/settings.testnet.yml @@ -61,6 +61,9 @@ secp_multisig_cell_code_hash: "0xc1fb0ae6915d3d4eded3498aedf5faddd8c5f6bd8921e0f hash_rate_statistical_interval: 900 average_block_time_interval: 100 +# type id code hash +type_id_code_hash: "0x00000000000000000000000000000000000000000000000000545950455f4944" + homepage_transactions_records_count: 15 homepage_block_records_count: 15 proposal_window: 10 From c308decce2c626d8555f496e19fc6e79f564bd70 Mon Sep 17 00:00:00 2001 From: NanZhang Date: Fri, 15 Sep 2023 14:25:06 +0800 Subject: [PATCH 2/9] chore: update build.yml (#1453) --- .github/workflows/build.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index de621a673..9fac8e31a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,21 +22,21 @@ jobs: image-name: '${{ steps.docker_build.outputs.imageFullName }}' image-tag: '${{ steps.docker_build.outputs.tags }}' steps: - - name: Free Disk Space (Ubuntu) - uses: jlumbroso/free-disk-space@main - with: - # this might remove tools that are actually needed, - # if set to "true" but frees about 6 GB - tool-cache: false - - # all of these default to true, but feel free to set to - # "false" if necessary for your workflow - android: true - dotnet: true - haskell: true - large-packages: true - docker-images: true - swap-storage: true +# - name: Free Disk Space (Ubuntu) +# uses: jlumbroso/free-disk-space@main +# with: +# # this might remove tools that are actually needed, +# # if set to "true" but frees about 6 GB +# tool-cache: false +# +# # all of these default to true, but feel free to set to +# # "false" if necessary for your workflow +# android: true +# dotnet: true +# haskell: true +# large-packages: true +# docker-images: true +# swap-storage: true - uses: actions/checkout@v3 with: fetch-depth: 0 From fa5ef5af9adee96109c0ff81f49d4f5cb24df98f Mon Sep 17 00:00:00 2001 From: Miles Zhang Date: Mon, 18 Sep 2023 09:14:45 +0800 Subject: [PATCH 3/9] Issue 374 2 (#1450) * feat: generate udt verification model to save token Signed-off-by: Miles Zhang * feat: set mailer for udt verify Signed-off-by: Miles Zhang * feat: rename udt's contact_info to email Signed-off-by: Miles Zhang * feat: update udt info and send token business logic Signed-off-by: Miles Zhang --------- Signed-off-by: Miles Zhang --- .../api/v1/udt_verifications_controller.rb | 31 +++ app/controllers/api/v1/udts_controller.rb | 17 +- app/lib/api/v1/exceptions.rb | 37 +++ app/mailers/udt_verification_mailer.rb | 12 + app/models/udt.rb | 8 +- app/models/udt_verification.rb | 43 ++++ .../send_token.en.text.erb | 12 + .../send_token.zh_CN.text.erb | 10 + config/environments/production.rb | 14 +- config/initializers/locales.rb | 2 + config/routes.rb | 3 +- ...20230913091025_create_udt_verifications.rb | 14 ++ ...28_change_contract_info_to_email_in_udt.rb | 5 + db/structure.sql | 70 +++++- .../v1/udt_verifications_controller_test.rb | 47 ++++ .../api/v1/udts_controller_test.rb | 214 ++++++++++++------ test/factories/udt_verifications.rb | 9 + .../send_token_email.en.text.erb | 12 + .../send_token_email.zh_CN.text.erb | 10 + .../udt_verification_mailer_preview.rb | 5 + test/mailers/udt_verification_mailer_test.rb | 27 +++ test/models/udt_verification_test.rb | 7 + 22 files changed, 534 insertions(+), 75 deletions(-) create mode 100644 app/controllers/api/v1/udt_verifications_controller.rb create mode 100644 app/mailers/udt_verification_mailer.rb create mode 100644 app/models/udt_verification.rb create mode 100644 app/views/udt_verification_mailer/send_token.en.text.erb create mode 100644 app/views/udt_verification_mailer/send_token.zh_CN.text.erb create mode 100644 config/initializers/locales.rb create mode 100644 db/migrate/20230913091025_create_udt_verifications.rb create mode 100644 db/migrate/20230914120928_change_contract_info_to_email_in_udt.rb create mode 100644 test/controllers/api/v1/udt_verifications_controller_test.rb create mode 100644 test/factories/udt_verifications.rb create mode 100644 test/fixtures/udt_verification_mailer/send_token_email.en.text.erb create mode 100644 test/fixtures/udt_verification_mailer/send_token_email.zh_CN.text.erb create mode 100644 test/mailers/previews/udt_verification_mailer_preview.rb create mode 100644 test/mailers/udt_verification_mailer_test.rb create mode 100644 test/models/udt_verification_test.rb diff --git a/app/controllers/api/v1/udt_verifications_controller.rb b/app/controllers/api/v1/udt_verifications_controller.rb new file mode 100644 index 000000000..2ee731e8e --- /dev/null +++ b/app/controllers/api/v1/udt_verifications_controller.rb @@ -0,0 +1,31 @@ +module Api + module V1 + class UdtVerificationsController < ApplicationController + before_action :check_udt_info, only: :update + before_action :set_locale, only: :update + + def update + udt_verification = UdtVerification.find_or_create_by(udt_id: @udt.id) + + udt_verification.refresh_token!(request.remote_ip) + UdtVerificationMailer.with(email: @udt.email, token: udt_verification.token, + locale: @locale).send_token.deliver_later + render json: :ok + rescue UdtVerification::TokenSentTooFrequentlyError + raise Api::V1::Exceptions::TokenSentTooFrequentlyError + end + + private + + def check_udt_info + @udt = Udt.find_by(type_hash: params[:id]) + raise Api::V1::Exceptions::UdtNotFoundError if @udt.nil? + raise Api::V1::Exceptions::UdtNoContactEmailError if @udt.email.blank? + end + + def set_locale + @locale = params[:locale] == "zh_CN" ? "zh_CN" : "en" + end + end + end +end diff --git a/app/controllers/api/v1/udts_controller.rb b/app/controllers/api/v1/udts_controller.rb index e9f598c90..3ad82eae0 100644 --- a/app/controllers/api/v1/udts_controller.rb +++ b/app/controllers/api/v1/udts_controller.rb @@ -34,14 +34,27 @@ def update icon_file: params[:icon_file], uan: params[:uan], display_name: params[:display_name], - contact_info: params[:contact_info] + email: params[:email] } - udt.update!(attrs) + if udt.email.blank? + raise Api::V1::Exceptions::UdtInfoInvalidError.new("Email can't be blank") if params[:email].blank? + + udt.update!(attrs) + else + raise Api::V1::Exceptions::UdtVerificationNotFoundError if udt.udt_verification.nil? + + udt.udt_verification.validate_token!(params[:token]) + udt.update!(attrs) + end render json: :ok rescue ActiveRecord::RecordNotFound raise Api::V1::Exceptions::UdtNotFoundError rescue ActiveRecord::RecordInvalid => e raise Api::V1::Exceptions::UdtInfoInvalidError.new(e) + rescue UdtVerification::TokenExpiredError + raise Api::V1::Exceptions::TokenExpiredError + rescue UdtVerification::TokenNotMatchError + raise Api::V1::Exceptions::TokenNotMatchError end def show diff --git a/app/lib/api/v1/exceptions.rb b/app/lib/api/v1/exceptions.rb index 1b9dce5f8..c011015fb 100644 --- a/app/lib/api/v1/exceptions.rb +++ b/app/lib/api/v1/exceptions.rb @@ -174,11 +174,13 @@ def initialize super code: 1027, status: 404, title: "URI parameters invalid", detail: "code hash should be start with 0x", href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" end end + class ScriptHashTypeParamsInvalidError < Error def initialize super code: 1028, status: 404, title: "URI parameters invalid", detail: "hash type should be 'type'", href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" end end + class ScriptNotFoundError < Error def initialize super code: 1029, status: 404, title: "Script not found", detail: "Script not found", href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" @@ -191,6 +193,41 @@ def initialize(detail) end end + class UdtVerificationInvalidError < Error + def initialize(detail) + super code: 1031, status: 400, title: "UDT verification invalid", detail: detail, href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" + end + end + + class UdtVerificationNotFoundError < Error + def initialize + super code: 1032, status: 404, title: "UDT Verification Not Found", detail: "No UDT verification records found by given type hash", href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" + end + end + + class UdtNoContactEmailError < Error + def initialize + super code: 1033, status: 400, title: "UDT has no contact email", detail: "", href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" + end + end + + class TokenExpiredError < Error + def initialize + super code: 1034, status: 400, title: "Token has expired", detail: "", href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" + end + end + + class TokenNotMatchError < Error + def initialize + super code: 1035, status: 400, title: "Token is not matched", detail: "", href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" + end + end + + class TokenSentTooFrequentlyError < Error + def initialize + super code: 1036, status: 400, title: "Token sent too frequently", detail: "", href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" + end + end end end end diff --git a/app/mailers/udt_verification_mailer.rb b/app/mailers/udt_verification_mailer.rb new file mode 100644 index 000000000..886dc9419 --- /dev/null +++ b/app/mailers/udt_verification_mailer.rb @@ -0,0 +1,12 @@ +class UdtVerificationMailer < ApplicationMailer + default from: "noreply@magickbase.com" + + def send_token + email = params[:email] + @token = params[:token] + locale = params[:locale] || "en" + I18n.with_locale(locale) do + mail(to: email, subject: "Token Info Verification") + end + end +end diff --git a/app/models/udt.rb b/app/models/udt.rb index 78aea622d..0b73b73b5 100644 --- a/app/models/udt.rb +++ b/app/models/udt.rb @@ -1,7 +1,10 @@ class Udt < ApplicationRecord + MAX_PAGINATES_PER = 100 + belongs_to :nrc_factory_cell, optional: true - MAX_PAGINATES_PER = 100 + has_one :udt_verification + enum udt_type: { sudt: 0, m_nft_token: 1, nrc_721_token: 2, spore_cell: 3 } validates_presence_of :total_amount @@ -9,6 +12,7 @@ class Udt < ApplicationRecord validates_length_of :full_name, minimum: 1, maximum: 100, allow_nil: true validates :decimal, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 39 }, allow_nil: true validates :total_amount, numericality: { greater_than_or_equal_to: 0 } + validates :email, format: { with: /\A(.+)@(.+)\z/, message: "Not a valid email" }, allow_nil: true attribute :code_hash, :ckb_hash @@ -60,7 +64,7 @@ def type_script # display_name :string # uan :string # h24_ckb_transactions_count :bigint default(0) -# contact_info :string +# email :string # # Indexes # diff --git a/app/models/udt_verification.rb b/app/models/udt_verification.rb new file mode 100644 index 000000000..f92c4276b --- /dev/null +++ b/app/models/udt_verification.rb @@ -0,0 +1,43 @@ +class UdtVerification < ApplicationRecord + SENT_FREQUENCY_MINUTES = 1 + KEEP_ALIVE_MINUTES = 10 + + class TokenExpiredError < StandardError; end + class TokenNotMatchError < StandardError; end + class TokenSentTooFrequentlyError < StandardError; end + + belongs_to :udt + + def refresh_token!(ip) + raise TokenSentTooFrequentlyError if sent_at.present? && self.sent_at + SENT_FREQUENCY_MINUTES.minutes > Time.now + + self.token = rand(999999).to_s.rjust(6, "0") + self.sent_at = Time.now + self.last_ip = ip + self.save! + end + + def validate_token!(token_params) + raise TokenExpiredError if self.sent_at + KEEP_ALIVE_MINUTES.minutes < Time.now + raise TokenNotMatchError if token != token_params.to_i + end +end + +# == Schema Information +# +# Table name: udt_verifications +# +# id :bigint not null, primary key +# token :integer +# sent_at :datetime +# last_ip :inet +# udt_id :bigint +# udt_type_hash :integer +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_udt_verifications_on_udt_id (udt_id) +# index_udt_verifications_on_udt_type_hash (udt_type_hash) UNIQUE +# diff --git a/app/views/udt_verification_mailer/send_token.en.text.erb b/app/views/udt_verification_mailer/send_token.en.text.erb new file mode 100644 index 000000000..f167d24fb --- /dev/null +++ b/app/views/udt_verification_mailer/send_token.en.text.erb @@ -0,0 +1,12 @@ +Dear Token Info Author, + +To ensure the effectiveness of your Token Info modification operation, we have generated a verification code for you to complete the Token Info update. + +Your verification code is: <%= @token %> + +Please note that this verification code is only valid for the next 10 minutes. Please use it to complete your Token Info modification within this time frame and refrain from sharing your code with others. If you have not initiated any action or if this operation is not associated with you, please disregard this email. + +If you have any questions or require assistance, please feel free to contact our customer support team.Thank you for your trust and support! + +Best regards, +The MagicKBase Team diff --git a/app/views/udt_verification_mailer/send_token.zh_CN.text.erb b/app/views/udt_verification_mailer/send_token.zh_CN.text.erb new file mode 100644 index 000000000..c307071f8 --- /dev/null +++ b/app/views/udt_verification_mailer/send_token.zh_CN.text.erb @@ -0,0 +1,10 @@ +尊敬的 Token Info 作者, + +为了确保本次 Token Info 修改操作的有效性,我们为您生成了一个验证码,用于完成 Token Info 的修改操作。 + +您的验证码是:<%= @token %> + +请注意,该验证码仅在接下来的10分钟内有效。请在此时间内完成 Token Info 的修改操作,并且请不要分享您的验证码给他人。如果您没有进行任何操作或者不是您的操作,请忽略此邮件。 + +如果您有任何疑问或需要帮助,请随时联系我们的客户支持团队。谢谢您的信任和支持! +MagicKBase 团队 diff --git a/config/environments/production.rb b/config/environments/production.rb index 4d2453274..d7172ea41 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -66,7 +66,19 @@ # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. - # config.action_mailer.raise_delivery_errors = false + config.action_mailer.raise_delivery_errors = true + config.action_mailer.delivery_method = :smtp + config.action_mailer.smtp_settings = { + address: ENV["SMTP_ADDRESS"], + port: ENV["SMTP_PORT"], + domain: "noreply@magickbase.com", + user_name: ENV["SMTP_USER"], + password: ENV["SMTP_PASSWORD"], + authentication: "plain", + enable_starttls_auto: true, + open_timeout: 5, + read_timeout: 5 + } # Enable locale fallbacks for I18n (makes lookups for any locale fall back to # the I18n.default_locale when a translation cannot be found). diff --git a/config/initializers/locales.rb b/config/initializers/locales.rb new file mode 100644 index 000000000..c7d592062 --- /dev/null +++ b/config/initializers/locales.rb @@ -0,0 +1,2 @@ +I18n.available_locales = [:en, :zh_CN] +I18n.default_locale = :en diff --git a/config/routes.rb b/config/routes.rb index 3e3e17fd6..ca2ae99cf 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -51,7 +51,7 @@ resources :block_statistics, only: :show ## TODO: unused route resources :epoch_statistics, only: :show resources :market_data, only: :show - resources :udts, only: %i(index show) do + resources :udts, only: %i(index show update) do collection do get :download_csv end @@ -60,6 +60,7 @@ resources :address_udt_transactions, only: :show resources :distribution_data, only: :show resources :monetary_data, only: :show + resources :udt_verifications, only: :update end end draw "v2" diff --git a/db/migrate/20230913091025_create_udt_verifications.rb b/db/migrate/20230913091025_create_udt_verifications.rb new file mode 100644 index 000000000..5441b35b9 --- /dev/null +++ b/db/migrate/20230913091025_create_udt_verifications.rb @@ -0,0 +1,14 @@ +class CreateUdtVerifications < ActiveRecord::Migration[7.0] + def change + create_table :udt_verifications do |t| + t.integer :token + t.datetime :sent_at + t.inet :last_ip + t.belongs_to :udt + t.integer :udt_type_hash + + t.timestamps + t.index :udt_type_hash, unique: true + end + end +end diff --git a/db/migrate/20230914120928_change_contract_info_to_email_in_udt.rb b/db/migrate/20230914120928_change_contract_info_to_email_in_udt.rb new file mode 100644 index 000000000..163bcb6f1 --- /dev/null +++ b/db/migrate/20230914120928_change_contract_info_to_email_in_udt.rb @@ -0,0 +1,5 @@ +class ChangeContractInfoToEmailInUdt < ActiveRecord::Migration[7.0] + def change + rename_column :udts, :contact_info, :email + end +end diff --git a/db/structure.sql b/db/structure.sql index 2d66ef4b6..341b75993 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -2195,6 +2195,41 @@ CREATE TABLE public.udt_transactions ( ); +-- +-- Name: udt_verifications; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.udt_verifications ( + id bigint NOT NULL, + token integer, + sent_at timestamp(6) without time zone, + last_ip inet, + udt_id bigint, + udt_type_hash integer, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + +-- +-- Name: udt_verifications_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.udt_verifications_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: udt_verifications_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.udt_verifications_id_seq OWNED BY public.udt_verifications.id; + + -- -- Name: udts; Type: TABLE; Schema: public; Owner: - -- @@ -2224,7 +2259,7 @@ CREATE TABLE public.udts ( display_name character varying, uan character varying, h24_ckb_transactions_count bigint DEFAULT 0, - contact_info character varying + email character varying ); @@ -2638,6 +2673,13 @@ ALTER TABLE ONLY public.type_scripts ALTER COLUMN id SET DEFAULT nextval('public ALTER TABLE ONLY public.udt_accounts ALTER COLUMN id SET DEFAULT nextval('public.udt_accounts_id_seq'::regclass); +-- +-- Name: udt_verifications id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.udt_verifications ALTER COLUMN id SET DEFAULT nextval('public.udt_verifications_id_seq'::regclass); + + -- -- Name: udts id; Type: DEFAULT; Schema: public; Owner: - -- @@ -3091,6 +3133,14 @@ ALTER TABLE ONLY public.udt_accounts ADD CONSTRAINT udt_accounts_pkey PRIMARY KEY (id); +-- +-- Name: udt_verifications udt_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.udt_verifications + ADD CONSTRAINT udt_verifications_pkey PRIMARY KEY (id); + + -- -- Name: udts udts_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -4127,6 +4177,20 @@ CREATE INDEX index_udt_transactions_on_ckb_transaction_id ON public.udt_transact CREATE INDEX index_udt_transactions_on_udt_id ON public.udt_transactions USING btree (udt_id); +-- +-- Name: index_udt_verifications_on_udt_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_udt_verifications_on_udt_id ON public.udt_verifications USING btree (udt_id); + + +-- +-- Name: index_udt_verifications_on_udt_type_hash; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_udt_verifications_on_udt_type_hash ON public.udt_verifications USING btree (udt_type_hash); + + -- -- Name: index_udts_on_type_hash; Type: INDEX; Schema: public; Owner: - -- @@ -4684,6 +4748,8 @@ INSERT INTO "schema_migrations" (version) VALUES ('20230711040233'), ('20230802015907'), ('20230808020637'), -('20230829061910'); +('20230829061910'), +('20230913091025'), +('20230914120928'); diff --git a/test/controllers/api/v1/udt_verifications_controller_test.rb b/test/controllers/api/v1/udt_verifications_controller_test.rb new file mode 100644 index 000000000..18de08a91 --- /dev/null +++ b/test/controllers/api/v1/udt_verifications_controller_test.rb @@ -0,0 +1,47 @@ +require "test_helper" + +module Api + module V1 + class UdtVerificationsControllerTest < ActionDispatch::IntegrationTest + test "raise error when udt not exist" do + valid_put api_v1_udt_verification_url("0x#{SecureRandom.hex(32)}") + + assert_equal 404, response.status + assert_equal [{ "title" => "UDT Not Found", "detail" => "No UDT records found by given type hash", "code" => 1026, "status" => 404 }], + JSON.parse(response.body) + end + + test "raise error when udt no contact mail" do + udt = create(:udt, published: true) + valid_put api_v1_udt_verification_url(udt.type_hash) + + assert_equal 400, response.status + assert_equal [{ "title" => "UDT has no contact email", "detail" => "", "code" => 1033, "status" => 400 }], + JSON.parse(response.body) + end + + test "raise error when sent too frequently" do + udt = create(:udt, published: true, email: "example@sudt.com") + create(:udt_verification, udt: udt, udt_type_hash: udt.type_hash) + valid_put api_v1_udt_verification_url(udt.type_hash) + + assert_equal 400, response.status + assert_equal [{ "title" => "Token sent too frequently", "detail" => "", "code" => 1036, "status" => 400 }], + JSON.parse(response.body) + end + + test "should sent successfully" do + udt = create(:udt, published: true, email: "example@sudt.com") + valid_put api_v1_udt_verification_url(udt.type_hash) + + assert_equal 200, response.status + assert_equal "ok", JSON.parse(response.body) + uv = UdtVerification.first + assert_not_nil uv.token + assert_not_nil uv.sent_at + assert_not_nil uv.last_ip + assert_equal ActiveJob::Base.queue_adapter.enqueued_jobs[0][:args][0], "UdtVerificationMailer" + end + end + end +end diff --git a/test/controllers/api/v1/udts_controller_test.rb b/test/controllers/api/v1/udts_controller_test.rb index 052bfc56a..f833acfb4 100644 --- a/test/controllers/api/v1/udts_controller_test.rb +++ b/test/controllers/api/v1/udts_controller_test.rb @@ -415,73 +415,153 @@ class UdtsControllerTest < ActionDispatch::IntegrationTest assert_response :success end - # test "should update udt info suceessfully" do - # udt = create(:udt, published: true) - - # valid_put api_v1_udt_url(udt.type_hash), params: { - # symbol: "GWK", - # full_name: "GodwokenToken on testnet_v1", - # decimal: "8", - # total_amount: "100000000000", - # description: "The sUDT_ERC20_Proxy of Godwoken Test Token.", - # operator_website: "https://udt.coin", - # icon_file: "https://img.udt.img", - # uan: "GWK.gw|gb.ckb", - # display_name: "GodwokenToken (via Godwoken Bridge from CKB)", - # contact_info: "contact@usdt.com" - # } - - # assert_response :success - # udt.reload - # assert_equal udt.symbol, "GWK" - # assert_equal udt.full_name, "GodwokenToken on testnet_v1" - # assert_equal udt.decimal, 8 - # assert_equal udt.total_amount, 100000000000 - # assert_equal udt.description, "The sUDT_ERC20_Proxy of Godwoken Test Token." - # assert_equal udt.operator_website, "https://udt.coin" - # assert_equal udt.icon_file, "https://img.udt.img" - # assert_equal udt.uan, "GWK.gw|gb.ckb" - # assert_equal udt.display_name, "GodwokenToken (via Godwoken Bridge from CKB)" - # assert_equal udt.contact_info, "contact@usdt.com" - - # end - - # test "raise parameters error when update udt" do - # udt = create(:udt, published: true) - - # valid_put api_v1_udt_url(udt.type_hash), params: { - # symbol: "GWK", - # full_name: "GodwokenToken on testnet_v1", - # decimal: "8", - # description: "The sUDT_ERC20_Proxy of Godwoken Test Token.", - # operator_website: "https://udt.coin", - # icon_file: "https://img.udt.img", - # uan: "GWK.gw|gb.ckb", - # display_name: "GodwokenToken (via Godwoken Bridge from CKB)" - # } - - # assert_equal 400, response.status - # assert_equal response.body, "[{\"title\":\"UDT info parameters invalid\",\"detail\":\"Validation failed: Total amount can't be blank, Total amount is not a number\",\"code\":1030,\"status\":400}]" - # end - - # test "raise not found error when update udt" do - # udt = create(:udt, published: true) - - # valid_put api_v1_udt_url("#{udt.type_hash}0"), params: { - # symbol: "GWK", - # full_name: "GodwokenToken on testnet_v1", - # decimal: "8", - # total_amount: "100000000", - # description: "The sUDT_ERC20_Proxy of Godwoken Test Token.", - # operator_website: "https://udt.coin", - # icon_file: "https://img.udt.img", - # uan: "GWK.gw|gb.ckb", - # display_name: "GodwokenToken (via Godwoken Bridge from CKB)" - # } - - # assert_equal 404, response.status - # assert_equal response.body, "[{\"title\":\"UDT Not Found\",\"detail\":\"No UDT records found by given type hash\",\"code\":1026,\"status\":404}]" - # end + test "should submit udt info suceessfully" do + udt = create(:udt, published: true) + + valid_put api_v1_udt_url(udt.type_hash), params: { + symbol: "GWK", + full_name: "GodwokenToken on testnet_v1", + decimal: "8", + total_amount: "100000000000", + description: "The sUDT_ERC20_Proxy of Godwoken Test Token.", + operator_website: "https://udt.coin", + icon_file: "https://img.udt.img", + uan: "GWK.gw|gb.ckb", + display_name: "GodwokenToken (via Godwoken Bridge from CKB)", + email: "contact@usdt.com" + } + + assert_response :success + udt.reload + assert_equal udt.symbol, "GWK" + assert_equal udt.full_name, "GodwokenToken on testnet_v1" + assert_equal udt.decimal, 8 + assert_equal udt.total_amount, 100000000000 + assert_equal udt.description, "The sUDT_ERC20_Proxy of Godwoken Test Token." + assert_equal udt.operator_website, "https://udt.coin" + assert_equal udt.icon_file, "https://img.udt.img" + assert_equal udt.uan, "GWK.gw|gb.ckb" + assert_equal udt.display_name, "GodwokenToken (via Godwoken Bridge from CKB)" + assert_equal udt.email, "contact@usdt.com" + end + + test "raise email blank error when submit udt" do + udt = create(:udt, published: true) + + valid_put api_v1_udt_url(udt.type_hash), params: { + symbol: "GWK", + full_name: "GodwokenToken on testnet_v1", + decimal: "8", + description: "The sUDT_ERC20_Proxy of Godwoken Test Token.", + operator_website: "https://udt.coin", + icon_file: "https://img.udt.img", + uan: "GWK.gw|gb.ckb", + display_name: "GodwokenToken (via Godwoken Bridge from CKB)" + } + + assert_equal 400, response.status + assert_equal [{ "title" => "UDT info parameters invalid", "detail" => "Email can't be blank", "code" => 1030, "status" => 400 }], + JSON.parse(response.body) + end + + test "raise email format error when submit udt" do + udt = create(:udt, published: true) + + valid_put api_v1_udt_url(udt.type_hash), params: { + symbol: "GWK", + full_name: "GodwokenToken on testnet_v1", + decimal: "8", + total_amount: "100000000000", + description: "The sUDT_ERC20_Proxy of Godwoken Test Token.", + operator_website: "https://udt.coin", + icon_file: "https://img.udt.img", + uan: "GWK.gw|gb.ckb", + display_name: "GodwokenToken (via Godwoken Bridge from CKB)", + email: "abcdefg" + } + + assert_equal 400, response.status + assert_equal [{ "title" => "UDT info parameters invalid", "detail" => "Validation failed: Email Not a valid email", "code" => 1030, "status" => 400 }], + JSON.parse(response.body) + end + + test "raise not found error when submit udt" do + udt = create(:udt, published: true) + + valid_put api_v1_udt_url("#{udt.type_hash}0"), params: { + symbol: "GWK", + full_name: "GodwokenToken on testnet_v1", + decimal: "8", + total_amount: "100000000", + description: "The sUDT_ERC20_Proxy of Godwoken Test Token.", + operator_website: "https://udt.coin", + icon_file: "https://img.udt.img", + uan: "GWK.gw|gb.ckb", + display_name: "GodwokenToken (via Godwoken Bridge from CKB)" + } + + assert_equal 404, response.status + assert_equal [{ "title" => "UDT Not Found", "detail" => "No UDT records found by given type hash", "code" => 1026, "status" => 404 }], + JSON.parse(response.body) + end + + test "raise no udt_verification error when update udt" do + udt = create(:udt, email: "abc@sudt.com", published: true) + valid_put api_v1_udt_url("#{udt.type_hash}"), params: { + symbol: "GWK", + full_name: "GodwokenToken on testnet_v1", + token: "123456" + } + + assert_equal 404, response.status + assert_equal [{ "title" => "UDT Verification Not Found", "detail" => "No UDT verification records found by given type hash", "code" => 1032, "status" => 404 }], + JSON.parse(response.body) + end + + test "raise udt_verification expired error when update udt" do + udt = create(:udt, email: "abc@sudt.com", published: true) + create(:udt_verification, sent_at: Time.now - 11.minutes, udt: udt) + valid_put api_v1_udt_url("#{udt.type_hash}"), params: { + symbol: "GWK", + full_name: "GodwokenToken on testnet_v1", + total_amount: "100000000", + token: "123456" + } + + assert_equal 400, response.status + assert_equal [{ "title" => "Token has expired", "detail" => "", "code" => 1034, "status" => 400 }], + JSON.parse(response.body) + end + + test "raise udt_verification token not match error when update udt" do + udt = create(:udt, email: "abc@sudt.com", published: true) + create(:udt_verification, udt: udt) + valid_put api_v1_udt_url("#{udt.type_hash}"), params: { + symbol: "GWK", + full_name: "GodwokenToken on testnet_v1", + total_amount: "100000000", + token: "123" + } + + assert_equal 400, response.status + assert_equal [{ "title" => "Token is not matched", "detail" => "", "code" => 1035, "status" => 400 }], + JSON.parse(response.body) + end + + test "should update successfully when update udt" do + udt = create(:udt, email: "abc@sudt.com", published: true) + create(:udt_verification, udt: udt) + valid_put api_v1_udt_url("#{udt.type_hash}"), params: { + symbol: "GWK", + full_name: "GodwokenToken on testnet_v1", + total_amount: "100000000", + token: "123456" + } + + assert_equal 200, response.status + assert_equal "ok", JSON.parse(response.body) + assert_equal "GWK", udt.reload.symbol + end end end end diff --git a/test/factories/udt_verifications.rb b/test/factories/udt_verifications.rb new file mode 100644 index 000000000..faeba6e38 --- /dev/null +++ b/test/factories/udt_verifications.rb @@ -0,0 +1,9 @@ +FactoryBot.define do + factory :udt_verification do + token { 123456 } + sent_at { Time.now } + last_ip { "127.0.0.1" } + udt_type_hash { "0x#{SecureRandom.hex(32)}" } + udt + end +end diff --git a/test/fixtures/udt_verification_mailer/send_token_email.en.text.erb b/test/fixtures/udt_verification_mailer/send_token_email.en.text.erb new file mode 100644 index 000000000..166d98fcf --- /dev/null +++ b/test/fixtures/udt_verification_mailer/send_token_email.en.text.erb @@ -0,0 +1,12 @@ +Dear Token Info Author, + +To ensure the effectiveness of your Token Info modification operation, we have generated a verification code for you to complete the Token Info update. + +Your verification code is: 123456 + +Please note that this verification code is only valid for the next 10 minutes. Please use it to complete your Token Info modification within this time frame and refrain from sharing your code with others. If you have not initiated any action or if this operation is not associated with you, please disregard this email. + +If you have any questions or require assistance, please feel free to contact our customer support team.Thank you for your trust and support! + +Best regards, +The MagicKBase Team diff --git a/test/fixtures/udt_verification_mailer/send_token_email.zh_CN.text.erb b/test/fixtures/udt_verification_mailer/send_token_email.zh_CN.text.erb new file mode 100644 index 000000000..eba324bd8 --- /dev/null +++ b/test/fixtures/udt_verification_mailer/send_token_email.zh_CN.text.erb @@ -0,0 +1,10 @@ +尊敬的 Token Info 作者, + +为了确保本次 Token Info 修改操作的有效性,我们为您生成了一个验证码,用于完成 Token Info 的修改操作。 + +您的验证码是:123456 + +请注意,该验证码仅在接下来的10分钟内有效。请在此时间内完成 Token Info 的修改操作,并且请不要分享您的验证码给他人。如果您没有进行任何操作或者不是您的操作,请忽略此邮件。 + +如果您有任何疑问或需要帮助,请随时联系我们的客户支持团队。谢谢您的信任和支持! +MagicKBase 团队 diff --git a/test/mailers/previews/udt_verification_mailer_preview.rb b/test/mailers/previews/udt_verification_mailer_preview.rb new file mode 100644 index 000000000..9f5a6cee4 --- /dev/null +++ b/test/mailers/previews/udt_verification_mailer_preview.rb @@ -0,0 +1,5 @@ +class UdtVerificationMailerPreview < ActionMailer::Preview + def send_token + UdtVerificationMailer.with(email: "receiver@example.com", token: "123456", locale: params[:locale]).send_token + end +end diff --git a/test/mailers/udt_verification_mailer_test.rb b/test/mailers/udt_verification_mailer_test.rb new file mode 100644 index 000000000..0193055cc --- /dev/null +++ b/test/mailers/udt_verification_mailer_test.rb @@ -0,0 +1,27 @@ +require "test_helper" + +class UdtVerificationMailerTest < ActionMailer::TestCase + test "send token" do + email = UdtVerificationMailer.with(email: "receiver@example.com", token: "123456").send_token + + assert_emails 1 do + email.deliver_now + end + + # Test the body of the sent email contains what we expect it to + assert_equal ["noreply@magickbase.com"], email.from + assert_equal ["receiver@example.com"], email.to + assert_equal "Token Info Verification", email.subject + assert_equal "#{read_fixture('send_token_email.en.text.erb').join}\n", email.body.to_s + end + + test "when zh_CN locale" do + email = UdtVerificationMailer.with(email: "receiver@example.com", token: "123456", locale: "zh_CN").send_token + + assert_emails 1 do + email.deliver_now + end + + assert_equal "#{read_fixture('send_token_email.zh_CN.text.erb').join}\n", email.body.to_s.tr("\r", "") + end +end diff --git a/test/models/udt_verification_test.rb b/test/models/udt_verification_test.rb new file mode 100644 index 000000000..0527a4ee2 --- /dev/null +++ b/test/models/udt_verification_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class UdtVerificationTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end From 11ad77c3f4d99d8b8601a27c5f917ff3ff94698b Mon Sep 17 00:00:00 2001 From: NanZhang Date: Mon, 18 Sep 2023 15:50:54 +0800 Subject: [PATCH 4/9] feat: add contract statistic (#1456) * feat: add contract statistic * chore: adjust tests --- app/controllers/api/v2/scripts_controller.rb | 27 +++++------- app/models/contract.rb | 29 +++++++------ .../v2/scripts/deployed_cells.json.jbuilder | 43 +++++++++---------- .../v2/scripts/referring_cells.json.jbuilder | 43 +++++++++---------- app/workers/contact_statistic_worker.rb | 20 +++++++++ ...230918033957_add_statistics_to_contract.rb | 9 ++++ db/structure.sql | 10 ++++- lib/scheduler.rb | 4 ++ 8 files changed, 110 insertions(+), 75 deletions(-) create mode 100644 app/workers/contact_statistic_worker.rb create mode 100644 db/migrate/20230918033957_add_statistics_to_contract.rb diff --git a/app/controllers/api/v2/scripts_controller.rb b/app/controllers/api/v2/scripts_controller.rb index 0378d3872..63c80ce39 100644 --- a/app/controllers/api/v2/scripts_controller.rb +++ b/app/controllers/api/v2/scripts_controller.rb @@ -9,12 +9,8 @@ class ScriptsController < BaseController def general_info head :not_found and return if @script.blank? || @contract.blank? - key = ["contract_info", @contract.code_hash, @contract.hash_type] - result = - Rails.cache.fetch(key, expires_in: 10.minutes) do - get_script_content - end - render json: { data: result } + expires_in 15.seconds, public: true, must_revalidate: true, stale_while_revalidate: 5.seconds + render json: { data: get_script_content } end def ckb_transactions @@ -31,23 +27,20 @@ def deployed_cells head :not_found and return if @contract.blank? expires_in 15.seconds, public: true, must_revalidate: true, stale_while_revalidate: 5.seconds - @deployed_cells = @contract.deployed_cells.page(@page).per(@page_size).fast_page + @deployed_cells = @contract.deployed_cell_outputs.live.page(@page).per(@page_size).fast_page end def referring_cells head :not_found and return if @contract.blank? expires_in 15.seconds, public: true, must_revalidate: true, stale_while_revalidate: 5.seconds - @referring_cells = @contract.referring_cells.page(@page).per(@page_size).fast_page + @referring_cells = @contract.referring_cell_outputs.live.page(@page).per(@page_size).fast_page end private def get_script_content - referring_cells = @contract&.referring_cell_outputs - deployed_cells = @contract&.deployed_cell_outputs&.live - transactions = @contract&.cell_dependencies - + deployed_cells = @contract.deployed_cell_outputs if deployed_cells.present? deployed_type_script = deployed_cells[0].type_script if deployed_type_script.code_hash == Settings.type_id_code_hash @@ -60,11 +53,11 @@ def get_script_content code_hash: @script.code_hash, hash_type: @script.hash_type, script_type: @script.class.to_s, - capacity_of_deployed_cells: deployed_cells&.sum(:capacity), - capacity_of_referring_cells: referring_cells&.sum(:capacity), - count_of_transactions: transactions&.count.to_i, - count_of_deployed_cells: deployed_cells&.count.to_i, - count_of_referring_cells: referring_cells&.count.to_i + capacity_of_deployed_cells: @contract.total_deployed_cells_capacity, + capacity_of_referring_cells: @contract.total_referring_cells_capacity, + count_of_transactions: @contract.ckb_transactions_count, + count_of_deployed_cells: @contract.deployed_cells_count, + count_of_referring_cells: @contract.referring_cells_count } end diff --git a/app/models/contract.rb b/app/models/contract.rb index 115e6e5d5..f6aeee812 100644 --- a/app/models/contract.rb +++ b/app/models/contract.rb @@ -25,18 +25,23 @@ def self.create_initial_data # # Table name: contracts # -# id :bigint not null, primary key -# code_hash :binary -# hash_type :string -# deployed_args :string -# role :string default("type_script") -# name :string -# symbol :string -# description :string -# verified :boolean default(FALSE) -# created_at :datetime not null -# updated_at :datetime not null -# deprecated :boolean +# id :bigint not null, primary key +# code_hash :binary +# hash_type :string +# deployed_args :string +# role :string default("type_script") +# name :string +# symbol :string +# description :string +# verified :boolean default(FALSE) +# created_at :datetime not null +# updated_at :datetime not null +# deprecated :boolean +# ckb_transactions_count :decimal(30, ) default(0) +# deployed_cells_count :decimal(30, ) default(0) +# referring_cells_count :decimal(30, ) default(0) +# total_deployed_cells_capacity :decimal(30, ) default(0) +# total_referring_cells_capacity :decimal(30, ) default(0) # # Indexes # diff --git a/app/views/api/v2/scripts/deployed_cells.json.jbuilder b/app/views/api/v2/scripts/deployed_cells.json.jbuilder index 7f84e408f..ddf21033d 100644 --- a/app/views/api/v2/scripts/deployed_cells.json.jbuilder +++ b/app/views/api/v2/scripts/deployed_cells.json.jbuilder @@ -1,27 +1,26 @@ json.data do json.deployed_cells @deployed_cells do |deployed_cell| - cell_output = deployed_cell.cell_output - json.id deployed_cell.cell_output.id - json.capacity deployed_cell.cell_output.capacity - json.ckb_transaction_id deployed_cell.cell_output.ckb_transaction_id - json.created_at deployed_cell.cell_output.created_at - json.updated_at deployed_cell.cell_output.updated_at - json.status cell_output.status - json.address_id cell_output.address_id - json.block_id cell_output.block_id - json.tx_hash cell_output.tx_hash - json.cell_index cell_output.cell_index - json.consumed_by_id cell_output.consumed_by_id - json.cell_type cell_output.cell_type - json.data_size cell_output.data_size - json.occupied_capacity cell_output.occupied_capacity - json.block_timestamp cell_output.block_timestamp - json.consumed_block_timestamp cell_output.consumed_block_timestamp - json.type_hash cell_output.type_hash - json.udt_amount cell_output.udt_amount - json.dao cell_output.dao - json.lock_script_id cell_output.lock_script_id - json.type_script_id cell_output.type_script_id + json.id deployed_cell.id + json.capacity deployed_cell.capacity + json.ckb_transaction_id deployed_cell.ckb_transaction_id + json.created_at deployed_cell.created_at + json.updated_at deployed_cell.updated_at + json.status deployed_cell.status + json.address_id deployed_cell.address_id + json.block_id deployed_cell.block_id + json.tx_hash deployed_cell.tx_hash + json.cell_index deployed_cell.cell_index + json.consumed_by_id deployed_cell.consumed_by_id + json.cell_type deployed_cell.cell_type + json.data_size deployed_cell.data_size + json.occupied_capacity deployed_cell.occupied_capacity + json.block_timestamp deployed_cell.block_timestamp + json.consumed_block_timestamp deployed_cell.consumed_block_timestamp + json.type_hash deployed_cell.type_hash + json.udt_amount deployed_cell.udt_amount + json.dao deployed_cell.dao + json.lock_script_id deployed_cell.lock_script_id + json.type_script_id deployed_cell.type_script_id end json.meta do json.total @deployed_cells.total_count diff --git a/app/views/api/v2/scripts/referring_cells.json.jbuilder b/app/views/api/v2/scripts/referring_cells.json.jbuilder index a4cc5f13f..c496c2e9e 100644 --- a/app/views/api/v2/scripts/referring_cells.json.jbuilder +++ b/app/views/api/v2/scripts/referring_cells.json.jbuilder @@ -1,27 +1,26 @@ json.data do json.referring_cells @referring_cells do |referring_cell| - cell_output = referring_cell.cell_output - json.id cell_output.id - json.capacity cell_output.capacity - json.ckb_transaction_id cell_output.ckb_transaction_id - json.created_at cell_output.created_at - json.updated_at cell_output.updated_at - json.status cell_output.status - json.address_id cell_output.address_id - json.block_id cell_output.block_id - json.tx_hash cell_output.tx_hash - json.cell_index cell_output.cell_index - json.consumed_by_id cell_output.consumed_by_id - json.cell_type cell_output.cell_type - json.data_size cell_output.data_size - json.occupied_capacity cell_output.occupied_capacity - json.block_timestamp cell_output.block_timestamp - json.consumed_block_timestamp cell_output.consumed_block_timestamp - json.type_hash cell_output.type_hash - json.udt_amount cell_output.udt_amount - json.dao cell_output.dao - json.lock_script_id cell_output.lock_script_id - json.type_script_id cell_output.type_script_id + json.id referring_cell.id + json.capacity referring_cell.capacity + json.ckb_transaction_id referring_cell.ckb_transaction_id + json.created_at referring_cell.created_at + json.updated_at referring_cell.updated_at + json.status referring_cell.status + json.address_id referring_cell.address_id + json.block_id referring_cell.block_id + json.tx_hash referring_cell.tx_hash + json.cell_index referring_cell.cell_index + json.consumed_by_id referring_cell.consumed_by_id + json.cell_type referring_cell.cell_type + json.data_size referring_cell.data_size + json.occupied_capacity referring_cell.occupied_capacity + json.block_timestamp referring_cell.block_timestamp + json.consumed_block_timestamp referring_cell.consumed_block_timestamp + json.type_hash referring_cell.type_hash + json.udt_amount referring_cell.udt_amount + json.dao referring_cell.dao + json.lock_script_id referring_cell.lock_script_id + json.type_script_id referring_cell.type_script_id end json.meta do json.total @referring_cells.total_count diff --git a/app/workers/contact_statistic_worker.rb b/app/workers/contact_statistic_worker.rb new file mode 100644 index 000000000..558132c41 --- /dev/null +++ b/app/workers/contact_statistic_worker.rb @@ -0,0 +1,20 @@ +class ContractStatisticWorker + include Sidekiq::Worker + sidekiq_options queue: "critical" + + def perform + Contract.find_each do |contract| + referring_cells = contract.referring_cell_outputs&.live + deployed_cells = contract.deployed_cell_outputs&.live + transactions = contract.cell_dependencies + + contract.update( + ckb_transactions_count: transactions.count, + deployed_cells_count: deployed_cells&.count.to_i, + referring_cells_count: referring_cells&.count.to_i, + total_deployed_cells_capacity: deployed_cells&.sum(:capacity), + total_referring_cells_capacity: referring_cells&.sum(:capacity) + ) + end + end +end diff --git a/db/migrate/20230918033957_add_statistics_to_contract.rb b/db/migrate/20230918033957_add_statistics_to_contract.rb new file mode 100644 index 000000000..e78c6eed2 --- /dev/null +++ b/db/migrate/20230918033957_add_statistics_to_contract.rb @@ -0,0 +1,9 @@ +class AddStatisticsToContract < ActiveRecord::Migration[7.0] + def change + add_column :contracts, :ckb_transactions_count, :decimal, precision: 30, default: 0 + add_column :contracts, :deployed_cells_count, :decimal, precision: 30, default: 0 + add_column :contracts, :referring_cells_count, :decimal, precision: 30, default: 0 + add_column :contracts, :total_deployed_cells_capacity, :decimal, precision: 30, default: 0 + add_column :contracts, :total_referring_cells_capacity, :decimal, precision: 30, default: 0 + end +end diff --git a/db/structure.sql b/db/structure.sql index 341b75993..3d2eae987 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -1048,7 +1048,12 @@ CREATE TABLE public.contracts ( verified boolean DEFAULT false, created_at timestamp(6) without time zone NOT NULL, updated_at timestamp(6) without time zone NOT NULL, - deprecated boolean + deprecated boolean, + ckb_transactions_count numeric(30,0) DEFAULT 0.0, + deployed_cells_count numeric(30,0) DEFAULT 0.0, + referring_cells_count numeric(30,0) DEFAULT 0.0, + total_deployed_cells_capacity numeric(30,0) DEFAULT 0.0, + total_referring_cells_capacity numeric(30,0) DEFAULT 0.0 ); @@ -4750,6 +4755,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20230808020637'), ('20230829061910'), ('20230913091025'), -('20230914120928'); +('20230914120928'), +('20230918033957'); diff --git a/lib/scheduler.rb b/lib/scheduler.rb index f440a9937..ff8d0fa58 100644 --- a/lib/scheduler.rb +++ b/lib/scheduler.rb @@ -121,4 +121,8 @@ def call_worker(clz) call_worker CleanAddressBlockSnapshotWorker end +s.every "1h", overlap: false do + call_worker ContractStatisticWorker +end + s.join From 17e3d9cfd59662014cdf617ddb91e49627ece3c7 Mon Sep 17 00:00:00 2001 From: NanZhang Date: Mon, 18 Sep 2023 16:03:50 +0800 Subject: [PATCH 5/9] chore: rename worker file name (#1457) --- .../{contact_statistic_worker.rb => contract_statistic_worker.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/workers/{contact_statistic_worker.rb => contract_statistic_worker.rb} (100%) diff --git a/app/workers/contact_statistic_worker.rb b/app/workers/contract_statistic_worker.rb similarity index 100% rename from app/workers/contact_statistic_worker.rb rename to app/workers/contract_statistic_worker.rb From baf0d35689e154ad8ce4fdbe0eaf69f1dd760e37 Mon Sep 17 00:00:00 2001 From: NanZhang Date: Tue, 19 Sep 2023 13:30:06 +0800 Subject: [PATCH 6/9] ci: update build steps (#1458) --- .github/workflows/build.yml | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9fac8e31a..ed096f644 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,21 +22,25 @@ jobs: image-name: '${{ steps.docker_build.outputs.imageFullName }}' image-tag: '${{ steps.docker_build.outputs.tags }}' steps: -# - name: Free Disk Space (Ubuntu) -# uses: jlumbroso/free-disk-space@main -# with: -# # this might remove tools that are actually needed, -# # if set to "true" but frees about 6 GB -# tool-cache: false -# -# # all of these default to true, but feel free to set to -# # "false" if necessary for your workflow -# android: true -# dotnet: true -# haskell: true -# large-packages: true -# docker-images: true -# swap-storage: true + - name: Update Package List and Remove Dotnet + run: | + sudo apt-get update + sudo apt-get remove -y '^dotnet-.*' + - name: Free Disk Space (Ubuntu) + uses: jlumbroso/free-disk-space@main + with: + # this might remove tools that are actually needed, + # if set to "true" but frees about 6 GB + tool-cache: false + + # all of these default to true, but feel free to set to + # "false" if necessary for your workflow + android: true + dotnet: true + haskell: true + large-packages: true + docker-images: true + swap-storage: true - uses: actions/checkout@v3 with: fetch-depth: 0 From c875e781cac75a0f5f4a65f3c10a781b7da1a537 Mon Sep 17 00:00:00 2001 From: zhangnan Date: Fri, 22 Sep 2023 11:58:16 +0800 Subject: [PATCH 7/9] fix: sync spore udt info error --- app/utils/ckb_utils.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/utils/ckb_utils.rb b/app/utils/ckb_utils.rb index 2423c75e5..f0d63ad4d 100644 --- a/app/utils/ckb_utils.rb +++ b/app/utils/ckb_utils.rb @@ -560,6 +560,7 @@ def self.parse_spore_cluster_data(hex_data) description_offset = [data.slice(16, 8)].pack("H*").unpack1("l") * 2 name = [data.slice(name_offset + 8..description_offset - 1)].pack("H*") description = [data.slice(description_offset + 8..-1)].pack("H*") + name = "#{name[0,97]}..." if name.length > 100 { name: name, description: description } rescue => _e { name: nil, description: nil } From 9bc618ec957b536a6ac23668f940af3d5fd2b750 Mon Sep 17 00:00:00 2001 From: NanZhang Date: Fri, 22 Sep 2023 12:26:39 +0800 Subject: [PATCH 8/9] Fix/sync spore udt info (#1460) fix: sync spore udt info error Co-authored-by: Chen Yu Co-authored-by: Miles Zhang --- app/utils/ckb_utils.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/utils/ckb_utils.rb b/app/utils/ckb_utils.rb index 2423c75e5..f0d63ad4d 100644 --- a/app/utils/ckb_utils.rb +++ b/app/utils/ckb_utils.rb @@ -560,6 +560,7 @@ def self.parse_spore_cluster_data(hex_data) description_offset = [data.slice(16, 8)].pack("H*").unpack1("l") * 2 name = [data.slice(name_offset + 8..description_offset - 1)].pack("H*") description = [data.slice(description_offset + 8..-1)].pack("H*") + name = "#{name[0,97]}..." if name.length > 100 { name: name, description: description } rescue => _e { name: nil, description: nil } From 4650ca64377309bcde3d2ffa2c1eed2a0ab45cdd Mon Sep 17 00:00:00 2001 From: NanZhang Date: Fri, 22 Sep 2023 17:16:59 +0800 Subject: [PATCH 9/9] refactor: contract association ckb_transactions query (#1461) --- app/controllers/api/v2/scripts_controller.rb | 9 +++------ app/views/api/v2/scripts/ckb_transactions.json.jbuilder | 2 +- app/views/api/v2/scripts/deployed_cells.json.jbuilder | 2 +- app/views/api/v2/scripts/referring_cells.json.jbuilder | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/app/controllers/api/v2/scripts_controller.rb b/app/controllers/api/v2/scripts_controller.rb index 63c80ce39..f3bdfdfb9 100644 --- a/app/controllers/api/v2/scripts_controller.rb +++ b/app/controllers/api/v2/scripts_controller.rb @@ -17,24 +17,21 @@ def ckb_transactions head :not_found and return if @contract.blank? expires_in 15.seconds, public: true, must_revalidate: true, stale_while_revalidate: 5.seconds - scope = CellDependency.where(contract_id: @contract.id).order(ckb_transaction_id: :desc) - tx_ids = scope.page(@page).per(@page_size).pluck(:ckb_transaction_id) - @ckb_transactions = CkbTransaction.find(tx_ids) - @total = scope.count + @ckb_transactions = @contract.ckb_transactions.order(id: :desc).page(@page).per(@page_size) end def deployed_cells head :not_found and return if @contract.blank? expires_in 15.seconds, public: true, must_revalidate: true, stale_while_revalidate: 5.seconds - @deployed_cells = @contract.deployed_cell_outputs.live.page(@page).per(@page_size).fast_page + @deployed_cells = @contract.deployed_cell_outputs.live.page(@page).per(@page_size) end def referring_cells head :not_found and return if @contract.blank? expires_in 15.seconds, public: true, must_revalidate: true, stale_while_revalidate: 5.seconds - @referring_cells = @contract.referring_cell_outputs.live.page(@page).per(@page_size).fast_page + @referring_cells = @contract.referring_cell_outputs.live.page(@page).per(@page_size) end private diff --git a/app/views/api/v2/scripts/ckb_transactions.json.jbuilder b/app/views/api/v2/scripts/ckb_transactions.json.jbuilder index 3ee41a87d..19b211f84 100644 --- a/app/views/api/v2/scripts/ckb_transactions.json.jbuilder +++ b/app/views/api/v2/scripts/ckb_transactions.json.jbuilder @@ -23,7 +23,7 @@ json.data do json.display_outputs tx.display_outputs end json.meta do - json.total @total + json.total @contract.ckb_transactions_count json.page_size @page_size.to_i end end diff --git a/app/views/api/v2/scripts/deployed_cells.json.jbuilder b/app/views/api/v2/scripts/deployed_cells.json.jbuilder index ddf21033d..ad742d4ea 100644 --- a/app/views/api/v2/scripts/deployed_cells.json.jbuilder +++ b/app/views/api/v2/scripts/deployed_cells.json.jbuilder @@ -23,7 +23,7 @@ json.data do json.type_script_id deployed_cell.type_script_id end json.meta do - json.total @deployed_cells.total_count + json.total @contract.deployed_cells_count json.page_size @page_size.to_i end end diff --git a/app/views/api/v2/scripts/referring_cells.json.jbuilder b/app/views/api/v2/scripts/referring_cells.json.jbuilder index c496c2e9e..4e13e3c26 100644 --- a/app/views/api/v2/scripts/referring_cells.json.jbuilder +++ b/app/views/api/v2/scripts/referring_cells.json.jbuilder @@ -23,7 +23,7 @@ json.data do json.type_script_id referring_cell.type_script_id end json.meta do - json.total @referring_cells.total_count + json.total @contract.referring_cells_count json.page_size @page_size.to_i end end