feat(user/bkotslu):v0.0.1: Add OTS lookup script
authorSteven Baltakatei Sandoval <baltakatei@gmail.com>
Tue, 1 Oct 2024 16:40:00 +0000 (16:40 +0000)
committerSteven Baltakatei Sandoval <baltakatei@gmail.com>
Tue, 1 Oct 2024 16:40:00 +0000 (16:40 +0000)
- Note: Status: only archives OTS files from provided input dirs at
  the moment.

user/bkotslu [new file with mode: 0755]

diff --git a/user/bkotslu b/user/bkotslu
new file mode 100755 (executable)
index 0000000..f2e1220
--- /dev/null
@@ -0,0 +1,264 @@
+#!/bin/bash
+# Desc: Utility for backing up and retrieving ots files
+# Version: 0.0.1
+
+OTS_FCACHE_DIR="$HOME/.cache/bkotslu/";
+MAX_FIND_DEPTH=12;
+
+yell() { echo "$0: $*" >&2; } # print script path and all args to stderr
+die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status
+must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails
+vbm() {
+    # Description: Prints verbose message ("vbm") to stderr if opVerbose is set to "true".
+    # Usage: vbm "DEBUG :verbose message here"
+    # Version 0.2.0
+    # Input: arg1: string
+    #        vars: opVerbose
+    # Output: stderr
+    # Depends: bash 5.1.16, GNU-coreutils 8.30 (echo, date)
+
+    if [ "$opVerbose" = "true" ]; then
+       functionTime="$(date --iso-8601=ns)"; # Save current time in nano seconds.
+       echo "[$functionTime]:$0:""$*" 1>&2;  # Display argument text.
+    fi
+
+    # End function
+    return 0; # Function finished.
+}; # Displays message if opVerbose true
+showUsage() {
+    # Desc: Display script usage information
+    # Usage: showUsage
+    # Version 0.0.2
+    # Input: none
+    # Output: stdout
+    # Depends: GNU-coreutils 8.30 (cat)
+    cat <<'EOF'
+    USAGE:
+        bkotslu [ options ] [FILE...]
+
+    OPTIONS:
+        -h, --help
+                Display help information.
+        --version
+                Display script version.
+        -v, --verbose
+                Display debugging info.
+        -i, --input-file
+                Define input file path for file to add ots file to.
+        -O, --output-dir
+                Define output directory path for storing ots backup files.
+                  DEFAULT: $HOME/.cache/bkotslu/
+        --
+                Indicate end of options.
+
+    EXAMPLE:
+      Hash foo.txt and lookup matching ots file
+        bkotslu -i foo.txt
+
+      Archive ots files from home directory
+        bkotslu -I $HOME/
+
+EOF
+}; # Display information on how to use this script.
+showVersion() {
+    # Desc: Displays script version and license information.
+    # Usage: showVersion
+    # Version: 0.0.2
+    # Input: scriptVersion   var containing version string
+    # Output: stdout
+    # Depends: vbm(), yell, GNU-coreutils 8.30
+
+    # Initialize function
+    vbm "DEBUG:showVersion function called."
+
+    cat <<'EOF'
+bkotslu 0.0.1
+Copyright (C) 2024 Steven Baltakatei Sandoval
+License GPLv3: GNU GPL version 3
+This is free software; you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
+
+    GNU Coreutils 8.32
+    Copyright (C) 2020 Free Software Foundation, Inc.
+    License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
+    This is free software: you are free to change and redistribute it.
+    There is NO WARRANTY, to the extent permitted by law.
+EOF
+    
+    # End function
+    vbm "DEBUG:showVersion function ended."
+    return 0; # Function finished.
+}; # Display script version.
+processArgs() {
+    # Desc: Processes arguments provided to script.
+    # Usage: processArgs "$@"
+    # Version: 1.0.0
+    # Input: "$@"          (list of arguments provided to the function)
+    # Output: Sets following variables used by other functions:
+    #   opVerbose            Indicates verbose mode enable status.  (ex: "true", "false")
+    #   pathDirOut1          Path to output directory.
+    #   pathDirIn1           Path to input directory.
+    #   pathFileIn1          Path to input file.
+    #   arrayPosArgs         Array of remaining positional argments
+    # Depends:
+    #   yell()           Displays messages to stderr.
+    #   vbm()            Displays messsages to stderr if opVerbose set to "true".
+    #   showUsage()      Displays usage information about parent script.
+    #   showVersion()    Displays version about parent script.
+    #   arrayPosArgs     Global array for storing non-option positional arguments (i.e. arguments following the `--` option).
+    # External dependencies: bash (5.1.16), echo
+    # Ref./Attrib.:
+    #  [1]: Marco Aurelio (2014-05-08). "echo that outputs to stderr". https://stackoverflow.com/a/23550347
+    #  [2]: "Handling positional parameters" (2018-05-12). https://wiki.bash-hackers.org/scripting/posparams
+
+    # Initialize function
+    vbm "DEBUG:processArgs function called."
+
+    # Perform work
+    if [ $# -le 0 ]; then yell "FATAL:No arguments provided."; showUsage; fi;
+    while [ ! $# -eq 0 ]; do   # While number of arguments ($#) is not (!) equal to (-eq) zero (0).
+       #yell "DEBUG:Starting processArgs while loop." # Debug stderr message. See [1].
+        #yell "DEBUG:Provided arguments are:""$*"      # Debug stderr message. See [1].
+       case "$1" in
+           -h | --help) showUsage; exit 1;; # Display usage.
+           --version) showVersion; exit 1;; # Show version
+           -v | --verbose) opVerbose="true"; vbm "DEBUG:Verbose mode enabled.";; # Enable verbose mode. See [1].
+           -i | --input-file) # Define input file path
+               if [ -f "$2" ]; then # If $2 is file that exists, set pathFileIn1 to $2, pop $2.
+                   pathFileIn1="$2";
+                    vbm "DEBUG:Input file pathFileIn1 set to:${pathFileIn1}";
+                   shift;
+               else
+                   die "FATAL: Specified input file does not exist:$2";
+               fi;; 
+           -I | --input-dir) # Define input directory path
+               if [ -d "$2" ]; then # If $2 is dir that exists, set pathDirIn1 to $2, pop $2.
+                   pathDirIn1="$2";
+                    vbm "DEBUG:Input directory pathDirIn1 set to:${pathDirIn1}";
+                   shift;                  
+               else # Display error if $2 is not a valid dir.
+                   die "FATAL:Specified input directory does not exist:$2";
+               fi;;
+           -O | --output-dir) # Define output directory path
+               if [ -d "$2" ]; then # If $2 is dir that exists, set pathDirOut1 to $2, pop $2
+                   pathDirOut1="$2";
+                    vbm "DEBUG:Output directory pathDirOut1 set to:${pathDirOut1}";
+                   shift;                  
+               else
+                   die "FATAL:Specified output directory is not valid:$2";
+               fi;;
+            --) # End of all options. See [2].
+                shift;
+                for arg in "$@"; do
+                    vbm "DEBUG:adding to arrayPosArgs:$arg";
+                    arrayPosArgs+=("$arg");
+                done;
+                break;;
+            -*) showUsage; die "FATAL: Unrecognized option.";; # Display usage
+           *) showUsage; die "FATAL: Unrecognized argument.";; # Handle unrecognized options. See [1].
+       esac;
+       shift;
+    done;
+
+    ## Identify ots file cache dir
+    if [[ -z "$pathDirOut1" ]]; then
+        vbm "STATUS:No output directory for caching OTS files specified.";
+        pathDirOut1="$OTS_FCACHE_DIR";
+        vbm "STATUS:Assuming OTS files to be cached in:${pathDirOut1}";
+    fi;
+    ## Create cache dir if necessary
+    if [[ ! -d "$pathDirOut1" ]]; then
+        vbm "STATUS:Creating OTS file cache directory:${pathDirOut1}";
+        must mkdir -p "$pathDirOut1";
+    fi;
+    
+    # End function
+    vbm "DEBUG:processArgs function ended.";
+    return  0; # Function finished.
+}; # Evaluate script options from positional arguments (ex: $1, $2, $3, etc.).
+get_ots_filehash() {
+    # Desc: Gets hash of an opentimestamp'd file from ots file
+    # Usage: get_ots_filehash FILE
+    # Example: get_ots_filehash foo.txt.ots
+    # Depends: ots 0.7.0, GNU grep 3.7, GNU Coreutils 8.32
+    #          vbm()              verbose output
+    #          BK-2020-03  yell()
+    # Input:  arg1    OTS file path
+    # Output: stdout  sha256 file hash (lowercase)
+    local output;
+
+    vbm "DEBUG:Starting get_ots_filehash() on:$1";
+    if output="$( "$(which ots)" info "$1" | \
+            grep -E "^File sha256 hash: " | \
+            head -n1 | \
+            sed -E -e 's/(^File sha256 hash: )([0-9a-f]+$)/\2/g'; )" && \
+            [[ -n "$output" ]]; then
+        vbm "STATUS:Read file hash via ots from:$1";
+        printf "%s" "$output";
+        return 0;
+    else
+        yell "ERROR:Encountered problem getting file hash via ots from:$1";
+        return 1;
+    fi;
+}; # Gets hash of file from ots file
+get_ots_oldestblock() {
+    # Desc: Gets earliest Bitcoin block number from ots file
+    # Usage: get_ots_oldestblock FILE
+    # Example: get_ots_oldestblock foo.txt.ots
+    # Input: arg1     path  OTS file path
+    # Output: stdout  int   Bitcoin block number
+    # Depends: OpenTimestamps 0.7.0, GNU grep 3.7, GNU Coreutils 8.32
+    #          vbm()
+    #          BK-2020-03: yell()
+    local output;
+
+    vbm "DEBUG:Starting get_ots_oldestblock() on:$1";    
+    if output="$( "$(which ots)" info "$1" | \
+            grep -E "verify BitcoinBlockHeaderAttestation\([0-9]+\)" | \
+            sort | head -n1 | \
+            sed -E -e 's/(^    verify BitcoinBlockHeaderAttestation)\(([0-9]+)(\))/\2/g'; )" && \
+            [[ -n "$output" ]]; then
+        vbm "STATUS:Retrieved Bitcoin block via ots from:$1";
+        printf "%s" "$output";
+        return 0;
+    else
+        yell "ERROR:Encountered problem getting Bitcoin block number via ots from:$1";
+        return 1;
+    fi;
+}; # Gets oldest Bitcoin block from ots file
+cache_ots_file() {
+    # Desc: Scans and caches an OTS file for storing
+    if fhash="$(must get_ots_filehash "$line")" && \
+            block="$(must get_ots_oldestblock "$line")"; then
+        fout="${fhash}_${block}.otsu"; # file name out
+        pout="${pathDirOut1}/${fout}"; # file path out
+        vbm "STATUS:Found OTS file at ${line} with hash ${fhash} and block ${block} and saving to ${pout}";
+        must cp -n "$line" "$pout";
+    else
+        yell "ERROR:Problem analyzing file with OpenTimestamps:${line}";
+    fi;
+}; # Scans a single OTS file
+
+main() {
+    processArgs "$@";
+
+
+    # Scan provided dir for OTS files to cache.
+    if [[ -n "$pathDirIn1" ]]; then
+        while read -r line; do
+            line="$(readlink -f "$line")";
+            cache_ots_file;
+        done < <(find "$pathDirIn1" -maxdepth "$MAX_FIND_DEPTH" -type f -name "*.ots");
+    fi;
+
+    # Lookup OTS file from archive for provided file.
+    if [[ -n "$pathFileIn1" ]]; then
+        :
+    fi;
+    
+    
+
+
+}; # main program
+
+main "$@";