aboutsummaryrefslogtreecommitdiff
path: root/message/message.go
blob: aa2a3e58b8234143456ba935fc40f9d421aebb97 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package message

import (
	"bytes"
	"crypto/sha256"
	"encoding/json"
	"fmt"
	"math/bits"
	"time"

	ipfs "github.com/ipfs/go-ipfs-api"
)

var IPFSGateway = "http://localhost:5001" // IPFS Gateway used to connect to the IPFS daemon

// Set up an IPFS instance based on the IPFSGateway
func InitIPFS() *ipfs.Shell {
	return ipfs.NewShell(IPFSGateway)
}

// 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
	Timestamp int64
	Nonce     int
}

// String method for Message: "Message *hash* sent at *human readable timestamp* with nonce *nonce*:\n*message*"
func (m *Message) String() string {
	return fmt.Sprintf("Message %x sent at %s with nonce %d:\n%s", m.Hash(), time.Unix(m.Timestamp, 0).Format(time.RFC3339), m.Nonce, m.Message)
}

// 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 timestamp plus the nonce as a byte slice
func (m *Message) Hash() [32]byte {
	hash := sha256.Sum256([]byte(m.Message + fmt.Sprintf("%d", m.Timestamp) + fmt.Sprintf("%d", 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, timestamp int64) *Message {
	m := Message{Message: msg, Timestamp: timestamp}
	m.ProofOfWork(n)
	return &m
}

// Map Messages maps the stamp of the message to the message itself
type Messages map[string]*Message

// Add a message to the Messages map
func (m *Messages) Add(msg *Message) {
	(*m)[msg.Stamp()] = msg
}

// Remove a message from the Messages map by stamp
func (m *Messages) Remove(stamp string) {
	delete(*m, stamp)
}

// Return a JSON representation of the Messages map
func (m *Messages) JSON() ([]byte, error) {
	return json.Marshal(m)
}

// Add the messages as a JSON object to IPFS
func (m *Messages) AddToIPFS() (string, error) {
	json, err := m.JSON()
	if err != nil {
		return "", err
	}
	return AddJSONToIPFS(json)
}

func AddJSONToIPFS(json []byte) (string, error) {
	// Create an IPFS instance based on the IPFSGateway
	myIPFS := ipfs.NewShell(IPFSGateway)
	// Turn the JSON into a Reader and add it to IPFS
	cid, err := myIPFS.Add(bytes.NewReader(json))
	if err != nil {
		return "", err
	}
	return cid, nil
}