From 3d67bf23b0c1ea265bae4e23ad083022f8eb4a35 Mon Sep 17 00:00:00 2001 From: sekulicd Date: Wed, 20 May 2020 13:41:54 +0200 Subject: [PATCH 1/3] added factory method FromPublicKeys for creating multiscript payment --- payment/payment.go | 36 +++++++++++++++++++++++++++++++++++- payment/payment_test.go | 29 ++++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/payment/payment.go b/payment/payment.go index 6fe6fe1..5ae1450 100644 --- a/payment/payment.go +++ b/payment/payment.go @@ -3,7 +3,9 @@ package payment import ( "crypto/sha256" "errors" + "fmt" "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/txscript" "github.com/vulpemventures/go-elements/address" "github.com/vulpemventures/go-elements/network" "golang.org/x/crypto/ripemd160" @@ -11,7 +13,8 @@ import ( ) const ( - Op0 = 0x00 + Op0 = 0x00 // 0 + OpCheckMultiSig = 0xae // 174 ) // Payment defines the structure that holds the information different addresses @@ -45,6 +48,37 @@ func FromPublicKey(pubkey *btcec.PublicKey, net *network.Network) *Payment { return &Payment{tmpNet, pubkey, pkHash, nil, nil, script, witnessHash[:]} } +// FromPublicKeys creates a multi-signature Payment struct from list of public key's +func FromPublicKeys(pubkeys []*btcec.PublicKey, numOfPksRequired int, net *network.Network) (*Payment, error) { + if len(pubkeys) < numOfPksRequired { + errorMsg := fmt.Sprintf("unable to generate multisig script with "+ + "%d required signatures when there are only %d public "+ + "keys available", numOfPksRequired, len(pubkeys)) + return nil, errors.New(errorMsg) + } + + var tmpNet *network.Network + if net == nil { + tmpNet = &network.Liquid + } else { + tmpNet = net + } + + builder := txscript.NewScriptBuilder().AddInt64(int64(numOfPksRequired)) + for _, key := range pubkeys { + builder.AddData(key.SerializeCompressed()) + } + builder.AddInt64(int64(len(pubkeys))) + builder.AddOp(OpCheckMultiSig) + + multiSigScript, err := builder.Script() + if err != nil { + return nil, err + } + + return FromScript(multiSigScript, tmpNet) +} + // FromPayment creates a Payment struct from a another Payment func FromPayment(payment *Payment) (*Payment, error) { if payment.Script == nil || len(payment.Script) == 0 { diff --git a/payment/payment_test.go b/payment/payment_test.go index 9457343..702ff41 100644 --- a/payment/payment_test.go +++ b/payment/payment_test.go @@ -8,12 +8,16 @@ import ( "testing" ) -const privKeyHex = "1cc080a4cd371eafcad489a29664af6a7276b362fe783443ce036552482b971d" +const ( + privKeyHex1 = "1cc080a4cd371eafcad489a29664af6a7276b362fe783443ce036552482b971d" + privKeyHex2 = "4d6718d4a02f774e752faa97e2c3b70db6b9d9ed5bd2fcecb093bd650f449a51" +) -var privateKeyBytes, _ = hex.DecodeString(privKeyHex) +var privateKeyBytes1, _ = hex.DecodeString(privKeyHex1) +var privateKeyBytes2, _ = hex.DecodeString(privKeyHex2) func TestLegacyAddress(t *testing.T) { - _, publicKey := btcec.PrivKeyFromBytes(btcec.S256(), privateKeyBytes) + _, publicKey := btcec.PrivKeyFromBytes(btcec.S256(), privateKeyBytes1) pay := payment.FromPublicKey(publicKey, &network.Regtest) if pay.PubKeyHash() != "2dxEMfPLNa6rZRAfPe7wNWoaUptyBzQ2Zva" { @@ -22,7 +26,7 @@ func TestLegacyAddress(t *testing.T) { } func TestSegwitAddress(t *testing.T) { - _, publicKey := btcec.PrivKeyFromBytes(btcec.S256(), privateKeyBytes) + _, publicKey := btcec.PrivKeyFromBytes(btcec.S256(), privateKeyBytes1) pay := payment.FromPublicKey(publicKey, &network.Regtest) p2pkh, err := pay.WitnessPubKeyHash() @@ -35,7 +39,7 @@ func TestSegwitAddress(t *testing.T) { } func TestScriptHash(t *testing.T) { - _, publicKey := btcec.PrivKeyFromBytes(btcec.S256(), privateKeyBytes) + _, publicKey := btcec.PrivKeyFromBytes(btcec.S256(), privateKeyBytes1) p2wpkh := payment.FromPublicKey(publicKey, &network.Regtest) pay, err := payment.FromPayment(p2wpkh) p2sh, err := pay.ScriptHash() @@ -72,3 +76,18 @@ func TestP2WSH(t *testing.T) { t.Errorf("TestSegwitAddress: error when encoding segwit") } } + +func TestFromPublicKeys(t *testing.T) { + _, publicKey1 := btcec.PrivKeyFromBytes(btcec.S256(), privateKeyBytes1) + _, publicKey2 := btcec.PrivKeyFromBytes(btcec.S256(), privateKeyBytes2) + + p2ms, err := payment.FromPublicKeys([]*btcec.PublicKey{publicKey1, publicKey2}, 1, &network.Regtest) + if err != nil { + t.Error(err) + } + + if hex.EncodeToString(p2ms.Script) != "5121036f5646ed688b9279369da0a4ad78953ae7e6d300436ca8a3264360efe38236e321023c61f59e9a3a3eb01c3ed0cf967ad217153944bcf2498a8fd6e70b27c7ab6ee652ae" { + t.Error("hax value of p2ms script not as expected") + } + +} From d7d5dff6737d73ad966b2ee3816fd12c1c0ede56 Mon Sep 17 00:00:00 2001 From: sekulicd Date: Thu, 23 Jun 2022 16:30:35 +0200 Subject: [PATCH 2/3] FromOutputScript --- .golangci.yml | 57 ++++++++++++++++ payment/payment.go | 86 +++++++++++++++++++++++ payment/payment_test.go | 148 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 291 insertions(+) create mode 100644 .golangci.yml diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..f32f795 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,57 @@ +run: + deadline: 10m + +linters: + disable-all: true + enable: + - deadcode + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - structcheck + - typecheck + - unused + - varcheck + - asciicheck + - bidichk + - bodyclose + - contextcheck + - cyclop + - dupl + - durationcheck + - errorlint + - funlen + - gocognit + - gocritic + - gocyclo + - gofmt + - goimports + - gosec + - ifshort + - ireturn + - lll + - misspell + - nestif + - nilerr + - nilnil + - nlreturn + - noctx + - predeclared + - revive + - rowserrcheck + - sqlclosecheck + - stylecheck + - tagliatelle + - tenv + - unconvert + - unparam + - wastedassign + - whitespace + - wrapcheck + - gochecknoglobals + - gochecknoinits + - gomnd + - thelper + - gofumpt diff --git a/payment/payment.go b/payment/payment.go index c6f9260..b0a0ceb 100644 --- a/payment/payment.go +++ b/payment/payment.go @@ -373,3 +373,89 @@ func buildScript(hash []byte, scriptType string) []byte { script, _ := builder.Script() return script } + +func FromOutputScript( + net *network.Network, + outputScript []byte, + blindingKey *btcec.PublicKey, +) (string, error) { + p, err := FromScript(outputScript, net, blindingKey) + if err != nil { + return "", err + } + + switch address.GetScriptType(outputScript) { + case address.P2WpkhScript: + addr, err := p.WitnessPubKeyHash() + if err != nil { + return "", err + } + + if blindingKey != nil { + addr, err = p.ConfidentialWitnessPubKeyHash() + if err != nil { + return "", err + } + } + + return addr, nil + case address.P2WshScript: + addr, err := p.WitnessScriptHash() + if err != nil { + return "", err + } + + if blindingKey != nil { + addr, err = p.ConfidentialWitnessScriptHash() + if err != nil { + return "", err + } + } + + return addr, nil + case address.P2ShScript: + addr, err := p.ScriptHash() + if err != nil { + return "", err + } + + if blindingKey != nil { + addr, err = p.ConfidentialScriptHash() + if err != nil { + return "", err + } + } + + return addr, nil + case address.P2PkhScript: + addr, err := p.PubKeyHash() + if err != nil { + return "", err + } + + if blindingKey != nil { + addr, err = p.ConfidentialPubKeyHash() + if err != nil { + return "", err + } + } + + return addr, nil + case address.P2TRScript: + addr, err := p.TaprootAddress() + if err != nil { + return "", err + } + + if blindingKey != nil { + addr, err = p.ConfidentialTaprootAddress() + if err != nil { + return "", err + } + } + + return addr, nil + default: + return "", errors.New("unsupported script type") + } +} diff --git a/payment/payment_test.go b/payment/payment_test.go index f3764be..e84c6fb 100644 --- a/payment/payment_test.go +++ b/payment/payment_test.go @@ -216,3 +216,151 @@ func TestSegwitScriptHashConfidential(t *testing.T) { "rzmrq2mc3c6aa85wgxxfm9v8r062qwq4ty579p54pn2q2hq6f9r3gz0h4tn" assert.Equal(t, expected, addr) } + +func TestFromOutputScript(t *testing.T) { + privKey, err := btcec.NewPrivateKey() + if err != nil { + t.Fatal(err) + } + + blindPrivKey, err := btcec.NewPrivateKey() + if err != nil { + t.Fatal(err) + } + + redeemScript := "52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959" + + "f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d0" + + "8ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09" + + "b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950" + + "cfe52a52ae" + redeemScriptBytes, err := hex.DecodeString(redeemScript) + if err != nil { + t.Error(err) + } + + //p2pkh + p2pkh := payment.FromPublicKey( + privKey.PubKey(), + &network.Regtest, + blindPrivKey.PubKey(), + ) + + p2pkhAddress, err := p2pkh.ConfidentialPubKeyHash() + if err != nil { + t.Fatal(err) + } + + p2pkhAddress1, err := payment.FromOutputScript( + &network.Regtest, + p2pkh.Script, + blindPrivKey.PubKey(), + ) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, p2pkhAddress, p2pkhAddress1) + + //p2sh + p2ms, err := payment.FromScript( + redeemScriptBytes, + &network.Regtest, + blindPrivKey.PubKey(), + ) + if err != nil { + t.Error(err) + } + + p2sh, err := payment.FromPayment(p2ms) + if err != nil { + t.Error(err) + } + + p2shAddress, err := p2sh.ConfidentialScriptHash() + if err != nil { + t.Error(err) + } + + p2shAddress1, err := payment.FromOutputScript( + &network.Regtest, + p2sh.Script, + blindPrivKey.PubKey(), + ) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, p2shAddress, p2shAddress1) + //p2wpkh + p2wpkh := payment.FromPublicKey( + privKey.PubKey(), + &network.Regtest, + blindPrivKey.PubKey(), + ) + + p2wpkhAddress, err := p2wpkh.ConfidentialWitnessPubKeyHash() + if err != nil { + t.Fatal(err) + } + + p2wpkhAddress1, err := payment.FromOutputScript( + &network.Regtest, + p2wpkh.WitnessScript, + blindPrivKey.PubKey(), + ) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, p2wpkhAddress, p2wpkhAddress1) + + //p2wsh + p2wsh, err := payment.FromPayment(p2ms) + if err != nil { + t.Error(err) + } + + p2wshAddress, err := p2wsh.ConfidentialWitnessScriptHash() + if err != nil { + t.Error(err) + } + + p2wshAddress1, err := payment.FromOutputScript( + &network.Regtest, + p2wsh.WitnessScript, + blindPrivKey.PubKey(), + ) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, p2wshAddress, p2wshAddress1) + + //p2tr + p2tr, err := payment.FromTaprootScriptTreeHash( + privKey.PubKey(), + nil, + &network.Regtest, + blindingKey.PubKey(), + ) + + if err != nil { + t.Error(err) + } + + p2trAddress, err := p2tr.ConfidentialTaprootAddress() + if err != nil { + t.Error(err) + } + + p2trAddress1, err := payment.FromOutputScript( + &network.Regtest, + p2tr.Script, + blindPrivKey.PubKey(), + ) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, p2trAddress, p2trAddress1) +} From 43dc3dbfeb41eb0bb4c3785cb0b981c2a5cc42cb Mon Sep 17 00:00:00 2001 From: sekulicd Date: Thu, 23 Jun 2022 16:35:55 +0200 Subject: [PATCH 3/3] remove golanglint file --- .golangci.yml | 57 --------------------------------------------------- 1 file changed, 57 deletions(-) delete mode 100644 .golangci.yml diff --git a/.golangci.yml b/.golangci.yml deleted file mode 100644 index f32f795..0000000 --- a/.golangci.yml +++ /dev/null @@ -1,57 +0,0 @@ -run: - deadline: 10m - -linters: - disable-all: true - enable: - - deadcode - - errcheck - - gosimple - - govet - - ineffassign - - staticcheck - - structcheck - - typecheck - - unused - - varcheck - - asciicheck - - bidichk - - bodyclose - - contextcheck - - cyclop - - dupl - - durationcheck - - errorlint - - funlen - - gocognit - - gocritic - - gocyclo - - gofmt - - goimports - - gosec - - ifshort - - ireturn - - lll - - misspell - - nestif - - nilerr - - nilnil - - nlreturn - - noctx - - predeclared - - revive - - rowserrcheck - - sqlclosecheck - - stylecheck - - tagliatelle - - tenv - - unconvert - - unparam - - wastedassign - - whitespace - - wrapcheck - - gochecknoglobals - - gochecknoinits - - gomnd - - thelper - - gofumpt