Skip to content
This repository was archived by the owner on Apr 11, 2022. It is now read-only.

Commit e7eac58

Browse files
committed
implement LUD-15 balanceNotify.
1 parent 9b8410e commit e7eac58

File tree

9 files changed

+114
-24
lines changed

9 files changed

+114
-24
lines changed

account_functions.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,7 @@ func getAccountIdFromLNURLWithdraw(r *http.Request) (string, error) {
8989
log.Error().Err(err).Msg("lnurl-withdraw with no reference to any account")
9090
return "", errors.New("Unexpected incomplete lnurl-withdraw.")
9191
}
92+
93+
func balanceWithReserve(balance int64) int64 {
94+
return int64(float64(balance) * 0.992)
95+
}

account_handlers.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/json"
66
"fmt"
77
"net/http"
8+
"net/url"
89
"strconv"
910
"time"
1011

@@ -162,7 +163,7 @@ func lnurlWithdraw(w http.ResponseWriter, r *http.Request) {
162163
// get balance
163164
balance := balanceWithReserve(data.GetAccountBalance(accountId))
164165

165-
if balance < 10000 {
166+
if balance < MIN_WITHDRAWABLE {
166167
json.NewEncoder(w).Encode(lnurl.ErrorResponse("the minimum withdrawal is 10 sat, your balance is " + strconv.FormatInt(balance, 10) + " msat."))
167168
return
168169
}
@@ -174,7 +175,7 @@ func lnurlWithdraw(w http.ResponseWriter, r *http.Request) {
174175
s.ServiceURL, r.URL.RawQuery),
175176
K1: hex.EncodeToString(make([]byte, 32)), // we don't care
176177
MaxWithdrawable: balance,
177-
MinWithdrawable: 100000,
178+
MinWithdrawable: MIN_WITHDRAWABLE,
178179
DefaultDescription: fmt.Sprintf("etleneum.com %s balance withdraw", accountId),
179180
BalanceCheck: getStaticLNURLWithdraw(accountId),
180181
})
@@ -187,8 +188,15 @@ func lnurlWithdrawCallback(w http.ResponseWriter, r *http.Request) {
187188
return
188189
}
189190

190-
bolt11 := r.URL.Query().Get("pr")
191+
// LUD-15, save URL to notify the user later
192+
balanceNotify, _ := url.Parse(r.URL.Query().Get("balanceNotify"))
193+
if balanceNotify != nil && balanceNotify.Host != "" {
194+
data.UpdateAccountMetadata(accountId, func(am *data.AccountMetadata) {
195+
am.BalanceNotify = balanceNotify.String()
196+
})
197+
}
191198

199+
bolt11 := r.URL.Query().Get("pr")
192200
if s.FreeMode {
193201
json.NewEncoder(w).Encode(lnurl.OkResponse())
194202
return

call_functions.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ import (
1111
"github.com/fiatjaf/etleneum/runlua"
1212
)
1313

14+
var balanceNotifyClient = http.Client{
15+
Timeout: time.Second * 2,
16+
CheckRedirect: func(r *http.Request, via []*http.Request) error {
17+
return fmt.Errorf("target '%s' has returned a redirect", r.URL)
18+
},
19+
}
20+
1421
type CallContext struct {
1522
VisitedContracts map[string]bool
1623
Transfers []data.Transfer
@@ -104,6 +111,28 @@ func runCallGlobal(call *data.Call, useBalance bool) (err error) {
104111
return err
105112
}
106113

114+
// at this point the call has succeeded, we can then notify all accounts
115+
// that have a balanceNotify URL set on their metadata
116+
go func() {
117+
time.Sleep(2 * time.Second) // give some time for the call to be finished
118+
for key, balance := range callContext.AccountBalances {
119+
if balanceWithReserve(balance) >= MIN_WITHDRAWABLE {
120+
metadata := data.GetAccountMetadata(key)
121+
if metadata.BalanceNotify != "" {
122+
log := log.With().Str("account", key).Str("url", metadata.BalanceNotify).Int64("balance", balance).Logger()
123+
resp, err := balanceNotifyClient.Post(metadata.BalanceNotify, "", nil)
124+
if err != nil {
125+
log.Warn().Err(err).Msg("balanceNotify call failed")
126+
} else if resp.StatusCode >= 300 {
127+
log.Warn().Int("status", resp.StatusCode).Msg("balanceNotify call returned bad status code")
128+
} else {
129+
log.Info().Msg("balanceNotify call succeeded")
130+
}
131+
}
132+
}
133+
}
134+
}()
135+
107136
return nil
108137
}
109138

constants.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package main
2+
3+
const MIN_WITHDRAWABLE = 25000

data/account.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import (
77
"path/filepath"
88
)
99

10+
type AccountMetadata struct {
11+
BalanceNotify string `json:"balanceNotify"`
12+
}
13+
1014
func GetAccountBalance(key string) (msatoshi int64) {
1115
if err := readJSON(
1216
filepath.Join(DatabasePath, "accounts", key, "balance.json"),
@@ -104,3 +108,31 @@ func CancelWithdraw(key string, amount int64, hash string) error {
104108
Finish(fmt.Sprintf("withdraw %s has failed.", hash))
105109
return nil
106110
}
111+
112+
func GetAccountMetadata(key string) (am AccountMetadata) {
113+
readJSON(
114+
filepath.Join(DatabasePath, "accounts", key, "metadata.json"),
115+
&am,
116+
)
117+
return am
118+
}
119+
120+
func UpdateAccountMetadata(key string, mod func(am *AccountMetadata)) error {
121+
Start()
122+
123+
am := GetAccountMetadata(key)
124+
125+
mod(&am)
126+
127+
err := writeJSON(
128+
filepath.Join(DatabasePath, "accounts", key, "metadata.json"),
129+
am,
130+
)
131+
if err != nil {
132+
Abort()
133+
return err
134+
}
135+
136+
Finish(fmt.Sprintf("account %s metadata was updated.", key))
137+
return nil
138+
}

data/git.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"time"
1111
)
1212

13-
func execute(name string, args ...string) error {
13+
func execute(name string, args ...string) (string, error) {
1414
stdout := &bytes.Buffer{}
1515
stderr := &bytes.Buffer{}
1616

@@ -20,49 +20,54 @@ func execute(name string, args ...string) error {
2020
cmd.Stderr = stderr
2121

2222
err := cmd.Run()
23+
out := stdout.String() + stderr.String()
2324
if err != nil {
2425
stdout.WriteTo(os.Stderr)
2526
stderr.WriteTo(os.Stderr)
26-
return err
27+
return out, err
2728
}
2829

29-
return nil
30+
return out, nil
3031
}
3132

3233
func gitAdd(path string) error {
33-
if err := execute("git", "add", "."); err != nil {
34+
if _, err := execute("git", "add", "."); err != nil {
3435
return err
3536
}
3637

3738
return nil
3839
}
3940

4041
func gitCommit(message string) error {
41-
if err := execute("git", "commit", "-m", message, "--no-edit"); err != nil {
42+
if out, err := execute("git", "commit", "-m", message, "--no-edit"); err != nil {
43+
if strings.Contains(out, "nothing to commit, working tree clean") {
44+
return nil
45+
}
46+
4247
return err
4348
}
4449

4550
return nil
4651
}
4752

4853
func gitReset() error {
49-
if err := execute("git", "reset", "--hard", "HEAD"); err != nil {
54+
if _, err := execute("git", "reset", "--hard", "HEAD"); err != nil {
5055
return err
5156
}
5257

5358
return nil
5459
}
5560

5661
func gitPull() error {
57-
if err := execute("git", "pull", "origin", "master", "--rebase"); err != nil {
62+
if _, err := execute("git", "pull", "origin", "master", "--rebase"); err != nil {
5863
return err
5964
}
6065

6166
return nil
6267
}
6368

6469
func gitPush() error {
65-
if err := execute("git", "push", "origin", "master"); err != nil {
70+
if _, err := execute("git", "push", "origin", "master"); err != nil {
6671
return err
6772
}
6873

data/helpers.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package data
22

33
import (
4+
"bytes"
45
"encoding/json"
56
"fmt"
67
"io/ioutil"
@@ -32,9 +33,14 @@ func writeFile(path string, contents []byte) error {
3233
}
3334

3435
func writeJSON(path string, contents interface{}) error {
35-
if payloadJSON, err := json.MarshalIndent(contents, "", " "); err != nil {
36+
res := &bytes.Buffer{}
37+
enc := json.NewEncoder(res)
38+
enc.SetIndent("", " ")
39+
enc.SetEscapeHTML(false)
40+
41+
if err := enc.Encode(contents); err != nil {
3642
return err
37-
} else if err := writeFile(path, payloadJSON); err != nil {
43+
} else if err := writeFile(path, res.Bytes()); err != nil {
3844
return err
3945
}
4046

helpers.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,6 @@ func jsonError(w http.ResponseWriter, message string, code int) {
4242
})
4343
}
4444

45-
func balanceWithReserve(balance int64) int64 {
46-
return int64(float64(balance) * 0.992)
47-
}
48-
4945
func diffDeltaOneliner(prefix string, idelta gojsondiff.Delta) (lines []string) {
5046
key := prefix
5147
if key != "" {

main.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"context"
55
"embed"
6+
"fmt"
67
"io"
78
"net/http"
89
"net/url"
@@ -54,13 +55,19 @@ var contractstreams = cmap.New()
5455
var static embed.FS
5556

5657
func main() {
57-
http.DefaultClient = &http.Client{Transport: &http.Transport{
58-
MaxIdleConns: 10,
59-
MaxConnsPerHost: 10,
60-
MaxIdleConnsPerHost: 2,
61-
IdleConnTimeout: 10 * time.Second,
62-
DisableCompression: true,
63-
}}
58+
http.DefaultClient = &http.Client{
59+
Timeout: time.Second * 5,
60+
Transport: &http.Transport{
61+
MaxIdleConns: 10,
62+
MaxConnsPerHost: 10,
63+
MaxIdleConnsPerHost: 2,
64+
IdleConnTimeout: 10 * time.Second,
65+
DisableCompression: true,
66+
},
67+
CheckRedirect: func(r *http.Request, via []*http.Request) error {
68+
return fmt.Errorf("target '%s' has returned a redirect", r.URL)
69+
},
70+
}
6471

6572
if isRunningAsPlugin() {
6673
p := plugin.Plugin{

0 commit comments

Comments
 (0)