Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,53 @@

# Stage 1: Build the binary

FROM rust:slim-bullseye as builder
FROM rust:slim-bookworm AS builder

WORKDIR /app

COPY --link . /app

RUN for i in 1 2 3; do apt-get update && apt-get install --fix-missing -y cmake curl clang git pkg-config libssl-dev libdw-dev libpq-dev lld && break || sleep 10; done
RUN apt-get update \
&& apt-get install --no-install-recommends --fix-missing -y \
cmake \
curl \
clang \
make \
git \
pkg-config \
libssl-dev \
libdw-dev \
libpq-dev \
lld \
&& rm -rf /var/lib/apt/lists/*
ENV CARGO_NET_GIT_FETCH_WITH_CLI true

Check warning on line 24 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/
# TODO: Fix this with real processors.
RUN cargo build --locked --release -p processor && ls -lah target/release/
RUN cp target/release/processor /usr/local/bin

# add build info
ARG GIT_TAG
ENV GIT_TAG ${GIT_TAG}

Check warning on line 31 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/
ARG GIT_BRANCH
ENV GIT_BRANCH ${GIT_BRANCH}

Check warning on line 33 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/
ARG GIT_SHA
ENV GIT_SHA ${GIT_SHA}

Check warning on line 35 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/

# Stage 2: Create the final image

FROM debian:bullseye-slim
FROM debian:bookworm-slim

COPY --from=builder /usr/local/bin/processor /usr/local/bin

RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install --no-install-recommends --fix-missing -y \
libssl1.1 \
libssl3 \
ca-certificates \
net-tools \
tcpdump \
iproute2 \
netcat \
netcat-openbsd \
libdw-dev \
libpq-dev \
curl
Expand All @@ -45,11 +57,11 @@

# add build info
ARG GIT_TAG
ENV GIT_TAG ${GIT_TAG}

Check warning on line 60 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/
ARG GIT_BRANCH
ENV GIT_BRANCH ${GIT_BRANCH}

Check warning on line 62 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/
ARG GIT_SHA
ENV GIT_SHA ${GIT_SHA}

Check warning on line 64 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/

# The health check port
EXPOSE 8084
Expand Down
22 changes: 17 additions & 5 deletions Dockerfile.address-reputation-api
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,52 @@

# Stage 1: Build the binary

FROM rust:slim-bullseye as builder
FROM rust:slim-bookworm AS builder

WORKDIR /app

COPY --link . /app

RUN for i in 1 2 3; do apt-get update && apt-get install --fix-missing -y cmake curl clang git pkg-config libssl-dev libdw-dev libpq-dev lld && break || sleep 10; done
RUN apt-get update \
&& apt-get install --no-install-recommends --fix-missing -y \
cmake \
curl \
clang \
make \
git \
pkg-config \
libssl-dev \
libdw-dev \
libpq-dev \
lld \
&& rm -rf /var/lib/apt/lists/*
ENV CARGO_NET_GIT_FETCH_WITH_CLI true

Check warning on line 24 in Dockerfile.address-reputation-api

View workflow job for this annotation

GitHub Actions / BuildAddressReputationApi

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/
RUN cargo build --locked --release -p address-reputation-api && ls -lah target/release/
RUN cp target/release/address-reputation-api /usr/local/bin

# add build info
ARG GIT_TAG
ENV GIT_TAG ${GIT_TAG}

Check warning on line 30 in Dockerfile.address-reputation-api

View workflow job for this annotation

GitHub Actions / BuildAddressReputationApi

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/
ARG GIT_BRANCH
ENV GIT_BRANCH ${GIT_BRANCH}

Check warning on line 32 in Dockerfile.address-reputation-api

View workflow job for this annotation

GitHub Actions / BuildAddressReputationApi

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/
ARG GIT_SHA
ENV GIT_SHA ${GIT_SHA}

Check warning on line 34 in Dockerfile.address-reputation-api

View workflow job for this annotation

GitHub Actions / BuildAddressReputationApi

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/

# Stage 2: Create the final image

FROM debian:bullseye-slim
FROM debian:bookworm-slim

COPY --from=builder /usr/local/bin/address-reputation-api /usr/local/bin

RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install --no-install-recommends --fix-missing -y \
libssl1.1 \
libssl3 \
ca-certificates \
net-tools \
tcpdump \
iproute2 \
netcat \
netcat-openbsd \
libdw-dev \
libpq-dev \
curl
Expand All @@ -44,11 +56,11 @@

# add build info
ARG GIT_TAG
ENV GIT_TAG ${GIT_TAG}

Check warning on line 59 in Dockerfile.address-reputation-api

View workflow job for this annotation

GitHub Actions / BuildAddressReputationApi

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/
ARG GIT_BRANCH
ENV GIT_BRANCH ${GIT_BRANCH}

Check warning on line 61 in Dockerfile.address-reputation-api

View workflow job for this annotation

GitHub Actions / BuildAddressReputationApi

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/
ARG GIT_SHA
ENV GIT_SHA ${GIT_SHA}

Check warning on line 63 in Dockerfile.address-reputation-api

View workflow job for this annotation

GitHub Actions / BuildAddressReputationApi

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/

# API server port (matches ServerConfig default; override via config file)
EXPOSE 8080
Expand Down
216 changes: 210 additions & 6 deletions processor/src/processors/token_v2/token_models/token_claims.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ use field_count::FieldCount;
use parquet_derive::ParquetRecordWriter;
use serde::{Deserialize, Serialize};

// Map to keep track of the metadata of token offers that were claimed. The key is the token data id of the offer.
pub type TokenV1Claimed = AHashMap<String, TokenActivityHelperV1>;
/// (token_data_id, property_version). Token V1 offers of the same named token
/// with different property versions are distinct `current_token_pending_claims` rows.
pub type TokenV1OfferEventKey = (String, BigDecimal);

// Map to keep track of the metadata of token offers that were canceled. The key is the token data id of the offer.
pub type TokenV1Canceled = AHashMap<String, TokenActivityHelperV1>;
// Map to keep track of the metadata of token offers that were claimed.
pub type TokenV1Claimed = AHashMap<TokenV1OfferEventKey, TokenActivityHelperV1>;

// Map to keep track of the metadata of token offers that were canceled.
pub type TokenV1Canceled = AHashMap<TokenV1OfferEventKey, TokenActivityHelperV1>;

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct CurrentTokenPendingClaim {
Expand Down Expand Up @@ -166,6 +170,10 @@ impl CurrentTokenPendingClaim {
if let Some(offer) = &maybe_offer {
let table_handle = standardize_address(&table_item.handle.to_string());
let token_data_id = offer.token_id.token_data_id.to_id();
let offer_key = (
token_data_id.clone(),
offer.token_id.property_version.clone(),
);

// Try to find owner from write resources
let mut maybe_owner_address = table_handle_to_owner
Expand All @@ -174,10 +182,10 @@ impl CurrentTokenPendingClaim {

// If table handle isn't in TableHandleToOwner, try to find owner from token v1 claim events
if maybe_owner_address.is_none() {
if let Some(token_claimed) = tokens_claimed.get(&token_data_id) {
if let Some(token_claimed) = tokens_claimed.get(&offer_key) {
maybe_owner_address = token_claimed.from_address.clone();
}
if let Some(token_canceled) = tokens_canceled.get(&token_data_id) {
if let Some(token_canceled) = tokens_canceled.get(&offer_key) {
maybe_owner_address = token_canceled.from_address.clone();
}
}
Expand Down Expand Up @@ -337,3 +345,199 @@ impl From<CurrentTokenPendingClaim> for PostgresCurrentTokenPendingClaim {
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::processors::token_v2::token_v2_models::v2_token_activities::TokenActivityV2;
use ahash::AHashMap;
use aptos_indexer_processor_sdk::aptos_protos::transaction::v1::{
DeleteTableData, DeleteTableItem, Event, EventKey,
};
use bigdecimal::BigDecimal;

const ALICE: &str = "0x00000000000000000000000000000000000000000000000000000000000000aa";
const BOB: &str = "0x00000000000000000000000000000000000000000000000000000000000000bb";
const CAROL: &str = "0x00000000000000000000000000000000000000000000000000000000000000cc";
const ALICE_CLAIMS: &str = "0x0000000000000000000000000000000000000000000000000000000000000ca1";
const BOB_CLAIMS: &str = "0x0000000000000000000000000000000000000000000000000000000000000cb2";

fn ts() -> chrono::NaiveDateTime {
chrono::DateTime::from_timestamp(0, 0).unwrap().naive_utc()
}

fn token_id_json(property_version: u32) -> String {
format!(
r#"{{"token_data_id":{{"creator":"0x1","collection":"col","name":"tok"}},"property_version":"{property_version}"}}"#
)
}

fn claim_event(offerer: &str, to: &str, property_version: u32) -> Event {
Event {
key: Some(EventKey {
creation_number: 0,
account_address: offerer.to_string(),
}),
sequence_number: 0,
r#type: None,
type_str: "0x3::token_transfers::Claim".to_string(),
data: format!(
r#"{{"amount":"1","account":"{offerer}","to_address":"{to}","token_id":{}}}"#,
token_id_json(property_version)
),
}
}

fn cancel_event(offerer: &str, to: &str, property_version: u32) -> Event {
Event {
key: Some(EventKey {
creation_number: 0,
account_address: offerer.to_string(),
}),
sequence_number: 0,
r#type: None,
type_str: "0x3::token_transfers::CancelOffer".to_string(),
data: format!(
r#"{{"amount":"1","account":"{offerer}","to_address":"{to}","token_id":{}}}"#,
token_id_json(property_version)
),
}
}

fn delete_offer(handle: &str, to: &str, property_version: u32) -> DeleteTableItem {
let key = format!(
r#"{{"to_addr":"{to}","token_id":{}}}"#,
token_id_json(property_version)
);
DeleteTableItem {
state_key_hash: vec![],
handle: handle.to_string(),
key: key.clone(),
data: Some(DeleteTableData {
key,
key_type: "0x3::token_transfers::TokenOfferId".to_string(),
}),
}
}

fn parse_claim_events(events: &[Event]) -> (TokenV1Claimed, TokenV1Canceled) {
let mut tokens_claimed = AHashMap::new();
let mut tokens_canceled = AHashMap::new();
let mut withdrawn = AHashMap::new();
let mut deposited = AHashMap::new();
for (i, event) in events.iter().enumerate() {
TokenActivityV2::get_v1_from_parsed_event(
event,
1,
ts(),
i as i64,
&None,
&mut tokens_claimed,
&mut tokens_canceled,
&mut withdrawn,
&mut deposited,
)
.unwrap();
}
(tokens_claimed, tokens_canceled)
}

/// Two holders of the same named token (pv0 vs a mutated pv1) offer to the
/// same recipient. The recipient claims both in one txn. PendingClaims is
/// not rewritten, so from_address comes from the claim-event map.
///
/// Keying that map only by token_data_id keeps the last event and writes
/// Alice's delete against Bob's PK (wrong-row update / silent skip of
/// Alice's pending-claim row).
#[test]
fn claim_fallback_does_not_reuse_offerer_across_property_versions() {
let (tokens_claimed, tokens_canceled) =
parse_claim_events(&[claim_event(ALICE, CAROL, 0), claim_event(BOB, CAROL, 1)]);

assert_eq!(
tokens_claimed.len(),
2,
"both property versions must be kept"
);

let alice_row = CurrentTokenPendingClaim::from_delete_table_item(
&delete_offer(ALICE_CLAIMS, CAROL, 0),
1,
ts(),
&AHashMap::new(),
&tokens_claimed,
&tokens_canceled,
)
.unwrap()
.expect("alice pv0 claim must resolve");
let bob_row = CurrentTokenPendingClaim::from_delete_table_item(
&delete_offer(BOB_CLAIMS, CAROL, 1),
1,
ts(),
&AHashMap::new(),
&tokens_claimed,
&tokens_canceled,
)
.unwrap()
.expect("bob pv1 claim must resolve");

assert_eq!(alice_row.from_address, ALICE);
assert_eq!(alice_row.to_address, CAROL);
assert_eq!(alice_row.property_version, BigDecimal::from(0));
assert_eq!(alice_row.amount, BigDecimal::zero());

assert_eq!(bob_row.from_address, BOB);
assert_eq!(bob_row.to_address, CAROL);
assert_eq!(bob_row.property_version, BigDecimal::from(1));
assert_eq!(bob_row.amount, BigDecimal::zero());

assert_ne!(
(
&alice_row.token_data_id_hash,
&alice_row.property_version,
&alice_row.from_address,
&alice_row.to_address
),
(
&bob_row.token_data_id_hash,
&bob_row.property_version,
&bob_row.from_address,
&bob_row.to_address
),
"pending-claim PKs must stay distinct"
);
}

/// Cancel of pv1 must not steal the offerer of a same-token_data_id pv0 claim.
#[test]
fn cancel_fallback_does_not_overwrite_other_property_version_claim() {
let (tokens_claimed, tokens_canceled) =
parse_claim_events(&[claim_event(ALICE, CAROL, 0), cancel_event(BOB, CAROL, 1)]);

let alice_row = CurrentTokenPendingClaim::from_delete_table_item(
&delete_offer(ALICE_CLAIMS, CAROL, 0),
1,
ts(),
&AHashMap::new(),
&tokens_claimed,
&tokens_canceled,
)
.unwrap()
.expect("alice pv0 claim must resolve");
let bob_row = CurrentTokenPendingClaim::from_delete_table_item(
&delete_offer(BOB_CLAIMS, CAROL, 1),
1,
ts(),
&AHashMap::new(),
&tokens_claimed,
&tokens_canceled,
)
.unwrap()
.expect("bob pv1 cancel must resolve");

assert_eq!(alice_row.from_address, ALICE);
assert_eq!(alice_row.property_version, BigDecimal::from(0));
assert_eq!(bob_row.from_address, BOB);
assert_eq!(bob_row.property_version, BigDecimal::from(1));
}
}
Loading
Loading