pof

Proof of Freshness: collate proof of an authorship date.

Log | Files | << Repositories


tree 09246c4e9ff361b22eeac92ab5795657f860c85b
parent a1c61a95c8bd75161699bc4edccc2d2ca733944f
author esote <esote.net@gmail.com> 1563406481 -0500
committer esote <esote.net@gmail.com> 1563406481 -0500
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQTXAxYDuIzimYoNSPuhTmRAjzzC8gUCXS+wlAAKCRChTmRAjzzC
 8j+3AP4uU3vl1wCvxDj3j+Lx4xCILHmz+qg1ELjXufJFZqLnmwD/Sgzoc2jUKqAa
 WGZ1GklaolhpArDZtP4o5+85EQSbxQQ=
 =IUz1
 -----END PGP SIGNATURE-----

Reorganize sources

 README    |  80 ++++++++++----------
 arxiv.go  |  74 +++++++++++++++++++
 crypto.go | 106 ++++++++++++++++++++++++++
 news.go   |  72 ++++++++++++++++++
 nist.go   |  33 +++++++++
 pof.go    | 249 ++------------------------------------------------------------
 6 files changed, 332 insertions(+), 282 deletions(-)

diff --git a/README b/README
index 699a73c..3db93cc 100644
--- a/README
+++ b/README
@@ -4,27 +4,49 @@ Proof of Freshness. Run with:
 	$ cd $GOPATH/src/github.com/esote/pof && go run pof.go
 
 Collate values from multiple sources. Used to prove something cannot have been
-prepared ahead of the date specified within an hour or so.
+prepared ahead of the date specified within a day or so.
 
 Current sources:
-	- News (selects top 5 articles)
-		- Der Spiegel International
-		- New York Times World News
-		- BBC World News
-		- Reuters World News
-		- The Economist Latest Updates
-	- NIST randomness beacon v2
-	- Bitcoin block hash, depth 10
-	- Monero block hash, depth 10
 	- arXiv recently submitted preprints
+	- Cryptocurrency block hashes (depth 10)
+		- Bitcoin
+		- Monero
+	- News (5 latest articles)
+		- Der Spiegel international
+		- New York Times world news
+		- BBC world news
+		- Reuters world news
+		- The Economist latest updates
+	- NIST beacon v2
 
 Inspired by the proof of freshness used in Qubes OS's canaries.
 
 Example output:
 
-	Date: 2019-07-17 22:57 UTC
+	Date: 2019-07-17 23:32 UTC
 
-	Src: News: SPIEGEL ONLINE - International (https://www.spiegel.de/international/index.rss)
+	Src: arXiv (https://export.arxiv.org/api/query?search_query=all&sortBy=submittedDate&sortOrder=descending&max_results=10)
+	 ---
+	On Higher Dimensional Generalized Kuramoto Oscillator Systems (Max Lipton, et al., 2019-07-16T17:19:55Z)
+	Higher genus relative Gromov--Witten theory and DR-cycles (Honglu Fan, et al., 2019-07-16T17:03:16Z)
+	Measuring I2P Censorship at a Global Scale (Nguyen Phong Hoang, et al., 2019-07-16T16:57:22Z)
+	On the smallest singular value of multivariate Vandermonde matrices with cluster... (Stefan Kunis, et al., 2019-07-16T16:55:13Z)
+	Primarily Undergraduate Institutions and the Astronomy Community (Joseph Ribaudo, et al., 2019-07-16T16:53:41Z)
+	Measurement of the jet mass in high transverse momentum $Z(\rightarrow b\overlin... (ATLAS Collaboration, 2019-07-16T16:03:43Z)
+	On The Termination of a Flooding Process (Walter Hussak, et al., 2019-07-16T15:45:12Z)
+	Equilibrium Microcanonical Annealing for First-Order Phase Transitions (Nathan Rose, et al., 2019-07-16T15:25:52Z)
+	Minimisation of Event Structures (Paolo Baldan, et al., 2019-07-16T14:48:54Z)
+	Serre's Property (FA) for automorphism groups of free products (Naomi Andrew, 2019-07-16T14:14:11Z)
+
+	Src: Blockchain.Info [depth 10] (https://blockchain.info/block-height/585864?format=json)
+	 ---
+	0000000000000000001d7805ccdfcd4f91d491da214beedc9a555c5b134ec3d9
+
+	Src: Moneroblocks.Info [depth 10] (https://moneroblocks.info/api/get_block_header/1880770)
+	 ---
+	f003b6467d8379c8d70038b6798bf694fad0fcc813e1aa17ab34f33436ac9d22
+
+	Src: Der Spiegel international (https://www.spiegel.de/international/index.rss)
 	 ---
 	The New European Commission President: Von Der Leyen's Election Is a Big Moment for Europe
 	'Euro Orphans': The Romanian Children Growing up Without Their Parents
@@ -32,7 +54,7 @@ Example output:
 	A Question of Trust: Ursula Von Der Leyen Recruits Team to Win Over Brussels
 	Feeding People With Science: Plant Researchers Brace for Population Explosion
 
-	Src: News: NYT > World News (https://rss.nytimes.com/services/xml/rss/nyt/World.xml)
+	Src: New York Times world news (https://rss.nytimes.com/services/xml/rss/nyt/World.xml)
 	 ---
 	Ebola Outbreak in Congo Is Declared a Global Health Emergency
 	A Prosperous China Says  Men Preferred,  and Women Lose
@@ -40,15 +62,15 @@ Example output:
 	As Iran vs. West Tensions Rise, So Does Uncertainty Over Prisoners
 	What Kind of Peace Talks Are These : On the Front Lines of a 17-Year War
 
-	Src: News: BBC News - World (https://feeds.bbci.co.uk/news/world/rss.xml)
+	Src: BBC world news (https://feeds.bbci.co.uk/news/world/rss.xml)
 	 ---
 	El Chapo trial: Mexican drug lord Joaqu n Guzm n gets life in prison
 	DR Congo Ebola outbreak declared global health emergency
 	Opioid crisis: US drug overdose deaths drop by 5.1%
+	Kevin Spacey: Massachusetts prosecutors drop sex assault case
 	Dolphin megapod spotted off California coast swimming alongside boat
-	Battle of Waterloo: Excavation unearths amputated limbs
 
-	Src: News: Reuters: World News (http://feeds.reuters.com/reuters/worldnews)
+	Src: Reuters world news (http://feeds.reuters.com/reuters/worldnews)
 	 ---
 	U.S. removing Turkey from F-35 program after its Russian missile defense purchase
 	Croatia PM reshuffles cabinet, proposes six new ministers
@@ -56,7 +78,7 @@ Example output:
 	UK ministers could resign in battle over suspending parliament: BBC reporter
 	Criticizing populism, Britain's May has Brexit advice for successor
 
-	Src: News: Latest Updates (https://www.economist.com/latest/rss.xml)
+	Src: The Economist latest updates (https://www.economist.com/latest/rss.xml)
 	 ---
 	The next giant leap for mankind
 	Can Rory McIlroy win on his home turf?
@@ -64,27 +86,7 @@ Example output:
 	Unreliable evidence a forensic look at identification
 	The industry and poetry of David Smith
 
-	Src: NIST Beacon v2 (https://beacon.nist.gov/beacon/2.0/pulse/last)
+	Src: NIST beacon v2 (https://beacon.nist.gov/beacon/2.0/pulse/last)
 	 ---
-	019BB7134A0B15126A4677E81598EDFA8780AAD3B92B4950026EFB742BCE4EFE94BC6738772DB21D1E560CCBEC8648E01A4BE533EE03B8B4E92C774DCB53549F
+	7DADB246FF3F72466A83D24920EACFDEE2D0F035A226C664DD614323A0B7F8153A0FC3130128D40A2E270014BFB8FCF676296B39679C690B719C314C1B31A23F
 
-	Src: Blockchain.Info [block depth 10] (https://blockchain.info/block-height/585860?format=json)
-	 ---
-	00000000000000000004cdf0df4abd8b7a4d93d80d00b8349c2307d8903187ed
-
-	Src: Moneroblocks.Info [block depth 10] (https://moneroblocks.info/api/get_block_header/1880753)
-	 ---
-	e19dd23ad944ba4dc467a9cadb0e8db3c207e52c67349cc285302359c8eb6f0a
-
-	Src: arXiv recently submitted (https://export.arxiv.org/api/query?search_query=all&sortBy=submittedDate&sortOrder=descending&max_results=10)
-	 ---
-	On Higher Dimensional Generalized Kuramoto Oscillator Systems (Max Lipton, et al., 2019-07-16T17:19:55Z)
-	Higher genus relative Gromov--Witten theory and DR-cycles (Honglu Fan, et al., 2019-07-16T17:03:16Z)
-	Measuring I2P Censorship at a Global Scale (Nguyen Phong Hoang, et al., 2019-07-16T16:57:22Z)
-	On the smallest singular value of multivariate Vandermonde matrices with cluster... (Stefan Kunis, et al., 2019-07-16T16:55:13Z)
-	Primarily Undergraduate Institutions and the Astronomy Community (Joseph Ribaudo, et al., 2019-07-16T16:53:41Z)
-	Measurement of the jet mass in high transverse momentum $Z(\rightarrow b\overlin... (ATLAS Collaboration, 2019-07-16T16:03:43Z)
-	On The Termination of a Flooding Process (Walter Hussak, et al., 2019-07-16T15:45:12Z)
-	Equilibrium Microcanonical Annealing for First-Order Phase Transitions (Nathan Rose, et al., 2019-07-16T15:25:52Z)
-	Minimisation of Event Structures (Paolo Baldan, et al., 2019-07-16T14:48:54Z)
-	Serre's Property (FA) for automorphism groups of free products (Naomi Andrew, 2019-07-16T14:14:11Z)
diff --git a/arxiv.go b/arxiv.go
new file mode 100644
index 0000000..b17f2a0
--- /dev/null
+++ b/arxiv.go
@@ -0,0 +1,74 @@
+package main
+
+import (
+	"encoding/xml"
+	"fmt"
+	"strings"
+)
+
+// arXiv recently submitted preprints.
+func arxiv() error {
+	const (
+		queryUrl = "https://export.arxiv.org/api/query?" +
+			"search_query=all&sortBy=submittedDate&" +
+			"sortOrder=descending&max_results=%d"
+		count  = 10
+		maxlen = 80
+	)
+
+	url := fmt.Sprintf(queryUrl, count)
+
+	data, err := getRead(url)
+
+	if err != nil {
+		return err
+	}
+
+	var arxiv struct {
+		Entries []struct {
+			Published string   `xml:"published"`
+			Title     string   `xml:"title"`
+			Authors   []string `xml:"author>name"`
+		} `xml:"entry"`
+	}
+
+	if err := xml.Unmarshal(data, &arxiv); err != nil {
+		return err
+	}
+
+	if len(arxiv.Entries) != count {
+		return fmt.Errorf("response length mismatched %d", count)
+	}
+
+	heading("arXiv", url)
+
+	for _, entry := range arxiv.Entries {
+		entry.Title = nonascii.ReplaceAllString(entry.Title, " ")
+		entry.Title = strings.ReplaceAll(entry.Title, "\n ", "")
+
+		if len(entry.Title) > maxlen {
+			entry.Title = entry.Title[:maxlen] + "..."
+		}
+
+		entry.Title = strings.TrimSpace(entry.Title)
+
+		var author string
+
+		if len(entry.Authors) > 0 {
+			author = entry.Authors[0]
+		}
+
+		if len(entry.Authors) > 1 {
+			author += ", et al."
+		}
+
+		author = strings.TrimSpace(nonascii.ReplaceAllString(author, " "))
+
+		fmt.Printf("%s (%s, %s)\n", entry.Title, author,
+			entry.Published)
+	}
+
+	fmt.Println()
+
+	return nil
+}
diff --git a/crypto.go b/crypto.go
new file mode 100644
index 0000000..cebf928
--- /dev/null
+++ b/crypto.go
@@ -0,0 +1,106 @@
+package main
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"strconv"
+)
+
+// Bitcoin block hash.
+func btc() error {
+	const (
+		heightUrl = "https://blockchain.info/q/getblockcount"
+		blockUrl  = "https://blockchain.info/block-height/%d?format=json"
+		depth     = 10
+	)
+
+	data, err := getRead(heightUrl)
+
+	if err != nil {
+		return err
+	}
+
+	height, err := strconv.ParseInt(string(data), 10, 64)
+
+	if err != nil {
+		return err
+	}
+
+	url := fmt.Sprintf(blockUrl, height-depth)
+
+	data, err = getRead(url)
+
+	if err != nil {
+		return err
+	}
+
+	var block struct {
+		Blocks []struct {
+			Hash string
+		}
+	}
+
+	if err := json.Unmarshal(data, &block); err != nil {
+		return err
+	}
+
+	if len(block.Blocks) == 0 {
+		return errors.New("no blocks found")
+	}
+
+	heading(fmt.Sprintf("Blockchain.Info [depth %d]", depth), url)
+	fmt.Printf("%s\n\n", block.Blocks[0].Hash)
+
+	return nil
+}
+
+// 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
+	)
+
+	data, err := getRead(statsUrl)
+
+	if err != nil {
+		return err
+	}
+
+	var stats struct {
+		Height int64
+	}
+
+	if err := json.Unmarshal(data, &stats); err != nil {
+		return err
+	}
+
+	if stats.Height < depth {
+		return fmt.Errorf("stats.Height < %d", depth)
+	}
+
+	url := fmt.Sprintf(blockUrl, stats.Height-depth)
+
+	data, err = getRead(url)
+
+	if err != nil {
+		return err
+	}
+
+	var block struct {
+		BlockHeader struct {
+			Hash string
+		} `json:"block_header"`
+	}
+
+	if err := json.Unmarshal(data, &block); err != nil {
+		return err
+	}
+
+	heading(fmt.Sprintf("Moneroblocks.Info [depth %d]", depth), url)
+	fmt.Printf("%s\n\n", block.BlockHeader.Hash)
+
+	return nil
+}
diff --git a/news.go b/news.go
new file mode 100644
index 0000000..5dbc91f
--- /dev/null
+++ b/news.go
@@ -0,0 +1,72 @@
+package main
+
+import (
+	"encoding/xml"
+	"fmt"
+	"strings"
+)
+
+// International news feeds.
+func news() error {
+	const count = 5
+
+	type source struct {
+		name string
+		url  string
+	}
+
+	sources := [...]source{
+		{
+			"Der Spiegel international",
+			"https://www.spiegel.de/international/index.rss",
+		},
+		{
+			"New York Times world news",
+			"https://rss.nytimes.com/services/xml/rss/nyt/World.xml",
+		},
+		{
+			"BBC world news",
+			"https://feeds.bbci.co.uk/news/world/rss.xml",
+		},
+		{
+			"Reuters world news",
+			"http://feeds.reuters.com/reuters/worldnews",
+		},
+		{
+			"The Economist latest updates",
+			"https://www.economist.com/latest/rss.xml",
+		},
+	}
+
+	for _, source := range sources {
+		data, err := getRead(source.url)
+
+		if err != nil {
+			return err
+		}
+
+		// Structure of RSS feed, exposing only the useful fields
+		var rss struct {
+			Name   string   `xml:"channel>title"`
+			Titles []string `xml:"channel>item>title"`
+		}
+
+		if err = xml.Unmarshal(data, &rss); err != nil {
+			return err
+		}
+
+		if len(rss.Titles) < count {
+			return fmt.Errorf("couldn't find %d articles", count)
+		}
+
+		heading(source.name, source.url)
+
+		for i := 0; i < count; i++ {
+			fmt.Println(strings.TrimSpace(nonascii.ReplaceAllString(rss.Titles[i], " ")))
+		}
+
+		fmt.Println()
+	}
+
+	return nil
+}
diff --git a/nist.go b/nist.go
new file mode 100644
index 0000000..cb84d8a
--- /dev/null
+++ b/nist.go
@@ -0,0 +1,33 @@
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+)
+
+// NIST beacon v2.
+func nist() error {
+	const v2url = "https://beacon.nist.gov/beacon/2.0/pulse/last"
+
+	data, err := getRead(v2url)
+
+	if err != nil {
+		return err
+	}
+
+	var v2 struct {
+		Pulse struct {
+			OutputValue string
+		}
+	}
+
+	if err := json.Unmarshal(data, &v2); err != nil {
+		return err
+	}
+
+	heading("NIST beacon v2", v2url)
+	fmt.Printf("%s\n\n", v2.Pulse.OutputValue)
+
+	return nil
+
+}
diff --git a/pof.go b/pof.go
index d2d7836..df64c38 100644
--- a/pof.go
+++ b/pof.go
@@ -1,16 +1,11 @@
 package main
 
 import (
-	"encoding/json"
-	"encoding/xml"
-	"errors"
 	"fmt"
 	"io/ioutil"
 	"log"
 	"net/http"
 	"regexp"
-	"strconv"
-	"strings"
 	"time"
 )
 
@@ -20,11 +15,11 @@ func main() {
 		time.Now().UTC().Format("2006-01-02 15:04 MST"))
 
 	sources := [...]func() error{
-		news,
-		nist,
+		arxiv,
 		btc,
 		monero,
-		arxiv,
+		news,
+		nist,
 	}
 
 	for _, source := range sources {
@@ -34,242 +29,10 @@ func main() {
 	}
 }
 
-var re = regexp.MustCompile(`[^[:ascii:]]+`)
-
-// International news feeds.
-func news() error {
-	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",
-		"http://feeds.reuters.com/reuters/worldnews",
-		"https://www.economist.com/latest/rss.xml",
-	}
-
-	const count = 5
-
-	for _, url := range urls {
-		data, err := getRead(url)
-
-		if err != nil {
-			return err
-		}
-
-		// Structure of an RSS feed, exposing only the useful fields
-		var rss struct {
-			Name   string   `xml:"channel>title"`
-			Titles []string `xml:"channel>item>title"`
-		}
-
-		if err = xml.Unmarshal(data, &rss); err != nil {
-			return err
-		}
-
-		if len(rss.Titles) < count {
-			return fmt.Errorf("couldn't find %d articles", count)
-		}
-
-		fmt.Printf("Src: News: %s (%s)\n ---\n",
-			strings.TrimSpace(re.ReplaceAllString(rss.Name, " ")),
-			url)
-
-		for i := 0; i < count; i++ {
-			fmt.Println(strings.TrimSpace(re.ReplaceAllString(rss.Titles[i], " ")))
-		}
-
-		fmt.Println()
-	}
-
-	return nil
-}
-
-// NIST randomness beacon v2.
-func nist() error {
-	const v2url = "https://beacon.nist.gov/beacon/2.0/pulse/last"
-
-	data, err := getRead(v2url)
-
-	if err != nil {
-		return err
-	}
-
-	var v2 struct {
-		Pulse struct {
-			OutputValue string
-		}
-	}
-
-	if err := json.Unmarshal(data, &v2); err != nil {
-		return err
-	}
-
-	fmt.Printf("Src: NIST Beacon v2 (%s)\n ---\n", v2url)
-	fmt.Printf("%s\n\n", v2.Pulse.OutputValue)
-
-	return nil
-
-}
-
-// BTC block hash.
-func btc() error {
-	const (
-		heightUrl = "https://blockchain.info/q/getblockcount"
-		blockUrl  = "https://blockchain.info/block-height/%d?format=json"
-		depth     = 10
-	)
-
-	data, err := getRead(heightUrl)
-
-	if err != nil {
-		return err
-	}
-
-	height, err := strconv.ParseInt(string(data), 10, 64)
-
-	if err != nil {
-		return err
-	}
-
-	data, err = getRead(fmt.Sprintf(blockUrl, height-depth))
-
-	if err != nil {
-		return err
-	}
-
-	var block struct {
-		Blocks []struct {
-			Hash string
-		}
-	}
-
-	if err := json.Unmarshal(data, &block); err != nil {
-		return err
-	}
-
-	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(blockUrl, height-depth))
-	fmt.Printf("%s\n\n", block.Blocks[0].Hash)
-
-	return nil
-}
-
-// 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
-	)
-
-	data, err := getRead(statsUrl)
-
-	if err != nil {
-		return err
-	}
-
-	var stats struct {
-		Height int64
-	}
-
-	if err := json.Unmarshal(data, &stats); err != nil {
-		return err
-	}
-
-	if stats.Height < depth {
-		return fmt.Errorf("stats.Height < %d", depth)
-	}
-
-	data, err = getRead(fmt.Sprintf(blockUrl, stats.Height-depth))
-
-	if err != nil {
-		return err
-	}
-
-	var block struct {
-		BlockHeader struct {
-			Hash string
-		} `json:"block_header"`
-	}
-
-	if err := json.Unmarshal(data, &block); err != nil {
-		return err
-	}
-
-	fmt.Printf("Src: Moneroblocks.Info [block depth %d] (%s)\n ---\n",
-		depth, fmt.Sprintf(blockUrl, stats.Height-depth))
-	fmt.Printf("%s\n\n", block.BlockHeader.Hash)
-
-	return nil
-}
-
-// arXiv recently published preprints.
-func arxiv() error {
-	const (
-		queryUrl = "https://export.arxiv.org/api/query?" +
-			"search_query=all&sortBy=submittedDate&" +
-			"sortOrder=descending&max_results=%d"
-		count  = 10
-		maxlen = 80
-	)
-
-	data, err := getRead(fmt.Sprintf(queryUrl, count))
-
-	if err != nil {
-		return err
-	}
-
-	var arxiv struct {
-		Entries []struct {
-			Published string   `xml:"published"`
-			Title     string   `xml:"title"`
-			Authors   []string `xml:"author>name"`
-		} `xml:"entry"`
-	}
-
-	if err := xml.Unmarshal(data, &arxiv); err != nil {
-		return err
-	}
-
-	if len(arxiv.Entries) != count {
-		return fmt.Errorf("response length mismatched %d", count)
-	}
-
-	fmt.Printf("Src: arXiv recently submitted (%s)\n ---\n",
-		fmt.Sprintf(queryUrl, count))
-
-	for _, entry := range arxiv.Entries {
-		entry.Title = re.ReplaceAllString(entry.Title, " ")
-		entry.Title = strings.ReplaceAll(entry.Title, "\n ", "")
-
-		if len(entry.Title) > maxlen {
-			entry.Title = entry.Title[:maxlen] + "..."
-		}
-
-		entry.Title = strings.TrimSpace(entry.Title)
-
-		var author string
-
-		if len(entry.Authors) > 0 {
-			author = entry.Authors[0]
-		}
-
-		if len(entry.Authors) > 1 {
-			author += ", et al."
-		}
-
-		author = strings.TrimSpace(re.ReplaceAllString(author, " "))
-
-		fmt.Printf("%s (%s, %s)\n", entry.Title, author,
-			entry.Published)
-	}
-
-	fmt.Println()
+var nonascii = regexp.MustCompile(`[^[:ascii:]]+`)
 
-	return nil
+func heading(title string, url string) {
+	fmt.Printf("Src: %s (%s)\n ---\n", title, url)
 }
 
 // Make GET request and read body, reducing duplicate ioutil.ReadAll and error