2019-11-26 10:15:22 +00:00
#!/usr/bin/env perl
2020-02-24 11:22:13 +00:00
use strict ;
use warnings ;
2019-11-26 10:15:22 +00:00
use POSIX ;
use Getopt::Std ;
2023-08-25 12:12:32 +01:00
use File::Slurp ;
2019-11-26 10:15:22 +00:00
sub usage () {
2020-06-03 11:49:49 +01:00
die ( "usage: $0 [-p api-port] dev-private-key [short-commit-hash]\n" ) ;
2019-11-26 10:15:22 +00:00
}
my % opt ;
getopts ( 'p:' , \ % opt ) ;
2020-06-03 11:49:49 +01:00
usage ( ) if @ ARGV < 1 || @ ARGV > 2 ;
2019-11-26 10:15:22 +00:00
my $ port = $ opt { p } || 12391 ;
my $ privkey = shift @ ARGV ;
2020-06-03 11:49:49 +01:00
my $ commit_hash = shift @ ARGV ;
2019-11-26 10:15:22 +00:00
2020-02-24 11:22:13 +00:00
my $ git_dir = `git rev-parse --show-toplevel` ;
die ( "Cannot determine git top level dir\n" ) unless $ git_dir ;
chomp $ git_dir ;
chdir ( $ git_dir ) || die ( "Can't change directory to $git_dir: $!\n" ) ;
2019-11-26 10:15:22 +00:00
open ( POM , '<' , 'pom.xml' ) || die ( "Can't open 'pom.xml': $!\n" ) ;
my $ project ;
while ( <POM> ) {
if ( m/<artifactId>(\w+)<.artifactId>/o ) {
$ project = $ 1 ;
last ;
}
}
close ( POM ) ;
2023-08-25 12:12:32 +01:00
my $ apikey = read_file ( 'apikey.txt' ) ;
2020-06-03 11:49:49 +01:00
# Do we need to determine commit hash?
unless ( $ commit_hash ) {
# determine git branch
my $ branch_name = ` git symbolic-ref -q HEAD ` ;
chomp $ branch_name ;
$ branch_name =~ s | ^ refs /heads/ || ; # ${branch_name##refs/heads/}
2020-05-27 11:49:59 +01:00
2020-06-03 11:49:49 +01:00
# short-form commit hash on base branch (non-auto-update)
$ commit_hash || = `git show --no-patch --format=%h` ;
die ( "Can't find commit hash\n" ) if ! defined $ commit_hash ;
chomp $ commit_hash ;
printf "Commit hash on '%s' branch: %s\n" , $ branch_name , $ commit_hash ;
} else {
printf "Using given commit hash: %s\n" , $ commit_hash ;
}
2020-02-24 11:22:13 +00:00
2020-05-27 11:49:59 +01:00
# build timestamp / commit timestamp on base branch
2020-06-03 11:49:49 +01:00
my $ timestamp = `git show --no-patch --format=%ct ${commit_hash}` ;
2020-02-24 11:22:13 +00:00
die ( "Can't determine commit timestamp\n" ) if ! defined $ timestamp ;
$ timestamp *= 1000 ; # Convert to milliseconds
2019-11-26 10:15:22 +00:00
2020-02-24 11:22:13 +00:00
# locate sha256 utility
my $ SHA256 = `which sha256sum || which sha256` ;
2021-03-21 18:15:29 +00:00
chomp $ SHA256 ;
2021-04-01 08:27:56 +01:00
die ( "Can't find sha256sum or sha256\n" ) unless length ( $ SHA256 ) > 0 ;
2019-11-26 10:15:22 +00:00
2020-02-24 11:22:13 +00:00
# SHA256 of actual update file
2021-03-21 18:15:29 +00:00
my $ sha256 = `git show auto-update-${commit_hash}:${project}.update | ${SHA256} | head -c 64` ;
2020-02-24 11:22:13 +00:00
die ( "Can't calculate SHA256 of ${project}.update\n" ) unless $ sha256 =~ m/(\S{64})/ ;
chomp $ sha256 ;
2019-11-26 10:15:22 +00:00
2020-02-24 11:22:13 +00:00
# long-form commit hash of HEAD on auto-update branch
2025-01-21 18:22:25 -08:00
#my $update_hash = `git rev-parse refs/heads/auto-update-${commit_hash}`;
my $ update_hash = `git rev-parse origin/auto-update-${commit_hash}` ;
2020-02-24 11:22:13 +00:00
die ( "Can't find commit hash for HEAD on auto-update-${commit_hash} branch\n" ) if ! defined $ update_hash ;
chomp $ update_hash ;
2019-11-26 10:15:22 +00:00
printf "Build timestamp (ms): %d / 0x%016x\n" , $ timestamp , $ timestamp ;
2020-02-24 11:22:13 +00:00
printf "Auto-update commit hash: %s\n" , $ update_hash ;
2019-11-26 10:15:22 +00:00
printf "SHA256 of ${project}.update: %s\n" , $ sha256 ;
2020-02-24 11:22:13 +00:00
my $ tx_type = 10 ;
my $ tx_timestamp = time ( ) * 1000 ;
my $ tx_group_id = 1 ;
my $ service = 1 ;
2019-11-26 10:15:22 +00:00
printf "\nARBITRARY(%d) transaction with timestamp %d, txGroupID %d and service %d\n" , $ tx_type , $ tx_timestamp , $ tx_group_id , $ service ;
2020-02-24 11:22:13 +00:00
my $ data_hex = sprintf "%016x%s%s" , $ timestamp , $ update_hash , $ sha256 ;
printf "\nARBITRARY transaction data payload: %s\n" , $ data_hex ;
2019-11-26 10:15:22 +00:00
2020-02-24 11:22:13 +00:00
my $ n_payments = 0 ;
2023-08-25 12:12:32 +01:00
my $ data_type = 1 ; # RAW_DATA
2020-02-24 11:22:13 +00:00
my $ data_length = length ( $ data_hex ) / 2 ; # two hex chars per byte
2023-08-25 12:12:32 +01:00
my $ fee = 0.01 * 1e8 ;
my $ nonce = 0 ;
my $ name_length = 0 ;
my $ identifier_length = 0 ;
my $ method = 0 ; # PUT
my $ secret_length = 0 ;
my $ compression = 0 ; # None
my $ metadata_hash_length = 0 ;
2020-02-24 11:22:13 +00:00
die ( "Something's wrong: data length is not 60 bytes!\n" ) if $ data_length != 60 ;
2019-11-26 10:15:22 +00:00
my $ pubkey = `curl --silent --url http://localhost:${port}/utils/publickey --data ${privkey}` ;
2020-02-24 11:22:13 +00:00
die ( "Can't convert private key to public key:\n$pubkey\n" ) unless $ pubkey =~ m/^\w{44}$/ ;
2019-11-26 10:15:22 +00:00
printf "\nPublic key: %s\n" , $ pubkey ;
my $ pubkey_hex = `curl --silent --url http://localhost:${port}/utils/frombase58 --data ${pubkey}` ;
2020-02-24 11:22:13 +00:00
die ( "Can't convert base58 public key to hex:\n$pubkey_hex\n" ) unless $ pubkey_hex =~ m/^[A-Za-z0-9]{64}$/ ;
2019-11-26 10:15:22 +00:00
printf "Public key hex: %s\n" , $ pubkey_hex ;
my $ address = `curl --silent --url http://localhost:${port}/addresses/convert/${pubkey}` ;
2020-03-25 11:29:57 +00:00
die ( "Can't convert base58 public key to address:\n$address\n" ) unless $ address =~ m/^\w{33,34}$/ ;
2019-11-26 10:15:22 +00:00
printf "Address: %s\n" , $ address ;
my $ reference = `curl --silent --url http://localhost:${port}/addresses/lastreference/${address}` ;
2020-03-25 11:29:57 +00:00
die ( "Can't fetch last reference for $address:\n$reference\n" ) unless $ reference =~ m/^\w{87,88}$/ ;
2019-11-26 10:15:22 +00:00
printf "Last reference: %s\n" , $ reference ;
my $ reference_hex = `curl --silent --url http://localhost:${port}/utils/frombase58 --data ${reference}` ;
2020-02-24 11:22:13 +00:00
die ( "Can't convert base58 reference to hex:\n$reference_hex\n" ) unless $ reference_hex =~ m/^[A-Za-z0-9]{128}$/ ;
2019-11-26 10:15:22 +00:00
printf "Last reference hex: %s\n" , $ reference_hex ;
2023-08-25 12:12:32 +01:00
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 ) ;
2019-11-26 10:15:22 +00:00
printf "\nRaw transaction hex:\n%s\n" , $ raw_tx_hex ;
my $ raw_tx = `curl --silent --url http://localhost:${port}/utils/tobase58/${raw_tx_hex}` ;
2023-08-25 12:12:32 +01:00
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
2019-11-26 10:15:22 +00:00
printf "\nRaw transaction (base58):\n%s\n" , $ raw_tx ;
my $ sign_data = qq|' { "privateKey": "${privkey}", "transactionBytes": "${raw_tx}" } '| ;
my $ signed_tx = `curl --silent -H "accept: text/plain" -H "Content-Type: application/json" --url http://localhost:${port}/transactions/sign --data ${sign_data}` ;
2023-08-25 12:12:32 +01:00
die ( "Can't sign raw transaction:\n$signed_tx\n" ) unless $ signed_tx =~ m/^\w{390,410}$/ ; # +90ish longer than $raw_tx
2019-11-26 10:15:22 +00:00
printf "\nSigned transaction:\n%s\n" , $ signed_tx ;
2025-01-21 18:22:25 -08:00
# Get the origin URL - So that we will be able to TEST the obtaining of the qortal.update...
2019-11-27 15:35:46 +00:00
my $ origin = `git remote get-url origin` ;
2025-01-21 18:22:25 -08:00
chomp $ origin ; # Remove any trailing newlines
die ( "Unable to get github url for 'origin'?\n" ) unless $ origin ;
# Debug: Print the origin URL
print "Full Origin URL: $origin\n" ;
# Extract the repository path (e.g., Qortal/qortal) NOTE - github is case-sensitive with repo names
my $ repo ;
if ( $ origin =~ m/[:\/]([\w\-]+\/[\w\-]+)\.git$/ ) {
$ repo = $ 1 ;
print "Extracted direct repository path: $repo\n" ;
if ( $ repo =~ m/^qortal\//i ) {
$ repo =~ s/^qortal\//Qortal\// ;
print "Corrected repository path capitalization: $repo\n" ;
}
print "Please verify the direct repository path. Current: '$repo'\n" ;
print "If incorrect, input the correct direct repository path (e.g., 'Qortal/qortal' or 'bob/qortal').NOTE - github is CASE SENSITIVE for repository urls... Press Enter to keep the extracted version: " ;
my $ input = <STDIN> ;
if ( $ input =~ m/^qortal\//i ) {
$ input =~ s/^qortal\//Qortal\// ;
print "Corrected repository path capitalization: $repo\n" ;
}
chomp $ input ;
$ repo = $ input if $ input ; # Update repo if user provides input
} else {
# Default to qortal/qortal if extraction fails
$ repo = "Qortal/qortal" ;
print "Failed to extract repository path from origin URL. Using default: $repo\n" ;
# Prompt the user for confirmation or input
print "Please verify the repository path. Current: '$repo'\n" ;
print "If incorrect, input the correct repository path (e.g., 'Qortal/qortal' or 'BobsCodeburgers/qortal'). NOTE - GitHub is CASE SENSITIVE for repository urls... Press Enter to keep the default: " ;
my $ input = <STDIN> ;
if ( $ input =~ m/^qortal\//i ) {
$ input =~ s/^qortal\//Qortal\// ;
print "Corrected repository path capitalization: $repo\n" ;
}
chomp $ input ;
$ repo = $ input if $ input ; # Update repo if user provides input
}
# Debug: Print the final repository path
print "Final direct repository path: $repo\n" ;
# Construct the update URL
2020-02-24 11:22:13 +00:00
my $ update_url = "https://github.com/${repo}/raw/${update_hash}/${project}.update" ;
2025-01-21 18:22:25 -08:00
print "Final update URL: $update_url\n" ;
2019-11-27 15:35:46 +00:00
my $ fetch_result = `curl --silent -o /dev/null --location --range 0-1 --head --write-out '%{http_code}' --url ${update_url}` ;
2020-02-24 11:22:13 +00:00
die ( "\nUnable to fetch update from ${update_url}\n" ) if $ fetch_result ne '200' ;
2019-11-27 15:35:46 +00:00
printf "\nUpdate fetchable from ${update_url}\n" ;
2019-11-26 10:15:22 +00:00
# Flush STDOUT after every output
$| = 1 ;
print "\n" ;
for ( my $ delay = 5 ; $ delay > 0 ; - - $ delay ) {
printf "\rSubmitting transaction in %d second%s... CTRL-C to abort " , $ delay , ( $ delay != 1 ? 's' : '' ) ;
sleep 1 ;
}
printf "\rSubmitting transaction NOW... \n" ;
my $ result = `curl --silent --url http://localhost:${port}/transactions/process --data ${signed_tx}` ;
2020-02-24 11:22:13 +00:00
chomp $ result ;
die ( "Transaction wasn't accepted:\n$result\n" ) unless $ result eq 'true' ;
my $ decoded_tx = `curl --silent -H "Content-Type: application/json" --url http://localhost:${port}/transactions/decode --data ${signed_tx}` ;
2020-05-27 11:49:59 +01:00
printf "\nTransaction accepted:\n$decoded_tx\n" ;