diff --git a/client/cmd/bisonw-desktop/go.mod b/client/cmd/bisonw-desktop/go.mod index b46fdd8752..90062f1eda 100644 --- a/client/cmd/bisonw-desktop/go.mod +++ b/client/cmd/bisonw-desktop/go.mod @@ -95,7 +95,7 @@ require ( github.com/decred/dcrd/database/v3 v3.0.2 // indirect github.com/decred/dcrd/dcrec v1.0.1 // indirect github.com/decred/dcrd/dcrec/edwards/v2 v2.0.3 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.1-0.20240912225101-7333f805850b // indirect github.com/decred/dcrd/dcrjson/v4 v4.1.0 // indirect github.com/decred/dcrd/dcrutil/v4 v4.0.2 // indirect github.com/decred/dcrd/gcs/v4 v4.1.0 // indirect diff --git a/client/cmd/bisonw-desktop/go.sum b/client/cmd/bisonw-desktop/go.sum index 8c64429d8b..bbf30c4cc4 100644 --- a/client/cmd/bisonw-desktop/go.sum +++ b/client/cmd/bisonw-desktop/go.sum @@ -297,8 +297,8 @@ github.com/decred/dcrd/dcrec v1.0.1/go.mod h1:CO+EJd8eHFb8WHa84C7ZBkXsNUIywaTHb+ github.com/decred/dcrd/dcrec/edwards/v2 v2.0.3 h1:l/lhv2aJCUignzls81+wvga0TFlyoZx8QxRMQgXpZik= github.com/decred/dcrd/dcrec/edwards/v2 v2.0.3/go.mod h1:AKpV6+wZ2MfPRJnTbQ6NPgWrKzbe9RCIlCF/FKzMtM8= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.1-0.20240912225101-7333f805850b h1:HIjTBv19sQRjUiifEKPy6BLFKkOtV44uCnDThmYQ37s= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.1-0.20240912225101-7333f805850b/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/dcrjson/v4 v4.1.0 h1:WJVogRnYnNxB5hWoGHODvP4fNTG1JycTuHHKt/XucHk= github.com/decred/dcrd/dcrjson/v4 v4.1.0/go.mod h1:2qVikafVF9/X3PngQVmqkbUbyAl32uik0k/kydgtqMc= github.com/decred/dcrd/dcrutil/v4 v4.0.2 h1:eIl3E6gGln54qE8nk5o5lLtjh2/9C2Rz63OpD662h+8= diff --git a/dex/testing/loadbot/go.mod b/dex/testing/loadbot/go.mod index 73984596d6..d8921f702b 100644 --- a/dex/testing/loadbot/go.mod +++ b/dex/testing/loadbot/go.mod @@ -76,7 +76,7 @@ require ( github.com/decred/dcrd/database/v3 v3.0.2 // indirect github.com/decred/dcrd/dcrec v1.0.1 // indirect github.com/decred/dcrd/dcrec/edwards/v2 v2.0.3 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.1-0.20240912225101-7333f805850b // indirect github.com/decred/dcrd/dcrjson/v4 v4.1.0 // indirect github.com/decred/dcrd/dcrutil/v4 v4.0.2 // indirect github.com/decred/dcrd/gcs/v4 v4.1.0 // indirect diff --git a/dex/testing/loadbot/go.sum b/dex/testing/loadbot/go.sum index 7dd530c1dc..f29e1edae8 100644 --- a/dex/testing/loadbot/go.sum +++ b/dex/testing/loadbot/go.sum @@ -293,8 +293,8 @@ github.com/decred/dcrd/dcrec v1.0.1/go.mod h1:CO+EJd8eHFb8WHa84C7ZBkXsNUIywaTHb+ github.com/decred/dcrd/dcrec/edwards/v2 v2.0.3 h1:l/lhv2aJCUignzls81+wvga0TFlyoZx8QxRMQgXpZik= github.com/decred/dcrd/dcrec/edwards/v2 v2.0.3/go.mod h1:AKpV6+wZ2MfPRJnTbQ6NPgWrKzbe9RCIlCF/FKzMtM8= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.1-0.20240912225101-7333f805850b h1:HIjTBv19sQRjUiifEKPy6BLFKkOtV44uCnDThmYQ37s= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.1-0.20240912225101-7333f805850b/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/dcrjson/v4 v4.1.0 h1:WJVogRnYnNxB5hWoGHODvP4fNTG1JycTuHHKt/XucHk= github.com/decred/dcrd/dcrjson/v4 v4.1.0/go.mod h1:2qVikafVF9/X3PngQVmqkbUbyAl32uik0k/kydgtqMc= github.com/decred/dcrd/dcrutil/v4 v4.0.2 h1:eIl3E6gGln54qE8nk5o5lLtjh2/9C2Rz63OpD662h+8= diff --git a/go.mod b/go.mod index 3ff5abe61e..d8c0baa477 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,10 @@ go 1.21 require ( decred.org/dcrwallet/v4 v4.2.0 + filippo.io/edwards25519 v1.0.0 fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93 + github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 + github.com/athanorlabs/go-dleq v0.1.0 github.com/btcsuite/btcd v0.24.2-beta.rc1.0.20240625142744-cc26860b4026 github.com/btcsuite/btcd/btcec/v2 v2.3.4 github.com/btcsuite/btcd/btcutil v1.1.5 @@ -31,7 +34,7 @@ require ( github.com/decred/dcrd/crypto/blake256 v1.1.0 github.com/decred/dcrd/dcrec v1.0.1 github.com/decred/dcrd/dcrec/edwards/v2 v2.0.3 - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.1-0.20240912225101-7333f805850b github.com/decred/dcrd/dcrjson/v4 v4.1.0 github.com/decred/dcrd/dcrutil/v4 v4.0.2 github.com/decred/dcrd/gcs/v4 v4.1.0 @@ -43,6 +46,7 @@ require ( github.com/decred/go-socks v1.1.0 github.com/decred/slog v1.2.0 github.com/decred/vspd/types/v2 v2.1.0 + github.com/dev-warrior777/go-monero v0.1.0 github.com/dgraph-io/badger v1.6.2 github.com/ethereum/go-ethereum v1.14.8 github.com/fatih/color v1.16.0 @@ -51,6 +55,7 @@ require ( github.com/gcash/bchutil v0.0.0-20210113190856-6ea28dff4000 github.com/go-chi/chi/v5 v5.0.1 github.com/gorilla/websocket v1.5.1 + github.com/haven-protocol-org/monero-go-utils v0.0.0-20211126154105-058b2666f217 github.com/huandu/skiplist v1.2.0 github.com/jessevdk/go-flags v1.5.0 github.com/jrick/logrotate v1.0.0 @@ -81,7 +86,6 @@ require ( github.com/StackExchange/wmi v1.2.1 // indirect github.com/VictoriaMetrics/fastcache v1.12.2 // indirect github.com/aead/siphash v1.0.1 // indirect - github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/btcsuite/btcwallet/wallet/txrules v1.2.1 // indirect @@ -110,7 +114,6 @@ require ( github.com/decred/dcrd/database/v3 v3.0.2 // indirect github.com/decred/dcrd/lru v1.1.2 // indirect github.com/decred/dcrd/mixing v0.4.2 // indirect - github.com/decred/vspd/client/v3 v3.0.0 // indirect github.com/decred/vspd/client/v4 v4.0.0 // indirect github.com/decred/vspd/types/v3 v3.0.0 // indirect github.com/dgraph-io/ristretto v0.0.2 // indirect @@ -177,8 +180,6 @@ require ( github.com/zquestz/grab v0.0.0-20190224022517-abcee96e61b1 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/sys v0.23.0 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect - google.golang.org/grpc v1.56.3 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect diff --git a/go.sum b/go.sum index bb1d110807..0ecbe17a07 100644 --- a/go.sum +++ b/go.sum @@ -38,15 +38,13 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl 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= contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= -decred.org/cspp/v2 v2.2.0 h1:VSOUC1w0Wo+QOGS0r1XO6TLnO16X67KuvpDmRRYyr08= -decred.org/cspp/v2 v2.2.0/go.mod h1:9nO3bfvCheOPIFZw5f6sRQ42CjBFB5RKSaJ9Iq6G4MA= decred.org/cspp/v2 v2.3.0 h1:GC8emJnLbOVAkgBTHK/1wy6o/m0AVsN1r4m1ZnZZWjo= decred.org/cspp/v2 v2.3.0/go.mod h1:9nO3bfvCheOPIFZw5f6sRQ42CjBFB5RKSaJ9Iq6G4MA= -decred.org/dcrwallet/v4 v4.1.1 h1:imwPBboytp1PH6V8q7/JLTHiKgj/Scq9a3I1WmnJv0Y= -decred.org/dcrwallet/v4 v4.1.1/go.mod h1:WxerkRcUGVreJsAI0ptCBPUujPUmWncbdYbme8Kl5r0= decred.org/dcrwallet/v4 v4.2.0 h1:V+RO80FnRuJCPoOQIFKb26JM5Yn1+oXS11x8zZPR1T4= decred.org/dcrwallet/v4 v4.2.0/go.mod h1:VLK+FIBD4n/K7v5Sc+rYcSB4j3PiGm1MKDpxXjrlE1s= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= +filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93 h1:V2IC9t0Zj9Ur6qDbfhUuzVmIvXKFyxZXRJyigUvovs4= fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= @@ -104,6 +102,8 @@ github.com/ashanbrown/forbidigo v1.1.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBF github.com/ashanbrown/forbidigo v1.2.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI= github.com/ashanbrown/makezero v0.0.0-20210308000810-4155955488a0/go.mod h1:oG9Dnez7/ESBqc4EdrdNlryeo7d0KcW1ftXHm7nU/UU= github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde/go.mod h1:oG9Dnez7/ESBqc4EdrdNlryeo7d0KcW1ftXHm7nU/UU= +github.com/athanorlabs/go-dleq v0.1.0 h1:0/llWZG8fz2uintMBKOiBC502zCsDA8nt8vxI73W9Qc= +github.com/athanorlabs/go-dleq v0.1.0/go.mod h1:DWry6jSD7A13MKmeZA0AX3/xBeQCXDoygX99VPwL3yU= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -273,8 +273,6 @@ github.com/decred/dcrd/blockchain/standalone/v2 v2.2.1 h1:zeI9CHkLM9be4QOBmIAtoP github.com/decred/dcrd/blockchain/standalone/v2 v2.2.1/go.mod h1:yXZz/EgWdGw5nqMEvyKj/iXZ9I2VSyO95xKj6mRUMIM= github.com/decred/dcrd/blockchain/v5 v5.0.1 h1:IGr8rJsgBVKDBI8STzeuGF6Mej0xbIX4gVVBA9yEMRU= github.com/decred/dcrd/blockchain/v5 v5.0.1/go.mod h1:LtSV1+u8aBQzlExAQcl4HIJ6Bfi5f6Rvws/9euH4mDA= -github.com/decred/dcrd/certgen v1.1.3 h1:MYENpBWVSP6FkkLBSSnaBGEOWobPcgYBLDDo88szi9c= -github.com/decred/dcrd/certgen v1.1.3/go.mod h1:Od5y39J+r2ZlvrizyWu2cylcYu0+emTTVm3eix4W8bw= github.com/decred/dcrd/certgen v1.2.0 h1:FF6XXV//5q38/c6QbGQdR35ZJz0GPIkejsZZU3oHuBQ= github.com/decred/dcrd/certgen v1.2.0/go.mod h1:LRh6dF2WPQeDA6QQSZE+SfK7AL6FuFtCRDHZf8DyGzg= github.com/decred/dcrd/chaincfg/chainhash v1.0.4 h1:zRCv6tdncLfLTKYqu7hrXvs7hW+8FO/NvwoFvGsrluU= @@ -286,8 +284,6 @@ github.com/decred/dcrd/connmgr/v3 v3.1.2/go.mod h1:tdbErFiNOuy/sHrX2mwaOk+r1HLs3 github.com/decred/dcrd/container/lru v1.0.0 h1:7foQymtbu18aQWYiY9RnNIeE+kvpiN+fiBQ3+viyJjI= github.com/decred/dcrd/container/lru v1.0.0/go.mod h1:vlPwj0l+IzAHhQSsbgQnJgO5Cte78+yI065V+Mc5PRQ= 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/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/crypto/rand v1.0.0 h1:Ah9Asl36OZt09sGSMbJZuL1HfwGdlC38q/ZUeLDVKRg= @@ -301,8 +297,8 @@ github.com/decred/dcrd/dcrec v1.0.1/go.mod h1:CO+EJd8eHFb8WHa84C7ZBkXsNUIywaTHb+ github.com/decred/dcrd/dcrec/edwards/v2 v2.0.3 h1:l/lhv2aJCUignzls81+wvga0TFlyoZx8QxRMQgXpZik= github.com/decred/dcrd/dcrec/edwards/v2 v2.0.3/go.mod h1:AKpV6+wZ2MfPRJnTbQ6NPgWrKzbe9RCIlCF/FKzMtM8= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.1-0.20240912225101-7333f805850b h1:HIjTBv19sQRjUiifEKPy6BLFKkOtV44uCnDThmYQ37s= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.1-0.20240912225101-7333f805850b/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/dcrjson/v4 v4.1.0 h1:WJVogRnYnNxB5hWoGHODvP4fNTG1JycTuHHKt/XucHk= github.com/decred/dcrd/dcrjson/v4 v4.1.0/go.mod h1:2qVikafVF9/X3PngQVmqkbUbyAl32uik0k/kydgtqMc= github.com/decred/dcrd/dcrutil/v4 v4.0.2 h1:eIl3E6gGln54qE8nk5o5lLtjh2/9C2Rz63OpD662h+8= @@ -314,8 +310,6 @@ github.com/decred/dcrd/hdkeychain/v3 v3.1.2/go.mod h1:FnNJmZ7jqUDeAo6/c/xkQi5cux github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/decred/dcrd/lru v1.1.2 h1:KdCzlkxppuoIDGEvCGah1fZRicrDH36IipvlB1ROkFY= github.com/decred/dcrd/lru v1.1.2/go.mod h1:gEdCVgXs1/YoBvFWt7Scgknbhwik3FgVSzlnCcXL2N8= -github.com/decred/dcrd/mixing v0.3.0 h1:eUHpTdwTqXUllnn1ZYLfxucW/2UOkMmx4CyztipTJ9g= -github.com/decred/dcrd/mixing v0.3.0/go.mod h1:W3K7yJKmoI03G2U5Yw+HSRNe6lLBegi63ZR6fFLnM9c= github.com/decred/dcrd/mixing v0.4.2 h1:mpt2pNIFTI6L1hXrieAWJTQJv5t9WzHcNnhI+tnAG90= github.com/decred/dcrd/mixing v0.4.2/go.mod h1:VF87lOn41kitgWVOwmXoB4qMYF7+bxItZXyw4JfW3EQ= github.com/decred/dcrd/rpc/jsonrpc/types/v4 v4.3.0 h1:l0DnCcILTNrpy8APF3FLN312ChpkQaAuW30aC/RgBaw= @@ -330,8 +324,6 @@ github.com/decred/go-socks v1.1.0 h1:dnENcc0KIqQo3HSXdgboXAHgqsCIutkqq6ntQjYtm2U github.com/decred/go-socks v1.1.0/go.mod h1:sDhHqkZH0X4JjSa02oYOGhcGHYp12FsY1jQ/meV8md0= github.com/decred/slog v1.2.0 h1:soHAxV52B54Di3WtKLfPum9OFfWqwtf/ygf9njdfnPM= github.com/decred/slog v1.2.0/go.mod h1:kVXlGnt6DHy2fV5OjSeuvCJ0OmlmTF6LFpEPMu/fOY0= -github.com/decred/vspd/client/v3 v3.0.0 h1:4gAGDTeIU0r4quCxmV5Ez7T2J+P+OLPSibkCF+/Yb6w= -github.com/decred/vspd/client/v3 v3.0.0/go.mod h1:5pfPvIa6V38AmophMrKUCl3KMpEIxcltWtgL2R+wsW8= github.com/decred/vspd/client/v4 v4.0.0 h1:8qFB3t5CyBFuUU0LarJFsq/+twfUMPALwzOsB99r818= github.com/decred/vspd/client/v4 v4.0.0/go.mod h1:jhqu4KGGOskQcPVZ3XZLVZ1Wgkc9GQo+oEipr3gGODg= github.com/decred/vspd/types/v2 v2.1.0 h1:cUVlmHPeLVsksPRnr2WHsmC2t1Skl6g1WH0HmpcPS7w= @@ -340,6 +332,8 @@ github.com/decred/vspd/types/v3 v3.0.0 h1:jHlQIpp6aCjIcFs8WE3AaVCJe1kgepNTq+nkBK github.com/decred/vspd/types/v3 v3.0.0/go.mod h1:hwifRZu6tpkbhSg2jZCUwuPaO/oETgbSCWCYJd4XepY= github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= +github.com/dev-warrior777/go-monero v0.1.0 h1:86JJRiYNk2muLJFWM6kgb7qZh1gE/ETjvjHSW+3Rn5c= +github.com/dev-warrior777/go-monero v0.1.0/go.mod h1:DSf/k695WklA8lTl1EzqNLhtKP3BE3x5KU6tsPrXp4k= github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= github.com/dgraph-io/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQn3po= @@ -631,6 +625,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/haven-protocol-org/monero-go-utils v0.0.0-20211126154105-058b2666f217 h1:CflMOYZHhaBo+7up92oOYcesIG+qDCAKdJo+niKBFWM= +github.com/haven-protocol-org/monero-go-utils v0.0.0-20211126154105-058b2666f217/go.mod h1:vSMDRpw62HGWO1Fi9DQwfgs4e3JCbt475GWY/W5DQZI= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= @@ -710,8 +706,6 @@ github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= @@ -1196,8 +1190,6 @@ golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1294,8 +1286,6 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1316,8 +1306,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1406,16 +1394,12 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1428,8 +1412,6 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/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.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1611,8 +1593,6 @@ google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210426193834-eac7f76ac494/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210521181308-5ccab8a35a9a/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1641,8 +1621,6 @@ google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= -google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= diff --git a/internal/adaptorsigs/adaptor.go b/internal/adaptorsigs/adaptor.go new file mode 100644 index 0000000000..5cb0a2e1f1 --- /dev/null +++ b/internal/adaptorsigs/adaptor.go @@ -0,0 +1,489 @@ +package adaptorsigs + +import ( + "errors" + "fmt" + + "github.com/decred/dcrd/crypto/blake256" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/decred/dcrd/dcrec/secp256k1/v4/schnorr" +) + +// AdaptorSignatureSize is the size of an encoded adaptor Schnorr signature. +const AdaptorSignatureSize = 97 + +// scalarSize is the size of an encoded big endian scalar. +const scalarSize = 32 + +var ( + // rfc6979ExtraDataV0 is the extra data to feed to RFC6979 when generating + // the deterministic nonce for the EC-Schnorr-DCRv0 scheme. This ensures + // the same nonce is not generated for the same message and key as for other + // signing algorithms such as ECDSA. + // + // It is equal to BLAKE-256([]byte("EC-Schnorr-DCRv0")). + rfc6979ExtraDataV0 = [32]byte{ + 0x0b, 0x75, 0xf9, 0x7b, 0x60, 0xe8, 0xa5, 0x76, + 0x28, 0x76, 0xc0, 0x04, 0x82, 0x9e, 0xe9, 0xb9, + 0x26, 0xfa, 0x6f, 0x0d, 0x2e, 0xea, 0xec, 0x3a, + 0x4f, 0xd1, 0x44, 0x6a, 0x76, 0x83, 0x31, 0xcb, + } +) + +type affinePoint struct { + x secp256k1.FieldVal + y secp256k1.FieldVal +} + +func (p *affinePoint) asJacobian() *secp256k1.JacobianPoint { + var result secp256k1.JacobianPoint + result.X.Set(&p.x) + result.Y.Set(&p.y) + result.Z.SetInt(1) + return &result +} + +// AdaptorSignature is a signature with auxillary data that commits to a hidden +// value. When an adaptor signature is combined with a corresponding signature, +// the hidden value is revealed. Alternatively, when combined with a hidden +// value, the adaptor reveals the signature. +// +// An adaptor signature is created by either doing a public or private key +// tweak of a valid schnorr signature. A private key tweak can only be done by +// a party who knows the hidden value, and a public key tweak can be done by +// a party that only knows the point on the secp256k1 curve derived by the +// multiplying the hidden value by the generator point. +// +// Generally the workflow of using adaptor signatures is the following: +// 1. Party A randomly selects a hidden value and creates a private key +// modified adaptor signature of something for which party B requires +// a valid signature. +// 2. The Party B sees the PublicTweak in the adaptor signature, and creates +// a public key tweaked adaptor signature for something that party A +// requires a valid signature. +// 3. Since party A knows the hidden value, they can use the hidden value to +// create a valid signature from the public key tweaked adaptor signature. +// 4. When the valid signature is revealed, by being posted to the blockchain, +// party B can recover the tweak and use it to decrypt the private key +// tweaked adaptor signature that party A originally sent them. +type AdaptorSignature struct { + r secp256k1.FieldVal + s secp256k1.ModNScalar + t affinePoint + pubKeyTweak bool +} + +// Serialize returns a serialized adaptor signature in the following format: +// +// sig[0:32] x coordinate of the point R, encoded as a big-endian uint256 +// sig[32:64] s, encoded also as big-endian uint256 +// sig[64:96] x coordinate of the point T, encoded as a big-endian uint256 +// sig[96] first bit is 1 if the signature is public key tweaked, second bit +// is 1 if the y coordinate of T is odd. +func (sig *AdaptorSignature) Serialize() []byte { + var b [AdaptorSignatureSize]byte + sig.r.PutBytesUnchecked(b[0:32]) + sig.s.PutBytesUnchecked(b[32:64]) + sig.t.x.PutBytesUnchecked(b[64:96]) + if sig.pubKeyTweak { + b[96] = 1 + } + b[96] |= byte(sig.t.y.IsOddBit()) << 1 + return b[:] +} + +// ParseAdaptorSignature parses an adaptor signature from a serialized format. +func ParseAdaptorSignature(b []byte) (*AdaptorSignature, error) { + if len(b) != AdaptorSignatureSize { + str := fmt.Sprintf("malformed signature: wrong size: %d", len(b)) + return nil, errors.New(str) + } + + var r secp256k1.FieldVal + if overflow := r.SetBytes((*[32]byte)(b[0:32])); overflow > 0 { + str := "invalid signature: r >= field prime" + return nil, errors.New(str) + } + + var s secp256k1.ModNScalar + if overflow := s.SetBytes((*[32]byte)(b[32:64])); overflow > 0 { + str := "invalid signature: s >= group order" + return nil, errors.New(str) + } + + var t affinePoint + if overflow := t.x.SetBytes((*[32]byte)(b[64:96])); overflow > 0 { + str := "invalid signature: t.x >= field prime" + return nil, errors.New(str) + } + + isOdd := (b[96]>>1)&1 == 1 + if valid := secp256k1.DecompressY(&t.x, isOdd, &t.y); !valid { + str := "invalid signature: not for a valid curve point" + return nil, errors.New(str) + } + + pubKeyTweak := b[96]&1 == 1 + + return &AdaptorSignature{ + r: r, + s: s, + t: t, + pubKeyTweak: pubKeyTweak, + }, nil +} + +// IsEqual returns true if the adaptor signature is equal to another. +func (sig *AdaptorSignature) IsEqual(otherSig *AdaptorSignature) bool { + return sig.r.Equals(&otherSig.r) && + sig.s.Equals(&otherSig.s) && + sig.t.x.Equals(&otherSig.t.x) && + sig.t.y.Equals(&otherSig.t.y) && + sig.pubKeyTweak == otherSig.pubKeyTweak +} + +// schnorrAdaptorVerify verifies that the adaptor signature will result in a +// valid signature when decrypted with the tweak. +func schnorrAdaptorVerify(sig *AdaptorSignature, hash []byte, pubKey *secp256k1.PublicKey) error { + // The algorithm for producing a EC-Schnorr-DCRv0 signature is as follows: + // This deviates from the original algorithm in step 7. + // + // 1. Fail if m is not 32 bytes + // 2. Fail if Q is not a point on the curve + // 3. Fail if r >= p + // 4. Fail if s >= n + // 5. e = BLAKE-256(r || m) (Ensure r is padded to 32 bytes) + // 6. Fail if e >= n + // 7. R = s*G + e*Q - T + // 8. Fail if R is the point at infinity + // 9. Fail if R.y is odd + // 10. Verified if R.x == r + + // Step 1. + // + // Fail if m is not 32 bytes + if len(hash) != scalarSize { + str := fmt.Sprintf("wrong size for message (got %v, want %v)", + len(hash), scalarSize) + return errors.New(str) + } + + // Step 2. + // + // Fail if Q is not a point on the curve + if !pubKey.IsOnCurve() { + str := "pubkey point is not on curve" + return errors.New(str) + } + + // Step 3. + // + // Fail if r >= p + // + // Note this is already handled by the fact r is a field element. + + // Step 4. + // + // Fail if s >= n + // + // Note this is already handled by the fact s is a mod n scalar. + + // Step 5. + // + // e = BLAKE-256(r || m) (Ensure r is padded to 32 bytes) + var commitmentInput [scalarSize * 2]byte + sig.r.PutBytesUnchecked(commitmentInput[0:scalarSize]) + copy(commitmentInput[scalarSize:], hash[:]) + commitment := blake256.Sum256(commitmentInput[:]) + + // Step 6. + // + // Fail if e >= n + var e secp256k1.ModNScalar + if overflow := e.SetBytes(&commitment); overflow != 0 { + str := "hash of (R || m) too big" + return errors.New(str) + } + + // Step 7. + // + // R = s*G + e*Q - T + var Q, R, sG, eQ, encryptedR secp256k1.JacobianPoint + pubKey.AsJacobian(&Q) + secp256k1.ScalarBaseMultNonConst(&sig.s, &sG) + secp256k1.ScalarMultNonConst(&e, &Q, &eQ) + secp256k1.AddNonConst(&sG, &eQ, &R) + tInv := sig.t.asJacobian() + tInv.Y.Negate(1) + secp256k1.AddNonConst(&R, tInv, &encryptedR) + + // Step 8. + // + // Fail if R is the point at infinity + if (encryptedR.X.IsZero() && encryptedR.Y.IsZero()) || encryptedR.Z.IsZero() { + str := "calculated R point is the point at infinity" + return errors.New(str) + } + + // Step 9. + // + // Fail if R.y is odd + // + // Note that R must be in affine coordinates for this check. + encryptedR.ToAffine() + if encryptedR.Y.IsOdd() { + str := "calculated encrypted R y-value is odd" + return errors.New(str) + } + + // Step 10. + // + // Verified if R.x == r + // + // Note that R must be in affine coordinates for this check. + if !sig.r.Equals(&encryptedR.X) { + str := "calculated R point was not given R" + return errors.New(str) + } + + return nil +} + +// Verify checks that the adaptor signature, when decrypted using the tweak, +// will result in a valid schnorr signature for the given hash and public key. +func (sig *AdaptorSignature) Verify(hash []byte, pubKey *secp256k1.PublicKey) error { + if sig.pubKeyTweak { + return fmt.Errorf("only priv key tweaked adaptors can be verified") + } + + return schnorrAdaptorVerify(sig, hash, pubKey) +} + +// Decrypt returns a valid schnorr signature if the tweak is correct. +// This may not be a valid signature if the tweak is incorrect. The caller can +// use Verify to make sure it is a valid signature. +func (sig *AdaptorSignature) Decrypt(tweak *secp256k1.ModNScalar) (*schnorr.Signature, error) { + var expectedT secp256k1.JacobianPoint + secp256k1.ScalarBaseMultNonConst(tweak, &expectedT) + if !expectedT.EquivalentNonConst(sig.t.asJacobian()) { + return nil, fmt.Errorf("tweak does not match expected value") + } + + s := new(secp256k1.ModNScalar).Set(tweak) + if !sig.pubKeyTweak { + s.Negate() + } + s.Add(&sig.s) + + decryptedSig := schnorr.NewSignature(&sig.r, s) + return decryptedSig, nil +} + +// RecoverTweak recovers the tweak using the decrypted signature. +func (sig *AdaptorSignature) RecoverTweak(validSig *schnorr.Signature) (*secp256k1.ModNScalar, error) { + if !sig.pubKeyTweak { + return nil, fmt.Errorf("only pub key tweaked sigs can be recovered") + } + + s := validSig.S() + t := new(secp256k1.ModNScalar).NegateVal(&sig.s).Add(&s) + + // Verify the recovered tweak + var expectedT secp256k1.JacobianPoint + secp256k1.ScalarBaseMultNonConst(t, &expectedT) + if !expectedT.EquivalentNonConst(sig.t.asJacobian()) { + return nil, fmt.Errorf("tweak does not match expected value") + } + + return t, nil +} + +// PublicTweak returns the hidden value multiplied by the generator point. +func (sig *AdaptorSignature) PublicTweak() *secp256k1.JacobianPoint { + return sig.t.asJacobian() +} + +// schnorrEncryptedSign creates an adaptor signature by modifying the nonce in +// the commitment to be the sum of the nonce and the tweak. If the resulting +// signature is summed with the tweak, a valid signature is produced. +func schnorrEncryptedSign(privKey, nonce *secp256k1.ModNScalar, hash []byte, T *secp256k1.JacobianPoint) (*AdaptorSignature, error) { + // The algorithm for producing an encrypted EC-Schnorr-DCRv0 is as follows: + // The deviations from the original algorithm are in step 5 and 6. + // + // G = curve generator + // n = curve order + // d = private key + // m = message + // r, s = signature + // t = hidden value + // T = t * G + // + // 1. Fail if m is not 32 bytes + // 2. Fail if d = 0 or d >= n + // 3. Use RFC6979 to generate a deterministic nonce k in [1, n-1] + // parameterized by the private key, message being signed, extra data + // that identifies the scheme, and an iteration count + // 4. R = kG + // 5. Repeat from step 3 if R + T is odd + // 6. r = (R + T).x (R.x is the x coordinate of the point R + T) + // 7. e = BLAKE-256(r || m) (Ensure r is padded to 32 bytes) + // 8. Repeat from step 3 (with iteration + 1) if e >= n + // 9. s = k - e*d mod n + // 10. Return (r, s) + + // Step 4, + // + // R = kG + var R secp256k1.JacobianPoint + k := *nonce + secp256k1.ScalarBaseMultNonConst(&k, &R) + + // Step 5. + // + // Check if R + T is odd. If it is, we need to try again with a new nonce. + R.ToAffine() + var rPlusT secp256k1.JacobianPoint + secp256k1.AddNonConst(T, &R, &rPlusT) + rPlusT.ToAffine() + if rPlusT.Y.IsOdd() { + return nil, fmt.Errorf("need new nonce") + } + + // Step 6. + // + // r = (R + T).x (R.x is the x coordinate of the point R + T) + r := &rPlusT.X + + // Step 7. + // + // e = BLAKE-256(r + t || m) (Ensure r is padded to 32 bytes) + var commitmentInput [scalarSize * 2]byte + r.PutBytesUnchecked(commitmentInput[0:scalarSize]) + copy(commitmentInput[scalarSize:], hash[:]) + commitment := blake256.Sum256(commitmentInput[:]) + + // Step 8. + // + // Repeat from step 1 (with iteration + 1) if e >= N + var e secp256k1.ModNScalar + if overflow := e.SetBytes(&commitment); overflow != 0 { + k.Zero() + str := "hash of (R || m) too big" + return nil, errors.New(str) + } + + // Step 9. + // + // s = k - e*d mod n + s := new(secp256k1.ModNScalar).Mul2(&e, privKey).Negate().Add(&k) + k.Zero() + + // Step 10. + // + // Return (r, s, T) + affineT := new(secp256k1.JacobianPoint) + affineT.Set(T) + affineT.ToAffine() + t := affinePoint{x: affineT.X, y: affineT.Y} + return &AdaptorSignature{ + r: *r, + s: *s, + t: t, + pubKeyTweak: true}, nil +} + +// zeroArray zeroes the memory of a scalar array. +func zeroArray(a *[scalarSize]byte) { + for i := 0; i < scalarSize; i++ { + a[i] = 0x00 + } +} + +// PublicKeyTweakedAdaptorSig creates a public key tweaked adaptor signature. +// This is created by a party which does not know the hidden value, but knows +// the point on the secp256k1 curve derived by multiplying the hidden value by +// the generator point. The party that knows the hidden value can use it to +// create a valid signature from the adaptor signature. Then, the valid +// signature can be combined with the adaptor signature to reveal the hidden +// value. +func PublicKeyTweakedAdaptorSig(privKey *secp256k1.PrivateKey, hash []byte, T *secp256k1.JacobianPoint) (*AdaptorSignature, error) { + // The algorithm for producing an encrypted EC-Schnorr-DCRv0 is as follows: + // The deviations from the original algorithm are in step 5 and 6. + // + // G = curve generator + // n = curve order + // d = private key + // m = message + // r, s = signature + // t = hidden value + // T = t * G + // + // 1. Fail if m is not 32 bytes + // 2. Fail if d = 0 or d >= n + // 3. Use RFC6979 to generate a deterministic nonce k in [1, n-1] + // parameterized by the private key, message being signed, extra data + // that identifies the scheme, and an iteration count + // 4. R = kG + // 5. Repeat from step 3 if R + T is odd + // 6. r = (R + T).x (R.x is the x coordinate of the point R + T) + // 7. e = BLAKE-256(r || m) (Ensure r is padded to 32 bytes) + // 8. Repeat from step 3 (with iteration + 1) if e >= n + // 9. s = k - e*d mod n + // 10. Return (r, s) + + // Step 1. + // + // Fail if m is not 32 bytes + if len(hash) != scalarSize { + str := fmt.Sprintf("wrong size for message hash (got %v, want %v)", + len(hash), scalarSize) + return nil, errors.New(str) + } + + // Step 2. + // + // Fail if d = 0 or d >= n + privKeyScalar := &privKey.Key + if privKeyScalar.IsZero() { + str := "private key is zero" + return nil, errors.New(str) + } + + var privKeyBytes [scalarSize]byte + privKeyScalar.PutBytes(&privKeyBytes) + defer zeroArray(&privKeyBytes) + for iteration := uint32(0); ; iteration++ { + // Step 3. + // + // Use RFC6979 to generate a deterministic nonce k in [1, n-1] + // parameterized by the private key, message being signed, extra data + // that identifies the scheme, and an iteration count + k := secp256k1.NonceRFC6979(privKeyBytes[:], hash, rfc6979ExtraDataV0[:], + nil, iteration) + + // Steps 4-10. + sig, err := schnorrEncryptedSign(privKeyScalar, k, hash, T) + k.Zero() + if err != nil { + // Try again with a new nonce. + continue + } + + return sig, nil + } +} + +// PrivateKeyTweakedAdaptorSig creates a private key tweaked adaptor signature. +// This is created by a party which knows the hidden value. +func PrivateKeyTweakedAdaptorSig(sig *schnorr.Signature, pubKey *secp256k1.PublicKey, t *secp256k1.ModNScalar) *AdaptorSignature { + T := new(secp256k1.JacobianPoint) + secp256k1.ScalarBaseMultNonConst(t, T) + T.ToAffine() + + s := sig.S() + return &AdaptorSignature{ + r: sig.R(), + s: *new(secp256k1.ModNScalar).Add2(&s, t), + t: affinePoint{x: T.X, y: T.Y}, + } +} diff --git a/internal/adaptorsigs/adaptor_test.go b/internal/adaptorsigs/adaptor_test.go new file mode 100644 index 0000000000..f33a8d42a7 --- /dev/null +++ b/internal/adaptorsigs/adaptor_test.go @@ -0,0 +1,171 @@ +package adaptorsigs + +import ( + "math/rand" + "testing" + "time" + + "decred.org/dcrdex/dex/encode" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/decred/dcrd/dcrec/secp256k1/v4/schnorr" +) + +func TestAdaptorSignatureRandom(t *testing.T) { + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + for i := 0; i < 100; i++ { + // Generate two private keys. + privKey1, err := secp256k1.GeneratePrivateKeyFromRand(rng) + if err != nil { + t.Fatalf("failed to read random private key: %v", err) + } + privKey2, err := secp256k1.GeneratePrivateKeyFromRand(rng) + if err != nil { + t.Fatalf("failed to read random private key: %v", err) + } + + // Generate random hashes to sign. + var hash1, hash2 [32]byte + if _, err := rng.Read(hash1[:]); err != nil { + t.Fatalf("failed to read random hash: %v", err) + } + if _, err := rng.Read(hash2[:]); err != nil { + t.Fatalf("failed to read random hash: %v", err) + } + + // Generate random signature tweak + var tBuf [32]byte + if _, err := rng.Read(tBuf[:]); err != nil { + t.Fatalf("failed to read random private key: %v", err) + } + var tweak secp256k1.ModNScalar + tweak.SetBytes(&tBuf) + + // Sign hash1 with private key 1 + sig, err := schnorr.Sign(privKey1, hash1[:]) + if err != nil { + t.Fatalf("Sign error: %v", err) + } + + // The owner of priv key 1 knows the tweak. Sends a priv key tweaked adaptor sig + // to the owner of priv key 2. + adaptorSigPrivKeyTweak := PrivateKeyTweakedAdaptorSig(sig, privKey1.PubKey(), &tweak) + err = adaptorSigPrivKeyTweak.Verify(hash1[:], privKey1.PubKey()) + if err != nil { + t.Fatalf("verify error: %v", err) + } + + // The owner of privKey2 creates a public key tweaked adaptor sig using + // tweak * G, and sends it to the owner of privKey1. + adaptorSigPubKeyTweak, err := PublicKeyTweakedAdaptorSig(privKey2, hash2[:], adaptorSigPrivKeyTweak.PublicTweak()) + if err != nil { + t.Fatalf("PublicKeyTweakedAdaptorSig error: %v", err) + } + + // The owner of privKey1 knows the tweak, so they can decrypt the + // public key tweaked adaptor sig. + decryptedSig, err := adaptorSigPubKeyTweak.Decrypt(&tweak) + if err != nil { + t.Fatal(err) + } + if !decryptedSig.Verify(hash2[:], privKey2.PubKey()) { + t.Fatal("failed to verify decrypted signature") + } + + // Using the decrypted version of their sig, which has been made public, + // the owner of privKey2 can recover the tweak. + recoveredTweak, err := adaptorSigPubKeyTweak.RecoverTweak(decryptedSig) + if err != nil { + t.Fatal(err) + } + if !recoveredTweak.Equals(&tweak) { + t.Fatalf("original tweak %v != recovered %v", tweak, recoveredTweak) + } + + // Using the recovered tweak, the original priv key tweaked adaptor sig + // can be decrypted. + decryptedOriginalSig, err := adaptorSigPrivKeyTweak.Decrypt(&tweak) + if err != nil { + t.Fatal(err) + } + if valid := decryptedOriginalSig.Verify(hash1[:], privKey1.PubKey()); !valid { + t.Fatal("decrypted original sig is invalid") + } + } +} + +func RandomBytes(len int) []byte { + bytes := make([]byte, len) + _, err := rand.Read(bytes) + if err != nil { + panic("error reading random bytes: " + err.Error()) + } + return bytes +} + +func TestPublicKeyTweakParsing(t *testing.T) { + for i := 0; i < 100; i++ { + privKey, err := secp256k1.GeneratePrivateKey() + if err != nil { + t.Fatal(err) + } + hash := encode.RandomBytes(32) + tweak, err := secp256k1.GeneratePrivateKey() + if err != nil { + t.Fatal(err) + } + var T secp256k1.JacobianPoint + tweak.PubKey().AsJacobian(&T) + + adaptorSig, err := PublicKeyTweakedAdaptorSig(privKey, hash, &T) + if err != nil { + t.Fatal(err) + } + + serialized := adaptorSig.Serialize() + parsed, err := ParseAdaptorSignature(serialized) + if err != nil { + t.Fatal(err) + } + + if !adaptorSig.IsEqual(parsed) { + t.Fatalf("parsed sig does not equal original") + } + } +} + +func TestPrivateKeyTweakParsing(t *testing.T) { + for i := 0; i < 100; i++ { + privKey, err := secp256k1.GeneratePrivateKey() + if err != nil { + t.Fatal(err) + } + hash := encode.RandomBytes(32) + tweak, err := secp256k1.GeneratePrivateKey() + if err != nil { + t.Fatal(err) + } + + sig, err := schnorr.Sign(privKey, hash) + if err != nil { + t.Fatal(err) + } + + adaptorSig := PrivateKeyTweakedAdaptorSig(sig, privKey.PubKey(), &tweak.Key) + serialized := adaptorSig.Serialize() + parsed, err := ParseAdaptorSignature(serialized) + if err != nil { + t.Fatal(err) + } + + if !adaptorSig.IsEqual(parsed) { + t.Fatalf("parsed sig does not equal original") + } + } +} diff --git a/internal/adaptorsigs/dcr/dcr.go b/internal/adaptorsigs/dcr/dcr.go new file mode 100644 index 0000000000..e7c2ff0f73 --- /dev/null +++ b/internal/adaptorsigs/dcr/dcr.go @@ -0,0 +1,34 @@ +package dcr + +import "github.com/decred/dcrd/txscript/v4" + +func LockRefundTxScript(kal, kaf []byte, locktime int64) ([]byte, error) { + return txscript.NewScriptBuilder(). + AddOp(txscript.OP_IF). + AddData(kal). + AddOp(txscript.OP_2). + AddOp(txscript.OP_CHECKSIGALTVERIFY). + AddData(kaf). + AddOp(txscript.OP_2). + AddOp(txscript.OP_CHECKSIGALT). + AddOp(txscript.OP_ELSE). + AddInt64(locktime). + AddOp(txscript.OP_CHECKSEQUENCEVERIFY). + AddOp(txscript.OP_DROP). + AddData(kaf). + AddOp(txscript.OP_2). + AddOp(txscript.OP_CHECKSIGALT). + AddOp(txscript.OP_ENDIF). + Script() +} + +func LockTxScript(kal, kaf []byte) ([]byte, error) { + return txscript.NewScriptBuilder(). + AddData(kal). + AddOp(txscript.OP_2). + AddOp(txscript.OP_CHECKSIGALTVERIFY). + AddData(kaf). + AddOp(txscript.OP_2). + AddOp(txscript.OP_CHECKSIGALT). + Script() +} diff --git a/internal/adaptorsigs/dleq.go b/internal/adaptorsigs/dleq.go new file mode 100644 index 0000000000..6847deab08 --- /dev/null +++ b/internal/adaptorsigs/dleq.go @@ -0,0 +1,113 @@ +package adaptorsigs + +import ( + "bytes" + "fmt" + + "decred.org/dcrdex/dex/utils" + filipEdwards "filippo.io/edwards25519" + "filippo.io/edwards25519/field" + "github.com/athanorlabs/go-dleq" + "github.com/athanorlabs/go-dleq/ed25519" + dleqEdwards "github.com/athanorlabs/go-dleq/ed25519" + "github.com/athanorlabs/go-dleq/secp256k1" + dleqSecp "github.com/athanorlabs/go-dleq/secp256k1" + dcrEdwards "github.com/decred/dcrd/dcrec/edwards/v2" + dcrSecp "github.com/decred/dcrd/dcrec/secp256k1/v4" +) + +// ProveDLEQ generates a proof that the public keys generated from the provided +// secret on the secp256k1 and edwards25519 curves are derived from the same +// secret. +func ProveDLEQ(secret []byte) ([]byte, error) { + if len(secret) != 32 { + return nil, fmt.Errorf("secret must be 32 bytes") + } + + secretCopy := make([]byte, len(secret)) + copy(secretCopy, secret) + utils.ReverseSlice(secretCopy) + secretB := [32]byte{} + copy(secretB[:], secretCopy) + + proof, err := dleq.NewProof(ed25519.NewCurve(), secp256k1.NewCurve(), secretB) + if err != nil { + return nil, err + } + + return proof.Serialize(), nil +} + +// edwardsPointsEqual checks equality of edwards curve points in the dcrec +// and go-dleq libraries. +func edwardsPointsEqual(dcrPK *dcrEdwards.PublicKey, dleqPK *dleqEdwards.PointImpl) bool { + xB := dcrPK.GetX().Bytes() + yB := dcrPK.GetY().Bytes() + utils.ReverseSlice(xB) + utils.ReverseSlice(yB) + + x := new(field.Element) + y := new(field.Element) + z := new(field.Element) + t := new(field.Element) + + x.SetBytes(xB) + y.SetBytes(yB) + z.One() + t.Multiply(x, y) + + point := filipEdwards.NewIdentityPoint() + point.SetExtendedCoordinates(x, y, z, t) + + expDLEQ := dleqEdwards.NewPoint(point) + return dleqPK.Equals(expDLEQ) +} + +// VerifyDLEQ verifies a DLEQ proof that the public keys are generated from the +// same secret. +func VerifyDLEQ(spk *dcrSecp.PublicKey, epk *dcrEdwards.PublicKey, proofB []byte) error { + edwardsCurve := dleqEdwards.NewCurve() + secpCurve := dleqSecp.NewCurve() + + proof := new(dleq.Proof) + if err := proof.Deserialize(edwardsCurve, secpCurve, proofB); err != nil { + return err + } + if err := proof.Verify(edwardsCurve, secpCurve); err != nil { + return err + } + + edwardsPoint, ok := proof.CommitmentA.(*dleqEdwards.PointImpl) + if !ok { + return fmt.Errorf("expected ed25519.Point, got %T", proof.CommitmentA) + } + if !edwardsPointsEqual(epk, edwardsPoint) { + return fmt.Errorf("ed25519 points do not match") + } + + secpPoint, ok := proof.CommitmentB.(*dleqSecp.PointImpl) + if !ok { + return fmt.Errorf("expected secp256k1.Point, got %T", proof.CommitmentB) + } + if !bytes.Equal(secpPoint.Encode(), spk.SerializeCompressed()) { + return fmt.Errorf("secp256k1 points do not match") + } + + return nil +} + +// ExtractSecp256k1PubKeyFromProof extracts the secp256k1 public key from a +// DLEQ proof. +func ExtractSecp256k1PubKeyFromProof(proofB []byte) (*dcrSecp.PublicKey, error) { + proof := new(dleq.Proof) + if err := proof.Deserialize(dleqEdwards.NewCurve(), dleqSecp.NewCurve(), proofB); err != nil { + return nil, err + } + + secpPoint, ok := proof.CommitmentB.(*dleqSecp.PointImpl) + if !ok { + return nil, fmt.Errorf("expected secp256k1.Point, got %T", proof.CommitmentB) + } + + return dcrSecp.ParsePubKey(secpPoint.Encode()) +} diff --git a/internal/adaptorsigs/dleq_test.go b/internal/adaptorsigs/dleq_test.go new file mode 100644 index 0000000000..eecbdb29a7 --- /dev/null +++ b/internal/adaptorsigs/dleq_test.go @@ -0,0 +1,60 @@ +package adaptorsigs + +import ( + "testing" + + "decred.org/dcrdex/dex/encode" + "github.com/decred/dcrd/dcrec/edwards/v2" + "github.com/decred/dcrd/dcrec/secp256k1/v4" +) + +func TestDleqProof(t *testing.T) { + pubKeysFromSecret := func(secret [32]byte) (*edwards.PublicKey, *secp256k1.PublicKey) { + epk, _, err := edwards.PrivKeyFromScalar(secret[:]) + if err != nil { + t.Fatalf("PrivKeyFromScalar error: %v", err) + } + + scalarSecret := new(secp256k1.ModNScalar) + overflow := scalarSecret.SetBytes(&secret) + if overflow > 0 { + t.Fatalf("overflow: %d", overflow) + } + spk := secp256k1.NewPrivateKey(scalarSecret) + + return epk.PubKey(), spk.PubKey() + } + + var secret [32]byte + copy(secret[1:], encode.RandomBytes(31)) + + epk, spk := pubKeysFromSecret(secret) + proof, err := ProveDLEQ(secret[:]) + if err != nil { + t.Fatalf("ProveDLEQ error: %v", err) + } + err = VerifyDLEQ(spk, epk, proof) + if err != nil { + t.Fatalf("VerifyDLEQ error: %v", err) + } + + secret[31] += 1 + badEpk, badSpk := pubKeysFromSecret(secret) + err = VerifyDLEQ(badSpk, epk, proof) + if err == nil { + t.Fatalf("badSpk should not verify") + } + err = VerifyDLEQ(spk, badEpk, proof) + if err == nil { + t.Fatalf("badEpk should not verify") + } + + extractedSecp, err := ExtractSecp256k1PubKeyFromProof(proof) + if err != nil { + t.Fatalf("ExtractSecp256k1PubKeyFromProof error: %v", err) + } + + if !extractedSecp.IsEqual(spk) { + t.Fatalf("extractedSecp != spk") + } +} diff --git a/internal/cmd/xmrswap/main.go b/internal/cmd/xmrswap/main.go new file mode 100644 index 0000000000..4cb7c97dd8 --- /dev/null +++ b/internal/cmd/xmrswap/main.go @@ -0,0 +1,1260 @@ +package main + +import ( + "bytes" + "context" + "encoding/hex" + "errors" + "fmt" + "math" + "math/big" + "net/http" + "os" + "path/filepath" + "time" + + "decred.org/dcrdex/dex" + "decred.org/dcrdex/dex/config" + "decred.org/dcrdex/internal/adaptorsigs" + dcradaptor "decred.org/dcrdex/internal/adaptorsigs/dcr" + "decred.org/dcrwallet/v4/rpc/client/dcrwallet" + dcrwalletjson "decred.org/dcrwallet/v4/rpc/jsonrpc/types" + "github.com/agl/ed25519/edwards25519" + "github.com/decred/dcrd/chaincfg/chainhash" + "github.com/decred/dcrd/chaincfg/v3" + "github.com/decred/dcrd/dcrec" + "github.com/decred/dcrd/dcrec/edwards/v2" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/decred/dcrd/dcrec/secp256k1/v4/schnorr" + "github.com/decred/dcrd/rpcclient/v8" + "github.com/decred/dcrd/txscript/v4" + "github.com/decred/dcrd/txscript/v4/sign" + "github.com/decred/dcrd/txscript/v4/stdaddr" + "github.com/decred/dcrd/wire" + "github.com/decred/slog" + "github.com/dev-warrior777/go-monero/rpc" + "github.com/fatih/color" + "github.com/haven-protocol-org/monero-go-utils/base58" +) + +// TODO: Verification at all stages has not been implemented yet. + +// fieldIntSize is the size of a field element encoded +// as bytes. +const ( + fieldIntSize = 32 + dcrAmt = 7_000_000 // atoms + xmrAmt = 1_000 // 1e12 units + dumbFee = int64(6000) +) + +var ( + homeDir = os.Getenv("HOME") + dextestDir = filepath.Join(homeDir, "dextest") + bobDir = filepath.Join(dextestDir, "xmr", "wallets", "bob") + curve = edwards.Edwards() +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + if err := run(ctx); err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + } +} + +type walletClient = dcrwallet.Client + +type combinedClient struct { + *rpcclient.Client + *walletClient + chainParams *chaincfg.Params +} + +func newCombinedClient(nodeRPCClient *rpcclient.Client, chainParams *chaincfg.Params) *combinedClient { + return &combinedClient{ + nodeRPCClient, + dcrwallet.NewClient(dcrwallet.RawRequestCaller(nodeRPCClient), chainParams), + chainParams, + } +} + +type client struct { + xmr *rpc.Client + dcr *combinedClient + + kbvf, kbsf, kbvl, kbsl, vkbv *edwards.PrivateKey + pkbsf, pkbs *edwards.PublicKey + kaf, kal *secp256k1.PrivateKey + pkal, pkaf, pkasl, pkbsl *secp256k1.PublicKey + kbsfDleag, kbslDleag []byte + lockTxEsig *adaptorsigs.AdaptorSignature + lockTx *wire.MsgTx + vIn int +} + +func newRPCWallet(settings map[string]string, logger dex.Logger, net dex.Network) (*combinedClient, error) { + certs, err := os.ReadFile(settings["rpccert"]) + if err != nil { + return nil, fmt.Errorf("TLS certificate read error: %w", err) + } + + cfg := &rpcclient.ConnConfig{ + Host: settings["rpclisten"], + Endpoint: "ws", + User: settings["rpcuser"], + Pass: settings["rpcpass"], + Certificates: certs, + DisableConnectOnNew: true, // don't start until Connect + } + if cfg.User == "" { + cfg.User = "user" + } + if cfg.Pass == "" { + cfg.Pass = "pass" + } + + nodeRPCClient, err := rpcclient.New(cfg, nil) + if err != nil { + return nil, fmt.Errorf("error setting up rpc client: %w", err) + } + + var params *chaincfg.Params + switch net { + case dex.Simnet: + params = chaincfg.SimNetParams() + case dex.Testnet: + params = chaincfg.TestNet3Params() + case dex.Mainnet: + params = chaincfg.MainNetParams() + default: + return nil, fmt.Errorf("unknown network ID: %d", uint8(net)) + } + + return newCombinedClient(nodeRPCClient, params), nil +} + +func newClient(ctx context.Context, xmrAddr, dcrNode string) (*client, error) { + xmr := rpc.New(rpc.Config{ + Address: xmrAddr, + Client: &http.Client{}, + }) + + settings, err := config.Parse(filepath.Join(dextestDir, "dcr", dcrNode, fmt.Sprintf("%s.conf", dcrNode))) + if err != nil { + return nil, err + } + settings["account"] = "default" + + dcr, err := newRPCWallet(settings, dex.StdOutLogger(dcrNode, slog.LevelTrace), dex.Simnet) + if err != nil { + return nil, err + } + + err = dcr.Connect(ctx, false) + if err != nil { + return nil, err + } + + return &client{ + xmr: xmr, + dcr: dcr, + }, nil +} + +// reverse reverses a byte string. +func reverse(s *[32]byte) { + for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } +} + +// bigIntToEncodedBytes converts a big integer into its corresponding +// 32 byte little endian representation. +func bigIntToEncodedBytes(a *big.Int) *[32]byte { + s := new([32]byte) + if a == nil { + return s + } + // Caveat: a can be longer than 32 bytes. + aB := a.Bytes() + + // If we have a short byte string, expand + // it so that it's long enough. + aBLen := len(aB) + if aBLen < fieldIntSize { + diff := fieldIntSize - aBLen + for i := 0; i < diff; i++ { + aB = append([]byte{0x00}, aB...) + } + } + + for i := 0; i < fieldIntSize; i++ { + s[i] = aB[i] + } + + // Reverse the byte string --> little endian after + // encoding. + reverse(s) + + return s +} + +// encodedBytesToBigInt converts a 32 byte little endian representation of +// an integer into a big, big endian integer. +func encodedBytesToBigInt(s *[32]byte) *big.Int { + // Use a copy so we don't screw up our original + // memory. + sCopy := new([32]byte) + for i := 0; i < fieldIntSize; i++ { + sCopy[i] = s[i] + } + reverse(sCopy) + + bi := new(big.Int).SetBytes(sCopy[:]) + + return bi +} + +// scalarAdd adds two scalars. +func scalarAdd(a, b *big.Int) *big.Int { + feA := bigIntToFieldElement(a) + feB := bigIntToFieldElement(b) + sum := new(edwards25519.FieldElement) + + edwards25519.FeAdd(sum, feA, feB) + sumArray := new([32]byte) + edwards25519.FeToBytes(sumArray, sum) + + return encodedBytesToBigInt(sumArray) +} + +// bigIntToFieldElement converts a big little endian integer into its corresponding +// 40 byte field representation. +func bigIntToFieldElement(a *big.Int) *edwards25519.FieldElement { + aB := bigIntToEncodedBytes(a) + fe := new(edwards25519.FieldElement) + edwards25519.FeFromBytes(fe, aB) + return fe +} + +func sumPubKeys(pubA, pubB *edwards.PublicKey) *edwards.PublicKey { + pkSumX, pkSumY := curve.Add(pubA.GetX(), pubA.GetY(), pubB.GetX(), pubB.GetY()) + return edwards.NewPublicKey(pkSumX, pkSumY) +} + +// Convert the DCR value to atoms. +func toAtoms(v float64) uint64 { + return uint64(math.Round(v * 1e8)) +} + +// createNewXMRWallet uses the "own" wallet to create a new xmr wallet from keys +// and open it. Can only create one wallet at a time. +func createNewXMRWallet(ctx context.Context, genReq rpc.GenerateFromKeysRequest) (*rpc.Client, error) { + xmrChecker := rpc.New(rpc.Config{ + Address: "http://127.0.0.1:28484/json_rpc", + Client: &http.Client{}, + }) + + _, err := xmrChecker.GenerateFromKeys(ctx, &genReq) + if err != nil { + return nil, fmt.Errorf("unable to generate wallet: %v", err) + } + + openReq := rpc.OpenWalletRequest{ + Filename: genReq.Filename, + } + + err = xmrChecker.OpenWallet(ctx, &openReq) + if err != nil { + return nil, err + } + return xmrChecker, nil +} + +type prettyLogger struct { + c *color.Color +} + +func (cl prettyLogger) Write(p []byte) (n int, err error) { + return cl.c.Fprint(os.Stdout, string(p)) +} + +func run(ctx context.Context) error { + pl := prettyLogger{c: color.New(color.FgGreen)} + log := dex.NewLogger("T", dex.LevelInfo, pl) + + log.Info("Running success.") + if err := success(ctx); err != nil { + return err + } + log.Info("Success completed without error.") + log.Info("------------------") + log.Info("Running alice bails before xmr init.") + if err := aliceBailsBeforeXmrInit(ctx); err != nil { + return err + } + log.Info("Alice bails before xmr init completed without error.") + log.Info("------------------") + log.Info("Running refund.") + if err := refund(ctx); err != nil { + return err + } + log.Info("Refund completed without error.") + log.Info("------------------") + log.Info("Running bob bails after xmr init.") + if err := bobBailsAfterXmrInit(ctx); err != nil { + return err + } + log.Info("Bob bails after xmr init completed without error.") + return nil +} + +// generateDleag starts the trade by creating some keys. +func (c *client) generateDleag(ctx context.Context) (pkbsf *edwards.PublicKey, kbvf *edwards.PrivateKey, + pkaf *secp256k1.PublicKey, dleag []byte, err error) { + fail := func(err error) (*edwards.PublicKey, *edwards.PrivateKey, + *secp256k1.PublicKey, []byte, error) { + return nil, nil, nil, nil, err + } + // This private key is shared with bob and becomes half of the view key. + c.kbvf, err = edwards.GeneratePrivateKey() + if err != nil { + return fail(err) + } + + // Not shared. Becomes half the spend key. The pubkey is shared. + c.kbsf, err = edwards.GeneratePrivateKey() + if err != nil { + return fail(err) + } + c.pkbsf = c.kbsf.PubKey() + + // Not shared. This is used for all dcr signatures. Using a wallet + // address because funds may go here in the case of success. Any address + // would work for the spendTx though. + kafAddr, err := c.dcr.GetNewAddress(ctx, "default") + if err != nil { + return fail(err) + } + kafWIF, err := c.dcr.DumpPrivKey(ctx, kafAddr) + if err != nil { + return fail(err) + } + c.kaf = secp256k1.PrivKeyFromBytes(kafWIF.PrivKey()) + + // Share this pubkey with the other party. + c.pkaf = c.kaf.PubKey() + + c.kbsfDleag, err = adaptorsigs.ProveDLEQ(c.kbsf.Serialize()) + if err != nil { + return fail(err) + } + + c.pkasl, err = adaptorsigs.ExtractSecp256k1PubKeyFromProof(c.kbsfDleag) + if err != nil { + return fail(err) + } + + return c.pkbsf, c.kbvf, c.pkaf, c.kbsfDleag, nil +} + +// generateLockTxn creates even more keys and some transactions. +func (c *client) generateLockTxn(ctx context.Context, pkbsf *edwards.PublicKey, + kbvf *edwards.PrivateKey, pkaf *secp256k1.PublicKey, kbsfDleag []byte) (refundSig, + lockRefundTxScript, lockTxScript []byte, refundTx, spendRefundTx *wire.MsgTx, lockTxVout int, + pkbs *edwards.PublicKey, vkbv *edwards.PrivateKey, dleag []byte, bdcrpk *secp256k1.PublicKey, err error) { + + fail := func(err error) ([]byte, []byte, []byte, *wire.MsgTx, *wire.MsgTx, int, *edwards.PublicKey, *edwards.PrivateKey, []byte, *secp256k1.PublicKey, error) { + return nil, nil, nil, nil, nil, 0, nil, nil, nil, nil, err + } + c.kbsfDleag = kbsfDleag + c.pkasl, err = adaptorsigs.ExtractSecp256k1PubKeyFromProof(c.kbsfDleag) + if err != nil { + return fail(err) + } + c.kbvf = kbvf + c.pkbsf = pkbsf + c.pkaf = pkaf + + // This becomes the other half of the view key. + c.kbvl, err = edwards.GeneratePrivateKey() + if err != nil { + return fail(err) + } + + // This becomes the other half of the spend key and is shared. + c.kbsl, err = edwards.GeneratePrivateKey() + if err != nil { + return fail(err) + } + + // This kept private. This is used for all dcr signatures. + c.kal, err = secp256k1.GeneratePrivateKey() + if err != nil { + return fail(err) + } + + pkal := c.kal.PubKey() + + // This is the full xmr view key and is shared. Alice can also calculate + // it using kbvl. + vkbvBig := scalarAdd(c.kbvf.GetD(), c.kbvl.GetD()) + vkbvBig.Mod(vkbvBig, curve.N) + var vkbvBytes [32]byte + vkbvBig.FillBytes(vkbvBytes[:]) + c.vkbv, _, err = edwards.PrivKeyFromScalar(vkbvBytes[:]) + if err != nil { + return fail(fmt.Errorf("unable to create vkbv: %v", err)) + } + + // The public key for the xmr spend key. No party knows the full private + // key yet. + c.pkbs = sumPubKeys(c.kbsl.PubKey(), c.pkbsf) + + // The lock tx is the initial dcr transaction. + lockTxScript, err = dcradaptor.LockTxScript(pkal.SerializeCompressed(), c.pkaf.SerializeCompressed()) + if err != nil { + return fail(err) + } + + scriptAddr, err := stdaddr.NewAddressScriptHashV0(lockTxScript, c.dcr.chainParams) + if err != nil { + return fail(fmt.Errorf("error encoding script address: %w", err)) + } + p2shLockScriptVer, p2shLockScript := scriptAddr.PaymentScript() + // Add the transaction output. + txOut := &wire.TxOut{ + Value: dcrAmt, + Version: p2shLockScriptVer, + PkScript: p2shLockScript, + } + unfundedLockTx := wire.NewMsgTx() + unfundedLockTx.AddTxOut(txOut) + txBytes, err := unfundedLockTx.Bytes() + if err != nil { + return fail(err) + } + + fundRes, err := c.dcr.FundRawTransaction(ctx, hex.EncodeToString(txBytes), "default", dcrwalletjson.FundRawTransactionOptions{}) + if err != nil { + return fail(err) + } + + txBytes, err = hex.DecodeString(fundRes.Hex) + if err != nil { + return fail(err) + } + + c.lockTx = wire.NewMsgTx() + if err = c.lockTx.FromBytes(txBytes); err != nil { + return fail(err) + } + for i, out := range c.lockTx.TxOut { + if bytes.Equal(out.PkScript, p2shLockScript) { + c.vIn = i + break + } + } + + durationLocktime := int64(2) // blocks + // Unable to use time for tests as this is multiples of 512 seconds. + // durationLocktime := int64(10) // seconds * 512 + // durationLocktime |= wire.SequenceLockTimeIsSeconds + + // The refund tx does not outright refund but moves funds to the refund + // script's address. This is signed by both parties before the initial tx. + lockRefundTxScript, err = dcradaptor.LockRefundTxScript(pkal.SerializeCompressed(), c.pkaf.SerializeCompressed(), durationLocktime) + if err != nil { + return fail(err) + } + + scriptAddr, err = stdaddr.NewAddressScriptHashV0(lockRefundTxScript, c.dcr.chainParams) + if err != nil { + return fail(fmt.Errorf("error encoding script address: %w", err)) + } + p2shScriptVer, p2shScript := scriptAddr.PaymentScript() + txOut = &wire.TxOut{ + Value: dcrAmt - dumbFee, + Version: p2shScriptVer, + PkScript: p2shScript, + } + refundTx = wire.NewMsgTx() + refundTx.AddTxOut(txOut) + h := c.lockTx.TxHash() + op := wire.NewOutPoint(&h, uint32(c.vIn), 0) + txIn := wire.NewTxIn(op, dcrAmt, nil) + refundTx.AddTxIn(txIn) + + // This sig must be shared with Alice. + refundSig, err = sign.RawTxInSignature(refundTx, c.vIn, lockTxScript, txscript.SigHashAll, c.kal.Serialize(), dcrec.STSchnorrSecp256k1) + if err != nil { + return fail(err) + } + + // SpendRefundTx is used in the final refund. Alice can sign it after a + // time and send wherever. Bob must use a signature that will reveal his + // half of the xmr key. + newAddr, err := c.dcr.GetNewAddress(ctx, "default") + if err != nil { + return fail(err) + } + p2AddrScriptVer, p2AddrScript := newAddr.PaymentScript() + txOut = &wire.TxOut{ + Value: dcrAmt - dumbFee - dumbFee, + Version: p2AddrScriptVer, + PkScript: p2AddrScript, + } + spendRefundTx = wire.NewMsgTx() + spendRefundTx.AddTxOut(txOut) + h = refundTx.TxHash() + op = wire.NewOutPoint(&h, 0, 0) + txIn = wire.NewTxIn(op, dcrAmt, nil) + txIn.Sequence = uint32(durationLocktime) + spendRefundTx.AddTxIn(txIn) + spendRefundTx.Version = wire.TxVersionTreasury + + c.kbslDleag, err = adaptorsigs.ProveDLEQ(c.kbsl.Serialize()) + if err != nil { + return fail(err) + } + c.pkbsl, err = adaptorsigs.ExtractSecp256k1PubKeyFromProof(c.kbslDleag) + if err != nil { + return fail(err) + } + + return refundSig, lockRefundTxScript, lockTxScript, refundTx, spendRefundTx, c.vIn, c.pkbs, c.vkbv, c.kbslDleag, pkal, nil +} + +// generateRefundSigs signs the refund tx and shares the spendRefund esig that +// allows bob to spend the refund tx. +func (c *client) generateRefundSigs(refundTx, spendRefundTx *wire.MsgTx, vIn int, lockTxScript, lockRefundTxScript []byte, dleag []byte) (esig *adaptorsigs.AdaptorSignature, refundSig []byte, err error) { + fail := func(err error) (*adaptorsigs.AdaptorSignature, []byte, error) { + return nil, nil, err + } + c.kbslDleag = dleag + c.vIn = vIn + c.pkbsl, err = adaptorsigs.ExtractSecp256k1PubKeyFromProof(c.kbslDleag) + if err != nil { + return fail(err) + } + + hash, err := txscript.CalcSignatureHash(lockRefundTxScript, txscript.SigHashAll, spendRefundTx, 0, nil) + if err != nil { + return fail(err) + } + + var h chainhash.Hash + copy(h[:], hash) + + jacobianBobPubKey := new(secp256k1.JacobianPoint) + c.pkbsl.AsJacobian(jacobianBobPubKey) + esig, err = adaptorsigs.PublicKeyTweakedAdaptorSig(c.kaf, h[:], jacobianBobPubKey) + if err != nil { + return fail(err) + } + + // Share with bob. + refundSig, err = sign.RawTxInSignature(refundTx, c.vIn, lockTxScript, txscript.SigHashAll, c.kaf.Serialize(), dcrec.STSchnorrSecp256k1) + if err != nil { + return fail(err) + } + + return esig, refundSig, nil +} + +// initDcr is the first transaction to happen and creates a dcr transaction. +func (c *client) initDcr(ctx context.Context) (spendTx *wire.MsgTx, err error) { + fail := func(err error) (*wire.MsgTx, error) { + return nil, err + } + pkaslAddr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1(0, stdaddr.Hash160(c.pkaf.SerializeCompressed()), c.dcr.chainParams) + if err != nil { + return fail(err) + } + p2AddrScriptVer, p2AddrScript := pkaslAddr.PaymentScript() + + txOut := &wire.TxOut{ + Value: dcrAmt - dumbFee, + Version: p2AddrScriptVer, + PkScript: p2AddrScript, + } + spendTx = wire.NewMsgTx() + spendTx.AddTxOut(txOut) + h := c.lockTx.TxHash() + op := wire.NewOutPoint(&h, uint32(c.vIn), 0) + txIn := wire.NewTxIn(op, dcrAmt, nil) + spendTx.AddTxIn(txIn) + + tx, complete, err := c.dcr.SignRawTransaction(ctx, c.lockTx) + if err != nil { + return fail(err) + } + if !complete { + return fail(errors.New("lock tx sign not complete")) + } + + _, err = c.dcr.SendRawTransaction(ctx, tx, false) + if err != nil { + return fail(fmt.Errorf("unable to send lock tx: %v", err)) + } + + return spendTx, nil +} + +// initXmr sends an xmr transaciton. Alice can only do this after confirming the +// dcr transaction. +func (c *client) initXmr(ctx context.Context, vkbv *edwards.PrivateKey, pkbs *edwards.PublicKey) error { + c.vkbv = vkbv + c.pkbs = pkbs + var fullPubKey []byte + fullPubKey = append(fullPubKey, c.pkbs.SerializeCompressed()...) + fullPubKey = append(fullPubKey, c.vkbv.PubKey().SerializeCompressed()...) + + sharedAddr := base58.EncodeAddr(18, fullPubKey) + + dest := rpc.Destination{ + Amount: xmrAmt, + Address: sharedAddr, + } + sendReq := rpc.TransferRequest{ + Destinations: []rpc.Destination{dest}, + } + + sendRes, err := c.xmr.Transfer(ctx, &sendReq) + if err != nil { + return err + } + fmt.Printf("xmr sent\n%+v\n", *sendRes) + return nil +} + +// sendLockTxSig allows Alice to redeem the dcr. If bob does not send this alice +// can eventually take his btc. Otherwise bob refunding will reveal his half of +// the xmr spend key allowing Alice to refund. +func (c *client) sendLockTxSig(lockTxScript []byte, spendTx *wire.MsgTx) (esig *adaptorsigs.AdaptorSignature, err error) { + hash, err := txscript.CalcSignatureHash(lockTxScript, txscript.SigHashAll, spendTx, 0, nil) + if err != nil { + return nil, err + } + + var h chainhash.Hash + copy(h[:], hash) + + jacobianAlicePubKey := new(secp256k1.JacobianPoint) + c.pkasl.AsJacobian(jacobianAlicePubKey) + esig, err = adaptorsigs.PublicKeyTweakedAdaptorSig(c.kal, h[:], jacobianAlicePubKey) + if err != nil { + return nil, err + } + + c.lockTxEsig = esig + + return esig, nil +} + +// redeemDcr redeems the dcr, revealing a signature that reveals half of the xmr +// spend key. +func (c *client) redeemDcr(ctx context.Context, esig *adaptorsigs.AdaptorSignature, lockTxScript []byte, spendTx *wire.MsgTx, bobDCRPK *secp256k1.PublicKey) (kalSig []byte, err error) { + kasl := secp256k1.PrivKeyFromBytes(c.kbsf.Serialize()) + + kalSigShnorr, err := esig.Decrypt(&kasl.Key) + if err != nil { + return nil, err + } + kalSig = kalSigShnorr.Serialize() + kalSig = append(kalSig, byte(txscript.SigHashAll)) + + kafSig, err := sign.RawTxInSignature(spendTx, 0, lockTxScript, txscript.SigHashAll, c.kaf.Serialize(), dcrec.STSchnorrSecp256k1) + if err != nil { + return nil, err + } + + spendSig, err := txscript.NewScriptBuilder(). + AddData(kafSig). + AddData(kalSig). + AddData(lockTxScript). + Script() + if err != nil { + return nil, err + } + + spendTx.TxIn[0].SignatureScript = spendSig + + tx, err := c.dcr.SendRawTransaction(ctx, spendTx, false) + if err != nil { + return nil, err + } + + fmt.Println("Redeem Tx -", tx) + + return kalSig, nil +} + +// redeemXmr redeems xmr by creating a new xmr wallet with the complete spend +// and view private keys. +func (c *client) redeemXmr(ctx context.Context, kalSig []byte) (*rpc.Client, error) { + kalSigParsed, err := schnorr.ParseSignature(kalSig[:len(kalSig)-1]) + if err != nil { + return nil, err + } + kaslRecoveredScalar, err := c.lockTxEsig.RecoverTweak(kalSigParsed) + if err != nil { + return nil, err + } + kaslRecoveredBytes := kaslRecoveredScalar.Bytes() + kaslRecovered := secp256k1.PrivKeyFromBytes(kaslRecoveredBytes[:]) + + kbsfRecovered, _, err := edwards.PrivKeyFromScalar(kaslRecovered.Serialize()) + if err != nil { + return nil, fmt.Errorf("unable to recover kbsf: %v", err) + } + vkbsBig := scalarAdd(c.kbsl.GetD(), kbsfRecovered.GetD()) + vkbsBig.Mod(vkbsBig, curve.N) + var vkbsBytes [32]byte + vkbsBig.FillBytes(vkbsBytes[:]) + vkbs, _, err := edwards.PrivKeyFromScalar(vkbsBytes[:]) + if err != nil { + return nil, fmt.Errorf("unable to create vkbs: %v", err) + } + + var fullPubKey []byte + fullPubKey = append(fullPubKey, vkbs.PubKey().Serialize()...) + fullPubKey = append(fullPubKey, c.vkbv.PubKey().Serialize()...) + walletAddr := base58.EncodeAddr(18, fullPubKey) + walletFileName := fmt.Sprintf("%s_spend", walletAddr) + + var vkbvBytes [32]byte + copy(vkbvBytes[:], c.vkbv.Serialize()) + + reverse(&vkbsBytes) + reverse(&vkbvBytes) + + genReq := rpc.GenerateFromKeysRequest{ + Filename: walletFileName, + Address: walletAddr, + SpendKey: hex.EncodeToString(vkbsBytes[:]), + ViewKey: hex.EncodeToString(vkbvBytes[:]), + } + + xmrChecker, err := createNewXMRWallet(ctx, genReq) + if err != nil { + return nil, err + } + + return xmrChecker, nil +} + +// startRefund starts the refund and can be done by either party. +func (c *client) startRefund(ctx context.Context, kalSig, kafSig, lockTxScript []byte, refundTx *wire.MsgTx) error { + refundSig, err := txscript.NewScriptBuilder(). + AddData(kafSig). + AddData(kalSig). + AddData(lockTxScript). + Script() + if err != nil { + return err + } + + refundTx.TxIn[0].SignatureScript = refundSig + + tx, err := c.dcr.SendRawTransaction(ctx, refundTx, false) + if err != nil { + return err + } + + fmt.Println("Cancel Tx -", tx) + + return nil +} + +// refundDcr returns dcr to bob while revealing his half of the xmr spend key. +func (c *client) refundDcr(ctx context.Context, spendRefundTx *wire.MsgTx, esig *adaptorsigs.AdaptorSignature, lockRefundTxScript []byte) (kafSig []byte, err error) { + kasf := secp256k1.PrivKeyFromBytes(c.kbsl.Serialize()) + decryptedSig, err := esig.Decrypt(&kasf.Key) + if err != nil { + return nil, err + } + kafSig = decryptedSig.Serialize() + kafSig = append(kafSig, byte(txscript.SigHashAll)) + + kalSig, err := sign.RawTxInSignature(spendRefundTx, 0, lockRefundTxScript, txscript.SigHashAll, c.kal.Serialize(), dcrec.STSchnorrSecp256k1) + if err != nil { + return nil, err + } + + refundSig, err := txscript.NewScriptBuilder(). + AddData(kafSig). + AddData(kalSig). + AddOp(txscript.OP_TRUE). + AddData(lockRefundTxScript). + Script() + if err != nil { + return nil, err + } + + spendRefundTx.TxIn[0].SignatureScript = refundSig + + tx, err := c.dcr.SendRawTransaction(ctx, spendRefundTx, false) + if err != nil { + return nil, err + } + + fmt.Println("Refund tx -", tx) + + // TODO: Confirm refund happened. + return kafSig, nil +} + +// refundXmr refunds xmr but cannot happen without the dcr refund happening first. +func (c *client) refundXmr(ctx context.Context, kafSig []byte, esig *adaptorsigs.AdaptorSignature) (*rpc.Client, error) { + kafSigParsed, err := schnorr.ParseSignature(kafSig[:len(kafSig)-1]) + if err != nil { + return nil, err + } + kbslRecoveredScalar, err := esig.RecoverTweak(kafSigParsed) + if err != nil { + return nil, err + } + + kbslRecoveredBytes := kbslRecoveredScalar.Bytes() + kbslRecovered := secp256k1.PrivKeyFromBytes(kbslRecoveredBytes[:]) + + kaslRecovered, _, err := edwards.PrivKeyFromScalar(kbslRecovered.Serialize()) + if err != nil { + return nil, fmt.Errorf("unable to recover kasl: %v", err) + } + vkbsBig := scalarAdd(c.kbsf.GetD(), kaslRecovered.GetD()) + vkbsBig.Mod(vkbsBig, curve.N) + var vkbsBytes [32]byte + vkbsBig.FillBytes(vkbsBytes[:]) + vkbs, _, err := edwards.PrivKeyFromScalar(vkbsBytes[:]) + if err != nil { + return nil, fmt.Errorf("unable to create vkbs: %v", err) + } + + var fullPubKey []byte + fullPubKey = append(fullPubKey, vkbs.PubKey().Serialize()...) + fullPubKey = append(fullPubKey, c.vkbv.PubKey().Serialize()...) + walletAddr := base58.EncodeAddr(18, fullPubKey) + walletFileName := fmt.Sprintf("%s_spend", walletAddr) + + var vkbvBytes [32]byte + copy(vkbvBytes[:], c.vkbv.Serialize()) + + reverse(&vkbsBytes) + reverse(&vkbvBytes) + + genReq := rpc.GenerateFromKeysRequest{ + Filename: walletFileName, + Address: walletAddr, + SpendKey: hex.EncodeToString(vkbsBytes[:]), + ViewKey: hex.EncodeToString(vkbvBytes[:]), + } + + xmrChecker, err := createNewXMRWallet(ctx, genReq) + if err != nil { + return nil, err + } + + return xmrChecker, nil +} + +// takeDcr is the punish if Bob takes too long. Alice gets the dcr while bob +// gets nothing. +func (c *client) takeDcr(ctx context.Context, lockRefundTxScript []byte, spendRefundTx *wire.MsgTx) (err error) { + newAddr, err := c.dcr.GetNewAddress(ctx, "default") + if err != nil { + return err + } + p2AddrScriptVer, p2AddrScript := newAddr.PaymentScript() + txOut := &wire.TxOut{ + Value: dcrAmt - dumbFee - dumbFee, + Version: p2AddrScriptVer, + PkScript: p2AddrScript, + } + spendRefundTx.TxOut[0] = txOut + + kafSig, err := sign.RawTxInSignature(spendRefundTx, 0, lockRefundTxScript, txscript.SigHashAll, c.kaf.Serialize(), dcrec.STSchnorrSecp256k1) + if err != nil { + return err + } + refundSig, err := txscript.NewScriptBuilder(). + AddData(kafSig). + AddOp(txscript.OP_FALSE). + AddData(lockRefundTxScript). + Script() + if err != nil { + return err + } + + spendRefundTx.TxIn[0].SignatureScript = refundSig + + _, err = c.dcr.SendRawTransaction(ctx, spendRefundTx, false) + if err != nil { + return err + } + // TODO: Confirm refund happened. + return nil +} + +// success is a successful trade. +func success(ctx context.Context) error { + alice, err := newClient(ctx, "http://127.0.0.1:28284/json_rpc", "trading1") + if err != nil { + return err + } + balReq := rpc.GetBalanceRequest{ + AccountIndex: 0, + } + xmrBal, err := alice.xmr.GetBalance(ctx, &balReq) + if err != nil { + return err + } + fmt.Printf("alice xmr balance\n%+v\n", *xmrBal) + + dcrBal, err := alice.dcr.GetBalance(ctx, "default") + if err != nil { + return err + } + dcrBeforeBal := toAtoms(dcrBal.Balances[0].Total) + fmt.Printf("alice dcr balance %v\n", dcrBeforeBal) + + bob, err := newClient(ctx, "http://127.0.0.1:28184/json_rpc", "trading2") + if err != nil { + return err + } + + // Alice generates dleag. + + pkbsf, kbvf, pkaf, aliceDleag, err := alice.generateDleag(ctx) + if err != nil { + return err + } + + // Bob generates transactions but does not send anything yet. + + _, lockRefundTxScript, lockTxScript, refundTx, spendRefundTx, vIn, pkbs, vkbv, bobDleag, bDCRPK, err := bob.generateLockTxn(ctx, pkbsf, kbvf, pkaf, aliceDleag) + if err != nil { + return fmt.Errorf("unalbe to generate lock transactions: %v", err) + } + + // Alice signs a refund script for Bob. + + _, _, err = alice.generateRefundSigs(refundTx, spendRefundTx, vIn, lockTxScript, lockRefundTxScript, bobDleag) + if err != nil { + return err + } + + // Bob initializes the swap with dcr being sent. + + spendTx, err := bob.initDcr(ctx) + if err != nil { + return err + } + + // Alice inits her monero side. + if err := alice.initXmr(ctx, vkbv, pkbs); err != nil { + return err + } + + time.Sleep(time.Second * 5) + + // Bob sends esig after confirming on chain xmr tx. + bobEsig, err := bob.sendLockTxSig(lockTxScript, spendTx) + if err != nil { + return err + } + + // Alice redeems using the esig. + kalSig, err := alice.redeemDcr(ctx, bobEsig, lockTxScript, spendTx, bDCRPK) + if err != nil { + return err + } + + // Prove that bob can't just sign the spend tx for the signature we need. + ks, err := sign.RawTxInSignature(spendTx, 0, lockTxScript, txscript.SigHashAll, bob.kal.Serialize(), dcrec.STSchnorrSecp256k1) + if err != nil { + return err + } + if bytes.Equal(ks, kalSig) { + return errors.New("bob was able to get the correct sig without alice") + } + + // Bob redeems the xmr with the dcr signature. + xmrChecker, err := bob.redeemXmr(ctx, kalSig) + if err != nil { + return err + } + + // NOTE: This wallet must sync so may take a long time on mainnet. + // TODO: Wait for wallet sync rather than a dumb sleep. + time.Sleep(time.Second * 40) + + xmrBal, err = xmrChecker.GetBalance(ctx, &balReq) + if err != nil { + return err + } + if xmrBal.Balance != xmrAmt { + return fmt.Errorf("expected redeem xmr balance of %d but got %d", xmrAmt, xmrBal.Balance) + } + + dcrBal, err = alice.dcr.GetBalance(ctx, "default") + if err != nil { + return err + } + dcrAfterBal := toAtoms(dcrBal.Balances[0].Total) + wantBal := dcrBeforeBal + dcrAmt - uint64(dumbFee) + if wantBal != dcrAfterBal { + return fmt.Errorf("expected alice balance to be %d but got %d", wantBal, dcrAfterBal) + } + + return nil +} + +// aliceBailsBeforeXmrInit is a trade that fails because alice does nothing after +// Bob inits. +func aliceBailsBeforeXmrInit(ctx context.Context) error { + alice, err := newClient(ctx, "http://127.0.0.1:28284/json_rpc", "trading1") + if err != nil { + return err + } + + bob, err := newClient(ctx, "http://127.0.0.1:28184/json_rpc", "trading2") + if err != nil { + return err + } + + dcrBal, err := bob.dcr.GetBalance(ctx, "default") + if err != nil { + return err + } + dcrBeforeBal := toAtoms(dcrBal.Balances[0].Total) + + // Alice generates dleag. + + pkbsf, kbvf, pkaf, aliceDleag, err := alice.generateDleag(ctx) + if err != nil { + return err + } + + // Bob generates transactions but does not send anything yet. + + bobRefundSig, lockRefundTxScript, lockTxScript, refundTx, spendRefundTx, vIn, _, _, bobDleag, _, err := bob.generateLockTxn(ctx, pkbsf, kbvf, pkaf, aliceDleag) + if err != nil { + return fmt.Errorf("unalbe to generate lock transactions: %v", err) + } + + // Alice signs a refund script for Bob. + + spendRefundESig, aliceRefundSig, err := alice.generateRefundSigs(refundTx, spendRefundTx, vIn, lockTxScript, lockRefundTxScript, bobDleag) + if err != nil { + return err + } + + // Bob initializes the swap with dcr being sent. + _, err = bob.initDcr(ctx) + if err != nil { + return err + } + + time.Sleep(time.Second * 5) + + // Bob starts the refund. + if err := bob.startRefund(ctx, bobRefundSig, aliceRefundSig, lockTxScript, refundTx); err != nil { + return err + } + + time.Sleep(time.Second * 5) + + // Bob refunds. + _, err = bob.refundDcr(ctx, spendRefundTx, spendRefundESig, lockRefundTxScript) + if err != nil { + return err + } + + time.Sleep(time.Second * 5) + + dcrBal, err = bob.dcr.GetBalance(ctx, "default") + if err != nil { + return err + } + + var initFee uint64 + for _, input := range bob.lockTx.TxIn { + initFee += uint64(input.ValueIn) + } + for _, output := range bob.lockTx.TxOut { + initFee -= uint64(output.Value) + } + + dcrAfterBal := toAtoms(dcrBal.Balances[0].Total) + wantBal := dcrBeforeBal - initFee - uint64(dumbFee)*2 + if wantBal != dcrAfterBal { + return fmt.Errorf("expected bob balance to be %d but got %d", wantBal, dcrAfterBal) + } + + return nil +} + +// refund is a failed trade where both parties have sent their initial funds and +// both get them back minus fees. +func refund(ctx context.Context) error { + alice, err := newClient(ctx, "http://127.0.0.1:28284/json_rpc", "trading1") + if err != nil { + return err + } + + bob, err := newClient(ctx, "http://127.0.0.1:28184/json_rpc", "trading2") + if err != nil { + return err + } + + // Alice generates dleag. + pkbsf, kbvf, pkaf, aliceDleag, err := alice.generateDleag(ctx) + if err != nil { + return err + } + + // Bob generates transactions but does not send anything yet. + bobRefundSig, lockRefundTxScript, lockTxScript, refundTx, spendRefundTx, vIn, pkbs, vkbv, bobDleag, _, err := bob.generateLockTxn(ctx, pkbsf, kbvf, pkaf, aliceDleag) + if err != nil { + return fmt.Errorf("unalbe to generate lock transactions: %v", err) + } + + // Alice signs a refund script for Bob. + spendRefundESig, aliceRefundSig, err := alice.generateRefundSigs(refundTx, spendRefundTx, vIn, lockTxScript, lockRefundTxScript, bobDleag) + if err != nil { + return err + } + + // Bob initializes the swap with dcr being sent. + _, err = bob.initDcr(ctx) + if err != nil { + return err + } + + // Alice inits her monero side. + if err := alice.initXmr(ctx, vkbv, pkbs); err != nil { + return err + } + + time.Sleep(time.Second * 5) + + // Bob starts the refund. + if err := bob.startRefund(ctx, bobRefundSig, aliceRefundSig, lockTxScript, refundTx); err != nil { + return err + } + + time.Sleep(time.Second * 5) + + // Bob refunds. + kafSig, err := bob.refundDcr(ctx, spendRefundTx, spendRefundESig, lockRefundTxScript) + if err != nil { + return err + } + + // Alice refunds. + xmrChecker, err := alice.refundXmr(ctx, kafSig, spendRefundESig) + if err != nil { + return err + } + + // NOTE: This wallet must sync so may take a long time on mainnet. + // TODO: Wait for wallet sync rather than a dumb sleep. + time.Sleep(time.Second * 40) + + balReq := rpc.GetBalanceRequest{} + bal, err := xmrChecker.GetBalance(ctx, &balReq) + if err != nil { + return err + } + if bal.Balance != xmrAmt { + return fmt.Errorf("expected refund xmr balance of %d but got %d", xmrAmt, bal.Balance) + } + + fmt.Printf("new xmr wallet balance\n%+v\n", *bal) + + return nil +} + +// bobBailsAfterXmrInit is a failed trade where bob disappears after both parties +// init and alice takes all his dcr while losing her xmr. Bob gets nothing. +func bobBailsAfterXmrInit(ctx context.Context) error { + alice, err := newClient(ctx, "http://127.0.0.1:28284/json_rpc", "trading1") + if err != nil { + return err + } + + bob, err := newClient(ctx, "http://127.0.0.1:28184/json_rpc", "trading2") + if err != nil { + return err + } + + dcrBal, err := alice.dcr.GetBalance(ctx, "default") + if err != nil { + return err + } + dcrBeforeBal := toAtoms(dcrBal.Balances[0].Total) + + // Alice generates dleag. + + pkbsf, kbvf, pkaf, aliceDleag, err := alice.generateDleag(ctx) + if err != nil { + return err + } + + // Bob generates transactions but does not send anything yet. + + bobRefundSig, lockRefundTxScript, lockTxScript, refundTx, spendRefundTx, vIn, pkbs, vkbv, bobDleag, _, err := bob.generateLockTxn(ctx, pkbsf, kbvf, pkaf, aliceDleag) + if err != nil { + return fmt.Errorf("unalbe to generate lock transactions: %v", err) + } + + // Alice signs a refund script for Bob. + + _, aliceRefundSig, err := alice.generateRefundSigs(refundTx, spendRefundTx, vIn, lockTxScript, lockRefundTxScript, bobDleag) + if err != nil { + return err + } + + // Bob initializes the swap with dcr being sent. + + _, err = bob.initDcr(ctx) + if err != nil { + return err + } + + // Alice inits her monero side. + if err := alice.initXmr(ctx, vkbv, pkbs); err != nil { + return err + } + + time.Sleep(time.Second * 5) + + // Alice starts the refund. + if err := alice.startRefund(ctx, bobRefundSig, aliceRefundSig, lockTxScript, refundTx); err != nil { + return err + } + + // Lessen this sleep for failure. Two blocks must be mined for success. + time.Sleep(time.Second * 35) + + if err := alice.takeDcr(ctx, lockRefundTxScript, spendRefundTx); err != nil { + return err + } + + time.Sleep(time.Second * 5) + + dcrBal, err = alice.dcr.GetBalance(ctx, "default") + if err != nil { + return err + } + + dcrAfterBal := toAtoms(dcrBal.Balances[0].Total) + wantBal := dcrBeforeBal + dcrAmt - uint64(dumbFee)*2 + if wantBal != dcrAfterBal { + return fmt.Errorf("expected alice balance to be %d but got %d", wantBal, dcrAfterBal) + } + return nil +} diff --git a/internal/libsecp256k1/README.md b/internal/libsecp256k1/README.md deleted file mode 100644 index 5112e5c0f6..0000000000 --- a/internal/libsecp256k1/README.md +++ /dev/null @@ -1,10 +0,0 @@ -### Package libsecp256k1 - -Package libsecp256k1 includes some primative cryptographic functions needed for -working with adaptor signatures that are not currently found in golang. This imports -code from https://github.com/tecnovert/secp256k1 and uses that with cgo. Both -that library and this package are in an experimental stage. - -### Usage - -Run the `build.sh` script. Currently untested on mac and will not work on Windows. diff --git a/internal/libsecp256k1/build.sh b/internal/libsecp256k1/build.sh deleted file mode 100755 index b19343f0c7..0000000000 --- a/internal/libsecp256k1/build.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -rm -fr secp256k1 -git clone https://github.com/tecnovert/secp256k1 -b anonswap_v0.2 - -cd secp256k1 -./autogen.sh -./configure --enable-module-dleag --enable-experimental --enable-module-generator --enable-module-ed25519 --enable-module-recovery --enable-module-ecdsaotves -make -cd .. diff --git a/internal/libsecp256k1/libsecp256k1.go b/internal/libsecp256k1/libsecp256k1.go deleted file mode 100644 index 637435cc79..0000000000 --- a/internal/libsecp256k1/libsecp256k1.go +++ /dev/null @@ -1,149 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package libsecp256k1 - -/* -#cgo CFLAGS: -g -Wall -#cgo LDFLAGS: -L. -l:secp256k1/.libs/libsecp256k1.a -#include "secp256k1/include/secp256k1_dleag.h" -#include "secp256k1/include/secp256k1_ecdsaotves.h" -#include - -secp256k1_context* _ctx() { - return secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); -} -*/ -import "C" -import ( - "errors" - "unsafe" - - "decred.org/dcrdex/dex/encode" - "github.com/decred/dcrd/dcrec/edwards/v2" - "github.com/decred/dcrd/dcrec/secp256k1/v4" -) - -const ( - ProofLen = 48893 - CTLen = 196 - maxSigLen = 72 // The actual size is variable. -) - -// Ed25519DleagProve creates a proof for checking a discrete logarithm is equal -// across the secp256k1 and ed25519 curves. -func Ed25519DleagProve(privKey *edwards.PrivateKey) (proof [ProofLen]byte, err error) { - secpCtx := C._ctx() - defer C.free(unsafe.Pointer(secpCtx)) - nonce := [32]byte{} - copy(nonce[:], encode.RandomBytes(32)) - key := [32]byte{} - copy(key[:], privKey.Serialize()) - n := (*C.uchar)(unsafe.Pointer(&nonce)) - k := (*C.uchar)(unsafe.Pointer(&key)) - nBits := uint64(252) - nb := (*C.ulong)(unsafe.Pointer(&nBits)) - plen := C.ulong(ProofLen) - p := (*C.uchar)(unsafe.Pointer(&proof)) - res := C.secp256k1_ed25519_dleag_prove(secpCtx, p, &plen, k, *nb, n) - if int(res) != 1 { - return [ProofLen]byte{}, errors.New("C.secp256k1_ed25519_dleag_prove exited with error") - } - return proof, nil -} - -// Ed25519DleagVerify verifies that a descrete logarithm is equal across the -// secp256k1 and ed25519 curves. -func Ed25519DleagVerify(proof [ProofLen]byte) bool { - secpCtx := C._ctx() - defer C.free(unsafe.Pointer(secpCtx)) - pl := C.ulong(ProofLen) - p := (*C.uchar)(unsafe.Pointer(&proof)) - res := C.secp256k1_ed25519_dleag_verify(secpCtx, p, pl) - return res == 1 -} - -// EcdsaotvesEncSign signs the hash and returns an encrypted signature. -func EcdsaotvesEncSign(signPriv *secp256k1.PrivateKey, encPub *secp256k1.PublicKey, hash [32]byte) (cyphertext [CTLen]byte, err error) { - secpCtx := C._ctx() - defer C.free(unsafe.Pointer(secpCtx)) - privBytes := [32]byte{} - copy(privBytes[:], signPriv.Serialize()) - priv := (*C.uchar)(unsafe.Pointer(&privBytes)) - pubBytes := [33]byte{} - copy(pubBytes[:], encPub.SerializeCompressed()) - pub := (*C.uchar)(unsafe.Pointer(&pubBytes)) - h := (*C.uchar)(unsafe.Pointer(&hash)) - s := (*C.uchar)(unsafe.Pointer(&cyphertext)) - res := C.ecdsaotves_enc_sign(secpCtx, s, priv, pub, h) - if int(res) != 1 { - return [CTLen]byte{}, errors.New("C.ecdsaotves_enc_sign exited with error") - } - return cyphertext, nil -} - -// EcdsaotvesEncVerify verifies the encrypted signature. -func EcdsaotvesEncVerify(signPub, encPub *secp256k1.PublicKey, hash [32]byte, cyphertext [CTLen]byte) bool { - secpCtx := C._ctx() - defer C.free(unsafe.Pointer(secpCtx)) - signBytes := [33]byte{} - copy(signBytes[:], signPub.SerializeCompressed()) - sp := (*C.uchar)(unsafe.Pointer(&signBytes)) - encBytes := [33]byte{} - copy(encBytes[:], encPub.SerializeCompressed()) - ep := (*C.uchar)(unsafe.Pointer(&encBytes)) - h := (*C.uchar)(unsafe.Pointer(&hash)) - c := (*C.uchar)(unsafe.Pointer(&cyphertext)) - res := C.ecdsaotves_enc_verify(secpCtx, sp, ep, h, c) - return res == 1 -} - -// EcdsaotvesDecSig retrieves the signature. -func EcdsaotvesDecSig(encPriv *secp256k1.PrivateKey, cyphertext [CTLen]byte) ([]byte, error) { - secpCtx := C._ctx() - defer C.free(unsafe.Pointer(secpCtx)) - encBytes := [32]byte{} - copy(encBytes[:], encPriv.Serialize()) - ep := (*C.uchar)(unsafe.Pointer(&encBytes)) - ct := (*C.uchar)(unsafe.Pointer(&cyphertext)) - var sig [maxSigLen]byte - s := (*C.uchar)(unsafe.Pointer(&sig)) - slen := C.ulong(maxSigLen) - res := C.ecdsaotves_dec_sig(secpCtx, s, &slen, ep, ct) - if int(res) != 1 { - return nil, errors.New("C.ecdsaotves_dec_sig exited with error") - } - sigCopy := make([]byte, maxSigLen) - copy(sigCopy, sig[:]) - // Remove trailing zeros. - for i := maxSigLen - 1; i >= 0; i-- { - if sigCopy[i] != 0 { - break - } - sigCopy = sigCopy[:i] - } - return sigCopy, nil -} - -// EcdsaotvesRecEncKey retrieves the encoded private key from signature and -// cyphertext. -func EcdsaotvesRecEncKey(encPub *secp256k1.PublicKey, cyphertext [CTLen]byte, sig []byte) (encPriv *secp256k1.PrivateKey, err error) { - secpCtx := C._ctx() - defer C.free(unsafe.Pointer(secpCtx)) - pubBytes := [33]byte{} - copy(pubBytes[:], encPub.SerializeCompressed()) - ep := (*C.uchar)(unsafe.Pointer(&pubBytes)) - ct := (*C.uchar)(unsafe.Pointer(&cyphertext)) - sigCopy := [maxSigLen]byte{} - copy(sigCopy[:], sig) - s := (*C.uchar)(unsafe.Pointer(&sigCopy)) - varSigLen := len(sig) - slen := C.ulong(varSigLen) - pkBytes := [32]byte{} - pk := (*C.uchar)(unsafe.Pointer(&pkBytes)) - res := C.ecdsaotves_rec_enc_key(secpCtx, pk, ep, ct, s, slen) - if int(res) != 1 { - return nil, errors.New("C.ecdsaotves_rec_enc_key exited with error") - } - return secp256k1.PrivKeyFromBytes(pkBytes[:]), nil -} diff --git a/internal/libsecp256k1/libsecp256k1_test.go b/internal/libsecp256k1/libsecp256k1_test.go deleted file mode 100644 index fd16d6e97f..0000000000 --- a/internal/libsecp256k1/libsecp256k1_test.go +++ /dev/null @@ -1,215 +0,0 @@ -//go:build libsecp256k1 - -package libsecp256k1 - -import ( - "bytes" - "math/rand" - "testing" - - "github.com/decred/dcrd/dcrec/edwards/v2" - "github.com/decred/dcrd/dcrec/secp256k1/v4" -) - -func randBytes(n int) []byte { - b := make([]byte, n) - rand.Read(b) - return b -} - -func TestEd25519DleagProve(t *testing.T) { - tests := []struct { - name string - }{{ - name: "ok", - }} - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - pk, err := edwards.GeneratePrivateKey() - if err != nil { - t.Fatal(err) - } - sPk := secp256k1.PrivKeyFromBytes(pk.Serialize()) - proof, err := Ed25519DleagProve(pk) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(sPk.PubKey().SerializeCompressed(), proof[:33]) { - t.Fatal("first 33 bytes of proof not equal to secp256k1 pubkey") - } - }) - } -} - -func TestEd25519DleagVerify(t *testing.T) { - pk, err := edwards.GeneratePrivateKey() - if err != nil { - panic(err) - } - proof, err := Ed25519DleagProve(pk) - if err != nil { - panic(err) - } - tests := []struct { - name string - proof [ProofLen]byte - ok bool - }{{ - name: "ok", - proof: proof, - ok: true, - }, { - name: "bad proof", - proof: func() (p [ProofLen]byte) { - copy(p[:], proof[:]) - p[0] ^= p[0] - return p - }(), - }} - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - ok := Ed25519DleagVerify(test.proof) - if ok != test.ok { - t.Fatalf("want %v but got %v", test.ok, ok) - } - }) - } -} - -func TestEcdsaotvesEncSign(t *testing.T) { - tests := []struct { - name string - }{{ - name: "ok", - }} - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - signPk, err := secp256k1.GeneratePrivateKey() - if err != nil { - t.Fatal(err) - } - encPk, err := secp256k1.GeneratePrivateKey() - if err != nil { - t.Fatal(err) - } - h := randBytes(32) - var hash [32]byte - copy(hash[:], h) - _, err = EcdsaotvesEncSign(signPk, encPk.PubKey(), hash) - if err != nil { - t.Fatal(err) - } - }) - } -} - -func TestEcdsaotvesEncVerify(t *testing.T) { - signPk, err := secp256k1.GeneratePrivateKey() - if err != nil { - t.Fatal(err) - } - encPk, err := secp256k1.GeneratePrivateKey() - if err != nil { - t.Fatal(err) - } - h := randBytes(32) - var hash [32]byte - copy(hash[:], h) - ct, err := EcdsaotvesEncSign(signPk, encPk.PubKey(), hash) - if err != nil { - t.Fatal(err) - } - tests := []struct { - name string - ok bool - ct [196]byte - }{{ - name: "ok", - ct: ct, - ok: true, - }, { - name: "bad sig", - ct: func() (c [CTLen]byte) { - copy(c[:], ct[:]) - c[0] ^= c[0] - return c - }(), - }} - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - ok := EcdsaotvesEncVerify(signPk.PubKey(), encPk.PubKey(), hash, test.ct) - if ok != test.ok { - t.Fatalf("want %v but got %v", test.ok, ok) - } - }) - } -} - -func TestEcdsaotvesDecSig(t *testing.T) { - signPk, err := secp256k1.GeneratePrivateKey() - if err != nil { - t.Fatal(err) - } - encPk, err := secp256k1.GeneratePrivateKey() - if err != nil { - t.Fatal(err) - } - h := randBytes(32) - var hash [32]byte - copy(hash[:], h) - ct, err := EcdsaotvesEncSign(signPk, encPk.PubKey(), hash) - if err != nil { - t.Fatal(err) - } - tests := []struct { - name string - }{{ - name: "ok", - }} - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - _, err := EcdsaotvesDecSig(encPk, ct) - if err != nil { - t.Fatal(err) - } - }) - } -} - -func TestEcdsaotvesRecEncKey(t *testing.T) { - signPk, err := secp256k1.GeneratePrivateKey() - if err != nil { - t.Fatal(err) - } - encPk, err := secp256k1.GeneratePrivateKey() - if err != nil { - t.Fatal(err) - } - h := randBytes(32) - var hash [32]byte - copy(hash[:], h) - ct, err := EcdsaotvesEncSign(signPk, encPk.PubKey(), hash) - if err != nil { - t.Fatal(err) - } - sig, err := EcdsaotvesDecSig(encPk, ct) - if err != nil { - t.Fatal(err) - } - tests := []struct { - name string - }{{ - name: "ok", - }} - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - pk, err := EcdsaotvesRecEncKey(encPk.PubKey(), ct, sig) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(pk.Serialize(), encPk.Serialize()) { - t.Fatal("private keys not equal") - } - }) - } -} diff --git a/internal/libsecp256k1/secp256k1 b/internal/libsecp256k1/secp256k1 deleted file mode 160000 index e3ebcd782a..0000000000 --- a/internal/libsecp256k1/secp256k1 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e3ebcd782a604f228784b10c50ffa099d9796720 diff --git a/run_tests.sh b/run_tests.sh index 1ec7227cab..ffd79def1c 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -9,10 +9,6 @@ echo "Go version: $GV" # Ensure html templates pass localization. go generate -x ./client/webserver/site # no -write -cd ./internal/libsecp256k1 -./build.sh -go test -race -tags libsecp256k1 - cd "$dir" # list of all modules to test