diff --git a/Monero-Effective-Ring-Size/analysis/black-marble-plots.R b/Monero-Effective-Ring-Size/analysis/black-marble-plots.R new file mode 100644 index 0000000..2c83090 --- /dev/null +++ b/Monero-Effective-Ring-Size/analysis/black-marble-plots.R @@ -0,0 +1,140 @@ +# Must install: +# install.packages(c("data.table", "ggplot2", "scales")) + +library(data.table) +library(ggplot2) +# Must also have scales package installed + + + +p2pool.upgrade <- as.Date("2023-03-18") +first.mordinal <- as.Date("2023-03-10") + + +black.marble.share <- melt(black.marble.share, id.vars = "block_date", variable.factor = FALSE) + +black.marble.share[grepl("mordinal", variable), variable := "Mordinal"] +black.marble.share[grepl("coinbase", variable), variable := "Coinbase"] + +black.marble.share <- black.marble.share[block_date >= as.Date("2023-02-01"), ] + +black.marble.share[variable == "Coinbase" & block_date < as.Date("2023-03-18"), mean(value)] +black.marble.share[variable == "Coinbase" & block_date > as.Date("2023-03-18"), mean(value)] + +png("Monero-Effective-Ring-Size/analysis/images/mordinal-coinbase-output-share.png", width = 800, height = 800) + +ggplot(black.marble.share) + + geom_line(aes(x = block_date, y = value, colour = variable), size = 1.5) + + ggtitle("Percentage of Monero Transaction Outputs That Are Mordinals and Coinbases") + + geom_vline( aes(xintercept = first.mordinal, + colour = "First Mordinal minted"), size = 1.5, linetype = 2, key_glyph = "rect") + + geom_vline( aes(xintercept = p2pool.upgrade, + colour = "P2Pool payout efficiency upgrade"), size = 1.5, linetype = 2, key_glyph = "rect") + + scale_colour_manual(name = NULL, aesthetics = c("colour", "fill"), + values = c("Mordinal" = "black", "Coinbase" = "darkgoldenrod2", "First Mordinal minted" = "khaki4", + "P2Pool payout efficiency upgrade" = "#FF6600FF")) + + xlab(" Date github.com/Rucknium") + + ylab("Share of Transaction Outputs") + + theme(plot.title = element_text(size = 20), legend.position = "top", + legend.text = element_text(size = 15), + axis.text = element_text(size = 15), + axis.title.x = element_text(size = 15, margin = margin(t = 10)), + axis.title.y = element_text(size = 15), strip.text = element_text(size = 15)) + + guides(colour = guide_legend(nrow = 2, byrow = TRUE)) + + geom_hline(aes(yintercept = 0), colour = "gray20") + + scale_y_continuous(labels = scales::percent_format(scale = 1), + breaks = seq(0, 100, by = 5), sec.axis = dup_axis(name = NULL)) + +dev.off() + + + + + +eff.ring.size.stats.mean <- eff.ring.size.stats[, .(block_timestamp_ring.time.date, + effective.ring.size.coinbase.mean, effective.ring.size.mordinal.mean, + effective.ring.size.coinbase.mordinal.mean )] + + +eff.ring.size.stats.mean <- melt(eff.ring.size.stats.mean, + id.vars = "block_timestamp_ring.time.date", variable.factor = FALSE) + +eff.ring.size.stats.mean <- eff.ring.size.stats.mean[block_timestamp_ring.time.date >= as.Date("2023-02-01"), ] + +eff.ring.size.stats.mean[grepl("coinbase.mordinal", variable), variable := "Combined Mordinal & Coinbase"] +eff.ring.size.stats.mean[grepl("mordinal", variable), variable := "Mordinal"] +eff.ring.size.stats.mean[grepl("coinbase", variable), variable := "Coinbase"] + + +png("Monero-Effective-Ring-Size/analysis/images/mean-effective-ring-size.png", width = 800, height = 800) + +ggplot(eff.ring.size.stats.mean) + + geom_line(aes(x = block_timestamp_ring.time.date , y = value, colour = variable), size = 1.5) + + ggtitle("Average Empirical Effective Ring Size of Monero Transactions") + + geom_vline( aes(xintercept = first.mordinal, + colour = "First Mordinal"), size = 1.5, linetype = 2, key_glyph = "rect") + + geom_vline( aes(xintercept = p2pool.upgrade, + colour = "P2Pool payout efficiency upgrade"), size = 1.5, linetype = 2, key_glyph = "rect") + + scale_color_manual(name = NULL, + values = c("Mordinal" = "black", "Coinbase" = "darkgoldenrod1", "Combined Mordinal & Coinbase" = "purple", + "First Mordinal" = "khaki4", "P2Pool payout efficiency upgrade" = "#FF6600FF")) + + xlab(" Date github.com/Rucknium") + + ylab("Effective ring size (ring size minus number of Mordinal/coinbase outputs in the ring)") + + theme(plot.title = element_text(size = 20), legend.position = "top", + legend.text = element_text(size = 15), + axis.text = element_text(size = 15), + axis.title.x = element_text(size = 15, margin = margin(t = 10)), + axis.title.y = element_text(size = 15), strip.text = element_text(size = 15)) + + guides(colour = guide_legend(nrow = 2, byrow = TRUE)) + + scale_y_continuous(breaks = 0:100, sec.axis = dup_axis(name = NULL)) +# Have an axis tick at every integer + +dev.off() + + + + + +eff.ring.size.stats.5th.percentile <- eff.ring.size.stats[, .(block_timestamp_ring.time.date, + effective.ring.size.coinbase.percentile.05, effective.ring.size.mordinal.percentile.05, + effective.ring.size.coinbase.mordinal.percentile.05 )] + + + +eff.ring.size.stats.5th.percentile <- melt(eff.ring.size.stats.5th.percentile, + id.vars = "block_timestamp_ring.time.date", variable.factor = FALSE) + +eff.ring.size.stats.5th.percentile <- eff.ring.size.stats.5th.percentile[ + block_timestamp_ring.time.date >= as.Date("2023-02-01"), ] + +eff.ring.size.stats.5th.percentile[grepl("coinbase.mordinal", variable), variable := "Combined Mordinal & Coinbase"] +eff.ring.size.stats.5th.percentile[grepl("mordinal", variable), variable := "Mordinal"] +eff.ring.size.stats.5th.percentile[grepl("coinbase", variable), variable := "Coinbase"] + + +png("Monero-Effective-Ring-Size/analysis/images/5th-percentile-effective-ring-size.png", width = 800, height = 800) + +ggplot(eff.ring.size.stats.5th.percentile) + + geom_line(aes(x = block_timestamp_ring.time.date , y = value, colour = variable), size = 1.5) + + ggtitle('Empirical Effective Ring Size for the "Unluckiest" 5 Percent of Monero Rings\n(i.e. 5th Percentile of Empirical Effective Ring Size)') + + geom_vline( aes(xintercept = first.mordinal, + colour = "First Mordinal"), size = 1.5, linetype = 2, key_glyph = "rect") + + geom_vline( aes(xintercept = p2pool.upgrade, + colour = "P2Pool payout efficiency upgrade"), size = 1.5, linetype = 2, key_glyph = "rect") + + scale_color_manual(name = NULL, + values = c("Mordinal" = "black", "Coinbase" = "darkgoldenrod1", "Combined Mordinal & Coinbase" = "purple", + "First Mordinal" = "khaki4", "P2Pool payout efficiency upgrade" = "#FF6600FF")) + + xlab(" Date github.com/Rucknium") + + ylab("Effective ring size (ring size minus number of Mordinal/coinbase outputs in the ring)") + + theme(plot.title = element_text(size = 20), legend.position = "top", + legend.text = element_text(size = 15), + axis.text = element_text(size = 15), + axis.title.x = element_text(size = 15, margin = margin(t = 10)), + axis.title.y = element_text(size = 15), strip.text = element_text(size = 15)) + + guides(colour = guide_legend(nrow = 2, byrow = TRUE)) + + scale_y_continuous(breaks = 0:100, sec.axis = dup_axis(name = NULL)) +# Have an axis tick at every integer + +dev.off() + + diff --git a/Monero-Effective-Ring-Size/black-marble-eff-ring-size.R b/Monero-Effective-Ring-Size/black-marble-eff-ring-size.R new file mode 100644 index 0000000..6ee333d --- /dev/null +++ b/Monero-Effective-Ring-Size/black-marble-eff-ring-size.R @@ -0,0 +1,61 @@ +# Must install: +# install.packages("data.table") + +library(data.table) + + +black.marble.share <- output.index[, .( + coinbase.share = 100 * sum(tx_num == 1)/.N, + mordinal.share = 100 * sum(is_mordinal)/.N), by = "block_date"] + +setorder(black.marble.share, block_date) + + +# Get number of Mordinals, fees paid, and tx size +output.index[is_mordinal & output_num == 1, .N] + +output.index[is_mordinal & output_num == 1, sum(tx_fee) * 0.000000000001] + +output.index[is_mordinal & output_num == 1, sum(tx_size_bytes)] + + + +eff.ring.size <- xmr.rings[, .( + n.coinbase.ring.members = sum(tx_num == 1), n.mordinal.ring.members = sum(is_mordinal), ring.size = .N), + by = c("tx_hash", "input_num", "block_timestamp_ring")] + +eff.ring.size[, share.mordinal.ring.members := n.mordinal.ring.members/ring.size] +eff.ring.size[, effective.ring.size.coinbase := ring.size - n.coinbase.ring.members] +eff.ring.size[, effective.ring.size.mordinal := ring.size - n.mordinal.ring.members] +eff.ring.size[, effective.ring.size.coinbase.mordinal := ring.size - n.coinbase.ring.members - n.mordinal.ring.members] +eff.ring.size[, block_timestamp_ring.time := as.POSIXct(block_timestamp_ring, origin = "1970-01-01")] + +eff.ring.size.date <- unique(eff.ring.size[, .(block_timestamp_ring.time = block_timestamp_ring.time)]) + +eff.ring.size.date[, block_timestamp_ring.time.isoweek := + paste(lubridate::isoyear(block_timestamp_ring.time), + formatC(lubridate::isoweek(block_timestamp_ring.time), width = 2, flag = "0"), sep = "-")] + +eff.ring.size.date[, block_timestamp_ring.time.date := as.Date(block_timestamp_ring.time)] + +eff.ring.size <- merge(eff.ring.size, eff.ring.size.date) +# speed improvement by splitting and then merging + +setorder(eff.ring.size, block_timestamp_ring.time.date) + + +eff.ring.size.stats <- eff.ring.size[, .( + effective.ring.size.coinbase.mean = as.numeric(mean(effective.ring.size.coinbase)), + effective.ring.size.coinbase.median = as.numeric(median(effective.ring.size.coinbase)), + effective.ring.size.coinbase.percentile.05 = as.numeric(quantile(effective.ring.size.coinbase, probs = 0.05)), + effective.ring.size.mordinal.mean = as.numeric(mean(effective.ring.size.mordinal)), + effective.ring.size.mordinal.median = as.numeric(median(effective.ring.size.mordinal)), + effective.ring.size.mordinal.percentile.05 = as.numeric(quantile(effective.ring.size.mordinal, probs = 0.05)), + effective.ring.size.coinbase.mordinal.mean = as.numeric(mean(effective.ring.size.coinbase.mordinal)), + effective.ring.size.coinbase.mordinal.median = as.numeric(median(effective.ring.size.coinbase.mordinal)), + effective.ring.size.coinbase.mordinal.percentile.05 = as.numeric(quantile(effective.ring.size.coinbase.mordinal, probs = 0.05)) + # as.numeric() to make sure results of each "by" subset are all floats +), by = "block_timestamp_ring.time.date"] + + +setorder(eff.ring.size.stats, block_timestamp_ring.time.date)