/ tools / publish-auto-update.pl
publish-auto-update.pl
  1  #!/usr/bin/env perl
  2  
  3  use strict;
  4  use warnings;
  5  use POSIX;
  6  use Getopt::Std;
  7  use File::Slurp;
  8  
  9  sub usage() {
 10  	die("usage: $0 [-p api-port] dev-private-key [short-commit-hash]\n");
 11  }
 12  
 13  my %opt;
 14  getopts('p:', \%opt);
 15  
 16  usage() if @ARGV < 1 || @ARGV > 2;
 17  
 18  my $port = $opt{p} || 10391;
 19  my $privkey = shift @ARGV;
 20  my $commit_hash = shift @ARGV;
 21  
 22  my $git_dir = `git rev-parse --show-toplevel`;
 23  die("Cannot determine git top level dir\n") unless $git_dir;
 24  
 25  chomp $git_dir;
 26  chdir($git_dir) || die("Can't change directory to $git_dir: $!\n");
 27  
 28  open(POM, '<', 'pom.xml') || die ("Can't open 'pom.xml': $!\n");
 29  my $project;
 30  while (<POM>) {
 31  	if (m/<artifactId>(\w+)<.artifactId>/o) {
 32  		$project = $1;
 33  		last;
 34  	}
 35  }
 36  close(POM);
 37  
 38  my $apikey = read_file('apikey.txt');
 39  
 40  # Do we need to determine commit hash?
 41  unless ($commit_hash) {
 42  	# determine git branch
 43  	my $branch_name = ` git symbolic-ref -q HEAD `;
 44  	chomp $branch_name;
 45  	$branch_name =~ s|^refs/heads/||; # ${branch_name##refs/heads/}
 46  
 47  	# short-form commit hash on base branch (non-auto-update)
 48  	$commit_hash ||= `git show --no-patch --format=%h`;
 49  	die("Can't find commit hash\n") if ! defined $commit_hash;
 50  	chomp $commit_hash;
 51  	printf "Commit hash on '%s' branch: %s\n", $branch_name, $commit_hash;
 52  } else {
 53  	printf "Using given commit hash: %s\n", $commit_hash;
 54  }
 55  
 56  # build timestamp / commit timestamp on base branch
 57  my $timestamp = `git show --no-patch --format=%ct ${commit_hash}`;
 58  die("Can't determine commit timestamp\n") if ! defined $timestamp;
 59  $timestamp *= 1000; # Convert to milliseconds
 60  
 61  # locate sha256 utility
 62  my $SHA256 = `which sha256sum || which sha256`;
 63  chomp $SHA256;
 64  die("Can't find sha256sum or sha256\n") unless length($SHA256) > 0;
 65  
 66  # SHA256 of actual update file
 67  my $sha256 = `git show auto-update-${commit_hash}:${project}.update | ${SHA256} | head -c 64`;
 68  die("Can't calculate SHA256 of ${project}.update\n") unless $sha256 =~ m/(\S{64})/;
 69  chomp $sha256;
 70  
 71  # long-form commit hash of HEAD on auto-update branch
 72  #my $update_hash = `git rev-parse refs/heads/auto-update-${commit_hash}`;
 73  my $update_hash = `git rev-parse origin/auto-update-${commit_hash}`;
 74  die("Can't find commit hash for HEAD on auto-update-${commit_hash} branch\n") if ! defined $update_hash;
 75  chomp $update_hash;
 76  
 77  printf "Build timestamp (ms): %d / 0x%016x\n", $timestamp, $timestamp;
 78  printf "Auto-update commit hash: %s\n", $update_hash;
 79  printf "SHA256 of ${project}.update: %s\n", $sha256;
 80  
 81  my $tx_type = 10;
 82  my $tx_timestamp = time() * 1000;
 83  my $tx_group_id = 1;
 84  my $service = 1;
 85  printf "\nARBITRARY(%d) transaction with timestamp %d, txGroupID %d and service %d\n", $tx_type, $tx_timestamp, $tx_group_id, $service;
 86  
 87  my $data_hex = sprintf "%016x%s%s", $timestamp, $update_hash, $sha256;
 88  printf "\nARBITRARY transaction data payload: %s\n", $data_hex;
 89  
 90  my $n_payments = 0;
 91  my $data_type = 1; # RAW_DATA
 92  my $data_length = length($data_hex) / 2; # two hex chars per byte
 93  my $fee = 0.01 * 1e8;
 94  my $nonce = 0;
 95  my $name_length = 0;
 96  my $identifier_length = 0;
 97  my $method = 0; # PUT
 98  my $secret_length = 0;
 99  my $compression = 0; # None
100  my $metadata_hash_length = 0;
101  
102  die("Something's wrong: data length is not 60 bytes!\n") if $data_length != 60;
103  
104  my $pubkey = `curl --silent --url http://localhost:${port}/utils/publickey --data ${privkey}`;
105  die("Can't convert private key to public key:\n$pubkey\n") unless $pubkey =~ m/^\w{44}$/;
106  printf "\nPublic key: %s\n", $pubkey;
107  
108  my $pubkey_hex = `curl --silent --url http://localhost:${port}/utils/frombase58 --data ${pubkey}`;
109  die("Can't convert base58 public key to hex:\n$pubkey_hex\n") unless $pubkey_hex =~ m/^[A-Za-z0-9]{64}$/;
110  printf "Public key hex: %s\n", $pubkey_hex;
111  
112  my $address = `curl --silent --url http://localhost:${port}/addresses/convert/${pubkey}`;
113  die("Can't convert base58 public key to address:\n$address\n") unless $address =~ m/^\w{33,34}$/;
114  printf "Address: %s\n", $address;
115  
116  my $reference = `curl --silent --url http://localhost:${port}/addresses/lastreference/${address}`;
117  die("Can't fetch last reference for $address:\n$reference\n") unless $reference =~ m/^\w{87,88}$/;
118  printf "Last reference: %s\n", $reference;
119  
120  my $reference_hex = `curl --silent --url http://localhost:${port}/utils/frombase58 --data ${reference}`;
121  die("Can't convert base58 reference to hex:\n$reference_hex\n") unless $reference_hex =~ m/^[A-Za-z0-9]{128}$/;
122  printf "Last reference hex: %s\n", $reference_hex;
123  
124  my $raw_tx_hex = sprintf("%08x%016x%08x%s%s%08x%08x%08x%08x%08x%08x%08x%08x%02x%08x%s%08x%08x%016x", $tx_type, $tx_timestamp, $tx_group_id, $reference_hex, $pubkey_hex, $nonce, $name_length, $identifier_length, $method, $secret_length, $compression, $n_payments, $service, $data_type, $data_length, $data_hex, $data_length, $metadata_hash_length, $fee);
125  printf "\nRaw transaction hex:\n%s\n", $raw_tx_hex;
126  
127  my $raw_tx = `curl --silent --url http://localhost:${port}/utils/tobase58/${raw_tx_hex}`;
128  die("Can't convert raw transaction hex to base58:\n$raw_tx\n") unless $raw_tx =~ m/^\w{300,320}$/; # Roughly 305 to 320 base58 chars
129  printf "\nRaw transaction (base58):\n%s\n", $raw_tx;
130  
131  my $sign_data = qq|' { "privateKey": "${privkey}", "transactionBytes": "${raw_tx}" } '|;
132  my $signed_tx = `curl --silent -H "accept: text/plain" -H "Content-Type: application/json" --url http://localhost:${port}/transactions/sign --data ${sign_data}`;
133  die("Can't sign raw transaction:\n$signed_tx\n") unless $signed_tx =~ m/^\w{390,410}$/; # +90ish longer than $raw_tx
134  printf "\nSigned transaction:\n%s\n", $signed_tx;
135  
136  # Get the origin URL - So that we will be able to TEST the obtaining of the forknet.update... 
137  my $origin = `git remote get-url origin`;
138  chomp $origin; # Remove any trailing newlines
139  die("Unable to get github url for 'origin'?\n") unless $origin;
140  
141  # Debug: Print the origin URL
142  print "Full Origin URL: $origin\n";
143  
144  # Extract the repository path (e.g., Forknet/forknet) NOTE - github is case-sensitive with repo names
145  my $repo;
146  if ($origin =~ m/[:\/]([\w\-]+\/[\w\-]+)\.git$/) {
147      $repo = $1;
148      print "Extracted direct repository path: $repo\n";
149      if ($repo =~ m/^forknet\//i) {
150          $repo =~ s/^forknet\//Forknet\//;
151          print "Corrected repository path capitalization: $repo\n";
152      }
153      print "Please verify the direct repository path. Current: '$repo'\n";
154      print "If incorrect, input the correct direct repository path (e.g., 'Forknet/forknet' or 'bob/forknet').NOTE - github is CASE SENSITIVE for repository urls... Press Enter to keep the extracted version: ";
155      my $input = <STDIN>;
156      if ($input =~ m/^forknet\//i) {
157          $input =~ s/^forknet\//Forknet\//;
158          print "Corrected repository path capitalization: $repo\n";
159      }
160      chomp $input;
161      $repo = $input if $input; # Update repo if user provides input
162  
163  } else {
164      # Default to forknet/forknet if extraction fails
165      $repo = "Forknet/forknet";
166      print "Failed to extract repository path from origin URL. Using default: $repo\n";
167  
168      # Prompt the user for confirmation or input
169      print "Please verify the repository path. Current: '$repo'\n";
170      print "If incorrect, input the correct repository path (e.g., 'Forknet/forknet' or 'BobsCodeburgers/forknet'). NOTE - GitHub is CASE SENSITIVE for repository urls... Press Enter to keep the default: ";
171      my $input = <STDIN>;
172      if ($input =~ m/^forknet\//i) {
173          $input =~ s/^forknet\//Forknet\//;
174          print "Corrected repository path capitalization: $repo\n";
175      }
176      chomp $input;
177      $repo = $input if $input; # Update repo if user provides input
178  }
179  
180  # Debug: Print the final repository path
181  print "Final direct repository path: $repo\n";
182  
183  # Construct the update URL
184  my $update_url = "https://github.com/${repo}/raw/${update_hash}/${project}.update";
185  print "Final update URL: $update_url\n";
186  
187  
188  my $fetch_result = `curl --silent -o /dev/null --location --range 0-1 --head --write-out '%{http_code}' --url ${update_url}`;
189  die("\nUnable to fetch update from ${update_url}\n") if $fetch_result ne '200';
190  printf "\nUpdate fetchable from ${update_url}\n";
191  
192  # Flush STDOUT after every output
193  $| = 1;
194  print "\n";
195  for (my $delay = 5; $delay > 0; --$delay) {
196  	printf "\rSubmitting transaction in %d second%s... CTRL-C to abort ", $delay, ($delay != 1 ? 's' : '');
197  	sleep 1;
198  }
199  
200  printf "\rSubmitting transaction NOW...                                    \n";
201  my $result = `curl --silent --url http://localhost:${port}/transactions/process --data ${signed_tx}`;
202  chomp $result;
203  die("Transaction wasn't accepted:\n$result\n") unless $result eq 'true';
204  
205  my $decoded_tx = `curl --silent -H "Content-Type: application/json" --url http://localhost:${port}/transactions/decode --data ${signed_tx}`;
206  printf "\nTransaction accepted:\n$decoded_tx\n";