Skip to content

Commit

Permalink
Added support for time bound transactions.
Browse files Browse the repository at this point in the history
  • Loading branch information
0xfe committed Mar 25, 2018
1 parent 7ce5323 commit 53e2a67
Show file tree
Hide file tree
Showing 35 changed files with 666 additions and 6,542 deletions.
20 changes: 4 additions & 16 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,11 @@ lumen flags bob auth_revocables

# Disable Bob's master key (by setting it's weight to 0)
lumen signer masterweight bob 0

# Create a time bound transaction only valid between given UTC timestamps
# Submit it later with: lumen tx submit "base64-encoded transaction string"
lumen pay 5 USD --from escrow --to bob --mintime '2017-06-06 12:00:00' --maxtime '2017-05-05 12:00:00' --nosubmit
# Output: base64-encoded transaction string
```

### Configuring Lumen
Expand Down
58 changes: 58 additions & 0 deletions cli/utils.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package cli

import (
"encoding/base64"
"fmt"
"os"
"strconv"
"strings"
"time"

"github.com/spf13/cobra"

Expand Down Expand Up @@ -49,6 +51,10 @@ func buildFlagsForTxOptions(cmd *cobra.Command) {
cmd.Flags().Bool("nosign", false, "don't sign transaction")
cmd.Flags().String("memotext", "", "memo text")
cmd.Flags().String("memoid", "", "memo ID")
cmd.Flags().String("memohash", "", "memo hash (base64-encoded)")
cmd.Flags().String("memoreturn", "", "memo return (base64-encoded)")
cmd.Flags().String("mintime", "", "not valid before 'YYYY-MM-DD HH:MM:SS' in UTC")
cmd.Flags().String("maxtime", "", "not valid after 'YYYY-MM-DD HH:MM:SS' in UTC")
cmd.Flags().StringSlice("signers", []string{}, "alternate signers (comma separated)")
}

Expand All @@ -68,6 +74,30 @@ func (cli *CLI) genTxOptions(cmd *cobra.Command, logFields logrus.Fields) (*micr
opts = opts.WithMemoID(id)
}

if memohash, err := cmd.Flags().GetString("memohash"); err == nil && memohash != "" {
hash, err := base64.StdEncoding.DecodeString(memohash)
if err != nil {
logrus.WithFields(logFields).Debugf("error decoding memohash: %v", err)
return nil, errors.Errorf("bad memohash: %s", memohash)
}

var memoHash [32]byte
copy(memoHash[:], hash[:])
opts = opts.WithMemoHash(memoHash)
}

if memoreturn, err := cmd.Flags().GetString("memoreturn"); err == nil && memoreturn != "" {
hash, err := base64.StdEncoding.DecodeString(memoreturn)
if err != nil {
logrus.WithFields(logFields).Debugf("error decoding memoreturn: %v", err)
return nil, errors.Errorf("bad memoreturn: %s", memoreturn)
}

var memoReturn [32]byte
copy(memoReturn[:], hash[:])
opts = opts.WithMemoReturn(memoReturn)
}

if signers, err := cmd.Flags().GetStringSlice("signers"); err == nil && len(signers) > 0 {
for _, signer := range signers {
logrus.WithFields(logFields).Debugf("adding signer: %s", signer)
Expand All @@ -82,6 +112,34 @@ func (cli *CLI) genTxOptions(cmd *cobra.Command, logFields logrus.Fields) (*micr
}
}

hasMinTime := false
hasMaxTime := false
minTimeBound := time.Now()
maxTimeBound := time.Now()
timeFormat := "2006-01-02 15:04:05"

if minTime, err := cmd.Flags().GetString("mintime"); err == nil && minTime != "" {
minTimeBound, err = time.Parse(timeFormat, minTime)
if err != nil {
return nil, errors.Errorf("bad --mintime: expecting YYYY-MM-DD HH:MM:SS, got: %v", minTime)
}
hasMinTime = true
}

if maxTime, err := cmd.Flags().GetString("maxtime"); err == nil && maxTime != "" {
maxTimeBound, err = time.Parse(timeFormat, maxTime)
if err != nil {
return nil, errors.Errorf("bad --maxtime: expecting YYYY-MM-DD HH:MM:SS")
}
hasMaxTime = true
}

if hasMinTime && hasMaxTime {
opts = opts.WithTimeBounds(minTimeBound.UTC(), maxTimeBound.UTC())
} else if hasMinTime || hasMaxTime {
return nil, errors.Errorf("need both --mintime and --maxtime")
}

if nosubmit, _ := cli.rootCmd.Flags().GetBool("nosubmit"); nosubmit {
handler := func(args ...interface{}) (bool, error) {
showSuccess(args[0].(string))
Expand Down
35 changes: 35 additions & 0 deletions lumen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ func run(cli *cli.CLI, command string) string {
return strings.TrimSpace(got)
}

func runArgs(cli *cli.CLI, args ...string) string {
fmt.Printf("$ lumen %s\n", strings.Join(args, " "))
got := cli.Embeddable().Run(args...)
fmt.Printf("%s\n", got)
return strings.TrimSpace(got)
}

func expectOutput(t *testing.T, cli *cli.CLI, want string, command string) {
got := run(cli, command)

Expand Down Expand Up @@ -287,3 +294,31 @@ func TestData(t *testing.T) {
expectOutput(t, cli, "", "data mo foo --clear")
expectOutput(t, cli, "error", "data mo foo")
}

func TestTimeBounds(t *testing.T) {
cli, cleanupFunc := newCLI()
defer cleanupFunc()

createFundedAccount(t, cli, "mo")
createFundedAccount(t, cli, "bob")

cli.Embeddable()
output := runArgs(cli, "pay", "1", "--from", "mo", "--to", "bob", "--mintime", "2017-01-01 12:00:00")
if output != "error" {
t.Errorf("want error, got %v", output)
}
output = runArgs(cli, "pay", "1", "--from", "mo", "--to", "bob", "--maxtime", "2017-01-01 12:00:00")
if output != "error" {
t.Errorf("want error, got %v", output)
}
output = runArgs(cli, "pay", "1", "--from", "mo", "--to", "bob", "--mintime", "2060-01-01 12:00:00", "--maxtime", "2075-01-01 12:00:00")
if output != "error" {
t.Errorf("want error, got %v", output)
}
output = runArgs(cli, "pay", "1", "--from", "mo", "--to", "bob", "--mintime", "2006-01-01 12:00:00", "--maxtime", "2075-01-01 12:00:00")
if output != "" {
t.Errorf("want nothing, got %v", output)
}

log.Print(output)
}
24 changes: 6 additions & 18 deletions vendor/github.com/0xfe/microstellar/Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 53e2a67

Please sign in to comment.