-
Notifications
You must be signed in to change notification settings - Fork 371
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: WIP GNO mail (no render) #641
base: master
Are you sure you want to change the base?
Changes from all commits
23054c2
5859d98
fd7295d
f84d5d3
9780eb0
70692c4
6ce733f
de3b1d1
d287870
6b26a3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
This is a very specific app. | ||
|
||
But the app could also be envisionned to sit on a system instead. | ||
If mail is a particular type of message, and a mail dapp is a user | ||
of a more universal messaging thing, then it brings some advantages. | ||
|
||
This mail app would then take just a few lines. | ||
And it would change the way we do things. | ||
|
||
For example, orders in p/demo/releases, DAO orders and so on could be other kind of | ||
messages. This way, it would be possible to search and have generic tools that | ||
operate on all point-to-point messages without reimplementing everything all | ||
the time. | ||
|
||
You could search inside the release notes emitted by a certain user, | ||
during a certain year. Or searching at the orders emitted by a DAO that contain | ||
a certain text. You could watch for a certain filter on a certain type of | ||
message coming from some organization. (While this can end-up being costly, we | ||
can imagine ways it can be made profitable, or growth-sustainable, instead) | ||
|
||
Therefore it would be possible to realize more interoperable systems. | ||
|
||
But not all point-to-point system have the same requirements. | ||
Some are producer-consumer, some are archive systems with a certain retention. | ||
Meaning a certain amount of configuration is required for the dapps. | ||
|
||
So it's a complex but interesting question. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Design choices | ||
|
||
At this point | ||
|
||
* mailbox automatically and freely created on both sides when a mail is sent. | ||
* sender needs to pay stamps, they would go to the realm which will be managed by a DAO to finance new tool | ||
* mails are unencrypted | ||
|
||
I assume people would actually not mind paying for stamps if price is reasonnable. | ||
for example it was atom, at 1atom=12usd, I would be okay to pay 0.25$=0.02atom per | ||
mail sent (The time spent redacting a mail costs more than the stamps; and | ||
it would feel less of a waste when I click the button if it costed money). | ||
|
||
Do we still need an admin? Yes. Maybe see p/acl | ||
Is it a public service? I think it can be a DAO. So semi-public? | ||
|
||
Encryption is a huge topic for GNO and not possible at the moment. | ||
|
||
## Various possible ideas | ||
|
||
Things like: | ||
|
||
TUI - there is nothing in a Render() now | ||
Respond to mails | ||
Mail attachment | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
GNO mail | ||
a demo | ||
|
||
📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 | ||
|
||
A mailbox connects you to the GNO mail system. | ||
One day maybe GNO mail v.419 can be | ||
used all over the Cosmos. | ||
|
||
📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 📪 | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package mail | ||
|
||
// A private structure to hold mails | ||
// current impl. is an array | ||
// RFC Would a ringbuffer with a certain capacity, and a "keep" feature | ||
// be better, I wonder. | ||
|
||
type container struct { | ||
mails []*Mail | ||
} | ||
|
||
func newContainer() *container { return &container{[]*Mail{}} } | ||
func newContainerWith(a []*Mail) *container { return &container{a} } | ||
|
||
func (ctr *container) push(mail *Mail) { | ||
ctr.mails = append(ctr.mails, mail) | ||
} | ||
|
||
func (ctr *container) filter(preds ...Predicate) []*Mail { | ||
a := []*Mail{} | ||
for _, mail := range ctr.mails { | ||
if mail.Satisfies(preds) { | ||
a = append(a, mail) | ||
} | ||
} | ||
return a | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// PKGPATH: gno.land/r/demo/mail_test | ||
package mail_test | ||
|
||
import ( | ||
"std" | ||
"strconv" | ||
"strings" | ||
"time" | ||
|
||
"gno.land/p/demo/avl" | ||
"gno.land/p/demo/testutils" | ||
"gno.land/r/demo/mail" | ||
) | ||
|
||
// Send mail, a great movie - SYNOPSIS | ||
// Starring "dude" (the OrigCaller) and "sanders" | ||
// ------------------------------------------------------------------ | ||
// Let there be some dude who wants to send mail: | ||
// .dude gets some money, so he can pay for stamps | ||
// .dude sent a mail to sanders | ||
// .now he has a mailbox, so does sanders | ||
// .mail got received by sanders | ||
// .mail also gets saved in the outbox | ||
// .the realm got money from the stamps | ||
// .sanders still has 0gno | ||
|
||
// TODO send more emails. | ||
// TODO mail.Id is not checked | ||
|
||
func init() {} | ||
|
||
func main() { | ||
var ( | ||
dude = testutils.TestAddress("dude") | ||
sanders = testutils.TestAddress("sanders") | ||
banker = std.GetBanker(std.BankerTypeReadonly) | ||
) | ||
std.TestSetOrigCaller(dude) | ||
std.TestSetOrigSend(std.Coins{{mail.Fee.Denom, mail.Fee.Amount}}, nil) | ||
coinsBefore := banker.GetCoins(std.GetOrigPkgAddr()).AmountOf(mail.Fee.Denom) | ||
coins := std.Coins{{mail.Fee.Denom, 10 * mail.Fee.Amount}} | ||
std.TestIssueCoins(dude, coins) | ||
if banker.GetCoins(dude).AmountOf(mail.Fee.Denom) == 0 { | ||
panic(".dude luckily gets some money, so he can pay for stamps") | ||
} | ||
// ------------------------------------------ | ||
mail.SendMail(sanders, "hi", "this is the dude") | ||
// ------------------------------------------ | ||
if !mail.HasAddressMailbox(dude) { | ||
panic(".now he has a mailbox") | ||
} | ||
if !mail.HasAddressMailbox(sanders) { | ||
panic(".and sanders also has a mailbox") | ||
} | ||
dudebox := mail.MustMailbox(dude) | ||
sandersbox := mail.MustMailbox(sanders) | ||
if len(sandersbox.Find(mail.RecipientIs{sanders})) < 1 { | ||
panic(".mail got received by sanders") | ||
} | ||
if len(sandersbox.Find(mail.RecipientIs{sanders}, mail.SenderIs{dude})) < 1 { | ||
panic(".mail got received by sanders") | ||
} | ||
if len(dudebox.Find(mail.SenderIs{dude}, mail.RecipientIs{sanders})) < 1 { | ||
panic(".mail to sanders is in large outbox, by date") | ||
} | ||
if len(sandersbox.ReceivedFrom(dude)) < 1 { | ||
panic(".mail got received by sanders") | ||
} | ||
if len(dudebox.SentTo(sanders)) < 1 { | ||
panic(".mail also saved in outbox") | ||
} | ||
coinsAfter := banker.GetCoins(std.GetOrigPkgAddr()).AmountOf(mail.Fee.Denom) | ||
if coinsAfter-coinsBefore != mail.Fee.Amount { | ||
panic(".realm received the expected amount from the stamps: recvd=" + strconv.Itoa(int(coinsAfter-coinsBefore))) | ||
} | ||
if banker.GetCoins(sanders).AmountOf(mail.Fee.Denom) != 0 { | ||
panic(".sanders still has 0gno") | ||
} | ||
} | ||
|
||
// Output: | ||
|
||
// Error: |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package mail | ||
|
||
import ( | ||
"std" | ||
"strings" | ||
"time" | ||
|
||
"gno.land/p/demo/ufmt" | ||
) | ||
|
||
type Mail struct { | ||
id int | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure we should be using an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Me neither.
As it's WIP and we have staging testnets I think we can keep an int for the time being. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's use |
||
topic string | ||
body string | ||
time time.Time | ||
sender std.Address | ||
recipient std.Address | ||
} | ||
|
||
func newMail(topic, body string, sender std.Address, recipient std.Address) *Mail { | ||
return &Mail{counter + 1, topic, body, time.Now(), sender, recipient} | ||
} | ||
|
||
func (mail *Mail) Satisfies(preds []Predicate) bool { | ||
for _, pred := range preds { | ||
if !pred.Satisfy(mail) { | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
|
||
func (mail *Mail) Contains(s string) bool { | ||
s = strings.ToLower(s) | ||
return strings.Contains(strings.ToLower(mail.topic), s) || strings.Contains(strings.ToLower(mail.body), s) | ||
} | ||
|
||
// time key for avl, like 1234567890_<counter> | ||
func (mail *Mail) GetTimeKey() string { | ||
return ufmt.Sprintf("%d_%d", time.Now().Unix(), counter) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about moving this
Mail
library logic toexamples/gno.land/p/demo/mail
, instead of having it sit in a Realm?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey Milos!
Thanks taking the time to review this. Yes, deciding what to put in
p/
andr/
has been one interrogation of mine.Here are my thoughts, here is my starting point:
p
is package, is like library, is common code for everyone to use, meaning it's quite stable.r
is realm, is app-like, meaning usage is restricted.By that logic, code that isn't reasonnably finalized at least during GoR is better kept in
r/
as it won't break anything (because nobody will be using it as a dependency).I think putting things in
p/demo
is a signal to other devs saying: this code is stable, well-thought out please use it. My app is not there yet, it's WIP.Another question I have with this app is:
Whether the functionality it offers is really about mails? or about generic messages.
If it's the latter, then the part to put in
p/
is not mail, it would be likep/demo/msg
, withr/demo/mail
being a realm (almost an app instance) using that system.This tells me it's a bit early to make the separation now.