package zcashrpc import ( "bytes" "encoding/base64" "encoding/json" "git.openprivacy.ca/openprivacy/libricochet-go/connectivity" "git.openprivacy.ca/openprivacy/libricochet-go/log" "io/ioutil" "net" "net/http" "strconv" ) // ZcashClient defines an interface for any zcash client to present. type ZcashClient interface { GetTotalBalance(minConfirmations int) (float64, error) GetTransaction(string) (Transaction, error) ListAddresses() ([]string, error) ListReceivedTransactionsByAddress(string) ([]ZcashTransaction, error) GetBlock(int) (Block, error) SendOne(string, string, string, float64) ([]byte, error) ValidateAddress(address string) (ZValidateAddressResponse, error) } type zcashClient struct { client http.Client address string auth string } // NewLocalClient creates a new Zcash rpc client over a local address func NewLocalClient(username, password string) ZcashClient { zc := new(zcashClient) zc.address = "127.0.0.1:8232" zc.client = http.Client{ Transport: &http.Transport{ Dial: func(network, addr string) (net.Conn, error) { conn, err := net.Dial("tcp", zc.address) return conn, err }, }, } zc.auth = base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) return zc } // NewOnionClient creates a new Zcash rpc client over an onion address func NewOnionClient(onion string, username, password string, acn connectivity.ACN) ZcashClient { zc := new(zcashClient) zc.address = onion + ".onion:9878" zc.client = http.Client{ Transport: &http.Transport{ Dial: func(network, addr string) (net.Conn, error) { conn, _, err := acn.Open(onion) return conn, err }, }, } zc.auth = base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) return zc } // Return the total value of funds stored in the node’s wallet. Set the minimum number of confirmations a private or transparent transaction must have in order to be included in the balance. Use 0 to count unconfirmed transactions. func (zc *zcashClient) GetTotalBalance(minConfirmations int) (float64, error) { body, err := zc.sendRequest(NewZGetTotalBalance(minConfirmations)) var totalBalance ZGetTotalBalanceResponse if err == nil { result := &ZcashResult{Result: &totalBalance} err = json.Unmarshal(body, &result) } return strconv.ParseFloat(totalBalance.Private, 64) } // GetTransaction returns a specific transaction given an id func (zc *zcashClient) GetBlock(id int) (Block, error) { body, err := zc.sendRequest(NewGetBlock(id)) block := Block{} if err == nil { result := &ZcashResult{Result: &block} err = json.Unmarshal(body, &result) } return block, err } // GetTransaction returns a specific transaction given an id func (zc *zcashClient) GetTransaction(id string) (Transaction, error) { body, err := zc.sendRequest(NewGetTransaction(id)) transaction := Transaction{} if err == nil { result := &ZcashResult{Result: &transaction} err = json.Unmarshal(body, &result) } return transaction, err } // Returns a list of all the zaddrs in this node’s wallet for which you have a spending key. func (zc *zcashClient) ListAddresses() ([]string, error) { body, err := zc.sendRequest(NewZListAddresses()) var addresses []string if err == nil { result := &ZcashResult{Result: &addresses} err = json.Unmarshal(body, &result) } return addresses, err } // ListReceivedTransactionsByAddress returns all the transactions received by a given zcash address func (zc *zcashClient) ListReceivedTransactionsByAddress(address string) ([]ZcashTransaction, error) { body, err := zc.sendRequest(NewZListReceivedByAddress(address)) var transactions []ZcashTransaction if err == nil { result := &ZcashResult{Result: &transactions} err = json.Unmarshal(body, &result) } for i := range transactions { transactions[i].Address = address } return transactions, err } // SendOne uses z_sendmany to send a single payment to the given address from the given fromAddress func (zc *zcashClient) SendOne(fromAddress string, toaddress string, memo string, amount float64) ([]byte, error) { jsonStr := NewZSendMany(fromAddress, []ZcashAmount{NewZcashAmount(toaddress, memo, amount)}) log.Debugf("Zcash SendMany %s", jsonStr) return zc.sendRequest(jsonStr) } // ValidateAddress returns the result of a z_validateaddress call func (zc *zcashClient) ValidateAddress(address string) (ZValidateAddressResponse, error) { body, err := zc.sendRequest(NewZValidateAddress(address)) var validation ZValidateAddressResponse if err == nil { result := &ZcashResult{Result: &validation} log.Debugf("Result: %s", body) err = json.Unmarshal(body, &result) } return validation, err } func (zc *zcashClient) sendRequest(jsonStr []byte) ([]byte, error) { req, err := http.NewRequest("POST", "http://"+zc.address, bytes.NewBuffer(jsonStr)) if err != nil { return nil, err } req.Header.Add("Authorization", "Basic "+zc.auth) log.Debugf("Sending request to zcash server %v", req) resp, err := zc.client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } log.Debugf("Got response from zcash server %s", body) return body, nil }