pof

Proof of Freshness: collate proof of an authorship date.

Log | Files | << Repositories


tree 68a8f65ca8ab70b4ffa5eef91daf2eafe76869fe
parent ab946168f88a9483f8d147a101ba5f27e8c2aa8c
author esote <esote.net@gmail.com> 1563334422 -0500
committer esote <esote.net@gmail.com> 1563334422 -0500
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQTXAxYDuIzimYoNSPuhTmRAjzzC8gUCXS6XGAAKCRChTmRAjzzC
 8ocyAQCMe7OAnV68j1AwisoMhp7zNKdBdmgaYBWLvrmEeuARVwEA0UVrpFWwVbi4
 vhEKQeljWfDGYyvnZgi7SjK4fxIwig4=
 =XN7L
 -----END PGP SIGNATURE-----

Clean up GET requests, clean up error checking

Make more things const, calculate array length at compile time.
Update README.
Document functions with comments.

 README |  55 ++++++++--------
 pof.go | 225 ++++++++++++++++++++++++++++-------------------------------------
 2 files changed, 126 insertions(+), 154 deletions(-)

diff --git a/README b/README
index 32867e4..13d10c0 100644
--- a/README
+++ b/README
@@ -21,56 +21,57 @@ Inspired by the proof of freshness used in Qubes OS's canaries.
 
 Example output:
 
-	Date: 2019-07-14 16:54 UTC
+	Date: 2019-07-17 03:29 UTC
 
 	Src: SPIEGEL ONLINE - International (https://www.spiegel.de/international/index.rss)
 	 ---
+	'Euro Orphans': The Romanian Children Growing up Without Their Parents
 	Far-Right AfD: Germany's Populist Party Embraces Its Extremist Wing
 	A Question of Trust: Ursula Von Der Leyen Recruits Team to Win Over Brussels
 	Feeding People With Science: Plant Researchers Brace for Population Explosion
 	A Heroine and a Figure of Hate: Carola Rackete and Europe's Troubling Refugee Policies
-	Sea-Watch Captain Carola Rackete: 'We Were All in a State of Total Despair'
 
 	Src: NYT > World News (https://rss.nytimes.com/services/xml/rss/nyt/World.xml)
 	 ---
-	Greens Are the New Hope for Europe s Center. For the Far Right, They re Enemy No. 1.
-	Hong Kong Protesters  New Target: A News Station Seen as China s Friend
-	India s Going to the Moon, and the Country Is Pumped
-	Acute Monsoon Flooding in Nepal and India Leaves Dozens Dead
-	British Leaks Describe Trump s  Act of Diplomatic Vandalism  on Iran Deal
+	A Prosperous China Says  Men Preferred,  and Women Lose
+	Epstein s Ties to Former Israeli Leader Shake Up Election Campaign
+	A Runaway Train Explosion Killed 47, but Deadly Cargo Still Rides the Rails
+	Iran s Top Leader Strikes Defiant Tone as Trump Says  We re Not Looking for Regime Change
+	Turkey s Erdogan Goes His Own Way as Distrust With U.S. Grows
 
 	Src: BBC News - World (https://feeds.bbci.co.uk/news/world/rss.xml)
 	 ---
-	Inside Iran: Iranians on Trump and the nuclear deal
-	Trump under fire for racially-charged tweets against congresswomen
-	UN calls for Libyan migrant detention centres to be shut
-	Bastille Day: Police clash with yellow vests after parade
-	Burundi's Imbonerakure leader named head of RTNB
+	US House condemns Trump's attacks on four congresswomen
+	Von der Leyen elected EU Commission head after MEPs vote
+	Johnny Clegg, South African musician and activist, dies aged 66
+	Norway's spy Town: 'They took him and they have broken him'
+	Facebook's Libra cryptocurrency attacked at Senate hearing
 
 	Src: Reuters: World News (http://feeds.reuters.com/reuters/worldnews)
 	 ---
-	Rouhani says Iran ready to talk to U.S. if sanctions lifted
-	Guatemala president postpones Washington visit where asylum talks were planned
-	Hong Kong extradition protesters escalate fight in suburbs
-	Death toll in Nepal floods rises to 55, thousands displaced
-	Erdogan says Trump can waive sanctions on Turkey: Haberturk
+	Gun megastore plan in New Zealand's Christchurch sparks backlash: media
+	Puerto Rico governor vows to remain in office after violent protests
+	ACLU files suit to block Trump rule to stop asylum-seekers
+	Germany's Von der Leyen secures powerful EU executive top job
+	German Conservative boss to succeed von der Leyen as minister
 
 	Src: Latest Updates (https://www.economist.com/latest/rss.xml)
 	 ---
-	Why much is at stake in a tale of teachers in Middle America
-	Helping people with learning disabilities into jobs
-	Is conservatism in crisis?
-	Berlin s Museum Island gets a much-needed revamp
-	 It s a pro-Western democracy, yet the president is a pariah Taiwan s leader on tour
+	Ursula von der Leyen is elected European Commission president
+	How slow can you grow?
+	Novak Djokovic wins the most thrilling men s tennis match ever
+	 The Farewell  is a poignant study of family, tradition and identity
+	What is a tiebreak?
 
 	Src: NIST Beacon v2 (https://beacon.nist.gov/beacon/2.0/pulse/last)
 	 ---
-	705F408C507E869D617972E6A532B2AB95BDE4AEA9910E58C3F931A3B09DAE5F6192A315F1E566B05D18B3FFE8D45AD1B24FA47018075A3651012893C43B2382
+	0CC14EA51480B1A72D20DBF4099676C9CE80535AECD6213BA8AB70F696FEF4D7415BECB951C9A0243AAF5511D4C8F335062E8F33273AB5FF61692407A99D2D67
 
-	Src: Blockchain.Info [block depth 10] (https://blockchain.info/blocks/?format=json)
+	Src: Blockchain.Info [block depth 10] (https://blockchain.info/block-height/585763?format=json)
 	 ---
-	000000000000000000024d92c86b07d4da27ea25c509dd8397809982facc4f1a
+	00000000000000000000f55723baa1e873632afb1c8fc27a496c9e44caab47d9
 
-	Src: Moneroblocks.Info [block depth 10] (https://moneroblocks.info/api/get_block_header/1878415)
+	Src: Moneroblocks.Info [block depth 10] (https://moneroblocks.info/api/get_block_header/1880172)
 	 ---
-	fbde38dc1da52e0718a065beb2acdef6bb28029328f84cd337d057853ddc4a3b
+	976170416a969c8e3a2cca3069a3c592e9e80ba81707ae75393bed3404b3f8f3
+
diff --git a/pof.go b/pof.go
index 28c7041..088f419 100644
--- a/pof.go
+++ b/pof.go
@@ -3,6 +3,7 @@ package main
 import (
 	"encoding/json"
 	"encoding/xml"
+	"errors"
 	"fmt"
 	"io/ioutil"
 	"log"
@@ -13,23 +14,25 @@ import (
 )
 
 func main() {
-	// date
+	// Date in UTC.
 	fmt.Printf("Date: %s\n\n",
 		time.Now().UTC().Format("2006-01-02 15:04 MST"))
 
-	// news feeds
-	news()
-
-	// NIST randomness beacons
-	nist()
-
-	// BTC block hash
-	btc()
+	sources := [...]func() error{
+		news,
+		nist,
+		btc,
+		monero,
+	}
 
-	// Monero block hash
-	monero()
+	for _, source := range sources {
+		if err := source(); err != nil {
+			log.Fatal(err)
+		}
+	}
 }
 
+// Structure of an RSS feed, exposing only the fields useful to print news().
 type Rss struct {
 	XMLName xml.Name `xml:"rss"`
 	Channel struct {
@@ -40,10 +43,11 @@ type Rss struct {
 	} `xml:"channel"`
 }
 
-func news() {
-	var re = regexp.MustCompile(`[^[:ascii:]]+`)
+// International news feeds.
+func news() error {
+	re := regexp.MustCompile(`[^[:ascii:]]+`)
 
-	urls := []string{
+	urls := [...]string{
 		"https://www.spiegel.de/international/index.rss",
 		"https://rss.nytimes.com/services/xml/rss/nyt/World.xml",
 		"https://feeds.bbci.co.uk/news/world/rss.xml",
@@ -57,11 +61,11 @@ func news() {
 		rss, err := parseRss(url)
 
 		if err != nil {
-			log.Fatal(err)
+			return err
 		}
 
 		if len(rss.Channel.Items) < count {
-			log.Fatalf("couldn't find %d items", count)
+			return fmt.Errorf("couldn't find %d items", count)
 		}
 
 		fmt.Printf("Src: %s (%s)\n ---\n", re.ReplaceAllString(rss.Channel.Title, " "), url)
@@ -72,51 +76,33 @@ func news() {
 
 		fmt.Println()
 	}
+
+	return nil
 }
 
+// GET and unmarshal specified RSS URL.
 func parseRss(url string) (*Rss, error) {
-	resp, err := http.Get(url)
+	data, err := getRead(url)
 
 	if err != nil {
 		return nil, err
 	}
 
-	rssxml, err := ioutil.ReadAll(resp.Body)
-
-	if err != nil {
-		return nil, err
-	}
-
-	if err := resp.Body.Close(); err != nil {
-		return nil, err
-	}
-
 	var rss Rss
 
-	if err := xml.Unmarshal(rssxml, &rss); err != nil {
-		return nil, err
-	}
+	err = xml.Unmarshal(data, &rss)
 
-	return &rss, nil
+	return &rss, err
 }
 
-func nist() {
-	v2URL := "https://beacon.nist.gov/beacon/2.0/pulse/last"
-
-	resp, err := http.Get(v2URL)
-
-	if err != nil {
-		log.Fatal(err)
-	}
+// NIST randomness beacon v2.
+func nist() error {
+	const v2url = "https://beacon.nist.gov/beacon/2.0/pulse/last"
 
-	v2JSON, err := ioutil.ReadAll(resp.Body)
+	data, err := getRead(v2url)
 
 	if err != nil {
-		log.Fatal(err)
-	}
-
-	if err := resp.Body.Close(); err != nil {
-		log.Fatal(err)
+		return err
 	}
 
 	var v2 struct {
@@ -125,142 +111,127 @@ func nist() {
 		}
 	}
 
-	if err := json.Unmarshal(v2JSON, &v2); err != nil {
-		log.Fatal(err)
+	if err := json.Unmarshal(data, &v2); err != nil {
+		return err
 	}
 
-	fmt.Printf("Src: NIST Beacon v2 (%s)\n ---\n", v2URL)
+	fmt.Printf("Src: NIST Beacon v2 (%s)\n ---\n", v2url)
 	fmt.Printf("%s\n\n", v2.Pulse.OutputValue)
 
-}
-
-func btc() {
-	btcHeightURL := "https://blockchain.info/q/getblockcount"
-	btcBlockURL := "https://blockchain.info/block-height/%d?format=json"
-
-	resp, err := http.Get(btcHeightURL)
-
-	if err != nil {
-		log.Fatal(err)
-	}
+	return nil
 
-	btcHeight, err := ioutil.ReadAll(resp.Body)
-
-	if err != nil {
-		log.Fatal(err)
-	}
+}
 
-	if err := resp.Body.Close(); err != nil {
-		log.Fatal(err)
-	}
+// BTC block hash.
+func btc() error {
+	const (
+		heightUrl = "https://blockchain.info/q/getblockcount"
+		blockUrl  = "https://blockchain.info/block-height/%d?format=json"
+		depth     = 10
+	)
 
-	height, err := strconv.ParseInt(string(btcHeight), 10, 64)
+	data, err := getRead(heightUrl)
 
 	if err != nil {
-		log.Fatal(err)
+		return err
 	}
 
-	const depth = 10
-
-	resp, err = http.Get(fmt.Sprintf(btcBlockURL, height-depth))
+	height, err := strconv.ParseInt(string(data), 10, 64)
 
 	if err != nil {
-		log.Fatal(err)
+		return err
 	}
 
-	btcBlockJSON, err := ioutil.ReadAll(resp.Body)
+	data, err = getRead(fmt.Sprintf(blockUrl, height-depth))
 
 	if err != nil {
-		log.Fatal(err)
+		return err
 	}
 
-	if err := resp.Body.Close(); err != nil {
-		log.Fatal(err)
-	}
-
-	var btcBlock struct {
+	var block struct {
 		Blocks []struct {
 			Hash string
 		}
 	}
 
-	if err := json.Unmarshal(btcBlockJSON, &btcBlock); err != nil {
-		log.Fatal(err)
+	if err := json.Unmarshal(data, &block); err != nil {
+		return err
 	}
 
-	if len(btcBlock.Blocks) == 0 {
-		log.Fatal("no blocks found")
+	if len(block.Blocks) == 0 {
+		return errors.New("no blocks found")
 	}
 
-	fmt.Printf("Src: Blockchain.Info [block depth %d] (%s)\n ---\n",
-		depth, fmt.Sprintf(btcBlockURL, height-depth))
+	fmt.Printf("Src: Blockchain.Info [block depth %d] (%s)\n ---\n", depth,
+		fmt.Sprintf(blockUrl, height-depth))
+	fmt.Printf("%s\n\n", block.Blocks[0].Hash)
 
-	fmt.Printf("%s\n\n", btcBlock.Blocks[0].Hash)
+	return nil
 }
 
-func monero() {
-	monStatURL := "https://moneroblocks.info/api/get_stats"
-	monURL := "https://moneroblocks.info/api/get_block_header/%d"
-
-	resp, err := http.Get(monStatURL)
-
-	if err != nil {
-		log.Fatal(err)
-	}
+// Monero block hash.
+func monero() error {
+	const (
+		statsUrl = "https://moneroblocks.info/api/get_stats"
+		blockUrl = "https://moneroblocks.info/api/get_block_header/%d"
+		depth    = 10
+	)
 
-	monJSON, err := ioutil.ReadAll(resp.Body)
+	data, err := getRead(statsUrl)
 
 	if err != nil {
-		log.Fatal(err)
-	}
-
-	if err := resp.Body.Close(); err != nil {
-		log.Fatal(err)
+		return err
 	}
 
-	var monStats struct {
+	var stats struct {
 		Height int64
 	}
 
-	if err := json.Unmarshal(monJSON, &monStats); err != nil {
-		log.Fatal(err)
+	if err := json.Unmarshal(data, &stats); err != nil {
+		return err
 	}
 
-	depth := int64(10)
-
-	if monStats.Height < depth {
-		log.Fatalf("monStats.Height < %d", depth)
-	}
-
-	resp, err = http.Get(fmt.Sprintf(monURL, monStats.Height-depth))
-
-	if err != nil {
-		log.Fatal(err)
+	if stats.Height < depth {
+		return fmt.Errorf("stats.Height < %d", depth)
 	}
 
-	monJSON, err = ioutil.ReadAll(resp.Body)
+	data, err = getRead(fmt.Sprintf(blockUrl, stats.Height-depth))
 
 	if err != nil {
-		log.Fatal(err)
-	}
-
-	if err := resp.Body.Close(); err != nil {
-		log.Fatal(err)
+		return err
 	}
 
-	var monBlockHdr struct {
+	var block struct {
 		BlockHeader struct {
 			Hash string
 		} `json:"block_header"`
 	}
 
-	if err := json.Unmarshal(monJSON, &monBlockHdr); err != nil {
-		log.Fatal(err)
+	if err := json.Unmarshal(data, &block); err != nil {
+		return err
 	}
 
 	fmt.Printf("Src: Moneroblocks.Info [block depth %d] (%s)\n ---\n",
-		depth,
-		fmt.Sprintf(monURL, monStats.Height-depth))
-	fmt.Printf("%s\n\n", monBlockHdr.BlockHeader.Hash)
+		depth, fmt.Sprintf(blockUrl, stats.Height-depth))
+	fmt.Printf("%s\n\n", block.BlockHeader.Hash)
+
+	return nil
+}
+
+// Make GET request and read body, reducing duplicate ioutil.ReadAll and error
+// checking code.
+func getRead(url string) ([]byte, error) {
+	resp, err := http.Get(url)
+
+	if err != nil {
+		return nil, err
+	}
+
+	data, err := ioutil.ReadAll(resp.Body)
+
+	if err != nil {
+		return nil, err
+	}
 
+	return data, resp.Body.Close()
 }