From d650172f5539c640ec5e8f1af670fc71c63e0e3c Mon Sep 17 00:00:00 2001 From: "vracek@protonmail.com" Date: Sat, 26 Oct 2019 19:40:40 -0600 Subject: [PATCH] multi: change getpaytocontractaddress result and add it's method to the rpc server --- go.mod | 1 + internal/rpc/jsonrpc/methods.go | 137 ++++++++++++++++++-------------- rpc/jsonrpc/types/results.go | 6 +- rpc/legacyrpc/contracts.go | 107 ------------------------- rpc/legacyrpc/contracts_test.go | 29 ------- 5 files changed, 83 insertions(+), 197 deletions(-) delete mode 100644 rpc/legacyrpc/contracts.go delete mode 100644 rpc/legacyrpc/contracts_test.go diff --git a/go.mod b/go.mod index 4c88cc3a3..6c7ea826f 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/decred/dcrd/chaincfg/v2 v2.3.0 github.com/decred/dcrd/connmgr v1.0.2 github.com/decred/dcrd/connmgr/v2 v2.0.0 + github.com/decred/dcrd/crypto/blake256 v1.0.0 github.com/decred/dcrd/dcrec v1.0.0 github.com/decred/dcrd/dcrjson/v2 v2.2.0 github.com/decred/dcrd/dcrjson/v3 v3.0.1 diff --git a/internal/rpc/jsonrpc/methods.go b/internal/rpc/jsonrpc/methods.go index 4eeb7e545..926c52530 100644 --- a/internal/rpc/jsonrpc/methods.go +++ b/internal/rpc/jsonrpc/methods.go @@ -12,6 +12,7 @@ import ( "encoding/binary" "encoding/hex" "encoding/json" + "io/ioutil" "math/big" "sort" "strconv" @@ -23,6 +24,7 @@ import ( blockchain "github.com/decred/dcrd/blockchain/standalone" "github.com/decred/dcrd/chaincfg/chainhash" "github.com/decred/dcrd/chaincfg/v2" + "github.com/decred/dcrd/crypto/blake256" "github.com/decred/dcrd/dcrec" "github.com/decred/dcrd/dcrjson/v3" "github.com/decred/dcrd/dcrutil/v2" @@ -80,8 +82,10 @@ var handlers = map[string]handler{ "getbestblockhash": {fn: (*Server).getBestBlockHash}, "getblockcount": {fn: (*Server).getBlockCount}, "getblockhash": {fn: (*Server).getBlockHash}, + "getcontracthash": {fn: (*Server).getContractHash}, "getinfo": {fn: (*Server).getInfo}, "getmasterpubkey": {fn: (*Server).getMasterPubkey}, + "getpaytocontractaddress": {fn: (*Server).getPayToContractAddress}, "getmultisigoutinfo": {fn: (*Server).getMultisigOutInfo}, "getnewaddress": {fn: (*Server).getNewAddress}, "getrawchangeaddress": {fn: (*Server).getRawChangeAddress}, @@ -832,64 +836,52 @@ func difficultyRatio(bits uint32, params *chaincfg.Params) float64 { return ratio } -<<<<<<< HEAD:internal/rpc/jsonrpc/methods.go -======= // hashContracts hashes contracts and places them in a array. ->>>>>>> legacyrpc: add getContractHash method:rpc/legacyrpc/methods.go -func hashContracts(contractArray [][]byte, contractAmounts int) [][]byte { - - var hashedContracts []hash.Hash - hashedContractsByte := make([][]byte, contractAmounts) +func hashContracts(contractArray [][]byte) [][]byte { + // var contractHasher hash.Hash = blake256.New() + hc32 := make([][32]byte, len(contractArray)) + hc := make([][]byte, len(contractArray)) for i := range contractArray { - hashedContracts[i] = hmac.New(sha512.New, contractArray[i]) - hashedContractsByte[i] = hashedContracts[i].Sum(nil) + hc32[i] = blake256.Sum256(contractArray[i]) + hc[i] = hc32[i][:] } - return hashedContractsByte + return hc } -// hashContracts hashes contracts and places them in a array. -func hashContracts(contractArray [][]byte, contractAmount int) [][]byte { - var hashedContracts []hash.Hash - hashedContracts := make([][]byte, contractAmount) - hasher := sha256.New() - for i := range contractArray { - hasher.Write(contractArray[i]) - hashedContracts[i] = hasher.Sum(nil) - } - - return hashedContracts -} - -// createContractArray creates a array of contracts from the input filepath slice. +// createContractArray creates a array of contracts in byte form from the input filepath slice. func createContractArray(filePaths []string) ([][]byte, error) { - contractArray := make([][]byte, len(filePaths)) + // TODO: add checking here for right file type + var contractArray = make([][]byte, len(filePaths)) for i := range filePaths { - contractArray[i], err = ioutil.ReadFile(filePaths[i]) - if err != nil { - return nil, err - } + contractArray[i], _ = ioutil.ReadFile(filePaths[i]) + /* + if err != nil { + return nil, err + } + */ } return contractArray, nil } // getContractHash returns a slice of hashed contracts in string form. -func getContractHash(s *Server, icmd interface{}) (interface{}, error) { - cmd := icmd.(*dcrjson.GetContractHashCmd) - - contractArray, err := createContractArray(cmd.FilePath[i]) +func (s *Server) getContractHash(ctx context.Context, icmd interface{}) (interface{}, error) { + cmd := icmd.(*types.GetContractHashCmd) + contractArray, err := createContractArray(cmd.FilePath) if err != nil { return nil, err } - hashedContracts := hashContracts(contractArray, len(cmd.FilePath)) + hashedContracts := hashContracts(contractArray) + + // TODO: how can I do this better, do I really need to make them as strings? hashedContractsString := make([]string, len(cmd.FilePath)) for i := range hashedContracts { hashedContractsString[i] = string(hashedContractsString[i]) } - return &dcrjson.GetContractHashResult{ + return &types.GetContractHashResult{ ContractHash: hashedContractsString, }, nil } @@ -1337,12 +1329,8 @@ func (s *Server) getNewAddress(ctx context.Context, icmd interface{}) (interface if cmd.Account != nil { acctName = *cmd.Account } -<<<<<<< HEAD:internal/rpc/jsonrpc/methods.go - account, err := w.AccountNumber(ctx, acctName) -======= - account, err := w.AccountNumber(acctName) ->>>>>>> legacyrpc: add getContractHash method:rpc/legacyrpc/methods.go + account, err := w.AccountNumber(ctx, acctName) if err != nil { if errors.Is(err, errors.NotExist) { return nil, errAccountNotFound @@ -1371,48 +1359,81 @@ func lexiSort(hashedContracts [][]byte) [][]byte { return hashedContracts } +func getP2PKHFromExtendedKey(extKey *hdkeychain.ExtendedKey, n *chaincfg.Params) (string, error) { + ecPubKey, err := extKey.ECPubKey() + if err != nil { + return "", err + } + pkHash := dcrutil.Hash160(ecPubKey.SerializeCompressed()) + addr, err := dcrutil.NewAddressPubKeyHash(pkHash, n, dcrec.STEcdsaSecp256k1) + if err != nil { + return "", err + } + return addr.String(), nil +} + // getPayToContractAddress handles the getpaytocontractaddress by returning a // address for a contract array -func getPayToContractAddress(s *Server, icmd interface{}) (interface{}, error) { - cmd := icmd.(*dcrjson.GetPayToContractAddressCmd) +func (s *Server) getPayToContractAddress(ctx context.Context, icmd interface{}) (interface{}, error) { + cmd := icmd.(*types.GetPayToContractAddressCmd) w, ok := s.walletLoader.LoadedWallet() if !ok { - return nil, ErrUnloadedWallet + return nil, errUnloadedWallet } - // TODO: I need to utilize the hdkeychain.Zero() after every childkey derivation - contractArray, err := createContractArray(cmd.FilePath[i]) + net := w.ChainParams() + + contractArray, err := createContractArray(cmd.FilePath) if err != nil { return nil, err } - // hash each contract and then sort them lexicographically - hashedContracts := hashContracts(contractArray, len(cmd.FilePath)) - hashedContracts = lexiSort(hashedContracts) + hashedContracts := lexiSort(hashContracts(contractArray)) - // obtain the contractHash extendedKey - contractHash := ContractHash(hashedContracts) + var contractHash []byte + for i := 0; i < len(hashedContracts); i++ { + for j := 0; i < len(hashedContracts); j++ { + contractHash = append(contractHash, hashedContracts[i][j]) + } + } + + ch32 := blake256.Sum256(contractHash) + stringCh := string(ch32[:]) + + contractsHashedExtKey, err := hdkeychain.NewKeyFromString(stringCh, net) + if err != nil { + return nil, err + } + defer contractsHashedExtKey.Zero() + + contractsHashedExtKey, err = contractsHashedExtKey.Child(0) + if err != nil { + return nil, err + } + + coinTypePrivKey, err := w.CoinTypePrivKey(ctx) + if err != nil { + return nil, err + } + defer coinTypePrivKey.Zero() - // paymentBase and coin type key are synonyms. - paymentBase, err := w.CoinTypeKey() + contractsHashedExtKey, err = hdkeychain.NewKeyFromString(coinTypePrivKey.String()+contractsHashedExtKey.String(), w.ChainParams()) if err != nil { return nil, err } - // append the contractHash derivation path to the paymentBase - extendedKey, err := NewKeyFromString(append(paymentBase.String(), contractHash.String())) + contractsHashedExtKey, err = contractsHashedExtKey.Child(0) if err != nil { return nil, err } - // take the address format 1. begins with the version byte denoting the address type and end with a 4 byte checksum - checksum (SHA256(SHA256(prefix+data))) - addr, err := extendedKey.Address(w.ChainParams()) + payToContractAddress, err := getP2PKHFromExtendedKey(contractsHashedExtKey, net) if err != nil { return nil, err } - return &dcrjson.GetPayToContractAddressResult{ - ContractAddress: addr.EncodeAddress(), + return &types.GetPayToContractAddressResult{ + Address: payToContractAddress, }, nil } diff --git a/rpc/jsonrpc/types/results.go b/rpc/jsonrpc/types/results.go index fbedcf690..4adaaece3 100644 --- a/rpc/jsonrpc/types/results.go +++ b/rpc/jsonrpc/types/results.go @@ -43,7 +43,7 @@ type GetBalanceResult struct { // GetContractHashResult models the data from the getcontracthash command. type GetContractHashResult struct { - ContractHash string `json:"contracthash"` + ContractHash []string `json:"contracthash"` } // GetMultisigOutInfoResult models the data returned from the getmultisigoutinfo @@ -63,9 +63,9 @@ type GetMultisigOutInfoResult struct { Amount float64 `json:"amount"` } -// GetPayToContractHashResult models the data returned from the getpaytocontracthash +// GetPayToContractAddressResult models the data returned from the getpaytocontracthash // command. -type GetPayToContractHashResult struct { +type GetPayToContractAddressResult struct { Address string `json:"address"` } diff --git a/rpc/legacyrpc/contracts.go b/rpc/legacyrpc/contracts.go deleted file mode 100644 index 1089ad37d..000000000 --- a/rpc/legacyrpc/contracts.go +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2013-2016 The btcsuite developers -// Copyright (c) 2015-2016 The Decred developers -// Copyright (c) 2017-2018 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package legacyrpc - -import ( - "bytes" - "encoding/binary" - "fmt" - - "io/ioutil" - - "github.com/decred/dcrd/hdkeychain" - "github.com/decred/dcrd/chainhash" -) - -// Contracthash gets the contract hash from the inputted hashedContracts -func ContractHash(hashedContracts [][]byte) (hdkeychain.ExtendedKey, error) { - // append the hashed contracts i.e h1 + h2 + h3 to create combinedContractHashes - combinedContractHashes := make([]byte, 1) - for i := range hashedContracts { - for j := range hashedContracts { - combinedContractHashes = append(combinedContractHashes, hashedContracts[i][j]) - } - } - - // apply the blake256 hash function to the combinedContractHashes - contractsHashed := chainhash.HashB(combinedContractHashes) - - // build the contract hash extended key - hashChan := make(chan hdkeychain.ExtendedKey) - go func (contractsHashed []byte) { - segmentedHashedContracts := make([][]byte, 16) - uint32segmentedHashedContracts := make([]uint32, 16) - for i := 0; i < 16; i++ { - switch i { - case i == 0: - segmentedHashedContracts[i] = contractsHashed[i*5 : (i*5)+5] - uint32segmentedHashedContracts[i] = byteToUInt32(segmentedHashedContracts[i]) - contractHash := hdkeychain.NewKeyFromString(string(segmentedHashedContracts[i]) - case i >= 1: - segmentedHashedContracts[i] = contractsHashed[i*5 : (i*5)+5] - uint32segmentedHashedContracts[i] = byteToUInt32(segmentedHashedContracts[i]) - contractHash, err := contractHash.Child(uint32segmentedHashedContracts[i]) - if err != nil { - return nil, err - } - } - } - - hashChan <- contractHash - }(contractsHashed) - - - return <- hashChan, nil -} - -// createContractArray creates a array of contracts from the input filepath slice. -// -// TODO: convert text to a input standard here before ReadFile path. -// or add error checking for specific text format -func createContractArray(filePaths []string) ([][]byte, error) { - var contractArray = make([][]byte, len(filePaths)) - for i := range filePaths { - contractArray[i], _ = ioutil.ReadFile(filePaths[i]) - } - - if len(contractArray) != len(filePaths) { - return nil, fmt.Errorf("Contract Array was not created successfully") - } - - return contractArray, nil -} - -func byteToUInt32(data []byte) (ret uint32) { - buf := bytes.NewBuffer(data) - binary.Read(buf, binary.LittleEndian, &ret) - return -} - -// hashContracts hashes contracts and places them in a lexigrahpically assorted array. -func hashContracts(contractArray [][]byte) [][]byte { - hashedContracts := make([][]byte, len(contractArray)) - - for i := range contractArray { - hashedContracts[i] = chainhash.HashB(contractArray[i]) - } - - return lexiSort(hashedContracts) -} - -func lexiSort(hashedContracts [][]byte) ([][]byte, error) { - for i := range hashedContracts { - for j := range hashedContracts { - if len(hashedContracts[i][j]) != 256 { - return nil, fmt.Errorf("Contract should be a size of 256 bits") - } else if hashedContracts[i][j] > hashedContracts[i][j+1] { - hashedContracts[i], hashedContracts[i+1] = hashedContracts[i+1], hashedContracts[i] - } - } - } - - return hashedContracts, nil -} diff --git a/rpc/legacyrpc/contracts_test.go b/rpc/legacyrpc/contracts_test.go deleted file mode 100644 index 7c41ee4d9..000000000 --- a/rpc/legacyrpc/contracts_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package dcrutil - -import "testing" - -func TestlexiSort(t *testing.T) { - - lexiSort() -} - -func TesthashContracts(t *testing.T) { - - hashContracts() -} - -func TestbyteToUInt32(t *testing.T) { - - byteToUInt32() -} - -func TestcreateContractArray(t *testing.T) { - - createContractArray() -} - -func TestContractHash(t *testing.T) { - // t.Fatal("not implemented") - - ContractHash() -}