Skip to content

Commit

Permalink
Add low level rpc to sign taproot inputs (#61)
Browse files Browse the repository at this point in the history
* Add internal support for schnorr signing

* Update deps

* Update protos and interface layer

* Fixes after review

* Fixes (#2)

* pair programming fixes

* fix prevouts assets, values and scripts

* Fixes

* Update deps

* Fixes

* Update deps

---------

Co-authored-by: Louis Singer <41042567+louisinger@users.noreply.github.com>
  • Loading branch information
altafan and louisinger authored Jan 30, 2024
1 parent 4c78c72 commit b109ba0
Show file tree
Hide file tree
Showing 10 changed files with 639 additions and 140 deletions.
420 changes: 284 additions & 136 deletions api-spec/protobuf/gen/go/ocean/v1/transaction.pb.go

Large diffs are not rendered by default.

40 changes: 40 additions & 0 deletions api-spec/protobuf/gen/go/ocean/v1/transaction_grpc.pb.go

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

16 changes: 15 additions & 1 deletion api-spec/protobuf/ocean/v1/transaction.proto
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ service TransactionService {

// Transfer returns a transaction to send funds to some receiver.
rpc Transfer(TransferRequest) returns (TransferResponse);


// PegInAddress returns what's necessary to peg funds of the Bitcoin
// main-chain and have them available on the Liquid side-chain.
Expand All @@ -61,6 +60,10 @@ service TransactionService {
// ClaimPegIn returns a transaction to claim funds pegged on the Bitcoin
// main-chain to have them available on the Liquid side-chain.
rpc ClaimPegIn(ClaimPegInRequest) returns (ClaimPegInResponse);

// SignPsetWithSchnorrKey signs all taproot inputs of the provided tx with
// the key at the given derivation path.
rpc SignPsetWithSchnorrKey(SignPsetWithSchnorrKeyRequest) returns (SignPsetWithSchnorrKeyResponse);
}

message GetTransactionRequest{
Expand Down Expand Up @@ -261,4 +264,15 @@ message ClaimPegInRequest{
message ClaimPegInResponse{
// Signed tx in hex format.
string tx_hex = 1;
}

message SignPsetWithSchnorrKeyRequest {
// The partial transaction to sign in base64 format.
string tx = 1;
// The sighash type. SIGHASH_DEFAULT is used for any input that does not specify one.
uint32 sighash_type = 2;
}

message SignPsetWithSchnorrKeyResponse {
string signed_tx = 1;
}
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require (
github.com/stretchr/testify v1.8.1
github.com/timshannon/badgerhold/v4 v4.0.2
github.com/vulpemventures/go-bip39 v1.0.2
github.com/vulpemventures/go-elements v0.5.1
github.com/vulpemventures/go-elements v0.5.3
github.com/vulpemventures/neutrino-elements v0.1.3
golang.org/x/crypto v0.1.0
golang.org/x/net v0.7.0
Expand All @@ -31,9 +31,12 @@ require (
github.com/jackc/pgconn v1.12.1
github.com/jackc/pgx/v4 v4.16.1
github.com/tyler-smith/go-bip39 v1.1.0
github.com/vulpemventures/go-bip32 v0.0.0-20200624192635-867c159da4d7
)

require (
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect
github.com/aead/siphash v1.0.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd/btcutil/psbt v1.1.4 // indirect
Expand Down
16 changes: 14 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc=
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw=
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc=
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
Expand Down Expand Up @@ -230,6 +234,8 @@ github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJ
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e h1:0XBUw73chJ1VYSsfvcPvVT7auykAJce9FpRr10L6Qhw=
github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
Expand Down Expand Up @@ -1154,6 +1160,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
Expand Down Expand Up @@ -1199,10 +1206,12 @@ github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941 h1:CTcw80hz/Sw8hqlKX5ZYvBUF5gAHSHwdjXxRf/cjDcI=
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:GXBJykxW2kUcktGdsgyay7uwwWvkljASfljNcT0mbh8=
github.com/vulpemventures/go-bip32 v0.0.0-20200624192635-867c159da4d7 h1:X7DtNv+YWy76kELMZB/xVkIJ7YNp2vpgMFVsDcQA40U=
github.com/vulpemventures/go-bip32 v0.0.0-20200624192635-867c159da4d7/go.mod h1:Zrvx8XgpWvSPdz1lXnuN083CkoZnzwxBLEB03S8et1I=
github.com/vulpemventures/go-bip39 v1.0.2 h1:+BgKOVo04okKf1wA4Fhv8ccvql+qFyzVUdVJKkb48r0=
github.com/vulpemventures/go-bip39 v1.0.2/go.mod h1:mjFmuv9D6BtANI6iscMmbHhmBOwjMCFfny3mxHnuDrk=
github.com/vulpemventures/go-elements v0.5.1 h1:F83n7dScOnAnpyH9VgWLdC68Qcva+2EWVzzNuW2UKak=
github.com/vulpemventures/go-elements v0.5.1/go.mod h1:aBGuWXHaiAIUIcwqCdtEh2iQ3kJjKwHU9ywvhlcRSeU=
github.com/vulpemventures/go-elements v0.5.3 h1:zaC/ynHFwCAzFSOMfzb6BcbD6FXASppSiGMycc95WVA=
github.com/vulpemventures/go-elements v0.5.3/go.mod h1:aBGuWXHaiAIUIcwqCdtEh2iQ3kJjKwHU9ywvhlcRSeU=
github.com/vulpemventures/go-secp256k1-zkp v1.1.6 h1:BmsrmXRLUibwa75Qkk8yELjpzCzlAjYFGLiLiOdq7Xo=
github.com/vulpemventures/go-secp256k1-zkp v1.1.6/go.mod h1:zo7CpgkuPgoe7fAV+inyxsI9IhGmcoFgyD8nqZaPSOM=
github.com/vulpemventures/neutrino-elements v0.1.3 h1:rzg6G1oL2fWodOZ716SOs71JuqttPCjjmig7TrlHrxM=
Expand Down Expand Up @@ -1291,6 +1300,7 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
Expand Down Expand Up @@ -1979,6 +1989,8 @@ k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54=
launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg=
modernc.org/cc/v3 v3.32.4/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878=
modernc.org/ccgo/v3 v3.9.2/go.mod h1:gnJpy6NIVqkETT+L5zPsQFj7L2kkhfPMzOghRNv/CFo=
Expand Down
85 changes: 85 additions & 0 deletions internal/core/application/transaction_service.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package application

import (
"bytes"
"context"
"encoding/binary"
"encoding/hex"
"fmt"
"math"
"strings"
"time"

"github.com/btcsuite/btcd/txscript"
log "github.com/sirupsen/logrus"
"github.com/vulpemventures/go-bip32"
"github.com/vulpemventures/go-elements/address"
"github.com/vulpemventures/go-elements/elementsutil"
"github.com/vulpemventures/go-elements/network"
Expand Down Expand Up @@ -636,6 +640,87 @@ func (ts *TransactionService) Transfer(
return txHex, nil
}

func (ts *TransactionService) SignPsetWithSchnorrKey(
ctx context.Context, tx string, sighashType uint32,
) (string, error) {
wallet, err := ts.repoManager.WalletRepository().GetWallet(ctx)
if err != nil {
return "", err
}
mnemonic, err := wallet.GetMnemonic()
if err != nil {
return "", err
}
ssWallet, err := singlesig.NewWalletFromMnemonic(singlesig.NewWalletFromMnemonicArgs{
RootPath: wallet.RootPath,
Mnemonic: mnemonic,
})
if err != nil {
return "", err
}

ptx, err := psetv2.NewPsetFromBase64(tx)
if err != nil {
return "", err
}
if len(ptx.Global.Xpubs) < 1 {
return "", fmt.Errorf("missing pset global xpubs")
}

// For each global xpub, retrieve account info if it belongs to the wallet.
// Account info are required to know its derivation index, used later.
xpubsInfo := make([]struct {
account *domain.Account
xpub *bip32.Key
}, 0, len(ptx.Global.Xpubs))
for _, xpub := range ptx.Global.Xpubs {
for _, account := range wallet.Accounts {
hdNode, err := bip32.B58Deserialize(account.Xpub)
if err != nil {
return "", err
}
accountXpub, err := hdNode.Serialize()
if err != nil {
return "", err
}

if bytes.Equal(xpub.ExtendedKey, accountXpub[:len(accountXpub)-4]) {
xpubsInfo = append(xpubsInfo, struct {
account *domain.Account
xpub *bip32.Key
}{account, hdNode})
break
}
}
}

// For each input that has a taproot bip32 derivation field,
// construct the derivation path by attaching the bip32 derivation to the
// account's index. This derivation path format is needed by the signing wallet.
derivationPathMap := make(map[string]string)
for _, in := range ptx.Inputs {
for _, derivation := range in.TapBip32Derivation {
for _, info := range xpubsInfo {
if derivation.MasterKeyFingerprint == binary.LittleEndian.Uint32(info.xpub.FingerPrint) {
derivationPath := []string{fmt.Sprintf("%d'", info.account.Index)}
for _, step := range derivation.Bip32Path {
derivationPath = append(derivationPath, fmt.Sprintf("%d", step))
}
derivationPathMap[hex.EncodeToString(in.GetUtxo().Script)] = strings.Join(derivationPath, "/")
break
}
}
}
}

return ssWallet.SignTaproot(singlesig.SignTaprootArgs{
PsetBase64: tx,
DerivationPathMap: derivationPathMap,
GenesisBlockHash: ts.network.GenesisBlockHash,
SighashType: txscript.SigHashType(sighashType),
})
}

func (ts *TransactionService) registerHandlerForWalletEvents() {
ts.repoManager.RegisterHandlerForWalletEvent(
domain.WalletUnlocked, func(_ domain.WalletEvent) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ func (c *tcpClient) unsubscribeForAccount(accountName string) {
// c.unsubscribeForScript(accountName, scriptHash)
// }
c.chHandler.clearAccount(accountName)
delete(c.reportHandlers, accountName)
}

func (c *tcpClient) subscribeForScripts(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ func (c *wsClient) unsubscribeForAccount(accountName string) {
// c.unsubscribeForScript(accountName, scriptHash)
// }
c.chHandler.clearAccount(accountName)
delete(c.reportHandlers, accountName)
}

func (c *wsClient) getScriptHashesHistory(scriptHashes []string) (map[string][]txInfo, error) {
Expand Down
20 changes: 20 additions & 0 deletions internal/interfaces/grpc/handler/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,26 @@ func (t *transaction) ClaimPegIn(
return nil, fmt.Errorf("to be implemented")
}

func (t *transaction) SignPsetWithSchnorrKey(
ctx context.Context, req *pb.SignPsetWithSchnorrKeyRequest,
) (*pb.SignPsetWithSchnorrKeyResponse, error) {
tx, err := parsePset(req.GetTx())
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}

signedTx, err := t.appSvc.SignPsetWithSchnorrKey(
ctx, tx, req.GetSighashType(),
)
if err != nil {
return nil, err
}

return &pb.SignPsetWithSchnorrKeyResponse{
SignedTx: signedTx,
}, nil
}

func validateTxid(txid string) error {
if txid == "" {
return fmt.Errorf("missing txid")
Expand Down
Loading

0 comments on commit b109ba0

Please sign in to comment.