mirror of
https://github.com/aljazceru/ark.git
synced 2025-12-18 12:44:19 +01:00
Rename folders (#97)
* Rename arkd folder & drop cli * Rename ark cli folder & update docs * Update readme * Fix * scripts: add build-all * Add target to build cli for all platforms * Update build scripts --------- Co-authored-by: tiero <3596602+tiero@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
0d8c7bffb2
commit
dc00d60585
24
server/.gitignore
vendored
Executable file
24
server/.gitignore
vendored
Executable file
@@ -0,0 +1,24 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with 'go test -c'
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
/build/
|
||||
/dist/
|
||||
|
||||
DS_Store
|
||||
._.DS_Store
|
||||
**/.DS_Store
|
||||
**/._.DS_Store
|
||||
|
||||
|
||||
go.work
|
||||
go.work.sum
|
||||
99
server/.goreleaser.yaml
Executable file
99
server/.goreleaser.yaml
Executable file
@@ -0,0 +1,99 @@
|
||||
builds:
|
||||
- id: "arkd"
|
||||
main: ./cmd/arkd
|
||||
ldflags:
|
||||
- -s -X 'main.version={{.Version}}' -X 'main.commit={{.Commit}}' -X 'main.date={{.Date}}'
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
binary: arkd
|
||||
|
||||
## flag the semver v**.**.**-<tag>.* as pre-release on Github
|
||||
release:
|
||||
prerelease: auto
|
||||
|
||||
checksum:
|
||||
name_template: "checksums.txt"
|
||||
|
||||
snapshot:
|
||||
name_template: "{{ .Tag }}-next"
|
||||
|
||||
changelog:
|
||||
use: github-native
|
||||
|
||||
archives:
|
||||
- id: arkd
|
||||
format: binary
|
||||
builds:
|
||||
- arkd
|
||||
name_template: "arkd-v{{ .Version }}-{{ .Os }}-{{ .Arch }}"
|
||||
|
||||
dockers:
|
||||
###########################
|
||||
# tag latest & prerelease #
|
||||
###########################
|
||||
|
||||
#amd64
|
||||
- image_templates:
|
||||
- "ghcr.io/ark-network/arkd:{{ .Tag }}-amd64"
|
||||
# push always either release or prerelease with a docker tag with the semver only
|
||||
skip_push: "false"
|
||||
use: buildx
|
||||
ids:
|
||||
- arkd
|
||||
dockerfile: goreleaser.Dockerfile
|
||||
# GOOS of the built binaries/packages that should be used.
|
||||
goos: linux
|
||||
# GOARCH of the built binaries/packages that should be used.
|
||||
goarch: amd64
|
||||
# Template of the docker build flags.
|
||||
build_flag_templates:
|
||||
- "--platform=linux/amd64"
|
||||
- "--pull"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.title=arkd"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||
- "--build-arg=VERSION={{.Version}}"
|
||||
- "--build-arg=COMMIT={{.Commit}}"
|
||||
- "--build-arg=DATE={{.Date}}"
|
||||
- image_templates:
|
||||
- "ghcr.io/ark-network/arkd:{{ .Tag }}-arm64v8"
|
||||
# push always either release or prerelease with a docker tag with the semver only
|
||||
skip_push: "false"
|
||||
use: buildx
|
||||
ids:
|
||||
- arkd
|
||||
dockerfile: goreleaser.Dockerfile
|
||||
# GOOS of the built binaries/packages that should be used.
|
||||
goos: linux
|
||||
# GOARCH of the built binaries/packages that should be used.
|
||||
goarch: arm64
|
||||
# Template of the docker build flags.
|
||||
build_flag_templates:
|
||||
- "--platform=linux/arm64/v8"
|
||||
- "--pull"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.title=arkd"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||
- "--build-arg=VERSION={{.Version}}"
|
||||
- "--build-arg=COMMIT={{.Commit}}"
|
||||
- "--build-arg=DATE={{.Date}}"
|
||||
|
||||
docker_manifests:
|
||||
- name_template: ghcr.io/ark-network/arkd:{{ .Tag }}
|
||||
image_templates:
|
||||
- ghcr.io/ark-network/arkd:{{ .Tag }}-amd64
|
||||
- ghcr.io/ark-network/arkd:{{ .Tag }}-arm64v8
|
||||
skip_push: "false"
|
||||
|
||||
- name_template: ghcr.io/ark-network/arkd:latest
|
||||
image_templates:
|
||||
- ghcr.io/ark-network/arkd:{{ .Tag }}-amd64
|
||||
- ghcr.io/ark-network/arkd:{{ .Tag }}-arm64v8
|
||||
skip_push: auto
|
||||
|
||||
22
server/LICENSE
Executable file
22
server/LICENSE
Executable file
@@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 ark-network
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
61
server/Makefile
Executable file
61
server/Makefile
Executable file
@@ -0,0 +1,61 @@
|
||||
.PHONY: build clean cov help intergrationtest lint run test vet proto proto-lint
|
||||
|
||||
## build: build for all platforms
|
||||
build:
|
||||
@echo "Building arkd binary..."
|
||||
@bash ./scripts/build
|
||||
|
||||
## clean: cleans the binary
|
||||
clean:
|
||||
@echo "Cleaning..."
|
||||
@go clean
|
||||
|
||||
## cov: generates coverage report
|
||||
cov:
|
||||
@echo "Coverage..."
|
||||
@go test -cover ./...
|
||||
|
||||
## help: prints this help message
|
||||
help:
|
||||
@echo "Usage: \n"
|
||||
@sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /'
|
||||
|
||||
## intergrationtest: runs integration tests
|
||||
integrationtest:
|
||||
@echo "Running integration tests..."
|
||||
@find . -name go.mod -execdir go test -v -count=1 -race $(go list ./... | grep internal/test) \;
|
||||
|
||||
## lint: lint codebase
|
||||
lint:
|
||||
@echo "Linting code..."
|
||||
@golangci-lint run --fix
|
||||
|
||||
## run: run in dev mode
|
||||
run: clean
|
||||
@echo "Running arkd in dev mode..."
|
||||
@export ARK_WALLET_ADDR=localhost:18000; \
|
||||
export ARK_ROUND_INTERVAL=30; \
|
||||
go run ./cmd/arkd
|
||||
|
||||
## test: runs unit and component tests
|
||||
test:
|
||||
@echo "Running unit tests..."
|
||||
@find . -name go.mod -execdir go test -v -count=1 -race ./... $(go list ./... | grep -v internal/test) \;
|
||||
|
||||
## vet: code analysis
|
||||
vet:
|
||||
@echo "Running code analysis..."
|
||||
@go vet ./...
|
||||
|
||||
## proto: compile proto stubs
|
||||
proto: proto-lint
|
||||
@echo "Compiling stubs..."
|
||||
@docker run --volume "$(shell pwd):/workspace" --workdir /workspace buf generate buf.build/vulpemventures/ocean
|
||||
@docker run --volume "$(shell pwd):/workspace" --workdir /workspace buf generate
|
||||
|
||||
## proto-lint: lint protos
|
||||
proto-lint:
|
||||
@echo "Linting protos..."
|
||||
@docker build -q -t buf -f buf.Dockerfile . &> /dev/null
|
||||
@docker run --volume "$(shell pwd):/workspace" --workdir /workspace buf lint
|
||||
|
||||
0
server/README.md
Executable file
0
server/README.md
Executable file
568
server/api-spec/openapi/swagger/ark/v1/service.swagger.json
Normal file
568
server/api-spec/openapi/swagger/ark/v1/service.swagger.json
Normal file
@@ -0,0 +1,568 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "ark/v1/service.proto",
|
||||
"version": "version not set"
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "ArkService"
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"paths": {
|
||||
"/v1/events": {
|
||||
"get": {
|
||||
"operationId": "ArkService_GetEventStream",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.(streaming responses)",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/v1GetEventStreamResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
},
|
||||
"title": "Stream result of v1GetEventStreamResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/faucet/{address}": {
|
||||
"post": {
|
||||
"operationId": "ArkService_Faucet",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1FaucetResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "address",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/payment/claim": {
|
||||
"post": {
|
||||
"operationId": "ArkService_ClaimPayment",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1ClaimPaymentResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1ClaimPaymentRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/payment/finalize": {
|
||||
"post": {
|
||||
"operationId": "ArkService_FinalizePayment",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1FinalizePaymentResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1FinalizePaymentRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/payment/register": {
|
||||
"post": {
|
||||
"operationId": "ArkService_RegisterPayment",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1RegisterPaymentResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1RegisterPaymentRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/ping/{paymentId}": {
|
||||
"get": {
|
||||
"operationId": "ArkService_Ping",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1PingResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "paymentId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/pubkey": {
|
||||
"get": {
|
||||
"operationId": "ArkService_GetPubkey",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1GetPubkeyResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/round/{txid}": {
|
||||
"get": {
|
||||
"operationId": "ArkService_GetRound",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1GetRoundResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "txid",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/vtxos/{address}": {
|
||||
"get": {
|
||||
"operationId": "ArkService_ListVtxos",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1ListVtxosResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "address",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"ArkService"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"@type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"rpcStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1ClaimPaymentRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Mocks wabisabi's credentials."
|
||||
},
|
||||
"outputs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1Output"
|
||||
},
|
||||
"description": "List of receivers for a registered payment."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1ClaimPaymentResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1FaucetResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1FinalizePaymentRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"signedForfeitTxs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Forfeit txs signed by the user."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1FinalizePaymentResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1GetEventStreamResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"roundFinalization": {
|
||||
"$ref": "#/definitions/v1RoundFinalizationEvent"
|
||||
},
|
||||
"roundFinalized": {
|
||||
"$ref": "#/definitions/v1RoundFinalizedEvent"
|
||||
},
|
||||
"roundFailed": {
|
||||
"$ref": "#/definitions/v1RoundFailed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1GetPubkeyResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pubkey": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1GetRoundResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"round": {
|
||||
"$ref": "#/definitions/v1Round"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1Input": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txid": {
|
||||
"type": "string"
|
||||
},
|
||||
"vout": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1ListVtxosResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"vtxos": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1Vtxo"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1Node": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txid": {
|
||||
"type": "string"
|
||||
},
|
||||
"tx": {
|
||||
"type": "string"
|
||||
},
|
||||
"parentTxid": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1Output": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {
|
||||
"type": "string",
|
||||
"description": "Either the offchain or onchain address."
|
||||
},
|
||||
"amount": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Amount to send in satoshis."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1PingResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1RegisterPaymentRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"inputs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1Input"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1RegisterPaymentResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Mocks wabisabi's credentials."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1Round": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"start": {
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"end": {
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"txid": {
|
||||
"type": "string"
|
||||
},
|
||||
"congestionTree": {
|
||||
"$ref": "#/definitions/v1Tree"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1RoundFailed": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1RoundFinalizationEvent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"poolPartialTx": {
|
||||
"type": "string"
|
||||
},
|
||||
"forfeitTxs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"congestionTree": {
|
||||
"$ref": "#/definitions/v1Tree"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1RoundFinalizedEvent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"poolTxid": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1Tree": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"levels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1TreeLevel"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1TreeLevel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nodes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1Node"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1Vtxo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"outpoint": {
|
||||
"$ref": "#/definitions/v1Input"
|
||||
},
|
||||
"receiver": {
|
||||
"$ref": "#/definitions/v1Output"
|
||||
},
|
||||
"spent": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"poolTxid": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
325
server/api-spec/openapi/swagger/ocean/v1/account.swagger.json
Normal file
325
server/api-spec/openapi/swagger/ocean/v1/account.swagger.json
Normal file
@@ -0,0 +1,325 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "ocean/v1/account.proto",
|
||||
"version": "version not set"
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "AccountService"
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"paths": {},
|
||||
"definitions": {
|
||||
"TemplateFormat": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"FORMAT_UNSPECIFIED",
|
||||
"FORMAT_DESCRIPTOR",
|
||||
"FORMAT_MINISCRIPT",
|
||||
"FORMAT_IONIO",
|
||||
"FORMAT_RAW"
|
||||
],
|
||||
"default": "FORMAT_UNSPECIFIED"
|
||||
},
|
||||
"googlerpcStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"@type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"v1AccountInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"namespace": {
|
||||
"type": "string",
|
||||
"description": "Account namespace."
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
"description": "Account label."
|
||||
},
|
||||
"derivationPath": {
|
||||
"type": "string",
|
||||
"description": "Derivation path."
|
||||
},
|
||||
"xpubs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Xpubs."
|
||||
},
|
||||
"masterBlindingKey": {
|
||||
"type": "string",
|
||||
"description": "The master blinding key of the account to derive blinding keypairs from."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1BalanceInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"confirmedBalance": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Balance of utxos with 1+ confirmations."
|
||||
},
|
||||
"unconfirmedBalance": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Balance of utxos with no confirmations."
|
||||
},
|
||||
"lockedBalance": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Balance of locked utxos."
|
||||
},
|
||||
"totalBalance": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Total balance."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1BalanceResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"balance": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/v1BalanceInfo"
|
||||
},
|
||||
"description": "The balance (total, confirmed, unconfirmed) per each asset."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1BlockDetails": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"hash": {
|
||||
"type": "string",
|
||||
"description": "Hash of the block."
|
||||
},
|
||||
"height": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Heighth (index) of the block."
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"description": "Timestamp of the block."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1CreateAccountBIP44Response": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"info": {
|
||||
"$ref": "#/definitions/v1AccountInfo",
|
||||
"description": "Info about the new account."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1CreateAccountCustomResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"info": {
|
||||
"$ref": "#/definitions/v1AccountInfo",
|
||||
"description": "Info about the new account."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1CreateAccountMultiSigResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"info": {
|
||||
"$ref": "#/definitions/v1AccountInfo",
|
||||
"description": "Info about the new account."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1DeleteAccountResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1DeriveAddressesResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"addresses": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1DeriveChangeAddressesResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"addresses": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1ListAddressesResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"addresses": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1ListUtxosResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"spendableUtxos": {
|
||||
"$ref": "#/definitions/v1Utxos",
|
||||
"description": "List of spendable utxos."
|
||||
},
|
||||
"lockedUtxos": {
|
||||
"$ref": "#/definitions/v1Utxos",
|
||||
"description": "List of currently locked utxos."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1SetAccountLabelResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"info": {
|
||||
"$ref": "#/definitions/v1AccountInfo",
|
||||
"description": "Info about the updated account."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1SetAccountTemplateResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1Template": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"format": {
|
||||
"$ref": "#/definitions/TemplateFormat"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1Utxo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txid": {
|
||||
"type": "string",
|
||||
"description": "Txid of the uxo."
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Output index."
|
||||
},
|
||||
"asset": {
|
||||
"type": "string",
|
||||
"description": "Asset."
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Value."
|
||||
},
|
||||
"script": {
|
||||
"type": "string",
|
||||
"description": "Script."
|
||||
},
|
||||
"assetBlinder": {
|
||||
"type": "string",
|
||||
"description": "Asset blinder for confidential utxo."
|
||||
},
|
||||
"valueBlinder": {
|
||||
"type": "string",
|
||||
"description": "Value blinder for confidential utxo."
|
||||
},
|
||||
"accountName": {
|
||||
"type": "string",
|
||||
"description": "Namespace of the account owning the utxo."
|
||||
},
|
||||
"spentStatus": {
|
||||
"$ref": "#/definitions/v1UtxoStatus",
|
||||
"description": "Info about utxo's spent status."
|
||||
},
|
||||
"confirmedStatus": {
|
||||
"$ref": "#/definitions/v1UtxoStatus",
|
||||
"description": "Info about utxo's confirmation status."
|
||||
},
|
||||
"redeemScript": {
|
||||
"type": "string",
|
||||
"description": "Redeem script locking the utxo in case its owned by a multisig account."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1UtxoStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txid": {
|
||||
"type": "string"
|
||||
},
|
||||
"blockInfo": {
|
||||
"$ref": "#/definitions/v1BlockDetails"
|
||||
},
|
||||
"txhex": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1Utxos": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"accountName": {
|
||||
"type": "string",
|
||||
"description": "Account namespace."
|
||||
},
|
||||
"utxos": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1Utxo"
|
||||
},
|
||||
"description": "List of utxos."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "ocean/v1/notification.proto",
|
||||
"version": "version not set"
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "NotificationService"
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"paths": {},
|
||||
"definitions": {
|
||||
"googlerpcStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"@type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"v1AddWebhookResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The id of the new webhook."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1BlockDetails": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"hash": {
|
||||
"type": "string",
|
||||
"description": "Hash of the block."
|
||||
},
|
||||
"height": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Heighth (index) of the block."
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"description": "Timestamp of the block."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1ListWebhooksResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"webhookInfo": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1WebhookInfo"
|
||||
},
|
||||
"description": "The list of info about the webhooks regitered for an action."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1RemoveWebhookResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1TransactionNotificationsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"eventType": {
|
||||
"$ref": "#/definitions/v1TxEventType",
|
||||
"description": "Tx event type."
|
||||
},
|
||||
"accountNames": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Account names."
|
||||
},
|
||||
"txhex": {
|
||||
"type": "string",
|
||||
"description": "Tx in hex format."
|
||||
},
|
||||
"txid": {
|
||||
"type": "string",
|
||||
"description": "Txid of transaction."
|
||||
},
|
||||
"blockDetails": {
|
||||
"$ref": "#/definitions/v1BlockDetails",
|
||||
"description": "Details of the block including the tx."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1TxEventType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"TX_EVENT_TYPE_UNSPECIFIED",
|
||||
"TX_EVENT_TYPE_BROADCASTED",
|
||||
"TX_EVENT_TYPE_UNCONFIRMED",
|
||||
"TX_EVENT_TYPE_CONFIRMED"
|
||||
],
|
||||
"default": "TX_EVENT_TYPE_UNSPECIFIED",
|
||||
"description": " - TX_EVENT_TYPE_BROADCASTED: Tx broadcasted.\n - TX_EVENT_TYPE_UNCONFIRMED: Tx unconfirmed.\n - TX_EVENT_TYPE_CONFIRMED: Tx confirmed."
|
||||
},
|
||||
"v1UnwatchExternalScriptResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1Utxo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txid": {
|
||||
"type": "string",
|
||||
"description": "Txid of the uxo."
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Output index."
|
||||
},
|
||||
"asset": {
|
||||
"type": "string",
|
||||
"description": "Asset."
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Value."
|
||||
},
|
||||
"script": {
|
||||
"type": "string",
|
||||
"description": "Script."
|
||||
},
|
||||
"assetBlinder": {
|
||||
"type": "string",
|
||||
"description": "Asset blinder for confidential utxo."
|
||||
},
|
||||
"valueBlinder": {
|
||||
"type": "string",
|
||||
"description": "Value blinder for confidential utxo."
|
||||
},
|
||||
"accountName": {
|
||||
"type": "string",
|
||||
"description": "Namespace of the account owning the utxo."
|
||||
},
|
||||
"spentStatus": {
|
||||
"$ref": "#/definitions/v1UtxoStatus",
|
||||
"description": "Info about utxo's spent status."
|
||||
},
|
||||
"confirmedStatus": {
|
||||
"$ref": "#/definitions/v1UtxoStatus",
|
||||
"description": "Info about utxo's confirmation status."
|
||||
},
|
||||
"redeemScript": {
|
||||
"type": "string",
|
||||
"description": "Redeem script locking the utxo in case its owned by a multisig account."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1UtxoEventType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"UTXO_EVENT_TYPE_UNSPECIFIED",
|
||||
"UTXO_EVENT_TYPE_NEW",
|
||||
"UTXO_EVENT_TYPE_CONFIRMED",
|
||||
"UTXO_EVENT_TYPE_LOCKED",
|
||||
"UTXO_EVENT_TYPE_UNLOCKED",
|
||||
"UTXO_EVENT_TYPE_SPENT",
|
||||
"UTXO_EVENT_TYPE_CONFIRMED_SPENT"
|
||||
],
|
||||
"default": "UTXO_EVENT_TYPE_UNSPECIFIED"
|
||||
},
|
||||
"v1UtxoStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txid": {
|
||||
"type": "string"
|
||||
},
|
||||
"blockInfo": {
|
||||
"$ref": "#/definitions/v1BlockDetails"
|
||||
},
|
||||
"txhex": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1UtxosNotificationsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"eventType": {
|
||||
"$ref": "#/definitions/v1UtxoEventType",
|
||||
"description": "The event's type occured for the utxos."
|
||||
},
|
||||
"utxos": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1Utxo"
|
||||
},
|
||||
"description": "List of utxos for which occured the event."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1WatchExternalScriptResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1WebhookEventType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"WEBHOOK_EVENT_TYPE_UNSPECIFIED",
|
||||
"WEBHOOK_EVENT_TYPE_TRANSACTION",
|
||||
"WEBHOOK_EVENT_TYPE_UTXO"
|
||||
],
|
||||
"default": "WEBHOOK_EVENT_TYPE_UNSPECIFIED",
|
||||
"description": " - WEBHOOK_EVENT_TYPE_TRANSACTION: Receive notification about transactions.\n - WEBHOOK_EVENT_TYPE_UTXO: Receive notifications about utxos."
|
||||
},
|
||||
"v1WebhookInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The id of the webhook."
|
||||
},
|
||||
"endpoint": {
|
||||
"type": "string",
|
||||
"description": "The endpoint of the external service to reach."
|
||||
},
|
||||
"isSecured": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the outgoing requests are authenticated."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "ocean/v1/transaction.proto",
|
||||
"version": "version not set"
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "TransactionService"
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"paths": {},
|
||||
"definitions": {
|
||||
"SelectUtxosRequestStrategy": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"STRATEGY_UNSPECIFIED",
|
||||
"STRATEGY_BRANCH_BOUND",
|
||||
"STRATEGY_FRAGMENT"
|
||||
],
|
||||
"default": "STRATEGY_UNSPECIFIED",
|
||||
"description": "Coin-selection algorithm."
|
||||
},
|
||||
"googlerpcStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"@type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"v1BlindPsetResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pset": {
|
||||
"type": "string",
|
||||
"description": "Updated partial transaction with blinded inputs/outputs in base64 format."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1BlockDetails": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"hash": {
|
||||
"type": "string",
|
||||
"description": "Hash of the block."
|
||||
},
|
||||
"height": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Heighth (index) of the block."
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"description": "Timestamp of the block."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1BroadcastTransactionResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txid": {
|
||||
"type": "string",
|
||||
"description": "Hash of the broadcasted transaction."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1BurnResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txHex": {
|
||||
"type": "string",
|
||||
"description": "Signed tx in hex format."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1ClaimPegInResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txHex": {
|
||||
"type": "string",
|
||||
"description": "Signed tx in hex format."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1CreatePsetResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pset": {
|
||||
"type": "string",
|
||||
"description": "New partial transaction in base64 format."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1EstimateFeesResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"feeAmount": {
|
||||
"type": "string",
|
||||
"format": "uint64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1GetTransactionResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txHex": {
|
||||
"type": "string",
|
||||
"description": "Raw transaction in hex format."
|
||||
},
|
||||
"blockDetails": {
|
||||
"$ref": "#/definitions/v1BlockDetails",
|
||||
"description": "Deatils of the block including the transaction."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1Input": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txid": {
|
||||
"type": "string",
|
||||
"description": "Previous output txid."
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Previous tx output index."
|
||||
},
|
||||
"script": {
|
||||
"type": "string",
|
||||
"description": "Prevout script."
|
||||
},
|
||||
"scriptsigSize": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Input scriptsig size."
|
||||
},
|
||||
"witnessSize": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Input witness size."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1MintResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txHex": {
|
||||
"type": "string",
|
||||
"description": "Signed tx in hex format."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1Output": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"asset": {
|
||||
"type": "string",
|
||||
"description": "Asset hash."
|
||||
},
|
||||
"amount": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Sent amount."
|
||||
},
|
||||
"address": {
|
||||
"type": "string",
|
||||
"description": "Address to send funds to."
|
||||
},
|
||||
"script": {
|
||||
"type": "string",
|
||||
"description": "ScriptPubkey to send funds to (alternative to address)."
|
||||
},
|
||||
"blindingPubkey": {
|
||||
"type": "string",
|
||||
"description": "Blinding public key to make output confidential (alternative to address)."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1PegInAddressResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"accountName": {
|
||||
"type": "string",
|
||||
"description": "Account name."
|
||||
},
|
||||
"mainChainAddress": {
|
||||
"type": "string",
|
||||
"description": "Main-chain deposit address to send bitcoin to."
|
||||
},
|
||||
"claimScript": {
|
||||
"type": "string",
|
||||
"description": "Claim script committed to by the main-chain address."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1RemintResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txHex": {
|
||||
"type": "string",
|
||||
"description": "Signed tx in hex format."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1SelectUtxosResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"utxos": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1Utxo"
|
||||
},
|
||||
"description": "List of selected utxos."
|
||||
},
|
||||
"change": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Eventual change amount if utxos cumulative sum exceeds the target amount."
|
||||
},
|
||||
"expirationDate": {
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"description": "Expiration date for the selected utxo, which are temporary locked to\nprevent double spending them."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1SignPsetResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pset": {
|
||||
"type": "string",
|
||||
"description": "Signed partial transaction in base64 format."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1SignPsetWithSchnorrKeyResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"signedTx": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1SignTransactionResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txHex": {
|
||||
"type": "string",
|
||||
"description": "Raw signed transaction."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1TransferResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txHex": {
|
||||
"type": "string",
|
||||
"description": "Signed tx in hex format."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1UnblindedInput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Index of the pset input."
|
||||
},
|
||||
"asset": {
|
||||
"type": "string",
|
||||
"description": "Revealed prevout asset."
|
||||
},
|
||||
"amount": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Revealed prevout amount."
|
||||
},
|
||||
"assetBlinder": {
|
||||
"type": "string",
|
||||
"description": "Prevout asset blinder."
|
||||
},
|
||||
"amountBlinder": {
|
||||
"type": "string",
|
||||
"description": "Prevout amount blinder."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1UpdatePsetResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pset": {
|
||||
"type": "string",
|
||||
"description": "Updated partial transaction in base64 format."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1Utxo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txid": {
|
||||
"type": "string",
|
||||
"description": "Txid of the uxo."
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Output index."
|
||||
},
|
||||
"asset": {
|
||||
"type": "string",
|
||||
"description": "Asset."
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Value."
|
||||
},
|
||||
"script": {
|
||||
"type": "string",
|
||||
"description": "Script."
|
||||
},
|
||||
"assetBlinder": {
|
||||
"type": "string",
|
||||
"description": "Asset blinder for confidential utxo."
|
||||
},
|
||||
"valueBlinder": {
|
||||
"type": "string",
|
||||
"description": "Value blinder for confidential utxo."
|
||||
},
|
||||
"accountName": {
|
||||
"type": "string",
|
||||
"description": "Namespace of the account owning the utxo."
|
||||
},
|
||||
"spentStatus": {
|
||||
"$ref": "#/definitions/v1UtxoStatus",
|
||||
"description": "Info about utxo's spent status."
|
||||
},
|
||||
"confirmedStatus": {
|
||||
"$ref": "#/definitions/v1UtxoStatus",
|
||||
"description": "Info about utxo's confirmation status."
|
||||
},
|
||||
"redeemScript": {
|
||||
"type": "string",
|
||||
"description": "Redeem script locking the utxo in case its owned by a multisig account."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1UtxoStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txid": {
|
||||
"type": "string"
|
||||
},
|
||||
"blockInfo": {
|
||||
"$ref": "#/definitions/v1BlockDetails"
|
||||
},
|
||||
"txhex": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
server/api-spec/openapi/swagger/ocean/v1/types.swagger.json
Normal file
44
server/api-spec/openapi/swagger/ocean/v1/types.swagger.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "ocean/v1/types.proto",
|
||||
"version": "version not set"
|
||||
},
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"paths": {},
|
||||
"definitions": {
|
||||
"googlerpcStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"@type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
197
server/api-spec/openapi/swagger/ocean/v1/wallet.swagger.json
Normal file
197
server/api-spec/openapi/swagger/ocean/v1/wallet.swagger.json
Normal file
@@ -0,0 +1,197 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "ocean/v1/wallet.proto",
|
||||
"version": "version not set"
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "WalletService"
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"paths": {},
|
||||
"definitions": {
|
||||
"GetInfoResponseNetwork": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"NETWORK_UNSPECIFIED",
|
||||
"NETWORK_MAINNET",
|
||||
"NETWORK_TESTNET",
|
||||
"NETWORK_REGTEST"
|
||||
],
|
||||
"default": "NETWORK_UNSPECIFIED"
|
||||
},
|
||||
"googlerpcStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"@type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"v1AccountInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"namespace": {
|
||||
"type": "string",
|
||||
"description": "Account namespace."
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
"description": "Account label."
|
||||
},
|
||||
"derivationPath": {
|
||||
"type": "string",
|
||||
"description": "Derivation path."
|
||||
},
|
||||
"xpubs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Xpubs."
|
||||
},
|
||||
"masterBlindingKey": {
|
||||
"type": "string",
|
||||
"description": "The master blinding key of the account to derive blinding keypairs from."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1AuthResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"verified": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1BuildInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Number of the version."
|
||||
},
|
||||
"commit": {
|
||||
"type": "string",
|
||||
"description": "Hash of the commit."
|
||||
},
|
||||
"date": {
|
||||
"type": "string",
|
||||
"description": "Date of the commit."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1ChangePasswordResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1CreateWalletResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1GenSeedResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mnemonic": {
|
||||
"type": "string",
|
||||
"description": "A mnemonic from where deriving signing and blinding key pairs."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1GetInfoResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"network": {
|
||||
"$ref": "#/definitions/GetInfoResponseNetwork",
|
||||
"title": "The Liquid network of the wallet"
|
||||
},
|
||||
"nativeAsset": {
|
||||
"type": "string",
|
||||
"description": "The Liquid Bitcoin (LBTC) asset hash of the network."
|
||||
},
|
||||
"rootPath": {
|
||||
"type": "string",
|
||||
"description": "The root derivation path of the HD wallet."
|
||||
},
|
||||
"birthdayBlockHash": {
|
||||
"type": "string",
|
||||
"description": "The hash of the block at wallet creation time."
|
||||
},
|
||||
"birthdayBlockHeight": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "The height of the block at wallet creation time."
|
||||
},
|
||||
"accounts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/v1AccountInfo"
|
||||
},
|
||||
"description": "List containing info about the wallet accounts."
|
||||
},
|
||||
"buildInfo": {
|
||||
"$ref": "#/definitions/v1BuildInfo",
|
||||
"description": "Info about the current version of the ocean wallet."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1LockResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v1RestoreWalletResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "String message returned within the process."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1StatusResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"initialized": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the wallet is initialized with seeds."
|
||||
},
|
||||
"synced": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the wallet is in sync, meaning it's keeping track of every utxo\nof every account."
|
||||
},
|
||||
"unlocked": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the wallet is unlocked."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1UnlockResponse": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
179
server/api-spec/protobuf/ark/v1/service.proto
Executable file
179
server/api-spec/protobuf/ark/v1/service.proto
Executable file
@@ -0,0 +1,179 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package ark.v1;
|
||||
|
||||
import "google/api/annotations.proto";
|
||||
|
||||
service ArkService {
|
||||
rpc RegisterPayment(RegisterPaymentRequest) returns (RegisterPaymentResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/payment/register"
|
||||
body: "*"
|
||||
};
|
||||
};
|
||||
rpc ClaimPayment(ClaimPaymentRequest) returns (ClaimPaymentResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/payment/claim"
|
||||
body: "*"
|
||||
};
|
||||
};
|
||||
rpc FinalizePayment(FinalizePaymentRequest) returns (FinalizePaymentResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/payment/finalize"
|
||||
body: "*"
|
||||
};
|
||||
};
|
||||
rpc GetRound(GetRoundRequest) returns (GetRoundResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/v1/round/{txid}"
|
||||
};
|
||||
};
|
||||
rpc GetEventStream(GetEventStreamRequest) returns (stream GetEventStreamResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/v1/events"
|
||||
};
|
||||
};
|
||||
rpc Ping(PingRequest) returns (PingResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/v1/ping/{payment_id}"
|
||||
};
|
||||
};
|
||||
rpc Faucet(FaucetRequest) returns (FaucetResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/faucet/{address}"
|
||||
};
|
||||
}
|
||||
rpc ListVtxos(ListVtxosRequest) returns (ListVtxosResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/v1/vtxos/{address}"
|
||||
};
|
||||
}
|
||||
rpc GetPubkey(GetPubkeyRequest) returns (GetPubkeyResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/v1/pubkey"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
message RegisterPaymentRequest {
|
||||
repeated Input inputs = 1;
|
||||
}
|
||||
message RegisterPaymentResponse {
|
||||
// Mocks wabisabi's credentials.
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message ClaimPaymentRequest {
|
||||
// Mocks wabisabi's credentials.
|
||||
string id = 1;
|
||||
// List of receivers for a registered payment.
|
||||
repeated Output outputs = 2;
|
||||
}
|
||||
message ClaimPaymentResponse {}
|
||||
|
||||
message FinalizePaymentRequest {
|
||||
// Forfeit txs signed by the user.
|
||||
repeated string signed_forfeit_txs = 1;
|
||||
}
|
||||
message FinalizePaymentResponse {}
|
||||
|
||||
message GetRoundRequest {
|
||||
string txid = 1;
|
||||
}
|
||||
message GetRoundResponse {
|
||||
Round round = 1;
|
||||
}
|
||||
|
||||
message Round {
|
||||
string id = 1;
|
||||
int64 start = 2;
|
||||
int64 end = 3;
|
||||
string txid = 4;
|
||||
Tree congestion_tree = 5;
|
||||
}
|
||||
|
||||
message Input {
|
||||
string txid = 1;
|
||||
uint32 vout = 2;
|
||||
}
|
||||
|
||||
message Output {
|
||||
// Either the offchain or onchain address.
|
||||
string address = 1;
|
||||
// Amount to send in satoshis.
|
||||
uint64 amount = 2;
|
||||
}
|
||||
|
||||
message RoundFinalizationEvent {
|
||||
string id = 1;
|
||||
string pool_partial_tx = 2;
|
||||
repeated string forfeit_txs = 3;
|
||||
Tree congestion_tree = 4;
|
||||
}
|
||||
|
||||
message Tree {
|
||||
repeated TreeLevel levels = 1;
|
||||
}
|
||||
|
||||
message TreeLevel {
|
||||
repeated Node nodes = 1;
|
||||
}
|
||||
|
||||
message Node {
|
||||
string txid = 1;
|
||||
string tx = 2;
|
||||
string parent_txid = 3;
|
||||
}
|
||||
|
||||
message RoundFinalizedEvent {
|
||||
string id = 1;
|
||||
string pool_txid = 2;
|
||||
}
|
||||
|
||||
message RoundFailed {
|
||||
string id = 1;
|
||||
string reason = 2;
|
||||
}
|
||||
|
||||
message GetEventStreamRequest {}
|
||||
|
||||
message GetEventStreamResponse {
|
||||
oneof event {
|
||||
RoundFinalizationEvent round_finalization = 1;
|
||||
RoundFinalizedEvent round_finalized = 2;
|
||||
RoundFailed round_failed = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message PingRequest {
|
||||
string payment_id = 1;
|
||||
}
|
||||
|
||||
message PingResponse {}
|
||||
|
||||
message FaucetRequest {
|
||||
string address = 1;
|
||||
}
|
||||
|
||||
message FaucetResponse {}
|
||||
|
||||
message ListVtxosRequest {
|
||||
string address = 1;
|
||||
}
|
||||
|
||||
message ListVtxosResponse {
|
||||
repeated Vtxo vtxos = 1;
|
||||
}
|
||||
|
||||
message Vtxo {
|
||||
Input outpoint = 1;
|
||||
Output receiver = 2;
|
||||
bool spent = 3;
|
||||
string pool_txid = 4;
|
||||
}
|
||||
|
||||
message GetPubkeyRequest {}
|
||||
|
||||
message GetPubkeyResponse {
|
||||
string pubkey = 1;
|
||||
}
|
||||
7
server/api-spec/protobuf/buf.lock
Normal file
7
server/api-spec/protobuf/buf.lock
Normal file
@@ -0,0 +1,7 @@
|
||||
# Generated by buf. DO NOT EDIT.
|
||||
version: v1
|
||||
deps:
|
||||
- remote: buf.build
|
||||
owner: googleapis
|
||||
repository: googleapis
|
||||
commit: b30c5775bfb3485d9da2e87b26590ac9
|
||||
12
server/api-spec/protobuf/buf.yaml
Executable file
12
server/api-spec/protobuf/buf.yaml
Executable file
@@ -0,0 +1,12 @@
|
||||
version: v1
|
||||
name: buf.build/ark-network/ark
|
||||
deps:
|
||||
- buf.build/googleapis/googleapis
|
||||
breaking:
|
||||
use:
|
||||
- FILE
|
||||
ignore_unstable_packages: true
|
||||
lint:
|
||||
use:
|
||||
- DEFAULT
|
||||
|
||||
2114
server/api-spec/protobuf/gen/ark/v1/service.pb.go
Normal file
2114
server/api-spec/protobuf/gen/ark/v1/service.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
848
server/api-spec/protobuf/gen/ark/v1/service.pb.gw.go
Normal file
848
server/api-spec/protobuf/gen/ark/v1/service.pb.gw.go
Normal file
@@ -0,0 +1,848 @@
|
||||
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
|
||||
// source: ark/v1/service.proto
|
||||
|
||||
/*
|
||||
Package arkv1 is a reverse proxy.
|
||||
|
||||
It translates gRPC into RESTful JSON APIs.
|
||||
*/
|
||||
package arkv1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// Suppress "imported and not used" errors
|
||||
var _ codes.Code
|
||||
var _ io.Reader
|
||||
var _ status.Status
|
||||
var _ = runtime.String
|
||||
var _ = utilities.NewDoubleArray
|
||||
var _ = metadata.Join
|
||||
|
||||
func request_ArkService_RegisterPayment_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq RegisterPaymentRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.RegisterPayment(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_ArkService_RegisterPayment_0(ctx context.Context, marshaler runtime.Marshaler, server ArkServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq RegisterPaymentRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.RegisterPayment(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_ArkService_ClaimPayment_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq ClaimPaymentRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.ClaimPayment(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_ArkService_ClaimPayment_0(ctx context.Context, marshaler runtime.Marshaler, server ArkServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq ClaimPaymentRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.ClaimPayment(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_ArkService_FinalizePayment_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq FinalizePaymentRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.FinalizePayment(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_ArkService_FinalizePayment_0(ctx context.Context, marshaler runtime.Marshaler, server ArkServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq FinalizePaymentRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.FinalizePayment(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_ArkService_GetRound_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetRoundRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["txid"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "txid")
|
||||
}
|
||||
|
||||
protoReq.Txid, err = runtime.String(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "txid", err)
|
||||
}
|
||||
|
||||
msg, err := client.GetRound(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_ArkService_GetRound_0(ctx context.Context, marshaler runtime.Marshaler, server ArkServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetRoundRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["txid"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "txid")
|
||||
}
|
||||
|
||||
protoReq.Txid, err = runtime.String(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "txid", err)
|
||||
}
|
||||
|
||||
msg, err := server.GetRound(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_ArkService_GetEventStream_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (ArkService_GetEventStreamClient, runtime.ServerMetadata, error) {
|
||||
var protoReq GetEventStreamRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
stream, err := client.GetEventStream(ctx, &protoReq)
|
||||
if err != nil {
|
||||
return nil, metadata, err
|
||||
}
|
||||
header, err := stream.Header()
|
||||
if err != nil {
|
||||
return nil, metadata, err
|
||||
}
|
||||
metadata.HeaderMD = header
|
||||
return stream, metadata, nil
|
||||
|
||||
}
|
||||
|
||||
func request_ArkService_Ping_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq PingRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["payment_id"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "payment_id")
|
||||
}
|
||||
|
||||
protoReq.PaymentId, err = runtime.String(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "payment_id", err)
|
||||
}
|
||||
|
||||
msg, err := client.Ping(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_ArkService_Ping_0(ctx context.Context, marshaler runtime.Marshaler, server ArkServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq PingRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["payment_id"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "payment_id")
|
||||
}
|
||||
|
||||
protoReq.PaymentId, err = runtime.String(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "payment_id", err)
|
||||
}
|
||||
|
||||
msg, err := server.Ping(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_ArkService_Faucet_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq FaucetRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["address"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address")
|
||||
}
|
||||
|
||||
protoReq.Address, err = runtime.String(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err)
|
||||
}
|
||||
|
||||
msg, err := client.Faucet(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_ArkService_Faucet_0(ctx context.Context, marshaler runtime.Marshaler, server ArkServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq FaucetRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["address"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address")
|
||||
}
|
||||
|
||||
protoReq.Address, err = runtime.String(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err)
|
||||
}
|
||||
|
||||
msg, err := server.Faucet(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_ArkService_ListVtxos_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq ListVtxosRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["address"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address")
|
||||
}
|
||||
|
||||
protoReq.Address, err = runtime.String(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err)
|
||||
}
|
||||
|
||||
msg, err := client.ListVtxos(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_ArkService_ListVtxos_0(ctx context.Context, marshaler runtime.Marshaler, server ArkServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq ListVtxosRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["address"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address")
|
||||
}
|
||||
|
||||
protoReq.Address, err = runtime.String(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err)
|
||||
}
|
||||
|
||||
msg, err := server.ListVtxos(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_ArkService_GetPubkey_0(ctx context.Context, marshaler runtime.Marshaler, client ArkServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetPubkeyRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
msg, err := client.GetPubkey(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_ArkService_GetPubkey_0(ctx context.Context, marshaler runtime.Marshaler, server ArkServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetPubkeyRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
msg, err := server.GetPubkey(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
// RegisterArkServiceHandlerServer registers the http handlers for service ArkService to "mux".
|
||||
// UnaryRPC :call ArkServiceServer directly.
|
||||
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
|
||||
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterArkServiceHandlerFromEndpoint instead.
|
||||
func RegisterArkServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ArkServiceServer) error {
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_RegisterPayment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ark.v1.ArkService/RegisterPayment", runtime.WithHTTPPathPattern("/v1/payment/register"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_ArkService_RegisterPayment_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_RegisterPayment_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_ClaimPayment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ark.v1.ArkService/ClaimPayment", runtime.WithHTTPPathPattern("/v1/payment/claim"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_ArkService_ClaimPayment_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_ClaimPayment_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_FinalizePayment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ark.v1.ArkService/FinalizePayment", runtime.WithHTTPPathPattern("/v1/payment/finalize"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_ArkService_FinalizePayment_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_FinalizePayment_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_ArkService_GetRound_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ark.v1.ArkService/GetRound", runtime.WithHTTPPathPattern("/v1/round/{txid}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_ArkService_GetRound_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_GetRound_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_ArkService_GetEventStream_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport")
|
||||
_, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_ArkService_Ping_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ark.v1.ArkService/Ping", runtime.WithHTTPPathPattern("/v1/ping/{payment_id}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_ArkService_Ping_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_Ping_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_Faucet_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ark.v1.ArkService/Faucet", runtime.WithHTTPPathPattern("/v1/faucet/{address}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_ArkService_Faucet_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_Faucet_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_ArkService_ListVtxos_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ark.v1.ArkService/ListVtxos", runtime.WithHTTPPathPattern("/v1/vtxos/{address}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_ArkService_ListVtxos_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_ListVtxos_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_ArkService_GetPubkey_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ark.v1.ArkService/GetPubkey", runtime.WithHTTPPathPattern("/v1/pubkey"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_ArkService_GetPubkey_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_GetPubkey_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterArkServiceHandlerFromEndpoint is same as RegisterArkServiceHandler but
|
||||
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
|
||||
func RegisterArkServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
|
||||
conn, err := grpc.DialContext(ctx, endpoint, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if cerr := conn.Close(); cerr != nil {
|
||||
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
||||
}
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
if cerr := conn.Close(); cerr != nil {
|
||||
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
||||
}
|
||||
}()
|
||||
}()
|
||||
|
||||
return RegisterArkServiceHandler(ctx, mux, conn)
|
||||
}
|
||||
|
||||
// RegisterArkServiceHandler registers the http handlers for service ArkService to "mux".
|
||||
// The handlers forward requests to the grpc endpoint over "conn".
|
||||
func RegisterArkServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
|
||||
return RegisterArkServiceHandlerClient(ctx, mux, NewArkServiceClient(conn))
|
||||
}
|
||||
|
||||
// RegisterArkServiceHandlerClient registers the http handlers for service ArkService
|
||||
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ArkServiceClient".
|
||||
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ArkServiceClient"
|
||||
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
|
||||
// "ArkServiceClient" to call the correct interceptors.
|
||||
func RegisterArkServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ArkServiceClient) error {
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_RegisterPayment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/RegisterPayment", runtime.WithHTTPPathPattern("/v1/payment/register"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ArkService_RegisterPayment_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_RegisterPayment_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_ClaimPayment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/ClaimPayment", runtime.WithHTTPPathPattern("/v1/payment/claim"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ArkService_ClaimPayment_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_ClaimPayment_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_FinalizePayment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/FinalizePayment", runtime.WithHTTPPathPattern("/v1/payment/finalize"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ArkService_FinalizePayment_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_FinalizePayment_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_ArkService_GetRound_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/GetRound", runtime.WithHTTPPathPattern("/v1/round/{txid}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ArkService_GetRound_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_GetRound_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_ArkService_GetEventStream_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/GetEventStream", runtime.WithHTTPPathPattern("/v1/events"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ArkService_GetEventStream_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_GetEventStream_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_ArkService_Ping_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/Ping", runtime.WithHTTPPathPattern("/v1/ping/{payment_id}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ArkService_Ping_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_Ping_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ArkService_Faucet_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/Faucet", runtime.WithHTTPPathPattern("/v1/faucet/{address}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ArkService_Faucet_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_Faucet_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_ArkService_ListVtxos_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/ListVtxos", runtime.WithHTTPPathPattern("/v1/vtxos/{address}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ArkService_ListVtxos_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_ListVtxos_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_ArkService_GetPubkey_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ark.v1.ArkService/GetPubkey", runtime.WithHTTPPathPattern("/v1/pubkey"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ArkService_GetPubkey_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ArkService_GetPubkey_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
pattern_ArkService_RegisterPayment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "payment", "register"}, ""))
|
||||
|
||||
pattern_ArkService_ClaimPayment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "payment", "claim"}, ""))
|
||||
|
||||
pattern_ArkService_FinalizePayment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "payment", "finalize"}, ""))
|
||||
|
||||
pattern_ArkService_GetRound_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "round", "txid"}, ""))
|
||||
|
||||
pattern_ArkService_GetEventStream_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "events"}, ""))
|
||||
|
||||
pattern_ArkService_Ping_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "ping", "payment_id"}, ""))
|
||||
|
||||
pattern_ArkService_Faucet_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "faucet", "address"}, ""))
|
||||
|
||||
pattern_ArkService_ListVtxos_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "vtxos", "address"}, ""))
|
||||
|
||||
pattern_ArkService_GetPubkey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "pubkey"}, ""))
|
||||
)
|
||||
|
||||
var (
|
||||
forward_ArkService_RegisterPayment_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ArkService_ClaimPayment_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ArkService_FinalizePayment_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ArkService_GetRound_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ArkService_GetEventStream_0 = runtime.ForwardResponseStream
|
||||
|
||||
forward_ArkService_Ping_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ArkService_Faucet_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ArkService_ListVtxos_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ArkService_GetPubkey_0 = runtime.ForwardResponseMessage
|
||||
)
|
||||
415
server/api-spec/protobuf/gen/ark/v1/service_grpc.pb.go
Normal file
415
server/api-spec/protobuf/gen/ark/v1/service_grpc.pb.go
Normal file
@@ -0,0 +1,415 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
|
||||
package arkv1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// ArkServiceClient is the client API for ArkService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type ArkServiceClient interface {
|
||||
RegisterPayment(ctx context.Context, in *RegisterPaymentRequest, opts ...grpc.CallOption) (*RegisterPaymentResponse, error)
|
||||
ClaimPayment(ctx context.Context, in *ClaimPaymentRequest, opts ...grpc.CallOption) (*ClaimPaymentResponse, error)
|
||||
FinalizePayment(ctx context.Context, in *FinalizePaymentRequest, opts ...grpc.CallOption) (*FinalizePaymentResponse, error)
|
||||
GetRound(ctx context.Context, in *GetRoundRequest, opts ...grpc.CallOption) (*GetRoundResponse, error)
|
||||
GetEventStream(ctx context.Context, in *GetEventStreamRequest, opts ...grpc.CallOption) (ArkService_GetEventStreamClient, error)
|
||||
Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error)
|
||||
Faucet(ctx context.Context, in *FaucetRequest, opts ...grpc.CallOption) (*FaucetResponse, error)
|
||||
ListVtxos(ctx context.Context, in *ListVtxosRequest, opts ...grpc.CallOption) (*ListVtxosResponse, error)
|
||||
GetPubkey(ctx context.Context, in *GetPubkeyRequest, opts ...grpc.CallOption) (*GetPubkeyResponse, error)
|
||||
}
|
||||
|
||||
type arkServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewArkServiceClient(cc grpc.ClientConnInterface) ArkServiceClient {
|
||||
return &arkServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *arkServiceClient) RegisterPayment(ctx context.Context, in *RegisterPaymentRequest, opts ...grpc.CallOption) (*RegisterPaymentResponse, error) {
|
||||
out := new(RegisterPaymentResponse)
|
||||
err := c.cc.Invoke(ctx, "/ark.v1.ArkService/RegisterPayment", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *arkServiceClient) ClaimPayment(ctx context.Context, in *ClaimPaymentRequest, opts ...grpc.CallOption) (*ClaimPaymentResponse, error) {
|
||||
out := new(ClaimPaymentResponse)
|
||||
err := c.cc.Invoke(ctx, "/ark.v1.ArkService/ClaimPayment", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *arkServiceClient) FinalizePayment(ctx context.Context, in *FinalizePaymentRequest, opts ...grpc.CallOption) (*FinalizePaymentResponse, error) {
|
||||
out := new(FinalizePaymentResponse)
|
||||
err := c.cc.Invoke(ctx, "/ark.v1.ArkService/FinalizePayment", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *arkServiceClient) GetRound(ctx context.Context, in *GetRoundRequest, opts ...grpc.CallOption) (*GetRoundResponse, error) {
|
||||
out := new(GetRoundResponse)
|
||||
err := c.cc.Invoke(ctx, "/ark.v1.ArkService/GetRound", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *arkServiceClient) GetEventStream(ctx context.Context, in *GetEventStreamRequest, opts ...grpc.CallOption) (ArkService_GetEventStreamClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &ArkService_ServiceDesc.Streams[0], "/ark.v1.ArkService/GetEventStream", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &arkServiceGetEventStreamClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type ArkService_GetEventStreamClient interface {
|
||||
Recv() (*GetEventStreamResponse, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type arkServiceGetEventStreamClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *arkServiceGetEventStreamClient) Recv() (*GetEventStreamResponse, error) {
|
||||
m := new(GetEventStreamResponse)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *arkServiceClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) {
|
||||
out := new(PingResponse)
|
||||
err := c.cc.Invoke(ctx, "/ark.v1.ArkService/Ping", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *arkServiceClient) Faucet(ctx context.Context, in *FaucetRequest, opts ...grpc.CallOption) (*FaucetResponse, error) {
|
||||
out := new(FaucetResponse)
|
||||
err := c.cc.Invoke(ctx, "/ark.v1.ArkService/Faucet", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *arkServiceClient) ListVtxos(ctx context.Context, in *ListVtxosRequest, opts ...grpc.CallOption) (*ListVtxosResponse, error) {
|
||||
out := new(ListVtxosResponse)
|
||||
err := c.cc.Invoke(ctx, "/ark.v1.ArkService/ListVtxos", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *arkServiceClient) GetPubkey(ctx context.Context, in *GetPubkeyRequest, opts ...grpc.CallOption) (*GetPubkeyResponse, error) {
|
||||
out := new(GetPubkeyResponse)
|
||||
err := c.cc.Invoke(ctx, "/ark.v1.ArkService/GetPubkey", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ArkServiceServer is the server API for ArkService service.
|
||||
// All implementations should embed UnimplementedArkServiceServer
|
||||
// for forward compatibility
|
||||
type ArkServiceServer interface {
|
||||
RegisterPayment(context.Context, *RegisterPaymentRequest) (*RegisterPaymentResponse, error)
|
||||
ClaimPayment(context.Context, *ClaimPaymentRequest) (*ClaimPaymentResponse, error)
|
||||
FinalizePayment(context.Context, *FinalizePaymentRequest) (*FinalizePaymentResponse, error)
|
||||
GetRound(context.Context, *GetRoundRequest) (*GetRoundResponse, error)
|
||||
GetEventStream(*GetEventStreamRequest, ArkService_GetEventStreamServer) error
|
||||
Ping(context.Context, *PingRequest) (*PingResponse, error)
|
||||
Faucet(context.Context, *FaucetRequest) (*FaucetResponse, error)
|
||||
ListVtxos(context.Context, *ListVtxosRequest) (*ListVtxosResponse, error)
|
||||
GetPubkey(context.Context, *GetPubkeyRequest) (*GetPubkeyResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedArkServiceServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedArkServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedArkServiceServer) RegisterPayment(context.Context, *RegisterPaymentRequest) (*RegisterPaymentResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RegisterPayment not implemented")
|
||||
}
|
||||
func (UnimplementedArkServiceServer) ClaimPayment(context.Context, *ClaimPaymentRequest) (*ClaimPaymentResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ClaimPayment not implemented")
|
||||
}
|
||||
func (UnimplementedArkServiceServer) FinalizePayment(context.Context, *FinalizePaymentRequest) (*FinalizePaymentResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method FinalizePayment not implemented")
|
||||
}
|
||||
func (UnimplementedArkServiceServer) GetRound(context.Context, *GetRoundRequest) (*GetRoundResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetRound not implemented")
|
||||
}
|
||||
func (UnimplementedArkServiceServer) GetEventStream(*GetEventStreamRequest, ArkService_GetEventStreamServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method GetEventStream not implemented")
|
||||
}
|
||||
func (UnimplementedArkServiceServer) Ping(context.Context, *PingRequest) (*PingResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented")
|
||||
}
|
||||
func (UnimplementedArkServiceServer) Faucet(context.Context, *FaucetRequest) (*FaucetResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Faucet not implemented")
|
||||
}
|
||||
func (UnimplementedArkServiceServer) ListVtxos(context.Context, *ListVtxosRequest) (*ListVtxosResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListVtxos not implemented")
|
||||
}
|
||||
func (UnimplementedArkServiceServer) GetPubkey(context.Context, *GetPubkeyRequest) (*GetPubkeyResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetPubkey not implemented")
|
||||
}
|
||||
|
||||
// UnsafeArkServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to ArkServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeArkServiceServer interface {
|
||||
mustEmbedUnimplementedArkServiceServer()
|
||||
}
|
||||
|
||||
func RegisterArkServiceServer(s grpc.ServiceRegistrar, srv ArkServiceServer) {
|
||||
s.RegisterService(&ArkService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _ArkService_RegisterPayment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RegisterPaymentRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ArkServiceServer).RegisterPayment(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ark.v1.ArkService/RegisterPayment",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ArkServiceServer).RegisterPayment(ctx, req.(*RegisterPaymentRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ArkService_ClaimPayment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ClaimPaymentRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ArkServiceServer).ClaimPayment(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ark.v1.ArkService/ClaimPayment",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ArkServiceServer).ClaimPayment(ctx, req.(*ClaimPaymentRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ArkService_FinalizePayment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(FinalizePaymentRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ArkServiceServer).FinalizePayment(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ark.v1.ArkService/FinalizePayment",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ArkServiceServer).FinalizePayment(ctx, req.(*FinalizePaymentRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ArkService_GetRound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetRoundRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ArkServiceServer).GetRound(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ark.v1.ArkService/GetRound",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ArkServiceServer).GetRound(ctx, req.(*GetRoundRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ArkService_GetEventStream_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(GetEventStreamRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(ArkServiceServer).GetEventStream(m, &arkServiceGetEventStreamServer{stream})
|
||||
}
|
||||
|
||||
type ArkService_GetEventStreamServer interface {
|
||||
Send(*GetEventStreamResponse) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type arkServiceGetEventStreamServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *arkServiceGetEventStreamServer) Send(m *GetEventStreamResponse) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func _ArkService_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PingRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ArkServiceServer).Ping(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ark.v1.ArkService/Ping",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ArkServiceServer).Ping(ctx, req.(*PingRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ArkService_Faucet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(FaucetRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ArkServiceServer).Faucet(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ark.v1.ArkService/Faucet",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ArkServiceServer).Faucet(ctx, req.(*FaucetRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ArkService_ListVtxos_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListVtxosRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ArkServiceServer).ListVtxos(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ark.v1.ArkService/ListVtxos",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ArkServiceServer).ListVtxos(ctx, req.(*ListVtxosRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ArkService_GetPubkey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetPubkeyRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ArkServiceServer).GetPubkey(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ark.v1.ArkService/GetPubkey",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ArkServiceServer).GetPubkey(ctx, req.(*GetPubkeyRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// ArkService_ServiceDesc is the grpc.ServiceDesc for ArkService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var ArkService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "ark.v1.ArkService",
|
||||
HandlerType: (*ArkServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "RegisterPayment",
|
||||
Handler: _ArkService_RegisterPayment_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ClaimPayment",
|
||||
Handler: _ArkService_ClaimPayment_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "FinalizePayment",
|
||||
Handler: _ArkService_FinalizePayment_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetRound",
|
||||
Handler: _ArkService_GetRound_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Ping",
|
||||
Handler: _ArkService_Ping_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Faucet",
|
||||
Handler: _ArkService_Faucet_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListVtxos",
|
||||
Handler: _ArkService_ListVtxos_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetPubkey",
|
||||
Handler: _ArkService_GetPubkey_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "GetEventStream",
|
||||
Handler: _ArkService_GetEventStream_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "ark/v1/service.proto",
|
||||
}
|
||||
1711
server/api-spec/protobuf/gen/ocean/v1/account.pb.go
Normal file
1711
server/api-spec/protobuf/gen/ocean/v1/account.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
487
server/api-spec/protobuf/gen/ocean/v1/account_grpc.pb.go
Normal file
487
server/api-spec/protobuf/gen/ocean/v1/account_grpc.pb.go
Normal file
@@ -0,0 +1,487 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
|
||||
package oceanv1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// AccountServiceClient is the client API for AccountService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type AccountServiceClient interface {
|
||||
// CreateAccountBIP44 creates a new BIP44 account.
|
||||
CreateAccountBIP44(ctx context.Context, in *CreateAccountBIP44Request, opts ...grpc.CallOption) (*CreateAccountBIP44Response, error)
|
||||
// CreateAccountMultiSig creates a new multisig account.
|
||||
CreateAccountMultiSig(ctx context.Context, in *CreateAccountMultiSigRequest, opts ...grpc.CallOption) (*CreateAccountMultiSigResponse, error)
|
||||
// CreateAccountCustom creates a new custom account for which loading a template.
|
||||
CreateAccountCustom(ctx context.Context, in *CreateAccountCustomRequest, opts ...grpc.CallOption) (*CreateAccountCustomResponse, error)
|
||||
// SetAccountLabel sets a label for the account that can be used later to refer to it.
|
||||
SetAccountLabel(ctx context.Context, in *SetAccountLabelRequest, opts ...grpc.CallOption) (*SetAccountLabelResponse, error)
|
||||
// SetAccountTemplate sets the template for the account used to generate new addresses.
|
||||
SetAccountTemplate(ctx context.Context, in *SetAccountTemplateRequest, opts ...grpc.CallOption) (*SetAccountTemplateResponse, error)
|
||||
// DeriveAddresses generates new address(es) for the account.
|
||||
DeriveAddresses(ctx context.Context, in *DeriveAddressesRequest, opts ...grpc.CallOption) (*DeriveAddressesResponse, error)
|
||||
// DeriveChangeAddresses generates new change address(es) for the account.
|
||||
DeriveChangeAddresses(ctx context.Context, in *DeriveChangeAddressesRequest, opts ...grpc.CallOption) (*DeriveChangeAddressesResponse, error)
|
||||
// ListAddresses returns all derived addresses for the account.
|
||||
ListAddresses(ctx context.Context, in *ListAddressesRequest, opts ...grpc.CallOption) (*ListAddressesResponse, error)
|
||||
// Balance returns the balance for the account, or for specific list of
|
||||
// account's addresses.
|
||||
Balance(ctx context.Context, in *BalanceRequest, opts ...grpc.CallOption) (*BalanceResponse, error)
|
||||
// ListUtxos returns the utxos for the account, or specific list of
|
||||
// account's addresses.
|
||||
ListUtxos(ctx context.Context, in *ListUtxosRequest, opts ...grpc.CallOption) (*ListUtxosResponse, error)
|
||||
// DeleteAccount deletes an existing account. The operation is allowed only
|
||||
// if the account has zero balance.
|
||||
DeleteAccount(ctx context.Context, in *DeleteAccountRequest, opts ...grpc.CallOption) (*DeleteAccountResponse, error)
|
||||
}
|
||||
|
||||
type accountServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewAccountServiceClient(cc grpc.ClientConnInterface) AccountServiceClient {
|
||||
return &accountServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *accountServiceClient) CreateAccountBIP44(ctx context.Context, in *CreateAccountBIP44Request, opts ...grpc.CallOption) (*CreateAccountBIP44Response, error) {
|
||||
out := new(CreateAccountBIP44Response)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.AccountService/CreateAccountBIP44", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *accountServiceClient) CreateAccountMultiSig(ctx context.Context, in *CreateAccountMultiSigRequest, opts ...grpc.CallOption) (*CreateAccountMultiSigResponse, error) {
|
||||
out := new(CreateAccountMultiSigResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.AccountService/CreateAccountMultiSig", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *accountServiceClient) CreateAccountCustom(ctx context.Context, in *CreateAccountCustomRequest, opts ...grpc.CallOption) (*CreateAccountCustomResponse, error) {
|
||||
out := new(CreateAccountCustomResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.AccountService/CreateAccountCustom", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *accountServiceClient) SetAccountLabel(ctx context.Context, in *SetAccountLabelRequest, opts ...grpc.CallOption) (*SetAccountLabelResponse, error) {
|
||||
out := new(SetAccountLabelResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.AccountService/SetAccountLabel", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *accountServiceClient) SetAccountTemplate(ctx context.Context, in *SetAccountTemplateRequest, opts ...grpc.CallOption) (*SetAccountTemplateResponse, error) {
|
||||
out := new(SetAccountTemplateResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.AccountService/SetAccountTemplate", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *accountServiceClient) DeriveAddresses(ctx context.Context, in *DeriveAddressesRequest, opts ...grpc.CallOption) (*DeriveAddressesResponse, error) {
|
||||
out := new(DeriveAddressesResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.AccountService/DeriveAddresses", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *accountServiceClient) DeriveChangeAddresses(ctx context.Context, in *DeriveChangeAddressesRequest, opts ...grpc.CallOption) (*DeriveChangeAddressesResponse, error) {
|
||||
out := new(DeriveChangeAddressesResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.AccountService/DeriveChangeAddresses", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *accountServiceClient) ListAddresses(ctx context.Context, in *ListAddressesRequest, opts ...grpc.CallOption) (*ListAddressesResponse, error) {
|
||||
out := new(ListAddressesResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.AccountService/ListAddresses", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *accountServiceClient) Balance(ctx context.Context, in *BalanceRequest, opts ...grpc.CallOption) (*BalanceResponse, error) {
|
||||
out := new(BalanceResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.AccountService/Balance", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *accountServiceClient) ListUtxos(ctx context.Context, in *ListUtxosRequest, opts ...grpc.CallOption) (*ListUtxosResponse, error) {
|
||||
out := new(ListUtxosResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.AccountService/ListUtxos", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *accountServiceClient) DeleteAccount(ctx context.Context, in *DeleteAccountRequest, opts ...grpc.CallOption) (*DeleteAccountResponse, error) {
|
||||
out := new(DeleteAccountResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.AccountService/DeleteAccount", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AccountServiceServer is the server API for AccountService service.
|
||||
// All implementations should embed UnimplementedAccountServiceServer
|
||||
// for forward compatibility
|
||||
type AccountServiceServer interface {
|
||||
// CreateAccountBIP44 creates a new BIP44 account.
|
||||
CreateAccountBIP44(context.Context, *CreateAccountBIP44Request) (*CreateAccountBIP44Response, error)
|
||||
// CreateAccountMultiSig creates a new multisig account.
|
||||
CreateAccountMultiSig(context.Context, *CreateAccountMultiSigRequest) (*CreateAccountMultiSigResponse, error)
|
||||
// CreateAccountCustom creates a new custom account for which loading a template.
|
||||
CreateAccountCustom(context.Context, *CreateAccountCustomRequest) (*CreateAccountCustomResponse, error)
|
||||
// SetAccountLabel sets a label for the account that can be used later to refer to it.
|
||||
SetAccountLabel(context.Context, *SetAccountLabelRequest) (*SetAccountLabelResponse, error)
|
||||
// SetAccountTemplate sets the template for the account used to generate new addresses.
|
||||
SetAccountTemplate(context.Context, *SetAccountTemplateRequest) (*SetAccountTemplateResponse, error)
|
||||
// DeriveAddresses generates new address(es) for the account.
|
||||
DeriveAddresses(context.Context, *DeriveAddressesRequest) (*DeriveAddressesResponse, error)
|
||||
// DeriveChangeAddresses generates new change address(es) for the account.
|
||||
DeriveChangeAddresses(context.Context, *DeriveChangeAddressesRequest) (*DeriveChangeAddressesResponse, error)
|
||||
// ListAddresses returns all derived addresses for the account.
|
||||
ListAddresses(context.Context, *ListAddressesRequest) (*ListAddressesResponse, error)
|
||||
// Balance returns the balance for the account, or for specific list of
|
||||
// account's addresses.
|
||||
Balance(context.Context, *BalanceRequest) (*BalanceResponse, error)
|
||||
// ListUtxos returns the utxos for the account, or specific list of
|
||||
// account's addresses.
|
||||
ListUtxos(context.Context, *ListUtxosRequest) (*ListUtxosResponse, error)
|
||||
// DeleteAccount deletes an existing account. The operation is allowed only
|
||||
// if the account has zero balance.
|
||||
DeleteAccount(context.Context, *DeleteAccountRequest) (*DeleteAccountResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedAccountServiceServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedAccountServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedAccountServiceServer) CreateAccountBIP44(context.Context, *CreateAccountBIP44Request) (*CreateAccountBIP44Response, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreateAccountBIP44 not implemented")
|
||||
}
|
||||
func (UnimplementedAccountServiceServer) CreateAccountMultiSig(context.Context, *CreateAccountMultiSigRequest) (*CreateAccountMultiSigResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreateAccountMultiSig not implemented")
|
||||
}
|
||||
func (UnimplementedAccountServiceServer) CreateAccountCustom(context.Context, *CreateAccountCustomRequest) (*CreateAccountCustomResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreateAccountCustom not implemented")
|
||||
}
|
||||
func (UnimplementedAccountServiceServer) SetAccountLabel(context.Context, *SetAccountLabelRequest) (*SetAccountLabelResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SetAccountLabel not implemented")
|
||||
}
|
||||
func (UnimplementedAccountServiceServer) SetAccountTemplate(context.Context, *SetAccountTemplateRequest) (*SetAccountTemplateResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SetAccountTemplate not implemented")
|
||||
}
|
||||
func (UnimplementedAccountServiceServer) DeriveAddresses(context.Context, *DeriveAddressesRequest) (*DeriveAddressesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DeriveAddresses not implemented")
|
||||
}
|
||||
func (UnimplementedAccountServiceServer) DeriveChangeAddresses(context.Context, *DeriveChangeAddressesRequest) (*DeriveChangeAddressesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DeriveChangeAddresses not implemented")
|
||||
}
|
||||
func (UnimplementedAccountServiceServer) ListAddresses(context.Context, *ListAddressesRequest) (*ListAddressesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListAddresses not implemented")
|
||||
}
|
||||
func (UnimplementedAccountServiceServer) Balance(context.Context, *BalanceRequest) (*BalanceResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Balance not implemented")
|
||||
}
|
||||
func (UnimplementedAccountServiceServer) ListUtxos(context.Context, *ListUtxosRequest) (*ListUtxosResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListUtxos not implemented")
|
||||
}
|
||||
func (UnimplementedAccountServiceServer) DeleteAccount(context.Context, *DeleteAccountRequest) (*DeleteAccountResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DeleteAccount not implemented")
|
||||
}
|
||||
|
||||
// UnsafeAccountServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to AccountServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeAccountServiceServer interface {
|
||||
mustEmbedUnimplementedAccountServiceServer()
|
||||
}
|
||||
|
||||
func RegisterAccountServiceServer(s grpc.ServiceRegistrar, srv AccountServiceServer) {
|
||||
s.RegisterService(&AccountService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _AccountService_CreateAccountBIP44_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreateAccountBIP44Request)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AccountServiceServer).CreateAccountBIP44(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.AccountService/CreateAccountBIP44",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AccountServiceServer).CreateAccountBIP44(ctx, req.(*CreateAccountBIP44Request))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AccountService_CreateAccountMultiSig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreateAccountMultiSigRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AccountServiceServer).CreateAccountMultiSig(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.AccountService/CreateAccountMultiSig",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AccountServiceServer).CreateAccountMultiSig(ctx, req.(*CreateAccountMultiSigRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AccountService_CreateAccountCustom_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreateAccountCustomRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AccountServiceServer).CreateAccountCustom(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.AccountService/CreateAccountCustom",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AccountServiceServer).CreateAccountCustom(ctx, req.(*CreateAccountCustomRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AccountService_SetAccountLabel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SetAccountLabelRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AccountServiceServer).SetAccountLabel(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.AccountService/SetAccountLabel",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AccountServiceServer).SetAccountLabel(ctx, req.(*SetAccountLabelRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AccountService_SetAccountTemplate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SetAccountTemplateRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AccountServiceServer).SetAccountTemplate(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.AccountService/SetAccountTemplate",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AccountServiceServer).SetAccountTemplate(ctx, req.(*SetAccountTemplateRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AccountService_DeriveAddresses_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeriveAddressesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AccountServiceServer).DeriveAddresses(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.AccountService/DeriveAddresses",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AccountServiceServer).DeriveAddresses(ctx, req.(*DeriveAddressesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AccountService_DeriveChangeAddresses_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeriveChangeAddressesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AccountServiceServer).DeriveChangeAddresses(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.AccountService/DeriveChangeAddresses",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AccountServiceServer).DeriveChangeAddresses(ctx, req.(*DeriveChangeAddressesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AccountService_ListAddresses_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListAddressesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AccountServiceServer).ListAddresses(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.AccountService/ListAddresses",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AccountServiceServer).ListAddresses(ctx, req.(*ListAddressesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AccountService_Balance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(BalanceRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AccountServiceServer).Balance(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.AccountService/Balance",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AccountServiceServer).Balance(ctx, req.(*BalanceRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AccountService_ListUtxos_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListUtxosRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AccountServiceServer).ListUtxos(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.AccountService/ListUtxos",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AccountServiceServer).ListUtxos(ctx, req.(*ListUtxosRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AccountService_DeleteAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeleteAccountRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AccountServiceServer).DeleteAccount(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.AccountService/DeleteAccount",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AccountServiceServer).DeleteAccount(ctx, req.(*DeleteAccountRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// AccountService_ServiceDesc is the grpc.ServiceDesc for AccountService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var AccountService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "ocean.v1.AccountService",
|
||||
HandlerType: (*AccountServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "CreateAccountBIP44",
|
||||
Handler: _AccountService_CreateAccountBIP44_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CreateAccountMultiSig",
|
||||
Handler: _AccountService_CreateAccountMultiSig_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CreateAccountCustom",
|
||||
Handler: _AccountService_CreateAccountCustom_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SetAccountLabel",
|
||||
Handler: _AccountService_SetAccountLabel_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SetAccountTemplate",
|
||||
Handler: _AccountService_SetAccountTemplate_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeriveAddresses",
|
||||
Handler: _AccountService_DeriveAddresses_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeriveChangeAddresses",
|
||||
Handler: _AccountService_DeriveChangeAddresses_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListAddresses",
|
||||
Handler: _AccountService_ListAddresses_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Balance",
|
||||
Handler: _AccountService_Balance_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListUtxos",
|
||||
Handler: _AccountService_ListUtxos_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteAccount",
|
||||
Handler: _AccountService_DeleteAccount_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "ocean/v1/account.proto",
|
||||
}
|
||||
1196
server/api-spec/protobuf/gen/ocean/v1/notification.pb.go
Normal file
1196
server/api-spec/protobuf/gen/ocean/v1/notification.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
392
server/api-spec/protobuf/gen/ocean/v1/notification_grpc.pb.go
Normal file
392
server/api-spec/protobuf/gen/ocean/v1/notification_grpc.pb.go
Normal file
@@ -0,0 +1,392 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
|
||||
package oceanv1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// NotificationServiceClient is the client API for NotificationService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type NotificationServiceClient interface {
|
||||
// WatchExternalScript allows to get notified about utxos/txs related to the given
|
||||
// external script, ie. not derived from a wallet account.
|
||||
// The service answers with the label assigned to the given script.
|
||||
// The label is used as identifier of the utxos/txs received from the streams.
|
||||
WatchExternalScript(ctx context.Context, in *WatchExternalScriptRequest, opts ...grpc.CallOption) (*WatchExternalScriptResponse, error)
|
||||
// UnwatchExternalScript allows to stop watching for the script identified with
|
||||
// the given label.
|
||||
UnwatchExternalScript(ctx context.Context, in *UnwatchExternalScriptRequest, opts ...grpc.CallOption) (*UnwatchExternalScriptResponse, error)
|
||||
// Notifies about events related to wallet transactions.
|
||||
TransactionNotifications(ctx context.Context, in *TransactionNotificationsRequest, opts ...grpc.CallOption) (NotificationService_TransactionNotificationsClient, error)
|
||||
// Notifies about events realted to wallet utxos.
|
||||
UtxosNotifications(ctx context.Context, in *UtxosNotificationsRequest, opts ...grpc.CallOption) (NotificationService_UtxosNotificationsClient, error)
|
||||
// Adds a webhook registered for some kind of event.
|
||||
AddWebhook(ctx context.Context, in *AddWebhookRequest, opts ...grpc.CallOption) (*AddWebhookResponse, error)
|
||||
// Removes some previously added webhook.
|
||||
RemoveWebhook(ctx context.Context, in *RemoveWebhookRequest, opts ...grpc.CallOption) (*RemoveWebhookResponse, error)
|
||||
// Returns registered webhooks.
|
||||
ListWebhooks(ctx context.Context, in *ListWebhooksRequest, opts ...grpc.CallOption) (*ListWebhooksResponse, error)
|
||||
}
|
||||
|
||||
type notificationServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewNotificationServiceClient(cc grpc.ClientConnInterface) NotificationServiceClient {
|
||||
return ¬ificationServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *notificationServiceClient) WatchExternalScript(ctx context.Context, in *WatchExternalScriptRequest, opts ...grpc.CallOption) (*WatchExternalScriptResponse, error) {
|
||||
out := new(WatchExternalScriptResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.NotificationService/WatchExternalScript", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *notificationServiceClient) UnwatchExternalScript(ctx context.Context, in *UnwatchExternalScriptRequest, opts ...grpc.CallOption) (*UnwatchExternalScriptResponse, error) {
|
||||
out := new(UnwatchExternalScriptResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.NotificationService/UnwatchExternalScript", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *notificationServiceClient) TransactionNotifications(ctx context.Context, in *TransactionNotificationsRequest, opts ...grpc.CallOption) (NotificationService_TransactionNotificationsClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &NotificationService_ServiceDesc.Streams[0], "/ocean.v1.NotificationService/TransactionNotifications", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := ¬ificationServiceTransactionNotificationsClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type NotificationService_TransactionNotificationsClient interface {
|
||||
Recv() (*TransactionNotificationsResponse, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type notificationServiceTransactionNotificationsClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *notificationServiceTransactionNotificationsClient) Recv() (*TransactionNotificationsResponse, error) {
|
||||
m := new(TransactionNotificationsResponse)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *notificationServiceClient) UtxosNotifications(ctx context.Context, in *UtxosNotificationsRequest, opts ...grpc.CallOption) (NotificationService_UtxosNotificationsClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &NotificationService_ServiceDesc.Streams[1], "/ocean.v1.NotificationService/UtxosNotifications", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := ¬ificationServiceUtxosNotificationsClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type NotificationService_UtxosNotificationsClient interface {
|
||||
Recv() (*UtxosNotificationsResponse, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type notificationServiceUtxosNotificationsClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *notificationServiceUtxosNotificationsClient) Recv() (*UtxosNotificationsResponse, error) {
|
||||
m := new(UtxosNotificationsResponse)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *notificationServiceClient) AddWebhook(ctx context.Context, in *AddWebhookRequest, opts ...grpc.CallOption) (*AddWebhookResponse, error) {
|
||||
out := new(AddWebhookResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.NotificationService/AddWebhook", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *notificationServiceClient) RemoveWebhook(ctx context.Context, in *RemoveWebhookRequest, opts ...grpc.CallOption) (*RemoveWebhookResponse, error) {
|
||||
out := new(RemoveWebhookResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.NotificationService/RemoveWebhook", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *notificationServiceClient) ListWebhooks(ctx context.Context, in *ListWebhooksRequest, opts ...grpc.CallOption) (*ListWebhooksResponse, error) {
|
||||
out := new(ListWebhooksResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.NotificationService/ListWebhooks", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// NotificationServiceServer is the server API for NotificationService service.
|
||||
// All implementations should embed UnimplementedNotificationServiceServer
|
||||
// for forward compatibility
|
||||
type NotificationServiceServer interface {
|
||||
// WatchExternalScript allows to get notified about utxos/txs related to the given
|
||||
// external script, ie. not derived from a wallet account.
|
||||
// The service answers with the label assigned to the given script.
|
||||
// The label is used as identifier of the utxos/txs received from the streams.
|
||||
WatchExternalScript(context.Context, *WatchExternalScriptRequest) (*WatchExternalScriptResponse, error)
|
||||
// UnwatchExternalScript allows to stop watching for the script identified with
|
||||
// the given label.
|
||||
UnwatchExternalScript(context.Context, *UnwatchExternalScriptRequest) (*UnwatchExternalScriptResponse, error)
|
||||
// Notifies about events related to wallet transactions.
|
||||
TransactionNotifications(*TransactionNotificationsRequest, NotificationService_TransactionNotificationsServer) error
|
||||
// Notifies about events realted to wallet utxos.
|
||||
UtxosNotifications(*UtxosNotificationsRequest, NotificationService_UtxosNotificationsServer) error
|
||||
// Adds a webhook registered for some kind of event.
|
||||
AddWebhook(context.Context, *AddWebhookRequest) (*AddWebhookResponse, error)
|
||||
// Removes some previously added webhook.
|
||||
RemoveWebhook(context.Context, *RemoveWebhookRequest) (*RemoveWebhookResponse, error)
|
||||
// Returns registered webhooks.
|
||||
ListWebhooks(context.Context, *ListWebhooksRequest) (*ListWebhooksResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedNotificationServiceServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedNotificationServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedNotificationServiceServer) WatchExternalScript(context.Context, *WatchExternalScriptRequest) (*WatchExternalScriptResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method WatchExternalScript not implemented")
|
||||
}
|
||||
func (UnimplementedNotificationServiceServer) UnwatchExternalScript(context.Context, *UnwatchExternalScriptRequest) (*UnwatchExternalScriptResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UnwatchExternalScript not implemented")
|
||||
}
|
||||
func (UnimplementedNotificationServiceServer) TransactionNotifications(*TransactionNotificationsRequest, NotificationService_TransactionNotificationsServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method TransactionNotifications not implemented")
|
||||
}
|
||||
func (UnimplementedNotificationServiceServer) UtxosNotifications(*UtxosNotificationsRequest, NotificationService_UtxosNotificationsServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method UtxosNotifications not implemented")
|
||||
}
|
||||
func (UnimplementedNotificationServiceServer) AddWebhook(context.Context, *AddWebhookRequest) (*AddWebhookResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method AddWebhook not implemented")
|
||||
}
|
||||
func (UnimplementedNotificationServiceServer) RemoveWebhook(context.Context, *RemoveWebhookRequest) (*RemoveWebhookResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RemoveWebhook not implemented")
|
||||
}
|
||||
func (UnimplementedNotificationServiceServer) ListWebhooks(context.Context, *ListWebhooksRequest) (*ListWebhooksResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListWebhooks not implemented")
|
||||
}
|
||||
|
||||
// UnsafeNotificationServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to NotificationServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeNotificationServiceServer interface {
|
||||
mustEmbedUnimplementedNotificationServiceServer()
|
||||
}
|
||||
|
||||
func RegisterNotificationServiceServer(s grpc.ServiceRegistrar, srv NotificationServiceServer) {
|
||||
s.RegisterService(&NotificationService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _NotificationService_WatchExternalScript_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(WatchExternalScriptRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotificationServiceServer).WatchExternalScript(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.NotificationService/WatchExternalScript",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotificationServiceServer).WatchExternalScript(ctx, req.(*WatchExternalScriptRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _NotificationService_UnwatchExternalScript_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UnwatchExternalScriptRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotificationServiceServer).UnwatchExternalScript(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.NotificationService/UnwatchExternalScript",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotificationServiceServer).UnwatchExternalScript(ctx, req.(*UnwatchExternalScriptRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _NotificationService_TransactionNotifications_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(TransactionNotificationsRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(NotificationServiceServer).TransactionNotifications(m, ¬ificationServiceTransactionNotificationsServer{stream})
|
||||
}
|
||||
|
||||
type NotificationService_TransactionNotificationsServer interface {
|
||||
Send(*TransactionNotificationsResponse) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type notificationServiceTransactionNotificationsServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *notificationServiceTransactionNotificationsServer) Send(m *TransactionNotificationsResponse) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func _NotificationService_UtxosNotifications_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(UtxosNotificationsRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(NotificationServiceServer).UtxosNotifications(m, ¬ificationServiceUtxosNotificationsServer{stream})
|
||||
}
|
||||
|
||||
type NotificationService_UtxosNotificationsServer interface {
|
||||
Send(*UtxosNotificationsResponse) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type notificationServiceUtxosNotificationsServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *notificationServiceUtxosNotificationsServer) Send(m *UtxosNotificationsResponse) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func _NotificationService_AddWebhook_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AddWebhookRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotificationServiceServer).AddWebhook(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.NotificationService/AddWebhook",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotificationServiceServer).AddWebhook(ctx, req.(*AddWebhookRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _NotificationService_RemoveWebhook_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RemoveWebhookRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotificationServiceServer).RemoveWebhook(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.NotificationService/RemoveWebhook",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotificationServiceServer).RemoveWebhook(ctx, req.(*RemoveWebhookRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _NotificationService_ListWebhooks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListWebhooksRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotificationServiceServer).ListWebhooks(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.NotificationService/ListWebhooks",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotificationServiceServer).ListWebhooks(ctx, req.(*ListWebhooksRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// NotificationService_ServiceDesc is the grpc.ServiceDesc for NotificationService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var NotificationService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "ocean.v1.NotificationService",
|
||||
HandlerType: (*NotificationServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "WatchExternalScript",
|
||||
Handler: _NotificationService_WatchExternalScript_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UnwatchExternalScript",
|
||||
Handler: _NotificationService_UnwatchExternalScript_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "AddWebhook",
|
||||
Handler: _NotificationService_AddWebhook_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RemoveWebhook",
|
||||
Handler: _NotificationService_RemoveWebhook_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListWebhooks",
|
||||
Handler: _NotificationService_ListWebhooks_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "TransactionNotifications",
|
||||
Handler: _NotificationService_TransactionNotifications_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "UtxosNotifications",
|
||||
Handler: _NotificationService_UtxosNotifications_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "ocean/v1/notification.proto",
|
||||
}
|
||||
2694
server/api-spec/protobuf/gen/ocean/v1/transaction.pb.go
Normal file
2694
server/api-spec/protobuf/gen/ocean/v1/transaction.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
689
server/api-spec/protobuf/gen/ocean/v1/transaction_grpc.pb.go
Normal file
689
server/api-spec/protobuf/gen/ocean/v1/transaction_grpc.pb.go
Normal file
@@ -0,0 +1,689 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
|
||||
package oceanv1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// TransactionServiceClient is the client API for TransactionService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type TransactionServiceClient interface {
|
||||
// GetTransaction returns the hex of a transaction given its id.
|
||||
GetTransaction(ctx context.Context, in *GetTransactionRequest, opts ...grpc.CallOption) (*GetTransactionResponse, error)
|
||||
// SelectUtxos returns a selction of utxos, to be used in another
|
||||
// transaction, for provided target amount and strategy.
|
||||
// Selected utxos are locked for predefined amount of time to prevent
|
||||
// double-spending them.
|
||||
SelectUtxos(ctx context.Context, in *SelectUtxosRequest, opts ...grpc.CallOption) (*SelectUtxosResponse, error)
|
||||
// EstimateFees returns the fee amount to pay for a tx containing the given
|
||||
// inputs and outputs.
|
||||
EstimateFees(ctx context.Context, in *EstimateFeesRequest, opts ...grpc.CallOption) (*EstimateFeesResponse, error)
|
||||
// SignTransaction signs a raw transaction in hex format.
|
||||
SignTransaction(ctx context.Context, in *SignTransactionRequest, opts ...grpc.CallOption) (*SignTransactionResponse, error)
|
||||
// BroadcastTransaction broadacats a raw transaction in hex format.
|
||||
BroadcastTransaction(ctx context.Context, in *BroadcastTransactionRequest, opts ...grpc.CallOption) (*BroadcastTransactionResponse, error)
|
||||
// CreatePset returns an unsigned pset for given inputs and outputs.
|
||||
CreatePset(ctx context.Context, in *CreatePsetRequest, opts ...grpc.CallOption) (*CreatePsetResponse, error)
|
||||
// UpdatePset adds the given inputs and outputs to the partial transaction.
|
||||
UpdatePset(ctx context.Context, in *UpdatePsetRequest, opts ...grpc.CallOption) (*UpdatePsetResponse, error)
|
||||
// BlindPset updates the given pset with required ins and outs blinded.
|
||||
BlindPset(ctx context.Context, in *BlindPsetRequest, opts ...grpc.CallOption) (*BlindPsetResponse, error)
|
||||
// SignPset updates the given pset adding the required signatures.
|
||||
SignPset(ctx context.Context, in *SignPsetRequest, opts ...grpc.CallOption) (*SignPsetResponse, error)
|
||||
// Mint returns a transaction that issues a new asset.
|
||||
Mint(ctx context.Context, in *MintRequest, opts ...grpc.CallOption) (*MintResponse, error)
|
||||
// Remint returns a transaction that re-issues an existing asset.
|
||||
Remint(ctx context.Context, in *RemintRequest, opts ...grpc.CallOption) (*RemintResponse, error)
|
||||
// Burn returns a transaction that burns some funds.
|
||||
Burn(ctx context.Context, in *BurnRequest, opts ...grpc.CallOption) (*BurnResponse, error)
|
||||
// Transfer returns a transaction to send funds to some receiver.
|
||||
Transfer(ctx context.Context, in *TransferRequest, opts ...grpc.CallOption) (*TransferResponse, error)
|
||||
// PegInAddress returns what's necessary to peg funds of the Bitcoin
|
||||
// main-chain and have them available on the Liquid side-chain.
|
||||
// Bitcoin funds must be sent to the main-chain address while the claim
|
||||
// output script must be used to redeem the LBTC ones.
|
||||
PegInAddress(ctx context.Context, in *PegInAddressRequest, opts ...grpc.CallOption) (*PegInAddressResponse, error)
|
||||
// ClaimPegIn returns a transaction to claim funds pegged on the Bitcoin
|
||||
// main-chain to have them available on the Liquid side-chain.
|
||||
ClaimPegIn(ctx context.Context, in *ClaimPegInRequest, opts ...grpc.CallOption) (*ClaimPegInResponse, error)
|
||||
// SignPsetWithSchnorrKey signs all taproot inputs of the provided tx with
|
||||
// the key at the given derivation path.
|
||||
SignPsetWithSchnorrKey(ctx context.Context, in *SignPsetWithSchnorrKeyRequest, opts ...grpc.CallOption) (*SignPsetWithSchnorrKeyResponse, error)
|
||||
}
|
||||
|
||||
type transactionServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewTransactionServiceClient(cc grpc.ClientConnInterface) TransactionServiceClient {
|
||||
return &transactionServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *transactionServiceClient) GetTransaction(ctx context.Context, in *GetTransactionRequest, opts ...grpc.CallOption) (*GetTransactionResponse, error) {
|
||||
out := new(GetTransactionResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.TransactionService/GetTransaction", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *transactionServiceClient) SelectUtxos(ctx context.Context, in *SelectUtxosRequest, opts ...grpc.CallOption) (*SelectUtxosResponse, error) {
|
||||
out := new(SelectUtxosResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.TransactionService/SelectUtxos", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *transactionServiceClient) EstimateFees(ctx context.Context, in *EstimateFeesRequest, opts ...grpc.CallOption) (*EstimateFeesResponse, error) {
|
||||
out := new(EstimateFeesResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.TransactionService/EstimateFees", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *transactionServiceClient) SignTransaction(ctx context.Context, in *SignTransactionRequest, opts ...grpc.CallOption) (*SignTransactionResponse, error) {
|
||||
out := new(SignTransactionResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.TransactionService/SignTransaction", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *transactionServiceClient) BroadcastTransaction(ctx context.Context, in *BroadcastTransactionRequest, opts ...grpc.CallOption) (*BroadcastTransactionResponse, error) {
|
||||
out := new(BroadcastTransactionResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.TransactionService/BroadcastTransaction", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *transactionServiceClient) CreatePset(ctx context.Context, in *CreatePsetRequest, opts ...grpc.CallOption) (*CreatePsetResponse, error) {
|
||||
out := new(CreatePsetResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.TransactionService/CreatePset", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *transactionServiceClient) UpdatePset(ctx context.Context, in *UpdatePsetRequest, opts ...grpc.CallOption) (*UpdatePsetResponse, error) {
|
||||
out := new(UpdatePsetResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.TransactionService/UpdatePset", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *transactionServiceClient) BlindPset(ctx context.Context, in *BlindPsetRequest, opts ...grpc.CallOption) (*BlindPsetResponse, error) {
|
||||
out := new(BlindPsetResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.TransactionService/BlindPset", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *transactionServiceClient) SignPset(ctx context.Context, in *SignPsetRequest, opts ...grpc.CallOption) (*SignPsetResponse, error) {
|
||||
out := new(SignPsetResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.TransactionService/SignPset", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *transactionServiceClient) Mint(ctx context.Context, in *MintRequest, opts ...grpc.CallOption) (*MintResponse, error) {
|
||||
out := new(MintResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.TransactionService/Mint", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *transactionServiceClient) Remint(ctx context.Context, in *RemintRequest, opts ...grpc.CallOption) (*RemintResponse, error) {
|
||||
out := new(RemintResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.TransactionService/Remint", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *transactionServiceClient) Burn(ctx context.Context, in *BurnRequest, opts ...grpc.CallOption) (*BurnResponse, error) {
|
||||
out := new(BurnResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.TransactionService/Burn", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *transactionServiceClient) Transfer(ctx context.Context, in *TransferRequest, opts ...grpc.CallOption) (*TransferResponse, error) {
|
||||
out := new(TransferResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.TransactionService/Transfer", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *transactionServiceClient) PegInAddress(ctx context.Context, in *PegInAddressRequest, opts ...grpc.CallOption) (*PegInAddressResponse, error) {
|
||||
out := new(PegInAddressResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.TransactionService/PegInAddress", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *transactionServiceClient) ClaimPegIn(ctx context.Context, in *ClaimPegInRequest, opts ...grpc.CallOption) (*ClaimPegInResponse, error) {
|
||||
out := new(ClaimPegInResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.TransactionService/ClaimPegIn", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *transactionServiceClient) SignPsetWithSchnorrKey(ctx context.Context, in *SignPsetWithSchnorrKeyRequest, opts ...grpc.CallOption) (*SignPsetWithSchnorrKeyResponse, error) {
|
||||
out := new(SignPsetWithSchnorrKeyResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.TransactionService/SignPsetWithSchnorrKey", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// TransactionServiceServer is the server API for TransactionService service.
|
||||
// All implementations should embed UnimplementedTransactionServiceServer
|
||||
// for forward compatibility
|
||||
type TransactionServiceServer interface {
|
||||
// GetTransaction returns the hex of a transaction given its id.
|
||||
GetTransaction(context.Context, *GetTransactionRequest) (*GetTransactionResponse, error)
|
||||
// SelectUtxos returns a selction of utxos, to be used in another
|
||||
// transaction, for provided target amount and strategy.
|
||||
// Selected utxos are locked for predefined amount of time to prevent
|
||||
// double-spending them.
|
||||
SelectUtxos(context.Context, *SelectUtxosRequest) (*SelectUtxosResponse, error)
|
||||
// EstimateFees returns the fee amount to pay for a tx containing the given
|
||||
// inputs and outputs.
|
||||
EstimateFees(context.Context, *EstimateFeesRequest) (*EstimateFeesResponse, error)
|
||||
// SignTransaction signs a raw transaction in hex format.
|
||||
SignTransaction(context.Context, *SignTransactionRequest) (*SignTransactionResponse, error)
|
||||
// BroadcastTransaction broadacats a raw transaction in hex format.
|
||||
BroadcastTransaction(context.Context, *BroadcastTransactionRequest) (*BroadcastTransactionResponse, error)
|
||||
// CreatePset returns an unsigned pset for given inputs and outputs.
|
||||
CreatePset(context.Context, *CreatePsetRequest) (*CreatePsetResponse, error)
|
||||
// UpdatePset adds the given inputs and outputs to the partial transaction.
|
||||
UpdatePset(context.Context, *UpdatePsetRequest) (*UpdatePsetResponse, error)
|
||||
// BlindPset updates the given pset with required ins and outs blinded.
|
||||
BlindPset(context.Context, *BlindPsetRequest) (*BlindPsetResponse, error)
|
||||
// SignPset updates the given pset adding the required signatures.
|
||||
SignPset(context.Context, *SignPsetRequest) (*SignPsetResponse, error)
|
||||
// Mint returns a transaction that issues a new asset.
|
||||
Mint(context.Context, *MintRequest) (*MintResponse, error)
|
||||
// Remint returns a transaction that re-issues an existing asset.
|
||||
Remint(context.Context, *RemintRequest) (*RemintResponse, error)
|
||||
// Burn returns a transaction that burns some funds.
|
||||
Burn(context.Context, *BurnRequest) (*BurnResponse, error)
|
||||
// Transfer returns a transaction to send funds to some receiver.
|
||||
Transfer(context.Context, *TransferRequest) (*TransferResponse, error)
|
||||
// PegInAddress returns what's necessary to peg funds of the Bitcoin
|
||||
// main-chain and have them available on the Liquid side-chain.
|
||||
// Bitcoin funds must be sent to the main-chain address while the claim
|
||||
// output script must be used to redeem the LBTC ones.
|
||||
PegInAddress(context.Context, *PegInAddressRequest) (*PegInAddressResponse, error)
|
||||
// ClaimPegIn returns a transaction to claim funds pegged on the Bitcoin
|
||||
// main-chain to have them available on the Liquid side-chain.
|
||||
ClaimPegIn(context.Context, *ClaimPegInRequest) (*ClaimPegInResponse, error)
|
||||
// SignPsetWithSchnorrKey signs all taproot inputs of the provided tx with
|
||||
// the key at the given derivation path.
|
||||
SignPsetWithSchnorrKey(context.Context, *SignPsetWithSchnorrKeyRequest) (*SignPsetWithSchnorrKeyResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedTransactionServiceServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedTransactionServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedTransactionServiceServer) GetTransaction(context.Context, *GetTransactionRequest) (*GetTransactionResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetTransaction not implemented")
|
||||
}
|
||||
func (UnimplementedTransactionServiceServer) SelectUtxos(context.Context, *SelectUtxosRequest) (*SelectUtxosResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SelectUtxos not implemented")
|
||||
}
|
||||
func (UnimplementedTransactionServiceServer) EstimateFees(context.Context, *EstimateFeesRequest) (*EstimateFeesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method EstimateFees not implemented")
|
||||
}
|
||||
func (UnimplementedTransactionServiceServer) SignTransaction(context.Context, *SignTransactionRequest) (*SignTransactionResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SignTransaction not implemented")
|
||||
}
|
||||
func (UnimplementedTransactionServiceServer) BroadcastTransaction(context.Context, *BroadcastTransactionRequest) (*BroadcastTransactionResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method BroadcastTransaction not implemented")
|
||||
}
|
||||
func (UnimplementedTransactionServiceServer) CreatePset(context.Context, *CreatePsetRequest) (*CreatePsetResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreatePset not implemented")
|
||||
}
|
||||
func (UnimplementedTransactionServiceServer) UpdatePset(context.Context, *UpdatePsetRequest) (*UpdatePsetResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UpdatePset not implemented")
|
||||
}
|
||||
func (UnimplementedTransactionServiceServer) BlindPset(context.Context, *BlindPsetRequest) (*BlindPsetResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method BlindPset not implemented")
|
||||
}
|
||||
func (UnimplementedTransactionServiceServer) SignPset(context.Context, *SignPsetRequest) (*SignPsetResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SignPset not implemented")
|
||||
}
|
||||
func (UnimplementedTransactionServiceServer) Mint(context.Context, *MintRequest) (*MintResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Mint not implemented")
|
||||
}
|
||||
func (UnimplementedTransactionServiceServer) Remint(context.Context, *RemintRequest) (*RemintResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Remint not implemented")
|
||||
}
|
||||
func (UnimplementedTransactionServiceServer) Burn(context.Context, *BurnRequest) (*BurnResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Burn not implemented")
|
||||
}
|
||||
func (UnimplementedTransactionServiceServer) Transfer(context.Context, *TransferRequest) (*TransferResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Transfer not implemented")
|
||||
}
|
||||
func (UnimplementedTransactionServiceServer) PegInAddress(context.Context, *PegInAddressRequest) (*PegInAddressResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method PegInAddress not implemented")
|
||||
}
|
||||
func (UnimplementedTransactionServiceServer) ClaimPegIn(context.Context, *ClaimPegInRequest) (*ClaimPegInResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ClaimPegIn not implemented")
|
||||
}
|
||||
func (UnimplementedTransactionServiceServer) SignPsetWithSchnorrKey(context.Context, *SignPsetWithSchnorrKeyRequest) (*SignPsetWithSchnorrKeyResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SignPsetWithSchnorrKey not implemented")
|
||||
}
|
||||
|
||||
// UnsafeTransactionServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to TransactionServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeTransactionServiceServer interface {
|
||||
mustEmbedUnimplementedTransactionServiceServer()
|
||||
}
|
||||
|
||||
func RegisterTransactionServiceServer(s grpc.ServiceRegistrar, srv TransactionServiceServer) {
|
||||
s.RegisterService(&TransactionService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _TransactionService_GetTransaction_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetTransactionRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TransactionServiceServer).GetTransaction(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.TransactionService/GetTransaction",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TransactionServiceServer).GetTransaction(ctx, req.(*GetTransactionRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TransactionService_SelectUtxos_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SelectUtxosRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TransactionServiceServer).SelectUtxos(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.TransactionService/SelectUtxos",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TransactionServiceServer).SelectUtxos(ctx, req.(*SelectUtxosRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TransactionService_EstimateFees_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(EstimateFeesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TransactionServiceServer).EstimateFees(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.TransactionService/EstimateFees",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TransactionServiceServer).EstimateFees(ctx, req.(*EstimateFeesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TransactionService_SignTransaction_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SignTransactionRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TransactionServiceServer).SignTransaction(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.TransactionService/SignTransaction",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TransactionServiceServer).SignTransaction(ctx, req.(*SignTransactionRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TransactionService_BroadcastTransaction_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(BroadcastTransactionRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TransactionServiceServer).BroadcastTransaction(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.TransactionService/BroadcastTransaction",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TransactionServiceServer).BroadcastTransaction(ctx, req.(*BroadcastTransactionRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TransactionService_CreatePset_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreatePsetRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TransactionServiceServer).CreatePset(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.TransactionService/CreatePset",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TransactionServiceServer).CreatePset(ctx, req.(*CreatePsetRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TransactionService_UpdatePset_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdatePsetRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TransactionServiceServer).UpdatePset(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.TransactionService/UpdatePset",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TransactionServiceServer).UpdatePset(ctx, req.(*UpdatePsetRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TransactionService_BlindPset_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(BlindPsetRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TransactionServiceServer).BlindPset(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.TransactionService/BlindPset",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TransactionServiceServer).BlindPset(ctx, req.(*BlindPsetRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TransactionService_SignPset_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SignPsetRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TransactionServiceServer).SignPset(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.TransactionService/SignPset",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TransactionServiceServer).SignPset(ctx, req.(*SignPsetRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TransactionService_Mint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(MintRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TransactionServiceServer).Mint(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.TransactionService/Mint",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TransactionServiceServer).Mint(ctx, req.(*MintRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TransactionService_Remint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RemintRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TransactionServiceServer).Remint(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.TransactionService/Remint",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TransactionServiceServer).Remint(ctx, req.(*RemintRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TransactionService_Burn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(BurnRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TransactionServiceServer).Burn(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.TransactionService/Burn",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TransactionServiceServer).Burn(ctx, req.(*BurnRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TransactionService_Transfer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(TransferRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TransactionServiceServer).Transfer(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.TransactionService/Transfer",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TransactionServiceServer).Transfer(ctx, req.(*TransferRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TransactionService_PegInAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PegInAddressRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TransactionServiceServer).PegInAddress(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.TransactionService/PegInAddress",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TransactionServiceServer).PegInAddress(ctx, req.(*PegInAddressRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TransactionService_ClaimPegIn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ClaimPegInRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TransactionServiceServer).ClaimPegIn(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.TransactionService/ClaimPegIn",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TransactionServiceServer).ClaimPegIn(ctx, req.(*ClaimPegInRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TransactionService_SignPsetWithSchnorrKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SignPsetWithSchnorrKeyRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TransactionServiceServer).SignPsetWithSchnorrKey(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.TransactionService/SignPsetWithSchnorrKey",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TransactionServiceServer).SignPsetWithSchnorrKey(ctx, req.(*SignPsetWithSchnorrKeyRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// TransactionService_ServiceDesc is the grpc.ServiceDesc for TransactionService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var TransactionService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "ocean.v1.TransactionService",
|
||||
HandlerType: (*TransactionServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetTransaction",
|
||||
Handler: _TransactionService_GetTransaction_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SelectUtxos",
|
||||
Handler: _TransactionService_SelectUtxos_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "EstimateFees",
|
||||
Handler: _TransactionService_EstimateFees_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SignTransaction",
|
||||
Handler: _TransactionService_SignTransaction_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "BroadcastTransaction",
|
||||
Handler: _TransactionService_BroadcastTransaction_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CreatePset",
|
||||
Handler: _TransactionService_CreatePset_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdatePset",
|
||||
Handler: _TransactionService_UpdatePset_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "BlindPset",
|
||||
Handler: _TransactionService_BlindPset_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SignPset",
|
||||
Handler: _TransactionService_SignPset_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Mint",
|
||||
Handler: _TransactionService_Mint_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Remint",
|
||||
Handler: _TransactionService_Remint_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Burn",
|
||||
Handler: _TransactionService_Burn_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Transfer",
|
||||
Handler: _TransactionService_Transfer_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "PegInAddress",
|
||||
Handler: _TransactionService_PegInAddress_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ClaimPegIn",
|
||||
Handler: _TransactionService_ClaimPegIn_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SignPsetWithSchnorrKey",
|
||||
Handler: _TransactionService_SignPsetWithSchnorrKey_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "ocean/v1/transaction.proto",
|
||||
}
|
||||
1463
server/api-spec/protobuf/gen/ocean/v1/types.pb.go
Normal file
1463
server/api-spec/protobuf/gen/ocean/v1/types.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
1466
server/api-spec/protobuf/gen/ocean/v1/wallet.pb.go
Normal file
1466
server/api-spec/protobuf/gen/ocean/v1/wallet.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
441
server/api-spec/protobuf/gen/ocean/v1/wallet_grpc.pb.go
Normal file
441
server/api-spec/protobuf/gen/ocean/v1/wallet_grpc.pb.go
Normal file
@@ -0,0 +1,441 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
|
||||
package oceanv1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// WalletServiceClient is the client API for WalletService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type WalletServiceClient interface {
|
||||
// GenSeed returns signing and blinding seed that should be used to create a
|
||||
// new HD Wallet.
|
||||
GenSeed(ctx context.Context, in *GenSeedRequest, opts ...grpc.CallOption) (*GenSeedResponse, error)
|
||||
// CreateWallet creates an HD Wallet based on signing, blinding seeds,
|
||||
// encrypts them with the password and persists the encrypted seeds.
|
||||
CreateWallet(ctx context.Context, in *CreateWalletRequest, opts ...grpc.CallOption) (*CreateWalletResponse, error)
|
||||
// Unlock tries to unlock the HD Wallet using the given password.
|
||||
Unlock(ctx context.Context, in *UnlockRequest, opts ...grpc.CallOption) (*UnlockResponse, error)
|
||||
// Lock locks the HD wallet.
|
||||
Lock(ctx context.Context, in *LockRequest, opts ...grpc.CallOption) (*LockResponse, error)
|
||||
// ChangePassword changes the password used to encrypt/decrypt the HD seeds.
|
||||
// It requires the wallet to be locked.
|
||||
ChangePassword(ctx context.Context, in *ChangePasswordRequest, opts ...grpc.CallOption) (*ChangePasswordResponse, error)
|
||||
// RestoreWallet restores an HD Wallet based on signing and blinding seeds,
|
||||
// encrypts them with the password and persists the encrypted seeds.
|
||||
RestoreWallet(ctx context.Context, in *RestoreWalletRequest, opts ...grpc.CallOption) (WalletService_RestoreWalletClient, error)
|
||||
// Status returns info about the status of the wallet.
|
||||
Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error)
|
||||
// GetInfo returns info about the HD wallet.
|
||||
GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error)
|
||||
// Auth verifies whether the given password is valid without unlocking the wallet
|
||||
Auth(ctx context.Context, in *AuthRequest, opts ...grpc.CallOption) (*AuthResponse, error)
|
||||
}
|
||||
|
||||
type walletServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewWalletServiceClient(cc grpc.ClientConnInterface) WalletServiceClient {
|
||||
return &walletServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *walletServiceClient) GenSeed(ctx context.Context, in *GenSeedRequest, opts ...grpc.CallOption) (*GenSeedResponse, error) {
|
||||
out := new(GenSeedResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.WalletService/GenSeed", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *walletServiceClient) CreateWallet(ctx context.Context, in *CreateWalletRequest, opts ...grpc.CallOption) (*CreateWalletResponse, error) {
|
||||
out := new(CreateWalletResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.WalletService/CreateWallet", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *walletServiceClient) Unlock(ctx context.Context, in *UnlockRequest, opts ...grpc.CallOption) (*UnlockResponse, error) {
|
||||
out := new(UnlockResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.WalletService/Unlock", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *walletServiceClient) Lock(ctx context.Context, in *LockRequest, opts ...grpc.CallOption) (*LockResponse, error) {
|
||||
out := new(LockResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.WalletService/Lock", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *walletServiceClient) ChangePassword(ctx context.Context, in *ChangePasswordRequest, opts ...grpc.CallOption) (*ChangePasswordResponse, error) {
|
||||
out := new(ChangePasswordResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.WalletService/ChangePassword", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *walletServiceClient) RestoreWallet(ctx context.Context, in *RestoreWalletRequest, opts ...grpc.CallOption) (WalletService_RestoreWalletClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &WalletService_ServiceDesc.Streams[0], "/ocean.v1.WalletService/RestoreWallet", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &walletServiceRestoreWalletClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type WalletService_RestoreWalletClient interface {
|
||||
Recv() (*RestoreWalletResponse, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type walletServiceRestoreWalletClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *walletServiceRestoreWalletClient) Recv() (*RestoreWalletResponse, error) {
|
||||
m := new(RestoreWalletResponse)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *walletServiceClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) {
|
||||
out := new(StatusResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.WalletService/Status", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *walletServiceClient) GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error) {
|
||||
out := new(GetInfoResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.WalletService/GetInfo", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *walletServiceClient) Auth(ctx context.Context, in *AuthRequest, opts ...grpc.CallOption) (*AuthResponse, error) {
|
||||
out := new(AuthResponse)
|
||||
err := c.cc.Invoke(ctx, "/ocean.v1.WalletService/Auth", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// WalletServiceServer is the server API for WalletService service.
|
||||
// All implementations should embed UnimplementedWalletServiceServer
|
||||
// for forward compatibility
|
||||
type WalletServiceServer interface {
|
||||
// GenSeed returns signing and blinding seed that should be used to create a
|
||||
// new HD Wallet.
|
||||
GenSeed(context.Context, *GenSeedRequest) (*GenSeedResponse, error)
|
||||
// CreateWallet creates an HD Wallet based on signing, blinding seeds,
|
||||
// encrypts them with the password and persists the encrypted seeds.
|
||||
CreateWallet(context.Context, *CreateWalletRequest) (*CreateWalletResponse, error)
|
||||
// Unlock tries to unlock the HD Wallet using the given password.
|
||||
Unlock(context.Context, *UnlockRequest) (*UnlockResponse, error)
|
||||
// Lock locks the HD wallet.
|
||||
Lock(context.Context, *LockRequest) (*LockResponse, error)
|
||||
// ChangePassword changes the password used to encrypt/decrypt the HD seeds.
|
||||
// It requires the wallet to be locked.
|
||||
ChangePassword(context.Context, *ChangePasswordRequest) (*ChangePasswordResponse, error)
|
||||
// RestoreWallet restores an HD Wallet based on signing and blinding seeds,
|
||||
// encrypts them with the password and persists the encrypted seeds.
|
||||
RestoreWallet(*RestoreWalletRequest, WalletService_RestoreWalletServer) error
|
||||
// Status returns info about the status of the wallet.
|
||||
Status(context.Context, *StatusRequest) (*StatusResponse, error)
|
||||
// GetInfo returns info about the HD wallet.
|
||||
GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error)
|
||||
// Auth verifies whether the given password is valid without unlocking the wallet
|
||||
Auth(context.Context, *AuthRequest) (*AuthResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedWalletServiceServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedWalletServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedWalletServiceServer) GenSeed(context.Context, *GenSeedRequest) (*GenSeedResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GenSeed not implemented")
|
||||
}
|
||||
func (UnimplementedWalletServiceServer) CreateWallet(context.Context, *CreateWalletRequest) (*CreateWalletResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreateWallet not implemented")
|
||||
}
|
||||
func (UnimplementedWalletServiceServer) Unlock(context.Context, *UnlockRequest) (*UnlockResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Unlock not implemented")
|
||||
}
|
||||
func (UnimplementedWalletServiceServer) Lock(context.Context, *LockRequest) (*LockResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Lock not implemented")
|
||||
}
|
||||
func (UnimplementedWalletServiceServer) ChangePassword(context.Context, *ChangePasswordRequest) (*ChangePasswordResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ChangePassword not implemented")
|
||||
}
|
||||
func (UnimplementedWalletServiceServer) RestoreWallet(*RestoreWalletRequest, WalletService_RestoreWalletServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method RestoreWallet not implemented")
|
||||
}
|
||||
func (UnimplementedWalletServiceServer) Status(context.Context, *StatusRequest) (*StatusResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Status not implemented")
|
||||
}
|
||||
func (UnimplementedWalletServiceServer) GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetInfo not implemented")
|
||||
}
|
||||
func (UnimplementedWalletServiceServer) Auth(context.Context, *AuthRequest) (*AuthResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Auth not implemented")
|
||||
}
|
||||
|
||||
// UnsafeWalletServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to WalletServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeWalletServiceServer interface {
|
||||
mustEmbedUnimplementedWalletServiceServer()
|
||||
}
|
||||
|
||||
func RegisterWalletServiceServer(s grpc.ServiceRegistrar, srv WalletServiceServer) {
|
||||
s.RegisterService(&WalletService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _WalletService_GenSeed_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GenSeedRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(WalletServiceServer).GenSeed(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.WalletService/GenSeed",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(WalletServiceServer).GenSeed(ctx, req.(*GenSeedRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _WalletService_CreateWallet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreateWalletRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(WalletServiceServer).CreateWallet(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.WalletService/CreateWallet",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(WalletServiceServer).CreateWallet(ctx, req.(*CreateWalletRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _WalletService_Unlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UnlockRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(WalletServiceServer).Unlock(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.WalletService/Unlock",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(WalletServiceServer).Unlock(ctx, req.(*UnlockRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _WalletService_Lock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(LockRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(WalletServiceServer).Lock(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.WalletService/Lock",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(WalletServiceServer).Lock(ctx, req.(*LockRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _WalletService_ChangePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ChangePasswordRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(WalletServiceServer).ChangePassword(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.WalletService/ChangePassword",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(WalletServiceServer).ChangePassword(ctx, req.(*ChangePasswordRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _WalletService_RestoreWallet_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(RestoreWalletRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(WalletServiceServer).RestoreWallet(m, &walletServiceRestoreWalletServer{stream})
|
||||
}
|
||||
|
||||
type WalletService_RestoreWalletServer interface {
|
||||
Send(*RestoreWalletResponse) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type walletServiceRestoreWalletServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *walletServiceRestoreWalletServer) Send(m *RestoreWalletResponse) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func _WalletService_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(StatusRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(WalletServiceServer).Status(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.WalletService/Status",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(WalletServiceServer).Status(ctx, req.(*StatusRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _WalletService_GetInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetInfoRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(WalletServiceServer).GetInfo(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.WalletService/GetInfo",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(WalletServiceServer).GetInfo(ctx, req.(*GetInfoRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _WalletService_Auth_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AuthRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(WalletServiceServer).Auth(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ocean.v1.WalletService/Auth",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(WalletServiceServer).Auth(ctx, req.(*AuthRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// WalletService_ServiceDesc is the grpc.ServiceDesc for WalletService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var WalletService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "ocean.v1.WalletService",
|
||||
HandlerType: (*WalletServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GenSeed",
|
||||
Handler: _WalletService_GenSeed_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CreateWallet",
|
||||
Handler: _WalletService_CreateWallet_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Unlock",
|
||||
Handler: _WalletService_Unlock_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Lock",
|
||||
Handler: _WalletService_Lock_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ChangePassword",
|
||||
Handler: _WalletService_ChangePassword_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Status",
|
||||
Handler: _WalletService_Status_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetInfo",
|
||||
Handler: _WalletService_GetInfo_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Auth",
|
||||
Handler: _WalletService_Auth_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "RestoreWallet",
|
||||
Handler: _WalletService_RestoreWallet_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "ocean/v1/wallet.proto",
|
||||
}
|
||||
17
server/buf.Dockerfile
Normal file
17
server/buf.Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
||||
FROM golang:1.21-alpine3.18 as builder
|
||||
|
||||
RUN apk add --no-cache git
|
||||
|
||||
RUN go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest
|
||||
RUN go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest
|
||||
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26.0
|
||||
RUN go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1.0
|
||||
|
||||
FROM bufbuild/buf
|
||||
|
||||
COPY --from=builder /go/bin/protoc-gen-openapiv2 /usr/local/bin/protoc-gen-openapiv2
|
||||
COPY --from=builder /go/bin/protoc-gen-grpc-gateway /usr/local/bin/protoc-gen-grpc-gateway
|
||||
COPY --from=builder /go/bin/protoc-gen-go /usr/local/bin/protoc-gen-go
|
||||
COPY --from=builder /go/bin/protoc-gen-go-grpc /usr/local/bin/protoc-gen-go-grpc
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/buf"]
|
||||
21
server/buf.gen.yaml
Executable file
21
server/buf.gen.yaml
Executable file
@@ -0,0 +1,21 @@
|
||||
version: v1
|
||||
managed:
|
||||
enabled: true
|
||||
go_package_prefix:
|
||||
default: github.com/ark-network/ark/api-spec/protobuf/gen
|
||||
except:
|
||||
- buf.build/googleapis/googleapis
|
||||
plugins:
|
||||
# Golang
|
||||
- plugin: go
|
||||
out: api-spec/protobuf/gen
|
||||
opt: paths=source_relative
|
||||
- plugin: go-grpc
|
||||
out: api-spec/protobuf/gen
|
||||
opt: paths=source_relative,require_unimplemented_servers=false
|
||||
- plugin: grpc-gateway
|
||||
out: api-spec/protobuf/gen
|
||||
opt: paths=source_relative
|
||||
#OpenApi
|
||||
- plugin: openapiv2
|
||||
out: api-spec/openapi/swagger
|
||||
4
server/buf.work.yaml
Executable file
4
server/buf.work.yaml
Executable file
@@ -0,0 +1,4 @@
|
||||
version: v1
|
||||
directories:
|
||||
- api-spec/protobuf
|
||||
|
||||
70
server/cmd/arkd/main.go
Executable file
70
server/cmd/arkd/main.go
Executable file
@@ -0,0 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
appconfig "github.com/ark-network/ark/internal/app-config"
|
||||
"github.com/ark-network/ark/internal/config"
|
||||
grpcservice "github.com/ark-network/ark/internal/interface/grpc"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
//nolint:all
|
||||
var (
|
||||
version = "dev"
|
||||
commit = "none"
|
||||
date = "unknown"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg, err := config.LoadConfig()
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("invalid config")
|
||||
}
|
||||
|
||||
log.SetLevel(log.Level(cfg.LogLevel))
|
||||
|
||||
svcConfig := grpcservice.Config{
|
||||
Port: cfg.Port,
|
||||
NoTLS: cfg.NoTLS,
|
||||
}
|
||||
|
||||
if cfg.RoundLifetime%512 != 0 {
|
||||
setLifetime := cfg.RoundLifetime
|
||||
cfg.RoundLifetime = cfg.RoundLifetime - (cfg.RoundLifetime % 512)
|
||||
log.Infof("round lifetime must be a multiple of 512, %d -> %d", setLifetime, cfg.RoundLifetime)
|
||||
}
|
||||
|
||||
appConfig := &appconfig.Config{
|
||||
DbType: cfg.DbType,
|
||||
DbDir: cfg.DbDir,
|
||||
RoundInterval: cfg.RoundInterval,
|
||||
Network: cfg.Network,
|
||||
SchedulerType: cfg.SchedulerType,
|
||||
TxBuilderType: cfg.TxBuilderType,
|
||||
BlockchainScannerType: cfg.BlockchainScannerType,
|
||||
WalletAddr: cfg.WalletAddr,
|
||||
MinRelayFee: cfg.MinRelayFee,
|
||||
RoundLifetime: cfg.RoundLifetime,
|
||||
}
|
||||
svc, err := grpcservice.NewService(svcConfig, appConfig)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.RegisterExitHandler(svc.Stop)
|
||||
|
||||
log.Info("starting service...")
|
||||
if err := svc.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
|
||||
<-sigChan
|
||||
|
||||
log.Info("shutting down service...")
|
||||
log.Exit(0)
|
||||
}
|
||||
84
server/go.mod
Normal file
84
server/go.mod
Normal file
@@ -0,0 +1,84 @@
|
||||
module github.com/ark-network/ark
|
||||
|
||||
go 1.21.0
|
||||
|
||||
replace github.com/ark-network/ark/common => ../common
|
||||
|
||||
require (
|
||||
github.com/ark-network/ark/common v0.0.0
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0
|
||||
github.com/dgraph-io/badger/v4 v4.1.0
|
||||
github.com/go-co-op/gocron v1.36.0
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/google/uuid v1.4.0
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/viper v1.17.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/timshannon/badgerhold/v4 v4.0.3
|
||||
github.com/urfave/cli/v2 v2.26.0
|
||||
github.com/vulpemventures/go-elements v0.5.3
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17
|
||||
google.golang.org/grpc v1.59.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
)
|
||||
|
||||
require github.com/stretchr/objx v0.5.0 // indirect
|
||||
|
||||
require (
|
||||
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect
|
||||
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/btcsuite/btcd v0.23.1
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2
|
||||
github.com/btcsuite/btcd/btcutil v1.1.3
|
||||
github.com/btcsuite/btcd/btcutil/psbt v1.1.4 // indirect
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/golang/glog v1.1.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/flatbuffers v23.5.9+incompatible // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.17.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.3.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.10.0 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941 // indirect
|
||||
github.com/vulpemventures/go-bip32 v0.0.0-20200624192635-867c159da4d7
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/crypto v0.14.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
742
server/go.sum
Normal file
742
server/go.sum
Normal file
@@ -0,0 +1,742 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc=
|
||||
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw=
|
||||
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc=
|
||||
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
|
||||
github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=
|
||||
github.com/btcsuite/btcd v0.23.1 h1:IB8cVQcC2X5mHbnfirLG5IZnkWYNTPlLZVrxUYSotbE=
|
||||
github.com/btcsuite/btcd v0.23.1/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
|
||||
github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0=
|
||||
github.com/btcsuite/btcd/btcutil/psbt v1.1.4 h1:Edx4AfBn+YPam2KP5AobDitulGp4r1Oibm8oruzkMdI=
|
||||
github.com/btcsuite/btcd/btcutil/psbt v1.1.4/go.mod h1:9AyU6EQVJ9Iw9zPyNT1lcdHd6cnEZdno5wLu5FY74os=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||
github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
|
||||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e h1:0XBUw73chJ1VYSsfvcPvVT7auykAJce9FpRr10L6Qhw=
|
||||
github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
|
||||
github.com/dgraph-io/badger/v4 v4.1.0 h1:E38jc0f+RATYrycSUf9LMv/t47XAy+3CApyYSq4APOQ=
|
||||
github.com/dgraph-io/badger/v4 v4.1.0/go.mod h1:P50u28d39ibBRmIJuQC/NSdBOg46HnHw7al2SW5QRHg=
|
||||
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
||||
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/go-co-op/gocron v1.36.0 h1:sEmAwg57l4JWQgzaVWYfKZ+w13uHOqeOtwjo72Ll5Wc=
|
||||
github.com/go-co-op/gocron v1.36.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
|
||||
github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
|
||||
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/flatbuffers v23.5.9+incompatible h1:mTPHyMn3/qO7lvBcm5S9p0olWUQgtQhBf2QWiz1U3qA=
|
||||
github.com/google/flatbuffers v23.5.9+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 h1:6UKoz5ujsI55KNpsJH3UwCq3T8kKbZwNZBNPuTTje8U=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1/go.mod h1:YvJ2f6MplWDhfxiUC3KpyTy76kYUZA4W3pTv/wdKQ9Y=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
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.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
|
||||
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
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=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ=
|
||||
github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY=
|
||||
github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
||||
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI=
|
||||
github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||
github.com/timshannon/badgerhold/v4 v4.0.3 h1:W6pd2qckoXw2cl8eH0ZCV/9CXNaXvaM26tzFi5Tj+v8=
|
||||
github.com/timshannon/badgerhold/v4 v4.0.3/go.mod h1:IkZIr0kcZLMdD7YJfW/G6epb6ZXHD/h0XR2BTk/VZg8=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI=
|
||||
github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941 h1:CTcw80hz/Sw8hqlKX5ZYvBUF5gAHSHwdjXxRf/cjDcI=
|
||||
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:GXBJykxW2kUcktGdsgyay7uwwWvkljASfljNcT0mbh8=
|
||||
github.com/vulpemventures/go-bip32 v0.0.0-20200624192635-867c159da4d7 h1:X7DtNv+YWy76kELMZB/xVkIJ7YNp2vpgMFVsDcQA40U=
|
||||
github.com/vulpemventures/go-bip32 v0.0.0-20200624192635-867c159da4d7/go.mod h1:Zrvx8XgpWvSPdz1lXnuN083CkoZnzwxBLEB03S8et1I=
|
||||
github.com/vulpemventures/go-elements v0.5.3 h1:zaC/ynHFwCAzFSOMfzb6BcbD6FXASppSiGMycc95WVA=
|
||||
github.com/vulpemventures/go-elements v0.5.3/go.mod h1:aBGuWXHaiAIUIcwqCdtEh2iQ3kJjKwHU9ywvhlcRSeU=
|
||||
github.com/vulpemventures/go-secp256k1-zkp v1.1.6 h1:BmsrmXRLUibwa75Qkk8yELjpzCzlAjYFGLiLiOdq7Xo=
|
||||
github.com/vulpemventures/go-secp256k1-zkp v1.1.6/go.mod h1:zo7CpgkuPgoe7fAV+inyxsI9IhGmcoFgyD8nqZaPSOM=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
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.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
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=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
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-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
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=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 h1:I6WNifs6pF9tNdSob2W24JtyxIYjzFB9qDlpUC76q+U=
|
||||
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
||||
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
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=
|
||||
launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54=
|
||||
launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
35
server/goreleaser.Dockerfile
Executable file
35
server/goreleaser.Dockerfile
Executable file
@@ -0,0 +1,35 @@
|
||||
FROM debian:buster-slim
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN set -ex \
|
||||
&& if [ "${TARGETPLATFORM}" = "linux/amd64" ]; then export TARGETPLATFORM=amd64; fi \
|
||||
&& if [ "${TARGETPLATFORM}" = "linux/arm64" ]; then export TARGETPLATFORM=arm64; fi \
|
||||
&& mv "arkd-linux-$TARGETPLATFORM" /usr/local/bin/arkd
|
||||
|
||||
|
||||
# $USER name, and data $DIR to be used in the 'final' image
|
||||
ARG USER=ark
|
||||
ARG DIR=/home/ark
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates
|
||||
|
||||
# NOTE: Default GID == UID == 1000
|
||||
RUN adduser --disabled-password \
|
||||
--home "$DIR/" \
|
||||
--gecos "" \
|
||||
"$USER"
|
||||
USER $USER
|
||||
|
||||
# Prevents 'VOLUME $DIR/.arkd/' being created as owned by 'root'
|
||||
RUN mkdir -p "$DIR/.arkd/"
|
||||
|
||||
# Expose volume containing all arkd data
|
||||
VOLUME $DIR/.arkd/
|
||||
|
||||
ENTRYPOINT [ "arkd" ]
|
||||
|
||||
249
server/internal/app-config/config.go
Normal file
249
server/internal/app-config/config.go
Normal file
@@ -0,0 +1,249 @@
|
||||
package appconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ark-network/ark/common"
|
||||
"github.com/ark-network/ark/internal/core/application"
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
"github.com/ark-network/ark/internal/infrastructure/db"
|
||||
oceanwallet "github.com/ark-network/ark/internal/infrastructure/ocean-wallet"
|
||||
scheduler "github.com/ark-network/ark/internal/infrastructure/scheduler/gocron"
|
||||
txbuilder "github.com/ark-network/ark/internal/infrastructure/tx-builder/covenant"
|
||||
txbuilderdummy "github.com/ark-network/ark/internal/infrastructure/tx-builder/dummy"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vulpemventures/go-elements/network"
|
||||
)
|
||||
|
||||
var (
|
||||
supportedDbs = supportedType{
|
||||
"badger": {},
|
||||
}
|
||||
supportedSchedulers = supportedType{
|
||||
"gocron": {},
|
||||
}
|
||||
supportedTxBuilders = supportedType{
|
||||
"dummy": {},
|
||||
"covenant": {},
|
||||
}
|
||||
supportedScanners = supportedType{
|
||||
"ocean": {},
|
||||
}
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
DbType string
|
||||
DbDir string
|
||||
RoundInterval int64
|
||||
Network common.Network
|
||||
SchedulerType string
|
||||
TxBuilderType string
|
||||
BlockchainScannerType string
|
||||
WalletAddr string
|
||||
MinRelayFee uint64
|
||||
RoundLifetime int64
|
||||
|
||||
repo ports.RepoManager
|
||||
svc application.Service
|
||||
wallet ports.WalletService
|
||||
txBuilder ports.TxBuilder
|
||||
scanner ports.BlockchainScanner
|
||||
scheduler ports.SchedulerService
|
||||
}
|
||||
|
||||
func (c *Config) Validate() error {
|
||||
if !supportedDbs.supports(c.DbType) {
|
||||
return fmt.Errorf("db type not supported, please select one of: %s", supportedDbs)
|
||||
}
|
||||
if !supportedSchedulers.supports(c.SchedulerType) {
|
||||
return fmt.Errorf("scheduler type not supported, please select one of: %s", supportedSchedulers)
|
||||
}
|
||||
if !supportedTxBuilders.supports(c.TxBuilderType) {
|
||||
return fmt.Errorf("tx builder type not supported, please select one of: %s", supportedTxBuilders)
|
||||
}
|
||||
if !supportedScanners.supports(c.BlockchainScannerType) {
|
||||
return fmt.Errorf("blockchain scanner type not supported, please select one of: %s", supportedScanners)
|
||||
}
|
||||
if c.RoundInterval < 5 {
|
||||
return fmt.Errorf("invalid round interval, must be at least 5 seconds")
|
||||
}
|
||||
if c.Network.Name != "liquid" && c.Network.Name != "testnet" {
|
||||
return fmt.Errorf("invalid network, must be either liquid or testnet")
|
||||
}
|
||||
if len(c.WalletAddr) <= 0 {
|
||||
return fmt.Errorf("missing onchain wallet address")
|
||||
}
|
||||
if c.MinRelayFee < 30 {
|
||||
return fmt.Errorf("invalid min relay fee, must be at least 30 sats")
|
||||
}
|
||||
if err := c.repoManager(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.walletService(); err != nil {
|
||||
return fmt.Errorf("failed to connect to wallet: %s", err)
|
||||
}
|
||||
if err := c.txBuilderService(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.scannerService(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.schedulerService(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.appService(); err != nil {
|
||||
return err
|
||||
}
|
||||
// round life time must be a multiple of 512
|
||||
if c.RoundLifetime <= 0 || c.RoundLifetime%512 != 0 {
|
||||
return fmt.Errorf("invalid round lifetime, must be greater than 0 and a multiple of 512")
|
||||
}
|
||||
seq, err := common.BIP68Encode(uint(c.RoundLifetime))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid round lifetime, %s", err)
|
||||
}
|
||||
|
||||
seconds, err := common.BIP68Decode(seq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid round lifetime, %s", err)
|
||||
}
|
||||
|
||||
if seconds != uint(c.RoundLifetime) {
|
||||
return fmt.Errorf("invalid round lifetime, must be a multiple of 512")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) AppService() application.Service {
|
||||
return c.svc
|
||||
}
|
||||
|
||||
func (c *Config) repoManager() error {
|
||||
var svc ports.RepoManager
|
||||
var err error
|
||||
switch c.DbType {
|
||||
case "badger":
|
||||
logger := log.New()
|
||||
svc, err = db.NewService(db.ServiceConfig{
|
||||
EventStoreType: c.DbType,
|
||||
RoundStoreType: c.DbType,
|
||||
VtxoStoreType: c.DbType,
|
||||
|
||||
EventStoreConfig: []interface{}{c.DbDir, logger},
|
||||
RoundStoreConfig: []interface{}{c.DbDir, logger},
|
||||
VtxoStoreConfig: []interface{}{c.DbDir, logger},
|
||||
})
|
||||
default:
|
||||
return fmt.Errorf("unknown db type")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.repo = svc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) walletService() error {
|
||||
svc, err := oceanwallet.NewService(c.WalletAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.wallet = svc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) txBuilderService() error {
|
||||
var svc ports.TxBuilder
|
||||
var err error
|
||||
net := c.mainChain()
|
||||
|
||||
switch c.TxBuilderType {
|
||||
case "dummy":
|
||||
svc = txbuilderdummy.NewTxBuilder(c.wallet, net)
|
||||
case "covenant":
|
||||
svc = txbuilder.NewTxBuilder(c.wallet, net, c.RoundLifetime)
|
||||
default:
|
||||
err = fmt.Errorf("unknown tx builder type")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.txBuilder = svc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) scannerService() error {
|
||||
var svc ports.BlockchainScanner
|
||||
var err error
|
||||
switch c.BlockchainScannerType {
|
||||
case "ocean":
|
||||
svc = c.wallet
|
||||
default:
|
||||
err = fmt.Errorf("unknown blockchain scanner type")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.scanner = svc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) schedulerService() error {
|
||||
var svc ports.SchedulerService
|
||||
var err error
|
||||
switch c.SchedulerType {
|
||||
case "gocron":
|
||||
svc = scheduler.NewScheduler()
|
||||
default:
|
||||
err = fmt.Errorf("unknown scheduler type")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.scheduler = svc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) appService() error {
|
||||
net := c.mainChain()
|
||||
svc, err := application.NewService(
|
||||
c.Network, net, c.RoundInterval, c.RoundLifetime, c.MinRelayFee,
|
||||
c.wallet, c.repo, c.txBuilder, c.scanner, c.scheduler,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.svc = svc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) mainChain() network.Network {
|
||||
net := network.Liquid
|
||||
if c.Network.Name != "mainnet" {
|
||||
net = network.Testnet
|
||||
}
|
||||
return net
|
||||
}
|
||||
|
||||
type supportedType map[string]struct{}
|
||||
|
||||
func (t supportedType) String() string {
|
||||
types := make([]string, 0, len(t))
|
||||
for tt := range t {
|
||||
types = append(types, tt)
|
||||
}
|
||||
return strings.Join(types, " | ")
|
||||
}
|
||||
|
||||
func (t supportedType) supports(typeStr string) bool {
|
||||
_, ok := t[typeStr]
|
||||
return ok
|
||||
}
|
||||
122
server/internal/config/config.go
Normal file
122
server/internal/config/config.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
common "github.com/ark-network/ark/common"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
WalletAddr string
|
||||
RoundInterval int64
|
||||
Port uint32
|
||||
DbType string
|
||||
DbDir string
|
||||
SchedulerType string
|
||||
TxBuilderType string
|
||||
BlockchainScannerType string
|
||||
NoTLS bool
|
||||
Network common.Network
|
||||
LogLevel int
|
||||
MinRelayFee uint64
|
||||
RoundLifetime int64
|
||||
}
|
||||
|
||||
var (
|
||||
Datadir = "DATADIR"
|
||||
WalletAddr = "WALLET_ADDR"
|
||||
RoundInterval = "ROUND_INTERVAL"
|
||||
Port = "PORT"
|
||||
DbType = "DB_TYPE"
|
||||
SchedulerType = "SCHEDULER_TYPE"
|
||||
TxBuilderType = "TX_BUILDER_TYPE"
|
||||
BlockchainScannerType = "BC_SCANNER_TYPE"
|
||||
Insecure = "INSECURE"
|
||||
LogLevel = "LOG_LEVEL"
|
||||
Network = "NETWORK"
|
||||
MinRelayFee = "MIN_RELAY_FEE"
|
||||
RoundLifetime = "ROUND_LIFETIME"
|
||||
|
||||
defaultDatadir = common.AppDataDir("arkd", false)
|
||||
defaultRoundInterval = 60
|
||||
defaultPort = 6000
|
||||
defaultDbType = "badger"
|
||||
defaultSchedulerType = "gocron"
|
||||
defaultTxBuilderType = "covenant"
|
||||
defaultBlockchainScannerType = "ocean"
|
||||
defaultInsecure = true
|
||||
defaultNetwork = "testnet"
|
||||
defaultLogLevel = 5
|
||||
defaultMinRelayFee = 30
|
||||
defaultRoundLifetime = 512
|
||||
)
|
||||
|
||||
func LoadConfig() (*Config, error) {
|
||||
viper.SetEnvPrefix("ARK")
|
||||
viper.AutomaticEnv()
|
||||
|
||||
viper.SetDefault(Datadir, defaultDatadir)
|
||||
viper.SetDefault(RoundInterval, defaultRoundInterval)
|
||||
viper.SetDefault(Port, defaultPort)
|
||||
viper.SetDefault(DbType, defaultDbType)
|
||||
viper.SetDefault(SchedulerType, defaultSchedulerType)
|
||||
viper.SetDefault(TxBuilderType, defaultTxBuilderType)
|
||||
viper.SetDefault(BlockchainScannerType, defaultBlockchainScannerType)
|
||||
viper.SetDefault(Insecure, defaultInsecure)
|
||||
viper.SetDefault(LogLevel, defaultLogLevel)
|
||||
viper.SetDefault(Network, defaultNetwork)
|
||||
viper.SetDefault(RoundLifetime, defaultRoundLifetime)
|
||||
viper.SetDefault(MinRelayFee, defaultMinRelayFee)
|
||||
|
||||
net, err := getNetwork()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := initDatadir(); err != nil {
|
||||
return nil, fmt.Errorf("error while creating datadir: %s", err)
|
||||
}
|
||||
|
||||
return &Config{
|
||||
WalletAddr: viper.GetString(WalletAddr),
|
||||
RoundInterval: viper.GetInt64(RoundInterval),
|
||||
Port: viper.GetUint32(Port),
|
||||
DbType: viper.GetString(DbType),
|
||||
SchedulerType: viper.GetString(SchedulerType),
|
||||
TxBuilderType: viper.GetString(TxBuilderType),
|
||||
BlockchainScannerType: viper.GetString(BlockchainScannerType),
|
||||
NoTLS: viper.GetBool(Insecure),
|
||||
DbDir: filepath.Join(viper.GetString(Datadir), "db"),
|
||||
LogLevel: viper.GetInt(LogLevel),
|
||||
Network: net,
|
||||
MinRelayFee: viper.GetUint64(MinRelayFee),
|
||||
RoundLifetime: viper.GetInt64(RoundLifetime),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func initDatadir() error {
|
||||
datadir := viper.GetString(Datadir)
|
||||
return makeDirectoryIfNotExists(datadir)
|
||||
}
|
||||
|
||||
func makeDirectoryIfNotExists(path string) error {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return os.MkdirAll(path, os.ModeDir|0755)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getNetwork() (common.Network, error) {
|
||||
switch strings.ToLower(viper.GetString(Network)) {
|
||||
case "mainnet":
|
||||
return common.MainNet, nil
|
||||
case "testnet":
|
||||
return common.TestNet, nil
|
||||
default:
|
||||
return common.Network{}, fmt.Errorf("unknown network")
|
||||
}
|
||||
}
|
||||
601
server/internal/core/application/service.go
Normal file
601
server/internal/core/application/service.go
Normal file
@@ -0,0 +1,601 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ark-network/ark/common"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vulpemventures/go-elements/network"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
)
|
||||
|
||||
var (
|
||||
paymentsThreshold = int64(128)
|
||||
dustAmount = uint64(450)
|
||||
faucetVtxo = domain.VtxoKey{
|
||||
Txid: "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
VOut: 0,
|
||||
}
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
Start() error
|
||||
Stop()
|
||||
SpendVtxos(ctx context.Context, inputs []domain.VtxoKey) (string, error)
|
||||
ClaimVtxos(ctx context.Context, creds string, receivers []domain.Receiver) error
|
||||
SignVtxos(ctx context.Context, forfeitTxs []string) error
|
||||
FaucetVtxos(ctx context.Context, pubkey *secp256k1.PublicKey) error
|
||||
GetRoundByTxid(ctx context.Context, poolTxid string) (*domain.Round, error)
|
||||
GetEventsChannel(ctx context.Context) <-chan domain.RoundEvent
|
||||
UpdatePaymentStatus(ctx context.Context, id string) error
|
||||
ListVtxos(ctx context.Context, pubkey *secp256k1.PublicKey) ([]domain.Vtxo, error)
|
||||
GetPubkey(ctx context.Context) (string, error)
|
||||
}
|
||||
|
||||
type service struct {
|
||||
network common.Network
|
||||
onchainNework network.Network
|
||||
pubkey *secp256k1.PublicKey
|
||||
roundLifetime int64
|
||||
roundInterval int64
|
||||
minRelayFee uint64
|
||||
|
||||
wallet ports.WalletService
|
||||
repoManager ports.RepoManager
|
||||
builder ports.TxBuilder
|
||||
scanner ports.BlockchainScanner
|
||||
sweeper *sweeper
|
||||
|
||||
paymentRequests *paymentsMap
|
||||
forfeitTxs *forfeitTxsMap
|
||||
|
||||
eventsCh chan domain.RoundEvent
|
||||
}
|
||||
|
||||
func NewService(
|
||||
network common.Network, onchainNetwork network.Network,
|
||||
roundInterval, roundLifetime int64, minRelayFee uint64,
|
||||
walletSvc ports.WalletService, repoManager ports.RepoManager,
|
||||
builder ports.TxBuilder, scanner ports.BlockchainScanner,
|
||||
scheduler ports.SchedulerService,
|
||||
) (Service, error) {
|
||||
eventsCh := make(chan domain.RoundEvent)
|
||||
paymentRequests := newPaymentsMap(nil)
|
||||
|
||||
genesisHash, _ := chainhash.NewHashFromStr(onchainNetwork.GenesisBlockHash)
|
||||
forfeitTxs := newForfeitTxsMap(genesisHash)
|
||||
pubkey, err := walletSvc.GetPubkey(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch pubkey: %s", err)
|
||||
}
|
||||
|
||||
sweeper := newSweeper(walletSvc, repoManager, builder, scheduler)
|
||||
|
||||
svc := &service{
|
||||
network, onchainNetwork, pubkey,
|
||||
roundLifetime, roundInterval, minRelayFee,
|
||||
walletSvc, repoManager, builder, scanner, sweeper,
|
||||
paymentRequests, forfeitTxs, eventsCh,
|
||||
}
|
||||
repoManager.RegisterEventsHandler(
|
||||
func(round *domain.Round) {
|
||||
svc.updateProjectionStore(round)
|
||||
svc.propagateEvents(round)
|
||||
},
|
||||
)
|
||||
|
||||
if err := svc.restoreWatchingVtxos(); err != nil {
|
||||
return nil, fmt.Errorf("failed to restore watching vtxos: %s", err)
|
||||
}
|
||||
go svc.listenToRedemptions()
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
func (s *service) Start() error {
|
||||
log.Debug("starting sweeper service")
|
||||
if err := s.sweeper.start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug("starting app service")
|
||||
go s.start()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *service) Stop() {
|
||||
s.sweeper.stop()
|
||||
// nolint
|
||||
vtxos, _ := s.repoManager.Vtxos().GetSpendableVtxos(
|
||||
context.Background(), "",
|
||||
)
|
||||
if len(vtxos) > 0 {
|
||||
s.stopWatchingVtxos(vtxos)
|
||||
}
|
||||
|
||||
s.wallet.Close()
|
||||
log.Debug("closed connection to wallet")
|
||||
s.repoManager.Close()
|
||||
log.Debug("closed connection to db")
|
||||
}
|
||||
|
||||
func (s *service) SpendVtxos(ctx context.Context, inputs []domain.VtxoKey) (string, error) {
|
||||
vtxos, err := s.repoManager.Vtxos().GetVtxos(ctx, inputs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, v := range vtxos {
|
||||
if v.Spent {
|
||||
return "", fmt.Errorf("input %s:%d already spent", v.Txid, v.VOut)
|
||||
}
|
||||
}
|
||||
|
||||
payment, err := domain.NewPayment(vtxos)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := s.paymentRequests.push(*payment); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return payment.Id, nil
|
||||
}
|
||||
|
||||
func (s *service) ClaimVtxos(ctx context.Context, creds string, receivers []domain.Receiver) error {
|
||||
// Check credentials
|
||||
payment, ok := s.paymentRequests.view(creds)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid credentials")
|
||||
}
|
||||
|
||||
if err := payment.AddReceivers(receivers); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.paymentRequests.update(*payment)
|
||||
}
|
||||
|
||||
func (s *service) UpdatePaymentStatus(_ context.Context, id string) error {
|
||||
return s.paymentRequests.updatePingTimestamp(id)
|
||||
}
|
||||
|
||||
func (s *service) FaucetVtxos(ctx context.Context, userPubkey *secp256k1.PublicKey) error {
|
||||
pubkey := hex.EncodeToString(userPubkey.SerializeCompressed())
|
||||
|
||||
payment, err := domain.NewPayment([]domain.Vtxo{
|
||||
{
|
||||
VtxoKey: faucetVtxo,
|
||||
Receiver: domain.Receiver{
|
||||
Pubkey: pubkey,
|
||||
Amount: 10000,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := payment.AddReceivers([]domain.Receiver{
|
||||
{Pubkey: pubkey, Amount: 1000},
|
||||
{Pubkey: pubkey, Amount: 1000},
|
||||
{Pubkey: pubkey, Amount: 1000},
|
||||
{Pubkey: pubkey, Amount: 1000},
|
||||
{Pubkey: pubkey, Amount: 1000},
|
||||
{Pubkey: pubkey, Amount: 1000},
|
||||
{Pubkey: pubkey, Amount: 1000},
|
||||
{Pubkey: pubkey, Amount: 1000},
|
||||
{Pubkey: pubkey, Amount: 1000},
|
||||
{Pubkey: pubkey, Amount: 1000},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.paymentRequests.push(*payment); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.paymentRequests.updatePingTimestamp(payment.Id)
|
||||
}
|
||||
|
||||
func (s *service) SignVtxos(ctx context.Context, forfeitTxs []string) error {
|
||||
return s.forfeitTxs.sign(forfeitTxs)
|
||||
}
|
||||
|
||||
func (s *service) ListVtxos(ctx context.Context, pubkey *secp256k1.PublicKey) ([]domain.Vtxo, error) {
|
||||
pk := hex.EncodeToString(pubkey.SerializeCompressed())
|
||||
return s.repoManager.Vtxos().GetSpendableVtxos(ctx, pk)
|
||||
}
|
||||
|
||||
func (s *service) GetEventsChannel(ctx context.Context) <-chan domain.RoundEvent {
|
||||
return s.eventsCh
|
||||
}
|
||||
|
||||
func (s *service) GetRoundByTxid(ctx context.Context, poolTxid string) (*domain.Round, error) {
|
||||
return s.repoManager.Rounds().GetRoundWithTxid(ctx, poolTxid)
|
||||
}
|
||||
|
||||
func (s *service) GetPubkey(ctx context.Context) (string, error) {
|
||||
pubkey, err := common.EncodePubKey(s.network.PubKey, s.pubkey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return pubkey, nil
|
||||
}
|
||||
|
||||
func (s *service) start() {
|
||||
s.startRound()
|
||||
}
|
||||
|
||||
func (s *service) startRound() {
|
||||
round := domain.NewRound(dustAmount)
|
||||
changes, _ := round.StartRegistration()
|
||||
if err := s.repoManager.Events().Save(
|
||||
context.Background(), round.Id, changes...,
|
||||
); err != nil {
|
||||
log.WithError(err).Warn("failed to store new round events")
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
time.Sleep(time.Duration(s.roundInterval/2) * time.Second)
|
||||
s.startFinalization()
|
||||
}()
|
||||
|
||||
log.Debugf("started registration stage for new round: %s", round.Id)
|
||||
}
|
||||
|
||||
func (s *service) startFinalization() {
|
||||
ctx := context.Background()
|
||||
round, err := s.repoManager.Rounds().GetCurrentRound(ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("failed to retrieve current round")
|
||||
return
|
||||
}
|
||||
|
||||
var changes []domain.RoundEvent
|
||||
defer func() {
|
||||
if len(changes) > 0 {
|
||||
if err := s.repoManager.Events().Save(ctx, round.Id, changes...); err != nil {
|
||||
log.WithError(err).Warn("failed to store new round events")
|
||||
}
|
||||
}
|
||||
|
||||
if round.IsFailed() {
|
||||
s.startRound()
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Duration((s.roundInterval/2)-1) * time.Second)
|
||||
s.finalizeRound()
|
||||
}()
|
||||
|
||||
if round.IsFailed() {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: understand how many payments must be popped from the queue and actually registered for the round
|
||||
num := s.paymentRequests.len()
|
||||
if num == 0 {
|
||||
err := fmt.Errorf("no payments registered")
|
||||
changes = round.Fail(fmt.Errorf("round aborted: %s", err))
|
||||
log.WithError(err).Debugf("round %s aborted", round.Id)
|
||||
return
|
||||
}
|
||||
if num > paymentsThreshold {
|
||||
num = paymentsThreshold
|
||||
}
|
||||
payments := s.paymentRequests.pop(num)
|
||||
changes, err = round.RegisterPayments(payments)
|
||||
if err != nil {
|
||||
changes = round.Fail(fmt.Errorf("failed to register payments: %s", err))
|
||||
log.WithError(err).Warn("failed to register payments")
|
||||
return
|
||||
}
|
||||
|
||||
unsignedPoolTx, tree, err := s.builder.BuildPoolTx(s.pubkey, payments, s.minRelayFee)
|
||||
if err != nil {
|
||||
changes = round.Fail(fmt.Errorf("failed to create pool tx: %s", err))
|
||||
log.WithError(err).Warn("failed to create pool tx")
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("pool tx created for round %s", round.Id)
|
||||
|
||||
connectors, forfeitTxs, err := s.builder.BuildForfeitTxs(s.pubkey, unsignedPoolTx, payments)
|
||||
if err != nil {
|
||||
changes = round.Fail(fmt.Errorf("failed to create connectors and forfeit txs: %s", err))
|
||||
log.WithError(err).Warn("failed to create connectors and forfeit txs")
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("forfeit transactions created for round %s", round.Id)
|
||||
|
||||
events, err := round.StartFinalization(connectors, tree, unsignedPoolTx)
|
||||
if err != nil {
|
||||
changes = round.Fail(fmt.Errorf("failed to start finalization: %s", err))
|
||||
log.WithError(err).Warn("failed to start finalization")
|
||||
return
|
||||
}
|
||||
changes = append(changes, events...)
|
||||
|
||||
s.forfeitTxs.push(forfeitTxs)
|
||||
|
||||
log.Debugf("started finalization stage for round: %s", round.Id)
|
||||
}
|
||||
|
||||
func (s *service) finalizeRound() {
|
||||
defer s.startRound()
|
||||
|
||||
ctx := context.Background()
|
||||
round, err := s.repoManager.Rounds().GetCurrentRound(ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("failed to retrieve current round")
|
||||
return
|
||||
}
|
||||
if round.IsFailed() {
|
||||
return
|
||||
}
|
||||
|
||||
var changes []domain.RoundEvent
|
||||
defer func() {
|
||||
if err := s.repoManager.Events().Save(ctx, round.Id, changes...); err != nil {
|
||||
log.WithError(err).Warn("failed to store new round events")
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
forfeitTxs, leftUnsigned := s.forfeitTxs.pop()
|
||||
if len(leftUnsigned) > 0 {
|
||||
err := fmt.Errorf("%d forfeit txs left to sign", len(leftUnsigned))
|
||||
changes = round.Fail(fmt.Errorf("failed to finalize round: %s", err))
|
||||
log.WithError(err).Warn("failed to finalize round")
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("signing round transaction %s\n", round.Id)
|
||||
signedPoolTx, err := s.wallet.SignPset(ctx, round.UnsignedTx, true)
|
||||
if err != nil {
|
||||
changes = round.Fail(fmt.Errorf("failed to sign round tx: %s", err))
|
||||
log.WithError(err).Warn("failed to sign round tx")
|
||||
return
|
||||
}
|
||||
|
||||
txid, err := s.wallet.BroadcastTransaction(ctx, signedPoolTx)
|
||||
if err != nil {
|
||||
changes = round.Fail(fmt.Errorf("failed to broadcast pool tx: %s", err))
|
||||
log.WithError(err).Warn("failed to broadcast pool tx")
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
expirationTimestamp := now + s.roundLifetime + 30 // add 30 secs to be sure that the tx is confirmed
|
||||
|
||||
if err := s.sweeper.schedule(expirationTimestamp, txid, round.CongestionTree); err != nil {
|
||||
changes = round.Fail(fmt.Errorf("failed to schedule sweep tx: %s", err))
|
||||
log.WithError(err).Warn("failed to schedule sweep tx")
|
||||
return
|
||||
}
|
||||
|
||||
changes, _ = round.EndFinalization(forfeitTxs, txid)
|
||||
|
||||
log.Debugf("finalized round %s with pool tx %s", round.Id, round.Txid)
|
||||
}
|
||||
|
||||
func (s *service) listenToRedemptions() {
|
||||
ctx := context.Background()
|
||||
chVtxos := s.scanner.GetNotificationChannel(ctx)
|
||||
for vtxoKeys := range chVtxos {
|
||||
if len(vtxoKeys) > 0 {
|
||||
for {
|
||||
// TODO: make sure that the vtxos haven't been already spent, otherwise
|
||||
// broadcast the corresponding forfeit tx and connector to prevent
|
||||
// getting cheated.
|
||||
vtxos, err := s.repoManager.Vtxos().RedeemVtxos(ctx, vtxoKeys)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("failed to redeem vtxos, retrying...")
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
if len(vtxos) > 0 {
|
||||
log.Debugf("redeemed %d vtxos", len(vtxos))
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *service) updateProjectionStore(round *domain.Round) {
|
||||
ctx := context.Background()
|
||||
lastChange := round.Events()[len(round.Events())-1]
|
||||
// Update the vtxo set only after a round is finalized.
|
||||
if _, ok := lastChange.(domain.RoundFinalized); ok {
|
||||
repo := s.repoManager.Vtxos()
|
||||
spentVtxos := getSpentVtxos(round.Payments)
|
||||
if len(spentVtxos) > 0 {
|
||||
for {
|
||||
if err := repo.SpendVtxos(ctx, spentVtxos); err != nil {
|
||||
log.WithError(err).Warn("failed to add new vtxos, retrying soon")
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
log.Debugf("spent %d vtxos", len(spentVtxos))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
newVtxos := s.getNewVtxos(round)
|
||||
for {
|
||||
if err := repo.AddVtxos(ctx, newVtxos); err != nil {
|
||||
log.WithError(err).Warn("failed to add new vtxos, retrying soon")
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
log.Debugf("added %d new vtxos", len(newVtxos))
|
||||
break
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
if err := s.startWatchingVtxos(newVtxos); err != nil {
|
||||
log.WithError(err).Warn(
|
||||
"failed to start watching vtxos, retrying in a moment...",
|
||||
)
|
||||
continue
|
||||
}
|
||||
log.Debugf("started watching %d vtxos", len(newVtxos))
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Always update the status of the round.
|
||||
for {
|
||||
if err := s.repoManager.Rounds().AddOrUpdateRound(ctx, *round); err != nil {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func (s *service) propagateEvents(round *domain.Round) {
|
||||
lastEvent := round.Events()[len(round.Events())-1]
|
||||
switch e := lastEvent.(type) {
|
||||
case domain.RoundFinalizationStarted:
|
||||
forfeitTxs := s.forfeitTxs.view()
|
||||
s.eventsCh <- domain.RoundFinalizationStarted{
|
||||
Id: e.Id,
|
||||
CongestionTree: e.CongestionTree,
|
||||
Connectors: e.Connectors,
|
||||
PoolTx: e.PoolTx,
|
||||
UnsignedForfeitTxs: forfeitTxs,
|
||||
}
|
||||
case domain.RoundFinalized, domain.RoundFailed:
|
||||
s.eventsCh <- e
|
||||
}
|
||||
}
|
||||
|
||||
func (s *service) getNewVtxos(round *domain.Round) []domain.Vtxo {
|
||||
leaves := round.CongestionTree.Leaves()
|
||||
vtxos := make([]domain.Vtxo, 0)
|
||||
for _, node := range leaves {
|
||||
tx, _ := psetv2.NewPsetFromBase64(node.Tx)
|
||||
for i, out := range tx.Outputs {
|
||||
for _, p := range round.Payments {
|
||||
var pubkey string
|
||||
found := false
|
||||
for _, r := range p.Receivers {
|
||||
if r.IsOnchain() {
|
||||
continue
|
||||
}
|
||||
|
||||
buf, _ := hex.DecodeString(r.Pubkey)
|
||||
pk, _ := secp256k1.ParsePubKey(buf)
|
||||
script, _ := s.builder.GetVtxoScript(pk, s.pubkey)
|
||||
if bytes.Equal(script, out.Script) {
|
||||
found = true
|
||||
pubkey = r.Pubkey
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
vtxos = append(vtxos, domain.Vtxo{
|
||||
VtxoKey: domain.VtxoKey{Txid: node.Txid, VOut: uint32(i)},
|
||||
Receiver: domain.Receiver{Pubkey: pubkey, Amount: out.Value},
|
||||
PoolTx: round.Txid,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return vtxos
|
||||
}
|
||||
|
||||
func (s *service) startWatchingVtxos(vtxos []domain.Vtxo) error {
|
||||
scripts, err := s.extractVtxosScripts(vtxos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.scanner.WatchScripts(context.Background(), scripts)
|
||||
}
|
||||
|
||||
func (s *service) stopWatchingVtxos(vtxos []domain.Vtxo) {
|
||||
scripts, err := s.extractVtxosScripts(vtxos)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("failed to extract scripts from vtxos")
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
if err := s.scanner.UnwatchScripts(context.Background(), scripts); err != nil {
|
||||
log.WithError(err).Warn("failed to stop watching vtxos, retrying in a moment...")
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
log.Debugf("stopped watching %d vtxos", len(vtxos))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func (s *service) restoreWatchingVtxos() error {
|
||||
vtxos, err := s.repoManager.Vtxos().GetSpendableVtxos(
|
||||
context.Background(), "",
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(vtxos) <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.startWatchingVtxos(vtxos); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("restored watching %d vtxos", len(vtxos))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *service) extractVtxosScripts(vtxos []domain.Vtxo) ([]string, error) {
|
||||
indexedScripts := make(map[string]struct{})
|
||||
for _, vtxo := range vtxos {
|
||||
buf, err := hex.DecodeString(vtxo.Pubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userPubkey, err := secp256k1.ParsePubKey(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
script, err := s.builder.GetVtxoScript(userPubkey, s.pubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
indexedScripts[hex.EncodeToString(script)] = struct{}{}
|
||||
}
|
||||
scripts := make([]string, 0, len(indexedScripts))
|
||||
for script := range indexedScripts {
|
||||
scripts = append(scripts, script)
|
||||
}
|
||||
return scripts, nil
|
||||
}
|
||||
|
||||
func getSpentVtxos(payments map[string]domain.Payment) []domain.VtxoKey {
|
||||
vtxos := make([]domain.VtxoKey, 0)
|
||||
for _, p := range payments {
|
||||
for _, vtxo := range p.Inputs {
|
||||
if vtxo.VtxoKey == faucetVtxo {
|
||||
continue
|
||||
}
|
||||
vtxos = append(vtxos, vtxo.VtxoKey)
|
||||
}
|
||||
}
|
||||
return vtxos
|
||||
}
|
||||
579
server/internal/core/application/sweeper.go
Normal file
579
server/internal/core/application/sweeper.go
Normal file
@@ -0,0 +1,579 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
)
|
||||
|
||||
// sweeper is an unexported service running while the main application service is started
|
||||
// it is responsible for sweeping onchain shared outputs that expired
|
||||
// it also handles delaying the sweep events in case some parts of the tree are broadcasted
|
||||
// when a round is finalized, the main application service schedules a sweep event on the newly created congestion tree
|
||||
type sweeper struct {
|
||||
wallet ports.WalletService
|
||||
repoManager ports.RepoManager
|
||||
builder ports.TxBuilder
|
||||
scheduler ports.SchedulerService
|
||||
|
||||
// cache of scheduled tasks, avoid scheduling the same sweep event multiple times
|
||||
scheduledTasks map[string]struct{}
|
||||
}
|
||||
|
||||
func newSweeper(
|
||||
wallet ports.WalletService,
|
||||
repoManager ports.RepoManager,
|
||||
builder ports.TxBuilder,
|
||||
scheduler ports.SchedulerService,
|
||||
) *sweeper {
|
||||
return &sweeper{
|
||||
wallet,
|
||||
repoManager,
|
||||
builder,
|
||||
scheduler,
|
||||
make(map[string]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sweeper) start() error {
|
||||
s.scheduler.Start()
|
||||
|
||||
allRounds, err := s.repoManager.Rounds().GetSweepableRounds(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, round := range allRounds {
|
||||
task := s.createTask(round.Txid, round.CongestionTree)
|
||||
task()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sweeper) stop() {
|
||||
s.scheduler.Stop()
|
||||
}
|
||||
|
||||
// removeTask update the cached map of scheduled tasks
|
||||
func (s *sweeper) removeTask(treeRootTxid string) {
|
||||
delete(s.scheduledTasks, treeRootTxid)
|
||||
}
|
||||
|
||||
// schedule set up a task to be executed once at the given timestamp
|
||||
func (s *sweeper) schedule(
|
||||
expirationTimestamp int64, roundTxid string, congestionTree tree.CongestionTree,
|
||||
) error {
|
||||
root, err := congestionTree.Root()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, scheduled := s.scheduledTasks[root.Txid]; scheduled {
|
||||
return nil
|
||||
}
|
||||
|
||||
task := s.createTask(roundTxid, congestionTree)
|
||||
fancyTime := time.Unix(expirationTimestamp, 0).Format("2006-01-02 15:04:05")
|
||||
log.Debugf("scheduled sweep task at %s", fancyTime)
|
||||
if err := s.scheduler.ScheduleTaskOnce(expirationTimestamp, task); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.scheduledTasks[root.Txid] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// createTask returns a function passed as handler in the scheduler
|
||||
// it tries to craft a sweep tx containing the onchain outputs of the given congestion tree
|
||||
// if some parts of the tree have been broadcasted in the meantine, it will schedule the next taskes for the remaining parts of the tree
|
||||
func (s *sweeper) createTask(
|
||||
roundTxid string, congestionTree tree.CongestionTree,
|
||||
) func() {
|
||||
return func() {
|
||||
ctx := context.Background()
|
||||
root, err := congestionTree.Root()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while getting root node")
|
||||
return
|
||||
}
|
||||
|
||||
s.removeTask(root.Txid)
|
||||
log.Debugf("sweeper: %s", root.Txid)
|
||||
|
||||
sweepInputs := make([]ports.SweepInput, 0)
|
||||
vtxoKeys := make([]domain.VtxoKey, 0) // vtxos associated to the sweep inputs
|
||||
|
||||
// inspect the congestion tree to find onchain shared outputs
|
||||
sharedOutputs, err := s.findSweepableOutputs(ctx, congestionTree)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while inspecting congestion tree")
|
||||
return
|
||||
}
|
||||
|
||||
for expiredAt, inputs := range sharedOutputs {
|
||||
// if the shared outputs are not expired, schedule a sweep task for it
|
||||
if time.Unix(expiredAt, 0).After(time.Now()) {
|
||||
subtrees, err := computeSubTrees(congestionTree, inputs)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while computing subtrees")
|
||||
continue
|
||||
}
|
||||
|
||||
for _, subTree := range subtrees {
|
||||
// mitigate the risk to get BIP68 non-final errors by scheduling the task 30 seconds after the expiration time
|
||||
if err := s.schedule(int64(expiredAt), roundTxid, subTree); err != nil {
|
||||
log.WithError(err).Error("error while scheduling sweep task")
|
||||
continue
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// iterate over the expired shared outputs
|
||||
for _, input := range inputs {
|
||||
// sweepableVtxos related to the sweep input
|
||||
sweepableVtxos := make([]domain.VtxoKey, 0)
|
||||
|
||||
// check if input is the vtxo itself
|
||||
vtxos, _ := s.repoManager.Vtxos().GetVtxos(
|
||||
ctx,
|
||||
[]domain.VtxoKey{
|
||||
{
|
||||
Txid: input.InputArgs.Txid,
|
||||
VOut: input.InputArgs.TxIndex,
|
||||
},
|
||||
},
|
||||
)
|
||||
if len(vtxos) > 0 {
|
||||
if !vtxos[0].Swept && !vtxos[0].Redeemed {
|
||||
sweepableVtxos = append(sweepableVtxos, vtxos[0].VtxoKey)
|
||||
}
|
||||
} else {
|
||||
// if it's not a vtxo, find all the vtxos leaves reachable from that input
|
||||
vtxosLeaves, err := congestionTree.FindLeaves(input.InputArgs.Txid, input.InputArgs.TxIndex)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while finding vtxos leaves")
|
||||
continue
|
||||
}
|
||||
|
||||
for _, leaf := range vtxosLeaves {
|
||||
pset, err := psetv2.NewPsetFromBase64(leaf.Tx)
|
||||
if err != nil {
|
||||
log.Error(fmt.Errorf("error while decoding pset: %w", err))
|
||||
continue
|
||||
}
|
||||
|
||||
vtxo, err := extractVtxoOutpoint(pset)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
sweepableVtxos = append(sweepableVtxos, *vtxo)
|
||||
}
|
||||
|
||||
if len(sweepableVtxos) <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
firstVtxo, err := s.repoManager.Vtxos().GetVtxos(ctx, sweepableVtxos[:1])
|
||||
if err != nil {
|
||||
log.Error(fmt.Errorf("error while getting vtxo: %w", err))
|
||||
sweepInputs = append(sweepInputs, input) // add the input anyway in order to try to sweep it
|
||||
continue
|
||||
}
|
||||
|
||||
if firstVtxo[0].Swept || firstVtxo[0].Redeemed {
|
||||
// we assume that if the first vtxo is swept or redeemed, the shared output has been spent
|
||||
// skip, the output is already swept or spent by a unilateral redeem
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if len(sweepableVtxos) > 0 {
|
||||
vtxoKeys = append(vtxoKeys, sweepableVtxos...)
|
||||
sweepInputs = append(sweepInputs, input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(sweepInputs) > 0 {
|
||||
// build the sweep transaction with all the expired non-swept shared outputs
|
||||
sweepTx, err := s.builder.BuildSweepTx(s.wallet, sweepInputs)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while building sweep tx")
|
||||
return
|
||||
}
|
||||
|
||||
err = nil
|
||||
txid := ""
|
||||
// retry until the tx is broadcasted or the error is not BIP68 final
|
||||
for len(txid) == 0 && (err == nil || err == fmt.Errorf("non-BIP68-final")) {
|
||||
if err != nil {
|
||||
log.Debugln("sweep tx not BIP68 final, retrying in 5 seconds")
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
txid, err = s.wallet.BroadcastTransaction(ctx, sweepTx)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while broadcasting sweep tx")
|
||||
return
|
||||
}
|
||||
if len(txid) > 0 {
|
||||
log.Debugln("sweep tx broadcasted:", txid)
|
||||
vtxosRepository := s.repoManager.Vtxos()
|
||||
|
||||
// mark the vtxos as swept
|
||||
if err := vtxosRepository.SweepVtxos(ctx, vtxoKeys); err != nil {
|
||||
log.Error(fmt.Errorf("error while deleting vtxos: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("%d vtxos swept", len(vtxoKeys))
|
||||
|
||||
roundVtxos, err := vtxosRepository.GetVtxosForRound(ctx, roundTxid)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while getting vtxos for round")
|
||||
return
|
||||
}
|
||||
|
||||
allSwept := true
|
||||
for _, vtxo := range roundVtxos {
|
||||
allSwept = allSwept && vtxo.Swept
|
||||
if !allSwept {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if allSwept {
|
||||
// update the round
|
||||
roundRepo := s.repoManager.Rounds()
|
||||
round, err := roundRepo.GetRoundWithTxid(ctx, roundTxid)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while getting round")
|
||||
return
|
||||
}
|
||||
|
||||
round.Sweep()
|
||||
|
||||
if err := roundRepo.AddOrUpdateRound(ctx, *round); err != nil {
|
||||
log.WithError(err).Error("error while marking round as swept")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// onchainOutputs iterates over all the nodes' outputs in the congestion tree and checks their onchain state
|
||||
// returns the sweepable outputs as ports.SweepInput mapped by their expiration time
|
||||
func (s *sweeper) findSweepableOutputs(
|
||||
ctx context.Context,
|
||||
congestionTree tree.CongestionTree,
|
||||
) (map[int64][]ports.SweepInput, error) {
|
||||
sweepableOutputs := make(map[int64][]ports.SweepInput)
|
||||
blocktimeCache := make(map[string]int64) // txid -> blocktime
|
||||
nodesToCheck := congestionTree[0] // init with the root
|
||||
|
||||
for len(nodesToCheck) > 0 {
|
||||
newNodesToCheck := make([]tree.Node, 0)
|
||||
|
||||
for _, node := range nodesToCheck {
|
||||
isPublished, blocktime, err := s.wallet.IsTransactionPublished(ctx, node.Txid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var expirationTime int64
|
||||
var sweepInputs []ports.SweepInput
|
||||
|
||||
if !isPublished {
|
||||
if _, ok := blocktimeCache[node.ParentTxid]; !ok {
|
||||
isPublished, blocktime, err := s.wallet.IsTransactionPublished(ctx, node.ParentTxid)
|
||||
if !isPublished || err != nil {
|
||||
return nil, fmt.Errorf("tx %s not found", node.Txid)
|
||||
}
|
||||
|
||||
blocktimeCache[node.ParentTxid] = blocktime
|
||||
}
|
||||
|
||||
expirationTime, sweepInputs, err = s.nodeToSweepInputs(blocktimeCache[node.ParentTxid], node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// cache the blocktime for future use
|
||||
blocktimeCache[node.Txid] = int64(blocktime)
|
||||
|
||||
// if the tx is onchain, it means that the input is spent
|
||||
// add the children to the nodes in order to check them during the next iteration
|
||||
// We will return the error below, but are we going to schedule the tasks for the "children roots"?
|
||||
if !node.Leaf {
|
||||
children := congestionTree.Children(node.Txid)
|
||||
newNodesToCheck = append(newNodesToCheck, children...)
|
||||
continue
|
||||
}
|
||||
|
||||
// if the node is a leaf, the vtxos outputs should added as onchain outputs if they are not swept yet
|
||||
vtxoExpiration, sweepInput, err := s.leafToSweepInput(ctx, blocktime, node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sweepInput != nil {
|
||||
expirationTime = vtxoExpiration
|
||||
sweepInputs = []ports.SweepInput{*sweepInput}
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := sweepableOutputs[expirationTime]; !ok {
|
||||
sweepableOutputs[expirationTime] = make([]ports.SweepInput, 0)
|
||||
}
|
||||
sweepableOutputs[expirationTime] = append(sweepableOutputs[expirationTime], sweepInputs...)
|
||||
}
|
||||
|
||||
nodesToCheck = newNodesToCheck
|
||||
}
|
||||
|
||||
return sweepableOutputs, nil
|
||||
}
|
||||
|
||||
func (s *sweeper) leafToSweepInput(ctx context.Context, txBlocktime int64, node tree.Node) (int64, *ports.SweepInput, error) {
|
||||
pset, err := psetv2.NewPsetFromBase64(node.Tx)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
|
||||
vtxo, err := extractVtxoOutpoint(pset)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
|
||||
fromRepo, err := s.repoManager.Vtxos().GetVtxos(ctx, []domain.VtxoKey{*vtxo})
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
|
||||
if len(fromRepo) == 0 {
|
||||
return -1, nil, fmt.Errorf("vtxo not found")
|
||||
}
|
||||
|
||||
if fromRepo[0].Swept {
|
||||
return -1, nil, nil
|
||||
}
|
||||
|
||||
if fromRepo[0].Redeemed {
|
||||
return -1, nil, nil
|
||||
}
|
||||
|
||||
// if the vtxo is not swept or redeemed, add it to the onchain outputs
|
||||
pubKeyBytes, err := hex.DecodeString(fromRepo[0].Pubkey)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
|
||||
pubKey, err := secp256k1.ParsePubKey(pubKeyBytes)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
|
||||
sweepLeaf, lifetime, err := s.builder.GetLeafSweepClosure(node, pubKey)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
|
||||
sweepInput := ports.SweepInput{
|
||||
InputArgs: psetv2.InputArgs{
|
||||
Txid: vtxo.Txid,
|
||||
TxIndex: vtxo.VOut,
|
||||
},
|
||||
SweepLeaf: *sweepLeaf,
|
||||
Amount: fromRepo[0].Amount,
|
||||
}
|
||||
|
||||
return txBlocktime + lifetime, &sweepInput, nil
|
||||
}
|
||||
|
||||
func (s *sweeper) nodeToSweepInputs(parentBlocktime int64, node tree.Node) (int64, []ports.SweepInput, error) {
|
||||
pset, err := psetv2.NewPsetFromBase64(node.Tx)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
|
||||
if len(pset.Inputs) != 1 {
|
||||
return -1, nil, fmt.Errorf("invalid node pset, expect 1 input, got %d", len(pset.Inputs))
|
||||
}
|
||||
|
||||
// if the tx is not onchain, it means that the input is an existing shared output
|
||||
input := pset.Inputs[0]
|
||||
txid := chainhash.Hash(input.PreviousTxid).String()
|
||||
index := input.PreviousTxIndex
|
||||
|
||||
sweepLeaf, lifetime, err := extractSweepLeaf(input)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
}
|
||||
|
||||
expirationTime := parentBlocktime + lifetime
|
||||
|
||||
amount := uint64(0)
|
||||
for _, out := range pset.Outputs {
|
||||
amount += out.Value
|
||||
}
|
||||
|
||||
sweepInputs := []ports.SweepInput{
|
||||
{
|
||||
InputArgs: psetv2.InputArgs{
|
||||
Txid: txid,
|
||||
TxIndex: index,
|
||||
},
|
||||
SweepLeaf: *sweepLeaf,
|
||||
Amount: amount,
|
||||
},
|
||||
}
|
||||
|
||||
return expirationTime, sweepInputs, nil
|
||||
}
|
||||
|
||||
func computeSubTrees(congestionTree tree.CongestionTree, inputs []ports.SweepInput) ([]tree.CongestionTree, error) {
|
||||
subTrees := make(map[string]tree.CongestionTree, 0)
|
||||
|
||||
// for each sweepable input, create a sub congestion tree
|
||||
// it allows to skip the part of the tree that has been broadcasted in the next task
|
||||
for _, input := range inputs {
|
||||
subTree, err := computeSubTree(congestionTree, input.InputArgs.Txid)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while finding sub tree")
|
||||
continue
|
||||
}
|
||||
|
||||
root, err := subTree.Root()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while getting root node")
|
||||
continue
|
||||
}
|
||||
|
||||
subTrees[root.Txid] = subTree
|
||||
}
|
||||
|
||||
// filter out the sub trees, remove the ones that are included in others
|
||||
filteredSubTrees := make([]tree.CongestionTree, 0)
|
||||
for i, subTree := range subTrees {
|
||||
notIncludedInOtherTrees := true
|
||||
|
||||
for j, otherSubTree := range subTrees {
|
||||
if i == j {
|
||||
continue
|
||||
}
|
||||
contains, err := containsTree(otherSubTree, subTree)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while checking if a tree contains another")
|
||||
continue
|
||||
}
|
||||
|
||||
if contains {
|
||||
notIncludedInOtherTrees = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if notIncludedInOtherTrees {
|
||||
filteredSubTrees = append(filteredSubTrees, subTree)
|
||||
}
|
||||
}
|
||||
|
||||
return filteredSubTrees, nil
|
||||
}
|
||||
|
||||
func computeSubTree(congestionTree tree.CongestionTree, newRoot string) (tree.CongestionTree, error) {
|
||||
for _, level := range congestionTree {
|
||||
for _, node := range level {
|
||||
if node.Txid == newRoot || node.ParentTxid == newRoot {
|
||||
newTree := make(tree.CongestionTree, 0)
|
||||
newTree = append(newTree, []tree.Node{node})
|
||||
|
||||
children := congestionTree.Children(node.Txid)
|
||||
for len(children) > 0 {
|
||||
newTree = append(newTree, children)
|
||||
newChildren := make([]tree.Node, 0)
|
||||
for _, child := range children {
|
||||
newChildren = append(newChildren, congestionTree.Children(child.Txid)...)
|
||||
}
|
||||
children = newChildren
|
||||
}
|
||||
|
||||
return newTree, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("failed to create subtree, new root not found")
|
||||
}
|
||||
|
||||
func containsTree(tr0 tree.CongestionTree, tr1 tree.CongestionTree) (bool, error) {
|
||||
tr1Root, err := tr1.Root()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, level := range tr0 {
|
||||
for _, node := range level {
|
||||
if node.Txid == tr1Root.Txid {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// given a congestion tree input, searches and returns the sweep leaf and its lifetime in seconds
|
||||
func extractSweepLeaf(input psetv2.Input) (sweepLeaf *psetv2.TapLeafScript, lifetime int64, err error) {
|
||||
for _, leaf := range input.TapLeafScript {
|
||||
isSweep, _, seconds, err := tree.DecodeSweepScript(leaf.Script)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if isSweep {
|
||||
lifetime = int64(seconds)
|
||||
sweepLeaf = &leaf
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if sweepLeaf == nil {
|
||||
return nil, 0, fmt.Errorf("sweep leaf not found")
|
||||
}
|
||||
|
||||
return sweepLeaf, lifetime, nil
|
||||
}
|
||||
|
||||
// assuming the pset is a leaf in the congestion tree, returns the vtxos outputs
|
||||
func extractVtxoOutpoint(pset *psetv2.Pset) (*domain.VtxoKey, error) {
|
||||
if len(pset.Outputs) != 2 {
|
||||
return nil, fmt.Errorf("invalid leaf pset, expect 2 outputs, got %d", len(pset.Outputs))
|
||||
}
|
||||
|
||||
utx, err := pset.UnsignedTx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &domain.VtxoKey{
|
||||
Txid: utx.TxHash().String(),
|
||||
VOut: 0,
|
||||
}, nil
|
||||
}
|
||||
254
server/internal/core/application/utils.go
Normal file
254
server/internal/core/application/utils.go
Normal file
@@ -0,0 +1,254 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ark-network/ark/common"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
)
|
||||
|
||||
type timedPayment struct {
|
||||
domain.Payment
|
||||
timestamp time.Time
|
||||
pingTimestamp time.Time
|
||||
}
|
||||
|
||||
type paymentsMap struct {
|
||||
lock *sync.RWMutex
|
||||
payments map[string]*timedPayment
|
||||
}
|
||||
|
||||
func newPaymentsMap(payments []domain.Payment) *paymentsMap {
|
||||
paymentsById := make(map[string]*timedPayment)
|
||||
for _, p := range payments {
|
||||
paymentsById[p.Id] = &timedPayment{p, time.Now(), time.Time{}}
|
||||
}
|
||||
lock := &sync.RWMutex{}
|
||||
return &paymentsMap{lock, paymentsById}
|
||||
}
|
||||
|
||||
func (m *paymentsMap) len() int64 {
|
||||
m.lock.RLock()
|
||||
defer m.lock.RUnlock()
|
||||
|
||||
count := int64(0)
|
||||
for _, p := range m.payments {
|
||||
if len(p.Receivers) > 0 {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (m *paymentsMap) push(payment domain.Payment) error {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
if _, ok := m.payments[payment.Id]; ok {
|
||||
return fmt.Errorf("duplicated inputs")
|
||||
}
|
||||
|
||||
m.payments[payment.Id] = &timedPayment{payment, time.Now(), time.Time{}}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *paymentsMap) pop(num int64) []domain.Payment {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
paymentsByTime := make([]timedPayment, 0, len(m.payments))
|
||||
for _, p := range m.payments {
|
||||
// Skip payments without registered receivers.
|
||||
if len(p.Receivers) <= 0 {
|
||||
continue
|
||||
}
|
||||
// Skip payments for which users didn't notify to be online in the last minute.
|
||||
if p.pingTimestamp.IsZero() || time.Since(p.pingTimestamp).Minutes() > 1 {
|
||||
continue
|
||||
}
|
||||
paymentsByTime = append(paymentsByTime, *p)
|
||||
}
|
||||
sort.SliceStable(paymentsByTime, func(i, j int) bool {
|
||||
return paymentsByTime[i].timestamp.Before(paymentsByTime[j].timestamp)
|
||||
})
|
||||
|
||||
if num < 0 || num > int64(len(paymentsByTime)) {
|
||||
num = int64(len(paymentsByTime))
|
||||
}
|
||||
|
||||
payments := make([]domain.Payment, 0, num)
|
||||
for _, p := range paymentsByTime[:num] {
|
||||
payments = append(payments, p.Payment)
|
||||
delete(m.payments, p.Id)
|
||||
}
|
||||
return payments
|
||||
}
|
||||
|
||||
func (m *paymentsMap) update(payment domain.Payment) error {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
p, ok := m.payments[payment.Id]
|
||||
if !ok {
|
||||
return fmt.Errorf("payment %s not found", payment.Id)
|
||||
}
|
||||
|
||||
p.Payment = payment
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *paymentsMap) updatePingTimestamp(id string) error {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
payment, ok := m.payments[id]
|
||||
if !ok {
|
||||
return fmt.Errorf("payment %s not found", id)
|
||||
}
|
||||
|
||||
payment.pingTimestamp = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *paymentsMap) view(id string) (*domain.Payment, bool) {
|
||||
m.lock.RLock()
|
||||
defer m.lock.RUnlock()
|
||||
|
||||
payment, ok := m.payments[id]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return &domain.Payment{
|
||||
Id: payment.Id,
|
||||
Inputs: payment.Inputs,
|
||||
Receivers: payment.Receivers,
|
||||
}, true
|
||||
}
|
||||
|
||||
type signedTx struct {
|
||||
tx string
|
||||
signed bool
|
||||
}
|
||||
|
||||
type forfeitTxsMap struct {
|
||||
lock *sync.RWMutex
|
||||
forfeitTxs map[string]*signedTx
|
||||
genesisBlockHash *chainhash.Hash
|
||||
}
|
||||
|
||||
func newForfeitTxsMap(genesisBlockHash *chainhash.Hash) *forfeitTxsMap {
|
||||
return &forfeitTxsMap{&sync.RWMutex{}, make(map[string]*signedTx), genesisBlockHash}
|
||||
}
|
||||
|
||||
func (m *forfeitTxsMap) push(txs []string) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
faucetTxID, _ := hex.DecodeString(faucetVtxo.Txid)
|
||||
|
||||
for _, tx := range txs {
|
||||
ptx, _ := psetv2.NewPsetFromBase64(tx)
|
||||
utx, _ := ptx.UnsignedTx()
|
||||
|
||||
signed := false
|
||||
|
||||
// find the faucet vtxos, and mark them as signed
|
||||
for _, input := range ptx.Inputs {
|
||||
if bytes.Equal(input.PreviousTxid, faucetTxID) {
|
||||
signed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
m.forfeitTxs[utx.TxHash().String()] = &signedTx{tx, signed}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *forfeitTxsMap) sign(txs []string) error {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
for _, tx := range txs {
|
||||
ptx, _ := psetv2.NewPsetFromBase64(tx)
|
||||
utx, _ := ptx.UnsignedTx()
|
||||
txid := utx.TxHash().String()
|
||||
|
||||
if _, ok := m.forfeitTxs[txid]; ok {
|
||||
for index, input := range ptx.Inputs {
|
||||
if len(input.TapScriptSig) > 0 {
|
||||
for _, tapScriptSig := range input.TapScriptSig {
|
||||
leafHash, err := chainhash.NewHash(tapScriptSig.LeafHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preimage, err := common.TaprootPreimage(
|
||||
m.genesisBlockHash,
|
||||
ptx,
|
||||
index,
|
||||
leafHash,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sig, err := schnorr.ParseSignature(tapScriptSig.Signature)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pubkey, err := schnorr.ParsePubKey(tapScriptSig.PubKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sig.Verify(preimage, pubkey) {
|
||||
m.forfeitTxs[txid].signed = true
|
||||
} else {
|
||||
return fmt.Errorf("invalid signature")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *forfeitTxsMap) pop() (signed, unsigned []string) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
for _, t := range m.forfeitTxs {
|
||||
if t.signed {
|
||||
signed = append(signed, t.tx)
|
||||
} else {
|
||||
unsigned = append(unsigned, t.tx)
|
||||
}
|
||||
}
|
||||
|
||||
m.forfeitTxs = make(map[string]*signedTx)
|
||||
return signed, unsigned
|
||||
}
|
||||
|
||||
func (m *forfeitTxsMap) view() []string {
|
||||
m.lock.RLock()
|
||||
defer m.lock.RUnlock()
|
||||
|
||||
txs := make([]string, 0, len(m.forfeitTxs))
|
||||
for _, tx := range m.forfeitTxs {
|
||||
txs = append(txs, tx.tx)
|
||||
}
|
||||
return txs
|
||||
}
|
||||
44
server/internal/core/domain/events.go
Normal file
44
server/internal/core/domain/events.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package domain
|
||||
|
||||
import "github.com/ark-network/ark/common/tree"
|
||||
|
||||
type RoundEvent interface {
|
||||
isEvent()
|
||||
}
|
||||
|
||||
func (r RoundStarted) isEvent() {}
|
||||
func (r RoundFinalizationStarted) isEvent() {}
|
||||
func (r RoundFinalized) isEvent() {}
|
||||
func (r RoundFailed) isEvent() {}
|
||||
func (r PaymentsRegistered) isEvent() {}
|
||||
|
||||
type RoundStarted struct {
|
||||
Id string
|
||||
Timestamp int64
|
||||
}
|
||||
|
||||
type RoundFinalizationStarted struct {
|
||||
Id string
|
||||
CongestionTree tree.CongestionTree
|
||||
Connectors []string
|
||||
UnsignedForfeitTxs []string
|
||||
PoolTx string
|
||||
}
|
||||
|
||||
type RoundFinalized struct {
|
||||
Id string
|
||||
Txid string
|
||||
ForfeitTxs []string
|
||||
Timestamp int64
|
||||
}
|
||||
|
||||
type RoundFailed struct {
|
||||
Id string
|
||||
Err string
|
||||
Timestamp int64
|
||||
}
|
||||
|
||||
type PaymentsRegistered struct {
|
||||
Id string
|
||||
Payments []Payment
|
||||
}
|
||||
129
server/internal/core/domain/payment.go
Normal file
129
server/internal/core/domain/payment.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const dustAmount = 450
|
||||
|
||||
type Payment struct {
|
||||
Id string
|
||||
Inputs []Vtxo
|
||||
Receivers []Receiver
|
||||
}
|
||||
|
||||
func NewPayment(inputs []Vtxo) (*Payment, error) {
|
||||
p := &Payment{
|
||||
Id: uuid.New().String(),
|
||||
Inputs: inputs,
|
||||
}
|
||||
if err := p.validate(true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p *Payment) AddReceivers(receivers []Receiver) (err error) {
|
||||
if p.Receivers == nil {
|
||||
p.Receivers = make([]Receiver, 0)
|
||||
}
|
||||
p.Receivers = append(p.Receivers, receivers...)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
p.Receivers = p.Receivers[:len(p.Receivers)-len(receivers)]
|
||||
}
|
||||
}()
|
||||
err = p.validate(false)
|
||||
return
|
||||
}
|
||||
|
||||
func (p Payment) TotalInputAmount() uint64 {
|
||||
tot := uint64(0)
|
||||
for _, in := range p.Inputs {
|
||||
tot += in.Amount
|
||||
}
|
||||
return tot
|
||||
}
|
||||
|
||||
func (p Payment) TotalOutputAmount() uint64 {
|
||||
tot := uint64(0)
|
||||
for _, r := range p.Receivers {
|
||||
tot += r.Amount
|
||||
}
|
||||
return tot
|
||||
}
|
||||
|
||||
func (p Payment) validate(ignoreOuts bool) error {
|
||||
if len(p.Id) <= 0 {
|
||||
return fmt.Errorf("missing id")
|
||||
}
|
||||
if len(p.Inputs) <= 0 {
|
||||
return fmt.Errorf("missing inputs")
|
||||
}
|
||||
if ignoreOuts {
|
||||
return nil
|
||||
}
|
||||
if len(p.Receivers) <= 0 {
|
||||
return fmt.Errorf("missing outputs")
|
||||
}
|
||||
// Check that input and output and output amounts match.
|
||||
inAmount := p.TotalInputAmount()
|
||||
outAmount := uint64(0)
|
||||
for _, r := range p.Receivers {
|
||||
if len(r.OnchainAddress) <= 0 && len(r.Pubkey) <= 0 {
|
||||
return fmt.Errorf("missing receiver destination")
|
||||
}
|
||||
if r.Amount < dustAmount {
|
||||
return fmt.Errorf("receiver amount must be greater than dust")
|
||||
}
|
||||
outAmount += r.Amount
|
||||
}
|
||||
if inAmount != outAmount {
|
||||
return fmt.Errorf("input and output amounts mismatch")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type VtxoKey struct {
|
||||
Txid string
|
||||
VOut uint32
|
||||
}
|
||||
|
||||
func (k VtxoKey) Hash() string {
|
||||
calcHash := func(buf []byte, hasher hash.Hash) []byte {
|
||||
_, _ = hasher.Write(buf)
|
||||
return hasher.Sum(nil)
|
||||
}
|
||||
|
||||
hash160 := func(buf []byte) []byte {
|
||||
return calcHash(calcHash(buf, sha256.New()), sha256.New())
|
||||
}
|
||||
|
||||
buf, _ := hex.DecodeString(k.Txid)
|
||||
buf = append(buf, byte(k.VOut))
|
||||
return hex.EncodeToString(hash160(buf))
|
||||
}
|
||||
|
||||
type Receiver struct {
|
||||
Pubkey string
|
||||
Amount uint64
|
||||
OnchainAddress string
|
||||
}
|
||||
|
||||
func (r Receiver) IsOnchain() bool {
|
||||
return len(r.OnchainAddress) > 0
|
||||
}
|
||||
|
||||
type Vtxo struct {
|
||||
VtxoKey
|
||||
Receiver
|
||||
PoolTx string
|
||||
Spent bool
|
||||
Redeemed bool
|
||||
Swept bool
|
||||
}
|
||||
111
server/internal/core/domain/payment_test.go
Normal file
111
server/internal/core/domain/payment_test.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package domain_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var inputs = []domain.Vtxo{
|
||||
{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
VOut: 0,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Pubkey: "030000000000000000000000000000000000000000000000000000000000000001",
|
||||
Amount: 1000,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestPayment(t *testing.T) {
|
||||
t.Run("new_payment", func(t *testing.T) {
|
||||
t.Run("vaild", func(t *testing.T) {
|
||||
payment, err := domain.NewPayment(inputs)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, payment)
|
||||
require.NotEmpty(t, payment.Id)
|
||||
require.Exactly(t, inputs, payment.Inputs)
|
||||
require.Empty(t, payment.Receivers)
|
||||
})
|
||||
|
||||
t.Run("invaild", func(t *testing.T) {
|
||||
fixtures := []struct {
|
||||
inputs []domain.Vtxo
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
inputs: nil,
|
||||
expectedErr: "missing inputs",
|
||||
},
|
||||
}
|
||||
|
||||
for _, f := range fixtures {
|
||||
payment, err := domain.NewPayment(f.inputs)
|
||||
require.EqualError(t, err, f.expectedErr)
|
||||
require.Nil(t, payment)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("add_receivers", func(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
payment, err := domain.NewPayment(inputs)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, payment)
|
||||
|
||||
err = payment.AddReceivers([]domain.Receiver{
|
||||
{
|
||||
Pubkey: "030000000000000000000000000000000000000000000000000000000000000001",
|
||||
Amount: 450,
|
||||
},
|
||||
{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 550,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
fixtures := []struct {
|
||||
receivers []domain.Receiver
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
receivers: nil,
|
||||
expectedErr: "missing outputs",
|
||||
},
|
||||
{
|
||||
receivers: []domain.Receiver{
|
||||
{
|
||||
Pubkey: "030000000000000000000000000000000000000000000000000000000000000001",
|
||||
Amount: 400,
|
||||
},
|
||||
},
|
||||
expectedErr: "receiver amount must be greater than dust",
|
||||
},
|
||||
{
|
||||
receivers: []domain.Receiver{
|
||||
{
|
||||
Pubkey: "030000000000000000000000000000000000000000000000000000000000000001",
|
||||
Amount: 600,
|
||||
},
|
||||
},
|
||||
expectedErr: "input and output amounts mismatch",
|
||||
},
|
||||
}
|
||||
|
||||
payment, err := domain.NewPayment(inputs)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, payment)
|
||||
|
||||
for _, f := range fixtures {
|
||||
err := payment.AddReceivers(f.receivers)
|
||||
require.EqualError(t, err, f.expectedErr)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
253
server/internal/core/domain/round.go
Normal file
253
server/internal/core/domain/round.go
Normal file
@@ -0,0 +1,253 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
UndefinedStage RoundStage = iota
|
||||
RegistrationStage
|
||||
FinalizationStage
|
||||
)
|
||||
|
||||
type RoundStage int
|
||||
|
||||
func (s RoundStage) String() string {
|
||||
switch s {
|
||||
case RegistrationStage:
|
||||
return "REGISTRATION_STAGE"
|
||||
case FinalizationStage:
|
||||
return "FINALIZATION_STAGE"
|
||||
default:
|
||||
return "UNDEFINED_STAGE"
|
||||
}
|
||||
}
|
||||
|
||||
type Stage struct {
|
||||
Code RoundStage
|
||||
Ended bool
|
||||
Failed bool
|
||||
}
|
||||
|
||||
type Round struct {
|
||||
Id string
|
||||
StartingTimestamp int64
|
||||
EndingTimestamp int64
|
||||
Stage Stage
|
||||
Payments map[string]Payment
|
||||
Txid string
|
||||
UnsignedTx string
|
||||
ForfeitTxs []string
|
||||
CongestionTree tree.CongestionTree
|
||||
Connectors []string
|
||||
DustAmount uint64
|
||||
Version uint
|
||||
Swept bool // true if all the vtxos are vtxo.Swept
|
||||
changes []RoundEvent
|
||||
}
|
||||
|
||||
func NewRound(dustAmount uint64) *Round {
|
||||
return &Round{
|
||||
Id: uuid.New().String(),
|
||||
DustAmount: dustAmount,
|
||||
Payments: make(map[string]Payment),
|
||||
changes: make([]RoundEvent, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func NewRoundFromEvents(events []RoundEvent) *Round {
|
||||
r := &Round{}
|
||||
|
||||
for _, event := range events {
|
||||
r.On(event, true)
|
||||
}
|
||||
|
||||
r.changes = append([]RoundEvent{}, events...)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Round) Events() []RoundEvent {
|
||||
return r.changes
|
||||
}
|
||||
|
||||
func (r *Round) On(event RoundEvent, replayed bool) {
|
||||
switch e := event.(type) {
|
||||
case RoundStarted:
|
||||
r.Stage.Code = RegistrationStage
|
||||
r.Id = e.Id
|
||||
r.StartingTimestamp = e.Timestamp
|
||||
case RoundFinalizationStarted:
|
||||
r.Stage.Code = FinalizationStage
|
||||
r.CongestionTree = e.CongestionTree
|
||||
r.Connectors = append([]string{}, e.Connectors...)
|
||||
r.UnsignedTx = e.PoolTx
|
||||
case RoundFinalized:
|
||||
r.Stage.Ended = true
|
||||
r.Txid = e.Txid
|
||||
r.ForfeitTxs = append([]string{}, e.ForfeitTxs...)
|
||||
r.EndingTimestamp = e.Timestamp
|
||||
case RoundFailed:
|
||||
r.Stage.Failed = true
|
||||
r.EndingTimestamp = e.Timestamp
|
||||
case PaymentsRegistered:
|
||||
if r.Payments == nil {
|
||||
r.Payments = make(map[string]Payment)
|
||||
}
|
||||
for _, p := range e.Payments {
|
||||
r.Payments[p.Id] = p
|
||||
}
|
||||
}
|
||||
|
||||
if replayed {
|
||||
r.Version++
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Round) StartRegistration() ([]RoundEvent, error) {
|
||||
empty := Stage{}
|
||||
if r.Stage != empty {
|
||||
return nil, fmt.Errorf("not in a valid stage to start payment registration")
|
||||
}
|
||||
|
||||
event := RoundStarted{
|
||||
Id: r.Id,
|
||||
Timestamp: time.Now().Unix(),
|
||||
}
|
||||
r.raise(event)
|
||||
|
||||
return []RoundEvent{event}, nil
|
||||
}
|
||||
|
||||
func (r *Round) RegisterPayments(payments []Payment) ([]RoundEvent, error) {
|
||||
if r.Stage.Code != RegistrationStage || r.IsFailed() {
|
||||
return nil, fmt.Errorf("not in a valid stage to register payments")
|
||||
}
|
||||
if len(payments) <= 0 {
|
||||
return nil, fmt.Errorf("missing payments to register")
|
||||
}
|
||||
for _, p := range payments {
|
||||
if err := p.validate(false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
event := PaymentsRegistered{
|
||||
Id: r.Id,
|
||||
Payments: payments,
|
||||
}
|
||||
r.raise(event)
|
||||
|
||||
return []RoundEvent{event}, nil
|
||||
}
|
||||
|
||||
func (r *Round) StartFinalization(connectors []string, congestionTree tree.CongestionTree, poolTx string) ([]RoundEvent, error) {
|
||||
if len(connectors) <= 0 {
|
||||
return nil, fmt.Errorf("missing list of connectors")
|
||||
}
|
||||
if len(congestionTree) <= 0 {
|
||||
return nil, fmt.Errorf("missing congestion tree")
|
||||
}
|
||||
if len(poolTx) <= 0 {
|
||||
return nil, fmt.Errorf("missing unsigned pool tx")
|
||||
}
|
||||
if r.Stage.Code != RegistrationStage || r.IsFailed() {
|
||||
return nil, fmt.Errorf("not in a valid stage to start payment finalization")
|
||||
}
|
||||
if len(r.Payments) <= 0 {
|
||||
return nil, fmt.Errorf("no payments registered")
|
||||
}
|
||||
|
||||
event := RoundFinalizationStarted{
|
||||
Id: r.Id,
|
||||
CongestionTree: congestionTree,
|
||||
Connectors: connectors,
|
||||
PoolTx: poolTx,
|
||||
}
|
||||
r.raise(event)
|
||||
|
||||
return []RoundEvent{event}, nil
|
||||
}
|
||||
|
||||
func (r *Round) EndFinalization(forfeitTxs []string, txid string) ([]RoundEvent, error) {
|
||||
if len(forfeitTxs) <= 0 {
|
||||
return nil, fmt.Errorf("missing list of signed forfeit txs")
|
||||
}
|
||||
if len(txid) <= 0 {
|
||||
return nil, fmt.Errorf("missing pool txid")
|
||||
}
|
||||
if r.Stage.Code != FinalizationStage || r.IsFailed() {
|
||||
return nil, fmt.Errorf("not in a valid stage to end payment finalization")
|
||||
}
|
||||
if r.Stage.Ended {
|
||||
return nil, fmt.Errorf("round already finalized")
|
||||
}
|
||||
event := RoundFinalized{
|
||||
Id: r.Id,
|
||||
Txid: txid,
|
||||
ForfeitTxs: forfeitTxs,
|
||||
Timestamp: time.Now().Unix(),
|
||||
}
|
||||
r.raise(event)
|
||||
|
||||
return []RoundEvent{event}, nil
|
||||
}
|
||||
|
||||
func (r *Round) Fail(err error) []RoundEvent {
|
||||
if r.Stage.Failed {
|
||||
return nil
|
||||
}
|
||||
event := RoundFailed{
|
||||
Id: r.Id,
|
||||
Err: err.Error(),
|
||||
Timestamp: time.Now().Unix(),
|
||||
}
|
||||
r.raise(event)
|
||||
|
||||
return []RoundEvent{event}
|
||||
}
|
||||
|
||||
func (r *Round) IsStarted() bool {
|
||||
empty := Stage{}
|
||||
return !r.IsFailed() && !r.IsEnded() && r.Stage != empty
|
||||
}
|
||||
|
||||
func (r *Round) IsEnded() bool {
|
||||
return !r.IsFailed() && r.Stage.Code == FinalizationStage && r.Stage.Ended
|
||||
}
|
||||
|
||||
func (r *Round) IsFailed() bool {
|
||||
return r.Stage.Failed
|
||||
}
|
||||
|
||||
func (r *Round) TotalInputAmount() uint64 {
|
||||
totInputs := 0
|
||||
for _, p := range r.Payments {
|
||||
totInputs += len(p.Inputs)
|
||||
}
|
||||
return uint64(totInputs * int(r.DustAmount))
|
||||
}
|
||||
|
||||
func (r *Round) TotalOutputAmount() uint64 {
|
||||
tot := uint64(0)
|
||||
for _, p := range r.Payments {
|
||||
tot += p.TotalOutputAmount()
|
||||
}
|
||||
return tot
|
||||
}
|
||||
|
||||
func (r *Round) Sweep() {
|
||||
r.Swept = true
|
||||
}
|
||||
|
||||
func (r *Round) raise(event RoundEvent) {
|
||||
if r.changes == nil {
|
||||
r.changes = make([]RoundEvent, 0)
|
||||
}
|
||||
r.changes = append(r.changes, event)
|
||||
r.On(event, false)
|
||||
}
|
||||
28
server/internal/core/domain/round_repo.go
Normal file
28
server/internal/core/domain/round_repo.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type RoundEventRepository interface {
|
||||
Save(ctx context.Context, id string, events ...RoundEvent) error
|
||||
Load(ctx context.Context, id string) (*Round, error)
|
||||
}
|
||||
|
||||
type RoundRepository interface {
|
||||
AddOrUpdateRound(ctx context.Context, round Round) error
|
||||
GetCurrentRound(ctx context.Context) (*Round, error)
|
||||
GetRoundWithId(ctx context.Context, id string) (*Round, error)
|
||||
GetRoundWithTxid(ctx context.Context, txid string) (*Round, error)
|
||||
GetSweepableRounds(ctx context.Context) ([]Round, error)
|
||||
}
|
||||
|
||||
type VtxoRepository interface {
|
||||
AddVtxos(ctx context.Context, vtxos []Vtxo) error
|
||||
SpendVtxos(ctx context.Context, vtxos []VtxoKey) error
|
||||
RedeemVtxos(ctx context.Context, vtxos []VtxoKey) ([]Vtxo, error)
|
||||
GetVtxos(ctx context.Context, vtxos []VtxoKey) ([]Vtxo, error)
|
||||
GetVtxosForRound(ctx context.Context, txid string) ([]Vtxo, error)
|
||||
SweepVtxos(ctx context.Context, vtxos []VtxoKey) error
|
||||
GetSpendableVtxos(ctx context.Context, pubkey string) ([]Vtxo, error)
|
||||
}
|
||||
576
server/internal/core/domain/round_test.go
Normal file
576
server/internal/core/domain/round_test.go
Normal file
@@ -0,0 +1,576 @@
|
||||
package domain_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
dustAmount = uint64(450)
|
||||
payments = []domain.Payment{
|
||||
{
|
||||
Id: "0",
|
||||
Inputs: []domain.Vtxo{{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: txid,
|
||||
VOut: 0,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Pubkey: pubkey,
|
||||
Amount: 2000,
|
||||
},
|
||||
}},
|
||||
Receivers: []domain.Receiver{
|
||||
{
|
||||
Pubkey: pubkey,
|
||||
Amount: 700,
|
||||
},
|
||||
{
|
||||
Pubkey: pubkey,
|
||||
Amount: 700,
|
||||
},
|
||||
{
|
||||
Pubkey: pubkey,
|
||||
Amount: 600,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "1",
|
||||
Inputs: []domain.Vtxo{
|
||||
{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: txid,
|
||||
VOut: 0,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Pubkey: pubkey,
|
||||
Amount: 1000,
|
||||
},
|
||||
},
|
||||
{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: txid,
|
||||
VOut: 0,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Pubkey: pubkey,
|
||||
Amount: 1000,
|
||||
},
|
||||
},
|
||||
},
|
||||
Receivers: []domain.Receiver{{
|
||||
Pubkey: pubkey,
|
||||
Amount: 2000,
|
||||
}},
|
||||
},
|
||||
}
|
||||
emptyPtx = "cHNldP8BAgQCAAAAAQQBAAEFAQABBgEDAfsEAgAAAAA="
|
||||
emptyTx = "0200000000000000000000"
|
||||
txid = "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
pubkey = "030000000000000000000000000000000000000000000000000000000000000001"
|
||||
congestionTree = tree.CongestionTree{
|
||||
{
|
||||
{
|
||||
Txid: txid,
|
||||
Tx: emptyPtx,
|
||||
ParentTxid: txid,
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
Txid: txid,
|
||||
Tx: emptyPtx,
|
||||
ParentTxid: txid,
|
||||
},
|
||||
{
|
||||
Txid: txid,
|
||||
Tx: emptyPtx,
|
||||
ParentTxid: txid,
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
Txid: txid,
|
||||
Tx: emptyPtx,
|
||||
ParentTxid: txid,
|
||||
},
|
||||
{
|
||||
Txid: txid,
|
||||
Tx: emptyPtx,
|
||||
ParentTxid: txid,
|
||||
},
|
||||
{
|
||||
Txid: txid,
|
||||
Tx: emptyPtx,
|
||||
ParentTxid: txid,
|
||||
},
|
||||
{
|
||||
Txid: txid,
|
||||
Tx: emptyPtx,
|
||||
ParentTxid: txid,
|
||||
},
|
||||
},
|
||||
}
|
||||
connectors = []string{emptyPtx, emptyPtx, emptyPtx}
|
||||
forfeitTxs = []string{emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx}
|
||||
poolTx = emptyTx
|
||||
)
|
||||
|
||||
func TestRound(t *testing.T) {
|
||||
testStartRegistration(t)
|
||||
|
||||
testRegisterPayments(t)
|
||||
|
||||
testStartFinalization(t)
|
||||
|
||||
testEndFinalization(t)
|
||||
|
||||
testFail(t)
|
||||
}
|
||||
|
||||
func testStartRegistration(t *testing.T) {
|
||||
t.Run("start_registration", func(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
round := domain.NewRound(dustAmount)
|
||||
require.NotNil(t, round)
|
||||
require.NotEmpty(t, round.Id)
|
||||
require.Empty(t, round.Events())
|
||||
require.False(t, round.IsStarted())
|
||||
require.False(t, round.IsEnded())
|
||||
require.False(t, round.IsFailed())
|
||||
|
||||
events, err := round.StartRegistration()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, events, 1)
|
||||
require.True(t, round.IsStarted())
|
||||
require.False(t, round.IsEnded())
|
||||
require.False(t, round.IsFailed())
|
||||
|
||||
event, ok := events[0].(domain.RoundStarted)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, round.Id, event.Id)
|
||||
require.Equal(t, round.StartingTimestamp, event.Timestamp)
|
||||
})
|
||||
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
fixtures := []struct {
|
||||
round *domain.Round
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "id",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.UndefinedStage,
|
||||
Failed: true,
|
||||
},
|
||||
},
|
||||
expectedErr: "not in a valid stage to start payment registration",
|
||||
},
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "id",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.RegistrationStage,
|
||||
},
|
||||
},
|
||||
expectedErr: "not in a valid stage to start payment registration",
|
||||
},
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "id",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.FinalizationStage,
|
||||
},
|
||||
},
|
||||
expectedErr: "not in a valid stage to start payment registration",
|
||||
},
|
||||
}
|
||||
|
||||
for _, f := range fixtures {
|
||||
events, err := f.round.StartRegistration()
|
||||
require.EqualError(t, err, f.expectedErr)
|
||||
require.Empty(t, events)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func testRegisterPayments(t *testing.T) {
|
||||
t.Run("register_payments", func(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
round := domain.NewRound(dustAmount)
|
||||
events, err := round.StartRegistration()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, events)
|
||||
|
||||
events, err = round.RegisterPayments(payments)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, events, 1)
|
||||
require.Condition(t, func() bool {
|
||||
for _, payment := range payments {
|
||||
_, ok := round.Payments[payment.Id]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
event, ok := events[0].(domain.PaymentsRegistered)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, round.Id, event.Id)
|
||||
require.Equal(t, payments, event.Payments)
|
||||
})
|
||||
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
fixtures := []struct {
|
||||
round *domain.Round
|
||||
payments []domain.Payment
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "id",
|
||||
Stage: domain.Stage{},
|
||||
},
|
||||
payments: payments,
|
||||
expectedErr: "not in a valid stage to register payments",
|
||||
},
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "id",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.RegistrationStage,
|
||||
Failed: true,
|
||||
},
|
||||
},
|
||||
payments: payments,
|
||||
expectedErr: "not in a valid stage to register payments",
|
||||
},
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "id",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.FinalizationStage,
|
||||
},
|
||||
},
|
||||
payments: payments,
|
||||
expectedErr: "not in a valid stage to register payments",
|
||||
},
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "id",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.RegistrationStage,
|
||||
},
|
||||
},
|
||||
payments: nil,
|
||||
expectedErr: "missing payments to register",
|
||||
},
|
||||
}
|
||||
|
||||
for _, f := range fixtures {
|
||||
events, err := f.round.RegisterPayments(f.payments)
|
||||
require.EqualError(t, err, f.expectedErr)
|
||||
require.Empty(t, events)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func testStartFinalization(t *testing.T) {
|
||||
t.Run("start_finalization", func(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
round := domain.NewRound(dustAmount)
|
||||
events, err := round.StartRegistration()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, events)
|
||||
|
||||
events, err = round.RegisterPayments(payments)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, events)
|
||||
|
||||
events, err = round.StartFinalization(connectors, congestionTree, poolTx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, events, 1)
|
||||
require.True(t, round.IsStarted())
|
||||
require.False(t, round.IsEnded())
|
||||
require.False(t, round.IsFailed())
|
||||
|
||||
event, ok := events[0].(domain.RoundFinalizationStarted)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, round.Id, event.Id)
|
||||
require.Exactly(t, connectors, event.Connectors)
|
||||
require.Exactly(t, congestionTree, event.CongestionTree)
|
||||
require.Exactly(t, poolTx, event.PoolTx)
|
||||
})
|
||||
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
paymentsById := map[string]domain.Payment{}
|
||||
for _, p := range payments {
|
||||
paymentsById[p.Id] = p
|
||||
}
|
||||
fixtures := []struct {
|
||||
round *domain.Round
|
||||
connectors []string
|
||||
tree tree.CongestionTree
|
||||
poolTx string
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "0",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.RegistrationStage,
|
||||
},
|
||||
Payments: paymentsById,
|
||||
},
|
||||
connectors: nil,
|
||||
tree: congestionTree,
|
||||
poolTx: poolTx,
|
||||
expectedErr: "missing list of connectors",
|
||||
},
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "0",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.RegistrationStage,
|
||||
},
|
||||
Payments: paymentsById,
|
||||
},
|
||||
connectors: connectors,
|
||||
tree: nil,
|
||||
poolTx: poolTx,
|
||||
expectedErr: "missing congestion tree",
|
||||
},
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "0",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.RegistrationStage,
|
||||
},
|
||||
Payments: paymentsById,
|
||||
},
|
||||
connectors: connectors,
|
||||
tree: congestionTree,
|
||||
poolTx: "",
|
||||
expectedErr: "missing unsigned pool tx",
|
||||
},
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "0",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.RegistrationStage,
|
||||
},
|
||||
Payments: nil,
|
||||
},
|
||||
connectors: connectors,
|
||||
tree: congestionTree,
|
||||
poolTx: poolTx,
|
||||
expectedErr: "no payments registered",
|
||||
},
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "0",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.UndefinedStage,
|
||||
},
|
||||
Payments: paymentsById,
|
||||
},
|
||||
connectors: connectors,
|
||||
tree: congestionTree,
|
||||
poolTx: poolTx,
|
||||
expectedErr: "not in a valid stage to start payment finalization",
|
||||
},
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "0",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.RegistrationStage,
|
||||
Failed: true,
|
||||
},
|
||||
Payments: paymentsById,
|
||||
},
|
||||
connectors: connectors,
|
||||
tree: congestionTree,
|
||||
poolTx: poolTx,
|
||||
expectedErr: "not in a valid stage to start payment finalization",
|
||||
},
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "0",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.FinalizationStage,
|
||||
},
|
||||
Payments: paymentsById,
|
||||
},
|
||||
connectors: connectors,
|
||||
tree: congestionTree,
|
||||
poolTx: poolTx,
|
||||
expectedErr: "not in a valid stage to start payment finalization",
|
||||
},
|
||||
}
|
||||
|
||||
for _, f := range fixtures {
|
||||
events, err := f.round.StartFinalization(f.connectors, f.tree, f.poolTx)
|
||||
require.EqualError(t, err, f.expectedErr)
|
||||
require.Empty(t, events)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func testEndFinalization(t *testing.T) {
|
||||
t.Run("end_registration", func(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
round := domain.NewRound(dustAmount)
|
||||
events, err := round.StartRegistration()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, events)
|
||||
|
||||
events, err = round.RegisterPayments(payments)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, events)
|
||||
|
||||
events, err = round.StartFinalization(connectors, congestionTree, poolTx)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, events)
|
||||
|
||||
events, err = round.EndFinalization(forfeitTxs, txid)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, events, 1)
|
||||
require.False(t, round.IsStarted())
|
||||
require.True(t, round.IsEnded())
|
||||
require.False(t, round.IsFailed())
|
||||
|
||||
event, ok := events[0].(domain.RoundFinalized)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, round.Id, event.Id)
|
||||
require.Exactly(t, txid, event.Txid)
|
||||
require.Exactly(t, forfeitTxs, event.ForfeitTxs)
|
||||
require.Exactly(t, round.EndingTimestamp, event.Timestamp)
|
||||
})
|
||||
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
paymentsById := map[string]domain.Payment{}
|
||||
for _, p := range payments {
|
||||
paymentsById[p.Id] = p
|
||||
}
|
||||
fixtures := []struct {
|
||||
round *domain.Round
|
||||
forfeitTxs []string
|
||||
txid string
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "0",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.FinalizationStage,
|
||||
},
|
||||
},
|
||||
forfeitTxs: nil,
|
||||
txid: txid,
|
||||
expectedErr: "missing list of signed forfeit txs",
|
||||
},
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "0",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.FinalizationStage,
|
||||
},
|
||||
},
|
||||
forfeitTxs: forfeitTxs,
|
||||
txid: "",
|
||||
expectedErr: "missing pool txid",
|
||||
},
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "0",
|
||||
},
|
||||
forfeitTxs: forfeitTxs,
|
||||
txid: txid,
|
||||
expectedErr: "not in a valid stage to end payment finalization",
|
||||
},
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "0",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.RegistrationStage,
|
||||
},
|
||||
},
|
||||
forfeitTxs: forfeitTxs,
|
||||
txid: txid,
|
||||
expectedErr: "not in a valid stage to end payment finalization",
|
||||
},
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "0",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.FinalizationStage,
|
||||
Failed: true,
|
||||
},
|
||||
},
|
||||
forfeitTxs: []string{emptyPtx, emptyPtx, emptyPtx, emptyPtx},
|
||||
txid: txid,
|
||||
expectedErr: "not in a valid stage to end payment finalization",
|
||||
},
|
||||
{
|
||||
round: &domain.Round{
|
||||
Id: "0",
|
||||
Stage: domain.Stage{
|
||||
Code: domain.FinalizationStage,
|
||||
Ended: true,
|
||||
},
|
||||
},
|
||||
forfeitTxs: []string{emptyPtx, emptyPtx, emptyPtx, emptyPtx},
|
||||
txid: txid,
|
||||
expectedErr: "round already finalized",
|
||||
},
|
||||
}
|
||||
|
||||
for _, f := range fixtures {
|
||||
events, err := f.round.EndFinalization(f.forfeitTxs, f.txid)
|
||||
require.EqualError(t, err, f.expectedErr)
|
||||
require.Empty(t, events)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func testFail(t *testing.T) {
|
||||
t.Run("fail", func(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
round := domain.NewRound(dustAmount)
|
||||
events, err := round.StartRegistration()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, events)
|
||||
|
||||
events, err = round.RegisterPayments(payments)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, events)
|
||||
|
||||
reason := fmt.Errorf("some valid reason")
|
||||
events = round.Fail(reason)
|
||||
require.Len(t, events, 1)
|
||||
require.False(t, round.IsStarted())
|
||||
require.False(t, round.IsEnded())
|
||||
require.True(t, round.IsFailed())
|
||||
|
||||
event, ok := events[0].(domain.RoundFailed)
|
||||
require.True(t, ok)
|
||||
require.Exactly(t, round.Id, event.Id)
|
||||
require.Exactly(t, round.EndingTimestamp, event.Timestamp)
|
||||
require.EqualError(t, reason, event.Err)
|
||||
|
||||
events = round.Fail(reason)
|
||||
require.Empty(t, events)
|
||||
})
|
||||
})
|
||||
}
|
||||
11
server/internal/core/ports/repo_manager.go
Normal file
11
server/internal/core/ports/repo_manager.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package ports
|
||||
|
||||
import "github.com/ark-network/ark/internal/core/domain"
|
||||
|
||||
type RepoManager interface {
|
||||
Events() domain.RoundEventRepository
|
||||
Rounds() domain.RoundRepository
|
||||
Vtxos() domain.VtxoRepository
|
||||
RegisterEventsHandler(func(*domain.Round))
|
||||
Close()
|
||||
}
|
||||
12
server/internal/core/ports/scanner.go
Normal file
12
server/internal/core/ports/scanner.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package ports
|
||||
|
||||
import (
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type BlockchainScanner interface {
|
||||
WatchScripts(ctx context.Context, scripts []string) error
|
||||
UnwatchScripts(ctx context.Context, scripts []string) error
|
||||
GetNotificationChannel(ctx context.Context) chan []domain.VtxoKey
|
||||
}
|
||||
9
server/internal/core/ports/scheduler.go
Normal file
9
server/internal/core/ports/scheduler.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package ports
|
||||
|
||||
type SchedulerService interface {
|
||||
Start()
|
||||
Stop()
|
||||
|
||||
ScheduleTask(interval int64, immediate bool, task func()) error
|
||||
ScheduleTaskOnce(delay int64, task func()) error
|
||||
}
|
||||
29
server/internal/core/ports/tx_builder.go
Normal file
29
server/internal/core/ports/tx_builder.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package ports
|
||||
|
||||
import (
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
)
|
||||
|
||||
type SweepInput struct {
|
||||
InputArgs psetv2.InputArgs
|
||||
SweepLeaf psetv2.TapLeafScript
|
||||
Amount uint64
|
||||
}
|
||||
|
||||
type TxBuilder interface {
|
||||
BuildPoolTx(
|
||||
aspPubkey *secp256k1.PublicKey, payments []domain.Payment, minRelayFee uint64,
|
||||
) (poolTx string, congestionTree tree.CongestionTree, err error)
|
||||
BuildForfeitTxs(
|
||||
aspPubkey *secp256k1.PublicKey, poolTx string, payments []domain.Payment,
|
||||
) (connectors []string, forfeitTxs []string, err error)
|
||||
BuildSweepTx(
|
||||
wallet WalletService,
|
||||
inputs []SweepInput,
|
||||
) (signedSweepTx string, err error)
|
||||
GetLeafSweepClosure(node tree.Node, userPubKey *secp256k1.PublicKey) (*psetv2.TapLeafScript, int64, error)
|
||||
GetVtxoScript(userPubkey, aspPubkey *secp256k1.PublicKey) ([]byte, error)
|
||||
}
|
||||
37
server/internal/core/ports/wallet.go
Normal file
37
server/internal/core/ports/wallet.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package ports
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
)
|
||||
|
||||
type WalletService interface {
|
||||
BlockchainScanner
|
||||
Status(ctx context.Context) (WalletStatus, error)
|
||||
GetPubkey(ctx context.Context) (*secp256k1.PublicKey, error)
|
||||
DeriveAddresses(ctx context.Context, num int) ([]string, error)
|
||||
SignPset(
|
||||
ctx context.Context, pset string, extractRawTx bool,
|
||||
) (string, error)
|
||||
SelectUtxos(ctx context.Context, asset string, amount uint64) ([]TxInput, uint64, error)
|
||||
BroadcastTransaction(ctx context.Context, txHex string) (string, error)
|
||||
SignPsetWithKey(ctx context.Context, pset string, inputIndexes []int) (string, error) // inputIndexes == nil means sign all inputs
|
||||
IsTransactionPublished(ctx context.Context, txid string) (isPublished bool, blocktime int64, err error)
|
||||
EstimateFees(ctx context.Context, pset string) (uint64, error)
|
||||
Close()
|
||||
}
|
||||
|
||||
type WalletStatus interface {
|
||||
IsInitialized() bool
|
||||
IsUnlocked() bool
|
||||
IsSynced() bool
|
||||
}
|
||||
|
||||
type TxInput interface {
|
||||
GetTxid() string
|
||||
GetIndex() uint32
|
||||
GetScript() string
|
||||
GetAsset() string
|
||||
GetValue() uint64
|
||||
}
|
||||
154
server/internal/infrastructure/db/badger/event_repo.go
Normal file
154
server/internal/infrastructure/db/badger/event_repo.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package badgerdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
dbtypes "github.com/ark-network/ark/internal/infrastructure/db/types"
|
||||
"github.com/dgraph-io/badger/v4"
|
||||
"github.com/timshannon/badgerhold/v4"
|
||||
)
|
||||
|
||||
const eventStoreDir = "round-events"
|
||||
|
||||
type eventsDTO struct {
|
||||
Events [][]byte
|
||||
}
|
||||
|
||||
type eventRepository struct {
|
||||
store *badgerhold.Store
|
||||
lock *sync.RWMutex
|
||||
chUpdates chan *domain.Round
|
||||
handler func(round *domain.Round)
|
||||
}
|
||||
|
||||
func NewRoundEventRepository(config ...interface{}) (dbtypes.EventStore, error) {
|
||||
if len(config) != 2 {
|
||||
return nil, fmt.Errorf("invalid config")
|
||||
}
|
||||
baseDir, ok := config[0].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid base directory")
|
||||
}
|
||||
|
||||
var logger badger.Logger
|
||||
if config[1] != nil {
|
||||
logger, ok = config[1].(badger.Logger)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid logger")
|
||||
}
|
||||
}
|
||||
|
||||
var dir string
|
||||
if len(baseDir) > 0 {
|
||||
dir = filepath.Join(baseDir, eventStoreDir)
|
||||
}
|
||||
store, err := createDB(dir, logger)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open round events store: %s", err)
|
||||
}
|
||||
chEvents := make(chan *domain.Round)
|
||||
lock := &sync.RWMutex{}
|
||||
repo := &eventRepository{store, lock, chEvents, nil}
|
||||
go repo.listen()
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
func (r *eventRepository) Save(
|
||||
ctx context.Context, id string, events ...domain.RoundEvent,
|
||||
) error {
|
||||
allEvents, err := r.get(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
allEvents = append(allEvents, events...)
|
||||
if err := r.upsert(ctx, id, allEvents); err != nil {
|
||||
return err
|
||||
}
|
||||
go r.publishEvents(allEvents)
|
||||
return nil
|
||||
}
|
||||
func (r *eventRepository) Load(
|
||||
ctx context.Context, id string,
|
||||
) (*domain.Round, error) {
|
||||
events, err := r.get(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return domain.NewRoundFromEvents(events), nil
|
||||
}
|
||||
|
||||
func (r *eventRepository) RegisterEventsHandler(
|
||||
handler func(round *domain.Round),
|
||||
) {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
r.handler = handler
|
||||
}
|
||||
|
||||
func (r *eventRepository) Close() {
|
||||
close(r.chUpdates)
|
||||
r.store.Close()
|
||||
}
|
||||
|
||||
func (r *eventRepository) get(
|
||||
ctx context.Context, id string,
|
||||
) ([]domain.RoundEvent, error) {
|
||||
dto := eventsDTO{}
|
||||
var err error
|
||||
if ctx.Value("tx") != nil {
|
||||
tx := ctx.Value("tx").(*badger.Txn)
|
||||
err = r.store.TxGet(tx, id, &dto)
|
||||
} else {
|
||||
err = r.store.Get(id, &dto)
|
||||
}
|
||||
if err != nil {
|
||||
if err == badgerhold.ErrNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get events with id %s: %s", id, err)
|
||||
}
|
||||
|
||||
return deserializeEvents(dto.Events)
|
||||
}
|
||||
|
||||
func (r *eventRepository) upsert(
|
||||
ctx context.Context, id string, events []domain.RoundEvent,
|
||||
) error {
|
||||
buf, err := serializeEvents(events)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ctx.Value("tx") != nil {
|
||||
tx := ctx.Value("tx").(*badger.Txn)
|
||||
err = r.store.TxUpsert(tx, id, buf)
|
||||
} else {
|
||||
err = r.store.Upsert(id, buf)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to upsert events with id %s: %s", id, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *eventRepository) listen() {
|
||||
for updatedRound := range r.chUpdates {
|
||||
r.lock.RLock()
|
||||
if r.handler != nil {
|
||||
r.handler(updatedRound)
|
||||
}
|
||||
r.lock.RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *eventRepository) publishEvents(events []domain.RoundEvent) {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
round := domain.NewRoundFromEvents(events)
|
||||
r.chUpdates <- round
|
||||
}
|
||||
140
server/internal/infrastructure/db/badger/round_repo.go
Normal file
140
server/internal/infrastructure/db/badger/round_repo.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package badgerdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
dbtypes "github.com/ark-network/ark/internal/infrastructure/db/types"
|
||||
"github.com/dgraph-io/badger/v4"
|
||||
"github.com/timshannon/badgerhold/v4"
|
||||
)
|
||||
|
||||
const roundStoreDir = "rounds"
|
||||
|
||||
type roundRepository struct {
|
||||
store *badgerhold.Store
|
||||
}
|
||||
|
||||
func NewRoundRepository(config ...interface{}) (dbtypes.RoundStore, error) {
|
||||
if len(config) != 2 {
|
||||
return nil, fmt.Errorf("invalid config")
|
||||
}
|
||||
baseDir, ok := config[0].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid base directory")
|
||||
}
|
||||
var logger badger.Logger
|
||||
if config[1] != nil {
|
||||
logger, ok = config[1].(badger.Logger)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid logger")
|
||||
}
|
||||
}
|
||||
|
||||
var dir string
|
||||
if len(baseDir) > 0 {
|
||||
dir = filepath.Join(baseDir, roundStoreDir)
|
||||
}
|
||||
store, err := createDB(dir, logger)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open round events store: %s", err)
|
||||
}
|
||||
|
||||
return &roundRepository{store}, nil
|
||||
}
|
||||
|
||||
func (r *roundRepository) AddOrUpdateRound(
|
||||
ctx context.Context, round domain.Round,
|
||||
) error {
|
||||
return r.addOrUpdateRound(ctx, round)
|
||||
}
|
||||
|
||||
func (r *roundRepository) GetCurrentRound(
|
||||
ctx context.Context,
|
||||
) (*domain.Round, error) {
|
||||
query := badgerhold.Where("Stage.Ended").Eq(false).And("Stage.Failed").Eq(false)
|
||||
rounds, err := r.findRound(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rounds) <= 0 {
|
||||
return nil, fmt.Errorf("ongoing round not found")
|
||||
}
|
||||
return &rounds[0], nil
|
||||
}
|
||||
|
||||
func (r *roundRepository) GetRoundWithId(
|
||||
ctx context.Context, id string,
|
||||
) (*domain.Round, error) {
|
||||
query := badgerhold.Where("Id").Eq(id)
|
||||
rounds, err := r.findRound(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rounds) <= 0 {
|
||||
return nil, fmt.Errorf("round with id %s not found", id)
|
||||
}
|
||||
round := &rounds[0]
|
||||
return round, nil
|
||||
}
|
||||
|
||||
func (r *roundRepository) GetRoundWithTxid(
|
||||
ctx context.Context, txid string,
|
||||
) (*domain.Round, error) {
|
||||
query := badgerhold.Where("Txid").Eq(txid)
|
||||
rounds, err := r.findRound(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rounds) <= 0 {
|
||||
return nil, fmt.Errorf("round with txid %s not found", txid)
|
||||
}
|
||||
round := &rounds[0]
|
||||
return round, nil
|
||||
}
|
||||
|
||||
func (r *roundRepository) GetSweepableRounds(
|
||||
ctx context.Context,
|
||||
) ([]domain.Round, error) {
|
||||
query := badgerhold.Where("Stage.Code").Eq(domain.FinalizationStage).
|
||||
And("Stage.Ended").Eq(true).And("Swept").Eq(false)
|
||||
rounds, err := r.findRound(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rounds, nil
|
||||
}
|
||||
|
||||
func (r *roundRepository) Close() {
|
||||
r.store.Close()
|
||||
}
|
||||
|
||||
func (r *roundRepository) findRound(
|
||||
ctx context.Context, query *badgerhold.Query,
|
||||
) ([]domain.Round, error) {
|
||||
var rounds []domain.Round
|
||||
var err error
|
||||
|
||||
if ctx.Value("tx") != nil {
|
||||
tx := ctx.Value("tx").(*badger.Txn)
|
||||
err = r.store.TxFind(tx, &rounds, query)
|
||||
} else {
|
||||
err = r.store.Find(&rounds, query)
|
||||
}
|
||||
|
||||
return rounds, err
|
||||
}
|
||||
|
||||
func (r *roundRepository) addOrUpdateRound(
|
||||
ctx context.Context, round domain.Round,
|
||||
) (err error) {
|
||||
if ctx.Value("tx") != nil {
|
||||
tx := ctx.Value("tx").(*badger.Txn)
|
||||
err = r.store.TxUpsert(tx, round.Id, round)
|
||||
} else {
|
||||
err = r.store.Upsert(round.Id, round)
|
||||
}
|
||||
return
|
||||
}
|
||||
116
server/internal/infrastructure/db/badger/utils.go
Normal file
116
server/internal/infrastructure/db/badger/utils.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package badgerdb
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/dgraph-io/badger/v4"
|
||||
"github.com/dgraph-io/badger/v4/options"
|
||||
"github.com/timshannon/badgerhold/v4"
|
||||
)
|
||||
|
||||
func createDB(dbDir string, logger badger.Logger) (*badgerhold.Store, error) {
|
||||
isInMemory := len(dbDir) <= 0
|
||||
|
||||
opts := badger.DefaultOptions(dbDir)
|
||||
opts.Logger = logger
|
||||
|
||||
if isInMemory {
|
||||
opts.InMemory = true
|
||||
} else {
|
||||
opts.Compression = options.ZSTD
|
||||
}
|
||||
|
||||
db, err := badgerhold.Open(badgerhold.Options{
|
||||
Encoder: badgerhold.DefaultEncode,
|
||||
Decoder: badgerhold.DefaultDecode,
|
||||
SequenceBandwith: 100,
|
||||
Options: opts,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !isInMemory {
|
||||
ticker := time.NewTicker(30 * time.Minute)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
<-ticker.C
|
||||
if err := db.Badger().RunValueLogGC(0.5); err != nil && err != badger.ErrNoRewrite {
|
||||
logger.Errorf("%s", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func serializeEvents(events []domain.RoundEvent) (*eventsDTO, error) {
|
||||
rawEvents := make([][]byte, 0, len(events))
|
||||
for _, event := range events {
|
||||
buf, err := serializeEvent(event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawEvents = append(rawEvents, buf)
|
||||
}
|
||||
return &eventsDTO{rawEvents}, nil
|
||||
}
|
||||
|
||||
func deserializeEvents(rawEvents [][]byte) ([]domain.RoundEvent, error) {
|
||||
events := make([]domain.RoundEvent, 0)
|
||||
for _, buf := range rawEvents {
|
||||
event, err := deserializeEvent(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events = append(events, event)
|
||||
}
|
||||
return events, nil
|
||||
}
|
||||
|
||||
func serializeEvent(event domain.RoundEvent) ([]byte, error) {
|
||||
switch eventType := event.(type) {
|
||||
default:
|
||||
return json.Marshal(eventType)
|
||||
}
|
||||
}
|
||||
|
||||
func deserializeEvent(buf []byte) (domain.RoundEvent, error) {
|
||||
{
|
||||
var event = domain.RoundFailed{}
|
||||
if err := json.Unmarshal(buf, &event); err == nil && len(event.Err) > 0 {
|
||||
return event, nil
|
||||
}
|
||||
}
|
||||
{
|
||||
var event = domain.RoundFinalized{}
|
||||
if err := json.Unmarshal(buf, &event); err == nil && len(event.Txid) > 0 {
|
||||
return event, nil
|
||||
}
|
||||
}
|
||||
{
|
||||
var event = domain.RoundFinalizationStarted{}
|
||||
if err := json.Unmarshal(buf, &event); err == nil && len(event.CongestionTree) > 0 {
|
||||
return event, nil
|
||||
}
|
||||
}
|
||||
{
|
||||
var event = domain.PaymentsRegistered{}
|
||||
if err := json.Unmarshal(buf, &event); err == nil && len(event.Payments) > 0 {
|
||||
return event, nil
|
||||
}
|
||||
}
|
||||
{
|
||||
var event = domain.RoundStarted{}
|
||||
if err := json.Unmarshal(buf, &event); err == nil && event.Timestamp > 0 {
|
||||
return event, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown event")
|
||||
}
|
||||
245
server/internal/infrastructure/db/badger/vtxo_repo.go
Normal file
245
server/internal/infrastructure/db/badger/vtxo_repo.go
Normal file
@@ -0,0 +1,245 @@
|
||||
package badgerdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
dbtypes "github.com/ark-network/ark/internal/infrastructure/db/types"
|
||||
"github.com/dgraph-io/badger/v4"
|
||||
"github.com/timshannon/badgerhold/v4"
|
||||
)
|
||||
|
||||
const vtxoStoreDir = "vtxos"
|
||||
|
||||
type vtxoRepository struct {
|
||||
store *badgerhold.Store
|
||||
}
|
||||
|
||||
func NewVtxoRepository(config ...interface{}) (dbtypes.VtxoStore, error) {
|
||||
if len(config) != 2 {
|
||||
return nil, fmt.Errorf("invalid config")
|
||||
}
|
||||
baseDir, ok := config[0].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid base directory")
|
||||
}
|
||||
var logger badger.Logger
|
||||
if config[1] != nil {
|
||||
logger, ok = config[1].(badger.Logger)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid logger")
|
||||
}
|
||||
}
|
||||
|
||||
var dir string
|
||||
if len(baseDir) > 0 {
|
||||
dir = filepath.Join(baseDir, vtxoStoreDir)
|
||||
}
|
||||
store, err := createDB(dir, logger)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open round events store: %s", err)
|
||||
}
|
||||
|
||||
return &vtxoRepository{store}, nil
|
||||
}
|
||||
|
||||
func (r *vtxoRepository) AddVtxos(
|
||||
ctx context.Context, vtxos []domain.Vtxo,
|
||||
) error {
|
||||
return r.addVtxos(ctx, vtxos)
|
||||
}
|
||||
|
||||
func (r *vtxoRepository) SpendVtxos(
|
||||
ctx context.Context, vtxoKeys []domain.VtxoKey,
|
||||
) error {
|
||||
for _, vtxoKey := range vtxoKeys {
|
||||
if err := r.spendVtxo(ctx, vtxoKey); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *vtxoRepository) RedeemVtxos(
|
||||
ctx context.Context, vtxoKeys []domain.VtxoKey,
|
||||
) ([]domain.Vtxo, error) {
|
||||
vtxos := make([]domain.Vtxo, 0, len(vtxoKeys))
|
||||
for _, vtxoKey := range vtxoKeys {
|
||||
vtxo, err := r.redeemVtxo(ctx, vtxoKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if vtxo != nil {
|
||||
vtxos = append(vtxos, *vtxo)
|
||||
}
|
||||
}
|
||||
return vtxos, nil
|
||||
}
|
||||
|
||||
func (r *vtxoRepository) GetVtxos(
|
||||
ctx context.Context, vtxoKeys []domain.VtxoKey,
|
||||
) ([]domain.Vtxo, error) {
|
||||
vtxos := make([]domain.Vtxo, 0, len(vtxoKeys))
|
||||
for _, vtxoKey := range vtxoKeys {
|
||||
vtxo, err := r.getVtxo(ctx, vtxoKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vtxos = append(vtxos, *vtxo)
|
||||
}
|
||||
return vtxos, nil
|
||||
}
|
||||
|
||||
func (r *vtxoRepository) GetVtxosForRound(
|
||||
ctx context.Context, txid string,
|
||||
) ([]domain.Vtxo, error) {
|
||||
query := badgerhold.Where("Txid").Eq(txid)
|
||||
return r.findVtxos(ctx, query)
|
||||
}
|
||||
|
||||
func (r *vtxoRepository) GetSpendableVtxos(
|
||||
ctx context.Context, pubkey string,
|
||||
) ([]domain.Vtxo, error) {
|
||||
query := badgerhold.Where("Spent").Eq(false).And("Redeemed").Eq(false).And("Swept").Eq(false)
|
||||
if len(pubkey) > 0 {
|
||||
query = query.And("Pubkey").Eq(pubkey)
|
||||
}
|
||||
return r.findVtxos(ctx, query)
|
||||
}
|
||||
|
||||
func (r *vtxoRepository) SweepVtxos(
|
||||
ctx context.Context, vtxoKeys []domain.VtxoKey,
|
||||
) error {
|
||||
for _, vtxoKey := range vtxoKeys {
|
||||
if err := r.sweepVtxo(ctx, vtxoKey); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *vtxoRepository) Close() {
|
||||
r.store.Close()
|
||||
}
|
||||
|
||||
func (r *vtxoRepository) addVtxos(
|
||||
ctx context.Context, vtxos []domain.Vtxo,
|
||||
) (err error) {
|
||||
for _, vtxo := range vtxos {
|
||||
vtxoKey := vtxo.VtxoKey.Hash()
|
||||
if ctx.Value("tx") != nil {
|
||||
tx := ctx.Value("tx").(*badger.Txn)
|
||||
err = r.store.TxInsert(tx, vtxoKey, vtxo)
|
||||
} else {
|
||||
err = r.store.Insert(vtxoKey, vtxo)
|
||||
}
|
||||
}
|
||||
if err != nil && err == badgerhold.ErrKeyExists {
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r *vtxoRepository) getVtxo(
|
||||
ctx context.Context, vtxoKey domain.VtxoKey,
|
||||
) (*domain.Vtxo, error) {
|
||||
var vtxo domain.Vtxo
|
||||
var err error
|
||||
if ctx.Value("tx") != nil {
|
||||
tx := ctx.Value("tx").(*badger.Txn)
|
||||
err = r.store.TxGet(tx, vtxoKey.Hash(), &vtxo)
|
||||
} else {
|
||||
err = r.store.Get(vtxoKey.Hash(), &vtxo)
|
||||
}
|
||||
if err != nil && err == badgerhold.ErrNotFound {
|
||||
return nil, fmt.Errorf("vtxo %s:%d not found", vtxoKey.Txid, vtxoKey.VOut)
|
||||
}
|
||||
|
||||
return &vtxo, nil
|
||||
}
|
||||
|
||||
func (r *vtxoRepository) spendVtxo(ctx context.Context, vtxoKey domain.VtxoKey) error {
|
||||
vtxo, err := r.getVtxo(ctx, vtxoKey)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "not found") {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
if vtxo.Spent {
|
||||
return nil
|
||||
}
|
||||
|
||||
vtxo.Spent = true
|
||||
if ctx.Value("tx") != nil {
|
||||
tx := ctx.Value("tx").(*badger.Txn)
|
||||
err = r.store.TxUpdate(tx, vtxoKey.Hash(), *vtxo)
|
||||
} else {
|
||||
err = r.store.Update(vtxoKey.Hash(), *vtxo)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *vtxoRepository) redeemVtxo(ctx context.Context, vtxoKey domain.VtxoKey) (*domain.Vtxo, error) {
|
||||
vtxo, err := r.getVtxo(ctx, vtxoKey)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "not found") {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if vtxo.Redeemed {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
vtxo.Redeemed = true
|
||||
if ctx.Value("tx") != nil {
|
||||
tx := ctx.Value("tx").(*badger.Txn)
|
||||
err = r.store.TxUpdate(tx, vtxoKey.Hash(), *vtxo)
|
||||
} else {
|
||||
err = r.store.Update(vtxoKey.Hash(), *vtxo)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vtxo, nil
|
||||
}
|
||||
|
||||
func (r *vtxoRepository) findVtxos(ctx context.Context, query *badgerhold.Query) ([]domain.Vtxo, error) {
|
||||
var vtxos []domain.Vtxo
|
||||
var err error
|
||||
|
||||
if ctx.Value("tx") != nil {
|
||||
tx := ctx.Value("tx").(*badger.Txn)
|
||||
err = r.store.TxFind(tx, &vtxos, query)
|
||||
} else {
|
||||
err = r.store.Find(&vtxos, query)
|
||||
}
|
||||
|
||||
return vtxos, err
|
||||
}
|
||||
|
||||
func (r *vtxoRepository) sweepVtxo(ctx context.Context, vtxoKey domain.VtxoKey) error {
|
||||
vtxo, err := r.getVtxo(ctx, vtxoKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if vtxo.Swept {
|
||||
return nil
|
||||
}
|
||||
|
||||
vtxo.Swept = true
|
||||
if ctx.Value("tx") != nil {
|
||||
tx := ctx.Value("tx").(*badger.Txn)
|
||||
err = r.store.TxUpdate(tx, vtxoKey.Hash(), *vtxo)
|
||||
} else {
|
||||
err = r.store.Update(vtxoKey.Hash(), *vtxo)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
90
server/internal/infrastructure/db/service.go
Normal file
90
server/internal/infrastructure/db/service.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
badgerdb "github.com/ark-network/ark/internal/infrastructure/db/badger"
|
||||
dbtypes "github.com/ark-network/ark/internal/infrastructure/db/types"
|
||||
)
|
||||
|
||||
var (
|
||||
eventStoreTypes = map[string]func(...interface{}) (dbtypes.EventStore, error){
|
||||
"badger": badgerdb.NewRoundEventRepository,
|
||||
}
|
||||
roundStoreTypes = map[string]func(...interface{}) (dbtypes.RoundStore, error){
|
||||
"badger": badgerdb.NewRoundRepository,
|
||||
}
|
||||
vtxoStoreTypes = map[string]func(...interface{}) (dbtypes.VtxoStore, error){
|
||||
"badger": badgerdb.NewVtxoRepository,
|
||||
}
|
||||
)
|
||||
|
||||
type ServiceConfig struct {
|
||||
EventStoreType string
|
||||
RoundStoreType string
|
||||
VtxoStoreType string
|
||||
|
||||
EventStoreConfig []interface{}
|
||||
RoundStoreConfig []interface{}
|
||||
VtxoStoreConfig []interface{}
|
||||
}
|
||||
|
||||
type service struct {
|
||||
eventStore dbtypes.EventStore
|
||||
roundStore dbtypes.RoundStore
|
||||
vtxoStore dbtypes.VtxoStore
|
||||
}
|
||||
|
||||
func NewService(config ServiceConfig) (ports.RepoManager, error) {
|
||||
eventStoreFactory, ok := eventStoreTypes[config.EventStoreType]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("event store type not supported")
|
||||
}
|
||||
roundStoreFactory, ok := roundStoreTypes[config.RoundStoreType]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("round store type not supported")
|
||||
}
|
||||
vtxoStoreFactory, ok := vtxoStoreTypes[config.VtxoStoreType]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("vtxo store type not supported")
|
||||
}
|
||||
|
||||
eventStore, err := eventStoreFactory(config.EventStoreConfig...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open event store: %s", err)
|
||||
}
|
||||
roundStore, err := roundStoreFactory(config.RoundStoreConfig...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open round store: %s", err)
|
||||
}
|
||||
vtxoStore, err := vtxoStoreFactory(config.VtxoStoreConfig...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open vtxo store: %s", err)
|
||||
}
|
||||
|
||||
return &service{eventStore, roundStore, vtxoStore}, nil
|
||||
}
|
||||
|
||||
func (s *service) RegisterEventsHandler(handler func(round *domain.Round)) {
|
||||
s.eventStore.RegisterEventsHandler(handler)
|
||||
}
|
||||
|
||||
func (s *service) Events() domain.RoundEventRepository {
|
||||
return s.eventStore
|
||||
}
|
||||
|
||||
func (s *service) Rounds() domain.RoundRepository {
|
||||
return s.roundStore
|
||||
}
|
||||
|
||||
func (s *service) Vtxos() domain.VtxoRepository {
|
||||
return s.vtxoStore
|
||||
}
|
||||
|
||||
func (s *service) Close() {
|
||||
s.eventStore.Close()
|
||||
s.roundStore.Close()
|
||||
s.vtxoStore.Close()
|
||||
}
|
||||
417
server/internal/infrastructure/db/service_test.go
Normal file
417
server/internal/infrastructure/db/service_test.go
Normal file
@@ -0,0 +1,417 @@
|
||||
package db_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
"github.com/ark-network/ark/internal/infrastructure/db"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
emptyPtx = "cHNldP8BAgQCAAAAAQQBAAEFAQABBgEDAfsEAgAAAAA="
|
||||
emptyTx = "0200000000000000000000"
|
||||
txid = "00000000000000000000000000000000000000000000000000000000000000000"
|
||||
pubkey1 = "0300000000000000000000000000000000000000000000000000000000000000001"
|
||||
pubkey2 = "0200000000000000000000000000000000000000000000000000000000000000002"
|
||||
)
|
||||
|
||||
var congestionTree = [][]tree.Node{
|
||||
{
|
||||
{
|
||||
Txid: txid,
|
||||
Tx: emptyPtx,
|
||||
ParentTxid: txid,
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
Txid: txid,
|
||||
Tx: emptyPtx,
|
||||
ParentTxid: txid,
|
||||
},
|
||||
{
|
||||
Txid: txid,
|
||||
Tx: emptyPtx,
|
||||
ParentTxid: txid,
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
Txid: txid,
|
||||
Tx: emptyPtx,
|
||||
ParentTxid: txid,
|
||||
},
|
||||
{
|
||||
Txid: txid,
|
||||
Tx: emptyPtx,
|
||||
ParentTxid: txid,
|
||||
},
|
||||
{
|
||||
Txid: txid,
|
||||
Tx: emptyPtx,
|
||||
ParentTxid: txid,
|
||||
},
|
||||
{
|
||||
Txid: txid,
|
||||
Tx: emptyPtx,
|
||||
ParentTxid: txid,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestService(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config db.ServiceConfig
|
||||
}{
|
||||
{
|
||||
name: "repo_manager_with_badger_stores",
|
||||
config: db.ServiceConfig{
|
||||
EventStoreType: "badger",
|
||||
RoundStoreType: "badger",
|
||||
VtxoStoreType: "badger",
|
||||
EventStoreConfig: []interface{}{"", nil},
|
||||
RoundStoreConfig: []interface{}{"", nil},
|
||||
VtxoStoreConfig: []interface{}{"", nil},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
svc, err := db.NewService(tt.config)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, svc)
|
||||
|
||||
testRoundEventRepository(t, svc)
|
||||
testRoundRepository(t, svc)
|
||||
testVtxoRepository(t, svc)
|
||||
|
||||
svc.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testRoundEventRepository(t *testing.T, svc ports.RepoManager) {
|
||||
t.Run("test_event_repository", func(t *testing.T) {
|
||||
fixtures := []struct {
|
||||
roundId string
|
||||
events []domain.RoundEvent
|
||||
handler func(*domain.Round)
|
||||
}{
|
||||
{
|
||||
roundId: "42dd81f7-cadd-482c-bf69-8e9209aae9f3",
|
||||
events: []domain.RoundEvent{
|
||||
domain.RoundStarted{
|
||||
Id: "42dd81f7-cadd-482c-bf69-8e9209aae9f3",
|
||||
Timestamp: 1701190270,
|
||||
},
|
||||
},
|
||||
handler: func(round *domain.Round) {
|
||||
require.NotNil(t, round)
|
||||
require.Len(t, round.Events(), 1)
|
||||
require.True(t, round.IsStarted())
|
||||
require.False(t, round.IsFailed())
|
||||
require.False(t, round.IsEnded())
|
||||
},
|
||||
},
|
||||
{
|
||||
roundId: "1ea610ff-bf3e-4068-9bfd-b6c3f553467e",
|
||||
events: []domain.RoundEvent{
|
||||
domain.RoundStarted{
|
||||
Id: "1ea610ff-bf3e-4068-9bfd-b6c3f553467e",
|
||||
Timestamp: 1701190270,
|
||||
},
|
||||
domain.RoundFinalizationStarted{
|
||||
Id: "1ea610ff-bf3e-4068-9bfd-b6c3f553467e",
|
||||
CongestionTree: congestionTree,
|
||||
Connectors: []string{emptyPtx, emptyPtx},
|
||||
PoolTx: emptyTx,
|
||||
},
|
||||
},
|
||||
handler: func(round *domain.Round) {
|
||||
require.NotNil(t, round)
|
||||
require.Len(t, round.Events(), 2)
|
||||
require.Len(t, round.CongestionTree, 3)
|
||||
require.Equal(t, round.CongestionTree.NumberOfNodes(), 7)
|
||||
require.Len(t, round.Connectors, 2)
|
||||
},
|
||||
},
|
||||
{
|
||||
roundId: "7578231e-428d-45ae-aaa4-e62c77ad5cec",
|
||||
events: []domain.RoundEvent{
|
||||
domain.RoundStarted{
|
||||
Id: "7578231e-428d-45ae-aaa4-e62c77ad5cec",
|
||||
Timestamp: 1701190270,
|
||||
},
|
||||
domain.RoundFinalizationStarted{
|
||||
Id: "7578231e-428d-45ae-aaa4-e62c77ad5cec",
|
||||
CongestionTree: congestionTree,
|
||||
Connectors: []string{emptyPtx, emptyPtx},
|
||||
PoolTx: emptyTx,
|
||||
},
|
||||
domain.RoundFinalized{
|
||||
Id: "7578231e-428d-45ae-aaa4-e62c77ad5cec",
|
||||
Txid: txid,
|
||||
ForfeitTxs: []string{emptyPtx, emptyPtx, emptyPtx, emptyPtx},
|
||||
Timestamp: 1701190300,
|
||||
},
|
||||
},
|
||||
handler: func(round *domain.Round) {
|
||||
require.NotNil(t, round)
|
||||
require.Len(t, round.Events(), 3)
|
||||
require.False(t, round.IsStarted())
|
||||
require.False(t, round.IsFailed())
|
||||
require.True(t, round.IsEnded())
|
||||
require.NotEmpty(t, round.Txid)
|
||||
},
|
||||
},
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
for _, f := range fixtures {
|
||||
svc.RegisterEventsHandler(f.handler)
|
||||
|
||||
err := svc.Events().Save(ctx, f.roundId, f.events...)
|
||||
require.NoError(t, err)
|
||||
|
||||
round, err := svc.Events().Load(ctx, f.roundId)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, round)
|
||||
require.Equal(t, f.roundId, round.Id)
|
||||
require.Len(t, round.Events(), len(f.events))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func testRoundRepository(t *testing.T, svc ports.RepoManager) {
|
||||
t.Run("test_round_repository", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
now := time.Now()
|
||||
|
||||
roundId := uuid.New().String()
|
||||
|
||||
round, err := svc.Rounds().GetRoundWithId(ctx, roundId)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, round)
|
||||
|
||||
events := []domain.RoundEvent{
|
||||
domain.RoundStarted{
|
||||
Id: roundId,
|
||||
Timestamp: now.Unix(),
|
||||
},
|
||||
}
|
||||
round = domain.NewRoundFromEvents(events)
|
||||
err = svc.Rounds().AddOrUpdateRound(ctx, *round)
|
||||
require.NoError(t, err)
|
||||
|
||||
currentRound, err := svc.Rounds().GetCurrentRound(ctx)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, currentRound)
|
||||
require.Condition(t, roundsMatch(*round, *currentRound))
|
||||
|
||||
roundById, err := svc.Rounds().GetRoundWithId(ctx, roundId)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, roundById)
|
||||
require.Condition(t, roundsMatch(*round, *roundById))
|
||||
|
||||
newEvents := []domain.RoundEvent{
|
||||
domain.PaymentsRegistered{
|
||||
Id: roundId,
|
||||
Payments: []domain.Payment{
|
||||
{
|
||||
Id: uuid.New().String(),
|
||||
Inputs: []domain.Vtxo{{}},
|
||||
Receivers: []domain.Receiver{{}},
|
||||
},
|
||||
{
|
||||
Id: uuid.New().String(),
|
||||
Inputs: []domain.Vtxo{{}},
|
||||
Receivers: []domain.Receiver{{}, {}, {}},
|
||||
},
|
||||
},
|
||||
},
|
||||
domain.RoundFinalizationStarted{
|
||||
Id: roundId,
|
||||
CongestionTree: congestionTree,
|
||||
Connectors: []string{emptyPtx, emptyPtx},
|
||||
PoolTx: emptyTx,
|
||||
},
|
||||
}
|
||||
events = append(events, newEvents...)
|
||||
updatedRound := domain.NewRoundFromEvents(events)
|
||||
|
||||
err = svc.Rounds().AddOrUpdateRound(ctx, *updatedRound)
|
||||
require.NoError(t, err)
|
||||
|
||||
currentRound, err = svc.Rounds().GetCurrentRound(ctx)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, currentRound)
|
||||
require.Condition(t, roundsMatch(*updatedRound, *currentRound))
|
||||
|
||||
roundById, err = svc.Rounds().GetRoundWithId(ctx, updatedRound.Id)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, currentRound)
|
||||
require.Condition(t, roundsMatch(*updatedRound, *roundById))
|
||||
|
||||
newEvents = []domain.RoundEvent{
|
||||
domain.RoundFinalized{
|
||||
Id: roundId,
|
||||
Txid: txid,
|
||||
ForfeitTxs: []string{emptyPtx, emptyPtx, emptyPtx, emptyPtx},
|
||||
Timestamp: now.Add(60 * time.Second).Unix(),
|
||||
},
|
||||
}
|
||||
events = append(events, newEvents...)
|
||||
finalizedRound := domain.NewRoundFromEvents(events)
|
||||
|
||||
err = svc.Rounds().AddOrUpdateRound(ctx, *finalizedRound)
|
||||
require.NoError(t, err)
|
||||
|
||||
currentRound, err = svc.Rounds().GetCurrentRound(ctx)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, currentRound)
|
||||
|
||||
roundById, err = svc.Rounds().GetRoundWithId(ctx, roundId)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, roundById)
|
||||
require.Condition(t, roundsMatch(*finalizedRound, *roundById))
|
||||
|
||||
roundByTxid, err := svc.Rounds().GetRoundWithTxid(ctx, txid)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, roundByTxid)
|
||||
require.Condition(t, roundsMatch(*finalizedRound, *roundByTxid))
|
||||
})
|
||||
}
|
||||
|
||||
func testVtxoRepository(t *testing.T, svc ports.RepoManager) {
|
||||
t.Run("test_vtxo_repository", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
userVtxos := []domain.Vtxo{
|
||||
{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: txid,
|
||||
VOut: 0,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Pubkey: pubkey1,
|
||||
Amount: 1000,
|
||||
},
|
||||
},
|
||||
{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: txid,
|
||||
VOut: 1,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Pubkey: pubkey1,
|
||||
Amount: 2000,
|
||||
},
|
||||
},
|
||||
}
|
||||
newVtxos := append(userVtxos, domain.Vtxo{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: txid,
|
||||
VOut: 1,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Pubkey: pubkey2,
|
||||
Amount: 2000,
|
||||
},
|
||||
})
|
||||
|
||||
vtxoKeys := make([]domain.VtxoKey, 0, len(userVtxos))
|
||||
for _, v := range userVtxos {
|
||||
vtxoKeys = append(vtxoKeys, v.VtxoKey)
|
||||
}
|
||||
|
||||
vtxos, err := svc.Vtxos().GetVtxos(ctx, vtxoKeys)
|
||||
require.Error(t, err)
|
||||
require.Empty(t, vtxos)
|
||||
|
||||
spendableVtxos, err := svc.Vtxos().GetSpendableVtxos(ctx, pubkey1)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, spendableVtxos)
|
||||
|
||||
spendableVtxos, err = svc.Vtxos().GetSpendableVtxos(ctx, "")
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, spendableVtxos)
|
||||
|
||||
err = svc.Vtxos().AddVtxos(ctx, newVtxos)
|
||||
require.NoError(t, err)
|
||||
|
||||
vtxos, err = svc.Vtxos().GetVtxos(ctx, vtxoKeys)
|
||||
require.NoError(t, err)
|
||||
require.Exactly(t, userVtxos, vtxos)
|
||||
|
||||
spendableVtxos, err = svc.Vtxos().GetSpendableVtxos(ctx, pubkey1)
|
||||
require.NoError(t, err)
|
||||
require.Exactly(t, vtxos, spendableVtxos)
|
||||
|
||||
spendableVtxos, err = svc.Vtxos().GetSpendableVtxos(ctx, "")
|
||||
require.NoError(t, err)
|
||||
require.Exactly(t, userVtxos, spendableVtxos)
|
||||
|
||||
err = svc.Vtxos().SpendVtxos(ctx, vtxoKeys[:1])
|
||||
require.NoError(t, err)
|
||||
|
||||
spentVtxos, err := svc.Vtxos().GetVtxos(ctx, vtxoKeys[:1])
|
||||
require.NoError(t, err)
|
||||
require.Len(t, spentVtxos, len(vtxoKeys[:1]))
|
||||
for _, v := range spentVtxos {
|
||||
require.True(t, v.Spent)
|
||||
}
|
||||
|
||||
spendableVtxos, err = svc.Vtxos().GetSpendableVtxos(ctx, pubkey1)
|
||||
require.NoError(t, err)
|
||||
require.Exactly(t, vtxos[1:], spendableVtxos)
|
||||
})
|
||||
}
|
||||
|
||||
func roundsMatch(expected, got domain.Round) assert.Comparison {
|
||||
return func() bool {
|
||||
if expected.Id != got.Id {
|
||||
return false
|
||||
}
|
||||
if expected.StartingTimestamp != got.StartingTimestamp {
|
||||
return false
|
||||
}
|
||||
if expected.EndingTimestamp != got.EndingTimestamp {
|
||||
return false
|
||||
}
|
||||
if expected.Stage != got.Stage {
|
||||
return false
|
||||
}
|
||||
if !reflect.DeepEqual(expected.Payments, got.Payments) {
|
||||
return false
|
||||
}
|
||||
if expected.Txid != got.Txid {
|
||||
return false
|
||||
}
|
||||
if expected.UnsignedTx != got.UnsignedTx {
|
||||
return false
|
||||
}
|
||||
if !reflect.DeepEqual(expected.ForfeitTxs, got.ForfeitTxs) {
|
||||
return false
|
||||
}
|
||||
if !reflect.DeepEqual(expected.CongestionTree, got.CongestionTree) {
|
||||
return false
|
||||
}
|
||||
if !reflect.DeepEqual(expected.Connectors, got.Connectors) {
|
||||
return false
|
||||
}
|
||||
if expected.Version != got.Version {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
19
server/internal/infrastructure/db/types/types.go
Normal file
19
server/internal/infrastructure/db/types/types.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package dbtypes
|
||||
|
||||
import "github.com/ark-network/ark/internal/core/domain"
|
||||
|
||||
type EventStore interface {
|
||||
domain.RoundEventRepository
|
||||
RegisterEventsHandler(func(*domain.Round))
|
||||
Close()
|
||||
}
|
||||
|
||||
type RoundStore interface {
|
||||
domain.RoundRepository
|
||||
Close()
|
||||
}
|
||||
|
||||
type VtxoStore interface {
|
||||
domain.VtxoRepository
|
||||
Close()
|
||||
}
|
||||
30
server/internal/infrastructure/ocean-wallet/account.go
Normal file
30
server/internal/infrastructure/ocean-wallet/account.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package oceanwallet
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
pb "github.com/ark-network/ark/api-spec/protobuf/gen/ocean/v1"
|
||||
"github.com/vulpemventures/go-elements/address"
|
||||
)
|
||||
|
||||
func (s *service) DeriveAddresses(
|
||||
ctx context.Context, numOfAddresses int,
|
||||
) ([]string, error) {
|
||||
res, err := s.accountClient.DeriveAddresses(ctx, &pb.DeriveAddressesRequest{
|
||||
AccountName: accountLabel,
|
||||
NumOfAddresses: uint64(numOfAddresses),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addresses := make([]string, 0, numOfAddresses)
|
||||
for _, addr := range res.GetAddresses() {
|
||||
if isConf, _ := address.IsConfidential(addr); !isConf {
|
||||
addresses = append(addresses, addr)
|
||||
continue
|
||||
}
|
||||
info, _ := address.FromConfidential(addr)
|
||||
addresses = append(addresses, info.Address)
|
||||
}
|
||||
return addresses, nil
|
||||
}
|
||||
47
server/internal/infrastructure/ocean-wallet/notification.go
Normal file
47
server/internal/infrastructure/ocean-wallet/notification.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package oceanwallet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
|
||||
pb "github.com/ark-network/ark/api-spec/protobuf/gen/ocean/v1"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
func (s *service) WatchScripts(ctx context.Context, scripts []string) error {
|
||||
for _, script := range scripts {
|
||||
if _, err := s.notifyClient.WatchExternalScript(ctx, &pb.WatchExternalScriptRequest{
|
||||
Script: script,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *service) UnwatchScripts(ctx context.Context, scripts []string) error {
|
||||
for _, script := range scripts {
|
||||
scriptHash := calcScriptHash(script)
|
||||
if _, err := s.notifyClient.UnwatchExternalScript(ctx, &pb.UnwatchExternalScriptRequest{
|
||||
Label: scriptHash,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *service) GetNotificationChannel(ctx context.Context) chan []domain.VtxoKey {
|
||||
return s.chVtxos
|
||||
}
|
||||
|
||||
func calcScriptHash(script string) string {
|
||||
buf, _ := hex.DecodeString(script)
|
||||
hashedBuf := sha256.Sum256(buf)
|
||||
hash, _ := chainhash.NewHash(hashedBuf[:])
|
||||
return hash.String()
|
||||
}
|
||||
140
server/internal/infrastructure/ocean-wallet/service.go
Normal file
140
server/internal/infrastructure/ocean-wallet/service.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package oceanwallet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
pb "github.com/ark-network/ark/api-spec/protobuf/gen/ocean/v1"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type service struct {
|
||||
addr string
|
||||
conn *grpc.ClientConn
|
||||
walletClient pb.WalletServiceClient
|
||||
accountClient pb.AccountServiceClient
|
||||
txClient pb.TransactionServiceClient
|
||||
notifyClient pb.NotificationServiceClient
|
||||
chVtxos chan []domain.VtxoKey
|
||||
}
|
||||
|
||||
func NewService(addr string) (ports.WalletService, error) {
|
||||
conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
walletClient := pb.NewWalletServiceClient(conn)
|
||||
accountClient := pb.NewAccountServiceClient(conn)
|
||||
txClient := pb.NewTransactionServiceClient(conn)
|
||||
notifyClient := pb.NewNotificationServiceClient(conn)
|
||||
chVtxos := make(chan []domain.VtxoKey)
|
||||
svc := &service{
|
||||
addr: addr,
|
||||
conn: conn,
|
||||
walletClient: walletClient,
|
||||
accountClient: accountClient,
|
||||
txClient: txClient,
|
||||
notifyClient: notifyClient,
|
||||
chVtxos: chVtxos,
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
status, err := svc.Status(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !(status.IsInitialized() && status.IsUnlocked()) {
|
||||
return nil, fmt.Errorf("wallet must be already initialized and unlocked")
|
||||
}
|
||||
|
||||
// Create ark account at startup if needed.
|
||||
info, err := walletClient.GetInfo(ctx, &pb.GetInfoRequest{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, account := range info.GetAccounts() {
|
||||
if account.GetLabel() == accountLabel {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
if _, err := accountClient.CreateAccountBIP44(ctx, &pb.CreateAccountBIP44Request{
|
||||
Label: accountLabel,
|
||||
Unconfidential: true,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
go svc.listenToNotificaitons()
|
||||
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
func (s *service) Close() {
|
||||
close(s.chVtxos)
|
||||
s.conn.Close()
|
||||
}
|
||||
|
||||
func (s *service) listenToNotificaitons() {
|
||||
var stream pb.NotificationService_UtxosNotificationsClient
|
||||
var err error
|
||||
for {
|
||||
stream, err = s.notifyClient.UtxosNotifications(context.Background(), &pb.UtxosNotificationsRequest{})
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
for {
|
||||
msg, err := stream.Recv()
|
||||
if err != nil {
|
||||
if err == io.EOF || status.Convert(err).Code() == codes.Canceled {
|
||||
return
|
||||
}
|
||||
log.WithError(err).Warn("received unexpected error from source")
|
||||
return
|
||||
}
|
||||
|
||||
if msg.GetEventType() != pb.UtxoEventType_UTXO_EVENT_TYPE_NEW &&
|
||||
msg.GetEventType() != pb.UtxoEventType_UTXO_EVENT_TYPE_CONFIRMED {
|
||||
continue
|
||||
}
|
||||
vtxos := toVtxos(msg.GetUtxos())
|
||||
if len(vtxos) > 0 {
|
||||
go func() {
|
||||
s.chVtxos <- vtxos
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toVtxos(utxos []*pb.Utxo) []domain.VtxoKey {
|
||||
vtxos := make([]domain.VtxoKey, 0, len(utxos))
|
||||
for _, utxo := range utxos {
|
||||
// We want to notify for activity related to vtxos owner, therefore we skip
|
||||
// returning anything related to the internal accounts of the wallet, like
|
||||
// for example bip84-account0.
|
||||
if strings.HasPrefix(utxo.GetAccountName(), "bip") {
|
||||
continue
|
||||
}
|
||||
|
||||
vtxos = append(vtxos, domain.VtxoKey{
|
||||
Txid: utxo.GetTxid(),
|
||||
VOut: utxo.GetIndex(),
|
||||
})
|
||||
}
|
||||
return vtxos
|
||||
}
|
||||
270
server/internal/infrastructure/ocean-wallet/transaction.go
Normal file
270
server/internal/infrastructure/ocean-wallet/transaction.go
Normal file
@@ -0,0 +1,270 @@
|
||||
package oceanwallet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
pb "github.com/ark-network/ark/api-spec/protobuf/gen/ocean/v1"
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/vulpemventures/go-elements/elementsutil"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
)
|
||||
|
||||
const (
|
||||
zero32 = "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
)
|
||||
|
||||
func (s *service) SignPset(
|
||||
ctx context.Context, pset string, extractRawTx bool,
|
||||
) (string, error) {
|
||||
res, err := s.txClient.SignPset(ctx, &pb.SignPsetRequest{
|
||||
Pset: pset,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
signedPset := res.GetPset()
|
||||
|
||||
if !extractRawTx {
|
||||
return signedPset, nil
|
||||
}
|
||||
|
||||
ptx, err := psetv2.NewPsetFromBase64(signedPset)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := psetv2.MaybeFinalizeAll(ptx); err != nil {
|
||||
return "", fmt.Errorf("failed to finalize signed pset: %s", err)
|
||||
}
|
||||
|
||||
extractedTx, err := psetv2.Extract(ptx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to extract signed pset: %s", err)
|
||||
}
|
||||
|
||||
txHex, err := extractedTx.ToHex()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to convert extracted tx to hex: %s", err)
|
||||
}
|
||||
|
||||
return txHex, nil
|
||||
}
|
||||
|
||||
func (s *service) SelectUtxos(ctx context.Context, asset string, amount uint64) ([]ports.TxInput, uint64, error) {
|
||||
res, err := s.txClient.SelectUtxos(ctx, &pb.SelectUtxosRequest{
|
||||
AccountName: accountLabel,
|
||||
TargetAsset: asset,
|
||||
TargetAmount: amount,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
inputs := make([]ports.TxInput, 0, len(res.GetUtxos()))
|
||||
for _, utxo := range res.GetUtxos() {
|
||||
// check that the utxos are not confidential
|
||||
if utxo.GetAssetBlinder() != zero32 || utxo.GetValueBlinder() != zero32 {
|
||||
return nil, 0, fmt.Errorf("utxo is confidential")
|
||||
}
|
||||
|
||||
inputs = append(inputs, utxo)
|
||||
}
|
||||
|
||||
return inputs, res.GetChange(), nil
|
||||
}
|
||||
|
||||
func (s *service) GetTransaction(
|
||||
ctx context.Context, txid string,
|
||||
) (string, int64, error) {
|
||||
res, err := s.txClient.GetTransaction(ctx, &pb.GetTransactionRequest{
|
||||
Txid: txid,
|
||||
})
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
if res.GetBlockDetails().GetTimestamp() > 0 {
|
||||
return res.GetTxHex(), res.BlockDetails.GetTimestamp(), nil
|
||||
}
|
||||
|
||||
// if not confirmed, we return now + 30 secs to estimate the next blocktime
|
||||
return res.GetTxHex(), time.Now().Unix() + 30, nil
|
||||
}
|
||||
|
||||
func (s *service) BroadcastTransaction(
|
||||
ctx context.Context, txHex string,
|
||||
) (string, error) {
|
||||
res, err := s.txClient.BroadcastTransaction(
|
||||
ctx, &pb.BroadcastTransactionRequest{
|
||||
TxHex: txHex,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "non-BIP68-final") {
|
||||
return "", fmt.Errorf("non-BIP68-final")
|
||||
}
|
||||
|
||||
return "", err
|
||||
}
|
||||
return res.GetTxid(), nil
|
||||
}
|
||||
|
||||
func (s *service) IsTransactionPublished(
|
||||
ctx context.Context, txid string,
|
||||
) (bool, int64, error) {
|
||||
_, blocktime, err := s.GetTransaction(ctx, txid)
|
||||
if err != nil {
|
||||
if strings.Contains(strings.ToLower(err.Error()), "missing transaction") {
|
||||
return false, 0, nil
|
||||
}
|
||||
return false, 0, err
|
||||
}
|
||||
|
||||
return true, blocktime, nil
|
||||
}
|
||||
|
||||
func (s *service) SignPsetWithKey(ctx context.Context, b64 string, indexes []int) (string, error) {
|
||||
pset, err := psetv2.NewPsetFromBase64(b64)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if indexes == nil {
|
||||
for i := 0; i < len(pset.Inputs); i++ {
|
||||
indexes = append(indexes, i)
|
||||
}
|
||||
}
|
||||
|
||||
key, masterKey, err := s.getPubkey(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fingerprint := binary.LittleEndian.Uint32(masterKey.FingerPrint)
|
||||
extendedKey, err := masterKey.Serialize()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
pset.Global.Xpubs = []psetv2.Xpub{{
|
||||
ExtendedKey: extendedKey[:len(extendedKey)-4],
|
||||
MasterFingerprint: fingerprint,
|
||||
DerivationPath: derivationPath,
|
||||
}}
|
||||
|
||||
updater, err := psetv2.NewUpdater(pset)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
bip32derivation := psetv2.DerivationPathWithPubKey{
|
||||
PubKey: key.SerializeCompressed(),
|
||||
MasterKeyFingerprint: fingerprint,
|
||||
Bip32Path: derivationPath,
|
||||
}
|
||||
|
||||
for _, i := range indexes {
|
||||
if len(pset.Inputs[i].TapLeafScript) == 0 {
|
||||
return "", fmt.Errorf("no tap leaf script found for input %d", i)
|
||||
}
|
||||
|
||||
leafHash := pset.Inputs[i].TapLeafScript[0].TapHash()
|
||||
|
||||
if err := updater.AddInTapBip32Derivation(i, psetv2.TapDerivationPathWithPubKey{
|
||||
DerivationPathWithPubKey: bip32derivation,
|
||||
LeafHashes: [][]byte{leafHash[:]},
|
||||
}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := updater.AddInSighashType(i, txscript.SigHashDefault); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
unsignedPset, err := pset.ToBase64()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
signedPset, err := s.txClient.SignPsetWithSchnorrKey(ctx, &pb.SignPsetWithSchnorrKeyRequest{
|
||||
Tx: unsignedPset,
|
||||
SighashType: uint32(txscript.SigHashDefault),
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return signedPset.GetSignedTx(), nil
|
||||
}
|
||||
|
||||
func (s *service) EstimateFees(
|
||||
ctx context.Context, pset string,
|
||||
) (uint64, error) {
|
||||
tx, err := psetv2.NewPsetFromBase64(pset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
inputs := make([]*pb.Input, 0, len(tx.Inputs))
|
||||
outputs := make([]*pb.Output, 0, len(tx.Outputs))
|
||||
|
||||
for _, in := range tx.Inputs {
|
||||
pbInput := &pb.Input{
|
||||
Txid: chainhash.Hash(in.PreviousTxid).String(),
|
||||
Index: in.PreviousTxIndex,
|
||||
}
|
||||
|
||||
if len(in.TapLeafScript) == 1 {
|
||||
isSweep, _, _, err := tree.DecodeSweepScript(in.TapLeafScript[0].Script)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if isSweep {
|
||||
pbInput.WitnessSize = 64
|
||||
pbInput.ScriptsigSize = 0
|
||||
}
|
||||
} else {
|
||||
if in.WitnessUtxo == nil {
|
||||
return 0, fmt.Errorf("missing witness utxo, cannot estimate fees")
|
||||
}
|
||||
|
||||
pbInput.Script = hex.EncodeToString(in.WitnessUtxo.Script)
|
||||
}
|
||||
|
||||
inputs = append(inputs, pbInput)
|
||||
}
|
||||
|
||||
for _, out := range tx.Outputs {
|
||||
outputs = append(outputs, &pb.Output{
|
||||
Asset: elementsutil.AssetHashFromBytes(
|
||||
append([]byte{0x01}, out.Asset...),
|
||||
),
|
||||
Amount: out.Value,
|
||||
Script: hex.EncodeToString(out.Script),
|
||||
})
|
||||
}
|
||||
|
||||
fee, err := s.txClient.EstimateFees(
|
||||
ctx,
|
||||
&pb.EstimateFeesRequest{
|
||||
Inputs: inputs,
|
||||
Outputs: outputs,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to estimate fees: %s", err)
|
||||
}
|
||||
|
||||
// we add 5 sats in order to avoid min-relay-fee not met errors
|
||||
return fee.GetFeeAmount() + 5, nil
|
||||
}
|
||||
95
server/internal/infrastructure/ocean-wallet/wallet.go
Normal file
95
server/internal/infrastructure/ocean-wallet/wallet.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package oceanwallet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
pb "github.com/ark-network/ark/api-spec/protobuf/gen/ocean/v1"
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
"github.com/btcsuite/btcd/btcutil/hdkeychain"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/vulpemventures/go-bip32"
|
||||
)
|
||||
|
||||
const accountLabel = "ark"
|
||||
|
||||
var derivationPath = []uint32{0, 0}
|
||||
|
||||
func (s *service) GetPubkey(ctx context.Context) (*secp256k1.PublicKey, error) {
|
||||
key, _, err := s.getPubkey(ctx)
|
||||
return key, err
|
||||
}
|
||||
|
||||
func (s *service) Status(
|
||||
ctx context.Context,
|
||||
) (ports.WalletStatus, error) {
|
||||
res, err := s.walletClient.Status(ctx, &pb.StatusRequest{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return walletStatus{res}, nil
|
||||
}
|
||||
|
||||
type walletStatus struct {
|
||||
*pb.StatusResponse
|
||||
}
|
||||
|
||||
func (w walletStatus) IsInitialized() bool {
|
||||
return w.StatusResponse.GetInitialized()
|
||||
}
|
||||
func (w walletStatus) IsUnlocked() bool {
|
||||
return w.StatusResponse.GetUnlocked()
|
||||
}
|
||||
func (w walletStatus) IsSynced() bool {
|
||||
return w.StatusResponse.GetSynced()
|
||||
}
|
||||
|
||||
func (s *service) findAccount(ctx context.Context, label string) (*pb.AccountInfo, error) {
|
||||
res, err := s.walletClient.GetInfo(ctx, &pb.GetInfoRequest{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(res.GetAccounts()) <= 0 {
|
||||
return nil, fmt.Errorf("wallet is locked")
|
||||
}
|
||||
|
||||
for _, account := range res.GetAccounts() {
|
||||
if account.GetLabel() == label {
|
||||
return account, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("account not found")
|
||||
}
|
||||
|
||||
func (s *service) getPubkey(ctx context.Context) (*secp256k1.PublicKey, *bip32.Key, error) {
|
||||
account, err := s.findAccount(ctx, accountLabel)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
xpub := account.GetXpubs()[0]
|
||||
|
||||
node, err := hdkeychain.NewKeyFromString(xpub)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, i := range derivationPath {
|
||||
node, err = node.Derive(i)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
key, err := node.ECPubKey()
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
masterKey, err := bip32.B58Deserialize(xpub)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return key, masterKey, nil
|
||||
}
|
||||
45
server/internal/infrastructure/scheduler/gocron/service.go
Normal file
45
server/internal/infrastructure/scheduler/gocron/service.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package scheduler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
"github.com/go-co-op/gocron"
|
||||
)
|
||||
|
||||
type service struct {
|
||||
scheduler *gocron.Scheduler
|
||||
}
|
||||
|
||||
func NewScheduler() ports.SchedulerService {
|
||||
svc := gocron.NewScheduler(time.UTC)
|
||||
return &service{svc}
|
||||
}
|
||||
|
||||
func (s *service) Start() {
|
||||
s.scheduler.StartAsync()
|
||||
}
|
||||
|
||||
func (s *service) Stop() {
|
||||
s.scheduler.Stop()
|
||||
}
|
||||
|
||||
func (s *service) ScheduleTask(interval int64, immediate bool, task func()) error {
|
||||
if immediate {
|
||||
_, err := s.scheduler.Every(int(interval)).Seconds().Do(task)
|
||||
return err
|
||||
}
|
||||
_, err := s.scheduler.Every(int(interval)).Seconds().WaitForSchedule().Do(task)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *service) ScheduleTaskOnce(at int64, task func()) error {
|
||||
delay := at - time.Now().Unix()
|
||||
if delay < 0 {
|
||||
return fmt.Errorf("cannot schedule task in the past")
|
||||
}
|
||||
|
||||
_, err := s.scheduler.Every(int(delay)).Seconds().WaitForSchedule().LimitRunsTo(1).Do(task)
|
||||
return err
|
||||
}
|
||||
483
server/internal/infrastructure/tx-builder/covenant/builder.go
Normal file
483
server/internal/infrastructure/tx-builder/covenant/builder.go
Normal file
@@ -0,0 +1,483 @@
|
||||
package txbuilder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/vulpemventures/go-elements/address"
|
||||
"github.com/vulpemventures/go-elements/network"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
"github.com/vulpemventures/go-elements/taproot"
|
||||
)
|
||||
|
||||
const (
|
||||
connectorAmount = uint64(450)
|
||||
dustLimit = uint64(450)
|
||||
)
|
||||
|
||||
type txBuilder struct {
|
||||
wallet ports.WalletService
|
||||
net *network.Network
|
||||
roundLifetime int64 // in seconds
|
||||
}
|
||||
|
||||
func NewTxBuilder(
|
||||
wallet ports.WalletService, net network.Network, roundLifetime int64,
|
||||
) ports.TxBuilder {
|
||||
return &txBuilder{wallet, &net, roundLifetime}
|
||||
}
|
||||
|
||||
func (b *txBuilder) GetVtxoScript(userPubkey, aspPubkey *secp256k1.PublicKey) ([]byte, error) {
|
||||
outputScript, _, err := b.getLeafScriptAndTree(userPubkey, aspPubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outputScript, nil
|
||||
}
|
||||
|
||||
func (b *txBuilder) BuildSweepTx(wallet ports.WalletService, inputs []ports.SweepInput) (signedSweepTx string, err error) {
|
||||
sweepPset, err := sweepTransaction(
|
||||
wallet,
|
||||
inputs,
|
||||
b.net.AssetID,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
sweepPsetBase64, err := sweepPset.ToBase64()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
signedSweepPsetB64, err := wallet.SignPsetWithKey(ctx, sweepPsetBase64, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
signedPset, err := psetv2.NewPsetFromBase64(signedSweepPsetB64)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := psetv2.FinalizeAll(signedPset); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
extractedTx, err := psetv2.Extract(signedPset)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return extractedTx.ToHex()
|
||||
}
|
||||
|
||||
func (b *txBuilder) BuildForfeitTxs(
|
||||
aspPubkey *secp256k1.PublicKey, poolTx string, payments []domain.Payment,
|
||||
) (connectors []string, forfeitTxs []string, err error) {
|
||||
connectorTxs, err := b.createConnectors(poolTx, payments, aspPubkey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
forfeitTxs, err = b.createForfeitTxs(aspPubkey, payments, connectorTxs)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for _, tx := range connectorTxs {
|
||||
buf, _ := tx.ToBase64()
|
||||
connectors = append(connectors, buf)
|
||||
}
|
||||
return connectors, forfeitTxs, nil
|
||||
}
|
||||
|
||||
func (b *txBuilder) BuildPoolTx(
|
||||
aspPubkey *secp256k1.PublicKey, payments []domain.Payment, minRelayFee uint64,
|
||||
) (poolTx string, congestionTree tree.CongestionTree, err error) {
|
||||
// The creation of the tree and the pool tx are tightly coupled:
|
||||
// - building the tree requires knowing the shared outpoint (txid:vout)
|
||||
// - building the pool tx requires knowing the shared output script and amount
|
||||
// The idea here is to first create all the data for the outputs of the txs
|
||||
// of the congestion tree to calculate the shared output script and amount.
|
||||
// With these data the pool tx can be created, and once the shared utxo
|
||||
// outpoint is obtained, the congestion tree can be finally created.
|
||||
// The factory function `treeFactoryFn` returned below holds all outputs data
|
||||
// generated in the process and takes the shared utxo outpoint as argument.
|
||||
// This is safe as the memory allocated for `craftCongestionTree` is freed
|
||||
// only after `BuildPoolTx` returns.
|
||||
treeFactoryFn, sharedOutputScript, sharedOutputAmount, err := craftCongestionTree(
|
||||
b.net.AssetID, aspPubkey, payments, minRelayFee, b.roundLifetime,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ptx, err := b.createPoolTx(
|
||||
sharedOutputAmount, sharedOutputScript, payments, aspPubkey,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
unsignedTx, err := ptx.UnsignedTx()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tree, err := treeFactoryFn(psetv2.InputArgs{
|
||||
Txid: unsignedTx.TxHash().String(),
|
||||
TxIndex: 0,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
poolTx, err = ptx.ToBase64()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
congestionTree = tree
|
||||
return
|
||||
}
|
||||
|
||||
func (b *txBuilder) GetLeafSweepClosure(
|
||||
node tree.Node, userPubKey *secp256k1.PublicKey,
|
||||
) (*psetv2.TapLeafScript, int64, error) {
|
||||
if !node.Leaf {
|
||||
return nil, 0, fmt.Errorf("node is not a leaf")
|
||||
}
|
||||
|
||||
pset, err := psetv2.NewPsetFromBase64(node.Tx)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
input := pset.Inputs[0]
|
||||
|
||||
sweepLeaf, lifetime, err := extractSweepLeaf(input)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// craft the vtxo taproot tree
|
||||
vtxoScript, err := tree.VtxoScript(userPubKey)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
vtxoTaprootTree := taproot.AssembleTaprootScriptTree(
|
||||
*vtxoScript,
|
||||
sweepLeaf.TapElementsLeaf,
|
||||
)
|
||||
|
||||
proofIndex := vtxoTaprootTree.LeafProofIndex[sweepLeaf.TapHash()]
|
||||
proof := vtxoTaprootTree.LeafMerkleProofs[proofIndex]
|
||||
|
||||
return &psetv2.TapLeafScript{
|
||||
TapElementsLeaf: proof.TapElementsLeaf,
|
||||
ControlBlock: proof.ToControlBlock(sweepLeaf.ControlBlock.InternalKey),
|
||||
}, lifetime, nil
|
||||
}
|
||||
|
||||
func (b *txBuilder) getLeafScriptAndTree(
|
||||
userPubkey, aspPubkey *secp256k1.PublicKey,
|
||||
) ([]byte, *taproot.IndexedElementsTapScriptTree, error) {
|
||||
redeemClosure, err := tree.VtxoScript(userPubkey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
sweepClosure, err := tree.SweepScript(aspPubkey, uint(b.roundLifetime))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
taprootTree := taproot.AssembleTaprootScriptTree(
|
||||
*redeemClosure, *sweepClosure,
|
||||
)
|
||||
|
||||
root := taprootTree.RootNode.TapHash()
|
||||
unspendableKey := tree.UnspendableKey()
|
||||
taprootKey := taproot.ComputeTaprootOutputKey(unspendableKey, root[:])
|
||||
|
||||
outputScript, err := taprootOutputScript(taprootKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return outputScript, taprootTree, nil
|
||||
}
|
||||
|
||||
func (b *txBuilder) createPoolTx(
|
||||
sharedOutputAmount uint64, sharedOutputScript []byte,
|
||||
payments []domain.Payment, aspPubKey *secp256k1.PublicKey,
|
||||
) (*psetv2.Pset, error) {
|
||||
aspScript, err := p2wpkhScript(aspPubKey, b.net)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
receivers := getOnchainReceivers(payments)
|
||||
connectorsAmount := connectorAmount * countSpentVtxos(payments)
|
||||
targetAmount := sharedOutputAmount + connectorsAmount
|
||||
|
||||
outputs := []psetv2.OutputArgs{
|
||||
{
|
||||
Asset: b.net.AssetID,
|
||||
Amount: sharedOutputAmount,
|
||||
Script: sharedOutputScript,
|
||||
},
|
||||
{
|
||||
Asset: b.net.AssetID,
|
||||
Amount: connectorsAmount,
|
||||
Script: aspScript,
|
||||
},
|
||||
}
|
||||
|
||||
for _, receiver := range receivers {
|
||||
targetAmount += receiver.Amount
|
||||
|
||||
receiverScript, err := address.ToOutputScript(receiver.OnchainAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputs = append(outputs, psetv2.OutputArgs{
|
||||
Asset: b.net.AssetID,
|
||||
Amount: receiver.Amount,
|
||||
Script: receiverScript,
|
||||
})
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
utxos, change, err := b.wallet.SelectUtxos(ctx, b.net.AssetID, targetAmount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var dust uint64
|
||||
if change > 0 {
|
||||
if change < dustLimit {
|
||||
dust = change
|
||||
change = 0
|
||||
} else {
|
||||
outputs = append(outputs, psetv2.OutputArgs{
|
||||
Asset: b.net.AssetID,
|
||||
Amount: change,
|
||||
Script: aspScript,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ptx, err := psetv2.New(nil, outputs, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updater, err := psetv2.NewUpdater(ptx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := addInputs(updater, utxos); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b64, err := ptx.ToBase64()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
feeAmount, err := b.wallet.EstimateFees(ctx, b64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dust > feeAmount {
|
||||
feeAmount = dust
|
||||
} else {
|
||||
feeAmount += dust
|
||||
}
|
||||
|
||||
if dust == 0 {
|
||||
if feeAmount == change {
|
||||
// fees = change, remove change output
|
||||
ptx.Outputs = ptx.Outputs[:len(ptx.Outputs)-1]
|
||||
} else if feeAmount < change {
|
||||
// change covers the fees, reduce change amount
|
||||
ptx.Outputs[len(ptx.Outputs)-1].Value = change - feeAmount
|
||||
} else {
|
||||
// change is not enough to cover fees, re-select utxos
|
||||
if change > 0 {
|
||||
// remove change output if present
|
||||
ptx.Outputs = ptx.Outputs[:len(ptx.Outputs)-1]
|
||||
}
|
||||
newUtxos, change, err := b.wallet.SelectUtxos(ctx, b.net.AssetID, feeAmount-change)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if change > 0 {
|
||||
if err := updater.AddOutputs([]psetv2.OutputArgs{
|
||||
{
|
||||
Asset: b.net.AssetID,
|
||||
Amount: change,
|
||||
Script: aspScript,
|
||||
},
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := addInputs(updater, newUtxos); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add fee output
|
||||
if err := updater.AddOutputs([]psetv2.OutputArgs{
|
||||
{
|
||||
Asset: b.net.AssetID,
|
||||
Amount: feeAmount,
|
||||
},
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ptx, nil
|
||||
}
|
||||
|
||||
func (b *txBuilder) createConnectors(
|
||||
poolTx string, payments []domain.Payment, aspPubkey *secp256k1.PublicKey,
|
||||
) ([]*psetv2.Pset, error) {
|
||||
txid, _ := getTxid(poolTx)
|
||||
|
||||
aspScript, err := p2wpkhScript(aspPubkey, b.net)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
connectorOutput := psetv2.OutputArgs{
|
||||
Asset: b.net.AssetID,
|
||||
Script: aspScript,
|
||||
Amount: connectorAmount,
|
||||
}
|
||||
|
||||
numberOfConnectors := countSpentVtxos(payments)
|
||||
|
||||
previousInput := psetv2.InputArgs{
|
||||
Txid: txid,
|
||||
TxIndex: 1,
|
||||
}
|
||||
|
||||
if numberOfConnectors == 1 {
|
||||
outputs := []psetv2.OutputArgs{connectorOutput}
|
||||
connectorTx, err := craftConnectorTx(previousInput, outputs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []*psetv2.Pset{connectorTx}, nil
|
||||
}
|
||||
|
||||
totalConnectorAmount := connectorAmount * numberOfConnectors
|
||||
|
||||
connectors := make([]*psetv2.Pset, 0, numberOfConnectors-1)
|
||||
for i := uint64(0); i < numberOfConnectors-1; i++ {
|
||||
outputs := []psetv2.OutputArgs{connectorOutput}
|
||||
totalConnectorAmount -= connectorAmount
|
||||
if totalConnectorAmount > 0 {
|
||||
outputs = append(outputs, psetv2.OutputArgs{
|
||||
Asset: b.net.AssetID,
|
||||
Script: aspScript,
|
||||
Amount: totalConnectorAmount,
|
||||
})
|
||||
}
|
||||
connectorTx, err := craftConnectorTx(previousInput, outputs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txid, _ := getPsetId(connectorTx)
|
||||
|
||||
previousInput = psetv2.InputArgs{
|
||||
Txid: txid,
|
||||
TxIndex: 1,
|
||||
}
|
||||
|
||||
connectors = append(connectors, connectorTx)
|
||||
}
|
||||
|
||||
return connectors, nil
|
||||
}
|
||||
|
||||
func (b *txBuilder) createForfeitTxs(
|
||||
aspPubkey *secp256k1.PublicKey, payments []domain.Payment, connectors []*psetv2.Pset,
|
||||
) ([]string, error) {
|
||||
aspScript, err := p2wpkhScript(aspPubkey, b.net)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
forfeitTxs := make([]string, 0)
|
||||
for _, payment := range payments {
|
||||
for _, vtxo := range payment.Inputs {
|
||||
pubkeyBytes, err := hex.DecodeString(vtxo.Pubkey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode pubkey: %s", err)
|
||||
}
|
||||
|
||||
vtxoPubkey, err := secp256k1.ParsePubKey(pubkeyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vtxoScript, vtxoTaprootTree, err := b.getLeafScriptAndTree(vtxoPubkey, aspPubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, connector := range connectors {
|
||||
txs, err := craftForfeitTxs(
|
||||
connector, vtxo, vtxoTaprootTree, vtxoScript, aspScript,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
forfeitTxs = append(forfeitTxs, txs...)
|
||||
}
|
||||
}
|
||||
}
|
||||
return forfeitTxs, nil
|
||||
}
|
||||
|
||||
// given a congestion tree input, searches and returns the sweep leaf and its lifetime in seconds
|
||||
func extractSweepLeaf(input psetv2.Input) (sweepLeaf *psetv2.TapLeafScript, lifetime int64, err error) {
|
||||
for _, leaf := range input.TapLeafScript {
|
||||
isSweep, _, seconds, err := tree.DecodeSweepScript(leaf.Script)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if isSweep {
|
||||
lifetime = int64(seconds)
|
||||
sweepLeaf = &leaf
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if sweepLeaf == nil {
|
||||
return nil, 0, fmt.Errorf("sweep leaf not found")
|
||||
}
|
||||
|
||||
return sweepLeaf, lifetime, nil
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
package txbuilder_test
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/ark-network/ark/common"
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
txbuilder "github.com/ark-network/ark/internal/infrastructure/tx-builder/covenant"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/vulpemventures/go-elements/network"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
)
|
||||
|
||||
const (
|
||||
testingKey = "apub1qgvdtj5ttpuhkldavhq8thtm5auyk0ec4dcmrfdgu0u5hgp9we22v3hrs4x"
|
||||
minRelayFee = uint64(30)
|
||||
roundLifetime = int64(1209344)
|
||||
)
|
||||
|
||||
var (
|
||||
wallet *mockedWallet
|
||||
pubkey *secp256k1.PublicKey
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
wallet = &mockedWallet{}
|
||||
wallet.On("EstimateFees", mock.Anything, mock.Anything).
|
||||
Return(uint64(100), nil)
|
||||
wallet.On("SelectUtxos", mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(randomInput, uint64(0), nil)
|
||||
|
||||
_, pubkey, _ = common.DecodePubKey(testingKey)
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestBuildPoolTx(t *testing.T) {
|
||||
builder := txbuilder.NewTxBuilder(wallet, network.Liquid, roundLifetime)
|
||||
|
||||
fixtures, err := parsePoolTxFixtures()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, fixtures)
|
||||
|
||||
if len(fixtures.Valid) > 0 {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
for _, f := range fixtures.Valid {
|
||||
poolTx, congestionTree, err := builder.BuildPoolTx(pubkey, f.Payments, minRelayFee)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, poolTx)
|
||||
require.NotEmpty(t, congestionTree)
|
||||
require.Equal(t, f.ExpectedNumOfNodes, congestionTree.NumberOfNodes())
|
||||
require.Len(t, congestionTree.Leaves(), f.ExpectedNumOfLeaves)
|
||||
|
||||
err = tree.ValidateCongestionTree(congestionTree, poolTx, pubkey, roundLifetime)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if len(fixtures.Invalid) > 0 {
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
for _, f := range fixtures.Invalid {
|
||||
poolTx, congestionTree, err := builder.BuildPoolTx(pubkey, f.Payments, minRelayFee)
|
||||
require.EqualError(t, err, f.ExpectedErr)
|
||||
require.Empty(t, poolTx)
|
||||
require.Empty(t, congestionTree)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildForfeitTxs(t *testing.T) {
|
||||
builder := txbuilder.NewTxBuilder(wallet, network.Liquid, 1209344)
|
||||
|
||||
fixtures, err := parseForfeitTxsFixtures()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, fixtures)
|
||||
|
||||
if len(fixtures.Valid) > 0 {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
for _, f := range fixtures.Valid {
|
||||
connectors, forfeitTxs, err := builder.BuildForfeitTxs(
|
||||
pubkey, f.PoolTx, f.Payments,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, connectors, f.ExpectedNumOfConnectors)
|
||||
require.Len(t, forfeitTxs, f.ExpectedNumOfForfeitTxs)
|
||||
|
||||
expectedInputTxid := f.PoolTxid
|
||||
// Verify the chain of connectors
|
||||
for _, connector := range connectors {
|
||||
tx, err := psetv2.NewPsetFromBase64(connector)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, tx)
|
||||
|
||||
require.Len(t, tx.Inputs, 1)
|
||||
require.Len(t, tx.Outputs, 2)
|
||||
|
||||
inputTxid := chainhash.Hash(tx.Inputs[0].PreviousTxid).String()
|
||||
require.Equal(t, expectedInputTxid, inputTxid)
|
||||
require.Equal(t, 1, int(tx.Inputs[0].PreviousTxIndex))
|
||||
|
||||
expectedInputTxid = getTxid(tx)
|
||||
}
|
||||
|
||||
// decode and check forfeit txs
|
||||
for _, forfeitTx := range forfeitTxs {
|
||||
tx, err := psetv2.NewPsetFromBase64(forfeitTx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, tx.Inputs, 2)
|
||||
require.Len(t, tx.Outputs, 2)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if len(fixtures.Invalid) > 0 {
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
for _, f := range fixtures.Invalid {
|
||||
connectors, forfeitTxs, err := builder.BuildForfeitTxs(
|
||||
pubkey, f.PoolTx, f.Payments,
|
||||
)
|
||||
require.EqualError(t, err, f.ExpectedErr)
|
||||
require.Empty(t, connectors)
|
||||
require.Empty(t, forfeitTxs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func randomInput() []ports.TxInput {
|
||||
txid := randomHex(32)
|
||||
input := &mockedInput{}
|
||||
input.On("GetAsset").Return("5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225")
|
||||
input.On("GetValue").Return(uint64(1000))
|
||||
input.On("GetScript").Return("a914ea9f486e82efb3dd83a69fd96e3f0113757da03c87")
|
||||
input.On("GetTxid").Return(txid)
|
||||
input.On("GetIndex").Return(uint32(0))
|
||||
|
||||
return []ports.TxInput{input}
|
||||
}
|
||||
|
||||
func randomHex(len int) string {
|
||||
buf := make([]byte, len)
|
||||
// nolint
|
||||
rand.Read(buf)
|
||||
return hex.EncodeToString(buf)
|
||||
}
|
||||
|
||||
type poolTxFixtures struct {
|
||||
Valid []struct {
|
||||
Payments []domain.Payment
|
||||
ExpectedNumOfNodes int
|
||||
ExpectedNumOfLeaves int
|
||||
}
|
||||
Invalid []struct {
|
||||
Payments []domain.Payment
|
||||
ExpectedErr string
|
||||
}
|
||||
}
|
||||
|
||||
func parsePoolTxFixtures() (*poolTxFixtures, error) {
|
||||
file, err := os.ReadFile("testdata/fixtures.json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v := map[string]interface{}{}
|
||||
if err := json.Unmarshal(file, &v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vv := v["buildPoolTx"].(map[string]interface{})
|
||||
file, _ = json.Marshal(vv)
|
||||
var fixtures poolTxFixtures
|
||||
if err := json.Unmarshal(file, &fixtures); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &fixtures, nil
|
||||
}
|
||||
|
||||
type forfeitTxsFixtures struct {
|
||||
Valid []struct {
|
||||
Payments []domain.Payment
|
||||
ExpectedNumOfConnectors int
|
||||
ExpectedNumOfForfeitTxs int
|
||||
PoolTx string
|
||||
PoolTxid string
|
||||
}
|
||||
Invalid []struct {
|
||||
Payments []domain.Payment
|
||||
ExpectedErr string
|
||||
PoolTx string
|
||||
}
|
||||
}
|
||||
|
||||
func parseForfeitTxsFixtures() (*forfeitTxsFixtures, error) {
|
||||
file, err := os.ReadFile("testdata/fixtures.json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v := map[string]interface{}{}
|
||||
if err := json.Unmarshal(file, &v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vv := v["buildForfeitTxs"].(map[string]interface{})
|
||||
file, _ = json.Marshal(vv)
|
||||
var fixtures forfeitTxsFixtures
|
||||
if err := json.Unmarshal(file, &fixtures); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &fixtures, nil
|
||||
}
|
||||
|
||||
func getTxid(tx *psetv2.Pset) string {
|
||||
utx, _ := tx.UnsignedTx()
|
||||
return utx.TxHash().String()
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package txbuilder
|
||||
|
||||
import (
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
"github.com/vulpemventures/go-elements/transaction"
|
||||
)
|
||||
|
||||
func craftConnectorTx(
|
||||
input psetv2.InputArgs, outputs []psetv2.OutputArgs,
|
||||
) (*psetv2.Pset, error) {
|
||||
ptx, _ := psetv2.New(nil, nil, nil)
|
||||
updater, _ := psetv2.NewUpdater(ptx)
|
||||
|
||||
if err := updater.AddInputs(
|
||||
[]psetv2.InputArgs{input},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: add prevout.
|
||||
|
||||
if err := updater.AddOutputs(outputs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ptx, nil
|
||||
}
|
||||
|
||||
func getConnectorInputs(pset *psetv2.Pset) ([]psetv2.InputArgs, []*transaction.TxOutput) {
|
||||
txID, _ := getPsetId(pset)
|
||||
|
||||
inputs := make([]psetv2.InputArgs, 0, len(pset.Outputs))
|
||||
witnessUtxos := make([]*transaction.TxOutput, 0, len(pset.Outputs))
|
||||
|
||||
for i, output := range pset.Outputs {
|
||||
utx, _ := pset.UnsignedTx()
|
||||
|
||||
if output.Value == connectorAmount && len(output.Script) > 0 {
|
||||
inputs = append(inputs, psetv2.InputArgs{
|
||||
Txid: txID,
|
||||
TxIndex: uint32(i),
|
||||
})
|
||||
witnessUtxos = append(witnessUtxos, utx.Outputs[i])
|
||||
}
|
||||
}
|
||||
|
||||
return inputs, witnessUtxos
|
||||
}
|
||||
104
server/internal/infrastructure/tx-builder/covenant/forfeit.go
Normal file
104
server/internal/infrastructure/tx-builder/covenant/forfeit.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package txbuilder
|
||||
|
||||
import (
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/vulpemventures/go-elements/elementsutil"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
"github.com/vulpemventures/go-elements/taproot"
|
||||
"github.com/vulpemventures/go-elements/transaction"
|
||||
)
|
||||
|
||||
func craftForfeitTxs(
|
||||
connectorTx *psetv2.Pset,
|
||||
vtxo domain.Vtxo,
|
||||
vtxoTaprootTree *taproot.IndexedElementsTapScriptTree,
|
||||
vtxoScript, aspScript []byte,
|
||||
) (forfeitTxs []string, err error) {
|
||||
connectors, prevouts := getConnectorInputs(connectorTx)
|
||||
|
||||
for i, connectorInput := range connectors {
|
||||
connectorPrevout := prevouts[i]
|
||||
asset := elementsutil.AssetHashFromBytes(connectorPrevout.Asset)
|
||||
|
||||
pset, err := psetv2.New(nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updater, err := psetv2.NewUpdater(pset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vtxoInput := psetv2.InputArgs{
|
||||
Txid: vtxo.Txid,
|
||||
TxIndex: vtxo.VOut,
|
||||
}
|
||||
|
||||
vtxoAmount, _ := elementsutil.ValueToBytes(vtxo.Amount)
|
||||
vtxoPrevout := &transaction.TxOutput{
|
||||
Asset: connectorPrevout.Asset,
|
||||
Value: vtxoAmount,
|
||||
Script: vtxoScript,
|
||||
}
|
||||
|
||||
if err := updater.AddInputs([]psetv2.InputArgs{connectorInput, vtxoInput}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = updater.AddInWitnessUtxo(0, connectorPrevout); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := updater.AddInSighashType(0, txscript.SigHashAll); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = updater.AddInWitnessUtxo(1, vtxoPrevout); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := updater.AddInSighashType(1, txscript.SigHashDefault); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
unspendableKey := tree.UnspendableKey()
|
||||
|
||||
for _, proof := range vtxoTaprootTree.LeafMerkleProofs {
|
||||
tapScript := psetv2.NewTapLeafScript(proof, unspendableKey)
|
||||
if err := updater.AddInTapLeafScript(1, tapScript); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
connectorAmount, err := elementsutil.ValueFromBytes(connectorPrevout.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = updater.AddOutputs([]psetv2.OutputArgs{
|
||||
{
|
||||
Asset: asset,
|
||||
Amount: vtxo.Amount + connectorAmount - 30,
|
||||
Script: aspScript,
|
||||
},
|
||||
{
|
||||
Asset: asset,
|
||||
Amount: 30,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tx, err := pset.ToBase64()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
forfeitTxs = append(forfeitTxs, tx)
|
||||
}
|
||||
return forfeitTxs, nil
|
||||
}
|
||||
203
server/internal/infrastructure/tx-builder/covenant/mocks_test.go
Normal file
203
server/internal/infrastructure/tx-builder/covenant/mocks_test.go
Normal file
@@ -0,0 +1,203 @@
|
||||
package txbuilder_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
type mockedWallet struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// BroadcastTransaction implements ports.WalletService.
|
||||
func (m *mockedWallet) BroadcastTransaction(ctx context.Context, txHex string) (string, error) {
|
||||
args := m.Called(ctx, txHex)
|
||||
|
||||
var res string
|
||||
if a := args.Get(0); a != nil {
|
||||
res = a.(string)
|
||||
}
|
||||
return res, args.Error(1)
|
||||
}
|
||||
|
||||
// Close implements ports.WalletService.
|
||||
func (m *mockedWallet) Close() {
|
||||
m.Called()
|
||||
}
|
||||
|
||||
// DeriveAddresses implements ports.WalletService.
|
||||
func (m *mockedWallet) DeriveAddresses(ctx context.Context, num int) ([]string, error) {
|
||||
args := m.Called(ctx, num)
|
||||
|
||||
var res []string
|
||||
if a := args.Get(0); a != nil {
|
||||
res = a.([]string)
|
||||
}
|
||||
return res, args.Error(1)
|
||||
}
|
||||
|
||||
// GetPubkey implements ports.WalletService.
|
||||
func (m *mockedWallet) GetPubkey(ctx context.Context) (*secp256k1.PublicKey, error) {
|
||||
args := m.Called(ctx)
|
||||
|
||||
var res *secp256k1.PublicKey
|
||||
if a := args.Get(0); a != nil {
|
||||
res = a.(*secp256k1.PublicKey)
|
||||
}
|
||||
return res, args.Error(1)
|
||||
}
|
||||
|
||||
// SignPset implements ports.WalletService.
|
||||
func (m *mockedWallet) SignPset(ctx context.Context, pset string, extractRawTx bool) (string, error) {
|
||||
args := m.Called(ctx, pset, extractRawTx)
|
||||
|
||||
var res string
|
||||
if a := args.Get(0); a != nil {
|
||||
res = a.(string)
|
||||
}
|
||||
return res, args.Error(1)
|
||||
}
|
||||
|
||||
// Status implements ports.WalletService.
|
||||
func (m *mockedWallet) Status(ctx context.Context) (ports.WalletStatus, error) {
|
||||
args := m.Called(ctx)
|
||||
|
||||
var res ports.WalletStatus
|
||||
if a := args.Get(0); a != nil {
|
||||
res = a.(ports.WalletStatus)
|
||||
}
|
||||
return res, args.Error(1)
|
||||
}
|
||||
|
||||
func (m *mockedWallet) SelectUtxos(ctx context.Context, asset string, amount uint64) ([]ports.TxInput, uint64, error) {
|
||||
args := m.Called(ctx, asset, amount)
|
||||
|
||||
var res0 func() []ports.TxInput
|
||||
if a := args.Get(0); a != nil {
|
||||
res0 = a.(func() []ports.TxInput)
|
||||
}
|
||||
var res1 uint64
|
||||
if a := args.Get(1); a != nil {
|
||||
res1 = a.(uint64)
|
||||
}
|
||||
return res0(), res1, args.Error(2)
|
||||
}
|
||||
|
||||
func (m *mockedWallet) EstimateFees(ctx context.Context, pset string) (uint64, error) {
|
||||
args := m.Called(ctx, pset)
|
||||
|
||||
var res uint64
|
||||
if a := args.Get(0); a != nil {
|
||||
res = a.(uint64)
|
||||
}
|
||||
return res, args.Error(1)
|
||||
}
|
||||
|
||||
func (m *mockedWallet) IsTransactionPublished(ctx context.Context, txid string) (bool, int64, error) {
|
||||
args := m.Called(ctx, txid)
|
||||
|
||||
var res bool
|
||||
if a := args.Get(0); a != nil {
|
||||
res = a.(bool)
|
||||
}
|
||||
|
||||
var blocktime int64
|
||||
if b := args.Get(1); b != nil {
|
||||
blocktime = b.(int64)
|
||||
}
|
||||
|
||||
return res, blocktime, args.Error(2)
|
||||
}
|
||||
|
||||
func (m *mockedWallet) SignPsetWithKey(ctx context.Context, pset string, inputIndexes []int) (string, error) {
|
||||
args := m.Called(ctx, pset, inputIndexes)
|
||||
|
||||
var res string
|
||||
if a := args.Get(0); a != nil {
|
||||
res = a.(string)
|
||||
}
|
||||
return res, args.Error(1)
|
||||
}
|
||||
|
||||
func (m *mockedWallet) WatchScripts(
|
||||
ctx context.Context, scripts []string,
|
||||
) error {
|
||||
args := m.Called(ctx, scripts)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *mockedWallet) UnwatchScripts(
|
||||
ctx context.Context, scripts []string,
|
||||
) error {
|
||||
args := m.Called(ctx, scripts)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *mockedWallet) GetNotificationChannel(ctx context.Context) chan []domain.VtxoKey {
|
||||
args := m.Called(ctx)
|
||||
|
||||
var res chan []domain.VtxoKey
|
||||
if a := args.Get(0); a != nil {
|
||||
res = a.(chan []domain.VtxoKey)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type mockedInput struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *mockedInput) GetTxid() string {
|
||||
args := m.Called()
|
||||
|
||||
var res string
|
||||
if a := args.Get(0); a != nil {
|
||||
res = a.(string)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (m *mockedInput) GetIndex() uint32 {
|
||||
args := m.Called()
|
||||
|
||||
var res uint32
|
||||
if a := args.Get(0); a != nil {
|
||||
res = a.(uint32)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (m *mockedInput) GetScript() string {
|
||||
args := m.Called()
|
||||
|
||||
var res string
|
||||
if a := args.Get(0); a != nil {
|
||||
res = a.(string)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (m *mockedInput) GetAsset() string {
|
||||
args := m.Called()
|
||||
|
||||
var res string
|
||||
if a := args.Get(0); a != nil {
|
||||
res = a.(string)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (m *mockedInput) GetValue() uint64 {
|
||||
args := m.Called()
|
||||
|
||||
var res uint64
|
||||
if a := args.Get(0); a != nil {
|
||||
res = a.(uint64)
|
||||
}
|
||||
return res
|
||||
}
|
||||
135
server/internal/infrastructure/tx-builder/covenant/sweep.go
Normal file
135
server/internal/infrastructure/tx-builder/covenant/sweep.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package txbuilder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/ark-network/ark/common"
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
"github.com/vulpemventures/go-elements/address"
|
||||
"github.com/vulpemventures/go-elements/elementsutil"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
"github.com/vulpemventures/go-elements/taproot"
|
||||
"github.com/vulpemventures/go-elements/transaction"
|
||||
)
|
||||
|
||||
func sweepTransaction(
|
||||
wallet ports.WalletService,
|
||||
sweepInputs []ports.SweepInput,
|
||||
lbtc string,
|
||||
) (*psetv2.Pset, error) {
|
||||
sweepPset, err := psetv2.New(nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updater, err := psetv2.NewUpdater(sweepPset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
amount := uint64(0)
|
||||
|
||||
for i, input := range sweepInputs {
|
||||
leaf := input.SweepLeaf
|
||||
isSweep, _, lifetime, err := tree.DecodeSweepScript(leaf.Script)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if isSweep {
|
||||
amount += input.Amount
|
||||
|
||||
if err := updater.AddInputs([]psetv2.InputArgs{input.InputArgs}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := updater.AddInTapLeafScript(i, leaf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
assetHash, err := elementsutil.AssetHashToBytes(lbtc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value, err := elementsutil.ValueToBytes(input.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
root := leaf.ControlBlock.RootHash(leaf.Script)
|
||||
taprootKey := taproot.ComputeTaprootOutputKey(leaf.ControlBlock.InternalKey, root)
|
||||
script, err := taprootOutputScript(taprootKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
witnessUtxo := transaction.NewTxOutput(assetHash, value, script)
|
||||
|
||||
if err := updater.AddInWitnessUtxo(i, witnessUtxo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sequence, err := common.BIP68EncodeAsNumber(lifetime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updater.Pset.Inputs[i].Sequence = sequence
|
||||
continue
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid sweep script")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
sweepAddress, err := wallet.DeriveAddresses(ctx, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
script, err := address.ToOutputScript(sweepAddress[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := updater.AddOutputs([]psetv2.OutputArgs{
|
||||
{
|
||||
Asset: lbtc,
|
||||
Amount: amount,
|
||||
Script: script,
|
||||
},
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b64, err := sweepPset.ToBase64()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fees, err := wallet.EstimateFees(ctx, b64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if amount < fees {
|
||||
return nil, fmt.Errorf("insufficient funds (%d) to cover fees (%d) for sweep transaction", amount, fees)
|
||||
}
|
||||
|
||||
updater.Pset.Outputs[0].Value = amount - fees
|
||||
|
||||
if err := updater.AddOutputs([]psetv2.OutputArgs{
|
||||
{
|
||||
Asset: lbtc,
|
||||
Amount: fees,
|
||||
},
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sweepPset, nil
|
||||
}
|
||||
229
server/internal/infrastructure/tx-builder/covenant/testdata/fixtures.json
vendored
Normal file
229
server/internal/infrastructure/tx-builder/covenant/testdata/fixtures.json
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
{
|
||||
"buildPoolTx": {
|
||||
"valid": [
|
||||
{
|
||||
"payments": [
|
||||
{
|
||||
"id": "0",
|
||||
"inputs": [
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 1100
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 1100
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"expectedNumOfNodes": 1,
|
||||
"expectedNumOfLeaves": 1
|
||||
},
|
||||
{
|
||||
"payments": [
|
||||
{
|
||||
"id": "0",
|
||||
"inputs": [
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 1100
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 600
|
||||
},
|
||||
{
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"expectedNumOfNodes": 3,
|
||||
"expectedNumOfLeaves": 2
|
||||
},
|
||||
{
|
||||
"payments": [
|
||||
{
|
||||
"id": "0",
|
||||
"inputs": [
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 1100
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 600
|
||||
},
|
||||
{
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "0",
|
||||
"inputs": [
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 1100
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 600
|
||||
},
|
||||
{
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "0",
|
||||
"inputs": [
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 1100
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 600
|
||||
},
|
||||
{
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"expectedNumOfNodes": 11,
|
||||
"expectedNumOfLeaves": 6
|
||||
},
|
||||
{
|
||||
"payments": [
|
||||
{
|
||||
"id": "a242cdd8-f3d5-46c0-ae98-94135a2bee3f",
|
||||
"inputs": [
|
||||
{
|
||||
"txid": "755c820771284d85ea4bbcc246565b4eddadc44237a7e57a0f9cb78a840d1d41",
|
||||
"vout": 0,
|
||||
"pubkey": "02c87e5c1758df5ad42a918ec507b6e8dfcdcebf22f64f58eb4ad5804257d658a5",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"txid": "66a0df86fcdeb84b8877adfe0b2c556dba30305d72ddbd4c49355f6930355357",
|
||||
"vout": 0,
|
||||
"pubkey": "02c87e5c1758df5ad42a918ec507b6e8dfcdcebf22f64f58eb4ad5804257d658a5",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"txid": "9913159bc7aa493ca53cbb9cbc88f97ba01137c814009dc7ef520c3fafc67909",
|
||||
"vout": 1,
|
||||
"pubkey": "02c87e5c1758df5ad42a918ec507b6e8dfcdcebf22f64f58eb4ad5804257d658a5",
|
||||
"amount": 500
|
||||
},
|
||||
{
|
||||
"txid": "5e10e77a7cdedc153be5193a4b6055a7802706ded4f2a9efefe86ed2f9a6ae60",
|
||||
"vout": 0,
|
||||
"pubkey": "02c87e5c1758df5ad42a918ec507b6e8dfcdcebf22f64f58eb4ad5804257d658a5",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"txid": "5e10e77a7cdedc153be5193a4b6055a7802706ded4f2a9efefe86ed2f9a6ae60",
|
||||
"vout": 1,
|
||||
"pubkey": "02c87e5c1758df5ad42a918ec507b6e8dfcdcebf22f64f58eb4ad5804257d658a5",
|
||||
"amount": 1000
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"pubkey": "02c87e5c1758df5ad42a918ec507b6e8dfcdcebf22f64f58eb4ad5804257d658a5",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"pubkey": "02c87e5c1758df5ad42a918ec507b6e8dfcdcebf22f64f58eb4ad5804257d658a5",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"pubkey": "02c87e5c1758df5ad42a918ec507b6e8dfcdcebf22f64f58eb4ad5804257d658a5",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"pubkey": "02c87e5c1758df5ad42a918ec507b6e8dfcdcebf22f64f58eb4ad5804257d658a5",
|
||||
"amount": 1000
|
||||
},
|
||||
{
|
||||
"pubkey": "02c87e5c1758df5ad42a918ec507b6e8dfcdcebf22f64f58eb4ad5804257d658a5",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"expectedNumOfNodes": 9,
|
||||
"expectedNumOfLeaves": 5
|
||||
}
|
||||
],
|
||||
"invalid": []
|
||||
},
|
||||
"buildForfeitTxs": {
|
||||
"valid": [
|
||||
{
|
||||
"payments": [
|
||||
{
|
||||
"id": "0",
|
||||
"inputs": [
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 0,
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 600
|
||||
},
|
||||
{
|
||||
"txid": "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
"vout": 1,
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 500
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 600
|
||||
},
|
||||
{
|
||||
"pubkey": "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
"amount": 500
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"poolTx": "cHNldP8BAgQCAAAAAQQBAQEFAQMBBgEDAfsEAgAAAAABDiDk7dXxh4KQzgLO8i1ABtaLCe4aPL12GVhN1E9zM1ePLwEPBAAAAAABEAT/////AAEDCOgDAAAAAAAAAQQWABSNnpy01UJqd99eTg2M1IpdKId11gf8BHBzZXQCICWyUQcOKcoZBDzzPM1zJOLdqwPsxK4LXnfE/A5c9slaB/wEcHNldAgEAAAAAAABAwh4BQAAAAAAAAEEFgAUjZ6ctNVCanffXk4NjNSKXSiHddYH/ARwc2V0AiAlslEHDinKGQQ88zzNcyTi3asD7MSuC153xPwOXPbJWgf8BHBzZXQIBAAAAAAAAQMI9AEAAAAAAAABBAAH/ARwc2V0AiAlslEHDinKGQQ88zzNcyTi3asD7MSuC153xPwOXPbJWgf8BHBzZXQIBAAAAAAA",
|
||||
"poolTxid": "7981fce656f266472cc742444527cb32a8bed8c90fed6d47adbfc4c8780d4d9a",
|
||||
"expectedNumOfForfeitTxs": 4,
|
||||
"expectedNumOfConnectors": 1
|
||||
}
|
||||
],
|
||||
"invalid": []
|
||||
}
|
||||
}
|
||||
448
server/internal/infrastructure/tx-builder/covenant/tree.go
Normal file
448
server/internal/infrastructure/tx-builder/covenant/tree.go
Normal file
@@ -0,0 +1,448 @@
|
||||
package txbuilder
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
"github.com/vulpemventures/go-elements/taproot"
|
||||
)
|
||||
|
||||
type treeFactory func(outpoint psetv2.InputArgs) (tree.CongestionTree, error)
|
||||
|
||||
type node struct {
|
||||
sweepKey *secp256k1.PublicKey
|
||||
receivers []domain.Receiver
|
||||
left *node
|
||||
right *node
|
||||
asset string
|
||||
feeSats uint64
|
||||
roundLifetime int64
|
||||
|
||||
_inputTaprootKey *secp256k1.PublicKey
|
||||
_inputTaprootTree *taproot.IndexedElementsTapScriptTree
|
||||
}
|
||||
|
||||
func (n *node) isLeaf() bool {
|
||||
return len(n.receivers) == 1
|
||||
}
|
||||
|
||||
func (n *node) getAmount() uint64 {
|
||||
var amount uint64
|
||||
for _, r := range n.receivers {
|
||||
amount += r.Amount
|
||||
}
|
||||
|
||||
if n.isLeaf() {
|
||||
return amount
|
||||
}
|
||||
|
||||
return amount + n.feeSats*uint64(n.countChildren())
|
||||
}
|
||||
|
||||
func (n *node) countChildren() int {
|
||||
result := 0
|
||||
|
||||
if n.left != nil {
|
||||
result++
|
||||
result += n.left.countChildren()
|
||||
}
|
||||
|
||||
if n.right != nil {
|
||||
result++
|
||||
result += n.right.countChildren()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (n *node) getChildren() []*node {
|
||||
if n.isLeaf() {
|
||||
return nil
|
||||
}
|
||||
|
||||
children := make([]*node, 0, 2)
|
||||
|
||||
if n.left != nil {
|
||||
children = append(children, n.left)
|
||||
}
|
||||
|
||||
if n.right != nil {
|
||||
children = append(children, n.right)
|
||||
}
|
||||
|
||||
return children
|
||||
}
|
||||
|
||||
func (n *node) getOutputs() ([]psetv2.OutputArgs, error) {
|
||||
if n.isLeaf() {
|
||||
taprootKey, _, err := n.getVtxoWitnessData()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
script, err := taprootOutputScript(taprootKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output := &psetv2.OutputArgs{
|
||||
Asset: n.asset,
|
||||
Amount: uint64(n.getAmount()),
|
||||
Script: script,
|
||||
}
|
||||
|
||||
return []psetv2.OutputArgs{*output}, nil
|
||||
}
|
||||
|
||||
outputs := make([]psetv2.OutputArgs, 0, 2)
|
||||
children := n.getChildren()
|
||||
|
||||
for _, child := range children {
|
||||
childWitnessProgram, _, err := child.getWitnessData()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
script, err := taprootOutputScript(childWitnessProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputs = append(outputs, psetv2.OutputArgs{
|
||||
Asset: n.asset,
|
||||
Amount: child.getAmount() + child.feeSats,
|
||||
Script: script,
|
||||
})
|
||||
}
|
||||
|
||||
return outputs, nil
|
||||
}
|
||||
|
||||
func (n *node) getWitnessData() (
|
||||
*secp256k1.PublicKey, *taproot.IndexedElementsTapScriptTree, error,
|
||||
) {
|
||||
if n._inputTaprootKey != nil && n._inputTaprootTree != nil {
|
||||
return n._inputTaprootKey, n._inputTaprootTree, nil
|
||||
}
|
||||
|
||||
sweepClosure, err := tree.SweepScript(n.sweepKey, uint(n.roundLifetime))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if n.isLeaf() {
|
||||
taprootKey, _, err := n.getVtxoWitnessData()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
branchTaprootScript := tree.BranchScript(
|
||||
taprootKey, nil, n.getAmount(), 0,
|
||||
)
|
||||
|
||||
branchTaprootTree := taproot.AssembleTaprootScriptTree(
|
||||
branchTaprootScript, *sweepClosure,
|
||||
)
|
||||
root := branchTaprootTree.RootNode.TapHash()
|
||||
|
||||
inputTapkey := taproot.ComputeTaprootOutputKey(
|
||||
tree.UnspendableKey(),
|
||||
root[:],
|
||||
)
|
||||
|
||||
n._inputTaprootKey = inputTapkey
|
||||
n._inputTaprootTree = branchTaprootTree
|
||||
|
||||
return inputTapkey, branchTaprootTree, nil
|
||||
}
|
||||
|
||||
leftKey, _, err := n.left.getWitnessData()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rightKey, _, err := n.right.getWitnessData()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
leftAmount := n.left.getAmount() + n.feeSats
|
||||
rightAmount := n.right.getAmount() + n.feeSats
|
||||
branchTaprootLeaf := tree.BranchScript(
|
||||
leftKey, rightKey, leftAmount, rightAmount,
|
||||
)
|
||||
|
||||
branchTaprootTree := taproot.AssembleTaprootScriptTree(
|
||||
branchTaprootLeaf, *sweepClosure,
|
||||
)
|
||||
root := branchTaprootTree.RootNode.TapHash()
|
||||
|
||||
taprootKey := taproot.ComputeTaprootOutputKey(
|
||||
tree.UnspendableKey(),
|
||||
root[:],
|
||||
)
|
||||
|
||||
n._inputTaprootKey = taprootKey
|
||||
n._inputTaprootTree = branchTaprootTree
|
||||
|
||||
return taprootKey, branchTaprootTree, nil
|
||||
}
|
||||
|
||||
func (n *node) getVtxoWitnessData() (
|
||||
*secp256k1.PublicKey, *taproot.IndexedElementsTapScriptTree, error,
|
||||
) {
|
||||
if !n.isLeaf() {
|
||||
return nil, nil, fmt.Errorf("cannot call vtxoWitness on a non-leaf node")
|
||||
}
|
||||
|
||||
sweepClosure, err := tree.SweepScript(n.sweepKey, uint(n.roundLifetime))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
key, err := hex.DecodeString(n.receivers[0].Pubkey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pubkey, err := secp256k1.ParsePubKey(key)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
vtxoLeaf, err := tree.VtxoScript(pubkey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// TODO: add forfeit path
|
||||
leafTaprootTree := taproot.AssembleTaprootScriptTree(
|
||||
*vtxoLeaf, *sweepClosure,
|
||||
)
|
||||
root := leafTaprootTree.RootNode.TapHash()
|
||||
|
||||
taprootKey := taproot.ComputeTaprootOutputKey(
|
||||
tree.UnspendableKey(),
|
||||
root[:],
|
||||
)
|
||||
|
||||
return taprootKey, leafTaprootTree, nil
|
||||
}
|
||||
|
||||
func (n *node) getTreeNode(
|
||||
input psetv2.InputArgs, tapTree *taproot.IndexedElementsTapScriptTree,
|
||||
) (tree.Node, error) {
|
||||
pset, err := n.getTx(input, tapTree)
|
||||
if err != nil {
|
||||
return tree.Node{}, err
|
||||
}
|
||||
|
||||
txid, err := getPsetId(pset)
|
||||
if err != nil {
|
||||
return tree.Node{}, err
|
||||
}
|
||||
|
||||
tx, err := pset.ToBase64()
|
||||
if err != nil {
|
||||
return tree.Node{}, err
|
||||
}
|
||||
|
||||
parentTxid := chainhash.Hash(pset.Inputs[0].PreviousTxid).String()
|
||||
|
||||
return tree.Node{
|
||||
Txid: txid,
|
||||
Tx: tx,
|
||||
ParentTxid: parentTxid,
|
||||
Leaf: n.isLeaf(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *node) getTx(
|
||||
input psetv2.InputArgs, inputTapTree *taproot.IndexedElementsTapScriptTree,
|
||||
) (*psetv2.Pset, error) {
|
||||
pset, err := psetv2.New(nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updater, err := psetv2.NewUpdater(pset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := addTaprootInput(
|
||||
updater, input, tree.UnspendableKey(), inputTapTree,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
feeOutput := psetv2.OutputArgs{
|
||||
Amount: uint64(n.feeSats),
|
||||
Asset: n.asset,
|
||||
}
|
||||
|
||||
outputs, err := n.getOutputs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := updater.AddOutputs(append(outputs, feeOutput)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pset, nil
|
||||
}
|
||||
|
||||
func (n *node) createFinalCongestionTree() treeFactory {
|
||||
return func(poolTxInput psetv2.InputArgs) (tree.CongestionTree, error) {
|
||||
congestionTree := make(tree.CongestionTree, 0)
|
||||
|
||||
_, taprootTree, err := n.getWitnessData()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ins := []psetv2.InputArgs{poolTxInput}
|
||||
inTrees := []*taproot.IndexedElementsTapScriptTree{taprootTree}
|
||||
nodes := []*node{n}
|
||||
|
||||
for len(nodes) > 0 {
|
||||
nextNodes := make([]*node, 0)
|
||||
nextInputsArgs := make([]psetv2.InputArgs, 0)
|
||||
nextTaprootTrees := make([]*taproot.IndexedElementsTapScriptTree, 0)
|
||||
|
||||
treeLevel := make([]tree.Node, 0)
|
||||
|
||||
for i, node := range nodes {
|
||||
treeNode, err := node.getTreeNode(ins[i], inTrees[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
treeLevel = append(treeLevel, treeNode)
|
||||
|
||||
children := node.getChildren()
|
||||
|
||||
for i, child := range children {
|
||||
_, taprootTree, err := child.getWitnessData()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nextNodes = append(nextNodes, child)
|
||||
nextInputsArgs = append(nextInputsArgs, psetv2.InputArgs{
|
||||
Txid: treeNode.Txid,
|
||||
TxIndex: uint32(i),
|
||||
})
|
||||
nextTaprootTrees = append(nextTaprootTrees, taprootTree)
|
||||
}
|
||||
}
|
||||
|
||||
congestionTree = append(congestionTree, treeLevel)
|
||||
nodes = append([]*node{}, nextNodes...)
|
||||
ins = append([]psetv2.InputArgs{}, nextInputsArgs...)
|
||||
inTrees = append(
|
||||
[]*taproot.IndexedElementsTapScriptTree{}, nextTaprootTrees...,
|
||||
)
|
||||
}
|
||||
|
||||
return congestionTree, nil
|
||||
}
|
||||
}
|
||||
|
||||
func craftCongestionTree(
|
||||
asset string, aspPublicKey *secp256k1.PublicKey,
|
||||
payments []domain.Payment, feeSatsPerNode uint64, roundLifetime int64,
|
||||
) (
|
||||
buildCongestionTree treeFactory,
|
||||
sharedOutputScript []byte, sharedOutputAmount uint64, err error,
|
||||
) {
|
||||
receivers := getOffchainReceivers(payments)
|
||||
root, err := createPartialCongestionTree(
|
||||
receivers, aspPublicKey, asset, feeSatsPerNode, roundLifetime,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
taprootKey, _, err := root.getWitnessData()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sharedOutputScript, err = taprootOutputScript(taprootKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sharedOutputAmount = root.getAmount() + root.feeSats
|
||||
buildCongestionTree = root.createFinalCongestionTree()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func createPartialCongestionTree(
|
||||
receivers []domain.Receiver,
|
||||
aspPublicKey *secp256k1.PublicKey,
|
||||
asset string,
|
||||
feeSatsPerNode uint64,
|
||||
roundLifetime int64,
|
||||
) (root *node, err error) {
|
||||
if len(receivers) == 0 {
|
||||
return nil, fmt.Errorf("no receivers provided")
|
||||
}
|
||||
|
||||
nodes := make([]*node, 0, len(receivers))
|
||||
for _, r := range receivers {
|
||||
leafNode := &node{
|
||||
sweepKey: aspPublicKey,
|
||||
receivers: []domain.Receiver{r},
|
||||
asset: asset,
|
||||
feeSats: feeSatsPerNode,
|
||||
roundLifetime: roundLifetime,
|
||||
}
|
||||
nodes = append(nodes, leafNode)
|
||||
}
|
||||
|
||||
for len(nodes) > 1 {
|
||||
nodes, err = createUpperLevel(nodes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nodes[0], nil
|
||||
}
|
||||
|
||||
func createUpperLevel(nodes []*node) ([]*node, error) {
|
||||
if len(nodes)%2 != 0 {
|
||||
last := nodes[len(nodes)-1]
|
||||
pairs, err := createUpperLevel(nodes[:len(nodes)-1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return append(pairs, last), nil
|
||||
}
|
||||
|
||||
pairs := make([]*node, 0, len(nodes)/2)
|
||||
for i := 0; i < len(nodes); i += 2 {
|
||||
left := nodes[i]
|
||||
right := nodes[i+1]
|
||||
branchNode := &node{
|
||||
sweepKey: left.sweepKey,
|
||||
receivers: append(left.receivers, right.receivers...),
|
||||
left: left,
|
||||
right: right,
|
||||
asset: left.asset,
|
||||
feeSats: left.feeSats,
|
||||
roundLifetime: left.roundLifetime,
|
||||
}
|
||||
pairs = append(pairs, branchNode)
|
||||
}
|
||||
return pairs, nil
|
||||
}
|
||||
167
server/internal/infrastructure/tx-builder/covenant/utils.go
Normal file
167
server/internal/infrastructure/tx-builder/covenant/utils.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package txbuilder
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/vulpemventures/go-elements/address"
|
||||
"github.com/vulpemventures/go-elements/elementsutil"
|
||||
"github.com/vulpemventures/go-elements/network"
|
||||
"github.com/vulpemventures/go-elements/payment"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
"github.com/vulpemventures/go-elements/taproot"
|
||||
"github.com/vulpemventures/go-elements/transaction"
|
||||
)
|
||||
|
||||
func p2wpkhScript(publicKey *secp256k1.PublicKey, net *network.Network) ([]byte, error) {
|
||||
payment := payment.FromPublicKey(publicKey, net, nil)
|
||||
addr, err := payment.WitnessPubKeyHash()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return address.ToOutputScript(addr)
|
||||
}
|
||||
|
||||
func getTxid(txStr string) (string, error) {
|
||||
pset, err := psetv2.NewPsetFromBase64(txStr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return getPsetId(pset)
|
||||
}
|
||||
|
||||
func getPsetId(pset *psetv2.Pset) (string, error) {
|
||||
utx, err := pset.UnsignedTx()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return utx.TxHash().String(), nil
|
||||
}
|
||||
|
||||
func getOnchainReceivers(
|
||||
payments []domain.Payment,
|
||||
) []domain.Receiver {
|
||||
receivers := make([]domain.Receiver, 0)
|
||||
for _, payment := range payments {
|
||||
for _, receiver := range payment.Receivers {
|
||||
if receiver.IsOnchain() {
|
||||
receivers = append(receivers, receiver)
|
||||
}
|
||||
}
|
||||
}
|
||||
return receivers
|
||||
}
|
||||
|
||||
func getOffchainReceivers(
|
||||
payments []domain.Payment,
|
||||
) []domain.Receiver {
|
||||
receivers := make([]domain.Receiver, 0)
|
||||
for _, payment := range payments {
|
||||
for _, receiver := range payment.Receivers {
|
||||
if !receiver.IsOnchain() {
|
||||
receivers = append(receivers, receiver)
|
||||
}
|
||||
}
|
||||
}
|
||||
return receivers
|
||||
}
|
||||
|
||||
func toWitnessUtxo(in ports.TxInput) (*transaction.TxOutput, error) {
|
||||
valueBytes, err := elementsutil.ValueToBytes(in.GetValue())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert value to bytes: %s", err)
|
||||
}
|
||||
|
||||
assetBytes, err := elementsutil.AssetHashToBytes(in.GetAsset())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert asset to bytes: %s", err)
|
||||
}
|
||||
|
||||
scriptBytes, err := hex.DecodeString(in.GetScript())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode script: %s", err)
|
||||
}
|
||||
|
||||
return transaction.NewTxOutput(assetBytes, valueBytes, scriptBytes), nil
|
||||
}
|
||||
|
||||
func countSpentVtxos(payments []domain.Payment) uint64 {
|
||||
var sum uint64
|
||||
for _, payment := range payments {
|
||||
sum += uint64(len(payment.Inputs))
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
func addInputs(
|
||||
updater *psetv2.Updater,
|
||||
inputs []ports.TxInput,
|
||||
) error {
|
||||
for _, in := range inputs {
|
||||
inputArg := psetv2.InputArgs{
|
||||
Txid: in.GetTxid(),
|
||||
TxIndex: in.GetIndex(),
|
||||
}
|
||||
|
||||
witnessUtxo, err := toWitnessUtxo(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := updater.AddInputs([]psetv2.InputArgs{inputArg}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
index := int(updater.Pset.Global.InputCount) - 1
|
||||
if err := updater.AddInWitnessUtxo(index, witnessUtxo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := updater.AddInSighashType(index, txscript.SigHashAll); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// wrapper of updater methods adding a taproot input to the pset with all the necessary data to spend it via any taproot script
|
||||
func addTaprootInput(
|
||||
updater *psetv2.Updater,
|
||||
input psetv2.InputArgs,
|
||||
internalTaprootKey *secp256k1.PublicKey,
|
||||
taprootTree *taproot.IndexedElementsTapScriptTree,
|
||||
) error {
|
||||
if err := updater.AddInputs([]psetv2.InputArgs{input}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := updater.AddInTapInternalKey(0, schnorr.SerializePubKey(internalTaprootKey)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, proof := range taprootTree.LeafMerkleProofs {
|
||||
controlBlock := proof.ToControlBlock(internalTaprootKey)
|
||||
|
||||
if err := updater.AddInTapLeafScript(0, psetv2.TapLeafScript{
|
||||
TapElementsLeaf: taproot.NewBaseTapElementsLeaf(proof.Script),
|
||||
ControlBlock: controlBlock,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func taprootOutputScript(taprootKey *secp256k1.PublicKey) ([]byte, error) {
|
||||
return txscript.NewScriptBuilder().AddOp(txscript.OP_1).AddData(schnorr.SerializePubKey(taprootKey)).Script()
|
||||
}
|
||||
285
server/internal/infrastructure/tx-builder/dummy/builder.go
Normal file
285
server/internal/infrastructure/tx-builder/dummy/builder.go
Normal file
@@ -0,0 +1,285 @@
|
||||
package txbuilder
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/vulpemventures/go-elements/address"
|
||||
"github.com/vulpemventures/go-elements/network"
|
||||
"github.com/vulpemventures/go-elements/payment"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
"github.com/vulpemventures/go-elements/transaction"
|
||||
)
|
||||
|
||||
const (
|
||||
connectorAmount = 450
|
||||
sevenDays = 7 * 24 * 60 * 60
|
||||
)
|
||||
|
||||
type txBuilder struct {
|
||||
wallet ports.WalletService
|
||||
net network.Network
|
||||
}
|
||||
|
||||
func NewTxBuilder(
|
||||
wallet ports.WalletService, net network.Network,
|
||||
) ports.TxBuilder {
|
||||
return &txBuilder{wallet, net}
|
||||
}
|
||||
|
||||
// BuildSweepTx implements ports.TxBuilder.
|
||||
func (*txBuilder) BuildSweepTx(wallet ports.WalletService, inputs []ports.SweepInput) (signedSweepTx string, err error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// BuildForfeitTxs implements ports.TxBuilder.
|
||||
func (b *txBuilder) BuildForfeitTxs(
|
||||
aspPubkey *secp256k1.PublicKey, poolTx string, payments []domain.Payment,
|
||||
) (connectors []string, forfeitTxs []string, err error) {
|
||||
poolTxID, err := getTxid(poolTx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
aspScript, err := p2wpkhScript(aspPubkey, b.net)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
numberOfConnectors := countSpentVtxos(payments)
|
||||
|
||||
connectors, err = createConnectors(
|
||||
poolTxID,
|
||||
1,
|
||||
psetv2.OutputArgs{
|
||||
Asset: b.net.AssetID,
|
||||
Amount: connectorAmount,
|
||||
Script: aspScript,
|
||||
},
|
||||
aspScript,
|
||||
numberOfConnectors,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
connectorsAsInputs, err := connectorsToInputArgs(connectors)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
forfeitTxs = make([]string, 0)
|
||||
for _, payment := range payments {
|
||||
for _, vtxo := range payment.Inputs {
|
||||
for _, connector := range connectorsAsInputs {
|
||||
forfeitTx, err := createForfeitTx(
|
||||
connector,
|
||||
psetv2.InputArgs{
|
||||
Txid: vtxo.Txid,
|
||||
TxIndex: vtxo.VOut,
|
||||
},
|
||||
vtxo.Amount,
|
||||
aspScript,
|
||||
b.net,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
forfeitTxs = append(forfeitTxs, forfeitTx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return connectors, forfeitTxs, nil
|
||||
}
|
||||
|
||||
// BuildPoolTx implements ports.TxBuilder.
|
||||
func (b *txBuilder) BuildPoolTx(
|
||||
aspPubkey *secp256k1.PublicKey, payments []domain.Payment, minRelayFee uint64,
|
||||
) (poolTx string, congestionTree tree.CongestionTree, err error) {
|
||||
aspScriptBytes, err := p2wpkhScript(aspPubkey, b.net)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
offchainReceivers, onchainReceivers := receiversFromPayments(payments)
|
||||
sharedOutputAmount := sumReceivers(offchainReceivers)
|
||||
|
||||
numberOfConnectors := countSpentVtxos(payments)
|
||||
connectorOutputAmount := connectorAmount * numberOfConnectors
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
outputs := []psetv2.OutputArgs{
|
||||
{
|
||||
Asset: b.net.AssetID,
|
||||
Amount: sharedOutputAmount,
|
||||
Script: aspScriptBytes,
|
||||
},
|
||||
{
|
||||
Asset: b.net.AssetID,
|
||||
Amount: connectorOutputAmount,
|
||||
Script: aspScriptBytes,
|
||||
},
|
||||
}
|
||||
|
||||
amountToSelect := sharedOutputAmount + connectorOutputAmount
|
||||
|
||||
for _, receiver := range onchainReceivers {
|
||||
amountToSelect += receiver.Amount
|
||||
|
||||
receiverScript, err := address.ToOutputScript(receiver.OnchainAddress)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
outputs = append(outputs, psetv2.OutputArgs{
|
||||
Asset: b.net.AssetID,
|
||||
Amount: receiver.Amount,
|
||||
Script: receiverScript,
|
||||
})
|
||||
}
|
||||
|
||||
utxos, change, err := b.wallet.SelectUtxos(ctx, b.net.AssetID, amountToSelect)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if change > 0 {
|
||||
outputs = append(outputs, psetv2.OutputArgs{
|
||||
Asset: b.net.AssetID,
|
||||
Amount: change,
|
||||
Script: aspScriptBytes,
|
||||
})
|
||||
}
|
||||
|
||||
ptx, err := psetv2.New(toInputArgs(utxos), outputs, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
utx, err := ptx.UnsignedTx()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
congestionTree, err = buildCongestionTree(
|
||||
newOutputScriptFactory(aspPubkey, b.net),
|
||||
b.net,
|
||||
utx.TxHash().String(),
|
||||
offchainReceivers,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
poolTx, err = ptx.ToBase64()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return poolTx, congestionTree, err
|
||||
}
|
||||
|
||||
func (b *txBuilder) GetVtxoScript(userPubkey, _ *secp256k1.PublicKey) ([]byte, error) {
|
||||
p2wpkh := payment.FromPublicKey(userPubkey, &b.net, nil)
|
||||
addr, _ := p2wpkh.WitnessPubKeyHash()
|
||||
return address.ToOutputScript(addr)
|
||||
}
|
||||
|
||||
func (b *txBuilder) GetLeafSweepClosure(
|
||||
node tree.Node, userPubKey *secp256k1.PublicKey,
|
||||
) (*psetv2.TapLeafScript, int64, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func connectorsToInputArgs(connectors []string) ([]psetv2.InputArgs, error) {
|
||||
inputs := make([]psetv2.InputArgs, 0, len(connectors)+1)
|
||||
for i, psetb64 := range connectors {
|
||||
tx, err := psetv2.NewPsetFromBase64(psetb64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
utx, err := tx.UnsignedTx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txid := utx.TxHash().String()
|
||||
for j := range tx.Outputs {
|
||||
inputs = append(inputs, psetv2.InputArgs{
|
||||
Txid: txid,
|
||||
TxIndex: uint32(j),
|
||||
})
|
||||
if i != len(connectors)-1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputs, nil
|
||||
}
|
||||
|
||||
func getTxid(txStr string) (string, error) {
|
||||
pset, err := psetv2.NewPsetFromBase64(txStr)
|
||||
if err != nil {
|
||||
tx, err := transaction.NewTxFromHex(txStr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return tx.TxHash().String(), nil
|
||||
}
|
||||
|
||||
utx, err := pset.UnsignedTx()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return utx.TxHash().String(), nil
|
||||
}
|
||||
|
||||
func countSpentVtxos(payments []domain.Payment) uint64 {
|
||||
var sum uint64
|
||||
for _, payment := range payments {
|
||||
sum += uint64(len(payment.Inputs))
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
func receiversFromPayments(
|
||||
payments []domain.Payment,
|
||||
) (offchainReceivers, onchainReceivers []domain.Receiver) {
|
||||
for _, payment := range payments {
|
||||
for _, receiver := range payment.Receivers {
|
||||
if receiver.IsOnchain() {
|
||||
onchainReceivers = append(onchainReceivers, receiver)
|
||||
} else {
|
||||
offchainReceivers = append(offchainReceivers, receiver)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func sumReceivers(receivers []domain.Receiver) uint64 {
|
||||
var sum uint64
|
||||
for _, r := range receivers {
|
||||
sum += r.Amount
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
func toInputArgs(
|
||||
ins []ports.TxInput,
|
||||
) []psetv2.InputArgs {
|
||||
inputs := make([]psetv2.InputArgs, 0, len(ins))
|
||||
for _, in := range ins {
|
||||
inputs = append(inputs, psetv2.InputArgs{
|
||||
Txid: in.GetTxid(),
|
||||
TxIndex: in.GetIndex(),
|
||||
})
|
||||
}
|
||||
return inputs
|
||||
}
|
||||
407
server/internal/infrastructure/tx-builder/dummy/builder_test.go
Normal file
407
server/internal/infrastructure/tx-builder/dummy/builder_test.go
Normal file
@@ -0,0 +1,407 @@
|
||||
package txbuilder_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/ark-network/ark/common"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/ark-network/ark/internal/core/ports"
|
||||
txbuilder "github.com/ark-network/ark/internal/infrastructure/tx-builder/dummy"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/vulpemventures/go-elements/network"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
)
|
||||
|
||||
const (
|
||||
testingKey = "apub1qgvdtj5ttpuhkldavhq8thtm5auyk0ec4dcmrfdgu0u5hgp9we22v3hrs4x"
|
||||
fakePoolTx = "cHNldP8BAgQCAAAAAQQBAQEFAQMBBgEDAfsEAgAAAAABDiDk7dXxh4KQzgLO8i1ABtaLCe4aPL12GVhN1E9zM1ePLwEPBAAAAAABEAT/////AAEDCOgDAAAAAAAAAQQWABSNnpy01UJqd99eTg2M1IpdKId11gf8BHBzZXQCICWyUQcOKcoZBDzzPM1zJOLdqwPsxK4LXnfE/A5c9slaB/wEcHNldAgEAAAAAAABAwh4BQAAAAAAAAEEFgAUjZ6ctNVCanffXk4NjNSKXSiHddYH/ARwc2V0AiAlslEHDinKGQQ88zzNcyTi3asD7MSuC153xPwOXPbJWgf8BHBzZXQIBAAAAAAAAQMI9AEAAAAAAAABBAAH/ARwc2V0AiAlslEHDinKGQQ88zzNcyTi3asD7MSuC153xPwOXPbJWgf8BHBzZXQIBAAAAAAA"
|
||||
)
|
||||
|
||||
type input struct {
|
||||
txid string
|
||||
vout uint32
|
||||
}
|
||||
|
||||
func (i *input) GetTxid() string {
|
||||
return i.txid
|
||||
}
|
||||
|
||||
func (i *input) GetIndex() uint32 {
|
||||
return i.vout
|
||||
}
|
||||
|
||||
func (i *input) GetScript() string {
|
||||
return "a914ea9f486e82efb3dd83a69fd96e3f0113757da03c87"
|
||||
}
|
||||
|
||||
func (i *input) GetAsset() string {
|
||||
return "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225"
|
||||
}
|
||||
|
||||
func (i *input) GetValue() uint64 {
|
||||
return 1000
|
||||
}
|
||||
|
||||
type mockedWalletService struct{}
|
||||
|
||||
// BroadcastTransaction implements ports.WalletService.
|
||||
func (*mockedWalletService) BroadcastTransaction(ctx context.Context, txHex string) (string, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Close implements ports.WalletService.
|
||||
func (*mockedWalletService) Close() {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// DeriveAddresses implements ports.WalletService.
|
||||
func (*mockedWalletService) DeriveAddresses(ctx context.Context, num int) ([]string, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// GetPubkey implements ports.WalletService.
|
||||
func (*mockedWalletService) GetPubkey(ctx context.Context) (*secp256k1.PublicKey, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// SignPset implements ports.WalletService.
|
||||
func (*mockedWalletService) SignPset(ctx context.Context, pset string, extractRawTx bool) (string, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Status implements ports.WalletService.
|
||||
func (*mockedWalletService) Status(ctx context.Context) (ports.WalletStatus, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (*mockedWalletService) WatchScripts(ctx context.Context, scripts []string) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (*mockedWalletService) UnwatchScripts(ctx context.Context, scripts []string) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (*mockedWalletService) GetNotificationChannel(ctx context.Context) chan []domain.VtxoKey {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (*mockedWalletService) SelectUtxos(ctx context.Context, asset string, amount uint64) ([]ports.TxInput, uint64, error) {
|
||||
// random txid
|
||||
bytes := make([]byte, 32)
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
fakeInput := input{
|
||||
txid: hex.EncodeToString(bytes),
|
||||
vout: 0,
|
||||
}
|
||||
|
||||
return []ports.TxInput{&fakeInput}, 0, nil
|
||||
}
|
||||
|
||||
func (*mockedWalletService) EstimateFees(ctx context.Context, pset string) (uint64, error) {
|
||||
return 100, nil
|
||||
}
|
||||
|
||||
func (*mockedWalletService) SignPsetWithKey(ctx context.Context, pset string, inputIndex []int) (string, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (*mockedWalletService) IsTransactionPublished(ctx context.Context, txid string) (bool, int64, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func TestBuildCongestionTree(t *testing.T) {
|
||||
builder := txbuilder.NewTxBuilder(&mockedWalletService{}, network.Liquid)
|
||||
|
||||
fixtures := []struct {
|
||||
payments []domain.Payment
|
||||
expectedNodesNum int // 2*len(receivers)-1
|
||||
expectedLeavesNum int
|
||||
}{
|
||||
{
|
||||
payments: []domain.Payment{
|
||||
{
|
||||
Id: "0",
|
||||
Inputs: []domain.Vtxo{
|
||||
{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
VOut: 0,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 600,
|
||||
},
|
||||
},
|
||||
},
|
||||
Receivers: []domain.Receiver{
|
||||
{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 600,
|
||||
},
|
||||
{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 400,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedNodesNum: 3,
|
||||
expectedLeavesNum: 2,
|
||||
},
|
||||
{
|
||||
payments: []domain.Payment{
|
||||
{
|
||||
Id: "0",
|
||||
Inputs: []domain.Vtxo{
|
||||
{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
VOut: 0,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 600,
|
||||
},
|
||||
},
|
||||
},
|
||||
Receivers: []domain.Receiver{
|
||||
{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 600,
|
||||
},
|
||||
{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 400,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "0",
|
||||
Inputs: []domain.Vtxo{
|
||||
{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
VOut: 0,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 600,
|
||||
},
|
||||
},
|
||||
},
|
||||
Receivers: []domain.Receiver{
|
||||
{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 600,
|
||||
},
|
||||
{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 400,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "0",
|
||||
Inputs: []domain.Vtxo{
|
||||
{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
VOut: 0,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 600,
|
||||
},
|
||||
},
|
||||
},
|
||||
Receivers: []domain.Receiver{
|
||||
{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 600,
|
||||
},
|
||||
{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 400,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedNodesNum: 11,
|
||||
expectedLeavesNum: 6,
|
||||
},
|
||||
}
|
||||
|
||||
_, key, err := common.DecodePubKey(testingKey)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, key)
|
||||
|
||||
for _, f := range fixtures {
|
||||
poolTx, tree, err := builder.BuildPoolTx(key, f.payments, 30)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, f.expectedNodesNum, tree.NumberOfNodes())
|
||||
require.Len(t, tree.Leaves(), f.expectedLeavesNum)
|
||||
|
||||
poolPset, err := psetv2.NewPsetFromBase64(poolTx)
|
||||
require.NoError(t, err)
|
||||
|
||||
poolTxUnsigned, err := poolPset.UnsignedTx()
|
||||
require.NoError(t, err)
|
||||
|
||||
poolTxID := poolTxUnsigned.TxHash().String()
|
||||
|
||||
// check the root
|
||||
require.Len(t, tree[0], 1)
|
||||
require.Equal(t, poolTxID, tree[0][0].ParentTxid)
|
||||
|
||||
// check the leaves
|
||||
for _, leaf := range tree.Leaves() {
|
||||
pset, err := psetv2.NewPsetFromBase64(leaf.Tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, pset.Inputs, 1)
|
||||
require.Len(t, pset.Outputs, 1)
|
||||
|
||||
inputTxID := chainhash.Hash(pset.Inputs[0].PreviousTxid).String()
|
||||
require.Equal(t, leaf.ParentTxid, inputTxID)
|
||||
}
|
||||
|
||||
// check the nodes
|
||||
for _, level := range tree[:len(tree)-2] {
|
||||
for _, node := range level {
|
||||
pset, err := psetv2.NewPsetFromBase64(node.Tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, pset.Inputs, 1)
|
||||
require.Len(t, pset.Outputs, 2)
|
||||
|
||||
inputTxID := chainhash.Hash(pset.Inputs[0].PreviousTxid).String()
|
||||
require.Equal(t, node.ParentTxid, inputTxID)
|
||||
|
||||
children := tree.Children(node.Txid)
|
||||
require.Len(t, children, 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildForfeitTxs(t *testing.T) {
|
||||
builder := txbuilder.NewTxBuilder(&mockedWalletService{}, network.Liquid)
|
||||
|
||||
poolPset, err := psetv2.NewPsetFromBase64(fakePoolTx)
|
||||
require.NoError(t, err)
|
||||
|
||||
poolTxUnsigned, err := poolPset.UnsignedTx()
|
||||
require.NoError(t, err)
|
||||
|
||||
poolTxID := poolTxUnsigned.TxHash().String()
|
||||
|
||||
fixtures := []struct {
|
||||
payments []domain.Payment
|
||||
expectedNumOfForfeitTxs int
|
||||
expectedNumOfConnectors int
|
||||
}{
|
||||
{
|
||||
payments: []domain.Payment{
|
||||
{
|
||||
Id: "0",
|
||||
Inputs: []domain.Vtxo{
|
||||
{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
VOut: 0,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 600,
|
||||
},
|
||||
},
|
||||
{
|
||||
VtxoKey: domain.VtxoKey{
|
||||
Txid: "fd68e3c5796cc7db0a8036d486d5f625b6b2f2c014810ac020e1ac23e82c59d6",
|
||||
VOut: 1,
|
||||
},
|
||||
Receiver: domain.Receiver{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 400,
|
||||
},
|
||||
},
|
||||
},
|
||||
Receivers: []domain.Receiver{
|
||||
{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 600,
|
||||
},
|
||||
{
|
||||
Pubkey: "020000000000000000000000000000000000000000000000000000000000000002",
|
||||
Amount: 400,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedNumOfForfeitTxs: 4,
|
||||
expectedNumOfConnectors: 1,
|
||||
},
|
||||
}
|
||||
|
||||
_, key, err := common.DecodePubKey(testingKey)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, key)
|
||||
|
||||
for _, f := range fixtures {
|
||||
connectors, forfeitTxs, err := builder.BuildForfeitTxs(
|
||||
key, fakePoolTx, f.payments,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, connectors, f.expectedNumOfConnectors)
|
||||
require.Len(t, forfeitTxs, f.expectedNumOfForfeitTxs)
|
||||
|
||||
// decode and check connectors
|
||||
connectorsPsets := make([]*psetv2.Pset, 0, f.expectedNumOfConnectors)
|
||||
for _, pset := range connectors {
|
||||
p, err := psetv2.NewPsetFromBase64(pset)
|
||||
require.NoError(t, err)
|
||||
connectorsPsets = append(connectorsPsets, p)
|
||||
}
|
||||
|
||||
for i, pset := range connectorsPsets {
|
||||
require.Len(t, pset.Inputs, 1)
|
||||
require.Len(t, pset.Outputs, 2)
|
||||
|
||||
expectedInputTxid := poolTxID
|
||||
expectedInputVout := uint32(1)
|
||||
if i > 0 {
|
||||
tx, err := connectorsPsets[i-1].UnsignedTx()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, tx)
|
||||
expectedInputTxid = tx.TxHash().String()
|
||||
}
|
||||
|
||||
inputTxid := chainhash.Hash(pset.Inputs[0].PreviousTxid).String()
|
||||
require.Equal(t, expectedInputTxid, inputTxid)
|
||||
require.Equal(t, expectedInputVout, pset.Inputs[0].PreviousTxIndex)
|
||||
}
|
||||
|
||||
// decode and check forfeit txs
|
||||
forfeitTxsPsets := make([]*psetv2.Pset, 0, f.expectedNumOfForfeitTxs)
|
||||
for _, pset := range forfeitTxs {
|
||||
p, err := psetv2.NewPsetFromBase64(pset)
|
||||
require.NoError(t, err)
|
||||
forfeitTxsPsets = append(forfeitTxsPsets, p)
|
||||
}
|
||||
|
||||
// each forfeit tx should have 2 inputs and 2 outputs
|
||||
for _, pset := range forfeitTxsPsets {
|
||||
require.Len(t, pset.Inputs, 2)
|
||||
require.Len(t, pset.Outputs, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
103
server/internal/infrastructure/tx-builder/dummy/connectors.go
Normal file
103
server/internal/infrastructure/tx-builder/dummy/connectors.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package txbuilder
|
||||
|
||||
import (
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
)
|
||||
|
||||
func createConnectors(
|
||||
poolTxID string,
|
||||
connectorOutputIndex uint32,
|
||||
connectorOutput psetv2.OutputArgs,
|
||||
changeScript []byte,
|
||||
numberOfConnectors uint64,
|
||||
) (connectorsPsets []string, err error) {
|
||||
previousInput := psetv2.InputArgs{
|
||||
Txid: poolTxID,
|
||||
TxIndex: connectorOutputIndex,
|
||||
}
|
||||
|
||||
if numberOfConnectors == 1 {
|
||||
pset, err := psetv2.New(nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
updater, err := psetv2.NewUpdater(pset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = updater.AddInputs([]psetv2.InputArgs{previousInput})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = updater.AddOutputs([]psetv2.OutputArgs{connectorOutput})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base64, err := pset.ToBase64()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []string{base64}, nil
|
||||
}
|
||||
|
||||
// compute the initial amount of the connectors output in pool transaction
|
||||
remainingAmount := connectorAmount * numberOfConnectors
|
||||
|
||||
connectorsPset := make([]string, 0, numberOfConnectors-1)
|
||||
for i := uint64(0); i < numberOfConnectors-1; i++ {
|
||||
// create a new pset
|
||||
pset, err := psetv2.New(nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updater, err := psetv2.NewUpdater(pset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = updater.AddInputs([]psetv2.InputArgs{previousInput})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = updater.AddOutputs([]psetv2.OutputArgs{connectorOutput})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
changeAmount := remainingAmount - connectorOutput.Amount
|
||||
if changeAmount > 0 {
|
||||
changeOutput := psetv2.OutputArgs{
|
||||
Asset: connectorOutput.Asset,
|
||||
Amount: changeAmount,
|
||||
Script: changeScript,
|
||||
}
|
||||
err = updater.AddOutputs([]psetv2.OutputArgs{changeOutput})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tx, _ := pset.UnsignedTx()
|
||||
txid := tx.TxHash().String()
|
||||
|
||||
// make the change the next previousInput
|
||||
previousInput = psetv2.InputArgs{
|
||||
Txid: txid,
|
||||
TxIndex: 1,
|
||||
}
|
||||
}
|
||||
|
||||
base64, err := pset.ToBase64()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
connectorsPset = append(connectorsPset, base64)
|
||||
}
|
||||
|
||||
return connectorsPset, nil
|
||||
}
|
||||
42
server/internal/infrastructure/tx-builder/dummy/forfeit.go
Normal file
42
server/internal/infrastructure/tx-builder/dummy/forfeit.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package txbuilder
|
||||
|
||||
import (
|
||||
"github.com/vulpemventures/go-elements/network"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
)
|
||||
|
||||
func createForfeitTx(
|
||||
connectorInput psetv2.InputArgs,
|
||||
vtxoInput psetv2.InputArgs,
|
||||
vtxoAmount uint64,
|
||||
aspScript []byte,
|
||||
net network.Network,
|
||||
) (forfeitTx string, err error) {
|
||||
pset, err := psetv2.New(nil, nil, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
updater, err := psetv2.NewUpdater(pset)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = updater.AddInputs([]psetv2.InputArgs{connectorInput, vtxoInput})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = updater.AddOutputs([]psetv2.OutputArgs{
|
||||
{
|
||||
Asset: net.AssetID,
|
||||
Amount: vtxoAmount,
|
||||
Script: aspScript,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return pset.ToBase64()
|
||||
}
|
||||
309
server/internal/infrastructure/tx-builder/dummy/tree.go
Normal file
309
server/internal/infrastructure/tx-builder/dummy/tree.go
Normal file
@@ -0,0 +1,309 @@
|
||||
package txbuilder
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/vulpemventures/go-elements/address"
|
||||
"github.com/vulpemventures/go-elements/network"
|
||||
"github.com/vulpemventures/go-elements/payment"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
)
|
||||
|
||||
const (
|
||||
sharedOutputIndex = 0
|
||||
)
|
||||
|
||||
type outputScriptFactory func(leaves []domain.Receiver) ([]byte, error)
|
||||
|
||||
func p2wpkhScript(publicKey *secp256k1.PublicKey, net network.Network) ([]byte, error) {
|
||||
payment := payment.FromPublicKey(publicKey, &net, nil)
|
||||
addr, err := payment.WitnessPubKeyHash()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return address.ToOutputScript(addr)
|
||||
}
|
||||
|
||||
// newOtputScriptFactory returns an output script factory func that lock funds using the ASP public key only on all branches psbt. The leaves are instead locked by the leaf public key.
|
||||
func newOutputScriptFactory(aspPublicKey *secp256k1.PublicKey, net network.Network) outputScriptFactory {
|
||||
return func(leaves []domain.Receiver) ([]byte, error) {
|
||||
aspScript, err := p2wpkhScript(aspPublicKey, net)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch len(leaves) {
|
||||
case 0:
|
||||
return nil, nil
|
||||
case 1: // it's a leaf
|
||||
buf, err := hex.DecodeString(leaves[0].Pubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, err := secp256k1.ParsePubKey(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p2wpkhScript(key, net)
|
||||
default: // it's a branch, lock funds with ASP public key
|
||||
return aspScript, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// congestionTree builder iteratively creates a binary tree of Pset from a set of receivers
|
||||
// it also expect createOutputScript func managing the output script creation and the network to use (mainly for L-BTC asset id)
|
||||
func buildCongestionTree(
|
||||
createOutputScript outputScriptFactory,
|
||||
net network.Network,
|
||||
poolTxID string,
|
||||
receivers []domain.Receiver,
|
||||
) (congestionTree tree.CongestionTree, err error) {
|
||||
var nodes []*node
|
||||
|
||||
for _, r := range receivers {
|
||||
nodes = append(nodes, newLeaf(createOutputScript, net, r))
|
||||
}
|
||||
|
||||
for len(nodes) > 1 {
|
||||
nodes, err = createTreeLevel(nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
psets, err := nodes[0].psets(psetv2.InputArgs{
|
||||
Txid: poolTxID,
|
||||
TxIndex: sharedOutputIndex,
|
||||
}, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
maxLevel := 0
|
||||
for _, psetWithLevel := range psets {
|
||||
if psetWithLevel.level > maxLevel {
|
||||
maxLevel = psetWithLevel.level
|
||||
}
|
||||
}
|
||||
|
||||
congestionTree = make(tree.CongestionTree, maxLevel+1)
|
||||
|
||||
for _, psetWithLevel := range psets {
|
||||
utx, err := psetWithLevel.pset.UnsignedTx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txid := utx.TxHash().String()
|
||||
|
||||
psetB64, err := psetWithLevel.pset.ToBase64()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parentTxid := chainhash.Hash(psetWithLevel.pset.Inputs[0].PreviousTxid).String()
|
||||
|
||||
congestionTree[psetWithLevel.level] = append(congestionTree[psetWithLevel.level], tree.Node{
|
||||
Txid: txid,
|
||||
Tx: psetB64,
|
||||
ParentTxid: parentTxid,
|
||||
Leaf: psetWithLevel.leaf,
|
||||
})
|
||||
}
|
||||
|
||||
return congestionTree, nil
|
||||
}
|
||||
|
||||
func createTreeLevel(nodes []*node) ([]*node, error) {
|
||||
if len(nodes)%2 != 0 {
|
||||
last := nodes[len(nodes)-1]
|
||||
pairs, err := createTreeLevel(nodes[:len(nodes)-1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return append(pairs, last), nil
|
||||
}
|
||||
|
||||
pairs := make([]*node, 0, len(nodes)/2)
|
||||
for i := 0; i < len(nodes); i += 2 {
|
||||
pairs = append(pairs, newBranch(nodes[i], nodes[i+1]))
|
||||
}
|
||||
return pairs, nil
|
||||
}
|
||||
|
||||
// internal struct to build a binary tree of Pset
|
||||
type node struct {
|
||||
receivers []domain.Receiver
|
||||
left *node
|
||||
right *node
|
||||
createOutputScript outputScriptFactory
|
||||
network network.Network
|
||||
}
|
||||
|
||||
// create a node from a single receiver
|
||||
func newLeaf(
|
||||
createOutputScript outputScriptFactory,
|
||||
network network.Network,
|
||||
receiver domain.Receiver,
|
||||
) *node {
|
||||
return &node{
|
||||
receivers: []domain.Receiver{receiver},
|
||||
createOutputScript: createOutputScript,
|
||||
network: network,
|
||||
left: nil,
|
||||
right: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// aggregate two nodes into a branch node
|
||||
func newBranch(
|
||||
left *node,
|
||||
right *node,
|
||||
) *node {
|
||||
return &node{
|
||||
receivers: append(left.receivers, right.receivers...),
|
||||
createOutputScript: left.createOutputScript,
|
||||
network: left.network,
|
||||
left: left,
|
||||
right: right,
|
||||
}
|
||||
}
|
||||
|
||||
// is it the final node of the tree
|
||||
func (n *node) isLeaf() bool {
|
||||
return n.left == nil && n.right == nil
|
||||
}
|
||||
|
||||
// compute the output amount of a node
|
||||
func (n *node) amount() uint64 {
|
||||
var amount uint64
|
||||
for _, r := range n.receivers {
|
||||
amount += r.Amount
|
||||
}
|
||||
return amount
|
||||
}
|
||||
|
||||
// compute the output script of a node
|
||||
func (n *node) script() ([]byte, error) {
|
||||
return n.createOutputScript(n.receivers)
|
||||
}
|
||||
|
||||
// use script & amount to create OutputArgs
|
||||
func (n *node) output() (*psetv2.OutputArgs, error) {
|
||||
script, err := n.script()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &psetv2.OutputArgs{
|
||||
Asset: n.network.AssetID,
|
||||
Amount: n.amount(),
|
||||
Script: script,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// create the node Pset from the previous node Pset represented by input arg
|
||||
// if node is a branch, it adds two outputs to the Pset, one for the left branch and one for the right branch
|
||||
// if node is a leaf, it only adds one output to the Pset (the node output)
|
||||
func (n *node) pset(input psetv2.InputArgs) (*psetv2.Pset, error) {
|
||||
pset, err := psetv2.New(nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updater, err := psetv2.NewUpdater(pset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = updater.AddInputs([]psetv2.InputArgs{input})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if n.isLeaf() {
|
||||
output, err := n.output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = updater.AddOutputs([]psetv2.OutputArgs{*output})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pset, nil
|
||||
}
|
||||
|
||||
outputLeft, err := n.left.output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputRight, err := n.right.output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = updater.AddOutputs([]psetv2.OutputArgs{*outputLeft, *outputRight})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pset, nil
|
||||
}
|
||||
|
||||
type psetWithLevel struct {
|
||||
pset *psetv2.Pset
|
||||
level int
|
||||
leaf bool
|
||||
}
|
||||
|
||||
// create the node pset and all the psets of its children recursively, updating the input arg at each step
|
||||
// the function stops when it reaches a leaf node
|
||||
func (n *node) psets(input psetv2.InputArgs, level int) ([]psetWithLevel, error) {
|
||||
pset, err := n.pset(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodeResult := []psetWithLevel{
|
||||
{pset, level, n.isLeaf()},
|
||||
}
|
||||
|
||||
if n.isLeaf() {
|
||||
return nodeResult, nil
|
||||
}
|
||||
|
||||
unsignedTx, err := pset.UnsignedTx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txID := unsignedTx.TxHash().String()
|
||||
|
||||
psetsLeft, err := n.left.psets(psetv2.InputArgs{
|
||||
Txid: txID,
|
||||
TxIndex: 0,
|
||||
}, level+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
psetsRight, err := n.right.psets(psetv2.InputArgs{
|
||||
Txid: txID,
|
||||
TxIndex: 1,
|
||||
}, level+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return append(nodeResult, append(psetsLeft, psetsRight...)...), nil
|
||||
}
|
||||
46
server/internal/interface/grpc/config.go
Normal file
46
server/internal/interface/grpc/config.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package grpcservice
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Port uint32
|
||||
NoTLS bool
|
||||
}
|
||||
|
||||
func (c Config) Validate() error {
|
||||
lis, err := net.Listen("tcp", c.address())
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid port: %s", err)
|
||||
}
|
||||
defer lis.Close()
|
||||
|
||||
if !c.NoTLS {
|
||||
return fmt.Errorf("tls termination not supported yet")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Config) insecure() bool {
|
||||
return c.NoTLS
|
||||
}
|
||||
|
||||
func (c Config) address() string {
|
||||
return fmt.Sprintf(":%d", c.Port)
|
||||
}
|
||||
|
||||
func (c Config) listener() net.Listener {
|
||||
lis, _ := net.Listen("tcp", c.address())
|
||||
|
||||
if c.insecure() {
|
||||
return lis
|
||||
}
|
||||
return tls.NewListener(lis, c.tlsConfig())
|
||||
}
|
||||
|
||||
func (c Config) tlsConfig() *tls.Config {
|
||||
return nil
|
||||
}
|
||||
305
server/internal/interface/grpc/handlers/arkservice.go
Normal file
305
server/internal/interface/grpc/handlers/arkservice.go
Normal file
@@ -0,0 +1,305 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"sync"
|
||||
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
"github.com/ark-network/ark/common"
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/internal/core/application"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/google/uuid"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type listener struct {
|
||||
id string
|
||||
ch chan *arkv1.GetEventStreamResponse
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
svc application.Service
|
||||
|
||||
listenersLock *sync.Mutex
|
||||
listeners []*listener
|
||||
}
|
||||
|
||||
func NewHandler(service application.Service) arkv1.ArkServiceServer {
|
||||
h := &handler{
|
||||
svc: service,
|
||||
listenersLock: &sync.Mutex{},
|
||||
listeners: make([]*listener, 0),
|
||||
}
|
||||
|
||||
go h.listenToEvents()
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *handler) Ping(ctx context.Context, req *arkv1.PingRequest) (*arkv1.PingResponse, error) {
|
||||
if req.GetPaymentId() == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "missing payment id")
|
||||
}
|
||||
|
||||
if err := h.svc.UpdatePaymentStatus(ctx, req.GetPaymentId()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &arkv1.PingResponse{}, nil
|
||||
}
|
||||
|
||||
func (h *handler) RegisterPayment(ctx context.Context, req *arkv1.RegisterPaymentRequest) (*arkv1.RegisterPaymentResponse, error) {
|
||||
vtxosKeys := make([]domain.VtxoKey, 0, len(req.GetInputs()))
|
||||
for _, input := range req.GetInputs() {
|
||||
vtxosKeys = append(vtxosKeys, domain.VtxoKey{
|
||||
Txid: input.GetTxid(),
|
||||
VOut: input.GetVout(),
|
||||
})
|
||||
}
|
||||
|
||||
id, err := h.svc.SpendVtxos(ctx, vtxosKeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &arkv1.RegisterPaymentResponse{
|
||||
Id: id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *handler) ClaimPayment(ctx context.Context, req *arkv1.ClaimPaymentRequest) (*arkv1.ClaimPaymentResponse, error) {
|
||||
receivers, err := parseReceivers(req.GetOutputs())
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
if err := h.svc.ClaimVtxos(ctx, req.GetId(), receivers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &arkv1.ClaimPaymentResponse{}, nil
|
||||
}
|
||||
|
||||
func (h *handler) FinalizePayment(ctx context.Context, req *arkv1.FinalizePaymentRequest) (*arkv1.FinalizePaymentResponse, error) {
|
||||
forfeitTxs, err := parseTxs(req.GetSignedForfeitTxs())
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
if err := h.svc.SignVtxos(ctx, forfeitTxs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &arkv1.FinalizePaymentResponse{}, nil
|
||||
}
|
||||
|
||||
func (h *handler) Faucet(ctx context.Context, req *arkv1.FaucetRequest) (*arkv1.FaucetResponse, error) {
|
||||
_, pubkey, _, err := parseAddress(req.GetAddress())
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
if err := h.svc.FaucetVtxos(ctx, pubkey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &arkv1.FaucetResponse{}, nil
|
||||
}
|
||||
|
||||
func (h *handler) GetRound(ctx context.Context, req *arkv1.GetRoundRequest) (*arkv1.GetRoundResponse, error) {
|
||||
if len(req.GetTxid()) <= 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "missing pool txid")
|
||||
}
|
||||
|
||||
round, err := h.svc.GetRoundByTxid(ctx, req.GetTxid())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &arkv1.GetRoundResponse{
|
||||
Round: &arkv1.Round{
|
||||
Id: round.Id,
|
||||
Start: round.StartingTimestamp,
|
||||
End: round.EndingTimestamp,
|
||||
Txid: round.Txid,
|
||||
CongestionTree: castCongestionTree(round.CongestionTree),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *handler) GetEventStream(_ *arkv1.GetEventStreamRequest, stream arkv1.ArkService_GetEventStreamServer) error {
|
||||
listener := &listener{
|
||||
id: uuid.NewString(),
|
||||
ch: make(chan *arkv1.GetEventStreamResponse),
|
||||
}
|
||||
|
||||
defer h.removeListener(listener.id)
|
||||
defer close(listener.ch)
|
||||
|
||||
h.pushListener(listener)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-stream.Context().Done():
|
||||
return nil
|
||||
|
||||
case ev := <-listener.ch:
|
||||
if err := stream.Send(ev); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch ev.Event.(type) {
|
||||
case *arkv1.GetEventStreamResponse_RoundFinalized, *arkv1.GetEventStreamResponse_RoundFailed:
|
||||
if err := stream.Send(ev); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) ListVtxos(ctx context.Context, req *arkv1.ListVtxosRequest) (*arkv1.ListVtxosResponse, error) {
|
||||
hrp, userPubkey, aspPubkey, err := parseAddress(req.GetAddress())
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
vtxos, err := h.svc.ListVtxos(ctx, userPubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &arkv1.ListVtxosResponse{
|
||||
Vtxos: vtxoList(vtxos).toProto(hrp, aspPubkey),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *handler) GetPubkey(ctx context.Context, req *arkv1.GetPubkeyRequest) (*arkv1.GetPubkeyResponse, error) {
|
||||
pubkey, err := h.svc.GetPubkey(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &arkv1.GetPubkeyResponse{
|
||||
Pubkey: pubkey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *handler) pushListener(l *listener) {
|
||||
h.listenersLock.Lock()
|
||||
defer h.listenersLock.Unlock()
|
||||
|
||||
h.listeners = append(h.listeners, l)
|
||||
}
|
||||
|
||||
func (h *handler) removeListener(id string) {
|
||||
h.listenersLock.Lock()
|
||||
defer h.listenersLock.Unlock()
|
||||
|
||||
for i, listener := range h.listeners {
|
||||
if listener.id == id {
|
||||
h.listeners = append(h.listeners[:i], h.listeners[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// listenToEvents forwards events from the application layer to the set of listeners
|
||||
func (h *handler) listenToEvents() {
|
||||
channel := h.svc.GetEventsChannel(context.Background())
|
||||
for event := range channel {
|
||||
var ev *arkv1.GetEventStreamResponse
|
||||
|
||||
switch e := event.(type) {
|
||||
case domain.RoundFinalizationStarted:
|
||||
ev = &arkv1.GetEventStreamResponse{
|
||||
Event: &arkv1.GetEventStreamResponse_RoundFinalization{
|
||||
RoundFinalization: &arkv1.RoundFinalizationEvent{
|
||||
Id: e.Id,
|
||||
PoolPartialTx: e.PoolTx,
|
||||
CongestionTree: castCongestionTree(e.CongestionTree),
|
||||
ForfeitTxs: e.UnsignedForfeitTxs,
|
||||
},
|
||||
},
|
||||
}
|
||||
case domain.RoundFinalized:
|
||||
ev = &arkv1.GetEventStreamResponse{
|
||||
Event: &arkv1.GetEventStreamResponse_RoundFinalized{
|
||||
RoundFinalized: &arkv1.RoundFinalizedEvent{
|
||||
Id: e.Id,
|
||||
PoolTxid: e.Txid,
|
||||
},
|
||||
},
|
||||
}
|
||||
case domain.RoundFailed:
|
||||
ev = &arkv1.GetEventStreamResponse{
|
||||
Event: &arkv1.GetEventStreamResponse_RoundFailed{
|
||||
RoundFailed: &arkv1.RoundFailed{
|
||||
Id: e.Id,
|
||||
Reason: e.Err,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if ev != nil {
|
||||
for _, listener := range h.listeners {
|
||||
listener.ch <- ev
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type vtxoList []domain.Vtxo
|
||||
|
||||
func (v vtxoList) toProto(hrp string, aspKey *secp256k1.PublicKey) []*arkv1.Vtxo {
|
||||
list := make([]*arkv1.Vtxo, 0, len(v))
|
||||
for _, vv := range v {
|
||||
addr := vv.OnchainAddress
|
||||
if vv.Pubkey != "" {
|
||||
buf, _ := hex.DecodeString(vv.Pubkey)
|
||||
key, _ := secp256k1.ParsePubKey(buf)
|
||||
addr, _ = common.EncodeAddress(hrp, key, aspKey)
|
||||
}
|
||||
list = append(list, &arkv1.Vtxo{
|
||||
Outpoint: &arkv1.Input{
|
||||
Txid: vv.Txid,
|
||||
Vout: vv.VOut,
|
||||
},
|
||||
Receiver: &arkv1.Output{
|
||||
Address: addr,
|
||||
Amount: vv.Amount,
|
||||
},
|
||||
PoolTxid: vv.PoolTx,
|
||||
Spent: vv.Spent,
|
||||
})
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// castCongestionTree converts a tree.CongestionTree to a repeated arkv1.TreeLevel
|
||||
func castCongestionTree(congestionTree tree.CongestionTree) *arkv1.Tree {
|
||||
levels := make([]*arkv1.TreeLevel, 0, len(congestionTree))
|
||||
for _, level := range congestionTree {
|
||||
levelProto := &arkv1.TreeLevel{
|
||||
Nodes: make([]*arkv1.Node, 0, len(level)),
|
||||
}
|
||||
|
||||
for _, node := range level {
|
||||
levelProto.Nodes = append(levelProto.Nodes, &arkv1.Node{
|
||||
Txid: node.Txid,
|
||||
Tx: node.Tx,
|
||||
ParentTxid: node.ParentTxid,
|
||||
})
|
||||
}
|
||||
|
||||
levels = append(levels, levelProto)
|
||||
}
|
||||
return &arkv1.Tree{
|
||||
Levels: levels,
|
||||
}
|
||||
}
|
||||
64
server/internal/interface/grpc/handlers/utils.go
Normal file
64
server/internal/interface/grpc/handlers/utils.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
"github.com/ark-network/ark/common"
|
||||
"github.com/ark-network/ark/internal/core/domain"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/vulpemventures/go-elements/address"
|
||||
"github.com/vulpemventures/go-elements/psetv2"
|
||||
)
|
||||
|
||||
func parseTxs(txs []string) ([]string, error) {
|
||||
if len(txs) <= 0 {
|
||||
return nil, fmt.Errorf("missing list of forfeit txs")
|
||||
}
|
||||
for _, tx := range txs {
|
||||
if _, err := psetv2.NewPsetFromBase64(tx); err != nil {
|
||||
return nil, fmt.Errorf("invalid tx format")
|
||||
}
|
||||
}
|
||||
return txs, nil
|
||||
}
|
||||
|
||||
func parseAddress(addr string) (string, *secp256k1.PublicKey, *secp256k1.PublicKey, error) {
|
||||
if len(addr) <= 0 {
|
||||
return "", nil, nil, fmt.Errorf("missing address")
|
||||
}
|
||||
return common.DecodeAddress(addr)
|
||||
}
|
||||
|
||||
func parseReceivers(outs []*arkv1.Output) ([]domain.Receiver, error) {
|
||||
receivers := make([]domain.Receiver, 0, len(outs))
|
||||
for _, out := range outs {
|
||||
if out.GetAmount() == 0 {
|
||||
return nil, fmt.Errorf("missing output amount")
|
||||
}
|
||||
if len(out.GetAddress()) <= 0 {
|
||||
return nil, fmt.Errorf("missing output address")
|
||||
}
|
||||
var pubkey, addr string
|
||||
_, pk, _, err := common.DecodeAddress(out.GetAddress())
|
||||
if err != nil {
|
||||
if _, err := address.ToOutputScript(out.GetAddress()); err != nil {
|
||||
return nil, fmt.Errorf("invalid output address: unknown format")
|
||||
}
|
||||
if isConf, _ := address.IsConfidential(out.GetAddress()); isConf {
|
||||
return nil, fmt.Errorf("invalid output address: must be unconfidential")
|
||||
}
|
||||
addr = out.GetAddress()
|
||||
}
|
||||
if pk != nil {
|
||||
pubkey = hex.EncodeToString(pk.SerializeCompressed())
|
||||
}
|
||||
receivers = append(receivers, domain.Receiver{
|
||||
Pubkey: pubkey,
|
||||
Amount: out.GetAmount(),
|
||||
OnchainAddress: addr,
|
||||
})
|
||||
}
|
||||
return receivers, nil
|
||||
}
|
||||
16
server/internal/interface/grpc/interceptors/interceptor.go
Normal file
16
server/internal/interface/grpc/interceptors/interceptor.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package interceptors
|
||||
|
||||
import (
|
||||
middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// UnaryInterceptor returns the unary interceptor
|
||||
func UnaryInterceptor() grpc.ServerOption {
|
||||
return grpc.UnaryInterceptor(middleware.ChainUnaryServer(unaryLogger))
|
||||
}
|
||||
|
||||
// StreamInterceptor returns the stream interceptor with a logrus log
|
||||
func StreamInterceptor() grpc.ServerOption {
|
||||
return grpc.StreamInterceptor(middleware.ChainStreamServer(streamLogger))
|
||||
}
|
||||
28
server/internal/interface/grpc/interceptors/logger.go
Normal file
28
server/internal/interface/grpc/interceptors/logger.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package interceptors
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func unaryLogger(
|
||||
ctx context.Context,
|
||||
req interface{},
|
||||
info *grpc.UnaryServerInfo,
|
||||
handler grpc.UnaryHandler,
|
||||
) (interface{}, error) {
|
||||
log.Debugf("gRPC method: %s", info.FullMethod)
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
func streamLogger(
|
||||
srv interface{},
|
||||
stream grpc.ServerStream,
|
||||
info *grpc.StreamServerInfo,
|
||||
handler grpc.StreamHandler,
|
||||
) error {
|
||||
log.Debugf("gRPC method: %s", info.FullMethod)
|
||||
return handler(srv, stream)
|
||||
}
|
||||
0
server/internal/interface/grpc/permissions/.gitkeep
Executable file
0
server/internal/interface/grpc/permissions/.gitkeep
Executable file
64
server/internal/interface/grpc/service.go
Normal file
64
server/internal/interface/grpc/service.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package grpcservice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
appconfig "github.com/ark-network/ark/internal/app-config"
|
||||
interfaces "github.com/ark-network/ark/internal/interface"
|
||||
"github.com/ark-network/ark/internal/interface/grpc/handlers"
|
||||
"github.com/ark-network/ark/internal/interface/grpc/interceptors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
type service struct {
|
||||
config Config
|
||||
appConfig *appconfig.Config
|
||||
server *grpc.Server
|
||||
}
|
||||
|
||||
func NewService(
|
||||
svcConfig Config, appConfig *appconfig.Config,
|
||||
) (interfaces.Service, error) {
|
||||
if err := svcConfig.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("invalid service config: %s", err)
|
||||
}
|
||||
if err := appConfig.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("invalid app config: %s", err)
|
||||
}
|
||||
|
||||
grpcConfig := []grpc.ServerOption{
|
||||
interceptors.UnaryInterceptor(), interceptors.StreamInterceptor(),
|
||||
}
|
||||
if !svcConfig.NoTLS {
|
||||
return nil, fmt.Errorf("tls termination not supported yet")
|
||||
}
|
||||
creds := insecure.NewCredentials()
|
||||
grpcConfig = append(grpcConfig, grpc.Creds(creds))
|
||||
server := grpc.NewServer(grpcConfig...)
|
||||
handler := handlers.NewHandler(appConfig.AppService())
|
||||
arkv1.RegisterArkServiceServer(server, handler)
|
||||
return &service{svcConfig, appConfig, server}, nil
|
||||
}
|
||||
|
||||
func (s *service) Start() error {
|
||||
// nolint:all
|
||||
go s.server.Serve(s.config.listener())
|
||||
log.Infof("started listening at %s", s.config.address())
|
||||
|
||||
if err := s.appConfig.AppService().Start(); err != nil {
|
||||
return fmt.Errorf("failed to start app service: %s", err)
|
||||
}
|
||||
log.Info("started app service")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *service) Stop() {
|
||||
s.server.Stop()
|
||||
log.Info("stopped grpc server")
|
||||
s.appConfig.AppService().Stop()
|
||||
log.Info("stopped app service")
|
||||
}
|
||||
6
server/internal/interface/service.go
Executable file
6
server/internal/interface/service.go
Executable file
@@ -0,0 +1,6 @@
|
||||
package interfaces
|
||||
|
||||
type Service interface {
|
||||
Start() error
|
||||
Stop()
|
||||
}
|
||||
0
server/internal/test/.gitkeep
Executable file
0
server/internal/test/.gitkeep
Executable file
0
server/pkg/.gitkeep
Normal file
0
server/pkg/.gitkeep
Normal file
18
server/scripts/build
Executable file
18
server/scripts/build
Executable file
@@ -0,0 +1,18 @@
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
PARENT_PATH=$(dirname $(
|
||||
cd $(dirname $0)
|
||||
pwd -P
|
||||
))
|
||||
|
||||
OS=$(eval "go env GOOS")
|
||||
ARCH=$(eval "go env GOARCH")
|
||||
|
||||
pushd $PARENT_PATH
|
||||
mkdir -p build
|
||||
GO111MODULE=on go build -o build/arkd-$OS-$ARCH cmd/arkd/main.go
|
||||
popd
|
||||
|
||||
23
server/scripts/build-all
Executable file
23
server/scripts/build-all
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
PARENT_PATH=$(dirname $(
|
||||
cd $(dirname $0)
|
||||
pwd -P
|
||||
))
|
||||
|
||||
declare -a OS=("darwin" "linux")
|
||||
declare -a ARCH=("amd64" "arm64")
|
||||
|
||||
pushd $PARENT_PATH
|
||||
mkdir -p build
|
||||
|
||||
for os in "${OS[@]}"; do
|
||||
for arch in "${ARCH[@]}"; do
|
||||
echo "Building for $os $arch"
|
||||
GOOS=$os GOARCH=$arch go build -o build/arkd-$os-$arch cmd/arkd/main.go
|
||||
done
|
||||
done
|
||||
|
||||
popd
|
||||
Reference in New Issue
Block a user