aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoop Kiefte <ikojba@gmail.com>2021-12-10 13:01:14 +0000
committerGitHub <noreply@github.com>2021-12-10 13:01:14 +0000
commit7aa71680132984ba729c12af727fddd6d23670d2 (patch)
tree8cc5be934b5f6a0720245f0f2e1b8f365e26eec7
parentf0b034381cb18671b9ffe718b01ed429c33fbd12 (diff)
Implement Proof of Work
-rw-r--r--go.mod3
-rw-r--r--main.go37
-rw-r--r--message/message.go72
3 files changed, 112 insertions, 0 deletions
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..7c42f9f
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module git.kiefte.eu/lapingvino/infodump
+
+go 1.17
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..b4a0f20
--- /dev/null
+++ b/main.go
@@ -0,0 +1,37 @@
+// Infodump is a commandline social network that works over IPFS and communicates through PubSub
+package main
+
+import (
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+
+ "git.kiefte.eu/lapingvino/infodump/message"
+)
+
+// pubsub "github.com/libp2p/go-libp2p-pubsub"
+// ipfs "github.com/ipfs/go-ipfs-api"
+
+// Main function: get the minimal Proof of Work for a message as the first argument, the message as the rest of the argument and return the stamp
+func main() {
+ // Check if the user provided a number and a message
+ if len(os.Args) < 3 {
+ fmt.Println("Please provide a number and a message")
+ os.Exit(1)
+ }
+ // Get the number of initial zeroes from the user
+ n, err := strconv.Atoi(os.Args[1])
+ if err != nil {
+ fmt.Println("Please provide a number")
+ os.Exit(1)
+ }
+ // Get the message from the user
+ msg := strings.Join(os.Args[2:], " ")
+ // Create a new message
+ m := message.New(msg, n)
+ // Print the stamp
+ fmt.Println(m.Stamp())
+ // Print the message, nonce and number of leading zeroes
+ fmt.Println(m.Message, m.Nonce, m.Lead())
+}
diff --git a/message/message.go b/message/message.go
new file mode 100644
index 0000000..14e9cac
--- /dev/null
+++ b/message/message.go
@@ -0,0 +1,72 @@
+package message
+
+import (
+ "crypto/sha256"
+ "fmt"
+ "math/bits"
+)
+
+// Messages on Infodump use a "stamp" using the hashcash algorithm to prevent spam and enable storing messages by importance
+// The Message type contains the message itself and a nonce that is used to verify the stamp
+type Message struct {
+ Message string
+ Nonce int
+}
+
+// Proof of Work: Find the nonce for a message by hashing the message and checking for at least n initial zeroes in the binary representation of the resulting hash
+func (msg *Message) ProofOfWork(n int) {
+ // Create a local copy of the message and start counting
+ m := *msg
+ m.Nonce = 0
+ // Loop until we find a nonce that satisfies the proof of work
+ for {
+ // Increment the nonce and hash the message
+ m.Nonce++
+ hash := m.Hash()
+ // If the hash has at least n initial zeroes, we have found a valid nonce
+ if CountLeadingZeroes(hash) >= n {
+ break
+ }
+ }
+ // Set the message to the local copy
+ *msg = m
+}
+
+// Get the SHA256 hash of a message plus the nonce as a byte slice
+func (m *Message) Hash() [32]byte {
+ hash := sha256.Sum256([]byte(fmt.Sprintf("%s%d", m.Message, m.Nonce)))
+ return hash
+}
+
+// Bitwise count leading zeroes in a byte slice
+func CountLeadingZeroes(b [32]byte) int {
+ count := 0
+ for _, v := range b {
+ // If the byte is zero, that means there are 8 leading zeroes and we need to continue counting
+ if v == 0 {
+ count += 8
+ } else { // Otherwise, we add the number of leading zeroes in the byte and stop counting
+ // Bitwise count leading zeroes in the byte
+ count += bits.LeadingZeros8(v)
+ // If the byte is not zero, we can stop counting
+ break
+ }
+ }
+ return count
+}
+
+// Get the stamp of the message
+func (m *Message) Stamp() string {
+ return fmt.Sprintf("%x", m.Hash())
+}
+
+// Lead is a method that returns the number of leading zeroes in the hash of a message plus its nonce
+func (m *Message) Lead() int {
+ return CountLeadingZeroes(m.Hash())
+}
+
+func New(msg string, n int) *Message {
+ m := Message{Message: msg}
+ m.ProofOfWork(n)
+ return &m
+}