You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@milagro.apache.org by cm...@apache.org on 2019/09/03 08:58:15 UTC
[incubator-milagro-dta] 01/01: Qredo Plugin
This is an automated email from the ASF dual-hosted git repository.
cmorris pushed a commit to branch qredo-plugin
in repository https://gitbox.apache.org/repos/asf/incubator-milagro-dta.git
commit c8dab8c507da4d5de7e2e51046c0ff9f7bd32d1d
Author: Christopher Morris <ch...@morris.net>
AuthorDate: Wed Aug 28 14:43:28 2019 +0100
Qredo Plugin
---
pkg/qredoplugin/coin.go | 176 +++++++++++++++++++++++++++++++
pkg/qredoplugin/coin_test.go | 52 ++++++++++
pkg/qredoplugin/helpers.go | 225 ++++++++++++++++++++++++++++++++++++++++
pkg/qredoplugin/helpers_test.go | 47 +++++++++
pkg/qredoplugin/service.go | 103 ++++++++++++++++++
plugins/qredoplugin.go | 9 ++
6 files changed, 612 insertions(+)
diff --git a/pkg/qredoplugin/coin.go b/pkg/qredoplugin/coin.go
new file mode 100644
index 0000000..c2a6cfa
--- /dev/null
+++ b/pkg/qredoplugin/coin.go
@@ -0,0 +1,176 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package qredoplugin
+
+/*
+The coin constans are defined here
+https://github.com/satoshilabs/slips/blob/master/slip-0044.md
+https://www.thepolyglotdeveloper.com/2018/02/generate-cryptocurrency-private-keys-public-addresses-golang/
+https://godoc.org/bitbucket.org/dchapes/ripple/crypto/rkey#example-package--Address
+
+*/
+
+import (
+ "encoding/hex"
+ "fmt"
+ "strings"
+
+ "github.com/btcsuite/btcd/btcec"
+ "github.com/btcsuite/btcd/chaincfg"
+ "github.com/btcsuite/btcutil"
+ ethcrypto "github.com/ethereum/go-ethereum/crypto"
+ "github.com/pkg/errors"
+)
+
+var (
+ errUnsupportedCoin = errors.New("unsupported coin")
+)
+
+//Network list of Cryptocurrency networks
+type Network struct {
+ name string
+ symbol string
+ xpubkey byte
+ xprivatekey byte
+}
+
+var network = map[string]Network{
+ "rdd": {name: "reddcoin", symbol: "rdd", xpubkey: 0x3d, xprivatekey: 0xbd},
+ "dgb": {name: "digibyte", symbol: "dgb", xpubkey: 0x1e, xprivatekey: 0x80},
+ "doge": {name: "dogecoin", symbol: "doge", xpubkey: 0x1e, xprivatekey: 0x9e},
+ "via": {name: "viacoin", symbol: "via", xpubkey: 0x47, xprivatekey: 0xc7},
+ "btc": {name: "bitcoin", symbol: "btc", xpubkey: 0x00, xprivatekey: 0x80},
+ "testbtc": {name: "bitcoin testnet", symbol: "btc", xpubkey: 0x6f, xprivatekey: 0xef},
+ "ltc": {name: "litecoin", symbol: "ltc", xpubkey: 0x30, xprivatekey: 0xb0},
+}
+
+func addressForPublicKey(publicKey string, coinType int) (string, error) {
+ pubK, _ := hex.DecodeString(publicKey)
+ pubKeyBytes := []byte(pubK)
+
+ switch coinType {
+ case 0: //Bitcoin
+ return network["btc"].pubkeyToAddress(pubKeyBytes, false)
+ case 1: //Bitcoin Testnet
+ return network["testbtc"].pubkeyToAddress(pubKeyBytes, false)
+ case 2: //litecoin
+ return network["ltc"].pubkeyToAddress(pubKeyBytes, false)
+ case 3: //Dogecoin
+ return network["doge"].pubkeyToAddress(pubKeyBytes, false)
+ case 60: //Ethereum
+ compByte, err := hex.DecodeString(publicKey)
+ pubKey, err := btcec.ParsePubKey([]byte(compByte), btcec.S256())
+ if err != nil {
+ return "", errors.Wrap(err, "Failed to generate Ethereum Address")
+ }
+ pubECDSA := pubKey.ToECDSA()
+ address1 := ethcrypto.PubkeyToAddress(*pubECDSA)
+ retAddress := fmt.Sprintf("%x", address1)
+ return retAddress, nil
+ // case 128: //Monero
+ // case 144: //Ripple
+ // case 145: //Bitcoin Cash
+ // case 148: //Stellar
+ // case 1815: //Cardano
+ }
+ return "", errUnsupportedCoin
+}
+
+func addressForPrivateKey(privateKey []byte, coinType int) (string, error) {
+ switch coinType {
+ case 0: //Bitcoin
+ return network["btc"].privkeyToAddress(privateKey, false)
+ case 1: //Bitcoin Testnet
+ return network["testbtc"].privkeyToAddress(privateKey, false)
+ case 2: //litecoin
+ return network["ltc"].privkeyToAddress(privateKey, false)
+ case 3: //Dogecoin
+ return network["doge"].privkeyToAddress(privateKey, false)
+ case 60: //Ethereum
+ _, cpub1 := btcec.PrivKeyFromBytes(btcec.S256(), privateKey)
+ pubKey := cpub1.SerializeUncompressed()
+ address := ethcrypto.Keccak256(pubKey[1:])[12:]
+ addRet := strings.ToLower(fmt.Sprintf("%x", address))
+ return addRet, nil
+ // case 128: //Monero
+ // case 144: //Ripple
+ // case 145: //Bitcoin Cash
+ // case 148: //Stellar
+ // case 1815: //Cardano
+ }
+ return "", errUnsupportedCoin
+}
+
+func (network Network) privkeyToAddress(privateKey []byte, compressed bool) (string, error) {
+ wif, _ := network.createPrivateKeyFromBytes(privateKey)
+ address, _ := network.getAddress(wif)
+ if compressed == true {
+ address.SetFormat(btcutil.PKFCompressed)
+ } else {
+ address.SetFormat(btcutil.PKFUncompressed)
+ }
+ return address.EncodeAddress(), nil
+}
+
+func (network Network) pubkeyToAddress(publicKey []byte, compressed bool) (string, error) {
+ mainNetAddr, err := btcutil.NewAddressPubKey(publicKey, network.getNetworkParams())
+ if err != nil {
+ return "", errors.Wrap(err, "Failed to decode Public Key")
+ }
+ if compressed == true {
+ mainNetAddr.SetFormat(btcutil.PKFCompressed)
+ } else {
+ mainNetAddr.SetFormat(btcutil.PKFUncompressed)
+ }
+ return mainNetAddr.EncodeAddress(), nil
+}
+
+func (network Network) getNetworkParams() *chaincfg.Params {
+ networkParams := &chaincfg.MainNetParams
+ networkParams.PubKeyHashAddrID = network.xpubkey
+ networkParams.PrivateKeyID = network.xprivatekey
+ return networkParams
+}
+
+func (network Network) createPrivateKeyFromBytes(privateKey []byte) (*btcutil.WIF, error) {
+ secret, _ := btcec.PrivKeyFromBytes(btcec.S256(), privateKey)
+ return btcutil.NewWIF(secret, network.getNetworkParams(), true)
+}
+
+func (network Network) createPrivateKey() (*btcutil.WIF, error) {
+ secret, err := btcec.NewPrivateKey(btcec.S256())
+ if err != nil {
+ return nil, err
+ }
+ return btcutil.NewWIF(secret, network.getNetworkParams(), true)
+}
+
+func (network Network) importWIF(wifStr string) (*btcutil.WIF, error) {
+ wif, err := btcutil.DecodeWIF(wifStr)
+ if err != nil {
+ return nil, err
+ }
+ if !wif.IsForNet(network.getNetworkParams()) {
+ return nil, errors.New("The WIF string is not valid for the `" + network.name + "` network")
+ }
+ return wif, nil
+}
+
+func (network Network) getAddress(wif *btcutil.WIF) (*btcutil.AddressPubKey, error) {
+ return btcutil.NewAddressPubKey(wif.PrivKey.PubKey().SerializeUncompressed(), network.getNetworkParams())
+}
diff --git a/pkg/qredoplugin/coin_test.go b/pkg/qredoplugin/coin_test.go
new file mode 100644
index 0000000..9d9cff6
--- /dev/null
+++ b/pkg/qredoplugin/coin_test.go
@@ -0,0 +1,52 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package qredoplugin
+
+import (
+ "encoding/hex"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_AddressForPublicKey(t *testing.T) {
+ pubKey := "0487DBF8D88A860270AB7D689EB44C2DFFF768D2F7851A753FACF356978B82CE4ACB5C9B061FC884668D9BB46B83D6BF180A2099F397142785D2E03DACCEF03D01"
+ btcAddress, err := addressForPublicKey(pubKey, 0)
+ assert.Nil(t, err, "Error should be nil")
+ assert.Equal(t, "1MwiNcg3v19BLeawNJKL8L18m4Tzmtua5T", btcAddress)
+
+ btcTestNetAddress, err := addressForPublicKey(pubKey, 1)
+ assert.Nil(t, err, "Error should be nil")
+ assert.Equal(t, "n2Tfffm2j2aS7m4Z5sHhxFDTd44hfnDhPz", btcTestNetAddress)
+
+ _, err = addressForPublicKey(pubKey, 9999999999)
+ assert.EqualError(t, err, "unsupported coin")
+}
+func Test_AddressForPrivateKey(t *testing.T) {
+ privKey, _ := hex.DecodeString("EB354D4B18E0B4AC6E63369F33D0CFFE7F3C09101D29678877A6CE8879D7E152")
+ btcAddress, err := addressForPrivateKey(privKey, 0)
+ assert.Nil(t, err, "Error should be nil")
+ assert.Equal(t, "1MwiNcg3v19BLeawNJKL8L18m4Tzmtua5T", btcAddress)
+
+ btcTestNetAddress, err := addressForPrivateKey(privKey, 1)
+ assert.Nil(t, err, "Error should be nil")
+ assert.Equal(t, "n2Tfffm2j2aS7m4Z5sHhxFDTd44hfnDhPz", btcTestNetAddress)
+
+ _, err = addressForPrivateKey(privKey, 9999999999)
+ assert.EqualError(t, err, "unsupported coin")
+}
diff --git a/pkg/qredoplugin/helpers.go b/pkg/qredoplugin/helpers.go
new file mode 100644
index 0000000..9295106
--- /dev/null
+++ b/pkg/qredoplugin/helpers.go
@@ -0,0 +1,225 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package qredoplugin
+
+import (
+ "crypto/sha256"
+ "encoding/hex"
+ "fmt"
+ "math/big"
+
+ "github.com/apache/incubator-milagro-dta/libs/cryptowallet"
+ "github.com/apache/incubator-milagro-dta/libs/documents"
+ "github.com/apache/incubator-milagro-dta/pkg/common"
+ "github.com/btcsuite/btcd/btcec"
+ "github.com/pkg/errors"
+)
+
+func deriveFinalPrivateKey(s *Service, order documents.OrderDoc, beneficiariesSikeSK []byte, beneficiariesSeed []byte, beneficiaryIDDocumentCID string, nodeID string, signingBlsPK []byte) (string, error) {
+ if beneficiaryIDDocumentCID != "" {
+ //we are using the beneficiary specified in order Part 3
+ beneficiaryBlob := order.OrderPart3.BeneficiaryEncryptedData
+
+ //Decrypt the Envelope intented for the Beneficiary
+ privateKeyPart1of1, err := adhocEncryptedEnvelopeDecode(s, beneficiariesSikeSK, beneficiaryBlob, beneficiaryIDDocumentCID, signingBlsPK)
+ if err != nil {
+ return "", err
+ }
+ //Calculate the final private key by Eliptical Key addition of both parts
+ privateKeyPart2of2 := order.OrderDocument.OrderPart4.Secret
+
+ finalPrivateKey, err := addPrivateKeys(privateKeyPart1of1, privateKeyPart2of2)
+
+ if err != nil {
+ return "", err
+ }
+ return finalPrivateKey, err
+ }
+
+ //we are using the beneficiary specified in the order part 1
+ privateKeyPart2of2 := order.OrderDocument.OrderPart4.Secret
+ if order.OrderDocument.BeneficiaryCID != nodeID {
+ //need to forward this data to the beneficiary to complete redemption
+ return "", errors.New("Currently beneficiary must be the same as the Principal")
+ }
+ //restore the Seed
+ _, _, ecAddPrivateKey, err := cryptowallet.Bip44Address(beneficiariesSeed, cryptowallet.CoinTypeBitcoinMain, 0, 0, 0)
+ if err != nil {
+ return "", err
+ }
+ privateKeyPart1of1 := hex.EncodeToString(ecAddPrivateKey.Serialize())
+ finalPrivateKey, err := addPrivateKeys(privateKeyPart1of1, privateKeyPart2of2)
+ if err != nil {
+ return "", err
+ }
+ return finalPrivateKey, err
+
+}
+
+func adhocEncryptedEnvelopeEncode(s *Service, nodeID string, beneficiaryIDDocumentCID string, order documents.OrderDoc, blsSK []byte) ([]byte, error) {
+ //Regenerate the original Princaipal Priv Key based on Order
+ if beneficiaryIDDocumentCID == "" {
+ //beneficiaryIDDocumentCID is empty when it was passed in the Inital Deposit Order
+ return nil, nil
+ }
+ seedHex, err := common.RetrieveSeed(s.Store, order.Reference)
+ if err != nil {
+ return nil, err
+ }
+ seedOrderModifier := order.OrderDocument.OrderPart2.PreviousOrderCID
+ seed, err := hex.DecodeString(seedHex)
+ if err != nil {
+ return nil, err
+ }
+ concatenatedSeeds := append(seed, seedOrderModifier...)
+ finalSeed := sha256.Sum256(concatenatedSeeds)
+ finalSeedHex := hex.EncodeToString(finalSeed[:])
+ privateKeyPart1of2, err := cryptowallet.RedeemSecret(finalSeedHex)
+ if err != nil {
+ return nil, err
+ }
+ beneficiaryIDDocument, err := common.RetrieveIDDocFromIPFS(s.Ipfs, beneficiaryIDDocumentCID)
+ if err != nil {
+ return nil, err
+ }
+ secretBody := &documents.SimpleString{Content: privateKeyPart1of2}
+ header := &documents.Header{}
+ recipients := map[string]documents.IDDoc{
+ beneficiaryIDDocumentCID: beneficiaryIDDocument,
+ }
+ docEnv, err := documents.Encode(nodeID, nil, secretBody, header, blsSK, recipients)
+ if err != nil {
+ return nil, err
+ }
+ return docEnv, err
+}
+
+func adhocEncryptedEnvelopeDecode(s *Service, sikeSK []byte, beneficiaryBlob []byte, beneficiaryIDDocumentCID string, signingBlsPK []byte) (string, error) {
+ //Regenerate the original Principal Priv Key based on Order
+ secretBody := &documents.SimpleString{}
+ _, err := documents.Decode(beneficiaryBlob, "INTERNAL", sikeSK, beneficiaryIDDocumentCID, nil, secretBody, signingBlsPK)
+ if err != nil {
+ return "", err
+ }
+ return secretBody.Content, err
+}
+
+func generateFinalPubKey(s *Service, pubKeyPart2of2 string, order documents.OrderDoc) (string, string, error) {
+ beneficiaryIDDocumentCID := order.OrderDocument.BeneficiaryCID
+ coinType := order.Coin
+ var pubKeyPart1of2 string
+
+ if beneficiaryIDDocumentCID == "" {
+ //There is no beneficiary ID so we do it all locally based on
+ //Retrieve the Local Seed
+ seedHex, err := common.RetrieveSeed(s.Store, order.Reference)
+ if err != nil {
+ return "", "", err
+ }
+ seedOrderModifier := order.OrderDocument.OrderPart2.PreviousOrderCID
+ seed, err := hex.DecodeString(seedHex)
+ if err != nil {
+ return "", "", err
+ }
+ concatenatedSeeds := append(seed, seedOrderModifier...)
+ finalSeed := sha256.Sum256(concatenatedSeeds)
+ finalSeedHex := hex.EncodeToString(finalSeed[:])
+ //Use HD Wallet to obtain the local Public Key
+ pubKeyPart1of2, err = cryptowallet.RedeemPublicKey(finalSeedHex)
+
+ if err != nil {
+ return "", "", err
+ }
+ }
+
+ if beneficiaryIDDocumentCID != "" {
+ //There is a BeneficiaryID use it to generate the key
+ //Get beneficiary's identity out of IPFS
+ id := &documents.IDDoc{}
+ rawDocI, err := s.Ipfs.Get(beneficiaryIDDocumentCID)
+ if err != nil {
+ return "", "", errors.Wrapf(err, "Read identity Doc")
+ }
+ err = documents.DecodeIDDocument(rawDocI, beneficiaryIDDocumentCID, id)
+ if err != nil {
+ return "", "", err
+ }
+ pubKeyPart1of2 = hex.EncodeToString(id.BeneficiaryECPublicKey)
+ }
+
+ finalPublicKey, err := addPublicKeys(pubKeyPart2of2, pubKeyPart1of2)
+
+ if err != nil {
+ return "", "", err
+ }
+ addressForPublicKey, err := addressForPublicKey(finalPublicKey, int(coinType))
+ if err != nil {
+ return "", "", err
+ }
+ return finalPublicKey, addressForPublicKey, nil
+
+}
+
+//AddPrivateKeys Perform eliptical key additon on 2 privates keys
+func addPrivateKeys(key1 string, key2 string) (string, error) {
+ curveOrder := new(big.Int)
+ curveOrder.SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16)
+ priv1 := new(big.Int)
+ priv2 := new(big.Int)
+ priv3 := new(big.Int)
+ priv1.SetString(key1, 16)
+ priv2.SetString(key2, 16)
+ priv3.Add(priv1, priv2)
+ if curveOrder.Cmp(priv3) == -1 {
+ priv3.Sub(priv3, curveOrder)
+ }
+ priv4 := fmt.Sprintf("%064x", priv3)
+ return priv4, nil
+}
+
+//AddPublicKeys Perform eliptical key addition on 2 public keys
+func addPublicKeys(key1 string, key2 string) (string, error) {
+ pub1Hex, err := hex.DecodeString(key1)
+ if err != nil {
+ return "", errors.Wrap(err, "Failed to hex decode String")
+ }
+ dpub1, err := btcec.ParsePubKey(pub1Hex, btcec.S256())
+ if err != nil {
+ return "", errors.Wrap(err, "Failed to Parse Public Key")
+ }
+ pub2Hex, err := hex.DecodeString(key2)
+ if err != nil {
+ return "", errors.Wrap(err, "Failed to hex decode String")
+ }
+ dpub2, err := btcec.ParsePubKey(pub2Hex, btcec.S256())
+ if err != nil {
+ return "", errors.Wrap(err, "Failed to Parse Public Key")
+ }
+ x, y := btcec.S256().Add(dpub1.X, dpub1.Y, dpub2.X, dpub2.Y)
+ comp := fmt.Sprintf("04%064X%064X", x, y)
+ compByte, err := hex.DecodeString(comp)
+ if err != nil {
+ return "", err
+ }
+ pubKey, err := btcec.ParsePubKey([]byte(compByte), btcec.S256())
+ if err != nil {
+ return "", err
+ }
+ pubKeyString := hex.EncodeToString(pubKey.SerializeCompressed())
+ return pubKeyString, nil
+}
diff --git a/pkg/qredoplugin/helpers_test.go b/pkg/qredoplugin/helpers_test.go
new file mode 100644
index 0000000..857e2fa
--- /dev/null
+++ b/pkg/qredoplugin/helpers_test.go
@@ -0,0 +1,47 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package qredoplugin
+
+import (
+ "encoding/hex"
+ "testing"
+
+ "github.com/btcsuite/btcd/btcec"
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_AddKeys(t *testing.T) {
+ privKey1, _ := btcec.NewPrivateKey(btcec.S256())
+ privKey2, _ := btcec.NewPrivateKey(btcec.S256())
+ privKey1Str := hex.EncodeToString(privKey1.Serialize())
+ privKey2Str := hex.EncodeToString(privKey2.Serialize())
+
+ pubKey1 := btcec.PublicKey(privKey1.ToECDSA().PublicKey)
+ pubKey2 := btcec.PublicKey(privKey2.ToECDSA().PublicKey)
+ pubKey1Str := hex.EncodeToString(pubKey1.SerializeUncompressed())
+ pubKey2Str := hex.EncodeToString(pubKey2.SerializeUncompressed())
+
+ //run Additions
+ privAdd, _ := addPrivateKeys(privKey1Str, privKey2Str)
+ pubAdd, _ := addPublicKeys(pubKey1Str, pubKey2Str)
+
+ privAddBytes, _ := hex.DecodeString(privAdd)
+ _, pubKeyFromPrivate := btcec.PrivKeyFromBytes(btcec.S256(), privAddBytes)
+ pubKeyFromPrivateString := hex.EncodeToString(pubKeyFromPrivate.SerializeCompressed())
+ assert.Equal(t, pubKeyFromPrivateString, pubAdd, "Addition failed")
+}
diff --git a/pkg/qredoplugin/service.go b/pkg/qredoplugin/service.go
new file mode 100644
index 0000000..855b2fe
--- /dev/null
+++ b/pkg/qredoplugin/service.go
@@ -0,0 +1,103 @@
+package qredoplugin
+
+import (
+ "strconv"
+
+ "github.com/apache/incubator-milagro-dta/libs/cryptowallet"
+ "github.com/apache/incubator-milagro-dta/libs/documents"
+ "github.com/apache/incubator-milagro-dta/pkg/api"
+ "github.com/apache/incubator-milagro-dta/pkg/common"
+ "github.com/apache/incubator-milagro-dta/pkg/defaultservice"
+
+ "github.com/pkg/errors"
+)
+
+var (
+ extensionVendor = "Qredo"
+ pluginName = "qredoplugin"
+)
+
+// Service is the Qredo Plugin service
+type Service struct {
+ defaultservice.Service
+}
+
+// NewService returns a Milagro implementation of Service
+func NewService() *Service {
+ return &Service{}
+}
+
+// Name of the plugin
+func (s *Service) Name() string {
+ return pluginName
+}
+
+// Vendor of the plugin
+func (s *Service) Vendor() string {
+ return extensionVendor
+}
+
+// ValidateOrderRequest checks if the Coin type is valid
+func (s *Service) ValidateOrderRequest(req *api.OrderRequest) error {
+ if _, err := strconv.ParseInt(req.Extension["coin"], 10, 64); err != nil {
+ return errors.Wrap(err, "Failed to Parse Coin Type")
+ }
+
+ return nil
+}
+
+// PrepareOrderPart1 adds the coin type to the order
+func (s *Service) PrepareOrderPart1(order *documents.OrderDoc, reqExtension map[string]string) (fulfillExtension map[string]string, err error) {
+ coin, err := strconv.ParseInt(reqExtension["coin"], 10, 64)
+ if err != nil {
+ return nil, errors.Wrap(err, "Failed to Parse Coin Type")
+ }
+
+ order.Coin = coin
+ return nil, nil
+}
+
+// PrepareOrderResponse gets the updated order and returns the commitment and extension
+func (s *Service) PrepareOrderResponse(orderPart2 *documents.OrderDoc, reqExtension, fulfillExtension map[string]string) (commitment string, extension map[string]string, err error) {
+ pubKeyPart2of2 := orderPart2.OrderPart2.CommitmentPublicKey
+ finalPublicKey, cryptoAddress, err := generateFinalPubKey(s, pubKeyPart2of2, *orderPart2)
+
+ return finalPublicKey, map[string]string{"address": cryptoAddress}, nil
+}
+
+// ProduceBeneficiaryEncryptedData -
+func (s *Service) ProduceBeneficiaryEncryptedData(blsSK []byte, order *documents.OrderDoc, req *api.OrderSecretRequest) (encrypted []byte, extension map[string]string, err error) {
+ //There is no beneficiary supplided in either the Deposit or Redemption
+ if order.BeneficiaryCID == "" && req.BeneficiaryIDDocumentCID == "" {
+ return nil, nil, errors.New("Beneficiary must be supplied")
+ }
+
+ enc, err := adhocEncryptedEnvelopeEncode(s, s.NodeID(), req.BeneficiaryIDDocumentCID, *order, blsSK)
+ return enc, nil, err
+}
+
+// ProduceFinalSecret -
+func (s *Service) ProduceFinalSecret(seed, sikeSK []byte, order, orderPart4 *documents.OrderDoc, req *api.OrderSecretRequest, fulfillSecretRespomse *api.FulfillOrderSecretResponse) (secret, commitment string, extension map[string]string, err error) {
+ //retrieve principal IDDoc
+ principalDocID, err := common.RetrieveIDDocFromIPFS(s.Ipfs, order.PrincipalCID)
+ if err != nil {
+ return "", "", nil, err
+ }
+
+ finalPrivateKey, err := deriveFinalPrivateKey(s, *orderPart4, sikeSK, seed, req.BeneficiaryIDDocumentCID, s.NodeID(), principalDocID.BLSPublicKey)
+ if err != nil {
+ return "", "", nil, err
+ }
+ //Generate Public key & derive crypto address
+ finalPublicKey, finalPublicKeyCompressed, err := cryptowallet.PublicKeyFromPrivate(finalPrivateKey)
+ if err != nil {
+ return "", "", nil, err
+ }
+
+ addressForPublicKey, err := addressForPublicKey(finalPublicKey, int(order.Coin))
+ if err != nil {
+ return "", "", nil, err
+ }
+
+ return finalPrivateKey, finalPublicKeyCompressed, map[string]string{"address": addressForPublicKey}, nil
+}
diff --git a/plugins/qredoplugin.go b/plugins/qredoplugin.go
new file mode 100644
index 0000000..68bf54d
--- /dev/null
+++ b/plugins/qredoplugin.go
@@ -0,0 +1,9 @@
+package plugins
+
+import (
+ "github.com/apache/incubator-milagro-dta/pkg/qredoplugin"
+)
+
+func init() {
+ registerPlugin(qredoplugin.NewService())
+}