From e87da2e26f6106b28875e56984e36f42f7812bfe Mon Sep 17 00:00:00 2001 From: wqking Date: Sat, 20 Apr 2019 16:24:22 +0800 Subject: [PATCH 01/12] Added coinswap --- misc/coinswap.go | 44 +++++++++++++++++++++++++++++++++++++++++++ misc/coinswap_test.go | 30 +++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 misc/coinswap.go create mode 100644 misc/coinswap_test.go diff --git a/misc/coinswap.go b/misc/coinswap.go new file mode 100644 index 00000000..7a1e3d5e --- /dev/null +++ b/misc/coinswap.go @@ -0,0 +1,44 @@ +package misc + +// This file is for swapping coins from Phore to Synapse + +import ( + "github.com/phoreproject/synapse/chainhash" +) + +type proofEntry struct { + hash *chainhash.Hash + left bool +} + +func computeCombinedHash(left *chainhash.Hash, right *chainhash.Hash) chainhash.Hash { + return chainhash.HashH(append(left[:], right[:]...)) +} + +func computeProofRootHash(hash *chainhash.Hash, entries []proofEntry) chainhash.Hash { + h := *hash + for i := 0; i < len(entries); i++ { + if entries[i].left { + h = computeCombinedHash(entries[i].hash, &h) + } else { + h = computeCombinedHash(&h, entries[i].hash) + } + } + return h +} + +// The text is generated by SynapseSwap::proofListToText in C++ +func textToProofList(text string) []proofEntry { + proofList := []proofEntry{} + + for i := 0; i <= len(text)-65; i += 65 { + direction := text[i : i+1] + hex := text[i+1 : i+65] + entry := proofEntry{} + entry.left = (direction == "L") + entry.hash, _ = chainhash.NewHashFromStr(hex) + proofList = append(proofList, entry) + } + + return proofList +} diff --git a/misc/coinswap_test.go b/misc/coinswap_test.go new file mode 100644 index 00000000..bddd5c4b --- /dev/null +++ b/misc/coinswap_test.go @@ -0,0 +1,30 @@ +package misc + +import ( + "os" + "testing" + + "github.com/phoreproject/synapse/chainhash" + "github.com/sirupsen/logrus" +) + +func TestMain(m *testing.M) { + logrus.SetLevel(logrus.DebugLevel) + retCode := m.Run() + os.Exit(retCode) +} + +func TestComputeProofRootHash(t *testing.T) { + proofText := "L23934a6297dc21e97b29bcae674ce6ab2c3b55debc6a2ee4e225642397f9741bRc2475a9519779c88dbee4a271cfa385b8fc8aba78bf3576611229502cfa542ddL29730be3debbe966922465d420c01d8ec0e7a6390734015f02eafe2a600544b0L9547f2c631baa02fcfca1da0402c9a24eb33bc8fc884f8b121a339b6deb397dfR82348c73e26f97822f8850299b1bd490575e8760c254bba40df7b202312da36dR5623eee090c1ce433bb3b6bf9f33404a1ac08137b41a798a29aab8a962ce70b9L3c9ad4f01adbe8892b0983d6947467ae7c716024c5dd99c580f2b107fcc44260Ldb9932e212fc9f067a6d97637d7374f80a73cf0e59f9888c5a628ce0c16eda93Rd6794b7d2cd035c6fd43695210ee267d8fe5ccd3a810a45949a3beaef2b51f83La28320923cf8ffe7cc083704bcaf116390def62c23a438e7db6b53b7a5879274R1208e7f8d21725b4753c56fef059b2d5284cce6cef82823ed50068549119554cR5c919054901401c6245bef9803b9ad4c9fd4e35f298ca8e59952b2d7b1c48249R9a689187a020df1d026f6bb6b460f204258cad7dbeade42e9029b7f2fc9ae991R3101d97f5c93e99217e25bd7ce9338ee4d54982f18419ecbca1538d2b7eb1d46Lcb6e5397b511d2b34825ed54a0a12ee5546fa2dc2ce7a89b900577b1e1b2ebf8R31aa2dc8c598d6e5302015346d59e7bf9103c1d8142df53bd7db278ba9dce1cbR299011e91a0d9821036d6fc6a6f579b4e2203aa10b744fe2a841310921e9c6d0Rd17b016c8c75f2043b92358e523ab80427567cfdbd72517744412c4172939bdf" + proofList := textToProofList(proofText) + hashText := "00013a9407f671e11e2b81369d788dd8c5144f58ddcb9ee9c185274bc4f4a778" + hash, _ := chainhash.NewHashFromStr(hashText) + rootHashText := "25379816f6a026c5de9b4347fadcbea5942c77459c2bdc47ab10139ecade8f71" + rootHash, _ := chainhash.NewHashFromStr(rootHashText) + + proofHash := computeProofRootHash(hash, proofList) + + if proofHash != *rootHash { + t.Fatalf("Unmatched hash: root=%s computed=%s", rootHashText, proofHash.String()) + } +} From 1abca620932c750308983d04a977a8e05fd1bcdd Mon Sep 17 00:00:00 2001 From: wqking Date: Sun, 21 Apr 2019 09:26:17 +0800 Subject: [PATCH 02/12] Merkle tree proof verification works now. --- misc/coinswap.go | 18 +++++++++++++++++- misc/coinswap_test.go | 16 +++++++++++----- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/misc/coinswap.go b/misc/coinswap.go index 7a1e3d5e..aeda4eff 100644 --- a/misc/coinswap.go +++ b/misc/coinswap.go @@ -12,16 +12,21 @@ type proofEntry struct { } func computeCombinedHash(left *chainhash.Hash, right *chainhash.Hash) chainhash.Hash { - return chainhash.HashH(append(left[:], right[:]...)) + h := chainhash.HashH(chainhash.HashB(append(left[:], right[:]...))) + return h } func computeProofRootHash(hash *chainhash.Hash, entries []proofEntry) chainhash.Hash { h := *hash for i := 0; i < len(entries); i++ { if entries[i].left { + //fmt.Printf("Input L: %s R: %s\n", entries[i].hash.String(), h.String()) h = computeCombinedHash(entries[i].hash, &h) + //fmt.Printf("L compute: %s L\n", h.String()) } else { + //fmt.Printf("Input L: %s R: %s\n", h.String(), entries[i].hash.String()) h = computeCombinedHash(&h, entries[i].hash) + //fmt.Printf("R compute: %s\n", h.String()) } } return h @@ -40,5 +45,16 @@ func textToProofList(text string) []proofEntry { proofList = append(proofList, entry) } + /* + for i := 0; i < len(proofList); i++ { + e := proofList[i] + a := 0 + if e.left { + a = 1 + } + fmt.Printf("%02d %d %s\n", i, a, e.hash.String()) + } + */ + return proofList } diff --git a/misc/coinswap_test.go b/misc/coinswap_test.go index bddd5c4b..cc2cf7b2 100644 --- a/misc/coinswap_test.go +++ b/misc/coinswap_test.go @@ -15,12 +15,18 @@ func TestMain(m *testing.M) { } func TestComputeProofRootHash(t *testing.T) { - proofText := "L23934a6297dc21e97b29bcae674ce6ab2c3b55debc6a2ee4e225642397f9741bRc2475a9519779c88dbee4a271cfa385b8fc8aba78bf3576611229502cfa542ddL29730be3debbe966922465d420c01d8ec0e7a6390734015f02eafe2a600544b0L9547f2c631baa02fcfca1da0402c9a24eb33bc8fc884f8b121a339b6deb397dfR82348c73e26f97822f8850299b1bd490575e8760c254bba40df7b202312da36dR5623eee090c1ce433bb3b6bf9f33404a1ac08137b41a798a29aab8a962ce70b9L3c9ad4f01adbe8892b0983d6947467ae7c716024c5dd99c580f2b107fcc44260Ldb9932e212fc9f067a6d97637d7374f80a73cf0e59f9888c5a628ce0c16eda93Rd6794b7d2cd035c6fd43695210ee267d8fe5ccd3a810a45949a3beaef2b51f83La28320923cf8ffe7cc083704bcaf116390def62c23a438e7db6b53b7a5879274R1208e7f8d21725b4753c56fef059b2d5284cce6cef82823ed50068549119554cR5c919054901401c6245bef9803b9ad4c9fd4e35f298ca8e59952b2d7b1c48249R9a689187a020df1d026f6bb6b460f204258cad7dbeade42e9029b7f2fc9ae991R3101d97f5c93e99217e25bd7ce9338ee4d54982f18419ecbca1538d2b7eb1d46Lcb6e5397b511d2b34825ed54a0a12ee5546fa2dc2ce7a89b900577b1e1b2ebf8R31aa2dc8c598d6e5302015346d59e7bf9103c1d8142df53bd7db278ba9dce1cbR299011e91a0d9821036d6fc6a6f579b4e2203aa10b744fe2a841310921e9c6d0Rd17b016c8c75f2043b92358e523ab80427567cfdbd72517744412c4172939bdf" + proofText := "L1b74f997236425e2e42e6abcde553b2cabe64c67aebc297be921dc97624a9323Rdd42a5cf029522116657f38ba7abc88f5b38fa1c274aeedb889c7719955a47c2R90d0d50efde71fce94e8d213ce62ecb75e2555fa448958d2f6434e8b3ee4740fR5501e10d7b90e7ec87eb956d21a9fde86a6b15cf99d16750c40e0a09e5d463a8L3da0910c3d543c6984a1efc5b22988ee14d719022896940aa9e21bffe4b33cc1Rb823d0827e2b3bd28afafb4e7503e428a20be4690d519d13facd20dd922d1a44R9469b28a94af53b032500d0c930d431fb5fa8e0a64eee6f1e366581873434fd5Le5c217fd53ef06c71502e9f4691ada50a59f191d596c54c6007428161b0007dbL0f7d3ca8672bf5a5bd53b62f4a476e55663b5fd6db365352ae38b4e96bae1a6dLce462ab56f8f9df89c9ef268c2df18ab7cf2b25d77aa84ff1ebcde543ed1fa30Rb3accc225d38eac59aa0a8e8c73fef4adb84478fe57b6a6bd388786dda03ec7dRb9c09b68c9207c58945c32b36c58b899bb69dc8e7b403aa447bb049778abba57R8081ebb0d1c2ba7265852ef6b5350fa1034104eb11b7ed3d0e693c82d1f7664cR99e5eff4721d2d89170f60cd0e355dc74926324aa94559969e4d4448692917e0L78209bd8d8674a60dfc3ad33dfa9d59add69c89d2b0c3da1682f21a01f5d806dRee70f9382a46211d2312de52f75e2bfaf5d90b11a8b9c37e866713734c0fb61aR9611cb8847e702f3bdb5139aacdc540290f3e53b368b79f4511719b99db44e88R8965b2071c637ad7788689b930a083d3d4f572314fba408b45550efc12e4bfa9" proofList := textToProofList(proofText) - hashText := "00013a9407f671e11e2b81369d788dd8c5144f58ddcb9ee9c185274bc4f4a778" - hash, _ := chainhash.NewHashFromStr(hashText) - rootHashText := "25379816f6a026c5de9b4347fadcbea5942c77459c2bdc47ab10139ecade8f71" - rootHash, _ := chainhash.NewHashFromStr(rootHashText) + hashText := "1b7509f59479bc2b048fc8a5d6f5b1b239b3d3d5e818d663817f250bcac7ff47" + hash, err := chainhash.NewHashFromStr(hashText) + if err != nil { + t.Fatal(err) + } + rootHashText := "09a550c89002802b1d5ae473f839ced904439c03bcc19d399869d095312b3d52" + rootHash, err := chainhash.NewHashFromStr(rootHashText) + if err != nil { + t.Fatal(err) + } proofHash := computeProofRootHash(hash, proofList) From b3f80c0b31719233b63e0da9d6d0534de5e92d71 Mon Sep 17 00:00:00 2001 From: wqking Date: Mon, 22 Apr 2019 10:53:29 +0800 Subject: [PATCH 03/12] Use JSON for coin swap data format --- misc/coinswap.go | 41 ++++++++++++++++++++--------------------- misc/coinswap_test.go | 7 +++++-- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/misc/coinswap.go b/misc/coinswap.go index aeda4eff..499b2934 100644 --- a/misc/coinswap.go +++ b/misc/coinswap.go @@ -3,12 +3,14 @@ package misc // This file is for swapping coins from Phore to Synapse import ( + "encoding/json" + "github.com/phoreproject/synapse/chainhash" ) type proofEntry struct { - hash *chainhash.Hash left bool + hash *chainhash.Hash } func computeCombinedHash(left *chainhash.Hash, right *chainhash.Hash) chainhash.Hash { @@ -32,29 +34,26 @@ func computeProofRootHash(hash *chainhash.Hash, entries []proofEntry) chainhash. return h } +type jsonProofEntry struct { + Left int `json:"left"` + Hash string `json:"hash"` +} + // The text is generated by SynapseSwap::proofListToText in C++ func textToProofList(text string) []proofEntry { + entries := &[]*jsonProofEntry{} + err := json.Unmarshal([]byte(text), entries) + if err != nil { + panic(err) + } proofList := []proofEntry{} - - for i := 0; i <= len(text)-65; i += 65 { - direction := text[i : i+1] - hex := text[i+1 : i+65] - entry := proofEntry{} - entry.left = (direction == "L") - entry.hash, _ = chainhash.NewHashFromStr(hex) - proofList = append(proofList, entry) + for _, e := range *entries { + h, _ := chainhash.NewHashFromStr(e.Hash) + proofList = append(proofList, proofEntry{ + left: e.Left != 0, + hash: h, + }) + //fmt.Printf("%s\n", e.Hash) } - - /* - for i := 0; i < len(proofList); i++ { - e := proofList[i] - a := 0 - if e.left { - a = 1 - } - fmt.Printf("%02d %d %s\n", i, a, e.hash.String()) - } - */ - return proofList } diff --git a/misc/coinswap_test.go b/misc/coinswap_test.go index cc2cf7b2..3b7df224 100644 --- a/misc/coinswap_test.go +++ b/misc/coinswap_test.go @@ -4,6 +4,7 @@ import ( "os" "testing" + "github.com/btcsuite/btcd/btcec" "github.com/phoreproject/synapse/chainhash" "github.com/sirupsen/logrus" ) @@ -15,14 +16,14 @@ func TestMain(m *testing.M) { } func TestComputeProofRootHash(t *testing.T) { - proofText := "L1b74f997236425e2e42e6abcde553b2cabe64c67aebc297be921dc97624a9323Rdd42a5cf029522116657f38ba7abc88f5b38fa1c274aeedb889c7719955a47c2R90d0d50efde71fce94e8d213ce62ecb75e2555fa448958d2f6434e8b3ee4740fR5501e10d7b90e7ec87eb956d21a9fde86a6b15cf99d16750c40e0a09e5d463a8L3da0910c3d543c6984a1efc5b22988ee14d719022896940aa9e21bffe4b33cc1Rb823d0827e2b3bd28afafb4e7503e428a20be4690d519d13facd20dd922d1a44R9469b28a94af53b032500d0c930d431fb5fa8e0a64eee6f1e366581873434fd5Le5c217fd53ef06c71502e9f4691ada50a59f191d596c54c6007428161b0007dbL0f7d3ca8672bf5a5bd53b62f4a476e55663b5fd6db365352ae38b4e96bae1a6dLce462ab56f8f9df89c9ef268c2df18ab7cf2b25d77aa84ff1ebcde543ed1fa30Rb3accc225d38eac59aa0a8e8c73fef4adb84478fe57b6a6bd388786dda03ec7dRb9c09b68c9207c58945c32b36c58b899bb69dc8e7b403aa447bb049778abba57R8081ebb0d1c2ba7265852ef6b5350fa1034104eb11b7ed3d0e693c82d1f7664cR99e5eff4721d2d89170f60cd0e355dc74926324aa94559969e4d4448692917e0L78209bd8d8674a60dfc3ad33dfa9d59add69c89d2b0c3da1682f21a01f5d806dRee70f9382a46211d2312de52f75e2bfaf5d90b11a8b9c37e866713734c0fb61aR9611cb8847e702f3bdb5139aacdc540290f3e53b368b79f4511719b99db44e88R8965b2071c637ad7788689b930a083d3d4f572314fba408b45550efc12e4bfa9" + proofText := `[{"left":1,"hash":"1b74f997236425e2e42e6abcde553b2cabe64c67aebc297be921dc97624a9323"},{"left":1,"hash":"99e97ac121039b12ccae98b020e65be2e87fa76b152e1981edfd17ad9236ab9b"},{"left":1,"hash":"cdb63da025d64fcef8502360d4ff92ce73585f933097e4b8c9d6d4f42764e1a6"},{"left":0,"hash":"637a3ea530856c587067107829bf57fd487910b1f1688d6ef7b0eb2b6c5435f2"},{"left":0,"hash":"65567e55025fb9bd036a5421ee6ed948341c2db5c51ce8ad2dd84f4a6b254b49"},{"left":1,"hash":"b50c0cdef32a7091867c84c6aa1bdd106789d555e312c9e8fd676932ace570e8"},{"left":1,"hash":"747c93561ca44e0b3fcddeef4843e4c3e7332ae2aa273e2b17e126c37b531d3d"},{"left":1,"hash":"499bd10e09ca60d076fd335be8c3604ec46be3dcdadbcaf0f78b94ceb03f72f9"},{"left":1,"hash":"2b1a34ae4ce9f2e0ea7f7d6f9d1625c5eea5328ff5ecf738821103350aaa7804"},{"left":1,"hash":"774ec950bdf3310e4e00eea1fae3036ff40c387642d41619d27190e4e6026099"},{"left":0,"hash":"b1f90056ad3f8c857af752e9072ba20087d6c9934dd8264a61631ed0eb4fe5d1"},{"left":0,"hash":"51c1edc44b03532892322d49b2ae7c92da003d173731572d45664f408f33375e"},{"left":0,"hash":"64537e844116161caf73b89331131e22cb17874ada168070878537a577e767e7"},{"left":0,"hash":"7fc7d789a5a146c9b01c88f0871ed52740703d55468f0d3c0cb30e43a3207442"},{"left":1,"hash":"dd7ec62e2efd794cf52b357fda34a71e09e70e90882fe55d8af07cbfc02a8ecc"},{"left":0,"hash":"84741e23e7932f4eb9a87c407005748fcbe7f2639b00738510f7b6c3a1a310c5"},{"left":0,"hash":"9c1db4c5f3758f3593fe8ad40623ac07419785e6b25a2169acc779770c5b80b0"},{"left":0,"hash":"25bca9cf47142fe4cc0632dfe099f969b38cc8991d323df6644cb600e90bbf60"}]` proofList := textToProofList(proofText) hashText := "1b7509f59479bc2b048fc8a5d6f5b1b239b3d3d5e818d663817f250bcac7ff47" hash, err := chainhash.NewHashFromStr(hashText) if err != nil { t.Fatal(err) } - rootHashText := "09a550c89002802b1d5ae473f839ced904439c03bcc19d399869d095312b3d52" + rootHashText := "40ed5ea75f6a5ab79ae109198703df1153b1cd272b122609d1bb3dc1b4ca376e" rootHash, err := chainhash.NewHashFromStr(rootHashText) if err != nil { t.Fatal(err) @@ -33,4 +34,6 @@ func TestComputeProofRootHash(t *testing.T) { if proofHash != *rootHash { t.Fatalf("Unmatched hash: root=%s computed=%s", rootHashText, proofHash.String()) } + + btcec.ParsePubKey(nil, btcec.S256()) } From c9cbb8737f962a8a6eb9dfe1d7b225eb7c39445a Mon Sep 17 00:00:00 2001 From: wqking Date: Wed, 24 Apr 2019 14:56:35 +0800 Subject: [PATCH 04/12] Added signature verification. --- go.sum | 2 + misc/coinswap.go | 68 +++++++++++++++++++++++ misc/coinswap_test.go | 124 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+) diff --git a/go.sum b/go.sum index 4f10228c..85e5958a 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/btcsuite/btcd v0.0.0-20190209000034-12ce2fc7d321 h1:saxRQsKei5cMlpK0xaOLOJiRHFt3YbdYnsH45lu9LI8= github.com/btcsuite/btcd v0.0.0-20190209000034-12ce2fc7d321/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= +github.com/btcsuite/btcd v0.0.0-20190418232430-6867ff32788a h1:m/oCT7NlD/JpB64AxWzoS9GuZOCPKkTpt/bdIfeYPwA= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803 h1:j3AgPKKZtZStM2nyhrDSLSYgT7YHrZKdSkq1OYeLjvM= diff --git a/misc/coinswap.go b/misc/coinswap.go index 499b2934..e41a0c30 100644 --- a/misc/coinswap.go +++ b/misc/coinswap.go @@ -3,9 +3,13 @@ package misc // This file is for swapping coins from Phore to Synapse import ( + "encoding/hex" "encoding/json" "github.com/phoreproject/synapse/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + btcdhash "github.com/btcsuite/btcd/chaincfg/chainhash" ) type proofEntry struct { @@ -57,3 +61,67 @@ func textToProofList(text string) []proofEntry { } return proofList } + +type unlockItem struct { + txid chainhash.Hash + out int + scriptPubKey []byte + amount int64 + redeemScript []byte +} + +type jsonUnlockItem struct { + Txid string `json:"txid"` + Out int `json:"out"` + ScriptPubKey string `json:"scriptPubKey"` + Amount int64 `json:"amount"` + RedeemScript string `json:"redeemScript"` +} + +func textToUnlockItem(text string) unlockItem { + jsonItem := &jsonUnlockItem{} + err := json.Unmarshal([]byte(text), jsonItem) + if err != nil { + panic(err) + } + + txid, _ := chainhash.NewHashFromStr(jsonItem.Txid) + scriptPubKey, _ := hex.DecodeString(jsonItem.ScriptPubKey) + redeemScript, _ := hex.DecodeString(jsonItem.RedeemScript) + item := unlockItem{ + txid: *txid, + out: jsonItem.Out, + scriptPubKey: scriptPubKey, + amount: jsonItem.Amount, + redeemScript: redeemScript, + } + return item +} + +func verifyUnlockItem(item *unlockItem) error { + flags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures | txscript.ScriptStrictMultiSig | txscript.ScriptDiscourageUpgradableNops + + redeemTx := wire.NewMsgTx(wire.TxVersion) + h, _ := btcdhash.NewHash(item.txid.CloneBytes()) + prevOut := wire.NewOutPoint(h, uint32(item.out)) + txIn := wire.NewTxIn(prevOut, nil, nil) + redeemTx.AddTxIn(txIn) + + txOut := wire.NewTxOut(item.amount, item.scriptPubKey) + redeemTx.AddTxOut(txOut) + + redeemTx.TxIn[0].SignatureScript = item.redeemScript + + vm, err := txscript.NewEngine(item.scriptPubKey, redeemTx, 0, flags, nil, nil, -1) + if err != nil { + return err + } + + err = vm.Execute() + if err != nil { + return err + } + + return nil +} + diff --git a/misc/coinswap_test.go b/misc/coinswap_test.go index 3b7df224..2a9899cf 100644 --- a/misc/coinswap_test.go +++ b/misc/coinswap_test.go @@ -3,8 +3,15 @@ package misc import ( "os" "testing" + "fmt" + "encoding/hex" + "github.com/btcsuite/btcutil" "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + btcdhash "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/chaincfg" "github.com/phoreproject/synapse/chainhash" "github.com/sirupsen/logrus" ) @@ -37,3 +44,120 @@ func TestComputeProofRootHash(t *testing.T) { btcec.ParsePubKey(nil, btcec.S256()) } + +func TestUnlockItem(t * testing.T) { + unlockItemText := `{"txid":"03e73524b5bba21c4d2114900f6b8649e7ddc4480a8024c1ed09b883b5dbac45","out":0,"scriptPubKey":"210293704de00b9e47eee3848fc146fe659411ba839cda39534aa98a12611056939eac","amount":250000000000,"redeemScript":"483045022100c9ef24aa76cbb3b2341875c51af16aa0e1b17ac326203ab339c41c7578062f3b02203f5425c215872ee4a185f620a58baf986a9e614e7bac27b0df63412158af9d7401"}` + unlockItem := textToUnlockItem(unlockItemText) + + err := verifyUnlockItem(&unlockItem) + if err != nil { + t.Fatal(err) + } +} + +// This is from BTCD txscript sample code +func TestBtcdExample(t * testing.T) { + + // Ordinarily the private key would come from whatever storage mechanism + // is being used, but for this example just hard code it. + privKeyBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2" + + "d4f8720ee63e502ee2869afab7de234b80c") + if err != nil { + t.Fatal(err) + fmt.Println(err) + return + } + privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes) + pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed()) + addr, err := btcutil.NewAddressPubKeyHash(pubKeyHash, + &chaincfg.MainNetParams) + if err != nil { + t.Fatal(err) + fmt.Println(err) + return + } + + // For this example, create a fake transaction that represents what + // would ordinarily be the real transaction that is being spent. It + // contains a single output that pays to address in the amount of 1 BTC. + originTx := wire.NewMsgTx(wire.TxVersion) + prevOut := wire.NewOutPoint(&btcdhash.Hash{}, ^uint32(0)) + txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0}, nil) + originTx.AddTxIn(txIn) + pkScript, err := txscript.PayToAddrScript(addr) + if err != nil { + t.Fatal(err) + fmt.Println(err) + return + } + txOut := wire.NewTxOut(1, pkScript) + originTx.AddTxOut(txOut) + originTxHash := originTx.TxHash() + + // Create the transaction to redeem the fake transaction. + redeemTx := wire.NewMsgTx(wire.TxVersion) + + // Add the input(s) the redeeming transaction will spend. There is no + // signature script at this point since it hasn't been created or signed + // yet, hence nil is provided for it. + prevOut = wire.NewOutPoint(&originTxHash, 0) + txIn = wire.NewTxIn(prevOut, nil, nil) + redeemTx.AddTxIn(txIn) + + // Ordinarily this would contain that actual destination of the funds, + // but for this example don't bother. + txOut = wire.NewTxOut(0, nil) + redeemTx.AddTxOut(txOut) + + // Sign the redeeming transaction. + lookupKey := func(a btcutil.Address) (*btcec.PrivateKey, bool, error) { + // Ordinarily this function would involve looking up the private + // key for the provided address, but since the only thing being + // signed in this example uses the address associated with the + // private key from above, simply return it with the compressed + // flag set since the address is using the associated compressed + // public key. + // + // NOTE: If you want to prove the code is actually signing the + // transaction properly, uncomment the following line which + // intentionally returns an invalid key to sign with, which in + // turn will result in a failure during the script execution + // when verifying the signature. + // + // privKey.D.SetInt64(12345) + // + return privKey, true, nil + } + // Notice that the script database parameter is nil here since it isn't + // used. It must be specified when pay-to-script-hash transactions are + // being signed. + sigScript, err := txscript.SignTxOutput(&chaincfg.MainNetParams, + redeemTx, 0, originTx.TxOut[0].PkScript, txscript.SigHashAll, + txscript.KeyClosure(lookupKey), nil, nil) + if err != nil { + t.Fatal(err) + fmt.Println(err) + return + } + redeemTx.TxIn[0].SignatureScript = sigScript + + // Prove that the transaction has been validly signed by executing the + // script pair. + flags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures | + txscript.ScriptStrictMultiSig | + txscript.ScriptDiscourageUpgradableNops + vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0, + flags, nil, nil, -1) + if err != nil { + t.Fatal(err) + fmt.Println(err) + return + } + if err := vm.Execute(); err != nil { + t.Fatal(err) + fmt.Println(err) + return + } + //fmt.Println("Transaction successfully signed") + +} From 1f4a6ff96ee195d63f7a4871a9348bc8412514c5 Mon Sep 17 00:00:00 2001 From: wqking Date: Mon, 29 Apr 2019 09:29:21 +0800 Subject: [PATCH 05/12] Cleaned code --- .gitignore | 1 + misc/coinswap.go | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 28108b6e..7195c5f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.prof *.test +.vscode # binaries synapsebeacon diff --git a/misc/coinswap.go b/misc/coinswap.go index e41a0c30..a67ea29c 100644 --- a/misc/coinswap.go +++ b/misc/coinswap.go @@ -26,13 +26,9 @@ func computeProofRootHash(hash *chainhash.Hash, entries []proofEntry) chainhash. h := *hash for i := 0; i < len(entries); i++ { if entries[i].left { - //fmt.Printf("Input L: %s R: %s\n", entries[i].hash.String(), h.String()) h = computeCombinedHash(entries[i].hash, &h) - //fmt.Printf("L compute: %s L\n", h.String()) } else { - //fmt.Printf("Input L: %s R: %s\n", h.String(), entries[i].hash.String()) h = computeCombinedHash(&h, entries[i].hash) - //fmt.Printf("R compute: %s\n", h.String()) } } return h @@ -57,7 +53,6 @@ func textToProofList(text string) []proofEntry { left: e.Left != 0, hash: h, }) - //fmt.Printf("%s\n", e.Hash) } return proofList } From 3331434999d654ac85aa601a2fc967cb58afc128 Mon Sep 17 00:00:00 2001 From: wqking Date: Sat, 20 Apr 2019 16:24:22 +0800 Subject: [PATCH 06/12] Added coinswap --- misc/coinswap.go | 44 +++++++++++++++++++++++++++++++++++++++++++ misc/coinswap_test.go | 30 +++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 misc/coinswap.go create mode 100644 misc/coinswap_test.go diff --git a/misc/coinswap.go b/misc/coinswap.go new file mode 100644 index 00000000..7a1e3d5e --- /dev/null +++ b/misc/coinswap.go @@ -0,0 +1,44 @@ +package misc + +// This file is for swapping coins from Phore to Synapse + +import ( + "github.com/phoreproject/synapse/chainhash" +) + +type proofEntry struct { + hash *chainhash.Hash + left bool +} + +func computeCombinedHash(left *chainhash.Hash, right *chainhash.Hash) chainhash.Hash { + return chainhash.HashH(append(left[:], right[:]...)) +} + +func computeProofRootHash(hash *chainhash.Hash, entries []proofEntry) chainhash.Hash { + h := *hash + for i := 0; i < len(entries); i++ { + if entries[i].left { + h = computeCombinedHash(entries[i].hash, &h) + } else { + h = computeCombinedHash(&h, entries[i].hash) + } + } + return h +} + +// The text is generated by SynapseSwap::proofListToText in C++ +func textToProofList(text string) []proofEntry { + proofList := []proofEntry{} + + for i := 0; i <= len(text)-65; i += 65 { + direction := text[i : i+1] + hex := text[i+1 : i+65] + entry := proofEntry{} + entry.left = (direction == "L") + entry.hash, _ = chainhash.NewHashFromStr(hex) + proofList = append(proofList, entry) + } + + return proofList +} diff --git a/misc/coinswap_test.go b/misc/coinswap_test.go new file mode 100644 index 00000000..bddd5c4b --- /dev/null +++ b/misc/coinswap_test.go @@ -0,0 +1,30 @@ +package misc + +import ( + "os" + "testing" + + "github.com/phoreproject/synapse/chainhash" + "github.com/sirupsen/logrus" +) + +func TestMain(m *testing.M) { + logrus.SetLevel(logrus.DebugLevel) + retCode := m.Run() + os.Exit(retCode) +} + +func TestComputeProofRootHash(t *testing.T) { + proofText := "L23934a6297dc21e97b29bcae674ce6ab2c3b55debc6a2ee4e225642397f9741bRc2475a9519779c88dbee4a271cfa385b8fc8aba78bf3576611229502cfa542ddL29730be3debbe966922465d420c01d8ec0e7a6390734015f02eafe2a600544b0L9547f2c631baa02fcfca1da0402c9a24eb33bc8fc884f8b121a339b6deb397dfR82348c73e26f97822f8850299b1bd490575e8760c254bba40df7b202312da36dR5623eee090c1ce433bb3b6bf9f33404a1ac08137b41a798a29aab8a962ce70b9L3c9ad4f01adbe8892b0983d6947467ae7c716024c5dd99c580f2b107fcc44260Ldb9932e212fc9f067a6d97637d7374f80a73cf0e59f9888c5a628ce0c16eda93Rd6794b7d2cd035c6fd43695210ee267d8fe5ccd3a810a45949a3beaef2b51f83La28320923cf8ffe7cc083704bcaf116390def62c23a438e7db6b53b7a5879274R1208e7f8d21725b4753c56fef059b2d5284cce6cef82823ed50068549119554cR5c919054901401c6245bef9803b9ad4c9fd4e35f298ca8e59952b2d7b1c48249R9a689187a020df1d026f6bb6b460f204258cad7dbeade42e9029b7f2fc9ae991R3101d97f5c93e99217e25bd7ce9338ee4d54982f18419ecbca1538d2b7eb1d46Lcb6e5397b511d2b34825ed54a0a12ee5546fa2dc2ce7a89b900577b1e1b2ebf8R31aa2dc8c598d6e5302015346d59e7bf9103c1d8142df53bd7db278ba9dce1cbR299011e91a0d9821036d6fc6a6f579b4e2203aa10b744fe2a841310921e9c6d0Rd17b016c8c75f2043b92358e523ab80427567cfdbd72517744412c4172939bdf" + proofList := textToProofList(proofText) + hashText := "00013a9407f671e11e2b81369d788dd8c5144f58ddcb9ee9c185274bc4f4a778" + hash, _ := chainhash.NewHashFromStr(hashText) + rootHashText := "25379816f6a026c5de9b4347fadcbea5942c77459c2bdc47ab10139ecade8f71" + rootHash, _ := chainhash.NewHashFromStr(rootHashText) + + proofHash := computeProofRootHash(hash, proofList) + + if proofHash != *rootHash { + t.Fatalf("Unmatched hash: root=%s computed=%s", rootHashText, proofHash.String()) + } +} From 6252cae63c411fd5247f0dd46700ce2349d6220e Mon Sep 17 00:00:00 2001 From: wqking Date: Sun, 21 Apr 2019 09:26:17 +0800 Subject: [PATCH 07/12] Merkle tree proof verification works now. --- misc/coinswap.go | 18 +++++++++++++++++- misc/coinswap_test.go | 16 +++++++++++----- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/misc/coinswap.go b/misc/coinswap.go index 7a1e3d5e..aeda4eff 100644 --- a/misc/coinswap.go +++ b/misc/coinswap.go @@ -12,16 +12,21 @@ type proofEntry struct { } func computeCombinedHash(left *chainhash.Hash, right *chainhash.Hash) chainhash.Hash { - return chainhash.HashH(append(left[:], right[:]...)) + h := chainhash.HashH(chainhash.HashB(append(left[:], right[:]...))) + return h } func computeProofRootHash(hash *chainhash.Hash, entries []proofEntry) chainhash.Hash { h := *hash for i := 0; i < len(entries); i++ { if entries[i].left { + //fmt.Printf("Input L: %s R: %s\n", entries[i].hash.String(), h.String()) h = computeCombinedHash(entries[i].hash, &h) + //fmt.Printf("L compute: %s L\n", h.String()) } else { + //fmt.Printf("Input L: %s R: %s\n", h.String(), entries[i].hash.String()) h = computeCombinedHash(&h, entries[i].hash) + //fmt.Printf("R compute: %s\n", h.String()) } } return h @@ -40,5 +45,16 @@ func textToProofList(text string) []proofEntry { proofList = append(proofList, entry) } + /* + for i := 0; i < len(proofList); i++ { + e := proofList[i] + a := 0 + if e.left { + a = 1 + } + fmt.Printf("%02d %d %s\n", i, a, e.hash.String()) + } + */ + return proofList } diff --git a/misc/coinswap_test.go b/misc/coinswap_test.go index bddd5c4b..cc2cf7b2 100644 --- a/misc/coinswap_test.go +++ b/misc/coinswap_test.go @@ -15,12 +15,18 @@ func TestMain(m *testing.M) { } func TestComputeProofRootHash(t *testing.T) { - proofText := "L23934a6297dc21e97b29bcae674ce6ab2c3b55debc6a2ee4e225642397f9741bRc2475a9519779c88dbee4a271cfa385b8fc8aba78bf3576611229502cfa542ddL29730be3debbe966922465d420c01d8ec0e7a6390734015f02eafe2a600544b0L9547f2c631baa02fcfca1da0402c9a24eb33bc8fc884f8b121a339b6deb397dfR82348c73e26f97822f8850299b1bd490575e8760c254bba40df7b202312da36dR5623eee090c1ce433bb3b6bf9f33404a1ac08137b41a798a29aab8a962ce70b9L3c9ad4f01adbe8892b0983d6947467ae7c716024c5dd99c580f2b107fcc44260Ldb9932e212fc9f067a6d97637d7374f80a73cf0e59f9888c5a628ce0c16eda93Rd6794b7d2cd035c6fd43695210ee267d8fe5ccd3a810a45949a3beaef2b51f83La28320923cf8ffe7cc083704bcaf116390def62c23a438e7db6b53b7a5879274R1208e7f8d21725b4753c56fef059b2d5284cce6cef82823ed50068549119554cR5c919054901401c6245bef9803b9ad4c9fd4e35f298ca8e59952b2d7b1c48249R9a689187a020df1d026f6bb6b460f204258cad7dbeade42e9029b7f2fc9ae991R3101d97f5c93e99217e25bd7ce9338ee4d54982f18419ecbca1538d2b7eb1d46Lcb6e5397b511d2b34825ed54a0a12ee5546fa2dc2ce7a89b900577b1e1b2ebf8R31aa2dc8c598d6e5302015346d59e7bf9103c1d8142df53bd7db278ba9dce1cbR299011e91a0d9821036d6fc6a6f579b4e2203aa10b744fe2a841310921e9c6d0Rd17b016c8c75f2043b92358e523ab80427567cfdbd72517744412c4172939bdf" + proofText := "L1b74f997236425e2e42e6abcde553b2cabe64c67aebc297be921dc97624a9323Rdd42a5cf029522116657f38ba7abc88f5b38fa1c274aeedb889c7719955a47c2R90d0d50efde71fce94e8d213ce62ecb75e2555fa448958d2f6434e8b3ee4740fR5501e10d7b90e7ec87eb956d21a9fde86a6b15cf99d16750c40e0a09e5d463a8L3da0910c3d543c6984a1efc5b22988ee14d719022896940aa9e21bffe4b33cc1Rb823d0827e2b3bd28afafb4e7503e428a20be4690d519d13facd20dd922d1a44R9469b28a94af53b032500d0c930d431fb5fa8e0a64eee6f1e366581873434fd5Le5c217fd53ef06c71502e9f4691ada50a59f191d596c54c6007428161b0007dbL0f7d3ca8672bf5a5bd53b62f4a476e55663b5fd6db365352ae38b4e96bae1a6dLce462ab56f8f9df89c9ef268c2df18ab7cf2b25d77aa84ff1ebcde543ed1fa30Rb3accc225d38eac59aa0a8e8c73fef4adb84478fe57b6a6bd388786dda03ec7dRb9c09b68c9207c58945c32b36c58b899bb69dc8e7b403aa447bb049778abba57R8081ebb0d1c2ba7265852ef6b5350fa1034104eb11b7ed3d0e693c82d1f7664cR99e5eff4721d2d89170f60cd0e355dc74926324aa94559969e4d4448692917e0L78209bd8d8674a60dfc3ad33dfa9d59add69c89d2b0c3da1682f21a01f5d806dRee70f9382a46211d2312de52f75e2bfaf5d90b11a8b9c37e866713734c0fb61aR9611cb8847e702f3bdb5139aacdc540290f3e53b368b79f4511719b99db44e88R8965b2071c637ad7788689b930a083d3d4f572314fba408b45550efc12e4bfa9" proofList := textToProofList(proofText) - hashText := "00013a9407f671e11e2b81369d788dd8c5144f58ddcb9ee9c185274bc4f4a778" - hash, _ := chainhash.NewHashFromStr(hashText) - rootHashText := "25379816f6a026c5de9b4347fadcbea5942c77459c2bdc47ab10139ecade8f71" - rootHash, _ := chainhash.NewHashFromStr(rootHashText) + hashText := "1b7509f59479bc2b048fc8a5d6f5b1b239b3d3d5e818d663817f250bcac7ff47" + hash, err := chainhash.NewHashFromStr(hashText) + if err != nil { + t.Fatal(err) + } + rootHashText := "09a550c89002802b1d5ae473f839ced904439c03bcc19d399869d095312b3d52" + rootHash, err := chainhash.NewHashFromStr(rootHashText) + if err != nil { + t.Fatal(err) + } proofHash := computeProofRootHash(hash, proofList) From 7594b55e5555d793d5d6d1bb729835fc2ac23090 Mon Sep 17 00:00:00 2001 From: wqking Date: Mon, 22 Apr 2019 10:53:29 +0800 Subject: [PATCH 08/12] Use JSON for coin swap data format --- misc/coinswap.go | 41 ++++++++++++++++++++--------------------- misc/coinswap_test.go | 7 +++++-- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/misc/coinswap.go b/misc/coinswap.go index aeda4eff..499b2934 100644 --- a/misc/coinswap.go +++ b/misc/coinswap.go @@ -3,12 +3,14 @@ package misc // This file is for swapping coins from Phore to Synapse import ( + "encoding/json" + "github.com/phoreproject/synapse/chainhash" ) type proofEntry struct { - hash *chainhash.Hash left bool + hash *chainhash.Hash } func computeCombinedHash(left *chainhash.Hash, right *chainhash.Hash) chainhash.Hash { @@ -32,29 +34,26 @@ func computeProofRootHash(hash *chainhash.Hash, entries []proofEntry) chainhash. return h } +type jsonProofEntry struct { + Left int `json:"left"` + Hash string `json:"hash"` +} + // The text is generated by SynapseSwap::proofListToText in C++ func textToProofList(text string) []proofEntry { + entries := &[]*jsonProofEntry{} + err := json.Unmarshal([]byte(text), entries) + if err != nil { + panic(err) + } proofList := []proofEntry{} - - for i := 0; i <= len(text)-65; i += 65 { - direction := text[i : i+1] - hex := text[i+1 : i+65] - entry := proofEntry{} - entry.left = (direction == "L") - entry.hash, _ = chainhash.NewHashFromStr(hex) - proofList = append(proofList, entry) + for _, e := range *entries { + h, _ := chainhash.NewHashFromStr(e.Hash) + proofList = append(proofList, proofEntry{ + left: e.Left != 0, + hash: h, + }) + //fmt.Printf("%s\n", e.Hash) } - - /* - for i := 0; i < len(proofList); i++ { - e := proofList[i] - a := 0 - if e.left { - a = 1 - } - fmt.Printf("%02d %d %s\n", i, a, e.hash.String()) - } - */ - return proofList } diff --git a/misc/coinswap_test.go b/misc/coinswap_test.go index cc2cf7b2..3b7df224 100644 --- a/misc/coinswap_test.go +++ b/misc/coinswap_test.go @@ -4,6 +4,7 @@ import ( "os" "testing" + "github.com/btcsuite/btcd/btcec" "github.com/phoreproject/synapse/chainhash" "github.com/sirupsen/logrus" ) @@ -15,14 +16,14 @@ func TestMain(m *testing.M) { } func TestComputeProofRootHash(t *testing.T) { - proofText := "L1b74f997236425e2e42e6abcde553b2cabe64c67aebc297be921dc97624a9323Rdd42a5cf029522116657f38ba7abc88f5b38fa1c274aeedb889c7719955a47c2R90d0d50efde71fce94e8d213ce62ecb75e2555fa448958d2f6434e8b3ee4740fR5501e10d7b90e7ec87eb956d21a9fde86a6b15cf99d16750c40e0a09e5d463a8L3da0910c3d543c6984a1efc5b22988ee14d719022896940aa9e21bffe4b33cc1Rb823d0827e2b3bd28afafb4e7503e428a20be4690d519d13facd20dd922d1a44R9469b28a94af53b032500d0c930d431fb5fa8e0a64eee6f1e366581873434fd5Le5c217fd53ef06c71502e9f4691ada50a59f191d596c54c6007428161b0007dbL0f7d3ca8672bf5a5bd53b62f4a476e55663b5fd6db365352ae38b4e96bae1a6dLce462ab56f8f9df89c9ef268c2df18ab7cf2b25d77aa84ff1ebcde543ed1fa30Rb3accc225d38eac59aa0a8e8c73fef4adb84478fe57b6a6bd388786dda03ec7dRb9c09b68c9207c58945c32b36c58b899bb69dc8e7b403aa447bb049778abba57R8081ebb0d1c2ba7265852ef6b5350fa1034104eb11b7ed3d0e693c82d1f7664cR99e5eff4721d2d89170f60cd0e355dc74926324aa94559969e4d4448692917e0L78209bd8d8674a60dfc3ad33dfa9d59add69c89d2b0c3da1682f21a01f5d806dRee70f9382a46211d2312de52f75e2bfaf5d90b11a8b9c37e866713734c0fb61aR9611cb8847e702f3bdb5139aacdc540290f3e53b368b79f4511719b99db44e88R8965b2071c637ad7788689b930a083d3d4f572314fba408b45550efc12e4bfa9" + proofText := `[{"left":1,"hash":"1b74f997236425e2e42e6abcde553b2cabe64c67aebc297be921dc97624a9323"},{"left":1,"hash":"99e97ac121039b12ccae98b020e65be2e87fa76b152e1981edfd17ad9236ab9b"},{"left":1,"hash":"cdb63da025d64fcef8502360d4ff92ce73585f933097e4b8c9d6d4f42764e1a6"},{"left":0,"hash":"637a3ea530856c587067107829bf57fd487910b1f1688d6ef7b0eb2b6c5435f2"},{"left":0,"hash":"65567e55025fb9bd036a5421ee6ed948341c2db5c51ce8ad2dd84f4a6b254b49"},{"left":1,"hash":"b50c0cdef32a7091867c84c6aa1bdd106789d555e312c9e8fd676932ace570e8"},{"left":1,"hash":"747c93561ca44e0b3fcddeef4843e4c3e7332ae2aa273e2b17e126c37b531d3d"},{"left":1,"hash":"499bd10e09ca60d076fd335be8c3604ec46be3dcdadbcaf0f78b94ceb03f72f9"},{"left":1,"hash":"2b1a34ae4ce9f2e0ea7f7d6f9d1625c5eea5328ff5ecf738821103350aaa7804"},{"left":1,"hash":"774ec950bdf3310e4e00eea1fae3036ff40c387642d41619d27190e4e6026099"},{"left":0,"hash":"b1f90056ad3f8c857af752e9072ba20087d6c9934dd8264a61631ed0eb4fe5d1"},{"left":0,"hash":"51c1edc44b03532892322d49b2ae7c92da003d173731572d45664f408f33375e"},{"left":0,"hash":"64537e844116161caf73b89331131e22cb17874ada168070878537a577e767e7"},{"left":0,"hash":"7fc7d789a5a146c9b01c88f0871ed52740703d55468f0d3c0cb30e43a3207442"},{"left":1,"hash":"dd7ec62e2efd794cf52b357fda34a71e09e70e90882fe55d8af07cbfc02a8ecc"},{"left":0,"hash":"84741e23e7932f4eb9a87c407005748fcbe7f2639b00738510f7b6c3a1a310c5"},{"left":0,"hash":"9c1db4c5f3758f3593fe8ad40623ac07419785e6b25a2169acc779770c5b80b0"},{"left":0,"hash":"25bca9cf47142fe4cc0632dfe099f969b38cc8991d323df6644cb600e90bbf60"}]` proofList := textToProofList(proofText) hashText := "1b7509f59479bc2b048fc8a5d6f5b1b239b3d3d5e818d663817f250bcac7ff47" hash, err := chainhash.NewHashFromStr(hashText) if err != nil { t.Fatal(err) } - rootHashText := "09a550c89002802b1d5ae473f839ced904439c03bcc19d399869d095312b3d52" + rootHashText := "40ed5ea75f6a5ab79ae109198703df1153b1cd272b122609d1bb3dc1b4ca376e" rootHash, err := chainhash.NewHashFromStr(rootHashText) if err != nil { t.Fatal(err) @@ -33,4 +34,6 @@ func TestComputeProofRootHash(t *testing.T) { if proofHash != *rootHash { t.Fatalf("Unmatched hash: root=%s computed=%s", rootHashText, proofHash.String()) } + + btcec.ParsePubKey(nil, btcec.S256()) } From 82178f3e195cdcf8fdf5d61ab63b89561f7039c3 Mon Sep 17 00:00:00 2001 From: wqking Date: Wed, 24 Apr 2019 14:56:35 +0800 Subject: [PATCH 09/12] Added signature verification. --- misc/coinswap.go | 68 +++++++++++++++++++++++ misc/coinswap_test.go | 124 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+) diff --git a/misc/coinswap.go b/misc/coinswap.go index 499b2934..e41a0c30 100644 --- a/misc/coinswap.go +++ b/misc/coinswap.go @@ -3,9 +3,13 @@ package misc // This file is for swapping coins from Phore to Synapse import ( + "encoding/hex" "encoding/json" "github.com/phoreproject/synapse/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + btcdhash "github.com/btcsuite/btcd/chaincfg/chainhash" ) type proofEntry struct { @@ -57,3 +61,67 @@ func textToProofList(text string) []proofEntry { } return proofList } + +type unlockItem struct { + txid chainhash.Hash + out int + scriptPubKey []byte + amount int64 + redeemScript []byte +} + +type jsonUnlockItem struct { + Txid string `json:"txid"` + Out int `json:"out"` + ScriptPubKey string `json:"scriptPubKey"` + Amount int64 `json:"amount"` + RedeemScript string `json:"redeemScript"` +} + +func textToUnlockItem(text string) unlockItem { + jsonItem := &jsonUnlockItem{} + err := json.Unmarshal([]byte(text), jsonItem) + if err != nil { + panic(err) + } + + txid, _ := chainhash.NewHashFromStr(jsonItem.Txid) + scriptPubKey, _ := hex.DecodeString(jsonItem.ScriptPubKey) + redeemScript, _ := hex.DecodeString(jsonItem.RedeemScript) + item := unlockItem{ + txid: *txid, + out: jsonItem.Out, + scriptPubKey: scriptPubKey, + amount: jsonItem.Amount, + redeemScript: redeemScript, + } + return item +} + +func verifyUnlockItem(item *unlockItem) error { + flags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures | txscript.ScriptStrictMultiSig | txscript.ScriptDiscourageUpgradableNops + + redeemTx := wire.NewMsgTx(wire.TxVersion) + h, _ := btcdhash.NewHash(item.txid.CloneBytes()) + prevOut := wire.NewOutPoint(h, uint32(item.out)) + txIn := wire.NewTxIn(prevOut, nil, nil) + redeemTx.AddTxIn(txIn) + + txOut := wire.NewTxOut(item.amount, item.scriptPubKey) + redeemTx.AddTxOut(txOut) + + redeemTx.TxIn[0].SignatureScript = item.redeemScript + + vm, err := txscript.NewEngine(item.scriptPubKey, redeemTx, 0, flags, nil, nil, -1) + if err != nil { + return err + } + + err = vm.Execute() + if err != nil { + return err + } + + return nil +} + diff --git a/misc/coinswap_test.go b/misc/coinswap_test.go index 3b7df224..2a9899cf 100644 --- a/misc/coinswap_test.go +++ b/misc/coinswap_test.go @@ -3,8 +3,15 @@ package misc import ( "os" "testing" + "fmt" + "encoding/hex" + "github.com/btcsuite/btcutil" "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + btcdhash "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/chaincfg" "github.com/phoreproject/synapse/chainhash" "github.com/sirupsen/logrus" ) @@ -37,3 +44,120 @@ func TestComputeProofRootHash(t *testing.T) { btcec.ParsePubKey(nil, btcec.S256()) } + +func TestUnlockItem(t * testing.T) { + unlockItemText := `{"txid":"03e73524b5bba21c4d2114900f6b8649e7ddc4480a8024c1ed09b883b5dbac45","out":0,"scriptPubKey":"210293704de00b9e47eee3848fc146fe659411ba839cda39534aa98a12611056939eac","amount":250000000000,"redeemScript":"483045022100c9ef24aa76cbb3b2341875c51af16aa0e1b17ac326203ab339c41c7578062f3b02203f5425c215872ee4a185f620a58baf986a9e614e7bac27b0df63412158af9d7401"}` + unlockItem := textToUnlockItem(unlockItemText) + + err := verifyUnlockItem(&unlockItem) + if err != nil { + t.Fatal(err) + } +} + +// This is from BTCD txscript sample code +func TestBtcdExample(t * testing.T) { + + // Ordinarily the private key would come from whatever storage mechanism + // is being used, but for this example just hard code it. + privKeyBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2" + + "d4f8720ee63e502ee2869afab7de234b80c") + if err != nil { + t.Fatal(err) + fmt.Println(err) + return + } + privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes) + pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed()) + addr, err := btcutil.NewAddressPubKeyHash(pubKeyHash, + &chaincfg.MainNetParams) + if err != nil { + t.Fatal(err) + fmt.Println(err) + return + } + + // For this example, create a fake transaction that represents what + // would ordinarily be the real transaction that is being spent. It + // contains a single output that pays to address in the amount of 1 BTC. + originTx := wire.NewMsgTx(wire.TxVersion) + prevOut := wire.NewOutPoint(&btcdhash.Hash{}, ^uint32(0)) + txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0}, nil) + originTx.AddTxIn(txIn) + pkScript, err := txscript.PayToAddrScript(addr) + if err != nil { + t.Fatal(err) + fmt.Println(err) + return + } + txOut := wire.NewTxOut(1, pkScript) + originTx.AddTxOut(txOut) + originTxHash := originTx.TxHash() + + // Create the transaction to redeem the fake transaction. + redeemTx := wire.NewMsgTx(wire.TxVersion) + + // Add the input(s) the redeeming transaction will spend. There is no + // signature script at this point since it hasn't been created or signed + // yet, hence nil is provided for it. + prevOut = wire.NewOutPoint(&originTxHash, 0) + txIn = wire.NewTxIn(prevOut, nil, nil) + redeemTx.AddTxIn(txIn) + + // Ordinarily this would contain that actual destination of the funds, + // but for this example don't bother. + txOut = wire.NewTxOut(0, nil) + redeemTx.AddTxOut(txOut) + + // Sign the redeeming transaction. + lookupKey := func(a btcutil.Address) (*btcec.PrivateKey, bool, error) { + // Ordinarily this function would involve looking up the private + // key for the provided address, but since the only thing being + // signed in this example uses the address associated with the + // private key from above, simply return it with the compressed + // flag set since the address is using the associated compressed + // public key. + // + // NOTE: If you want to prove the code is actually signing the + // transaction properly, uncomment the following line which + // intentionally returns an invalid key to sign with, which in + // turn will result in a failure during the script execution + // when verifying the signature. + // + // privKey.D.SetInt64(12345) + // + return privKey, true, nil + } + // Notice that the script database parameter is nil here since it isn't + // used. It must be specified when pay-to-script-hash transactions are + // being signed. + sigScript, err := txscript.SignTxOutput(&chaincfg.MainNetParams, + redeemTx, 0, originTx.TxOut[0].PkScript, txscript.SigHashAll, + txscript.KeyClosure(lookupKey), nil, nil) + if err != nil { + t.Fatal(err) + fmt.Println(err) + return + } + redeemTx.TxIn[0].SignatureScript = sigScript + + // Prove that the transaction has been validly signed by executing the + // script pair. + flags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures | + txscript.ScriptStrictMultiSig | + txscript.ScriptDiscourageUpgradableNops + vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0, + flags, nil, nil, -1) + if err != nil { + t.Fatal(err) + fmt.Println(err) + return + } + if err := vm.Execute(); err != nil { + t.Fatal(err) + fmt.Println(err) + return + } + //fmt.Println("Transaction successfully signed") + +} From bb9f2d5e60d872f8db7539192c50509cad092a29 Mon Sep 17 00:00:00 2001 From: wqking Date: Mon, 29 Apr 2019 09:29:21 +0800 Subject: [PATCH 10/12] Cleaned code --- .gitignore | 1 + misc/coinswap.go | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index fc5c3f9d..d50a15e4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.prof *.test dist +.vscode # binaries synapsebeacon diff --git a/misc/coinswap.go b/misc/coinswap.go index e41a0c30..a67ea29c 100644 --- a/misc/coinswap.go +++ b/misc/coinswap.go @@ -26,13 +26,9 @@ func computeProofRootHash(hash *chainhash.Hash, entries []proofEntry) chainhash. h := *hash for i := 0; i < len(entries); i++ { if entries[i].left { - //fmt.Printf("Input L: %s R: %s\n", entries[i].hash.String(), h.String()) h = computeCombinedHash(entries[i].hash, &h) - //fmt.Printf("L compute: %s L\n", h.String()) } else { - //fmt.Printf("Input L: %s R: %s\n", h.String(), entries[i].hash.String()) h = computeCombinedHash(&h, entries[i].hash) - //fmt.Printf("R compute: %s\n", h.String()) } } return h @@ -57,7 +53,6 @@ func textToProofList(text string) []proofEntry { left: e.Left != 0, hash: h, }) - //fmt.Printf("%s\n", e.Hash) } return proofList } From 898b2b5610e651e002a4f29f507da1a60cb7ce01 Mon Sep 17 00:00:00 2001 From: wqking Date: Wed, 26 Jun 2019 08:58:35 +0800 Subject: [PATCH 11/12] Mod --- go.mod | 2 ++ go.sum | 1 + 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index faf2acb6..a3a23b9b 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.12 require ( github.com/beevik/ntp v0.2.0 + github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32 + github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803 github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f github.com/ethereum/go-ethereum v1.8.27 // indirect github.com/go-test/deep v1.0.1 diff --git a/go.sum b/go.sum index 5ffd452d..e18651f9 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,7 @@ github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32 h1:qkOC5Gd33k54tobS3 github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803 h1:j3AgPKKZtZStM2nyhrDSLSYgT7YHrZKdSkq1OYeLjvM= github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= From 9352663cea3e0ee3b70e84b965a0a00dc79afb47 Mon Sep 17 00:00:00 2001 From: wqking Date: Fri, 28 Jun 2019 10:54:59 +0800 Subject: [PATCH 12/12] Renamed folder misc to swap --- {misc => swap}/coinswap.go | 2 +- {misc => swap}/coinswap_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename {misc => swap}/coinswap.go (99%) rename {misc => swap}/coinswap_test.go (99%) diff --git a/misc/coinswap.go b/swap/coinswap.go similarity index 99% rename from misc/coinswap.go rename to swap/coinswap.go index a67ea29c..74739d2d 100644 --- a/misc/coinswap.go +++ b/swap/coinswap.go @@ -1,4 +1,4 @@ -package misc +package swap // This file is for swapping coins from Phore to Synapse diff --git a/misc/coinswap_test.go b/swap/coinswap_test.go similarity index 99% rename from misc/coinswap_test.go rename to swap/coinswap_test.go index 2a9899cf..7cb96525 100644 --- a/misc/coinswap_test.go +++ b/swap/coinswap_test.go @@ -1,4 +1,4 @@ -package misc +package swap import ( "os"