2020-12-19 10:01:29 +00:00
|
|
|
// Copyright 2017-2021 DERO Project. All rights reserved.
|
|
|
|
// Use of this source code in any form is governed by RESEARCH license.
|
|
|
|
// license can be found in the LICENSE file.
|
|
|
|
// GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
|
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
|
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
|
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
|
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
package walletapi
|
|
|
|
|
|
|
|
// this file needs serious improvements but have extremely limited time
|
|
|
|
/* this file handles communication with the daemon
|
|
|
|
* this includes receiving output information
|
|
|
|
*
|
|
|
|
* *
|
|
|
|
*/
|
|
|
|
//import "io"
|
|
|
|
//import "os"
|
|
|
|
import "fmt"
|
|
|
|
import "time"
|
|
|
|
import "sync"
|
|
|
|
import "bytes"
|
|
|
|
import "math/big"
|
|
|
|
|
|
|
|
//import "bufio"
|
|
|
|
import "strings"
|
|
|
|
import "context"
|
|
|
|
|
|
|
|
//import "runtime"
|
|
|
|
//import "compress/gzip"
|
|
|
|
import "encoding/hex"
|
|
|
|
|
|
|
|
import "runtime/debug"
|
|
|
|
|
|
|
|
import "github.com/romana/rlog"
|
|
|
|
|
|
|
|
//import "github.com/vmihailenco/msgpack"
|
|
|
|
|
|
|
|
//import "github.com/gorilla/websocket"
|
|
|
|
//import "github.com/mafredri/cdp/rpcc"
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
import "github.com/deroproject/derohe/rpc"
|
2020-12-19 10:01:29 +00:00
|
|
|
import "github.com/deroproject/derohe/block"
|
2021-02-22 17:48:14 +00:00
|
|
|
import "github.com/deroproject/derohe/cryptography/crypto"
|
2020-12-19 10:01:29 +00:00
|
|
|
import "github.com/deroproject/derohe/errormsg"
|
|
|
|
import "github.com/deroproject/derohe/transaction"
|
2021-02-22 17:48:14 +00:00
|
|
|
import "github.com/deroproject/derohe/cryptography/bn256"
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
import "github.com/creachadair/jrpc2"
|
|
|
|
|
|
|
|
// this global variable should be within wallet structure
|
|
|
|
var Connected bool = false
|
|
|
|
|
|
|
|
// there should be no global variables, so multiple wallets can run at the same time with different assset
|
2020-12-27 13:44:23 +00:00
|
|
|
|
2020-12-19 10:01:29 +00:00
|
|
|
var endpoint string
|
|
|
|
|
|
|
|
var output_lock sync.Mutex
|
|
|
|
|
2020-12-27 13:44:23 +00:00
|
|
|
var NotifyNewBlock *sync.Cond = sync.NewCond(&sync.Mutex{})
|
|
|
|
var NotifyHeightChange *sync.Cond = sync.NewCond(&sync.Mutex{})
|
|
|
|
|
|
|
|
// this function will wait n goroutines to wait for new block
|
|
|
|
func WaitNewBlock() {
|
|
|
|
NotifyNewBlock.L.Lock()
|
|
|
|
NotifyNewBlock.Wait()
|
|
|
|
NotifyNewBlock.L.Unlock()
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
|
|
|
|
2020-12-27 13:44:23 +00:00
|
|
|
// this function will wait n goroutines to wait till height changes
|
|
|
|
func WaitNewHeightBlock() {
|
|
|
|
NotifyHeightChange.L.Lock()
|
|
|
|
NotifyHeightChange.Wait()
|
|
|
|
NotifyHeightChange.L.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func Notify_broadcaster(req *jrpc2.Request) {
|
|
|
|
|
|
|
|
timer.Reset(timeout) // connection is alive
|
|
|
|
switch req.Method() {
|
|
|
|
|
|
|
|
case "Repoll":
|
|
|
|
NotifyNewBlock.L.Lock()
|
|
|
|
NotifyNewBlock.Broadcast()
|
|
|
|
NotifyNewBlock.L.Unlock()
|
|
|
|
case "HRepoll":
|
|
|
|
NotifyHeightChange.L.Lock()
|
|
|
|
NotifyHeightChange.Broadcast()
|
|
|
|
NotifyHeightChange.L.Unlock()
|
|
|
|
default:
|
|
|
|
rlog.Debugf("Notification received %s\n", req.Method())
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// triggers syncing with wallet every 5 seconds
|
|
|
|
func (w *Wallet_Memory) sync_loop() {
|
|
|
|
w.Sync_Wallet_Memory_With_Daemon() // sync with the daemon
|
|
|
|
}
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
func (cli *Client) Call(method string, params interface{}, result interface{}) error {
|
|
|
|
return cli.RPC.CallResult(context.Background(), method, params, result)
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns whether wallet was online some time ago
|
2020-12-27 13:44:23 +00:00
|
|
|
func (w *Wallet_Memory) IsDaemonOnlineCached() bool {
|
2020-12-19 10:01:29 +00:00
|
|
|
return Connected
|
|
|
|
}
|
|
|
|
|
|
|
|
// currently process url with compatibility for older ip address
|
|
|
|
func buildurl(endpoint string) string {
|
|
|
|
if strings.IndexAny(endpoint, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") >= 0 { // url is already complete
|
|
|
|
return strings.TrimSuffix(endpoint, "/")
|
|
|
|
} else {
|
|
|
|
return "http://" + endpoint
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// this is as simple as it gets
|
|
|
|
// single threaded communication to get the daemon status and height
|
|
|
|
// this will tell whether the wallet can connection successfully to daemon or not
|
2020-12-27 13:44:23 +00:00
|
|
|
func IsDaemonOnline() bool {
|
2020-12-19 10:01:29 +00:00
|
|
|
if rpc_client.WS == nil || rpc_client.RPC == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-12-27 13:44:23 +00:00
|
|
|
// sync the wallet with daemon, this is instantaneous and can be done with a single call
|
|
|
|
// we have now the apis to avoid polling
|
|
|
|
func (w *Wallet_Memory) Sync_Wallet_Memory_With_Daemon() {
|
|
|
|
first_time := true
|
|
|
|
for {
|
|
|
|
select {
|
2021-02-22 17:48:14 +00:00
|
|
|
case <-w.Quit:
|
2020-12-27 13:44:23 +00:00
|
|
|
break
|
|
|
|
default:
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
if !IsDaemonOnline() {
|
|
|
|
w.Daemon_Height = 0
|
|
|
|
w.Daemon_TopoHeight = 0
|
|
|
|
}
|
|
|
|
|
2020-12-27 13:44:23 +00:00
|
|
|
if (first_time && IsDaemonOnline()) || (!first_time && IsDaemonOnline()) {
|
|
|
|
first_time = false
|
2021-02-22 17:48:14 +00:00
|
|
|
//w.random_ring_members()
|
2020-12-27 13:44:23 +00:00
|
|
|
rlog.Debugf("wallet topo height %d daemon online topo height %d\n", w.account.TopoHeight, w.Daemon_TopoHeight)
|
|
|
|
previous := w.account.Balance_Result.Data
|
2021-02-22 17:48:14 +00:00
|
|
|
var scid crypto.Hash
|
|
|
|
if _, _, err := w.GetEncryptedBalanceAtTopoHeight(scid, -1, w.GetAddress().String()); err == nil {
|
|
|
|
if w.account.Balance_Result.Data != previous || (len(w.account.EntriesNative[scid]) >= 1 && strings.ToLower(w.account.Balance_Result.Data) != strings.ToLower(w.account.EntriesNative[scid][len(w.account.EntriesNative[scid])-1].EWData)) {
|
2020-12-27 13:44:23 +00:00
|
|
|
w.DecodeEncryptedBalance() // try to decode balance
|
2021-02-22 17:48:14 +00:00
|
|
|
|
|
|
|
w.SyncHistory(scid) // also update statement
|
|
|
|
w.save_if_disk() // save wallet
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
2020-12-27 13:44:23 +00:00
|
|
|
} else {
|
|
|
|
rlog.Infof("getbalance err %s", err)
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
|
|
|
}
|
2020-12-27 13:44:23 +00:00
|
|
|
time.Sleep(timeout) // wait 5 seconds
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
2020-12-27 13:44:23 +00:00
|
|
|
return
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// this is as simple as it gets
|
|
|
|
// single threaded communication to relay TX to daemon
|
|
|
|
// if this is successful, then daemon is in control
|
|
|
|
|
2020-12-27 13:44:23 +00:00
|
|
|
func (w *Wallet_Memory) SendTransaction(tx *transaction.Transaction) (err error) {
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
if tx == nil {
|
|
|
|
return fmt.Errorf("Can not send nil transaction")
|
|
|
|
}
|
|
|
|
|
2020-12-27 13:44:23 +00:00
|
|
|
if !IsDaemonOnline() {
|
2020-12-19 10:01:29 +00:00
|
|
|
return fmt.Errorf("offline or not connected. cannot send transaction.")
|
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
params := rpc.SendRawTransaction_Params{Tx_as_hex: hex.EncodeToString(tx.Serialize())}
|
|
|
|
var result rpc.SendRawTransaction_Result
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
// Issue a call with a response.
|
|
|
|
if err := rpc_client.Call("DERO.SendRawTransaction", params, &result); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
//fmt.Printf("raw transaction result %+v\n", result)
|
|
|
|
|
|
|
|
if result.Status == "OK" {
|
|
|
|
return nil
|
|
|
|
} else {
|
|
|
|
err = fmt.Errorf("Err %s", result.Status)
|
|
|
|
}
|
|
|
|
|
|
|
|
//fmt.Printf("err in response %+v", result)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-12-27 13:44:23 +00:00
|
|
|
func (w *Wallet_Memory) DecodeEncryptedBalance() (err error) {
|
2020-12-19 10:01:29 +00:00
|
|
|
hexdecoded, err := hex.DecodeString(w.account.Balance_Result.Data)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
el := new(crypto.ElGamal).Deserialize(hexdecoded)
|
2020-12-19 10:01:29 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
w.account.Balance_Mature = w.DecodeEncryptedBalanceNow(el)
|
|
|
|
return nil
|
|
|
|
}
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
// decode encrypted balance now
|
|
|
|
// it may take a long time, its currently sing threaded, need to parallelize
|
|
|
|
func (w *Wallet_Memory) DecodeEncryptedBalanceNow(el *crypto.ElGamal) uint64 {
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
balance_point := new(bn256.G1).Add(el.Left, new(bn256.G1).Neg(new(bn256.G1).ScalarMult(el.Right, w.account.Keys.Secret.BigInt())))
|
|
|
|
return Balance_lookup_table.Lookup(balance_point, w.account.Balance_Mature)
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// this is as simple as it gets
|
|
|
|
// single threaded communication gets whether the the key image is spent in pool or in blockchain
|
|
|
|
// this can leak informtion which keyimage belongs to us
|
|
|
|
// TODO in order to stop privacy leaks we must guess this information somehow on client side itself
|
|
|
|
// maybe the server can broadcast a bloomfilter or something else from the mempool keyimages
|
|
|
|
//
|
2021-02-22 17:48:14 +00:00
|
|
|
func (w *Wallet_Memory) GetEncryptedBalanceAtTopoHeight(scid crypto.Hash, topoheight int64, accountaddr string) (bits int, e *crypto.ElGamal, err error) {
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
defer func() {
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
rlog.Warnf("Stack trace \n%s", debug.Stack())
|
|
|
|
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
if !w.GetMode() { // if wallet is in offline mode , we cannot do anything
|
|
|
|
err = fmt.Errorf("wallet is in offline mode")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-12-27 13:44:23 +00:00
|
|
|
if !IsDaemonOnline() {
|
2020-12-19 10:01:29 +00:00
|
|
|
err = fmt.Errorf("offline or not connected")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
//var params rpc.GetEncryptedBalance_Params
|
|
|
|
var result rpc.GetEncryptedBalance_Result
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
// Issue a call with a response.
|
2021-02-22 17:48:14 +00:00
|
|
|
if err = rpc_client.Call("DERO.GetEncryptedBalance", rpc.GetEncryptedBalance_Params{SCID: scid, Address: accountaddr, TopoHeight: topoheight}, &result); err != nil {
|
2020-12-21 15:38:03 +00:00
|
|
|
rlog.Errorf("Call failed: %v", err)
|
2021-02-22 17:48:14 +00:00
|
|
|
|
|
|
|
if strings.Contains(strings.ToLower(err.Error()), strings.ToLower(errormsg.ErrAccountUnregistered.Error())) && accountaddr == w.GetAddress().String() && scid.IsZero() {
|
|
|
|
w.Error = errormsg.ErrAccountUnregistered
|
|
|
|
}
|
2020-12-21 15:38:03 +00:00
|
|
|
return
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
// fmt.Printf("GetEncryptedBalance result %+v\n", result)
|
|
|
|
if scid.IsZero() && accountaddr == w.GetAddress().String() {
|
|
|
|
if result.Status == errormsg.ErrAccountUnregistered.Error() {
|
|
|
|
w.Error = errormsg.ErrAccountUnregistered
|
|
|
|
w.account.Registered = false
|
|
|
|
} else {
|
|
|
|
w.account.Registered = true
|
|
|
|
}
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// fmt.Printf("status '%s' err '%s' %+v %+v \n", result.Status , w.Error , result.Status == errormsg.ErrAccountUnregistered.Error() , accountaddr == w.account.GetAddress().String())
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
if scid.IsZero() && result.Status == errormsg.ErrAccountUnregistered.Error() {
|
2020-12-19 10:01:29 +00:00
|
|
|
err = fmt.Errorf("%s", result.Status)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
w.Daemon_Height = uint64(result.DHeight)
|
|
|
|
w.Daemon_TopoHeight = result.DTopoheight
|
|
|
|
w.Merkle_Balance_TreeHash = result.DMerkle_Balance_TreeHash
|
|
|
|
|
|
|
|
if scid.IsZero() && accountaddr == w.GetAddress().String() {
|
|
|
|
w.account.Balance_Result = result
|
|
|
|
w.account.TopoHeight = result.Topoheight
|
|
|
|
}
|
|
|
|
|
|
|
|
if scid.IsZero() && result.Status != "OK" {
|
2020-12-19 10:01:29 +00:00
|
|
|
err = fmt.Errorf("%s", result.Status)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
hexdecoded, err := hex.DecodeString(result.Data)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
if accountaddr == w.GetAddress().String() && scid.IsZero() {
|
2020-12-19 10:01:29 +00:00
|
|
|
w.Error = nil
|
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
//fmt.Printf("decoding elgamal\n")
|
|
|
|
|
2020-12-19 10:01:29 +00:00
|
|
|
el := new(crypto.ElGamal).Deserialize(hexdecoded)
|
2021-02-22 17:48:14 +00:00
|
|
|
return result.Bits, el, nil
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
|
|
|
|
2020-12-27 13:44:23 +00:00
|
|
|
func (w *Wallet_Memory) DecodeEncryptedBalance_Memory(el *crypto.ElGamal, hint uint64) (balance uint64) {
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
var balance_point bn256.G1
|
|
|
|
|
|
|
|
balance_point.Add(el.Left, new(bn256.G1).Neg(new(bn256.G1).ScalarMult(el.Right, w.account.Keys.Secret.BigInt())))
|
|
|
|
|
|
|
|
return Balance_lookup_table.Lookup(&balance_point, hint)
|
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
func (w *Wallet_Memory) GetDecryptedBalanceAtTopoHeight(scid crypto.Hash, topoheight int64, accountaddr string) (balance uint64, err error) {
|
|
|
|
_, encrypted_balance, err := w.GetEncryptedBalanceAtTopoHeight(scid, topoheight, accountaddr)
|
2020-12-19 10:01:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return w.DecodeEncryptedBalance_Memory(encrypted_balance, 0), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// sync history of wallet from blockchain
|
2021-02-22 17:48:14 +00:00
|
|
|
func (w *Wallet_Memory) random_ring_members(scid crypto.Hash) (alist []string) {
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
//fmt.Printf("getting random_ring_members\n")
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
//if len(w.account.RingMembers) > 300 { // unregistered so skip
|
|
|
|
// return
|
|
|
|
//}
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
var result rpc.GetRandomAddress_Result
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
// Issue a call with a response.
|
2021-02-22 17:48:14 +00:00
|
|
|
if err := rpc_client.Call("DERO.GetRandomAddress", rpc.GetRandomAddress_Params{SCID: scid}, &result); err != nil {
|
2020-12-21 15:38:03 +00:00
|
|
|
rlog.Errorf("GetRandomAddress Call failed: %v", err)
|
2020-12-19 10:01:29 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
//fmt.Printf("ring members %+v\n", result)
|
|
|
|
|
|
|
|
// we have found a matching block hash, start syncing from here
|
2021-02-22 17:48:14 +00:00
|
|
|
//if w.account.RingMembers == nil {
|
|
|
|
//// w.account.RingMembers = map[string]int64{}
|
|
|
|
//}
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
for _, k := range result.Address {
|
|
|
|
if k != w.GetAddress().String() {
|
2021-02-22 17:48:14 +00:00
|
|
|
alist = append(alist, k)
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// sync history of wallet from blockchain
|
2021-02-22 17:48:14 +00:00
|
|
|
func (w *Wallet_Memory) SyncHistory(scid crypto.Hash) (balance uint64) {
|
2020-12-19 10:01:29 +00:00
|
|
|
if w.account.Balance_Result.Registration < 0 { // unregistered so skip
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
last_topo_height := int64(-1)
|
|
|
|
|
|
|
|
//fmt.Printf("finding sync point ( Registration point %d)\n", w.account.Balance_Result.Registration)
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
entries := w.account.EntriesNative[scid]
|
|
|
|
|
2020-12-19 10:01:29 +00:00
|
|
|
// we need to find a sync point, to minimize traffic
|
2021-02-22 17:48:14 +00:00
|
|
|
for i := len(entries) - 1; i >= 0; {
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
// below condition will trigger if chain got pruned on server
|
2021-02-22 17:48:14 +00:00
|
|
|
if w.account.Balance_Result.Registration >= entries[i].TopoHeight { // keep old history if chain got pruned
|
2020-12-19 10:01:29 +00:00
|
|
|
break
|
|
|
|
}
|
2021-02-22 17:48:14 +00:00
|
|
|
if last_topo_height == entries[i].TopoHeight {
|
2020-12-19 10:01:29 +00:00
|
|
|
i--
|
|
|
|
} else {
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
last_topo_height = entries[i].TopoHeight
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
var result rpc.GetBlockHeaderByHeight_Result
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
// Issue a call with a response.
|
2021-02-22 17:48:14 +00:00
|
|
|
if err := rpc_client.Call("DERO.GetBlockHeaderByTopoHeight", rpc.GetBlockHeaderByTopoHeight_Params{TopoHeight: uint64(entries[i].TopoHeight)}, &result); err != nil {
|
2020-12-21 15:38:03 +00:00
|
|
|
rlog.Errorf("GetBlockHeaderByTopoHeight Call failed: %v", err)
|
2020-12-19 10:01:29 +00:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
if i >= 1 && last_topo_height == entries[i-1].TopoHeight { // skipping any entries withing same block
|
2020-12-19 10:01:29 +00:00
|
|
|
for ; i >= 1; i-- {
|
2021-02-22 17:48:14 +00:00
|
|
|
if last_topo_height != entries[i-1].TopoHeight {
|
|
|
|
entries = entries[:i]
|
|
|
|
w.account.EntriesNative[scid] = entries
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if i == 0 {
|
2021-02-22 17:48:14 +00:00
|
|
|
w.account.EntriesNative[scid] = entries[:0] // discard all entries
|
2020-12-19 10:01:29 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// we have found a matching block hash, start syncing from here
|
2021-02-22 17:48:14 +00:00
|
|
|
if result.Status == "OK" && result.Block_Header.Hash == entries[i].BlockHash {
|
|
|
|
w.synchistory_internal(scid, entries[i].TopoHeight+1, w.account.Balance_Result.Topoheight)
|
2020-12-19 10:01:29 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//fmt.Printf("syncing loop using Registration %d\n", w.account.Balance_Result.Registration)
|
|
|
|
|
|
|
|
// if we reached here, means we should sync from scratch
|
2021-02-22 17:48:14 +00:00
|
|
|
w.synchistory_internal(scid, w.account.Balance_Result.Registration, w.account.Balance_Result.Topoheight)
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
//if w.account.Registration >= 0 {
|
|
|
|
// err :=
|
|
|
|
// err = w.synchistory_internal(w.account.Registration,6)
|
|
|
|
|
|
|
|
// }
|
|
|
|
// fmt.Printf("syncing err %s\n",err)
|
|
|
|
// fmt.Printf("entries %+v\n", w.account.Entries)
|
|
|
|
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// sync history
|
2021-02-22 17:48:14 +00:00
|
|
|
func (w *Wallet_Memory) synchistory_internal(scid crypto.Hash, start_topo, end_topo int64) error {
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
var err error
|
|
|
|
var start_balance_e *crypto.ElGamal
|
|
|
|
if start_topo == w.account.Balance_Result.Registration {
|
|
|
|
start_balance_e = crypto.ConstructElGamal(w.account.Keys.Public.G1(), crypto.ElGamal_BASE_G)
|
|
|
|
} else {
|
2021-02-22 17:48:14 +00:00
|
|
|
_, start_balance_e, err = w.GetEncryptedBalanceAtTopoHeight(scid, start_topo, w.GetAddress().String())
|
2020-12-19 10:01:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
_, end_balance_e, err := w.GetEncryptedBalanceAtTopoHeight(scid, end_topo, w.GetAddress().String())
|
2020-12-19 10:01:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
return w.synchistory_internal_binary_search(scid, start_topo, start_balance_e, end_topo, end_balance_e)
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
func (w *Wallet_Memory) synchistory_internal_binary_search(scid crypto.Hash, start_topo int64, start_balance_e *crypto.ElGamal, end_topo int64, end_balance_e *crypto.ElGamal) error {
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
//fmt.Printf("end %d start %d\n", end_topo, start_topo)
|
|
|
|
|
|
|
|
if end_topo < 0 {
|
|
|
|
return fmt.Errorf("done")
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if bytes.Compare(start_balance_e.Serialize(), end_balance_e.Serialize()) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
//for start_topo <= end_topo{
|
|
|
|
{
|
|
|
|
median := (start_topo + end_topo) / 2
|
|
|
|
|
|
|
|
//fmt.Printf("low %d high %d median %d\n", start_topo,end_topo,median)
|
|
|
|
|
|
|
|
if start_topo == median {
|
|
|
|
//fmt.Printf("syncing block %d\n", start_topo)
|
2021-02-22 17:48:14 +00:00
|
|
|
err := w.synchistory_block(scid, start_topo)
|
2020-12-19 10:01:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if end_topo-start_topo <= 1 {
|
2021-02-22 17:48:14 +00:00
|
|
|
return w.synchistory_block(scid, end_topo)
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
_, median_balance_e, err := w.GetEncryptedBalanceAtTopoHeight(scid, median, w.GetAddress().String())
|
2020-12-19 10:01:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if there is a change in lower section, if yes process more
|
|
|
|
if start_topo == w.account.Balance_Result.Registration || bytes.Compare(start_balance_e.Serialize(), median_balance_e.Serialize()) != 0 {
|
|
|
|
//fmt.Printf("lower\n")
|
2021-02-22 17:48:14 +00:00
|
|
|
err = w.synchistory_internal_binary_search(scid, start_topo, start_balance_e, median, median_balance_e)
|
2020-12-19 10:01:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if there is a change in higher section, if yes process more
|
|
|
|
if bytes.Compare(median_balance_e.Serialize(), end_balance_e.Serialize()) != 0 {
|
|
|
|
//fmt.Printf("higher\n")
|
2021-02-22 17:48:14 +00:00
|
|
|
err = w.synchistory_internal_binary_search(scid, median, median_balance_e, end_topo, end_balance_e)
|
2020-12-19 10:01:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*if IsRegisteredAtTopoHeight (addr,median) {
|
|
|
|
high = median - 1
|
|
|
|
}else{
|
|
|
|
low = median + 1
|
|
|
|
}*/
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
if end_topo - start_topo <= 1 {
|
|
|
|
err := w.synchistory_block(start_topo)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return w.synchistory_block(end_topo)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// this means the address is either a ring member or a sender or a receiver in atleast one of the blocks
|
|
|
|
middle := start_topo + (end_topo-start_topo)/2
|
|
|
|
middle_balance_e, err := w.GetEncryptedBalanceAtTopoHeight( middle ,w.account.GetAddress().String())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if there is a change in lower section, if yes process more
|
|
|
|
if bytes.Compare(start_balance_e.Serialize(), middle_balance_e.Serialize()) != 0 {
|
|
|
|
fmt.Printf("lower\n")
|
|
|
|
err = w.synchistory_internal_binary_search(start_topo,start_balance_e, middle, middle_balance_e )
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if there is a change in lower section, if yes process more
|
|
|
|
if bytes.Compare(middle_balance_e.Serialize(), end_balance_e.Serialize()) != 0 {
|
|
|
|
fmt.Printf("higher\n")
|
|
|
|
err = w.synchistory_internal_binary_search(middle, middle_balance_e, end_topo,end_balance_e )
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// extract history from a single block
|
|
|
|
// first get a block, then get all the txs
|
|
|
|
// Todo we should expose an API to get all txs which have the specific address as ring member
|
|
|
|
// for a particular block
|
|
|
|
// for the entire chain
|
2021-02-22 17:48:14 +00:00
|
|
|
func (w *Wallet_Memory) synchistory_block(scid crypto.Hash, topo int64) (err error) {
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
var local_entries []rpc.Entry
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
compressed_address := w.account.Keys.Public.EncodeCompressed()
|
|
|
|
|
|
|
|
var previous_balance_e, current_balance_e *crypto.ElGamal
|
|
|
|
var previous_balance, current_balance, total_sent, total_received uint64
|
|
|
|
|
|
|
|
if topo <= 0 || w.account.Balance_Result.Registration == topo {
|
|
|
|
previous_balance_e = crypto.ConstructElGamal(w.account.Keys.Public.G1(), crypto.ElGamal_BASE_G)
|
|
|
|
} else {
|
2021-02-22 17:48:14 +00:00
|
|
|
_, previous_balance_e, err = w.GetEncryptedBalanceAtTopoHeight(scid, topo-1, w.GetAddress().String())
|
2020-12-19 10:01:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
_, current_balance_e, err = w.GetEncryptedBalanceAtTopoHeight(scid, topo, w.GetAddress().String())
|
2020-12-19 10:01:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
EWData := fmt.Sprintf("%x", current_balance_e.Serialize())
|
|
|
|
|
|
|
|
previous_balance = w.DecodeEncryptedBalance_Memory(previous_balance_e, 0)
|
|
|
|
current_balance = w.DecodeEncryptedBalance_Memory(current_balance_e, 0)
|
|
|
|
|
|
|
|
// we can skip some check if both balances are equal ( means we are ring members in this block)
|
|
|
|
// this check will also fail if we total spend == total receivein the block
|
|
|
|
// currently it is not implmented, and we bruteforce everything
|
|
|
|
|
|
|
|
_ = current_balance
|
|
|
|
|
|
|
|
var bl block.Block
|
2021-02-22 17:48:14 +00:00
|
|
|
var bresult rpc.GetBlock_Result
|
|
|
|
if err = rpc_client.Call("DERO.GetBlock", rpc.GetBlock_Params{Height: uint64(topo)}, &bresult); err != nil {
|
2020-12-19 10:01:29 +00:00
|
|
|
return fmt.Errorf("getblock rpc failed")
|
|
|
|
}
|
|
|
|
|
|
|
|
block_bin, _ := hex.DecodeString(bresult.Blob)
|
|
|
|
bl.Deserialize(block_bin)
|
|
|
|
|
|
|
|
if len(bl.Tx_hashes) >= 1 {
|
|
|
|
|
|
|
|
//fmt.Printf("Requesting tx data %s", txhash);
|
|
|
|
|
|
|
|
for i := range bl.Tx_hashes {
|
|
|
|
var tx transaction.Transaction
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
var tx_params rpc.GetTransaction_Params
|
|
|
|
var tx_result rpc.GetTransaction_Result
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
tx_params.Tx_Hashes = append(tx_params.Tx_Hashes, bl.Tx_hashes[i].String())
|
|
|
|
|
|
|
|
if err = rpc_client.Call("DERO.GetTransaction", tx_params, &tx_result); err != nil {
|
|
|
|
return fmt.Errorf("gettransa rpc failed %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
tx_bin, err := hex.DecodeString(tx_result.Txs_as_hex[0])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-22 17:48:14 +00:00
|
|
|
if err = tx.DeserializeHeader(tx_bin); err != nil {
|
|
|
|
rlog.Warnf("Error deserialing txid %s incoming bytes '%s'", bl.Tx_hashes[i].String(), tx_result.Txs_as_hex[0])
|
|
|
|
continue
|
|
|
|
}
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
if tx.TransactionType == transaction.REGISTRATION {
|
|
|
|
continue
|
|
|
|
}
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
// since balance might change with tx, we track within tx using this
|
|
|
|
previous_balance_e_tx := new(crypto.ElGamal).Deserialize(previous_balance_e.Serialize())
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
for t := range tx.Payloads {
|
|
|
|
if int(tx.Payloads[t].Statement.RingSize) != len(tx_result.Txs[0].Ring[t]) {
|
|
|
|
rlog.Warnf("Error expected %d ringmembers for txid %s but got %d ", int(tx.Payloads[t].Statement.RingSize), bl.Tx_hashes[i].String(), len(tx_result.Txs[t].Ring))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if !tx.Payloads[t].SCID.IsZero() { // skip private tokens for now
|
|
|
|
continue
|
|
|
|
}
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
previous_balance = w.DecodeEncryptedBalanceNow(previous_balance_e_tx)
|
|
|
|
|
|
|
|
for j := 0; j < int(tx.Payloads[t].Statement.RingSize); j++ { // first fill in all the ring members
|
|
|
|
var buf [33]byte
|
|
|
|
copy(buf[:], tx_result.Txs[0].Ring[t][j][:])
|
|
|
|
tx.Payloads[t].Statement.Publickeylist_compressed = append(tx.Payloads[t].Statement.Publickeylist_compressed, buf)
|
|
|
|
}
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
for j := 0; j < int(tx.Payloads[t].Statement.RingSize); j++ { // check whether statement has public key
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
// check whether our address is a ring member if yes, process it as ours
|
|
|
|
if bytes.Compare(compressed_address, tx.Payloads[t].Statement.Publickeylist_compressed[j][:]) == 0 {
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
// this tx contains us either as a ring member, or sender or receiver, so add all members as ring members for future
|
|
|
|
// keep collecting ring members to make things exponentially complex
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
for k := range tx.Payloads[t].Statement.Publickeylist_compressed {
|
|
|
|
var p bn256.G1
|
|
|
|
if err = p.DecodeCompressed(tx.Payloads[t].Statement.Publickeylist_compressed[k][:]); err != nil {
|
|
|
|
fmt.Printf("key could not be decompressed")
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
} else {
|
|
|
|
tx.Payloads[t].Statement.Publickeylist = append(tx.Payloads[t].Statement.Publickeylist, &p)
|
|
|
|
}
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
/*for k := range tx.Statement.Publickeylist_compressed {
|
|
|
|
if j != k {
|
|
|
|
ringmember := address.NewAddressFromKeys((*crypto.Point)(tx.Statement.Publickeylist[k]))
|
|
|
|
ringmember.Mainnet = w.GetNetwork()
|
|
|
|
w.account.RingMembers[ringmember.String()] = 1
|
|
|
|
}
|
|
|
|
}*/
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
changes := crypto.ConstructElGamal(tx.Payloads[t].Statement.C[j], tx.Payloads[t].Statement.D)
|
|
|
|
changed_balance_e := previous_balance_e_tx.Add(changes)
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
previous_balance_e_tx = new(crypto.ElGamal).Deserialize(changed_balance_e.Serialize())
|
|
|
|
|
|
|
|
changed_balance := w.DecodeEncryptedBalance_Memory(changed_balance_e, previous_balance)
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
//fmt.Printf("%d changed_balance %d previous_balance %d len payload %d\n", t, changed_balance, previous_balance, len(tx.Payloads[t].RPCPayload))
|
|
|
|
|
|
|
|
entry := rpc.Entry{Height: bl.Height, Pos: t, TopoHeight: topo, BlockHash: bl.GetHash().String(), TransactionPos: i, TXID: tx.GetHash().String(), Time: time.Unix(int64(bl.Timestamp), 0), Fees: tx.Fees()}
|
|
|
|
|
|
|
|
entry.EWData = EWData
|
|
|
|
ring_member := false
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case previous_balance == changed_balance: //ring member/* handle 0 value tx but fees is deducted */
|
|
|
|
//fmt.Printf("Anon Ring Member in TX %s\n", bl.Tx_hashes[i].String())
|
|
|
|
ring_member = true
|
|
|
|
case previous_balance > changed_balance: // we generated this tx
|
|
|
|
entry.Burn = tx.Payloads[t].BurnValue
|
|
|
|
entry.Amount = previous_balance - changed_balance - (tx.Payloads[t].Statement.Fees)
|
|
|
|
entry.Fees = tx.Payloads[t].Statement.Fees
|
|
|
|
entry.Status = 1 // mark it as spend
|
|
|
|
total_sent += entry.Amount + entry.Fees // burn is in amount
|
|
|
|
|
|
|
|
rinputs := append([]byte{}, tx.Payloads[t].Statement.Roothash[:]...)
|
|
|
|
for l := range tx.Payloads[t].Statement.Publickeylist_compressed {
|
|
|
|
rinputs = append(rinputs, tx.Payloads[t].Statement.Publickeylist_compressed[l][:]...)
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
2021-02-22 17:48:14 +00:00
|
|
|
rencrypted := new(bn256.G1).ScalarMult(crypto.HashToPoint(crypto.HashtoNumber(append([]byte(crypto.PROTOCOL_CONSTANT), rinputs...))), w.account.Keys.Secret.BigInt())
|
|
|
|
r := crypto.ReducedHash(rencrypted.EncodeCompressed())
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
//fmt.Printf("t %d r calculated %s\n", t, r.Text(16))
|
|
|
|
// lets separate ring members
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
for k := range tx.Payloads[t].Statement.C {
|
|
|
|
// skip self address, this can be optimized way more
|
|
|
|
if tx.Payloads[t].Statement.Publickeylist[k].String() != w.account.Keys.Public.G1().String() {
|
|
|
|
var x bn256.G1
|
|
|
|
x.ScalarMult(crypto.G, new(big.Int).SetInt64(int64(entry.Amount-entry.Burn)))
|
|
|
|
x.Add(new(bn256.G1).Set(&x), new(bn256.G1).ScalarMult(tx.Payloads[t].Statement.Publickeylist[k], r))
|
|
|
|
if x.String() == tx.Payloads[t].Statement.C[k].String() {
|
|
|
|
// lets encrypt the payment id, it's simple, we XOR the paymentID
|
|
|
|
blinder := new(bn256.G1).ScalarMult(tx.Payloads[t].Statement.Publickeylist[k], r)
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
// proof is blinder + amount transferred, it will recover the encrypted rpc payload also also
|
|
|
|
proof := rpc.NewAddressFromKeys((*crypto.Point)(blinder))
|
|
|
|
proof.Proof = true
|
|
|
|
proof.Arguments = rpc.Arguments{{rpc.RPC_VALUE_TRANSFER, rpc.DataUint64, uint64(entry.Amount - entry.Burn)}}
|
|
|
|
entry.Proof = proof.String()
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
entry.PayloadType = tx.Payloads[t].RPCType
|
|
|
|
switch tx.Payloads[t].RPCType {
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
case 0:
|
|
|
|
crypto.EncryptDecryptUserData(blinder, tx.Payloads[t].RPCPayload)
|
|
|
|
sender_idx := uint(tx.Payloads[t].RPCPayload[0])
|
|
|
|
|
|
|
|
if sender_idx <= uint(tx.Payloads[t].Statement.RingSize) {
|
|
|
|
addr := rpc.NewAddressFromKeys((*crypto.Point)(tx.Payloads[t].Statement.Publickeylist[sender_idx]))
|
|
|
|
addr.Mainnet = w.GetNetwork()
|
|
|
|
entry.Sender = addr.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
entry.Payload = append(entry.Payload, tx.Payloads[t].RPCPayload[1:]...)
|
|
|
|
entry.Data = append(entry.Data, tx.Payloads[t].RPCPayload[:]...)
|
|
|
|
|
|
|
|
args, _ := entry.ProcessPayload()
|
|
|
|
_ = args
|
|
|
|
|
|
|
|
// fmt.Printf("data received %s idx %d arguments %s\n", string(entry.Payload), sender_idx, args)
|
|
|
|
|
|
|
|
default:
|
|
|
|
entry.PayloadError = fmt.Sprintf("unknown payload type %d", tx.Payloads[t].RPCType)
|
|
|
|
entry.Payload = tx.Payloads[t].RPCPayload
|
|
|
|
}
|
|
|
|
|
|
|
|
//paymentID := binary.BigEndian.Uint64(payment_id_encrypted_bytes[:]) // get decrypted payment id
|
|
|
|
|
|
|
|
addr := rpc.NewAddressFromKeys((*crypto.Point)(tx.Payloads[t].Statement.Publickeylist[k]))
|
|
|
|
addr.Mainnet = w.GetNetwork()
|
|
|
|
|
|
|
|
entry.Destination = addr.String()
|
|
|
|
|
|
|
|
//fmt.Printf("%d Sent funds to %s entry %+v\n", tx.Height, addr.String(), entry)
|
|
|
|
break
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case previous_balance < changed_balance: // someone sentus this amount
|
|
|
|
entry.Amount = changed_balance - previous_balance
|
|
|
|
entry.Incoming = true
|
|
|
|
|
|
|
|
// we should decode the payment id
|
|
|
|
var x bn256.G1
|
|
|
|
x.ScalarMult(crypto.G, new(big.Int).SetInt64(0-int64(entry.Amount))) // increase receiver's balance
|
|
|
|
x.Add(new(bn256.G1).Set(&x), tx.Payloads[t].Statement.C[j]) // get the blinder
|
|
|
|
|
|
|
|
blinder := &x
|
|
|
|
|
|
|
|
// enable receiver side proofs
|
|
|
|
proof := rpc.NewAddressFromKeys((*crypto.Point)(blinder))
|
|
|
|
proof.Proof = true
|
|
|
|
proof.Arguments = rpc.Arguments{{rpc.RPC_VALUE_TRANSFER, rpc.DataUint64, uint64(entry.Amount)}}
|
|
|
|
entry.Proof = proof.String()
|
|
|
|
|
|
|
|
entry.PayloadType = tx.Payloads[t].RPCType
|
|
|
|
switch tx.Payloads[t].RPCType {
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
case 0:
|
|
|
|
crypto.EncryptDecryptUserData(blinder, tx.Payloads[t].RPCPayload)
|
|
|
|
sender_idx := uint(tx.Payloads[t].RPCPayload[0])
|
2020-12-19 10:01:29 +00:00
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
if sender_idx <= uint(tx.Payloads[t].Statement.RingSize) {
|
|
|
|
addr := rpc.NewAddressFromKeys((*crypto.Point)(tx.Payloads[t].Statement.Publickeylist[sender_idx]))
|
|
|
|
addr.Mainnet = w.GetNetwork()
|
|
|
|
entry.Sender = addr.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
entry.Payload = append(entry.Payload, tx.Payloads[t].RPCPayload[1:]...)
|
|
|
|
entry.Data = append(entry.Data, tx.Payloads[t].RPCPayload[:]...)
|
|
|
|
|
|
|
|
args, _ := entry.ProcessPayload()
|
|
|
|
_ = args
|
|
|
|
|
|
|
|
// fmt.Printf("data received %s idx %d arguments %s\n", string(entry.Payload), sender_idx, args)
|
|
|
|
|
|
|
|
default:
|
|
|
|
entry.PayloadError = fmt.Sprintf("unknown payload type %d", tx.Payloads[t].RPCType)
|
|
|
|
entry.Payload = tx.Payloads[t].RPCPayload
|
|
|
|
}
|
|
|
|
|
|
|
|
//fmt.Printf("Received %s amount in TX(%d) %s payment id %x Src_ID %s data %s\n", globals.FormatMoney(changed_balance-previous_balance), tx.Height, bl.Tx_hashes[i].String(), entry.PaymentID, tx.Src_ID, tx.Data)
|
|
|
|
//fmt.Printf("Received amount in TX(%d) %s payment id %x Src_ID %s data %s\n", tx.Height, bl.Tx_hashes[i].String(), entry.PaymentID, tx.SrcID, tx.Data)
|
|
|
|
total_received += (changed_balance - previous_balance)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !ring_member { // do not book keep ring members
|
|
|
|
local_entries = append(local_entries, entry)
|
|
|
|
}
|
|
|
|
|
|
|
|
//break // this tx has been processed so skip it
|
|
|
|
|
|
|
|
}
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
//fmt.Printf("block %d %+v\n", topo, tx_result)
|
|
|
|
}
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
|
|
|
|
2021-02-22 17:48:14 +00:00
|
|
|
previous_balance = w.DecodeEncryptedBalance_Memory(previous_balance_e, 0)
|
|
|
|
coinbase_reward := current_balance - (previous_balance - total_sent + total_received)
|
|
|
|
|
|
|
|
//fmt.Printf("ht %d coinbase_reward %d curent balance %d previous_balance %d sent %d received %d\n", bl.Height, coinbase_reward, current_balance, previous_balance, total_sent, total_received)
|
|
|
|
|
|
|
|
if bytes.Compare(compressed_address, bl.Miner_TX.MinerAddress[:]) == 0 || coinbase_reward > 0 { // wallet user has minted a block
|
|
|
|
entry := rpc.Entry{Height: bl.Height, TopoHeight: topo, BlockHash: bl.GetHash().String(), TransactionPos: -1, Time: time.Unix(int64(bl.Timestamp), 0)}
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
entry.EWData = EWData
|
|
|
|
entry.Amount = current_balance - (previous_balance - total_sent + total_received)
|
|
|
|
entry.Coinbase = true
|
2021-02-22 17:48:14 +00:00
|
|
|
local_entries = append([]rpc.Entry{entry}, local_entries...)
|
2020-12-19 10:01:29 +00:00
|
|
|
|
|
|
|
//fmt.Printf("Coinbase Reward %s for block %d\n", globals.FormatMoney(current_balance-(previous_balance-total_sent+total_received)), topo)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, e := range local_entries {
|
2021-02-22 17:48:14 +00:00
|
|
|
w.InsertReplace(scid, e)
|
2020-12-19 10:01:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(local_entries) >= 1 {
|
2021-02-22 17:48:14 +00:00
|
|
|
w.save_if_disk() // save wallet()
|
2020-12-19 10:01:29 +00:00
|
|
|
// w.db.Sync()
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|