Browse Source

Implemented paging and improved POW calculation

main
Joop Kiefte 7 months ago committed by GitHub
parent
commit
407a75942f
  1. 152
      main.go
  2. 16
      message/message.go
  3. 12
      message/message_test.go

152
main.go

@ -18,12 +18,13 @@ import (
var LocalMessages = message.Messages{}
var DatabasePath = "infodump.db"
var DB *sql.DB
var MessageCache string
// Readline reads from a buffered stdin and returns the line
func Readline() string {
reader := bufio.NewReader(os.Stdin)
line, _ := reader.ReadString('\n')
return line
return line[:len(line)-1]
}
// MenuElements is a list of options for the menu, consisting of a description and a function
@ -90,10 +91,8 @@ func main() {
{"Start OLN Listener", StartOLNListener},
{"Read Messages", ReadMessages},
{"Write Message", WriteMessage},
{"Sync Messages", SyncMessages},
{"Set IPFS Gateway", SetIPFSGateway},
{"Set Database", SetDatabase},
{"Configure Followed Tags", ConfigureFollowedTags},
{"Sync Messages", SyncMenu},
{"Settings", SettingsMenu},
{"Quit", func() { os.Exit(0) }},
})
@ -103,6 +102,33 @@ func main() {
}
}
// A submenu for all settings
func SettingsMenu() {
// Present the user with a menu
Menu([]MenuElements{
{"Set IPFS Gateway", SetIPFSGateway},
{"Set Database", SetDatabase},
{"Configure Followed Tags", ConfigureFollowedTags},
{"Back", func() {}},
})
}
// A menu for the several parts of Sync:
// saving messages to the local database
// reading messages from the local database
// reading messages from the network
// writing messages to the network
func SyncMenu() {
// Present the user with a menu
Menu([]MenuElements{
{"Save Messages to Database", SaveMessagesToDatabase},
{"Read Messages from Database", ReadMessagesFromDatabase},
{"Read Messages from Network", ReadMessagesFromNetwork},
{"Write Messages to Network", WriteMessagesToNetwork},
{"Back", func() {}},
})
}
// StartOLNListener starts a PubSub listener that listens for messages from the network
// and adds them to LocalMessages
// For this it uses the IPFS gateway and listens on the topic "OLN", as well as
@ -124,7 +150,7 @@ func StartOLNListener() {
}
subs = append(subs, olnsub)
for _, tag := range followedTags {
tagssub, err := myIPFS.PubSubSubscribe("oln-tag-" + tag)
tagssub, err := myIPFS.PubSubSubscribe("oln-" + tag)
if err != nil {
fmt.Println(err)
} else {
@ -197,29 +223,91 @@ func GetMessagesFromDatabase(db *sql.DB) *message.Messages {
return &msgs
}
// ReadMessages shows the messages in LocalMessages sorted by importance, 10 at a time
func ReadMessages() {
// Show all messages in LocalMessages
fmt.Println("Local messages:")
LocalMessages.Each(func(m *message.Message) {
fmt.Println(m.String())
})
// Use LocalMessages to get the messages and get the sorted list of messages
// through the MessageList method
msgs := LocalMessages.MessageList()
// Loop through the messages and print them
for i, m := range msgs {
fmt.Println(m)
if i%10 == 9 {
fmt.Println("Press enter to continue... Type anything to stop")
contp := Readline()
if contp != "" {
return
}
}
}
}
func WriteMessage() {
// Get a message and an urgency from the user.
// The urgency is used to set the strength of the Proof of Work
fmt.Println("Enter a message: ")
m := Readline()
// If there is a message in the MessageCache, ask the user if they want to use it
// If there is no message in the MessageCache, ask the user to write a message
var m string
if MessageCache != "" {
fmt.Println("You wrote a message before that you didn't send yet. Do you want to use that message?")
fmt.Println("The message is:", MessageCache)
fmt.Println("Type 'yes' to use the message, or anything else to write a new message")
usep := Readline()
if usep == "yes" {
fmt.Println("Using message from cache")
m = MessageCache
} else {
fmt.Println("Writing new message")
m = usep
}
} else {
fmt.Println("Write a message:")
m = Readline()
}
fmt.Println("Enter an urgency (higher is stronger but takes longer to produce): ")
var urgency int
fmt.Scanln(&urgency)
// Create a Message object
msg := message.New(m, urgency, time.Now().Unix())
fmt.Println("How many seconds should we wait for the POW to be done? (default is 5): ")
var powtime int
fmt.Scanln(&powtime)
if powtime == 0 {
powtime = 5
}
// Create a new message object
msg, err := message.New(m, urgency, time.Now().Unix(), time.Duration(powtime)*time.Second)
if err != nil {
fmt.Println(err)
fmt.Println("Want to try again with another urgency or timeout? (y/n)")
contp := Readline()
if contp == "y" {
MessageCache = m
WriteMessage()
}
return
}
// MessageCache can be discarded after this point
MessageCache = ""
// Add the message to LocalMessages
LocalMessages.Add(msg)
// Ask if the user wants to write another message or save the messages to the database
fmt.Println("Do you want to write another message? (y/n)")
contp := Readline()
if contp == "y" {
WriteMessage()
} else {
fmt.Println("Do you want to save the messages to the database? (y/n)")
contp := Readline()
if contp == "y" {
SaveMessagesToDatabase()
}
}
}
func SyncMessages() {
// Implementing the Sync Menu options
// SaveMessagesToDatabase, ReadMessagesFromDatabase, ReadMessagesFromNetwork and WriteMessagesToNetwork
// SaveMessagesToDatabase saves the messages in LocalMessages to the database
func SaveMessagesToDatabase() {
// Update the database with the messages in LocalMessages
db := GetDatabase()
// Put the messages in the database
@ -228,26 +316,34 @@ func SyncMessages() {
if err != nil {
fmt.Println(err)
} else {
fmt.Println("Message", m.Stamp(), "synced")
fmt.Println("Message", m.Stamp(), "saved to database")
}
})
}
fmt.Println("Now loading messages from database...")
// ReadMessagesFromDatabase reads the messages from the database and adds them to LocalMessages
func ReadMessagesFromDatabase() {
// Get the messages from the database
messages := GetMessagesFromDatabase(db)
messages := GetMessagesFromDatabase(GetDatabase())
// Add the messages to LocalMessages
LocalMessages.AddMany(messages)
}
fmt.Println("Messages loaded from database")
// ReadMessagesFromNetwork reads the messages from the IPFS network and adds them to LocalMessages
func ReadMessagesFromNetwork() {
// Get the messages from the IPFS network
// TODO: Implement this
}
// WriteMessagesToNetwork writes the messages in LocalMessages to the IPFS network
func WriteMessagesToNetwork() {
// Add the messages to the IPFS network
cid, err := messages.AddToIPFS()
cid, err := LocalMessages.AddToIPFS()
if err != nil {
fmt.Println(err)
} else {
fmt.Println("Messages synced to IPFS: ", cid)
}
// Set LocalMessages to the messages that are in the database
LocalMessages = message.Messages{}
LocalMessages.AddMany(messages)
}
func TestIPFSGateway(gateway string) error {
@ -299,7 +395,7 @@ func SetDatabase() {
fmt.Println("Database path: ", DatabasePath)
fmt.Println("Is this correct? (y/n)")
answer := Readline()
if strings.HasPrefix(answer, "n") {
if answer == "n" {
fmt.Println("Enter the database path: ")
DatabasePath = Readline()
}
@ -313,7 +409,7 @@ func SetDatabase() {
if DB != nil {
fmt.Println("Database already set, overwrite? (y/n)")
answer := Readline()
if strings.HasPrefix(answer, "n") {
if answer == "n" {
return
}
}
@ -321,7 +417,7 @@ func SetDatabase() {
if _, err := os.Stat(DatabasePath); os.IsNotExist(err) {
fmt.Println("Database does not exist, create? (y/n)")
answer := Readline()
if strings.HasPrefix(answer, "n") {
if answer == "n" {
return
}
}

16
message/message.go

@ -64,11 +64,14 @@ func (m *Messages) MessageList() []*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) {
// If it takes too long, return an error
func (msg *Message) ProofOfWork(n int, timeout time.Duration) error {
// Create a local copy of the message and start counting
m := *msg
m.Nonce = 0
start := time.Now()
// Loop until we find a nonce that satisfies the proof of work
// If the nonce is not found within the timeout, return an error
for {
// Increment the nonce and hash the message
m.Nonce++
@ -77,9 +80,14 @@ func (msg *Message) ProofOfWork(n int) {
if CountLeadingZeroes(hash) >= n {
break
}
// If the nonce is not found within the timeout, return an error
if time.Since(start) > timeout {
return fmt.Errorf("proof of work timed out")
}
}
// Set the message to the local copy
*msg = m
return nil
}
// Get the SHA256 hash of a message plus the timestamp plus the nonce as a byte slice
@ -115,10 +123,10 @@ func (m *Message) Lead() int {
return CountLeadingZeroes(m.Hash())
}
func New(msg string, n int, timestamp int64) *Message {
func New(msg string, n int, timestamp int64, timeout time.Duration) (*Message, error) {
m := Message{Message: msg, Timestamp: timestamp}
m.ProofOfWork(n)
return &m
err := m.ProofOfWork(n, timeout)
return &m, err
}
// Map Messages maps the stamp of the message to the message itself

12
message/message_test.go

@ -8,11 +8,11 @@ import (
"git.kiefte.eu/lapingvino/infodump/message"
)
// Test if the code finishes in time
// Test if creating a proof of work of 16 leading zeros finishes in 10 seconds
func TestMessageTimeout(t *testing.T) {
time.AfterFunc(10*time.Second, func() {
t.Error("Test timed out")
})
_ = message.New("Hello World!", 16, time.Now().Unix())
// Success
msg := message.Message{Message: "test", Timestamp: time.Now().Unix()}
err := msg.ProofOfWork(16, 10*time.Second)
if err != nil {
t.Error(err)
}
}

Loading…
Cancel
Save