From fcf55077367ae1fd0c350cfe7fd4676b1c10378d Mon Sep 17 00:00:00 2001 From: Rucknium Date: Fri, 29 Apr 2022 20:44:29 +0000 Subject: [PATCH] Many updates; getting close to final version --- .../aggregate-spent-status-data.R | 201 +++++++++++--- Pre-fork-BCH-BTC-Spending/create-graphs.R | 251 ++++++++++++++++-- Pre-fork-BCH-BTC-Spending/get-block-times.R | 6 +- 3 files changed, 404 insertions(+), 54 deletions(-) diff --git a/Pre-fork-BCH-BTC-Spending/aggregate-spent-status-data.R b/Pre-fork-BCH-BTC-Spending/aggregate-spent-status-data.R index bbe8966..28ada3b 100644 --- a/Pre-fork-BCH-BTC-Spending/aggregate-spent-status-data.R +++ b/Pre-fork-BCH-BTC-Spending/aggregate-spent-status-data.R @@ -2,10 +2,13 @@ library(data.table) library(RSQLite) library(DBI) -library(scales) # NOTE: Also need lubridate package installed, but not loading it due to # it masking functions +# WARNING: This code assumes that pre-fork bitcoin has been spent every day since the fork, +# which is true up to March 31, 2022. If this code runs on later data, then will +# have to pre-fill data.frames with dates + bch.data.dir <- "" btc.data.dir <- "" # Input data directory here, with trailing "/" @@ -22,6 +25,9 @@ pre.fork.utxo.set <- setdiff(pre.fork.edgelist$destination_index, pre.fork.edgel DBI::dbWriteTable(con.bch, "pre_fork_utxo_set", data.frame(destination_index = pre.fork.utxo.set, stringsAsFactors = FALSE)) +DBI::dbWriteTable(con.btc, "pre_fork_utxo_set", + data.frame(destination_index = pre.fork.utxo.set, stringsAsFactors = FALSE)) +# Need to do this operation for both the BCH and BTC databases pre.fork.utxo.set.value <- DBI::dbGetQuery(con.bch, 'SELECT destination_index, value FROM edgelist_intermediate_2 WHERE destination_index IN (SELECT destination_index FROM pre_fork_utxo_set)') @@ -32,7 +38,10 @@ pre.fork.utxo.set.value[, sum(value)] / pre.fork.bitcoin.supply pre.fork.bitcoin.supply - pre.fork.utxo.set.value[, sum(value)] # [1] 2637.559 -pre.fork.utxo.set.value <- pre.fork.utxo.set.value[ ! destination_index %in% c(1740174960, 1740175469), ] +duplicated.destination_index <- + unlist(pre.fork.utxo.set.value[duplicated(destination_index), .(destination_index)]) + +pre.fork.utxo.set.value <- pre.fork.utxo.set.value[ ! destination_index %in% duplicated.destination_index, ] # Removes the transactions that are coinbases of blocks 91722, 91812, 91842, 91880 # Since they are duplicated transaction hashes. See: # https://bitcoin.stackexchange.com/questions/40444/what-happens-when-two-txids-collide @@ -42,63 +51,187 @@ excluded.duplicate.tx.hashes.output.count <- 4 excluded.duplicate.tx.hashes.value <- 50 * 4 -spent.status <- DBI::dbGetQuery(con.bch, - 'SELECT origin_index, block_height FROM edgelist_intermediate_2 WHERE origin_index IN (SELECT destination_index FROM pre_fork_utxo_set)') -colnames(spent.status) <- c("destination_index", "bch.spent.block_height") -setDT(spent.status) +bch.spent.status <- DBI::dbGetQuery(con.bch, + 'SELECT origin_index, block_height FROM edgelist_intermediate_1 WHERE origin_index IN (SELECT destination_index FROM pre_fork_utxo_set)') +colnames(bch.spent.status) <- c("destination_index", "bch.spent.block_height") +setDT(bch.spent.status) -spent.status <- merge(pre.fork.utxo.set.value, spent.status, all.x = TRUE) -# rm(pre.fork.utxo.set.value) +btc.spent.status <- DBI::dbGetQuery(con.btc, + 'SELECT origin_index, block_height FROM edgelist_intermediate_1 WHERE origin_index IN (SELECT destination_index FROM pre_fork_utxo_set)') +colnames(btc.spent.status) <- c("destination_index", "btc.spent.block_height") +setDT(btc.spent.status) + + +spent.status <- merge(pre.fork.utxo.set.value, bch.spent.status, all.x = TRUE) +rm(pre.fork.utxo.set.value, bch.spent.status) +spent.status <- merge(spent.status, btc.spent.status, all.x = TRUE) +rm(btc.spent.status) -# aggregate(spent.status$value, by = list(! is.na(spent.status$bch.spent.block_height)), FUN = sum) -# Group.1 x -# 1 FALSE 5699742 -# 2 TRUE 10779508 -# table(spent.status[value > 0, ! is.na(bch.spent.block_height)]) -# FALSE TRUE -# 29154762 21307064 bch.block.times <- readRDS(paste0(bch.data.dir, "block_times.rds")) bch.block.times[, block_time := as.POSIXct(block_time, origin = "1970-01-01", tz = "GMT")] -colnames(bch.block.times) <- c("bch.spent.block_height", "block_time") +colnames(bch.block.times) <- c("bch.spent.block_height", "bch.block_time") spent.status <- merge(spent.status, bch.block.times, all = TRUE, by = "bch.spent.block_height") # Note that due to all = TRUE this will get all blocks, # even if there are no target spent outputs within the block -spent.status.by.block <- spent.status[, - .(value = sum(value, na.rm= TRUE), n.outputs = length(destination_index[ (! is.na(value)) & value > 0])), - by = .(bch.spent.block_height, block_time)] -spent.status.by.block[, block_time.date := lubridate::date(block_time)] +btc.block.times <- readRDS(paste0(btc.data.dir, "block_times.rds")) -spent.status.by.date <- spent.status.by.block[, - .(value = sum(value, na.rm= TRUE), n.outputs = sum(n.outputs, na.rm= TRUE)), - by = .(block_time.date)] +btc.block.times[, block_time := as.POSIXct(block_time, origin = "1970-01-01", tz = "GMT")] +colnames(btc.block.times) <- c("btc.spent.block_height", "btc.block_time") -unspent <- spent.status.by.date[is.na(block_time.date), value] -cumsum.na.rm <- function(x) {x[is.na(x)] <- 0; cumsum(x)} -spent.status.by.date[, value.cumsum := cumsum.na.rm(value) - unspent] - -spent.status.by.date[, perc.value.cumsum := 100 * value.cumsum / sum(value, na.rm = TRUE)] -spent.status.by.date[, unspent.perc.value.cumsum := 100 - perc.value.cumsum] +spent.status <- merge(spent.status, btc.block.times, all = TRUE, by = "btc.spent.block_height") +write.csv(spent.status, file = paste0(bch.data.dir, "spent_status-test.csv"), row.names = FALSE) -btc.spent.status <- DBI::dbGetQuery(con.btc, - 'SELECT origin_index, block_height FROM edgelist_intermediate_2 WHERE origin_index IN (SELECT destination_index FROM pre_fork_utxo_set)') -colnames(btc.spent.status) <- c("destination_index, btc.spent.block_height") -setDT(btc.spent.status) -spent.status <- merge(spent.status, btc.spent.status, all.x = TRUE) +spent.status[, bch.block_time.date := lubridate::date(bch.block_time)] +spent.status[, btc.block_time.date := lubridate::date(btc.block_time)] +# Column format below is: +# {BTC spent status}{BCH spent status}.to.{BTC spent status}{BCH spent status} +# u = unspent; s = spent + +spent.status[, uu.to.su := as.Date(ifelse( + ifelse(is.na(btc.block_time.date), Inf, btc.block_time.date) < ifelse(is.na(bch.block_time.date), Inf, bch.block_time.date), + btc.block_time.date, rep(NA, .N)), origin = "1970-01-01")] + +spent.status[, uu.to.us := as.Date(ifelse( + ifelse(is.na(bch.block_time.date), Inf, bch.block_time.date) < ifelse(is.na(btc.block_time.date), Inf, btc.block_time.date), + bch.block_time.date, rep(NA, .N)), origin = "1970-01-01")] + +spent.status[, uu.to.ss := as.Date(ifelse( + ifelse(is.na(btc.block_time.date), Inf, btc.block_time.date) == ifelse(is.na(bch.block_time.date), Inf, bch.block_time.date), + btc.block_time.date, rep(NA, .N)), origin = "1970-01-01")] + +spent.status[, su.to.ss := as.Date(ifelse( + (! is.na(uu.to.su)) & + ifelse(is.na(bch.block_time.date), Inf, bch.block_time.date) > ifelse(is.na(btc.block_time.date), Inf, btc.block_time.date), + bch.block_time.date, rep(NA, .N)), origin = "1970-01-01")] + +spent.status[, us.to.ss := as.Date(ifelse( + (! is.na(uu.to.us)) & + ifelse(is.na(btc.block_time.date), Inf, btc.block_time.date) > ifelse(is.na(bch.block_time.date), Inf, bch.block_time.date), + btc.block_time.date, rep(NA, .N)), origin = "1970-01-01")] + + +uu.to.su <- spent.status[ (! is.na(uu.to.su)), + .(value.uu.to.su = sum(value, na.rm = TRUE), outputs.uu.to.su = .N), by = uu.to.su] +names(uu.to.su)[1] <- "block_time.date" + +uu.to.us <- spent.status[ (! is.na(uu.to.us)), + .(value.uu.to.us = sum(value, na.rm = TRUE), outputs.uu.to.us = .N), by = uu.to.us] +names(uu.to.us)[1] <- "block_time.date" + +uu.to.ss <- spent.status[ (! is.na(uu.to.ss)), + .(value.uu.to.ss = sum(value, na.rm = TRUE), outputs.uu.to.ss = .N), by = uu.to.ss] +names(uu.to.ss)[1] <- "block_time.date" + +su.to.ss <- spent.status[ (! is.na(su.to.ss)), + .(value.su.to.ss = sum(value, na.rm = TRUE), outputs.su.to.ss = .N), by = su.to.ss] +names(su.to.ss)[1] <- "block_time.date" + +us.to.ss <- spent.status[ (! is.na(us.to.ss)), + .(value.us.to.ss = sum(value, na.rm = TRUE), outputs.us.to.ss = .N), by = us.to.ss] +names(us.to.ss)[1] <- "block_time.date" + + +trans.matrix.prep <- + data.table(block_time.date = sort(unique(lubridate::date(c(spent.status$bch.block_time, spent.status$btc.block_time))))) + +trans.matrix.prep <- merge(trans.matrix.prep, uu.to.su, all = TRUE) +trans.matrix.prep <- merge(trans.matrix.prep, uu.to.us, all = TRUE) +trans.matrix.prep <- merge(trans.matrix.prep, uu.to.ss, all = TRUE) +trans.matrix.prep <- merge(trans.matrix.prep, su.to.ss, all = TRUE) +trans.matrix.prep <- merge(trans.matrix.prep, us.to.ss, all = TRUE) + + +trans.matrix.prep[is.na(trans.matrix.prep)] <- 0 + + + +spent.status.by.day <- + data.table(block_time.date = sort(unique(lubridate::date(c(spent.status$bch.block_time, spent.status$btc.block_time)))), + value.btc.unspent.bch.unspent = NA_real_, + outputs.btc.unspent.bch.unspent = NA_integer_, + value.btc.spent.bch.unspent = NA_real_, + outputs.btc.spent.bch.unspent = NA_integer_, + value.btc.unspent.bch.spent = NA_real_, + outputs.btc.unspent.bch.spent = NA_integer_, + value.btc.spent.bch.spent = NA_real_, + outputs.btc.spent.bch.spent = NA_integer_) + +for (day.i in spent.status.by.day$block_time.date) { + + spent.status[, btc.spent := (btc.block_time.date <= day.i) & (! is.na(btc.block_time.date))] + spent.status[, bch.spent := (bch.block_time.date <= day.i) & (! is.na(bch.block_time.date))] + + spent.status.by.day$value.btc.unspent.bch.unspent[spent.status.by.day$block_time.date == day.i] <- + spent.status[ (! btc.spent) & (! bch.spent), sum(value, na.rm = TRUE)] + + spent.status.by.day$outputs.btc.unspent.bch.unspent[spent.status.by.day$block_time.date == day.i] <- + spent.status[ (! btc.spent) & (! bch.spent), .N] + + + spent.status.by.day$value.btc.spent.bch.unspent[spent.status.by.day$block_time.date == day.i] <- + spent.status[ ( btc.spent) & (! bch.spent), sum(value, na.rm = TRUE)] + + spent.status.by.day$outputs.btc.spent.bch.unspent[spent.status.by.day$block_time.date == day.i] <- + spent.status[ ( btc.spent) & (! bch.spent), .N] + + + spent.status.by.day$value.btc.unspent.bch.spent[spent.status.by.day$block_time.date == day.i] <- + spent.status[ (! btc.spent) & ( bch.spent), sum(value, na.rm = TRUE)] + + spent.status.by.day$outputs.btc.unspent.bch.spent[spent.status.by.day$block_time.date == day.i] <- + spent.status[ (! btc.spent) & ( bch.spent), .N] + + + spent.status.by.day$value.btc.spent.bch.spent[spent.status.by.day$block_time.date == day.i] <- + spent.status[ ( btc.spent) & ( bch.spent), sum(value, na.rm = TRUE)] + + spent.status.by.day$outputs.btc.spent.bch.spent[spent.status.by.day$block_time.date == day.i] <- + spent.status[ ( btc.spent) & ( bch.spent), .N] + + cat(base::date(), which(spent.status.by.day$block_time.date == day.i), + "of", nrow(spent.status.by.day),"\n") + +} +# Fairly inefficient implementation, but gets the job done + + + +## Data validity check below + +value.row.sum.check <- rowSums(spent.status.by.day[, .( + value.btc.unspent.bch.unspent, value.btc.spent.bch.unspent, + value.btc.unspent.bch.spent, value.btc.spent.bch.spent) +]) + +stopifnot(max(value.row.sum.check) - min(value.row.sum.check) < 0.000001) +# Some small error allowed for floating point arithmetic inaccuracy + +outputs.row.sum.check <- rowSums(spent.status.by.day[, .( + outputs.btc.unspent.bch.unspent, outputs.btc.spent.bch.unspent, + outputs.btc.unspent.bch.spent, outputs.btc.spent.bch.spent) +]) + +stopifnot(max(outputs.row.sum.check) - min(outputs.row.sum.check) == 0) + + +saveRDS(spent.status.by.day, file = paste0(bch.data.dir, "spent_status_by_day.rds")) +saveRDS(trans.matrix.prep, file = paste0(bch.data.dir, "trans_matrix_prep.rds")) + diff --git a/Pre-fork-BCH-BTC-Spending/create-graphs.R b/Pre-fork-BCH-BTC-Spending/create-graphs.R index 11ded2f..4221e70 100644 --- a/Pre-fork-BCH-BTC-Spending/create-graphs.R +++ b/Pre-fork-BCH-BTC-Spending/create-graphs.R @@ -1,14 +1,107 @@ + library(data.table) library(ggplot2) library(scales) +library(Cairo) # NOTE: Also need lubridate package installed, but not loading it due to # it masking functions -spent.status.by.date <- spent.status.by.date[ ! is.na(block_time.date), ] -spent.status.by.date.reshaped <- melt(spent.status.by.date, id.vars = c("block_time.date"), - measure.vars = c("perc.value.cumsum", "unspent.perc.value.cumsum")) +sum.value.pre.fork <- sum(spent.status.by.day[1, + .(value.btc.unspent.bch.unspent, value.btc.spent.bch.unspent, + value.btc.unspent.bch.spent, value.btc.spent.bch.spent)]) + +sum.outputs.pre.fork <- sum(spent.status.by.day[1, + .(outputs.btc.unspent.bch.unspent, outputs.btc.spent.bch.unspent, + outputs.btc.unspent.bch.spent, outputs.btc.spent.bch.spent)]) + + +current.status <- spent.status.by.day[block_time.date == "2022-03-31", ] + +current.status[, + .(value.btc.unspent.bch.unspent, value.btc.spent.bch.unspent, + value.btc.unspent.bch.spent, value.btc.spent.bch.spent)] + +100 * current.status[, + .(value.btc.unspent.bch.unspent, value.btc.spent.bch.unspent, + value.btc.unspent.bch.spent, value.btc.spent.bch.spent)] / sum.value.pre.fork + + +current.status[, + .(outputs.btc.unspent.bch.unspent, outputs.btc.spent.bch.unspent, + outputs.btc.unspent.bch.spent, outputs.btc.spent.bch.spent)] + +100 * current.status[, + .(outputs.btc.unspent.bch.unspent, outputs.btc.spent.bch.unspent, + outputs.btc.unspent.bch.spent, outputs.btc.spent.bch.spent)] / sum.outputs.pre.fork + + + + + + +spent.status.by.day.value.reshaped <- melt(spent.status.by.day[, + .(block_time.date, value.btc.unspent.bch.unspent, value.btc.spent.bch.unspent, + value.btc.unspent.bch.spent, value.btc.spent.bch.spent)], id.vars = c("block_time.date"), + measure.vars = c("value.btc.unspent.bch.unspent", "value.btc.spent.bch.unspent", + "value.btc.unspent.bch.spent", "value.btc.spent.bch.spent")) + + +spent.status.by.day.value.reshaped[, block_time.date := as.POSIXct(block_time.date)] +spent.status.by.day.value.reshaped[, variable := + factor(variable, levels = c("value.btc.unspent.bch.unspent", "value.btc.unspent.bch.spent", + "value.btc.spent.bch.unspent", "value.btc.spent.bch.spent"))] + + +spent.status.by.day.outputs.reshaped <- melt(spent.status.by.day[, + .(block_time.date, outputs.btc.unspent.bch.unspent, outputs.btc.spent.bch.unspent, + outputs.btc.unspent.bch.spent, outputs.btc.spent.bch.spent)], id.vars = c("block_time.date"), + measure.vars = c("outputs.btc.unspent.bch.unspent", "outputs.btc.spent.bch.unspent", + "outputs.btc.unspent.bch.spent", "outputs.btc.spent.bch.spent")) + + +spent.status.by.day.outputs.reshaped[, block_time.date := as.POSIXct(block_time.date)] +spent.status.by.day.outputs.reshaped[, variable := + factor(variable, levels = c("outputs.btc.unspent.bch.unspent", "outputs.btc.spent.bch.unspent", + "outputs.btc.unspent.bch.spent", "outputs.btc.spent.bch.spent"))] + + + + +trans.matrix.prep.value.reshaped <- melt(trans.matrix.prep[, + .(block_time.date, value.uu.to.su, value.uu.to.us, value.uu.to.ss, + value.su.to.ss, value.us.to.ss)], id.vars = c("block_time.date"), + measure.vars = c("value.uu.to.su", "value.uu.to.us", "value.uu.to.ss", + "value.su.to.ss", "value.us.to.ss")) + + +trans.matrix.prep.value.reshaped[, block_time.date := as.POSIXct(block_time.date)] +trans.matrix.prep.value.reshaped[, variable := + factor(variable, levels = c("value.uu.to.su", "value.uu.to.us", "value.uu.to.ss", + "value.su.to.ss", "value.us.to.ss"))] + + + + + +trans.matrix.prep.outputs.reshaped <- melt(trans.matrix.prep[, + .(block_time.date, outputs.uu.to.su, outputs.uu.to.us, outputs.uu.to.ss, + outputs.su.to.ss, outputs.us.to.ss)], id.vars = c("block_time.date"), + measure.vars = c("outputs.uu.to.su", "outputs.uu.to.us", "outputs.uu.to.ss", + "outputs.su.to.ss", "outputs.us.to.ss")) + + +trans.matrix.prep.outputs.reshaped[, block_time.date := as.POSIXct(block_time.date)] +trans.matrix.prep.outputs.reshaped[, variable := + factor(variable, levels = c("outputs.uu.to.su", "outputs.uu.to.us", "outputs.uu.to.ss", + "outputs.su.to.ss", "outputs.us.to.ss"))] + + + +date.breaks <- seq.POSIXt(min(spent.status.by.day.value.reshaped$block_time.date), + max(spent.status.by.day.value.reshaped$block_time.date), by = "month") + c_trans <- function(a, b, breaks = b$breaks, format = b$format) { @@ -26,36 +119,160 @@ c_trans <- function(a, b, breaks = b$breaks, format = b$format) { rev_date <- c_trans("reverse", "time") -spent.status.by.date.reshaped[, block_time.date := as.POSIXct(block_time.date)] -spent.status.by.date.reshaped[, variable := - factor(variable, levels = c("perc.value.cumsum", "unspent.perc.value.cumsum"))] - # #FF9900 BTC color # https://gist.github.com/paladini/ef383fce1b782d919898 # #0AC18E BCH color # https://bitcoincashstandards.org/ -png(paste0(bch.data.dir, "preliminary-pre-fork-BCH-spent-status.png"), width = 800, height = 2000) +png(paste0(bch.data.dir, "preliminary-pre-fork-BTC-BCH-spent-status-by-value.png"), width = 800, height = 2000) print( - ggplot(spent.status.by.date.reshaped, aes(x = block_time.date, y=value, fill=variable)) + + ggplot(spent.status.by.day.value.reshaped, aes(x = block_time.date, y = value, fill = variable)) + + ggtitle("Spent status of Pre-fork BTC and BCH by Bitcoin Value") + geom_area(alpha = 0.6 , size = 0, colour = "black") + coord_flip() + - scale_x_continuous(trans = rev_date) + - scale_fill_manual(values = c("#0AC18E", "purple"), breaks = rev(c("perc.value.cumsum", "unspent.perc.value.cumsum"))) + - ylab("\t\t\t\t\t\tPercent github.com/Rucknium") + + scale_x_continuous(trans = rev_date, breaks = date.breaks, labels = date_format("%b-%Y"), expand = c(0, 0)) + + scale_y_continuous(breaks = sum.value.pre.fork * seq(0, 1, by = 0.1), + labels = scales::percent_format(scale = 100 * 1/sum.value.pre.fork, accuracy = 1), expand = c(0, 0)) + + scale_fill_manual( + labels = c("BTC & BCH spent", "BTC spent & BCH unspent", "BTC unspent & BCH spent", "BTC & BCH unspent"), + values = c("purple", "#FF9900", "#0AC18E", "darkgrey"), + breaks = c("value.btc.spent.bch.spent", "value.btc.spent.bch.unspent", + "value.btc.unspent.bch.spent", "value.btc.unspent.bch.unspent")) + + ylab("Percentage of Pre-fork Bitcoin Value") + theme(legend.position = "top", axis.title.y = element_blank(), + plot.title = element_text(size = 27), axis.text = element_text(size = 20), axis.title.x = element_text(size = 20), - legend.title = element_blank(), legend.text = element_text(size = 15)) + + legend.title = element_blank(), legend.text = element_text(size = 14)) + geom_vline(xintercept = as.POSIXct("2017-11-12"), linetype = 3) + - geom_text(aes(x = as.POSIXct("2017-11-12"), label = "Max BCH/BTC Exchange Rate", y = 25), colour = "white", size = 7.5) + + geom_text(aes(x = as.POSIXct("2017-11-12"), label = "Max BCH/BTC Exchange Rate", + y = 0.75 * sum.value.pre.fork), colour = "black", size = 6, check_overlap = TRUE) + geom_vline(xintercept = as.POSIXct("2017-12-20"), linetype = 3) + - geom_text(aes(x = as.POSIXct("2017-12-20"), label = "Max BCH/USD Exchange Rate", y = 25), colour = "white", size = 7.5) + + geom_text(aes(x = as.POSIXct("2017-12-20"), label = "Max BCH/USD Exchange Rate", + y = 0.75 * sum.value.pre.fork), colour = "black", size = 6, check_overlap = TRUE) + geom_vline(xintercept = as.POSIXct("2018-11-15"), linetype = 3) + - geom_text(aes(x = as.POSIXct("2018-11-15"), label = "BSV Hard Fork", y = 12), colour = "white", size = 7.5) + + geom_text(aes(x = as.POSIXct("2018-11-15"), label = "BSV Hard Fork", + y = 0.25 * sum.value.pre.fork), colour = "black", size = 6, check_overlap = TRUE) + geom_vline(xintercept = as.POSIXct("2020-11-15"), linetype = 3) + - geom_text(aes(x = as.POSIXct("2020-11-15"), label = "BCHABC Hard Fork", y = 15), colour = "white", size = 7.5) + geom_text(aes(x = as.POSIXct("2020-11-15"), label = "BCHABC Hard Fork", + y = 0.25 * sum.value.pre.fork), colour = "black", size = 6, check_overlap = TRUE) + + geom_text(aes(x = as.POSIXct("2022-03-15"), label = "github.com/Rucknium", + y = 0.87 * sum.value.pre.fork), colour = "black", size = 6, check_overlap = TRUE) ) # https://en.wikipedia.org/wiki/List_of_bitcoin_forks +# expand = c(0, 0) due to: +# https://stackoverflow.com/questions/48611719/remove-inner-padding-in-ggplot dev.off() + +png(paste0(bch.data.dir, "preliminary-pre-fork-BTC-BCH-spent-status-by-outputs.png"), width = 800, height = 2000) + +print( + ggplot(spent.status.by.day.outputs.reshaped, aes(x = block_time.date, y = value, fill = variable)) + + ggtitle("Spent status of Pre-fork BTC and BCH by Number of Outputs") + + geom_area(alpha = 0.6 , size = 0, colour = "black") + coord_flip() + + scale_x_continuous(trans = rev_date, breaks = date.breaks, labels = date_format("%b-%Y"), expand = c(0, 0)) + + scale_y_continuous(breaks = sum.outputs.pre.fork * seq(0, 1, by = 0.1), + labels = scales::percent_format(scale = 100 * 1/sum.outputs.pre.fork, accuracy = 1), expand = c(0, 0)) + + scale_fill_manual( + labels = c("BTC & BCH spent", "BTC spent & BCH unspent", "BTC unspent & BCH spent", "BTC & BCH unspent"), + values = c("purple", "#FF9900", "#0AC18E", "darkgrey"), + breaks = c("outputs.btc.spent.bch.spent", "outputs.btc.spent.bch.unspent", + "outputs.btc.unspent.bch.spent", "outputs.btc.unspent.bch.unspent")) + + ylab("Percentage of Pre-fork Outputs") + + theme(legend.position = "top", axis.title.y = element_blank(), + plot.title = element_text(size = 26), + axis.text = element_text(size = 20), axis.title.x = element_text(size = 20), + legend.title = element_blank(), legend.text = element_text(size = 14)) + + geom_vline(xintercept = as.POSIXct("2017-11-12"), linetype = 3) + + geom_text(aes(x = as.POSIXct("2017-11-12"), label = "Max BCH/BTC Exchange Rate", + y = 0.75 * sum.outputs.pre.fork), colour = "black", size = 6, check_overlap = TRUE) + + geom_vline(xintercept = as.POSIXct("2017-12-20"), linetype = 3) + + geom_text(aes(x = as.POSIXct("2017-12-20"), label = "Max BCH/USD Exchange Rate", + y = 0.75 * sum.outputs.pre.fork), colour = "black", size = 6, check_overlap = TRUE) + + geom_vline(xintercept = as.POSIXct("2018-11-15"), linetype = 3) + + geom_text(aes(x = as.POSIXct("2018-11-15"), label = "BSV Hard Fork", + y = 0.10 * sum.outputs.pre.fork), colour = "black", size = 6, check_overlap = TRUE) + + geom_vline(xintercept = as.POSIXct("2020-11-15"), linetype = 3) + + geom_text(aes(x = as.POSIXct("2020-11-15"), label = "BCHABC Hard Fork", + y = 0.15 * sum.outputs.pre.fork), colour = "black", size = 6, check_overlap = TRUE) + + geom_text(aes(x = as.POSIXct("2022-03-15"), label = "github.com/Rucknium", + y = 0.87 * sum.outputs.pre.fork), colour = "black", size = 6, check_overlap = TRUE) +) +dev.off() + + + +ann_text.value <- data.frame( + block_time.date = as.POSIXct("2022-03-15"), + value = 300000, + variable = factor("value.us.to.ss", levels = levels(trans.matrix.prep.value.reshaped$variable))) +# Due to +# https://stackoverflow.com/questions/11889625/annotating-text-on-individual-facet-in-ggplot2 + + +png(paste0(bch.data.dir, "preliminary-pre-fork-BTC-BCH-trans-matrix-by-value.png"), width = 800, height = 2000) + +print( + ggplot(trans.matrix.prep.value.reshaped, aes(x = block_time.date, y = value, fill = variable)) + + ggtitle("State Transition of Pre-fork BTC and BCH by Bitcoin Value\nKEY: {BTC Spent}{BCH Spent} to {BTC Spent}{BCH Spent}") + + geom_line(aes(color = variable)) + coord_flip() + + scale_x_continuous(trans = rev_date, labels = date_format("%b-%Y"), expand = c(0, 0), + breaks = date.breaks) + + scale_y_continuous(labels = scales::comma) + + facet_grid(. ~ variable, labeller = labeller(variable = + c(value.uu.to.su = "FF to TF", value.uu.to.us = "FF to FT", value.uu.to.ss = "FF to TT", + value.su.to.ss = "TF to TT", value.us.to.ss = "FT to TT" ))) + + ylab("Quantity of Bitcoin Value Transitioned per Day") + + theme(legend.position = "none", axis.title.y = element_blank(), + strip.text.x = element_text(size = 20), plot.title = element_text(size = 24), + axis.text = element_text(size = 15), axis.title.x = element_text(size = 15), + axis.text.x = element_text(angle = 270)) + + geom_vline(xintercept = as.POSIXct("2017-11-12"), linetype = 3) + + geom_vline(xintercept = as.POSIXct("2017-12-20"), linetype = 3) + + geom_vline(xintercept = as.POSIXct("2018-11-15"), linetype = 3) + + geom_vline(xintercept = as.POSIXct("2020-11-15"), linetype = 3) + + geom_text(data = ann_text.value, label = "github.com/Rucknium", + colour = "black", size = 4.5, check_overlap = TRUE) +) +dev.off() + + + +ann_text.output <- data.frame( + block_time.date = as.POSIXct("2022-03-15"), + value = 280000, + variable = factor("outputs.us.to.ss", levels = levels(trans.matrix.prep.outputs.reshaped$variable))) + +png(paste0(bch.data.dir, "preliminary-pre-fork-BTC-BCH-trans-matrix-by-outputs.png"), width = 800, height = 2000) + +print( + ggplot(trans.matrix.prep.outputs.reshaped, aes(x = block_time.date, y = value, fill = variable)) + + ggtitle("State Transition of Pre-fork BTC and BCH by Number of Outputs\nKEY: {BTC Spent}{BCH Spent} to {BTC Spent}{BCH Spent}") + + geom_line(aes(color = variable)) + coord_flip() + + scale_x_continuous(trans = rev_date, labels = date_format("%b-%Y"), expand = c(0, 0), + breaks = date.breaks) + + scale_y_continuous(labels = scales::comma) + + facet_grid(. ~ variable, labeller = labeller(variable = + c(outputs.uu.to.su = "FF to TF", outputs.uu.to.us = "FF to FT", outputs.uu.to.ss = "FF to TT", + outputs.su.to.ss = "TF to TT", outputs.us.to.ss = "FT to TT" ))) + + ylab("Number of Transitioned Outputs per Day") + + theme(legend.position = "none", axis.title.y = element_blank(), + strip.text.x = element_text(size = 20), plot.title = element_text(size = 24), + axis.text = element_text(size = 15), axis.title.x = element_text(size = 15), + axis.text.x = element_text(angle = 270)) + + geom_vline(xintercept = as.POSIXct("2017-11-12"), linetype = 3) + + geom_vline(xintercept = as.POSIXct("2017-12-20"), linetype = 3) + + geom_vline(xintercept = as.POSIXct("2018-11-15"), linetype = 3) + + geom_vline(xintercept = as.POSIXct("2020-11-15"), linetype = 3) + + geom_text(data = ann_text.output, label = "github.com/Rucknium", + colour = "black", size = 4.5, check_overlap = TRUE) +) +dev.off() + + + + + + + diff --git a/Pre-fork-BCH-BTC-Spending/get-block-times.R b/Pre-fork-BCH-BTC-Spending/get-block-times.R index 4697a39..8142cc8 100644 --- a/Pre-fork-BCH-BTC-Spending/get-block-times.R +++ b/Pre-fork-BCH-BTC-Spending/get-block-times.R @@ -6,7 +6,7 @@ bitcoin.conf.file <- "" data.dir <- "" # Input data directory here, with trailing "/" -bch.config <- rbch::conrpc(bitcoin.conf.file) +bitcoin.config <- rbch::conrpc(bitcoin.conf.file) initial.fork.height <- 478558 - 1 @@ -23,8 +23,8 @@ for (iter.block.height in initial.fork.height:current.block.height) { cat(iter.block.height, base::date(), "\n") } - block.hash <- rbch::getblockhash(bch.config, iter.block.height) - block.data <- rbch::getblock(bch.config, blockhash = block.hash@result, verbosity = "l1") + block.hash <- rbch::getblockhash(bitcoin.config, iter.block.height) + block.data <- rbch::getblock(bitcoin.config, blockhash = block.hash@result, verbosity = "l1") block.times[[iter.block.height - initial.fork.height + 1]] <- data.frame(block_height = iter.block.height, block_time = block.data@result$time) }