diff options
author | Joop Kiefte <ikojba@gmail.com> | 2021-12-10 13:01:14 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-10 13:01:14 +0000 |
commit | 7aa71680132984ba729c12af727fddd6d23670d2 (patch) | |
tree | 8cc5be934b5f6a0720245f0f2e1b8f365e26eec7 | |
parent | f0b034381cb18671b9ffe718b01ed429c33fbd12 (diff) |
Implement Proof of Work
-rw-r--r-- | go.mod | 3 | ||||
-rw-r--r-- | main.go | 37 | ||||
-rw-r--r-- | message/message.go | 72 |
3 files changed, 112 insertions, 0 deletions
@@ -0,0 +1,3 @@ +module git.kiefte.eu/lapingvino/infodump + +go 1.17 @@ -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 +} |