Skip to content

Commit

Permalink
Add functions to create and handle UMA invoices.
Browse files Browse the repository at this point in the history
  • Loading branch information
zhenlu committed Jul 16, 2024
1 parent 4a68e4d commit d189933
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 28 deletions.
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ module github.com/uma-universal-money-address/uma-go-sdk
go 1.21

require (
github.com/btcsuite/btcutil v1.0.2
github.com/decred/dcrd/bech32 v1.1.4
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0
github.com/ecies/go/v2 v2.0.9
github.com/google/uuid v1.6.0
github.com/stretchr/testify v1.8.4
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/ethereum/go-ethereum v1.13.8 // indirect
github.com/ethereum/go-ethereum v1.13.15 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.17.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
27 changes: 6 additions & 21 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNu
github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o=
github.com/aclements/go-gg v0.0.0-20170118225347-6dbb4e4fefb0/go.mod h1:55qNq4vcpkIuHowELi5C8e+1yUHtoLoOUR9QU5j7Tes=
github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794/go.mod h1:7e+I0LQFUI9AXWxOfsQROs9xPhoJtbsyWcjJqDd4KPY=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/ajstarks/svgo v0.0.0-20210923152817-c3b6e2f0c527/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
Expand Down Expand Up @@ -121,18 +120,8 @@ github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+Wji
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts=
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
Expand Down Expand Up @@ -180,12 +169,13 @@ github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXk
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/bech32 v1.1.4 h1:wFlLM7Oic0MlIhQZdCQhdIqVc4CNaQ0vNR9fgCoWfe0=
github.com/decred/dcrd/bech32 v1.1.4/go.mod h1:jliqHZmCbVfT06Lh1mQywEKFVidRclbBJIUmwdoKhu0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
Expand Down Expand Up @@ -223,8 +213,8 @@ github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHj
github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg=
github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0=
github.com/ethereum/go-ethereum v1.13.8 h1:1od+thJel3tM52ZUNQwvpYOeRHlbkVFZ5S8fhi0Lgsg=
github.com/ethereum/go-ethereum v1.13.8/go.mod h1:sc48XYQxCzH3fG9BcrXCOOgQk2JfZzNAmIKnceogzsA=
github.com/ethereum/go-ethereum v1.13.15 h1:U7sSGYGo4SPjP6iNIifNoyIAiNjrmQkz6EwQG+/EZWo=
github.com/ethereum/go-ethereum v1.13.15/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
Expand Down Expand Up @@ -374,6 +364,8 @@ github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v0.0.0-20161107002406-da06d194a00e/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
Expand Down Expand Up @@ -428,12 +420,10 @@ github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrO
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU=
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
Expand Down Expand Up @@ -461,7 +451,6 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
Expand Down Expand Up @@ -550,12 +539,10 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
Expand Down Expand Up @@ -721,7 +708,6 @@ go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnw
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
Expand All @@ -730,7 +716,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
Expand Down
22 changes: 21 additions & 1 deletion uma/protocol/invoice.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package protocol
import (
"fmt"

"github.com/btcsuite/btcutil/bech32"
"github.com/decred/dcrd/bech32"
"github.com/uma-universal-money-address/uma-go-sdk/uma/utils"
)

Expand Down Expand Up @@ -95,3 +95,23 @@ func (i *UmaInvoice) ToBech32String() (string, error) {

return bech32.Encode("uma", conv)
}

func FromBech32String(bech32Str string) (*UmaInvoice, error) {
hrp, data, err := bech32.DecodeNoLimit(bech32Str)
if err != nil {
return nil, err
}
if hrp != "uma" {
return nil, fmt.Errorf("invalid human readable part")
}
conv, err := bech32.ConvertBits(data, 5, 8, false)
if err != nil {
return nil, err
}
var i UmaInvoice
err = i.UnmarshalTLV(conv)
if err != nil {
return nil, err
}
return &i, nil
}
10 changes: 7 additions & 3 deletions uma/test/protocol_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ func TestBinaryCodableForCounterPartyDataOptions(t *testing.T) {
func TestUMAInvoiceTLVAndBech32(t *testing.T) {
kyc := umaprotocol.KycStatusVerified
signature := []byte("signature")
invoicenvoice := umaprotocol.UmaInvoice{
invoice := umaprotocol.UmaInvoice{
ReceiverUma: "[email protected]",
InvoiceUUID: "c7c07fec-cf00-431c-916f-6c13fc4b69f9",
Amount: 1000,
Expand All @@ -298,15 +298,19 @@ func TestUMAInvoiceTLVAndBech32(t *testing.T) {
Signature: &signature,
}

invoiceTLV, err := invoicenvoice.MarshalTLV()
invoiceTLV, err := invoice.MarshalTLV()
require.NoError(t, err)

invoice2 := umaprotocol.UmaInvoice{}
err = invoice2.UnmarshalTLV(invoiceTLV)
require.NoError(t, err)
require.Equal(t, invoicenvoice, invoice2)
require.Equal(t, invoice, invoice2)

bech32String, err := invoice2.ToBech32String()
require.NoError(t, err)
require.Equal(t, "uma1qqxzgen0daqxyctj9e3k7mgpy33nwcesxanx2cedvdnrqvpdxsenzced8ycnve3dxe3nzvmxvv6xyd3evcusypp3xqcrqqcnqqp4256yqyy425eqg3hkcmrpwgpqzfqyqucnqvpsxqcrqpgpqyrpkcm0d4cxc6tpde3k2w3393jk6ctfdsarqtrwv9kk2w3squpnqt3npvqnxrqudp68gurn8ghj7etcv9khqmr99e3k7mf0vdskcmrzv93kkeqfwd5kwmnpw36hyeg0e4m4j", bech32String)

invoice3, err := umaprotocol.FromBech32String(bech32String)
require.NoError(t, err)
require.Equal(t, invoice, *invoice3)
}
54 changes: 54 additions & 0 deletions uma/test/uma_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -804,3 +804,57 @@ func getPubKeyResponse(privateKey *secp256k1.PrivateKey) umaprotocol.PubKeyRespo
ExpirationTimestamp: nil,
}
}

func TestUMAInvoice(t *testing.T) {
privateKey, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err)

kyc := umaprotocol.KycStatusVerified
umaInvoice, err := uma.CreateUmaInvoice(
"[email protected]",
100000,
umaprotocol.InvoiceCurrency{
Code: "USD",
Name: "US Dollar",
Symbol: "$",
},
1721081249,
"https://vasp2.com/api/lnurl/payreq/$foo",
true,
&umaprotocol.CounterPartyDataOptions{
"name": umaprotocol.CounterPartyDataOption{Mandatory: false},
"email": umaprotocol.CounterPartyDataOption{Mandatory: false},
"compliance": umaprotocol.CounterPartyDataOption{Mandatory: true},
},
nil,
&kyc,
nil,
nil,
privateKey.Serialize(),
)
require.NoError(t, err)

encodedInvoice, err := umaInvoice.ToBech32String()
require.NoError(t, err)

decodedInvoice, err := uma.DecodeUmaInvoice(encodedInvoice)
require.NoError(t, err)
require.Equal(t, decodedInvoice.ReceiverUma, "[email protected]")
require.Equal(t, decodedInvoice.Amount, uint64(100000))
require.Equal(t, decodedInvoice.ReceivingCurrency.Code, "USD")
require.Equal(t, decodedInvoice.ReceivingCurrency.Name, "US Dollar")
require.Equal(t, decodedInvoice.ReceivingCurrency.Symbol, "$")
require.Equal(t, decodedInvoice.Expiration, uint64(1721081249))
require.Equal(t, decodedInvoice.Callback, "https://vasp2.com/api/lnurl/payreq/$foo")
require.Equal(t, decodedInvoice.IsSubjectToTravelRule, true)
require.Equal(t, *decodedInvoice.RequiredPayerData, umaprotocol.CounterPartyDataOptions{
"name": umaprotocol.CounterPartyDataOption{Mandatory: false},
"email": umaprotocol.CounterPartyDataOption{Mandatory: false},
"compliance": umaprotocol.CounterPartyDataOption{Mandatory: true},
})
require.Equal(t, *decodedInvoice.KycStatus, umaprotocol.KycStatusVerified)

publicKeyResponse := getPubKeyResponse(privateKey)
err = uma.VerifyUmaInvoiceSignature(*decodedInvoice, publicKeyResponse)
require.NoError(t, err)
}
72 changes: 71 additions & 1 deletion uma/uma.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
eciesgo "github.com/ecies/go/v2"
"github.com/google/uuid"
"github.com/uma-universal-money-address/uma-go-sdk/uma/protocol"
"github.com/uma-universal-money-address/uma-go-sdk/uma/utils"
)
Expand Down Expand Up @@ -115,7 +116,7 @@ func GenerateNonce() (*string, error) {
return &nonce, nil
}

func signPayload(payload []byte, privateKeyBytes []byte) (*string, error) {
func signPayloadToBytes(payload []byte, privateKeyBytes []byte) ([]byte, error) {
privateKey := secp256k1.PrivKeyFromBytes(privateKeyBytes)
hash := crypto.SHA256.New()
_, err := hash.Write(payload)
Expand All @@ -127,6 +128,14 @@ func signPayload(payload []byte, privateKeyBytes []byte) (*string, error) {
if err != nil {
return nil, err
}
return signature, nil
}

func signPayload(payload []byte, privateKeyBytes []byte) (*string, error) {
signature, err := signPayloadToBytes(payload, privateKeyBytes)
if err != nil {
return nil, err
}
signatureString := hex.EncodeToString(signature)
return &signatureString, nil
}
Expand Down Expand Up @@ -1011,3 +1020,64 @@ func VerifyPostTransactionCallbackSignature(
}
return verifySignature(*signablePayload, *callback.Signature, otherVaspPubKeyResponse)
}

func CreateUmaInvoice(
receiverUma string,
amount uint64,
receivingCurrency protocol.InvoiceCurrency,
expiration uint64,
callback string,
isSubjectToTravelRule bool,
requiredPayerData *protocol.CounterPartyDataOptions,
commentCharsAllowed *int,
receiverKycStatus *protocol.KycStatus,
invoiceLimit *uint64,
senderUma *string,
signingPrivateKey []byte,
) (*protocol.UmaInvoice, error) {
uuid := uuid.New().String()
invoice := protocol.UmaInvoice{
ReceiverUma: receiverUma,
InvoiceUUID: uuid,
Amount: amount,
ReceivingCurrency: receivingCurrency,
Expiration: expiration,
IsSubjectToTravelRule: isSubjectToTravelRule,
RequiredPayerData: requiredPayerData,
// TODO: modify the version once ready, all current version cannot support UMA invoice.
// Since this only add fields and features to the protocol, this won't break the current
// UMA version so it can be a minor version bump.
UmaVersion: UmaProtocolVersion,
CommentCharsAllowed: commentCharsAllowed,
SenderUma: senderUma,
InvoiceLimit: invoiceLimit,
KycStatus: receiverKycStatus,
Callback: callback,
Signature: nil,
}
signablePayload, err := invoice.MarshalTLV()
if err != nil {
return nil, err
}
signature, err := signPayloadToBytes(signablePayload, signingPrivateKey)
if err != nil {
return nil, err
}
invoice.Signature = &signature
return &invoice, nil
}

func DecodeUmaInvoice(invoice string) (*protocol.UmaInvoice, error) {
return protocol.FromBech32String(invoice)
}

func VerifyUmaInvoiceSignature(invoice protocol.UmaInvoice, otherVaspPubKeyResponse protocol.PubKeyResponse) error {
unsignedInvoice := invoice
unsignedInvoice.Signature = nil
signablePayload, err := unsignedInvoice.MarshalTLV()
if err != nil {
return err
}
signatureString := hex.EncodeToString(*invoice.Signature)
return verifySignature(signablePayload, signatureString, otherVaspPubKeyResponse)
}

0 comments on commit d189933

Please sign in to comment.