diff --git a/README.md b/README.md index 5c354bb..38cbe25 100644 --- a/README.md +++ b/README.md @@ -305,11 +305,11 @@ make proto ### Setup SQL database - As an environment variable via a `.env` file. ``` shell - DB_PATH=postgresql://user:password@localhost:5432/mydatabase + DB_PATH=postgres://user:password@localhost:5432/mydatabase ``` - Passed inline with commands that require it. ``` shell - export DB_PATH=postgresql://user:password@localhost:5432/mydatabase + export DB_PATH=postgres://user:password@localhost:5432/mydatabase ``` ### Migrating SQL database diff --git a/go.mod b/go.mod index d64b798..cca5727 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/jsternberg/zap-logfmt v1.3.0 github.com/miguelmota/go-ethereum-hdwallet v0.1.3 github.com/mitchellh/mapstructure v1.5.0 + github.com/onflow/flow-go-sdk v0.46.3 github.com/pelletier/go-toml/v2 v2.2.4 github.com/prometheus/client_golang v1.23.0 github.com/shopspring/decimal v1.4.0 @@ -101,6 +102,8 @@ require ( github.com/evalphobia/logrus_fluent v0.5.4 // indirect github.com/fluent/fluent-logger-golang v1.4.0 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c // indirect + github.com/fxamacker/circlehash v0.3.0 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect github.com/go-kit/kit v0.13.0 // indirect github.com/go-kit/log v0.2.1 // indirect @@ -140,13 +143,16 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/k0kubun/pp/v3 v3.2.0 // indirect github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/labstack/echo/v4 v4.11.3 // indirect github.com/labstack/gommon v0.4.0 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/linxGnu/grocksdb v1.8.14 // indirect + github.com/logrusorgru/aurora/v4 v4.0.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -162,6 +168,10 @@ require ( github.com/muesli/termenv v0.16.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect + github.com/onflow/atree v0.6.0 // indirect + github.com/onflow/cadence v0.42.12 // indirect + github.com/onflow/crypto v0.25.1 // indirect + github.com/onflow/go-ethereum v1.13.4 // indirect github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect github.com/philhofer/fwd v1.0.0 // indirect github.com/pkg/errors v0.9.1 // indirect @@ -185,17 +195,21 @@ require ( github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect + github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c // indirect github.com/tidwall/btree v1.7.0 // indirect github.com/tinylib/msgp v1.1.0 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect + github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/ugorji/go/codec v1.2.11 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect github.com/vmihailenco/msgpack/v4 v4.3.13 // indirect github.com/vmihailenco/tagparser v0.1.1 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + github.com/zeebo/blake3 v0.2.3 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 // indirect @@ -212,6 +226,8 @@ require ( golang.org/x/term v0.37.0 // indirect golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.9.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect + gonum.org/v1/gonum v0.16.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b // indirect diff --git a/go.sum b/go.sum index 95c4f1c..a1a4938 100644 --- a/go.sum +++ b/go.sum @@ -291,6 +291,10 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c h1:5tm/Wbs9d9r+qZaUFXk59CWDD0+77PBqDREffYkyi5c= +github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/circlehash v0.3.0 h1:XKdvTtIJV9t7DDUtsf0RIpC1OcxZtPbmgIH7ekx28WA= +github.com/fxamacker/circlehash v0.3.0/go.mod h1:3aq3OfVvsWtkWMb6A1owjOQFA+TLsD5FgJflnaQwtMM= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -521,13 +525,16 @@ github.com/jsternberg/zap-logfmt v1.3.0 h1:z1n1AOHVVydOOVuyphbOKyR4NICDQFiJMn1IK github.com/jsternberg/zap-logfmt v1.3.0/go.mod h1:N3DENp9WNmCZxvkBD/eReWwz1149BK6jEN9cQ4fNwZE= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs= +github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA= 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.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -552,6 +559,8 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/linxGnu/grocksdb v1.8.14 h1:HTgyYalNwBSG/1qCQUIott44wU5b2Y9Kr3z7SK5OfGQ= github.com/linxGnu/grocksdb v1.8.14/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= +github.com/logrusorgru/aurora/v4 v4.0.0 h1:sRjfPpun/63iADiSvGGjgA1cAYegEWMPCJdUpJYn9JA= +github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -612,6 +621,16 @@ github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a h1:dl github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a/go.mod h1:hVoHR2EVESiICEMbg137etN/Lx+lSrHPTD39Z/uE+2s= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/onflow/atree v0.6.0 h1:j7nQ2r8npznx4NX39zPpBYHmdy45f4xwoi+dm37Jk7c= +github.com/onflow/atree v0.6.0/go.mod h1:gBHU0M05qCbv9NN0kijLWMgC47gHVNBIp4KmsVFi0tc= +github.com/onflow/cadence v0.42.12 h1:AMnOzJUnuhc00OEukTml0m6J86VWOiNqG9fIgfOsIDw= +github.com/onflow/cadence v0.42.12/go.mod h1:1wFd+LiNiN6qoZXof3MBdpM6d8BsxbVIxOA77LbIYmE= +github.com/onflow/crypto v0.25.1 h1:0txy2PKPMM873JbpxQNbJmuOJtD56bfs48RQfm0ts5A= +github.com/onflow/crypto v0.25.1/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI= +github.com/onflow/flow-go-sdk v0.46.3 h1:kVe3duL2fS0ojcptB3tuZZSyatCqAsPtykso7irIaxo= +github.com/onflow/flow-go-sdk v0.46.3/go.mod h1:tfLjB9FZmwqtT5gaacjvpIhz7KCd67YPm6v+iqYAjEA= +github.com/onflow/go-ethereum v1.13.4 h1:iNO86fm8RbBbhZ87ZulblInqCdHnAQVY8okBrNsTevc= +github.com/onflow/go-ethereum v1.13.4/go.mod h1:cE/gEUkAffhwbVmMJYz+t1dAfVNHNwZCgc3BWtZxBGY= 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.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= @@ -755,6 +774,8 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c h1:HelZ2kAFadG0La9d+4htN4HzQ68Bm2iM9qKMSMES6xg= +github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c/go.mod h1:JlzghshsemAMDGZLytTFY8C1JQxQPhnatWqNwUXjggo= github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= @@ -763,6 +784,8 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d h1:5JInRQbk5UBX8JfUvKh2oYTLMVwj3p6n+wapDDm7hko= +github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d/go.mod h1:Nlx5Y115XQvNcIdIy7dZXaNSUpzwBSge4/Ivk93/Yog= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= @@ -779,6 +802,8 @@ github.com/vmihailenco/msgpack/v4 v4.3.13 h1:A2wsiTbvp63ilDaWmsk2wjx6xZdxQOvpiNl github.com/vmihailenco/msgpack/v4 v4.3.13/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= @@ -787,6 +812,13 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= +github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= +github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= +github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= @@ -863,6 +895,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -974,6 +1008,7 @@ golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1045,10 +1080,14 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -1188,6 +1227,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= +lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA= diff --git a/internal/relayertest/constants.go b/internal/relayertest/constants.go index a8d63bc..9d12237 100644 --- a/internal/relayertest/constants.go +++ b/internal/relayertest/constants.go @@ -59,6 +59,9 @@ var EvmChainCfgText string //go:embed testdata/xrpl_chain_config.toml var XrplChainCfgText string +//go:embed testdata/flow_chain_config.toml +var FlowChainCfgText string + //go:embed testdata/default_with_chain_config.toml var DefaultCfgTextWithChainCfg string diff --git a/internal/relayertest/mocks/chain_flow_client.go b/internal/relayertest/mocks/chain_flow_client.go new file mode 100644 index 0000000..7355a0d --- /dev/null +++ b/internal/relayertest/mocks/chain_flow_client.go @@ -0,0 +1,174 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: relayer/chains/flow/client.go +// +// Generated by this command: +// +// mockgen -source=relayer/chains/flow/client.go -mock_names Client=MockFlowClient -package mocks -destination internal/relayertest/mocks/chain_flow_client.go +// + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + big "math/big" + reflect "reflect" + time "time" + + flow "github.com/onflow/flow-go-sdk" + gomock "go.uber.org/mock/gomock" +) + +// MockFlowClient is a mock of Client interface. +type MockFlowClient struct { + ctrl *gomock.Controller + recorder *MockFlowClientMockRecorder + isgomock struct{} +} + +// MockFlowClientMockRecorder is the mock recorder for MockFlowClient. +type MockFlowClientMockRecorder struct { + mock *MockFlowClient +} + +// NewMockFlowClient creates a new mock instance. +func NewMockFlowClient(ctrl *gomock.Controller) *MockFlowClient { + mock := &MockFlowClient{ctrl: ctrl} + mock.recorder = &MockFlowClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockFlowClient) EXPECT() *MockFlowClientMockRecorder { + return m.recorder +} + +// BroadcastTx mocks base method. +func (m *MockFlowClient) BroadcastTx(ctx context.Context, txBlob []byte) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BroadcastTx", ctx, txBlob) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BroadcastTx indicates an expected call of BroadcastTx. +func (mr *MockFlowClientMockRecorder) BroadcastTx(ctx, txBlob any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BroadcastTx", reflect.TypeOf((*MockFlowClient)(nil).BroadcastTx), ctx, txBlob) +} + +// CheckAndConnect mocks base method. +func (m *MockFlowClient) CheckAndConnect(ctx context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CheckAndConnect", ctx) + ret0, _ := ret[0].(error) + return ret0 +} + +// CheckAndConnect indicates an expected call of CheckAndConnect. +func (mr *MockFlowClientMockRecorder) CheckAndConnect(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckAndConnect", reflect.TypeOf((*MockFlowClient)(nil).CheckAndConnect), ctx) +} + +// Connect mocks base method. +func (m *MockFlowClient) Connect(ctx context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Connect", ctx) + ret0, _ := ret[0].(error) + return ret0 +} + +// Connect indicates an expected call of Connect. +func (mr *MockFlowClientMockRecorder) Connect(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Connect", reflect.TypeOf((*MockFlowClient)(nil).Connect), ctx) +} + +// GetAccount mocks base method. +func (m *MockFlowClient) GetAccount(ctx context.Context, address string) (*flow.Account, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccount", ctx, address) + ret0, _ := ret[0].(*flow.Account) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAccount indicates an expected call of GetAccount. +func (mr *MockFlowClientMockRecorder) GetAccount(ctx, address any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockFlowClient)(nil).GetAccount), ctx, address) +} + +// GetBalance mocks base method. +func (m *MockFlowClient) GetBalance(ctx context.Context, address string) (*big.Int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBalance", ctx, address) + ret0, _ := ret[0].(*big.Int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBalance indicates an expected call of GetBalance. +func (mr *MockFlowClientMockRecorder) GetBalance(ctx, address any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBalance", reflect.TypeOf((*MockFlowClient)(nil).GetBalance), ctx, address) +} + +// GetBlockTimestamp mocks base method. +func (m *MockFlowClient) GetBlockTimestamp(ctx context.Context, txHash string) (*time.Time, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBlockTimestamp", ctx, txHash) + ret0, _ := ret[0].(*time.Time) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBlockTimestamp indicates an expected call of GetBlockTimestamp. +func (mr *MockFlowClientMockRecorder) GetBlockTimestamp(ctx, txHash any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlockTimestamp", reflect.TypeOf((*MockFlowClient)(nil).GetBlockTimestamp), ctx, txHash) +} + +// GetLatestBlockID mocks base method. +func (m *MockFlowClient) GetLatestBlockID(ctx context.Context) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLatestBlockID", ctx) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetLatestBlockID indicates an expected call of GetLatestBlockID. +func (mr *MockFlowClientMockRecorder) GetLatestBlockID(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestBlockID", reflect.TypeOf((*MockFlowClient)(nil).GetLatestBlockID), ctx) +} + +// GetTxResult mocks base method. +func (m *MockFlowClient) GetTxResult(ctx context.Context, txHash string) (*flow.TransactionResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTxResult", ctx, txHash) + ret0, _ := ret[0].(*flow.TransactionResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTxResult indicates an expected call of GetTxResult. +func (mr *MockFlowClientMockRecorder) GetTxResult(ctx, txHash any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTxResult", reflect.TypeOf((*MockFlowClient)(nil).GetTxResult), ctx, txHash) +} + +// StartLivelinessCheck mocks base method. +func (m *MockFlowClient) StartLivelinessCheck(ctx context.Context, interval time.Duration) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "StartLivelinessCheck", ctx, interval) +} + +// StartLivelinessCheck indicates an expected call of StartLivelinessCheck. +func (mr *MockFlowClientMockRecorder) StartLivelinessCheck(ctx, interval any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartLivelinessCheck", reflect.TypeOf((*MockFlowClient)(nil).StartLivelinessCheck), ctx, interval) +} diff --git a/internal/relayertest/mocks/signer_grpc.go b/internal/relayertest/mocks/signer_grpc.go index 47f2358..da51421 100644 --- a/internal/relayertest/mocks/signer_grpc.go +++ b/internal/relayertest/mocks/signer_grpc.go @@ -82,6 +82,26 @@ func (mr *MockFkmsServiceClientMockRecorder) SignEvm(ctx, in any, opts ...any) * return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignEvm", reflect.TypeOf((*MockFkmsServiceClient)(nil).SignEvm), varargs...) } +// SignFlow mocks base method. +func (m *MockFkmsServiceClient) SignFlow(ctx context.Context, in *fkmsv1.SignFlowRequest, opts ...grpc.CallOption) (*fkmsv1.SignFlowResponse, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SignFlow", varargs...) + ret0, _ := ret[0].(*fkmsv1.SignFlowResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SignFlow indicates an expected call of SignFlow. +func (mr *MockFkmsServiceClientMockRecorder) SignFlow(ctx, in any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignFlow", reflect.TypeOf((*MockFkmsServiceClient)(nil).SignFlow), varargs...) +} + // SignIcon mocks base method. func (m *MockFkmsServiceClient) SignIcon(ctx context.Context, in *fkmsv1.SignIconRequest, opts ...grpc.CallOption) (*fkmsv1.SignIconResponse, error) { m.ctrl.T.Helper() @@ -176,6 +196,21 @@ func (mr *MockFkmsServiceServerMockRecorder) SignEvm(arg0, arg1 any) *gomock.Cal return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignEvm", reflect.TypeOf((*MockFkmsServiceServer)(nil).SignEvm), arg0, arg1) } +// SignFlow mocks base method. +func (m *MockFkmsServiceServer) SignFlow(arg0 context.Context, arg1 *fkmsv1.SignFlowRequest) (*fkmsv1.SignFlowResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SignFlow", arg0, arg1) + ret0, _ := ret[0].(*fkmsv1.SignFlowResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SignFlow indicates an expected call of SignFlow. +func (mr *MockFkmsServiceServerMockRecorder) SignFlow(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignFlow", reflect.TypeOf((*MockFkmsServiceServer)(nil).SignFlow), arg0, arg1) +} + // SignIcon mocks base method. func (m *MockFkmsServiceServer) SignIcon(arg0 context.Context, arg1 *fkmsv1.SignIconRequest) (*fkmsv1.SignIconResponse, error) { m.ctrl.T.Helper() diff --git a/internal/relayertest/testdata/flow_chain_config.toml b/internal/relayertest/testdata/flow_chain_config.toml new file mode 100644 index 0000000..0590ca4 --- /dev/null +++ b/internal/relayertest/testdata/flow_chain_config.toml @@ -0,0 +1,10 @@ +endpoints = ["https://rest-testnet.onflow.org/v1"] +liveliness_checking_interval = 200000000000 +chain_type = "flow" +max_retry = 3 +query_timeout = 3000000000 +execute_timeout = 3000000000 +waiting_tx_duration = '50s' +checking_tx_interval = '5s' +chain_id = 539 +compute_limit = 2000 diff --git a/proto/fkms/v1/signer.pb.go b/proto/fkms/v1/signer.pb.go index cf724a5..5afb436 100644 --- a/proto/fkms/v1/signer.pb.go +++ b/proto/fkms/v1/signer.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.6 -// protoc v5.29.3 +// protoc-gen-go v1.35.2 +// protoc v6.33.1 // source: proto/fkms/v1/signer.proto package fkmsv1 @@ -11,7 +11,6 @@ import ( protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" - unsafe "unsafe" ) const ( @@ -27,6 +26,7 @@ const ( ChainType_EVM ChainType = 0 ChainType_XRPL ChainType = 1 ChainType_ICON ChainType = 2 + ChainType_FLOW ChainType = 3 ) // Enum value maps for ChainType. @@ -35,11 +35,13 @@ var ( 0: "EVM", 1: "XRPL", 2: "ICON", + 3: "FLOW", } ChainType_value = map[string]int32{ "EVM": 0, "XRPL": 1, "ICON": 2, + "FLOW": 3, } ) @@ -71,11 +73,12 @@ func (ChainType) EnumDescriptor() ([]byte, []int) { } type SignEvmRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - Message []byte `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` - unknownFields protoimpl.UnknownFields + state protoimpl.MessageState sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Message []byte `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` } func (x *SignEvmRequest) Reset() { @@ -123,10 +126,11 @@ func (x *SignEvmRequest) GetMessage() []byte { } type SignEvmResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` - unknownFields protoimpl.UnknownFields + state protoimpl.MessageState sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` } func (x *SignEvmResponse) Reset() { @@ -167,11 +171,12 @@ func (x *SignEvmResponse) GetSignature() []byte { } type SignXrplRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - SignerPayload *XrplSignerPayload `protobuf:"bytes,1,opt,name=signer_payload,json=signerPayload,proto3" json:"signer_payload,omitempty"` - Tss *Tss `protobuf:"bytes,2,opt,name=tss,proto3" json:"tss,omitempty"` - unknownFields protoimpl.UnknownFields + state protoimpl.MessageState sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SignerPayload *XrplSignerPayload `protobuf:"bytes,1,opt,name=signer_payload,json=signerPayload,proto3" json:"signer_payload,omitempty"` + Tss *Tss `protobuf:"bytes,2,opt,name=tss,proto3" json:"tss,omitempty"` } func (x *SignXrplRequest) Reset() { @@ -219,10 +224,11 @@ func (x *SignXrplRequest) GetTss() *Tss { } type SignXrplResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - TxBlob []byte `protobuf:"bytes,1,opt,name=tx_blob,json=txBlob,proto3" json:"tx_blob,omitempty"` - unknownFields protoimpl.UnknownFields + state protoimpl.MessageState sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TxBlob []byte `protobuf:"bytes,1,opt,name=tx_blob,json=txBlob,proto3" json:"tx_blob,omitempty"` } func (x *SignXrplResponse) Reset() { @@ -263,11 +269,12 @@ func (x *SignXrplResponse) GetTxBlob() []byte { } type SignIconRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - SignerPayload *IconSignerPayload `protobuf:"bytes,1,opt,name=signer_payload,json=signerPayload,proto3" json:"signer_payload,omitempty"` - Tss *Tss `protobuf:"bytes,2,opt,name=tss,proto3" json:"tss,omitempty"` - unknownFields protoimpl.UnknownFields + state protoimpl.MessageState sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SignerPayload *IconSignerPayload `protobuf:"bytes,1,opt,name=signer_payload,json=signerPayload,proto3" json:"signer_payload,omitempty"` + Tss *Tss `protobuf:"bytes,2,opt,name=tss,proto3" json:"tss,omitempty"` } func (x *SignIconRequest) Reset() { @@ -315,10 +322,11 @@ func (x *SignIconRequest) GetTss() *Tss { } type SignIconResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - TxParams []byte `protobuf:"bytes,1,opt,name=tx_params,json=txParams,proto3" json:"tx_params,omitempty"` - unknownFields protoimpl.UnknownFields + state protoimpl.MessageState sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TxParams []byte `protobuf:"bytes,1,opt,name=tx_params,json=txParams,proto3" json:"tx_params,omitempty"` } func (x *SignIconResponse) Reset() { @@ -358,15 +366,113 @@ func (x *SignIconResponse) GetTxParams() []byte { return nil } -type GetSignerAddressesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` +type SignFlowRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + SignerPayload *FlowSignerPayload `protobuf:"bytes,1,opt,name=signer_payload,json=signerPayload,proto3" json:"signer_payload,omitempty"` + Tss *Tss `protobuf:"bytes,2,opt,name=tss,proto3" json:"tss,omitempty"` +} + +func (x *SignFlowRequest) Reset() { + *x = SignFlowRequest{} + mi := &file_proto_fkms_v1_signer_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SignFlowRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignFlowRequest) ProtoMessage() {} + +func (x *SignFlowRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_fkms_v1_signer_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignFlowRequest.ProtoReflect.Descriptor instead. +func (*SignFlowRequest) Descriptor() ([]byte, []int) { + return file_proto_fkms_v1_signer_proto_rawDescGZIP(), []int{6} +} + +func (x *SignFlowRequest) GetSignerPayload() *FlowSignerPayload { + if x != nil { + return x.SignerPayload + } + return nil +} + +func (x *SignFlowRequest) GetTss() *Tss { + if x != nil { + return x.Tss + } + return nil +} + +type SignFlowResponse struct { + state protoimpl.MessageState sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TxBlob []byte `protobuf:"bytes,1,opt,name=tx_blob,json=txBlob,proto3" json:"tx_blob,omitempty"` +} + +func (x *SignFlowResponse) Reset() { + *x = SignFlowResponse{} + mi := &file_proto_fkms_v1_signer_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SignFlowResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignFlowResponse) ProtoMessage() {} + +func (x *SignFlowResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_fkms_v1_signer_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignFlowResponse.ProtoReflect.Descriptor instead. +func (*SignFlowResponse) Descriptor() ([]byte, []int) { + return file_proto_fkms_v1_signer_proto_rawDescGZIP(), []int{7} +} + +func (x *SignFlowResponse) GetTxBlob() []byte { + if x != nil { + return x.TxBlob + } + return nil +} + +type GetSignerAddressesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } func (x *GetSignerAddressesRequest) Reset() { *x = GetSignerAddressesRequest{} - mi := &file_proto_fkms_v1_signer_proto_msgTypes[6] + mi := &file_proto_fkms_v1_signer_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -378,7 +484,7 @@ func (x *GetSignerAddressesRequest) String() string { func (*GetSignerAddressesRequest) ProtoMessage() {} func (x *GetSignerAddressesRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_fkms_v1_signer_proto_msgTypes[6] + mi := &file_proto_fkms_v1_signer_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -391,19 +497,20 @@ func (x *GetSignerAddressesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSignerAddressesRequest.ProtoReflect.Descriptor instead. func (*GetSignerAddressesRequest) Descriptor() ([]byte, []int) { - return file_proto_fkms_v1_signer_proto_rawDescGZIP(), []int{6} + return file_proto_fkms_v1_signer_proto_rawDescGZIP(), []int{8} } type GetSignerAddressesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Signers []*Signers `protobuf:"bytes,1,rep,name=signers,proto3" json:"signers,omitempty"` - unknownFields protoimpl.UnknownFields + state protoimpl.MessageState sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Signers []*Signers `protobuf:"bytes,1,rep,name=signers,proto3" json:"signers,omitempty"` } func (x *GetSignerAddressesResponse) Reset() { *x = GetSignerAddressesResponse{} - mi := &file_proto_fkms_v1_signer_proto_msgTypes[7] + mi := &file_proto_fkms_v1_signer_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -415,7 +522,7 @@ func (x *GetSignerAddressesResponse) String() string { func (*GetSignerAddressesResponse) ProtoMessage() {} func (x *GetSignerAddressesResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_fkms_v1_signer_proto_msgTypes[7] + mi := &file_proto_fkms_v1_signer_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -428,7 +535,7 @@ func (x *GetSignerAddressesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSignerAddressesResponse.ProtoReflect.Descriptor instead. func (*GetSignerAddressesResponse) Descriptor() ([]byte, []int) { - return file_proto_fkms_v1_signer_proto_rawDescGZIP(), []int{7} + return file_proto_fkms_v1_signer_proto_rawDescGZIP(), []int{9} } func (x *GetSignerAddressesResponse) GetSigners() []*Signers { @@ -439,18 +546,19 @@ func (x *GetSignerAddressesResponse) GetSigners() []*Signers { } type XrplSignerPayload struct { - state protoimpl.MessageState `protogen:"open.v1"` - Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` - OracleId uint64 `protobuf:"varint,2,opt,name=oracle_id,json=oracleId,proto3" json:"oracle_id,omitempty"` - Fee string `protobuf:"bytes,3,opt,name=fee,proto3" json:"fee,omitempty"` - Sequence uint64 `protobuf:"varint,4,opt,name=sequence,proto3" json:"sequence,omitempty"` - unknownFields protoimpl.UnknownFields + state protoimpl.MessageState sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` + OracleId uint64 `protobuf:"varint,2,opt,name=oracle_id,json=oracleId,proto3" json:"oracle_id,omitempty"` + Fee string `protobuf:"bytes,3,opt,name=fee,proto3" json:"fee,omitempty"` + Sequence uint64 `protobuf:"varint,4,opt,name=sequence,proto3" json:"sequence,omitempty"` } func (x *XrplSignerPayload) Reset() { *x = XrplSignerPayload{} - mi := &file_proto_fkms_v1_signer_proto_msgTypes[8] + mi := &file_proto_fkms_v1_signer_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -462,7 +570,7 @@ func (x *XrplSignerPayload) String() string { func (*XrplSignerPayload) ProtoMessage() {} func (x *XrplSignerPayload) ProtoReflect() protoreflect.Message { - mi := &file_proto_fkms_v1_signer_proto_msgTypes[8] + mi := &file_proto_fkms_v1_signer_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -475,7 +583,7 @@ func (x *XrplSignerPayload) ProtoReflect() protoreflect.Message { // Deprecated: Use XrplSignerPayload.ProtoReflect.Descriptor instead. func (*XrplSignerPayload) Descriptor() ([]byte, []int) { - return file_proto_fkms_v1_signer_proto_rawDescGZIP(), []int{8} + return file_proto_fkms_v1_signer_proto_rawDescGZIP(), []int{10} } func (x *XrplSignerPayload) GetAccount() string { @@ -507,18 +615,19 @@ func (x *XrplSignerPayload) GetSequence() uint64 { } type IconSignerPayload struct { - state protoimpl.MessageState `protogen:"open.v1"` - Relayer string `protobuf:"bytes,1,opt,name=relayer,proto3" json:"relayer,omitempty"` - ContractAddress string `protobuf:"bytes,2,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` - StepLimit uint64 `protobuf:"varint,3,opt,name=step_limit,json=stepLimit,proto3" json:"step_limit,omitempty"` - NetworkId string `protobuf:"bytes,4,opt,name=network_id,json=networkId,proto3" json:"network_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Relayer string `protobuf:"bytes,1,opt,name=relayer,proto3" json:"relayer,omitempty"` + ContractAddress string `protobuf:"bytes,2,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` + StepLimit uint64 `protobuf:"varint,3,opt,name=step_limit,json=stepLimit,proto3" json:"step_limit,omitempty"` + NetworkId string `protobuf:"bytes,4,opt,name=network_id,json=networkId,proto3" json:"network_id,omitempty"` } func (x *IconSignerPayload) Reset() { *x = IconSignerPayload{} - mi := &file_proto_fkms_v1_signer_proto_msgTypes[9] + mi := &file_proto_fkms_v1_signer_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -530,7 +639,7 @@ func (x *IconSignerPayload) String() string { func (*IconSignerPayload) ProtoMessage() {} func (x *IconSignerPayload) ProtoReflect() protoreflect.Message { - mi := &file_proto_fkms_v1_signer_proto_msgTypes[9] + mi := &file_proto_fkms_v1_signer_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -543,7 +652,7 @@ func (x *IconSignerPayload) ProtoReflect() protoreflect.Message { // Deprecated: Use IconSignerPayload.ProtoReflect.Descriptor instead. func (*IconSignerPayload) Descriptor() ([]byte, []int) { - return file_proto_fkms_v1_signer_proto_rawDescGZIP(), []int{9} + return file_proto_fkms_v1_signer_proto_rawDescGZIP(), []int{11} } func (x *IconSignerPayload) GetRelayer() string { @@ -574,18 +683,104 @@ func (x *IconSignerPayload) GetNetworkId() string { return "" } -type Tss struct { - state protoimpl.MessageState `protogen:"open.v1"` - Message []byte `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` - RandomAddr []byte `protobuf:"bytes,2,opt,name=random_addr,json=randomAddr,proto3" json:"random_addr,omitempty"` - SignatureS []byte `protobuf:"bytes,3,opt,name=signature_s,json=signatureS,proto3" json:"signature_s,omitempty"` +type FlowSignerPayload struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + ComputeLimit uint64 `protobuf:"varint,2,opt,name=compute_limit,json=computeLimit,proto3" json:"compute_limit,omitempty"` + BlockId string `protobuf:"bytes,3,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` + KeyIndex uint32 `protobuf:"varint,4,opt,name=key_index,json=keyIndex,proto3" json:"key_index,omitempty"` + Sequence uint64 `protobuf:"varint,5,opt,name=sequence,proto3" json:"sequence,omitempty"` + ContractAddress string `protobuf:"bytes,6,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` +} + +func (x *FlowSignerPayload) Reset() { + *x = FlowSignerPayload{} + mi := &file_proto_fkms_v1_signer_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FlowSignerPayload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FlowSignerPayload) ProtoMessage() {} + +func (x *FlowSignerPayload) ProtoReflect() protoreflect.Message { + mi := &file_proto_fkms_v1_signer_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FlowSignerPayload.ProtoReflect.Descriptor instead. +func (*FlowSignerPayload) Descriptor() ([]byte, []int) { + return file_proto_fkms_v1_signer_proto_rawDescGZIP(), []int{12} +} + +func (x *FlowSignerPayload) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *FlowSignerPayload) GetComputeLimit() uint64 { + if x != nil { + return x.ComputeLimit + } + return 0 +} + +func (x *FlowSignerPayload) GetBlockId() string { + if x != nil { + return x.BlockId + } + return "" +} + +func (x *FlowSignerPayload) GetKeyIndex() uint32 { + if x != nil { + return x.KeyIndex + } + return 0 +} + +func (x *FlowSignerPayload) GetSequence() uint64 { + if x != nil { + return x.Sequence + } + return 0 +} + +func (x *FlowSignerPayload) GetContractAddress() string { + if x != nil { + return x.ContractAddress + } + return "" +} + +type Tss struct { + state protoimpl.MessageState sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message []byte `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + RandomAddr []byte `protobuf:"bytes,2,opt,name=random_addr,json=randomAddr,proto3" json:"random_addr,omitempty"` + SignatureS []byte `protobuf:"bytes,3,opt,name=signature_s,json=signatureS,proto3" json:"signature_s,omitempty"` } func (x *Tss) Reset() { *x = Tss{} - mi := &file_proto_fkms_v1_signer_proto_msgTypes[10] + mi := &file_proto_fkms_v1_signer_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -597,7 +792,7 @@ func (x *Tss) String() string { func (*Tss) ProtoMessage() {} func (x *Tss) ProtoReflect() protoreflect.Message { - mi := &file_proto_fkms_v1_signer_proto_msgTypes[10] + mi := &file_proto_fkms_v1_signer_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -610,7 +805,7 @@ func (x *Tss) ProtoReflect() protoreflect.Message { // Deprecated: Use Tss.ProtoReflect.Descriptor instead. func (*Tss) Descriptor() ([]byte, []int) { - return file_proto_fkms_v1_signer_proto_rawDescGZIP(), []int{10} + return file_proto_fkms_v1_signer_proto_rawDescGZIP(), []int{13} } func (x *Tss) GetMessage() []byte { @@ -635,16 +830,17 @@ func (x *Tss) GetSignatureS() []byte { } type Signers struct { - state protoimpl.MessageState `protogen:"open.v1"` - ChainType ChainType `protobuf:"varint,1,opt,name=chain_type,json=chainType,proto3,enum=fkms.v1.ChainType" json:"chain_type,omitempty"` - Addresses []string `protobuf:"bytes,2,rep,name=addresses,proto3" json:"addresses,omitempty"` - unknownFields protoimpl.UnknownFields + state protoimpl.MessageState sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainType ChainType `protobuf:"varint,1,opt,name=chain_type,json=chainType,proto3,enum=fkms.v1.ChainType" json:"chain_type,omitempty"` + Addresses []string `protobuf:"bytes,2,rep,name=addresses,proto3" json:"addresses,omitempty"` } func (x *Signers) Reset() { *x = Signers{} - mi := &file_proto_fkms_v1_signer_proto_msgTypes[11] + mi := &file_proto_fkms_v1_signer_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -656,7 +852,7 @@ func (x *Signers) String() string { func (*Signers) ProtoMessage() {} func (x *Signers) ProtoReflect() protoreflect.Message { - mi := &file_proto_fkms_v1_signer_proto_msgTypes[11] + mi := &file_proto_fkms_v1_signer_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -669,7 +865,7 @@ func (x *Signers) ProtoReflect() protoreflect.Message { // Deprecated: Use Signers.ProtoReflect.Descriptor instead. func (*Signers) Descriptor() ([]byte, []int) { - return file_proto_fkms_v1_signer_proto_rawDescGZIP(), []int{11} + return file_proto_fkms_v1_signer_proto_rawDescGZIP(), []int{14} } func (x *Signers) GetChainType() ChainType { @@ -688,73 +884,143 @@ func (x *Signers) GetAddresses() []string { var File_proto_fkms_v1_signer_proto protoreflect.FileDescriptor -const file_proto_fkms_v1_signer_proto_rawDesc = "" + - "\n" + - "\x1aproto/fkms/v1/signer.proto\x12\afkms.v1\"D\n" + - "\x0eSignEvmRequest\x12\x18\n" + - "\aaddress\x18\x01 \x01(\tR\aaddress\x12\x18\n" + - "\amessage\x18\x02 \x01(\fR\amessage\"/\n" + - "\x0fSignEvmResponse\x12\x1c\n" + - "\tsignature\x18\x01 \x01(\fR\tsignature\"t\n" + - "\x0fSignXrplRequest\x12A\n" + - "\x0esigner_payload\x18\x01 \x01(\v2\x1a.fkms.v1.XrplSignerPayloadR\rsignerPayload\x12\x1e\n" + - "\x03tss\x18\x02 \x01(\v2\f.fkms.v1.TssR\x03tss\"+\n" + - "\x10SignXrplResponse\x12\x17\n" + - "\atx_blob\x18\x01 \x01(\fR\x06txBlob\"t\n" + - "\x0fSignIconRequest\x12A\n" + - "\x0esigner_payload\x18\x01 \x01(\v2\x1a.fkms.v1.IconSignerPayloadR\rsignerPayload\x12\x1e\n" + - "\x03tss\x18\x02 \x01(\v2\f.fkms.v1.TssR\x03tss\"/\n" + - "\x10SignIconResponse\x12\x1b\n" + - "\ttx_params\x18\x01 \x01(\fR\btxParams\"\x1b\n" + - "\x19GetSignerAddressesRequest\"H\n" + - "\x1aGetSignerAddressesResponse\x12*\n" + - "\asigners\x18\x01 \x03(\v2\x10.fkms.v1.SignersR\asigners\"x\n" + - "\x11XrplSignerPayload\x12\x18\n" + - "\aaccount\x18\x01 \x01(\tR\aaccount\x12\x1b\n" + - "\toracle_id\x18\x02 \x01(\x04R\boracleId\x12\x10\n" + - "\x03fee\x18\x03 \x01(\tR\x03fee\x12\x1a\n" + - "\bsequence\x18\x04 \x01(\x04R\bsequence\"\x96\x01\n" + - "\x11IconSignerPayload\x12\x18\n" + - "\arelayer\x18\x01 \x01(\tR\arelayer\x12)\n" + - "\x10contract_address\x18\x02 \x01(\tR\x0fcontractAddress\x12\x1d\n" + - "\n" + - "step_limit\x18\x03 \x01(\x04R\tstepLimit\x12\x1d\n" + - "\n" + - "network_id\x18\x04 \x01(\tR\tnetworkId\"a\n" + - "\x03Tss\x12\x18\n" + - "\amessage\x18\x01 \x01(\fR\amessage\x12\x1f\n" + - "\vrandom_addr\x18\x02 \x01(\fR\n" + - "randomAddr\x12\x1f\n" + - "\vsignature_s\x18\x03 \x01(\fR\n" + - "signatureS\"Z\n" + - "\aSigners\x121\n" + - "\n" + - "chain_type\x18\x01 \x01(\x0e2\x12.fkms.v1.ChainTypeR\tchainType\x12\x1c\n" + - "\taddresses\x18\x02 \x03(\tR\taddresses*(\n" + - "\tChainType\x12\a\n" + - "\x03EVM\x10\x00\x12\b\n" + - "\x04XRPL\x10\x01\x12\b\n" + - "\x04ICON\x10\x022\xac\x02\n" + - "\vFkmsService\x12<\n" + - "\aSignEvm\x12\x17.fkms.v1.SignEvmRequest\x1a\x18.fkms.v1.SignEvmResponse\x12?\n" + - "\bSignXrpl\x12\x18.fkms.v1.SignXrplRequest\x1a\x19.fkms.v1.SignXrplResponse\x12?\n" + - "\bSignIcon\x12\x18.fkms.v1.SignIconRequest\x1a\x19.fkms.v1.SignIconResponse\x12]\n" + - "\x12GetSignerAddresses\x12\".fkms.v1.GetSignerAddressesRequest\x1a#.fkms.v1.GetSignerAddressesResponseB5Z3github.com/bandprotocol/falcon/proto/fkms/v1;fkmsv1b\x06proto3" +var file_proto_fkms_v1_signer_proto_rawDesc = []byte{ + 0x0a, 0x1a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x66, 0x6b, 0x6d, 0x73, 0x2f, 0x76, 0x31, 0x2f, + 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x66, 0x6b, + 0x6d, 0x73, 0x2e, 0x76, 0x31, 0x22, 0x44, 0x0a, 0x0e, 0x53, 0x69, 0x67, 0x6e, 0x45, 0x76, 0x6d, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x2f, 0x0a, 0x0f, 0x53, + 0x69, 0x67, 0x6e, 0x45, 0x76, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, + 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x74, 0x0a, 0x0f, + 0x53, 0x69, 0x67, 0x6e, 0x58, 0x72, 0x70, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x41, 0x0a, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x66, 0x6b, 0x6d, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x58, 0x72, 0x70, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x50, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x50, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x12, 0x1e, 0x0a, 0x03, 0x74, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0c, 0x2e, 0x66, 0x6b, 0x6d, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x73, 0x73, 0x52, 0x03, 0x74, + 0x73, 0x73, 0x22, 0x2b, 0x0a, 0x10, 0x53, 0x69, 0x67, 0x6e, 0x58, 0x72, 0x70, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x62, 0x6c, 0x6f, + 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x78, 0x42, 0x6c, 0x6f, 0x62, 0x22, + 0x74, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x49, 0x63, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x41, 0x0a, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x66, 0x6b, 0x6d, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x63, 0x6f, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x50, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x50, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1e, 0x0a, 0x03, 0x74, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x66, 0x6b, 0x6d, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x73, 0x73, + 0x52, 0x03, 0x74, 0x73, 0x73, 0x22, 0x2f, 0x0a, 0x10, 0x53, 0x69, 0x67, 0x6e, 0x49, 0x63, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x78, 0x5f, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x74, 0x78, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x74, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x46, 0x6c, + 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, 0x0e, 0x73, 0x69, 0x67, + 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x66, 0x6b, 0x6d, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x6f, 0x77, + 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x0d, 0x73, + 0x69, 0x67, 0x6e, 0x65, 0x72, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1e, 0x0a, 0x03, + 0x74, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x66, 0x6b, 0x6d, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x73, 0x73, 0x52, 0x03, 0x74, 0x73, 0x73, 0x22, 0x2b, 0x0a, 0x10, + 0x53, 0x69, 0x67, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x17, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x06, 0x74, 0x78, 0x42, 0x6c, 0x6f, 0x62, 0x22, 0x1b, 0x0a, 0x19, 0x47, 0x65, 0x74, + 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x48, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x53, 0x69, 0x67, + 0x6e, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x66, 0x6b, 0x6d, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x73, + 0x22, 0x78, 0x0a, 0x11, 0x58, 0x72, 0x70, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x50, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, + 0x1b, 0x0a, 0x09, 0x6f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x08, 0x6f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, + 0x66, 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x96, 0x01, 0x0a, 0x11, 0x49, + 0x63, 0x6f, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x65, 0x70, 0x4c, + 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, + 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x49, 0x64, 0x22, 0xd1, 0x01, 0x0a, 0x11, 0x46, 0x6c, 0x6f, 0x77, 0x53, 0x69, 0x67, 0x6e, + 0x65, 0x72, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x5f, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x70, + 0x75, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x10, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x61, 0x0a, 0x03, 0x54, 0x73, 0x73, 0x12, 0x18, + 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x61, 0x6e, 0x64, + 0x6f, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72, + 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x22, 0x5a, 0x0a, 0x07, 0x53, 0x69, + 0x67, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x31, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x66, 0x6b, 0x6d, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x2a, 0x32, 0x0a, 0x09, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x45, 0x56, 0x4d, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, + 0x58, 0x52, 0x50, 0x4c, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x43, 0x4f, 0x4e, 0x10, 0x02, + 0x12, 0x08, 0x0a, 0x04, 0x46, 0x4c, 0x4f, 0x57, 0x10, 0x03, 0x32, 0xed, 0x02, 0x0a, 0x0b, 0x46, + 0x6b, 0x6d, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3c, 0x0a, 0x07, 0x53, 0x69, + 0x67, 0x6e, 0x45, 0x76, 0x6d, 0x12, 0x17, 0x2e, 0x66, 0x6b, 0x6d, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x69, 0x67, 0x6e, 0x45, 0x76, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, + 0x2e, 0x66, 0x6b, 0x6d, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x45, 0x76, 0x6d, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x08, 0x53, 0x69, 0x67, 0x6e, + 0x58, 0x72, 0x70, 0x6c, 0x12, 0x18, 0x2e, 0x66, 0x6b, 0x6d, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x69, 0x67, 0x6e, 0x58, 0x72, 0x70, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, + 0x2e, 0x66, 0x6b, 0x6d, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x58, 0x72, 0x70, + 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x08, 0x53, 0x69, 0x67, + 0x6e, 0x49, 0x63, 0x6f, 0x6e, 0x12, 0x18, 0x2e, 0x66, 0x6b, 0x6d, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x69, 0x67, 0x6e, 0x49, 0x63, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x19, 0x2e, 0x66, 0x6b, 0x6d, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x49, 0x63, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x08, 0x53, 0x69, + 0x67, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x2e, 0x66, 0x6b, 0x6d, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x19, 0x2e, 0x66, 0x6b, 0x6d, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x46, + 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x12, 0x47, + 0x65, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x73, 0x12, 0x22, 0x2e, 0x66, 0x6b, 0x6d, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, + 0x69, 0x67, 0x6e, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x66, 0x6b, 0x6d, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x6e, 0x64, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x66, 0x61, 0x6c, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x66, 0x6b, 0x6d, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x66, 0x6b, 0x6d, 0x73, 0x76, + 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} var ( file_proto_fkms_v1_signer_proto_rawDescOnce sync.Once - file_proto_fkms_v1_signer_proto_rawDescData []byte + file_proto_fkms_v1_signer_proto_rawDescData = file_proto_fkms_v1_signer_proto_rawDesc ) func file_proto_fkms_v1_signer_proto_rawDescGZIP() []byte { file_proto_fkms_v1_signer_proto_rawDescOnce.Do(func() { - file_proto_fkms_v1_signer_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proto_fkms_v1_signer_proto_rawDesc), len(file_proto_fkms_v1_signer_proto_rawDesc))) + file_proto_fkms_v1_signer_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_fkms_v1_signer_proto_rawDescData) }) return file_proto_fkms_v1_signer_proto_rawDescData } var file_proto_fkms_v1_signer_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_proto_fkms_v1_signer_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_proto_fkms_v1_signer_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_proto_fkms_v1_signer_proto_goTypes = []any{ (ChainType)(0), // 0: fkms.v1.ChainType (*SignEvmRequest)(nil), // 1: fkms.v1.SignEvmRequest @@ -763,33 +1029,40 @@ var file_proto_fkms_v1_signer_proto_goTypes = []any{ (*SignXrplResponse)(nil), // 4: fkms.v1.SignXrplResponse (*SignIconRequest)(nil), // 5: fkms.v1.SignIconRequest (*SignIconResponse)(nil), // 6: fkms.v1.SignIconResponse - (*GetSignerAddressesRequest)(nil), // 7: fkms.v1.GetSignerAddressesRequest - (*GetSignerAddressesResponse)(nil), // 8: fkms.v1.GetSignerAddressesResponse - (*XrplSignerPayload)(nil), // 9: fkms.v1.XrplSignerPayload - (*IconSignerPayload)(nil), // 10: fkms.v1.IconSignerPayload - (*Tss)(nil), // 11: fkms.v1.Tss - (*Signers)(nil), // 12: fkms.v1.Signers + (*SignFlowRequest)(nil), // 7: fkms.v1.SignFlowRequest + (*SignFlowResponse)(nil), // 8: fkms.v1.SignFlowResponse + (*GetSignerAddressesRequest)(nil), // 9: fkms.v1.GetSignerAddressesRequest + (*GetSignerAddressesResponse)(nil), // 10: fkms.v1.GetSignerAddressesResponse + (*XrplSignerPayload)(nil), // 11: fkms.v1.XrplSignerPayload + (*IconSignerPayload)(nil), // 12: fkms.v1.IconSignerPayload + (*FlowSignerPayload)(nil), // 13: fkms.v1.FlowSignerPayload + (*Tss)(nil), // 14: fkms.v1.Tss + (*Signers)(nil), // 15: fkms.v1.Signers } var file_proto_fkms_v1_signer_proto_depIdxs = []int32{ - 9, // 0: fkms.v1.SignXrplRequest.signer_payload:type_name -> fkms.v1.XrplSignerPayload - 11, // 1: fkms.v1.SignXrplRequest.tss:type_name -> fkms.v1.Tss - 10, // 2: fkms.v1.SignIconRequest.signer_payload:type_name -> fkms.v1.IconSignerPayload - 11, // 3: fkms.v1.SignIconRequest.tss:type_name -> fkms.v1.Tss - 12, // 4: fkms.v1.GetSignerAddressesResponse.signers:type_name -> fkms.v1.Signers - 0, // 5: fkms.v1.Signers.chain_type:type_name -> fkms.v1.ChainType - 1, // 6: fkms.v1.FkmsService.SignEvm:input_type -> fkms.v1.SignEvmRequest - 3, // 7: fkms.v1.FkmsService.SignXrpl:input_type -> fkms.v1.SignXrplRequest - 5, // 8: fkms.v1.FkmsService.SignIcon:input_type -> fkms.v1.SignIconRequest - 7, // 9: fkms.v1.FkmsService.GetSignerAddresses:input_type -> fkms.v1.GetSignerAddressesRequest - 2, // 10: fkms.v1.FkmsService.SignEvm:output_type -> fkms.v1.SignEvmResponse - 4, // 11: fkms.v1.FkmsService.SignXrpl:output_type -> fkms.v1.SignXrplResponse - 6, // 12: fkms.v1.FkmsService.SignIcon:output_type -> fkms.v1.SignIconResponse - 8, // 13: fkms.v1.FkmsService.GetSignerAddresses:output_type -> fkms.v1.GetSignerAddressesResponse - 10, // [10:14] is the sub-list for method output_type - 6, // [6:10] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 11, // 0: fkms.v1.SignXrplRequest.signer_payload:type_name -> fkms.v1.XrplSignerPayload + 14, // 1: fkms.v1.SignXrplRequest.tss:type_name -> fkms.v1.Tss + 12, // 2: fkms.v1.SignIconRequest.signer_payload:type_name -> fkms.v1.IconSignerPayload + 14, // 3: fkms.v1.SignIconRequest.tss:type_name -> fkms.v1.Tss + 13, // 4: fkms.v1.SignFlowRequest.signer_payload:type_name -> fkms.v1.FlowSignerPayload + 14, // 5: fkms.v1.SignFlowRequest.tss:type_name -> fkms.v1.Tss + 15, // 6: fkms.v1.GetSignerAddressesResponse.signers:type_name -> fkms.v1.Signers + 0, // 7: fkms.v1.Signers.chain_type:type_name -> fkms.v1.ChainType + 1, // 8: fkms.v1.FkmsService.SignEvm:input_type -> fkms.v1.SignEvmRequest + 3, // 9: fkms.v1.FkmsService.SignXrpl:input_type -> fkms.v1.SignXrplRequest + 5, // 10: fkms.v1.FkmsService.SignIcon:input_type -> fkms.v1.SignIconRequest + 7, // 11: fkms.v1.FkmsService.SignFlow:input_type -> fkms.v1.SignFlowRequest + 9, // 12: fkms.v1.FkmsService.GetSignerAddresses:input_type -> fkms.v1.GetSignerAddressesRequest + 2, // 13: fkms.v1.FkmsService.SignEvm:output_type -> fkms.v1.SignEvmResponse + 4, // 14: fkms.v1.FkmsService.SignXrpl:output_type -> fkms.v1.SignXrplResponse + 6, // 15: fkms.v1.FkmsService.SignIcon:output_type -> fkms.v1.SignIconResponse + 8, // 16: fkms.v1.FkmsService.SignFlow:output_type -> fkms.v1.SignFlowResponse + 10, // 17: fkms.v1.FkmsService.GetSignerAddresses:output_type -> fkms.v1.GetSignerAddressesResponse + 13, // [13:18] is the sub-list for method output_type + 8, // [8:13] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_proto_fkms_v1_signer_proto_init() } @@ -801,9 +1074,9 @@ func file_proto_fkms_v1_signer_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_fkms_v1_signer_proto_rawDesc), len(file_proto_fkms_v1_signer_proto_rawDesc)), + RawDescriptor: file_proto_fkms_v1_signer_proto_rawDesc, NumEnums: 1, - NumMessages: 12, + NumMessages: 15, NumExtensions: 0, NumServices: 1, }, @@ -813,6 +1086,7 @@ func file_proto_fkms_v1_signer_proto_init() { MessageInfos: file_proto_fkms_v1_signer_proto_msgTypes, }.Build() File_proto_fkms_v1_signer_proto = out.File + file_proto_fkms_v1_signer_proto_rawDesc = nil file_proto_fkms_v1_signer_proto_goTypes = nil file_proto_fkms_v1_signer_proto_depIdxs = nil } diff --git a/proto/fkms/v1/signer.proto b/proto/fkms/v1/signer.proto index 7c6ba39..355915a 100644 --- a/proto/fkms/v1/signer.proto +++ b/proto/fkms/v1/signer.proto @@ -8,6 +8,7 @@ service FkmsService { rpc SignEvm(SignEvmRequest) returns (SignEvmResponse); rpc SignXrpl(SignXrplRequest) returns (SignXrplResponse); rpc SignIcon(SignIconRequest) returns (SignIconResponse); + rpc SignFlow(SignFlowRequest) returns (SignFlowResponse); rpc GetSignerAddresses(GetSignerAddressesRequest) returns (GetSignerAddressesResponse); } @@ -38,6 +39,15 @@ message SignIconResponse { bytes tx_params = 1; } +message SignFlowRequest { + FlowSignerPayload signer_payload = 1; + Tss tss = 2; +} + +message SignFlowResponse { + bytes tx_blob = 1; +} + message GetSignerAddressesRequest {} message GetSignerAddressesResponse { @@ -58,6 +68,15 @@ message IconSignerPayload { string network_id = 4; } +message FlowSignerPayload { + string address = 1; + uint64 compute_limit = 2; + string block_id = 3; + uint32 key_index = 4; + uint64 sequence = 5; + string contract_address = 6; +} + message Tss { bytes message = 1; bytes random_addr = 2; @@ -73,4 +92,5 @@ enum ChainType { EVM = 0; XRPL = 1; ICON = 2; + FLOW = 3; } diff --git a/proto/fkms/v1/signer_grpc.pb.go b/proto/fkms/v1/signer_grpc.pb.go index f0887a3..7059d5b 100644 --- a/proto/fkms/v1/signer_grpc.pb.go +++ b/proto/fkms/v1/signer_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.29.3 +// - protoc v6.33.1 // source: proto/fkms/v1/signer.proto package fkmsv1 @@ -22,6 +22,7 @@ const ( FkmsService_SignEvm_FullMethodName = "/fkms.v1.FkmsService/SignEvm" FkmsService_SignXrpl_FullMethodName = "/fkms.v1.FkmsService/SignXrpl" FkmsService_SignIcon_FullMethodName = "/fkms.v1.FkmsService/SignIcon" + FkmsService_SignFlow_FullMethodName = "/fkms.v1.FkmsService/SignFlow" FkmsService_GetSignerAddresses_FullMethodName = "/fkms.v1.FkmsService/GetSignerAddresses" ) @@ -32,6 +33,7 @@ type FkmsServiceClient interface { SignEvm(ctx context.Context, in *SignEvmRequest, opts ...grpc.CallOption) (*SignEvmResponse, error) SignXrpl(ctx context.Context, in *SignXrplRequest, opts ...grpc.CallOption) (*SignXrplResponse, error) SignIcon(ctx context.Context, in *SignIconRequest, opts ...grpc.CallOption) (*SignIconResponse, error) + SignFlow(ctx context.Context, in *SignFlowRequest, opts ...grpc.CallOption) (*SignFlowResponse, error) GetSignerAddresses(ctx context.Context, in *GetSignerAddressesRequest, opts ...grpc.CallOption) (*GetSignerAddressesResponse, error) } @@ -73,6 +75,16 @@ func (c *fkmsServiceClient) SignIcon(ctx context.Context, in *SignIconRequest, o return out, nil } +func (c *fkmsServiceClient) SignFlow(ctx context.Context, in *SignFlowRequest, opts ...grpc.CallOption) (*SignFlowResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SignFlowResponse) + err := c.cc.Invoke(ctx, FkmsService_SignFlow_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *fkmsServiceClient) GetSignerAddresses(ctx context.Context, in *GetSignerAddressesRequest, opts ...grpc.CallOption) (*GetSignerAddressesResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetSignerAddressesResponse) @@ -90,6 +102,7 @@ type FkmsServiceServer interface { SignEvm(context.Context, *SignEvmRequest) (*SignEvmResponse, error) SignXrpl(context.Context, *SignXrplRequest) (*SignXrplResponse, error) SignIcon(context.Context, *SignIconRequest) (*SignIconResponse, error) + SignFlow(context.Context, *SignFlowRequest) (*SignFlowResponse, error) GetSignerAddresses(context.Context, *GetSignerAddressesRequest) (*GetSignerAddressesResponse, error) mustEmbedUnimplementedFkmsServiceServer() } @@ -110,6 +123,9 @@ func (UnimplementedFkmsServiceServer) SignXrpl(context.Context, *SignXrplRequest func (UnimplementedFkmsServiceServer) SignIcon(context.Context, *SignIconRequest) (*SignIconResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SignIcon not implemented") } +func (UnimplementedFkmsServiceServer) SignFlow(context.Context, *SignFlowRequest) (*SignFlowResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SignFlow not implemented") +} func (UnimplementedFkmsServiceServer) GetSignerAddresses(context.Context, *GetSignerAddressesRequest) (*GetSignerAddressesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetSignerAddresses not implemented") } @@ -188,6 +204,24 @@ func _FkmsService_SignIcon_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _FkmsService_SignFlow_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignFlowRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FkmsServiceServer).SignFlow(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: FkmsService_SignFlow_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FkmsServiceServer).SignFlow(ctx, req.(*SignFlowRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _FkmsService_GetSignerAddresses_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetSignerAddressesRequest) if err := dec(in); err != nil { @@ -225,6 +259,10 @@ var FkmsService_ServiceDesc = grpc.ServiceDesc{ MethodName: "SignIcon", Handler: _FkmsService_SignIcon_Handler, }, + { + MethodName: "SignFlow", + Handler: _FkmsService_SignFlow_Handler, + }, { MethodName: "GetSignerAddresses", Handler: _FkmsService_GetSignerAddresses_Handler, diff --git a/relayer/chains/evm/provider.go b/relayer/chains/evm/provider.go index 0c80c87..81aad4e 100644 --- a/relayer/chains/evm/provider.go +++ b/relayer/chains/evm/provider.go @@ -466,6 +466,7 @@ func (cp *EVMChainProvider) prepareTransaction( } } + packetTimestamp := time.Unix(packet.CreatedAt, 0).UTC() tx := db.NewTransaction( txHash, packet.TunnelID, @@ -479,6 +480,7 @@ func (cp *EVMChainProvider) prepareTransaction( balanceDelta, signalPrices, blockTimestamp, + &packetTimestamp, ) return tx diff --git a/relayer/chains/flow/client.go b/relayer/chains/flow/client.go new file mode 100644 index 0000000..0b80999 --- /dev/null +++ b/relayer/chains/flow/client.go @@ -0,0 +1,322 @@ +package flow + +import ( + "context" + "fmt" + "math/big" + "sync" + "time" + + "github.com/onflow/flow-go-sdk" + flowhttp "github.com/onflow/flow-go-sdk/access/http" + + "github.com/bandprotocol/falcon/relayer/alert" + "github.com/bandprotocol/falcon/relayer/chains" + "github.com/bandprotocol/falcon/relayer/logger" +) + +// FlowClients holds Flow HTTP clients and the selected endpoint. +type FlowClients = chains.ClientPool[flowhttp.Client] + +// NewFlowClients creates and returns a new FlowClients instance with no endpoints. +func NewFlowClients() FlowClients { + return chains.NewClientPool[flowhttp.Client]() +} + +// ClientConnectionResult is the struct that contains the result of connecting to the specific endpoint. +type ClientConnectionResult struct { + Endpoint string + Client *flowhttp.Client + BlockHeight uint64 +} + +// Client defines the interface for interacting with the Flow blockchain. +type Client interface { + Connect(ctx context.Context) error + CheckAndConnect(ctx context.Context) error + StartLivelinessCheck(ctx context.Context, interval time.Duration) + GetAccount(ctx context.Context, address string) (*flow.Account, error) + GetLatestBlockID(ctx context.Context) (string, error) + BroadcastTx(ctx context.Context, txBlob []byte) (string, error) + GetTxResult(ctx context.Context, txHash string) (*flow.TransactionResult, error) + GetBalance(ctx context.Context, address string) (*big.Int, error) + GetBlockTimestamp(ctx context.Context, txHash string) (*time.Time, error) +} + +var _ Client = (*client)(nil) + +// client is the concrete implementation that handles Flow HTTP interactions. +type client struct { + ChainName string + Endpoints []string + QueryTimeout time.Duration + ExecuteTimeout time.Duration + + Log logger.Logger + alert alert.Alert + + clients FlowClients +} + +// NewClient creates a new Flow client from config. +func NewClient(chainName string, cfg *FlowChainProviderConfig, log logger.Logger, a alert.Alert) Client { + return &client{ + ChainName: chainName, + Endpoints: cfg.Endpoints, + QueryTimeout: cfg.QueryTimeout, + ExecuteTimeout: cfg.ExecuteTimeout, + Log: log.With("chain_name", chainName), + alert: a, + clients: NewFlowClients(), + } +} + +// Connect connects to all endpoints and selects the one with the highest block height. +func (c *client) Connect(_ context.Context) error { + var wg sync.WaitGroup + for _, endpoint := range c.Endpoints { + _, ok := c.clients.GetClient(endpoint) + if ok { + continue + } + + wg.Add(1) + go func(endpoint string) { + defer wg.Done() + fc, err := flowhttp.NewClient(endpoint) + if err != nil { + c.Log.Warn("Flow endpoint error", "endpoint", endpoint, err) + alert.HandleAlert( + c.alert, + alert.NewTopic(alert.ConnectSingleChainClientErrorMsg). + WithChainName(c.ChainName). + WithEndpoint(endpoint), + err.Error(), + ) + return + } + + alert.HandleReset( + c.alert, + alert.NewTopic(alert.ConnectSingleChainClientErrorMsg). + WithChainName(c.ChainName). + WithEndpoint(endpoint), + ) + c.clients.SetClient(endpoint, fc) + }(endpoint) + } + + wg.Wait() + res, err := c.getClientWithMaxHeight() + if err != nil { + c.Log.Error("Failed to connect to Flow chain", err) + return err + } + + // only log when new endpoint is used + if c.clients.GetSelectedEndpoint() != res.Endpoint { + c.Log.Info("Connected to Flow chain", "endpoint", res.Endpoint) + } + + c.clients.SetSelectedEndpoint(res.Endpoint) + + return nil +} + +// getClientWithMaxHeight selects the endpoint with the highest sealed block height. +func (c *client) getClientWithMaxHeight() (ClientConnectionResult, error) { + ch := make(chan ClientConnectionResult, len(c.Endpoints)) + + for _, endpoint := range c.Endpoints { + go func(endpoint string) { + fc, ok := c.clients.GetClient(endpoint) + if !ok { + ch <- ClientConnectionResult{endpoint, nil, 0} + return + } + + queryCtx, queryCancel := context.WithTimeout(context.Background(), c.QueryTimeout) + defer queryCancel() + bh, err := fc.GetLatestBlockHeader(queryCtx, true) + if err != nil { + c.Log.Warn("Failed to get block height", "endpoint", endpoint, err) + ch <- ClientConnectionResult{endpoint, nil, 0} + alert.HandleAlert( + c.alert, + alert.NewTopic(alert.ConnectSingleChainClientErrorMsg). + WithChainName(c.ChainName). + WithEndpoint(endpoint), + err.Error(), + ) + return + } + + c.Log.Debug("Get height of the given client", "endpoint", endpoint, "block_height", bh.Height) + alert.HandleReset( + c.alert, + alert.NewTopic(alert.ConnectSingleChainClientErrorMsg). + WithChainName(c.ChainName). + WithEndpoint(endpoint), + ) + + ch <- ClientConnectionResult{endpoint, fc, bh.Height} + }(endpoint) + } + + var result ClientConnectionResult + for range c.Endpoints { + r := <-ch + if r.Client != nil { + if r.BlockHeight > result.BlockHeight || + (r.Endpoint == c.clients.GetSelectedEndpoint() && r.BlockHeight == result.BlockHeight) { + result = r + } + } + } + + if result.Client == nil { + alert.HandleAlert( + c.alert, + alert.NewTopic(alert.ConnectAllChainClientErrorMsg).WithChainName(c.ChainName), + fmt.Sprintf("failed to connect to Flow chain on all endpoints: %s", c.Endpoints), + ) + return ClientConnectionResult{}, fmt.Errorf("[FlowClient] failed to connect to any endpoint") + } + + alert.HandleReset(c.alert, alert.NewTopic(alert.ConnectAllChainClientErrorMsg).WithChainName(c.ChainName)) + + return result, nil +} + +// CheckAndConnect connects if not already connected. +func (c *client) CheckAndConnect(ctx context.Context) error { + if _, err := c.clients.GetSelectedClient(); err != nil { + return c.Connect(ctx) + } + + return nil +} + +// StartLivelinessCheck periodically reconnects to verify endpoint health. +func (c *client) StartLivelinessCheck(ctx context.Context, interval time.Duration) { + ticker := time.NewTicker(interval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + c.Log.Info("Stopping liveliness check") + return + case <-ticker.C: + if err := c.Connect(ctx); err != nil { + c.Log.Error("Liveliness check: unable to reconnect to any endpoints", err) + } + } + } +} + +// GetAccount returns the account for the given address. +func (c *client) GetAccount(ctx context.Context, address string) (*flow.Account, error) { + fc, err := c.clients.GetSelectedClient() + if err != nil { + return nil, fmt.Errorf("[FlowClient] failed to get client: %w", err) + } + + queryCtx, queryCancel := context.WithTimeout(ctx, c.QueryTimeout) + defer queryCancel() + acc, err := fc.GetAccount(queryCtx, flow.HexToAddress(address)) + if err != nil { + return nil, fmt.Errorf("[FlowClient] failed to get account %s: %w", address, err) + } + + return acc, nil +} + +// GetLatestBlockID returns the hex-encoded ID of the latest sealed block. +func (c *client) GetLatestBlockID(ctx context.Context) (string, error) { + fc, err := c.clients.GetSelectedClient() + if err != nil { + return "", fmt.Errorf("[FlowClient] failed to get client: %w", err) + } + + queryCtx, queryCancel := context.WithTimeout(ctx, c.QueryTimeout) + defer queryCancel() + bh, err := fc.GetLatestBlockHeader(queryCtx, true) + if err != nil { + return "", fmt.Errorf("[FlowClient] failed to get latest block header: %w", err) + } + + return bh.ID.Hex(), nil +} + +// BroadcastTx decodes a flow.Transaction and broadcasts it to the network. +func (c *client) BroadcastTx(ctx context.Context, txBlob []byte) (string, error) { + fc, err := c.clients.GetSelectedClient() + if err != nil { + return "", fmt.Errorf("[FlowClient] failed to get client: %w", err) + } + + tx, err := flow.DecodeTransaction(txBlob) + if err != nil { + return "", fmt.Errorf("[FlowClient] failed to decode tx blob: %w", err) + } + + execCtx, execCancel := context.WithTimeout(ctx, c.ExecuteTimeout) + defer execCancel() + if err := fc.SendTransaction(execCtx, *tx); err != nil { + return "", fmt.Errorf("[FlowClient] failed to broadcast transaction: %w", err) + } + + return tx.ID().String(), nil +} + +// GetTxResult fetches the result of a transaction by its hash. +func (c *client) GetTxResult(ctx context.Context, txHash string) (*flow.TransactionResult, error) { + fc, err := c.clients.GetSelectedClient() + if err != nil { + return nil, fmt.Errorf("[FlowClient] failed to get client: %w", err) + } + + queryCtx, queryCancel := context.WithTimeout(ctx, c.QueryTimeout) + defer queryCancel() + result, err := fc.GetTransactionResult(queryCtx, flow.HexToID(txHash)) + if err != nil { + return nil, fmt.Errorf("[FlowClient] failed to get transaction result for %s: %w", txHash, err) + } + + return result, nil +} + +// GetBlockTimestamp fetches the block timestamp for the block containing the given transaction. +func (c *client) GetBlockTimestamp(ctx context.Context, txHash string) (*time.Time, error) { + fc, err := c.clients.GetSelectedClient() + if err != nil { + return nil, fmt.Errorf("[FlowClient] failed to get client: %w", err) + } + + queryCtx, queryCancel := context.WithTimeout(ctx, c.QueryTimeout) + defer queryCancel() + + result, err := fc.GetTransactionResult(queryCtx, flow.HexToID(txHash)) + if err != nil { + return nil, fmt.Errorf("[FlowClient] failed to get transaction result for %s: %w", txHash, err) + } + + bh, err := fc.GetBlockHeaderByID(queryCtx, result.BlockID) + if err != nil { + return nil, fmt.Errorf("[FlowClient] failed to get block header for tx %s: %w", txHash, err) + } + + t := bh.Timestamp + return &t, nil +} + +// GetBalance returns the FLOW token balance of the given address in UFix64 units (as big.Int). +func (c *client) GetBalance(ctx context.Context, address string) (*big.Int, error) { + acc, err := c.GetAccount(ctx, address) + if err != nil { + return nil, err + } + + return new(big.Int).SetUint64(acc.Balance), nil +} diff --git a/relayer/chains/flow/config.go b/relayer/chains/flow/config.go new file mode 100644 index 0000000..dfaa5a7 --- /dev/null +++ b/relayer/chains/flow/config.go @@ -0,0 +1,43 @@ +package flow + +import ( + "time" + + "github.com/bandprotocol/falcon/relayer/alert" + "github.com/bandprotocol/falcon/relayer/chains" + "github.com/bandprotocol/falcon/relayer/chains/types" + "github.com/bandprotocol/falcon/relayer/logger" + "github.com/bandprotocol/falcon/relayer/wallet" +) + +var _ chains.ChainProviderConfig = &FlowChainProviderConfig{} + +// FlowChainProviderConfig is the configuration for the Flow chain provider. +type FlowChainProviderConfig struct { + chains.BaseChainProviderConfig `mapstructure:",squash"` + + ComputeLimit uint64 `mapstructure:"compute_limit" toml:"compute_limit"` + WaitingTxDuration time.Duration `mapstructure:"waiting_tx_duration" toml:"waiting_tx_duration"` + CheckingTxInterval time.Duration `mapstructure:"checking_tx_interval" toml:"checking_tx_interval"` +} + +// NewChainProvider creates a new Flow chain provider. +func (cpc *FlowChainProviderConfig) NewChainProvider( + chainName string, + log logger.Logger, + w wallet.Wallet, + a alert.Alert, +) (chains.ChainProvider, error) { + c := NewClient(chainName, cpc, log, a) + cp, err := NewFlowChainProvider(chainName, c, cpc, log, w, a) + if err != nil { + return nil, err + } + + return cp, nil +} + +// GetChainType returns the chain type for Flow. +func (cpc *FlowChainProviderConfig) GetChainType() types.ChainType { + return types.ChainTypeFlow +} diff --git a/relayer/chains/flow/provider.go b/relayer/chains/flow/provider.go new file mode 100644 index 0000000..f88d759 --- /dev/null +++ b/relayer/chains/flow/provider.go @@ -0,0 +1,422 @@ +package flow + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + "time" + + "github.com/onflow/flow-go-sdk" + "github.com/shopspring/decimal" + + "github.com/bandprotocol/falcon/internal/relayermetrics" + "github.com/bandprotocol/falcon/relayer/alert" + bandtypes "github.com/bandprotocol/falcon/relayer/band/types" + "github.com/bandprotocol/falcon/relayer/chains" + "github.com/bandprotocol/falcon/relayer/chains/types" + "github.com/bandprotocol/falcon/relayer/db" + "github.com/bandprotocol/falcon/relayer/logger" + "github.com/bandprotocol/falcon/relayer/wallet" + flowwallet "github.com/bandprotocol/falcon/relayer/wallet/flow" +) + +const ( + flowFeeEvent = "FlowFees.FeesDeducted" + flowToWeiExp = 10 +) + +var _ chains.ChainProvider = (*FlowChainProvider)(nil) + +// FlowChainProvider handles interactions with the Flow blockchain. +type FlowChainProvider struct { + Config *FlowChainProviderConfig + ChainName string + + Client Client + + Log logger.Logger + + DB db.Database + + Alert alert.Alert + + FreeSigners chan wallet.Signer + Wallet wallet.Wallet +} + +// NewFlowChainProvider creates a new Flow chain provider. +func NewFlowChainProvider( + chainName string, + client Client, + cfg *FlowChainProviderConfig, + log logger.Logger, + w wallet.Wallet, + a alert.Alert, +) (*FlowChainProvider, error) { + return &FlowChainProvider{ + Config: cfg, + ChainName: chainName, + Client: client, + Log: log.With("chain_name", chainName), + Alert: a, + FreeSigners: chains.LoadSigners(w), + Wallet: w, + }, nil +} + +// Init connects to the Flow chain. +func (cp *FlowChainProvider) Init(ctx context.Context) error { + if err := cp.Client.Connect(ctx); err != nil { + return err + } + + go cp.Client.StartLivelinessCheck(ctx, cp.Config.LivelinessCheckingInterval) + + return nil +} + +// SetDatabase assigns the given database instance. +func (cp *FlowChainProvider) SetDatabase(database db.Database) { + cp.DB = database +} + +// QueryTunnelInfo returns an active XRPL tunnel with Skippable=true. +// No sequence is tracked here; the TunnelRelayer uses its in-memory +func (cp *FlowChainProvider) QueryTunnelInfo( + _ context.Context, + tunnelID uint64, + tunnelDestinationAddr string, +) (*types.Tunnel, error) { + tunnel := types.NewTunnel(tunnelID, tunnelDestinationAddr, true, nil, nil) + return tunnel, nil +} + +// RelayPacket relays the packet to the Flow chain. +func (cp *FlowChainProvider) RelayPacket(ctx context.Context, packet *bandtypes.Packet) error { + if err := cp.Client.CheckAndConnect(ctx); err != nil { + cp.Log.Error("Connect client error", err) + return fmt.Errorf("[FlowProvider] failed to connect client: %w", err) + } + + // Get a free signer matching the target address. + freeSigner := <-cp.FreeSigners + defer func() { cp.FreeSigners <- freeSigner }() + + log := cp.Log.With( + "tunnel_id", packet.TunnelID, + "sequence", packet.Sequence, + "signer_address", freeSigner.GetAddress(), + ) + + var lastErr error + for retryCount := 1; retryCount <= cp.Config.MaxRetry; retryCount++ { + log.Info("Relaying a message", "retry_count", retryCount) + + blockID, err := cp.Client.GetLatestBlockID(ctx) + if err != nil { + log.Error("Get latest block ID error", "retry_count", retryCount, err) + lastErr = err + continue + } + + acc, err := cp.Client.GetAccount(ctx, freeSigner.GetAddress()) + if err != nil { + log.Error("Get account error", "retry_count", retryCount, err) + lastErr = err + continue + } + + if len(acc.Keys) == 0 { + lastErr = fmt.Errorf("account %s has no keys", freeSigner.GetAddress()) + log.Error("Account has no keys", "retry_count", retryCount, lastErr) + continue + } + + keyIndex := uint32(acc.Keys[0].Index) + sequence := acc.Keys[0].SequenceNumber + + signerPayload := flowwallet.NewSignerPayload( + freeSigner.GetAddress(), + cp.Config.ComputeLimit, + blockID, + keyIndex, + sequence, + packet.TargetAddress, + ) + + payloadBytes, err := json.Marshal(signerPayload) + if err != nil { + log.Error("Marshal signer payload error", "retry_count", retryCount, err) + lastErr = err + continue + } + + signing, err := chains.SelectSigning(packet) + if err != nil { + log.Error("Select signing error", "retry_count", retryCount, err) + lastErr = err + continue + } + + rAddress, signature := chains.ExtractEVMSignature(signing.EVMSignature) + tssPayload := wallet.NewTssPayload(signing.Message, rAddress, signature) + + txBlob, err := freeSigner.Sign(payloadBytes, tssPayload) + if err != nil { + log.Error("Sign transaction error", "retry_count", retryCount, err) + lastErr = err + continue + } + + var balance *big.Int + if cp.DB != nil { + balance, err = cp.Client.GetBalance(ctx, freeSigner.GetAddress()) + if err != nil { + log.Error("Failed to get balance", "retry_count", retryCount, err) + alert.HandleAlert(cp.Alert, alert.NewTopic(alert.GetBalanceErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName), err.Error()) + } else { + alert.HandleReset(cp.Alert, alert.NewTopic(alert.GetBalanceErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName)) + } + } + + txHash, err := cp.Client.BroadcastTx(ctx, txBlob) + if err != nil { + log.Error("Broadcast transaction error", "retry_count", retryCount, err) + lastErr = err + continue + } + + createdAt := time.Now() + + log.Info( + "Submitted a message; checking transaction status", + "tx_hash", txHash, + "retry_count", retryCount, + ) + + // save pending tx in db + cp.handleSaveTransaction( + ctx, + txHash, + types.TX_STATUS_PENDING, + freeSigner.GetAddress(), + packet, + balance, + nil, + log, + retryCount, + ) + + // Poll for transaction confirmation. + txStatus, fee := cp.waitForTx(ctx, txHash, log) + + cp.handleMetrics(packet.TunnelID, createdAt, txStatus) + cp.handleSaveTransaction( + ctx, + txHash, + txStatus, + freeSigner.GetAddress(), + packet, + balance, + fee, + log, + retryCount, + ) + + if txStatus == types.TX_STATUS_SUCCESS { + alert.HandleReset( + cp.Alert, + alert.NewTopic(alert.RelayTxErrorMsg).WithTunnelID(packet.TunnelID).WithChainName(cp.ChainName), + ) + + return nil + } + + lastErr = fmt.Errorf("transaction %s ended with status %s", txHash, txStatus) + } + + alert.HandleAlert( + cp.Alert, + alert.NewTopic(alert.RelayTxErrorMsg).WithTunnelID(packet.TunnelID).WithChainName(cp.ChainName), + lastErr.Error(), + ) + + return fmt.Errorf("[FlowProvider] failed to relay packet after %d attempts", cp.Config.MaxRetry) +} + +// QueryBalance queries the FLOW balance for the given address. +func (cp *FlowChainProvider) QueryBalance(ctx context.Context, address string) (*big.Int, error) { + return cp.Client.GetBalance(ctx, address) +} + +// GetChainName retrieves the chain name from the chain provider. +func (cp *FlowChainProvider) GetChainName() string { return cp.ChainName } + +// ChainType retrieves the chain type from the chain provider. +func (cp *FlowChainProvider) ChainType() types.ChainType { + return types.ChainTypeFlow +} + +// GetWallet retrieves the wallet from the chain provider. +func (cp *FlowChainProvider) GetWallet() wallet.Wallet { + return cp.Wallet +} + +// waitForTx polls until the transaction is sealed, failed, or times out. +// Returns the final TxStatus and the fee (if extractable). +func (cp *FlowChainProvider) waitForTx( + ctx context.Context, + txHash string, + log logger.Logger, +) (types.TxStatus, *uint64) { + deadline := time.Now().Add(cp.Config.WaitingTxDuration) + for time.Now().Before(deadline) { + select { + case <-ctx.Done(): + return types.TX_STATUS_TIMEOUT, nil + default: + } + + result, err := cp.Client.GetTxResult(ctx, txHash) + if err != nil { + log.Debug("GetTxResult error, will retry", "tx_hash", txHash, err) + time.Sleep(cp.Config.CheckingTxInterval) + continue + } + + if result.Error != nil { + log.Error("Transaction failed on-chain", "tx_hash", txHash, result.Error) + return types.TX_STATUS_FAILED, extractFee(result) + } + + if result.Status == flow.TransactionStatusSealed { + fee := extractFee(result) + return types.TX_STATUS_SUCCESS, fee + } + + time.Sleep(cp.Config.CheckingTxInterval) + } + + return types.TX_STATUS_TIMEOUT, nil +} + +// extractFee extracts the fee amount from FlowFees.FeesDeducted event. +func extractFee(result *flow.TransactionResult) *uint64 { + for _, e := range result.Events { + if e.Value.EventType.QualifiedIdentifier == flowFeeEvent { + for i, f := range e.Value.GetFields() { + if f.Identifier == "amount" { + fee, ok := e.Value.GetFieldValues()[i].ToGoValue().(uint64) + if ok { + return &fee + } + } + } + } + } + + return nil +} + +// handleMetrics increments tx count and, for success/failed, records processing time (ms). +func (cp *FlowChainProvider) handleMetrics(tunnelID uint64, createdAt time.Time, txStatus types.TxStatus) { + // increment the transactions count metric + relayermetrics.IncTxsCount(tunnelID, cp.ChainName, cp.ChainType().String(), txStatus.String()) + + switch txStatus { + case types.TX_STATUS_SUCCESS, types.TX_STATUS_FAILED: + // track transaction processing time (ms) + relayermetrics.ObserveTxProcessTime( + tunnelID, + cp.ChainName, + cp.ChainType().String(), + txStatus.String(), + time.Since(createdAt).Milliseconds(), + ) + } +} + +// handleSaveTransaction saves the transaction result to the database. +func (cp *FlowChainProvider) handleSaveTransaction( + ctx context.Context, + txHash string, + txStatus types.TxStatus, + signerAddress string, + packet *bandtypes.Packet, + oldBalance *big.Int, + fee *uint64, + log logger.Logger, + retryCount int, +) { + if cp.DB == nil || txHash == "" { + return + } + + var signalPrices []db.SignalPrice + for _, p := range packet.SignalPrices { + signalPrices = append(signalPrices, *db.NewSignalPrice(p.SignalID, p.Price)) + } + + var blockTimestamp *time.Time + var err error + + feeDecimal := decimal.NullDecimal{} + balanceDelta := decimal.NullDecimal{} + + if txStatus == types.TX_STATUS_SUCCESS || txStatus == types.TX_STATUS_FAILED { + blockTimestamp, err = cp.Client.GetBlockTimestamp(ctx, txHash) + if err != nil { + log.Warn("Failed to get block timestamp", "tx_hash", txHash, err) + alert.HandleAlert(cp.Alert, alert.NewTopic(alert.GetHeaderBlockErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName), err.Error()) + } else { + alert.HandleReset(cp.Alert, alert.NewTopic(alert.GetHeaderBlockErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName)) + } + weiScale := decimal.New(1, flowToWeiExp) + if fee != nil { + feeDecimal = decimal.NewNullDecimal(decimal.NewFromBigInt(new(big.Int).SetUint64(*fee), 0).Mul(weiScale)) + } + if oldBalance != nil { + newBalance, err := cp.Client.GetBalance(ctx, signerAddress) + if err != nil { + log.Error("Failed to get balance after tx", "retry_count", retryCount, err) + alert.HandleAlert(cp.Alert, alert.NewTopic(alert.GetBalanceErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName), err.Error()) + } else { + diff := new(big.Int).Sub(newBalance, oldBalance) + balanceDelta = decimal.NewNullDecimal(decimal.NewFromBigInt(diff, 0).Mul(weiScale)) + alert.HandleReset(cp.Alert, alert.NewTopic(alert.GetBalanceErrorMsg). + WithTunnelID(packet.TunnelID). + WithChainName(cp.ChainName)) + } + } + } + + packetTimestamp := time.Unix(packet.CreatedAt, 0).UTC() + tx := db.NewTransaction( + txHash, + packet.TunnelID, + packet.Sequence, + cp.ChainName, + types.ChainTypeFlow, + signerAddress, + txStatus, + decimal.NewNullDecimal(decimal.NewFromInt(1)), // Flow doesn't expose gas units + feeDecimal, + balanceDelta, + signalPrices, + blockTimestamp, + &packetTimestamp, + ) + + chains.HandleSaveTransaction(cp.DB, cp.Alert, tx, log) +} diff --git a/relayer/chains/flow/provider_test.go b/relayer/chains/flow/provider_test.go new file mode 100644 index 0000000..cfd2783 --- /dev/null +++ b/relayer/chains/flow/provider_test.go @@ -0,0 +1,320 @@ +package flow_test + +import ( + "context" + "fmt" + "math/big" + "testing" + "time" + + cmbytes "github.com/cometbft/cometbft/libs/bytes" + flowsdk "github.com/onflow/flow-go-sdk" + "github.com/stretchr/testify/suite" + "go.uber.org/mock/gomock" + "go.uber.org/zap" + + tsstypes "github.com/bandprotocol/falcon/internal/bandchain/tss" + "github.com/bandprotocol/falcon/internal/relayertest/mocks" + "github.com/bandprotocol/falcon/relayer/alert" + bandtypes "github.com/bandprotocol/falcon/relayer/band/types" + "github.com/bandprotocol/falcon/relayer/chains" + "github.com/bandprotocol/falcon/relayer/chains/flow" + "github.com/bandprotocol/falcon/relayer/chains/types" + "github.com/bandprotocol/falcon/relayer/logger" + "github.com/bandprotocol/falcon/relayer/wallet" +) + +const testSignerAddress = "0x1234567890abcdef" + +type FlowProviderTestSuite struct { + suite.Suite + ctrl *gomock.Controller + + chainProvider *flow.FlowChainProvider + client *mocks.MockFlowClient + wallet *mocks.MockWallet + alert alert.Alert + log logger.Logger +} + +func TestFlowProviderTestSuite(t *testing.T) { + suite.Run(t, new(FlowProviderTestSuite)) +} + +func (s *FlowProviderTestSuite) SetupTest() { + s.ctrl = gomock.NewController(s.T()) + s.client = mocks.NewMockFlowClient(s.ctrl) + s.wallet = mocks.NewMockWallet(s.ctrl) + s.alert = nil + s.log = logger.NewZapLogWrapper(zap.Must(zap.NewDevelopment()).Sugar()) + + cfg := &flow.FlowChainProviderConfig{ + BaseChainProviderConfig: chains.BaseChainProviderConfig{ + MaxRetry: 3, + }, + ComputeLimit: 1000, + WaitingTxDuration: time.Second, + CheckingTxInterval: time.Millisecond, + } + + s.wallet.EXPECT().GetSigners().Return(nil) // consumed by LoadSigners in NewFlowChainProvider + + cp, err := flow.NewFlowChainProvider("flow-test", s.client, cfg, s.log, s.wallet, s.alert) + s.Require().NoError(err) + s.chainProvider = cp +} + +// newMockSigner creates a new mock signer with GetAddress returning testSignerAddress. +func (s *FlowProviderTestSuite) newMockSigner() *mocks.MockSigner { + signer := mocks.NewMockSigner(s.ctrl) + signer.EXPECT().GetAddress().Return(testSignerAddress).AnyTimes() + return signer +} + +// newTestPacket creates a minimal Packet with a non-nil CurrentGroupSigning. +func newTestPacket() *bandtypes.Packet { + return &bandtypes.Packet{ + TunnelID: 1, + Sequence: 1, + TargetAddress: "0xContractAddress", + SignalPrices: []bandtypes.SignalPrice{ + {SignalID: "CS:BAND-USD", Price: 5000}, + }, + CurrentGroupSigning: bandtypes.NewSigning( + 1, + cmbytes.HexBytes("deadbeef"), + bandtypes.NewEVMSignature( + cmbytes.HexBytes("raddress"), + cmbytes.HexBytes("signature"), + ), + tsstypes.SIGNING_STATUS_SUCCESS, + ), + } +} + +// newTestAccount creates a flow.Account with one key. +func newTestAccount() *flowsdk.Account { + return &flowsdk.Account{ + Keys: []*flowsdk.AccountKey{ + {Index: 0, SequenceNumber: 10}, + }, + } +} + +// --- Init --- + +func (s *FlowProviderTestSuite) TestInit() { + s.client.EXPECT().Connect(gomock.Any()).Return(nil) + s.client.EXPECT().StartLivelinessCheck(gomock.Any(), gomock.Any()) + + err := s.chainProvider.Init(context.Background()) + s.Require().NoError(err) + time.Sleep(10 * time.Millisecond) // wait for goroutine +} + +func (s *FlowProviderTestSuite) TestInit_ConnectError() { + s.client.EXPECT().Connect(gomock.Any()).Return(fmt.Errorf("connection failed")) + + err := s.chainProvider.Init(context.Background()) + s.Require().Error(err) + s.Contains(err.Error(), "connection failed") +} + +// --- QueryTunnelInfo --- + +func (s *FlowProviderTestSuite) TestQueryTunnelInfo() { + tunnel, err := s.chainProvider.QueryTunnelInfo(context.Background(), 1, "0xContractAddress") + s.Require().NoError(err) + s.Require().NotNil(tunnel) + s.Equal(uint64(1), tunnel.ID) + s.Equal("0xContractAddress", tunnel.TargetAddress) + s.True(tunnel.IsActive) +} + +// --- RelayPacket --- + +func (s *FlowProviderTestSuite) TestRelayPacket() { + signer := s.newMockSigner() + s.chainProvider.FreeSigners = make(chan wallet.Signer, 1) + s.chainProvider.FreeSigners <- signer + + packet := newTestPacket() + txBlob := []byte("signed-tx-blob") + + s.client.EXPECT().CheckAndConnect(gomock.Any()).Return(nil) + s.client.EXPECT().GetLatestBlockID(gomock.Any()).Return("abc123", nil) + s.client.EXPECT().GetAccount(gomock.Any(), testSignerAddress).Return(newTestAccount(), nil) + signer.EXPECT().Sign(gomock.Any(), gomock.Any()).Return(txBlob, nil) + s.client.EXPECT().BroadcastTx(gomock.Any(), txBlob).Return("txhash-001", nil) + s.client.EXPECT().GetTxResult(gomock.Any(), "txhash-001").Return(&flowsdk.TransactionResult{ + Status: flowsdk.TransactionStatusSealed, + Error: nil, + }, nil) + + err := s.chainProvider.RelayPacket(context.Background(), packet) + s.Require().NoError(err) +} + +func (s *FlowProviderTestSuite) TestRelayPacket_ConnectionError() { + signer := s.newMockSigner() + s.chainProvider.FreeSigners = make(chan wallet.Signer, 1) + s.chainProvider.FreeSigners <- signer + + s.client.EXPECT().CheckAndConnect(gomock.Any()).Return(fmt.Errorf("connection error")) + + err := s.chainProvider.RelayPacket(context.Background(), newTestPacket()) + s.Require().Error(err) + s.Contains(err.Error(), "connection error") +} + +func (s *FlowProviderTestSuite) TestRelayPacket_GetLatestBlockIDError() { + signer := s.newMockSigner() + s.chainProvider.FreeSigners = make(chan wallet.Signer, 1) + s.chainProvider.FreeSigners <- signer + + s.client.EXPECT().CheckAndConnect(gomock.Any()).Return(nil) + s.client.EXPECT().GetLatestBlockID(gomock.Any()).Return("", fmt.Errorf("rpc error")).Times(3) + + err := s.chainProvider.RelayPacket(context.Background(), newTestPacket()) + s.Require().Error(err) + s.Contains(err.Error(), "failed to relay packet after 3 attempts") +} + +func (s *FlowProviderTestSuite) TestRelayPacket_GetAccountError() { + signer := s.newMockSigner() + s.chainProvider.FreeSigners = make(chan wallet.Signer, 1) + s.chainProvider.FreeSigners <- signer + + s.client.EXPECT().CheckAndConnect(gomock.Any()).Return(nil) + s.client.EXPECT().GetLatestBlockID(gomock.Any()).Return("abc123", nil).Times(3) + s.client.EXPECT().GetAccount(gomock.Any(), testSignerAddress).Return(nil, fmt.Errorf("account not found")).Times(3) + + err := s.chainProvider.RelayPacket(context.Background(), newTestPacket()) + s.Require().Error(err) + s.Contains(err.Error(), "failed to relay packet after 3 attempts") +} + +func (s *FlowProviderTestSuite) TestRelayPacket_AccountHasNoKeys() { + signer := s.newMockSigner() + s.chainProvider.FreeSigners = make(chan wallet.Signer, 1) + s.chainProvider.FreeSigners <- signer + + emptyAccount := &flowsdk.Account{Keys: []*flowsdk.AccountKey{}} + + s.client.EXPECT().CheckAndConnect(gomock.Any()).Return(nil) + s.client.EXPECT().GetLatestBlockID(gomock.Any()).Return("abc123", nil).Times(3) + s.client.EXPECT().GetAccount(gomock.Any(), testSignerAddress).Return(emptyAccount, nil).Times(3) + + err := s.chainProvider.RelayPacket(context.Background(), newTestPacket()) + s.Require().Error(err) + s.Contains(err.Error(), "failed to relay packet after 3 attempts") +} + +func (s *FlowProviderTestSuite) TestRelayPacket_SignError() { + signer := s.newMockSigner() + s.chainProvider.FreeSigners = make(chan wallet.Signer, 1) + s.chainProvider.FreeSigners <- signer + + s.client.EXPECT().CheckAndConnect(gomock.Any()).Return(nil) + s.client.EXPECT().GetLatestBlockID(gomock.Any()).Return("abc123", nil).Times(3) + s.client.EXPECT().GetAccount(gomock.Any(), testSignerAddress).Return(newTestAccount(), nil).Times(3) + signer.EXPECT().Sign(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("sign failed")).Times(3) + + err := s.chainProvider.RelayPacket(context.Background(), newTestPacket()) + s.Require().Error(err) + s.Contains(err.Error(), "failed to relay packet after 3 attempts") +} + +func (s *FlowProviderTestSuite) TestRelayPacket_BroadcastError() { + signer := s.newMockSigner() + s.chainProvider.FreeSigners = make(chan wallet.Signer, 1) + s.chainProvider.FreeSigners <- signer + + txBlob := []byte("signed-tx-blob") + + s.client.EXPECT().CheckAndConnect(gomock.Any()).Return(nil) + s.client.EXPECT().GetLatestBlockID(gomock.Any()).Return("abc123", nil).Times(3) + s.client.EXPECT().GetAccount(gomock.Any(), testSignerAddress).Return(newTestAccount(), nil).Times(3) + signer.EXPECT().Sign(gomock.Any(), gomock.Any()).Return(txBlob, nil).Times(3) + s.client.EXPECT().BroadcastTx(gomock.Any(), txBlob).Return("", fmt.Errorf("broadcast failed")).Times(3) + + err := s.chainProvider.RelayPacket(context.Background(), newTestPacket()) + s.Require().Error(err) + s.Contains(err.Error(), "failed to relay packet after 3 attempts") +} + +func (s *FlowProviderTestSuite) TestRelayPacket_TxFailed() { + signer := s.newMockSigner() + s.chainProvider.FreeSigners = make(chan wallet.Signer, 1) + s.chainProvider.FreeSigners <- signer + + txBlob := []byte("signed-tx-blob") + + s.client.EXPECT().CheckAndConnect(gomock.Any()).Return(nil) + s.client.EXPECT().GetLatestBlockID(gomock.Any()).Return("abc123", nil).Times(3) + s.client.EXPECT().GetAccount(gomock.Any(), testSignerAddress).Return(newTestAccount(), nil).Times(3) + signer.EXPECT().Sign(gomock.Any(), gomock.Any()).Return(txBlob, nil).Times(3) + s.client.EXPECT().BroadcastTx(gomock.Any(), txBlob).Return("txhash-fail", nil).Times(3) + s.client.EXPECT().GetTxResult(gomock.Any(), "txhash-fail").Return(&flowsdk.TransactionResult{ + Status: flowsdk.TransactionStatusSealed, + Error: fmt.Errorf("execution reverted"), + }, nil).Times(3) + + err := s.chainProvider.RelayPacket(context.Background(), newTestPacket()) + s.Require().Error(err) + s.Contains(err.Error(), "failed to relay packet after 3 attempts") +} + +func (s *FlowProviderTestSuite) TestRelayPacket_MissingSigning() { + signer := s.newMockSigner() + s.chainProvider.FreeSigners = make(chan wallet.Signer, 1) + s.chainProvider.FreeSigners <- signer + + packet := &bandtypes.Packet{ + TunnelID: 1, + Sequence: 1, + TargetAddress: "0xContractAddress", + // No signing set + } + + txBlob := []byte("signed-tx-blob") + _ = txBlob + + s.client.EXPECT().CheckAndConnect(gomock.Any()).Return(nil) + s.client.EXPECT().GetLatestBlockID(gomock.Any()).Return("abc123", nil).Times(3) + s.client.EXPECT().GetAccount(gomock.Any(), testSignerAddress).Return(newTestAccount(), nil).Times(3) + + err := s.chainProvider.RelayPacket(context.Background(), packet) + s.Require().Error(err) + s.Contains(err.Error(), "failed to relay packet after 3 attempts") +} + +// --- QueryBalance --- + +func (s *FlowProviderTestSuite) TestQueryBalance() { + address := testSignerAddress + expected := big.NewInt(1_000_000_000) + s.client.EXPECT().GetBalance(gomock.Any(), address).Return(expected, nil) + + bal, err := s.chainProvider.QueryBalance(context.Background(), address) + s.Require().NoError(err) + s.Equal(expected, bal) +} + +func (s *FlowProviderTestSuite) TestQueryBalance_Error() { + s.client.EXPECT().GetBalance(gomock.Any(), testSignerAddress).Return(nil, fmt.Errorf("node unavailable")) + + bal, err := s.chainProvider.QueryBalance(context.Background(), testSignerAddress) + s.Require().Error(err) + s.Nil(bal) +} + +// --- GetChainName / ChainType --- + +func (s *FlowProviderTestSuite) TestGetChainName() { + s.Equal("flow-test", s.chainProvider.GetChainName()) +} + +func (s *FlowProviderTestSuite) TestChainType() { + s.Equal(types.ChainTypeFlow, s.chainProvider.ChainType()) +} diff --git a/relayer/chains/icon/provider.go b/relayer/chains/icon/provider.go index d85f5d9..9864510 100644 --- a/relayer/chains/icon/provider.go +++ b/relayer/chains/icon/provider.go @@ -191,11 +191,25 @@ func (cp *IconChainProvider) RelayPacket(ctx context.Context, packet *bandtypes. cp.handleMetrics(packet.TunnelID, createdAt, txResult) if cp.DB != nil { - tx := cp.prepareTransaction(ctx, txHash, freeSigner.GetAddress(), packet, &txResult, balance, log, retryCount) + tx := cp.prepareTransaction( + ctx, + txHash, + freeSigner.GetAddress(), + packet, + &txResult, + balance, + log, + retryCount, + ) chains.HandleSaveTransaction(cp.DB, cp.Alert, tx, log) } - relayermetrics.IncTxsCount(packet.TunnelID, cp.ChainName, types.ChainTypeIcon.String(), txResult.Status.String()) + relayermetrics.IncTxsCount( + packet.TunnelID, + cp.ChainName, + types.ChainTypeIcon.String(), + txResult.Status.String(), + ) if txResult.Status == types.TX_STATUS_SUCCESS { log.Info( @@ -285,7 +299,14 @@ func (cp *IconChainProvider) prepareTransaction( if txResult.BlockHeight != nil { block, err := cp.Client.GetBlockByHeight(txResult.BlockHeight) if err != nil { - log.Error("Failed to get block by height", "retry_count", retryCount, "block_height", txResult.BlockHeight, err) + log.Error( + "Failed to get block by height", + "retry_count", + retryCount, + "block_height", + txResult.BlockHeight, + err, + ) alert.HandleAlert(cp.Alert, alert.NewTopic(alert.GetHeaderBlockErrorMsg). WithTunnelID(packet.TunnelID). WithChainName(cp.ChainName), err.Error()) @@ -318,6 +339,8 @@ func (cp *IconChainProvider) prepareTransaction( } } + packetTimestamp := time.Unix(packet.CreatedAt, 0).UTC() + tx := db.NewTransaction( txHash, packet.TunnelID, @@ -331,6 +354,7 @@ func (cp *IconChainProvider) prepareTransaction( balanceDelta, signalPrices, blockTimestamp, + &packetTimestamp, ) return tx diff --git a/relayer/chains/types/chain_type.go b/relayer/chains/types/chain_type.go index e5165e5..9685801 100644 --- a/relayer/chains/types/chain_type.go +++ b/relayer/chains/types/chain_type.go @@ -17,12 +17,14 @@ const ( ChainTypeEVM ChainTypeXRPL ChainTypeIcon + ChainTypeFlow ) var chainTypeNameMap = map[ChainType]string{ ChainTypeEVM: "evm", ChainTypeXRPL: "xrpl", ChainTypeIcon: "icon", + ChainTypeFlow: "flow", } var nameToChainTypeMap map[string]ChainType diff --git a/relayer/chains/xrpl/provider.go b/relayer/chains/xrpl/provider.go index 59036d1..8e643eb 100644 --- a/relayer/chains/xrpl/provider.go +++ b/relayer/chains/xrpl/provider.go @@ -389,6 +389,7 @@ func (cp *XRPLChainProvider) prepareTransaction( } } + packetTimestamp := time.Unix(packet.CreatedAt, 0).UTC() tx := db.NewTransaction( txResult.TxHash, packet.TunnelID, @@ -402,6 +403,7 @@ func (cp *XRPLChainProvider) prepareTransaction( balanceDelta, signalPrices, closeTime, + &packetTimestamp, ) return tx diff --git a/relayer/config/config.go b/relayer/config/config.go index e09bff8..e926dd7 100644 --- a/relayer/config/config.go +++ b/relayer/config/config.go @@ -12,6 +12,7 @@ import ( "github.com/bandprotocol/falcon/relayer/band" "github.com/bandprotocol/falcon/relayer/chains" "github.com/bandprotocol/falcon/relayer/chains/evm" + "github.com/bandprotocol/falcon/relayer/chains/flow" "github.com/bandprotocol/falcon/relayer/chains/icon" chainstypes "github.com/bandprotocol/falcon/relayer/chains/types" "github.com/bandprotocol/falcon/relayer/chains/xrpl" @@ -105,6 +106,20 @@ func ParseChainProviderConfig(w ChainProviderConfigWrapper) (chains.ChainProvide return nil, err } + cfg = &newCfg + case chainstypes.ChainTypeFlow: + var newCfg flow.FlowChainProviderConfig + + decoderConfig.Result = &newCfg + decoder, err := mapstructure.NewDecoder(&decoderConfig) + if err != nil { + return nil, err + } + + if err := decoder.Decode(w); err != nil { + return nil, err + } + cfg = &newCfg default: return cfg, fmt.Errorf("unsupported chain type: %s", typeName) diff --git a/relayer/config/config_test.go b/relayer/config/config_test.go index c2401ec..881675b 100644 --- a/relayer/config/config_test.go +++ b/relayer/config/config_test.go @@ -14,6 +14,7 @@ import ( "github.com/bandprotocol/falcon/relayer/band" "github.com/bandprotocol/falcon/relayer/chains" "github.com/bandprotocol/falcon/relayer/chains/evm" + "github.com/bandprotocol/falcon/relayer/chains/flow" chainstypes "github.com/bandprotocol/falcon/relayer/chains/types" "github.com/bandprotocol/falcon/relayer/chains/xrpl" "github.com/bandprotocol/falcon/relayer/config" @@ -90,6 +91,21 @@ func TestParseChainProviderConfig(t *testing.T) { Fee: "10", }, }, + { + name: "valid flow chain", + in: config.ChainProviderConfigWrapper{ + "chain_type": "flow", + "endpoints": []string{"https://rest-testnet.onflow.org/v1"}, + "compute_limit": uint64(2000), + }, + out: &flow.FlowChainProviderConfig{ + BaseChainProviderConfig: chains.BaseChainProviderConfig{ + Endpoints: []string{"https://rest-testnet.onflow.org/v1"}, + ChainType: chainstypes.ChainTypeFlow, + }, + ComputeLimit: 2000, + }, + }, { name: "chain type not found", in: config.ChainProviderConfigWrapper{ @@ -211,3 +227,33 @@ func TestLoadXrplChainConfig(t *testing.T) { require.Equal(t, expect, actual) } + +func TestLoadFlowChainConfig(t *testing.T) { + tmpDir := t.TempDir() + cfgPath := path.Join(tmpDir, "flow_chain_config.toml") + + // write config file + err := os.WriteFile(cfgPath, []byte(relayertest.FlowChainCfgText), 0o600) + require.NoError(t, err) + + // load chain config + actual, err := config.LoadChainConfig(cfgPath) + require.NoError(t, err) + + expect := &flow.FlowChainProviderConfig{ + BaseChainProviderConfig: chains.BaseChainProviderConfig{ + Endpoints: []string{"https://rest-testnet.onflow.org/v1"}, + ChainType: chainstypes.ChainTypeFlow, + MaxRetry: 3, + ChainID: 539, + QueryTimeout: 3 * time.Second, + ExecuteTimeout: 3 * time.Second, + LivelinessCheckingInterval: 200 * time.Second, + }, + ComputeLimit: 2000, + WaitingTxDuration: 50 * time.Second, + CheckingTxInterval: 5 * time.Second, + } + + require.Equal(t, expect, actual) +} diff --git a/relayer/db/db.go b/relayer/db/db.go index 65b4d6b..1ba23b4 100644 --- a/relayer/db/db.go +++ b/relayer/db/db.go @@ -3,5 +3,4 @@ package db // Database defines the interface for the db interaction with the target chain. type Database interface { AddOrUpdateTransaction(transaction *Transaction) error - GetLatestTransaction(tunnelID uint64) (*Transaction, error) } diff --git a/relayer/db/migrations/postgres/00004_add_flow_chain_type.sql b/relayer/db/migrations/postgres/00004_add_flow_chain_type.sql new file mode 100644 index 0000000..61b4b84 --- /dev/null +++ b/relayer/db/migrations/postgres/00004_add_flow_chain_type.sql @@ -0,0 +1,7 @@ +-- +goose Up +ALTER TYPE chain_type ADD VALUE IF NOT EXISTS 'flow'; + +-- +goose Down +-- NOTE: PostgreSQL does not support removing values from an ENUM type. +-- To "undo" this, would typically have to drop and recreate the type, +-- which is dangerous if data already exists. diff --git a/relayer/db/migrations/postgres/00004_add_icon_chain_type.sql b/relayer/db/migrations/postgres/00005_add_icon_chain_type.sql similarity index 100% rename from relayer/db/migrations/postgres/00004_add_icon_chain_type.sql rename to relayer/db/migrations/postgres/00005_add_icon_chain_type.sql diff --git a/relayer/db/migrations/postgres/00006_add_packet_timestamp.sql b/relayer/db/migrations/postgres/00006_add_packet_timestamp.sql new file mode 100644 index 0000000..129dc6e --- /dev/null +++ b/relayer/db/migrations/postgres/00006_add_packet_timestamp.sql @@ -0,0 +1,5 @@ +-- +goose Up +ALTER TABLE transactions ADD COLUMN packet_timestamp TIMESTAMPTZ NULL; + +-- +goose Down +ALTER TABLE transactions DROP COLUMN packet_timestamp; diff --git a/relayer/db/migrations/sqlite/00001_create_transactions.sql b/relayer/db/migrations/sqlite/00001_create_transactions.sql index d04541f..785e9c0 100644 --- a/relayer/db/migrations/sqlite/00001_create_transactions.sql +++ b/relayer/db/migrations/sqlite/00001_create_transactions.sql @@ -7,20 +7,21 @@ CREATE TABLE transactions ( tunnel_id INTEGER NOT NULL, sequence INTEGER NOT NULL, chain_name TEXT NOT NULL, - chain_type TEXT NOT NULL CHECK (chain_type IN ('evm')), + chain_type TEXT NOT NULL CHECK (chain_type IN ('evm', 'xrpl', 'icon', 'flow')), status TEXT NOT NULL CHECK (status IN ('Pending','Success','Failed','Timeout')), sender TEXT, gas_used DECIMAL NULL, effective_gas_price DECIMAL NULL, balance_delta DECIMAL NULL, block_timestamp DATETIME NULL, - created_at DATETIME NOT NULL DEFAULT (datetime('now')), - updated_at DATETIME NOT NULL DEFAULT (datetime('now')) + packet_timestamp DATETIME NULL, + created_at DATETIME NOT NULL DEFAULT (datetime('now', 'utc')), + updated_at DATETIME NOT NULL DEFAULT (datetime('now', 'utc')) ); CREATE TABLE signal_prices ( transaction_id INTEGER NOT NULL, - signal_id TEXT NOT NULL, + signal_id TEXT NOT NULL, price INTEGER NOT NULL, PRIMARY KEY (transaction_id, signal_id), FOREIGN KEY (transaction_id) REFERENCES transactions(id) ON DELETE CASCADE diff --git a/relayer/db/sql.go b/relayer/db/sql.go index a2afda2..ca4ef9a 100644 --- a/relayer/db/sql.go +++ b/relayer/db/sql.go @@ -1,7 +1,6 @@ package db import ( - "errors" "fmt" "strings" @@ -10,15 +9,13 @@ import ( "gorm.io/gorm" "gorm.io/gorm/clause" "gorm.io/gorm/logger" - - chaintypes "github.com/bandprotocol/falcon/relayer/chains/types" ) var _ Database = &SQL{} // SQL is instance that wraps Gorm DB instance. type SQL struct { - db *gorm.DB + Db *gorm.DB } // NewSQL opens a new Gorm connection using the given driverName and dbPath. @@ -53,7 +50,7 @@ func NewSQL(dbPath string) (SQL, error) { return SQL{}, gorm.ErrUnsupportedDriver } - return SQL{db: db}, nil + return SQL{Db: db}, nil } // splitDbPath splits ":" into driver and DSN. @@ -78,29 +75,13 @@ func splitDbPath(dbPath string) (string, string, error) { // AddOrUpdateTransaction inserts a new Transaction record if none exists with the same TxHash. // If a record with the same TxHash exists, it updates the existing record with the new values. func (sql SQL) AddOrUpdateTransaction(transaction *Transaction) error { - return sql.db. + return sql.Db. Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "tx_hash"}}, DoUpdates: clause.AssignmentColumns([]string{ - "status", "gas_used", "effective_gas_price", "balance_delta", "block_timestamp", "updated_at", + "status", "gas_used", "effective_gas_price", "balance_delta", "block_timestamp", "packet_timestamp", "updated_at", }), }). Create(transaction). Error } - -func (sql SQL) GetLatestTransaction(tunnelID uint64) (*Transaction, error) { - var tx Transaction - result := sql.db. - Where("tunnel_id = ? AND status = ?", tunnelID, chaintypes.TX_STATUS_SUCCESS). - Order("block_timestamp DESC"). - First(&tx) - if result.Error != nil { - // Check if the error is specifically "Record Not Found" - if errors.Is(result.Error, gorm.ErrRecordNotFound) { - return nil, nil - } - return nil, result.Error - } - return &tx, nil -} diff --git a/relayer/db/types.go b/relayer/db/types.go index 49736ba..3e0e2f9 100644 --- a/relayer/db/types.go +++ b/relayer/db/types.go @@ -22,10 +22,11 @@ type Transaction struct { EffectiveGasPrice decimal.NullDecimal `gorm:"type:decimal"` BalanceDelta decimal.NullDecimal `gorm:"type:decimal"` - SignalPrices []SignalPrice - BlockTimestamp *time.Time `gorm:"default:NULL"` - CreatedAt time.Time - UpdatedAt time.Time + SignalPrices []SignalPrice + BlockTimestamp *time.Time `gorm:"default:NULL"` + PacketTimestamp *time.Time `gorm:"default:NULL"` + CreatedAt time.Time + UpdatedAt time.Time } // NewTransaction creates a new Transaction instance. @@ -42,6 +43,7 @@ func NewTransaction( balanceDelta decimal.NullDecimal, signalPrices []SignalPrice, blockTimestamp *time.Time, + packetTimestamp *time.Time, ) *Transaction { return &Transaction{ TxHash: txHash, @@ -56,6 +58,7 @@ func NewTransaction( BalanceDelta: balanceDelta, SignalPrices: signalPrices, BlockTimestamp: blockTimestamp, + PacketTimestamp: packetTimestamp, } } diff --git a/relayer/store/filesystem.go b/relayer/store/filesystem.go index 18211d7..3c8ad42 100644 --- a/relayer/store/filesystem.go +++ b/relayer/store/filesystem.go @@ -12,6 +12,7 @@ import ( "github.com/bandprotocol/falcon/relayer/config" "github.com/bandprotocol/falcon/relayer/wallet" "github.com/bandprotocol/falcon/relayer/wallet/evm" + "github.com/bandprotocol/falcon/relayer/wallet/flow" "github.com/bandprotocol/falcon/relayer/wallet/icon" "github.com/bandprotocol/falcon/relayer/wallet/xrpl" ) @@ -113,6 +114,8 @@ func (fs *FileSystem) NewWallet(chainType chainstypes.ChainType, chainName, pass return xrpl.NewWallet(passphrase, fs.HomePath, chainName) case chainstypes.ChainTypeIcon: return icon.NewWallet(passphrase, fs.HomePath, chainName) + case chainstypes.ChainTypeFlow: + return flow.NewWallet(passphrase, fs.HomePath, chainName) default: return nil, fmt.Errorf("unsupported chain type: %s", chainType) } diff --git a/relayer/wallet/flow/remote_signer.go b/relayer/wallet/flow/remote_signer.go new file mode 100644 index 0000000..a42890d --- /dev/null +++ b/relayer/wallet/flow/remote_signer.go @@ -0,0 +1,59 @@ +package flow + +import ( + "encoding/json" + + fkmsv1 "github.com/bandprotocol/falcon/proto/fkms/v1" + "github.com/bandprotocol/falcon/relayer/wallet" +) + +var _ wallet.Signer = (*RemoteSigner)(nil) + +// RemoteSigner is a signer that uses the KMS service to sign Flow transactions. +type RemoteSigner struct { + wallet.BaseRemoteSigner +} + +// NewRemoteSigner creates a new Flow RemoteSigner. +// Its signature matches wallet.RemoteSignerFactory so it can be passed +// directly to wallet.NewRemoteOnlyAdapter. +func NewRemoteSigner(name, address, url string, key string) (wallet.Signer, error) { + base, err := wallet.NewBaseRemoteSigner(name, address, url, key) + if err != nil { + return nil, err + } + + return &RemoteSigner{BaseRemoteSigner: *base}, nil +} + +// Sign requests the remote KMS to sign the Flow transaction and returns the signed tx blob. +func (r *RemoteSigner) Sign(payload []byte, tssPayload wallet.TssPayload) ([]byte, error) { + var signerPayload SignerPayload + if err := json.Unmarshal(payload, &signerPayload); err != nil { + return nil, err + } + + res, err := r.FkmsClient.SignFlow( + r.ContextWithKey(), + &fkmsv1.SignFlowRequest{ + SignerPayload: &fkmsv1.FlowSignerPayload{ + Address: signerPayload.Address, + ComputeLimit: signerPayload.ComputeLimit, + BlockId: signerPayload.BlockID, + KeyIndex: signerPayload.KeyIndex, + Sequence: signerPayload.Sequence, + ContractAddress: signerPayload.ContractAddress, + }, + Tss: &fkmsv1.Tss{ + Message: tssPayload.TssMessage, + RandomAddr: tssPayload.RandomAddr, + SignatureS: tssPayload.Signature, + }, + }, + ) + if err != nil { + return nil, err + } + + return res.TxBlob, nil +} diff --git a/relayer/wallet/flow/types.go b/relayer/wallet/flow/types.go new file mode 100644 index 0000000..9bbea25 --- /dev/null +++ b/relayer/wallet/flow/types.go @@ -0,0 +1,23 @@ +package flow + +// SignerPayload is the payload passed (JSON-serialized) to the Flow signer. +type SignerPayload struct { + Address string + ComputeLimit uint64 + BlockID string + KeyIndex uint32 + Sequence uint64 + ContractAddress string +} + +// NewSignerPayload creates a new SignerPayload. +func NewSignerPayload(address string, computeLimit uint64, blockID string, keyIndex uint32, sequence uint64, contractAddress string) SignerPayload { + return SignerPayload{ + Address: address, + ComputeLimit: computeLimit, + BlockID: blockID, + KeyIndex: keyIndex, + Sequence: sequence, + ContractAddress: contractAddress, + } +} diff --git a/relayer/wallet/flow/wallet.go b/relayer/wallet/flow/wallet.go new file mode 100644 index 0000000..3925892 --- /dev/null +++ b/relayer/wallet/flow/wallet.go @@ -0,0 +1,9 @@ +package flow + +import "github.com/bandprotocol/falcon/relayer/wallet" + +// NewWallet creates a new wallet for the Flow chain. +// Flow is remote-only — local key import is not supported. +func NewWallet(passphrase, homePath, chainName string) (*wallet.BaseWallet, error) { + return wallet.NewBaseWallet(passphrase, homePath, chainName, wallet.NewRemoteOnlyAdapter(NewRemoteSigner)) +}