func (wallet *EthereumWallet) BumpFee(txid chainhash.Hash) (*chainhash.Hash, error) {
	return util.CreateChainHash(txid.String())
}
BumpFee - Bump the fee for the given transaction
package wallet
import (
	"bytes"
	"context"
	"crypto/ecdsa"
	"encoding/binary"
	"encoding/gob"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/big"
	"net/http"
	"path"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"time"
	"github.com/OpenBazaar/multiwallet/config"
	wi "github.com/OpenBazaar/wallet-interface"
	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcutil"
	hd "github.com/btcsuite/btcutil/hdkeychain"
	"github.com/ethereum/go-ethereum/accounts/abi/bind"
	"github.com/ethereum/go-ethereum/accounts/keystore"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/nanmu42/etherscan-api"
	"github.com/op/go-logging"
	"golang.org/x/net/proxy"
	"gopkg.in/yaml.v2"
	"github.com/OpenBazaar/go-ethwallet/util"
	ut "github.com/OpenBazaar/openbazaar-go/util"
)
var _ = wi.Wallet(&EthereumWallet{})
var done, doneBalanceTicker chan bool
const (
	InfuraAPIKey = "v3/91c82af0169c4115940c76d331410749"
	EtherScanAPIKey = "KA15D8FCHGBFZ4CQ25Y4NZM24417AXWF7M"
	maxGasLimit     = 400000
)
var (
	emptyChainHash *chainhash.Hash
	EthCurrencyDefinition = wi.CurrencyDefinition{
		Code:         "ETH",
		Divisibility: 18,
	}
	log = logging.MustGetLogger("ethwallet")
)
func init() {
	mustInitEmptyChainHash()
}
func mustInitEmptyChainHash() {
	hash, err := chainhash.NewHashFromStr("")
	if err != nil {
		panic(fmt.Sprintf("creating emptyChainHash: %s", err.Error()))
	}
	emptyChainHash = hash
}
type EthConfiguration struct {
	RopstenPPAddress string `yaml:"ROPSTEN_PPv2_ADDRESS"`
	RegistryAddress  string `yaml:"ROPSTEN_REGISTRY"`
}
type EthRedeemScript struct {
	TxnID           common.Address
	Threshold       uint8
	Timeout         uint32
	Buyer           common.Address
	Seller          common.Address
	Moderator       common.Address
	MultisigAddress common.Address
	TokenAddress    common.Address
}
func SerializeEthScript(scrpt EthRedeemScript) ([]byte, error) {
	b := bytes.Buffer{}
	e := gob.NewEncoder(&b)
	err := e.Encode(scrpt)
	return b.Bytes(), err
}
func DeserializeEthScript(b []byte) (EthRedeemScript, error) {
	scrpt := EthRedeemScript{}
	buf := bytes.NewBuffer(b)
	d := gob.NewDecoder(buf)
	err := d.Decode(&scrpt)
	return scrpt, err
}
type PendingTxn struct {
	TxnID     common.Hash
	OrderID   string
	Amount    string
	Nonce     int32
	From      string
	To        string
	WithInput bool
}
func SerializePendingTxn(pTxn PendingTxn) ([]byte, error) {
	b := bytes.Buffer{}
	e := gob.NewEncoder(&b)
	err := e.Encode(pTxn)
	return b.Bytes(), err
}
func DeserializePendingTxn(b []byte) (PendingTxn, error) {
	pTxn := PendingTxn{}
	buf := bytes.NewBuffer(b)
	d := gob.NewDecoder(buf)
	err := d.Decode(&pTxn)
	return pTxn, err
}
func GenScriptHash(script EthRedeemScript) ([32]byte, string, error) {
	a := make([]byte, 4)
	binary.BigEndian.PutUint32(a, script.Timeout)
	arr := append(script.TxnID.Bytes(), append([]byte{script.Threshold},
		append(a[:], append(script.Buyer.Bytes(),
			append(script.Seller.Bytes(), append(script.Moderator.Bytes(),
				append(script.MultisigAddress.Bytes())...)...)...)...)...)...)
	var retHash [32]byte
	copy(retHash[:], crypto.Keccak256(arr)[:])
	ahashStr := hexutil.Encode(retHash[:])
	return retHash, ahashStr, nil
}
type EthereumWallet struct {
	client        *EthClient
	account       *Account
	address       *EthAddress
	service       *Service
	registry      *Registry
	ppsct         *Escrow
	db            wi.Datastore
	exchangeRates wi.ExchangeRates
	params        *chaincfg.Params
	listeners     []func(wi.TransactionCallback)
}
func NewEthereumWalletWithKeyfile(url, keyFile, passwd string) *EthereumWallet {
	client, err := NewEthClient(url)
	if err != nil {
		log.Fatalf("error initializing wallet: %v", err)
	}
	var myAccount *Account
	myAccount, err = NewAccountFromKeyfile(keyFile, passwd)
	if err != nil {
		log.Fatalf("key file validation failed: %s", err.Error())
	}
	addr := myAccount.Address()
	_, filename, _, _ := runtime.Caller(0)
	conf, err := ioutil.ReadFile(path.Join(path.Dir(filename), "../configuration.yaml"))
	if err != nil {
		log.Fatalf("ethereum config not found: %s", err.Error())
	}
	ethConfig := EthConfiguration{}
	err = yaml.Unmarshal(conf, &ethConfig)
	if err != nil {
		log.Fatalf("ethereum config not valid: %s", err.Error())
	}
	reg, err := NewRegistry(common.HexToAddress(ethConfig.RegistryAddress), client)
	if err != nil {
		log.Fatalf("error initilaizing contract failed: %s", err.Error())
	}
	return &EthereumWallet{client, myAccount, &EthAddress{&addr}, &Service{}, reg, nil, nil, nil, nil, []func(wi.TransactionCallback){}}
}
func NewEthereumWallet(cfg config.CoinConfig, params *chaincfg.Params, mnemonic string, proxy proxy.Dialer) (*EthereumWallet, error) {
	client, err := NewEthClient(cfg.ClientAPIs[0] + "/" + InfuraAPIKey)
	if err != nil {
		log.Errorf("error initializing wallet: %v", err)
		return nil, err
	}
	var myAccount *Account
	myAccount, err = NewAccountFromMnemonic(mnemonic, "", params)
	if err != nil {
		log.Errorf("mnemonic based pk generation failed: %s", err.Error())
		return nil, err
	}
	addr := myAccount.Address()
	ethConfig := EthConfiguration{}
	var regAddr interface{}
	var ok bool
	registryKey := "RegistryAddress"
	if strings.Contains(cfg.ClientAPIs[0], "rinkeby") {
		registryKey = "RinkebyRegistryAddress"
	} else if strings.Contains(cfg.ClientAPIs[0], "ropsten") {
		registryKey = "RopstenRegistryAddress"
	}
	if regAddr, ok = cfg.Options[registryKey]; !ok {
		log.Errorf("ethereum registry not found: %s", cfg.Options[registryKey])
		return nil, err
	}
	ethConfig.RegistryAddress = regAddr.(string)
	reg, err := NewRegistry(common.HexToAddress(ethConfig.RegistryAddress), client)
	if err != nil {
		log.Errorf("error initilaizing contract failed: %s", err.Error())
		return nil, err
	}
	er := NewEthereumPriceFetcher(proxy)
	return &EthereumWallet{client, myAccount, &EthAddress{&addr}, &Service{}, reg, nil, cfg.DB, er, params, []func(wi.TransactionCallback){}}, nil
}
func (wallet *EthereumWallet) Params() *chaincfg.Params {
	return wallet.params
}
func (wallet *EthereumWallet) GetBalance() (*big.Int, error) {
	return wallet.client.GetBalance(wallet.account.Address())
}
func (wallet *EthereumWallet) GetUnconfirmedBalance() (*big.Int, error) {
	return wallet.client.GetUnconfirmedBalance(wallet.account.Address())
}
func (wallet *EthereumWallet) Transfer(to string, value *big.Int, spendAll bool, fee big.Int) (common.Hash, error) {
	toAddress := common.HexToAddress(to)
	return wallet.client.Transfer(wallet.account, toAddress, value, spendAll, fee)
}
func (wallet *EthereumWallet) Start() {
	done = make(chan bool)
	doneBalanceTicker = make(chan bool)
	go func(wallet *EthereumWallet) {
		ticker := time.NewTicker(5 * time.Second)
		defer ticker.Stop()
		for {
			select {
			case <-done:
				return
			case <-ticker.C:
				txns, err := wallet.db.Txns().GetAll(true)
				if err != nil {
					continue
				}
				for _, txn := range txns {
					hash := common.HexToHash(txn.Txid)
					go func(txnData []byte) {
						_, err := wallet.checkTxnRcpt(&hash, txnData)
						if err != nil {
							log.Errorf(err.Error())
						}
					}(txn.Bytes)
				}
			}
		}
	}(wallet)
	go func(wallet *EthereumWallet) {
		ticker := time.NewTicker(15 * time.Second)
		defer ticker.Stop()
		currentBalance, err := wallet.GetBalance()
		if err != nil {
			log.Infof("err fetching initial balance: %v", err)
		}
		currentTip, _ := wallet.ChainTip()
		for {
			select {
			case <-doneBalanceTicker:
				return
			case <-ticker.C:
				fetchedBalance, err := wallet.GetBalance()
				if err != nil {
					log.Infof("err fetching balance at %v: %v", time.Now(), err)
					continue
				}
				if fetchedBalance.Cmp(currentBalance) != 0 {
					go wallet.processBalanceChange(currentBalance, fetchedBalance, currentTip)
					currentTip, _ = wallet.ChainTip()
					currentBalance = fetchedBalance
				}
			}
		}
	}(wallet)
}
func (wallet *EthereumWallet) processBalanceChange(previousBalance, currentBalance *big.Int, currentHead uint32) {
	count := 0
	cTip := int(currentHead)
	value := new(big.Int).Sub(currentBalance, previousBalance)
	for count < 30 {
		txns, err := wallet.TransactionsFromBlock(&cTip)
		if err == nil && len(txns) > 0 {
			count = 30
			txncb := wi.TransactionCallback{
				Txid:      util.EnsureCorrectPrefix(txns[0].Txid),
				Outputs:   []wi.TransactionOutput{},
				Inputs:    []wi.TransactionInput{},
				Height:    txns[0].Height,
				Timestamp: time.Now(),
				Value:     *value,
				WatchOnly: false,
			}
			for _, l := range wallet.listeners {
				go l(txncb)
			}
			continue
		}
		time.Sleep(2 * time.Second)
		count++
	}
}
func (wallet *EthereumWallet) invokeTxnCB(txnID string, value *big.Int) {
	txncb := wi.TransactionCallback{
		Txid:      util.EnsureCorrectPrefix(txnID),
		Outputs:   []wi.TransactionOutput{},
		Inputs:    []wi.TransactionInput{},
		Height:    0,
		Timestamp: time.Now(),
		Value:     *value,
		WatchOnly: false,
	}
	for _, l := range wallet.listeners {
		go l(txncb)
	}
}
func (wallet *EthereumWallet) CurrencyCode() string {
	if wallet.params == nil {
		return "ETH"
	}
	if wallet.params.Name == chaincfg.MainNetParams.Name {
		return "ETH"
	} else {
		return "TETH"
	}
}
func (wallet *EthereumWallet) IsDust(amount big.Int) bool {
	return amount.Cmp(big.NewInt(10000)) <= 0
}
func (wallet *EthereumWallet) MasterPrivateKey() *hd.ExtendedKey {
	return hd.NewExtendedKey([]byte{0x00, 0x00, 0x00, 0x00}, wallet.account.privateKey.D.Bytes(),
		wallet.account.address.Bytes(), wallet.account.address.Bytes(), 0, 0, true)
}
func (wallet *EthereumWallet) MasterPublicKey() *hd.ExtendedKey {
	publicKey := wallet.account.privateKey.Public()
	publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
	if !ok {
		log.Fatal("error casting public key to ECDSA")
	}
	publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA)
	return hd.NewExtendedKey([]byte{0x00, 0x00, 0x00, 0x00}, publicKeyBytes,
		wallet.account.address.Bytes(), wallet.account.address.Bytes(), 0, 0, false)
}
func (wallet *EthereumWallet) ChildKey(keyBytes []byte, chaincode []byte, isPrivateKey bool) (*hd.ExtendedKey, error) {
	parentFP := []byte{0x00, 0x00, 0x00, 0x00}
	version := []byte{0x04, 0x88, 0xad, 0xe4}  
	if !isPrivateKey {
		version = []byte{0x04, 0x88, 0xb2, 0x1e}
	}
	return hd.NewExtendedKey(version, keyBytes, chaincode, parentFP, 0, 0, isPrivateKey), nil
}
func (wallet *EthereumWallet) CurrentAddress(purpose wi.KeyPurpose) btcutil.Address {
	return *wallet.address
}
func (wallet *EthereumWallet) NewAddress(purpose wi.KeyPurpose) btcutil.Address {
	return *wallet.address
}
func (wallet *EthereumWallet) DecodeAddress(addr string) (btcutil.Address, error) {
	var (
		ethAddr common.Address
		err     error
	)
	if len(addr) > 64 {
		ethAddr, err = ethScriptToAddr(addr)
		if err != nil {
			log.Error(err.Error())
		}
	} else {
		ethAddr = common.HexToAddress(addr)
	}
	return EthAddress{&ethAddr}, err
}
func ethScriptToAddr(addr string) (common.Address, error) {
	rScriptBytes, err := hex.DecodeString(addr)
	if err != nil {
		return common.Address{}, err
	}
	rScript, err := DeserializeEthScript(rScriptBytes)
	if err != nil {
		return common.Address{}, err
	}
	_, sHash, err := GenScriptHash(rScript)
	if err != nil {
		return common.Address{}, err
	}
	return common.HexToAddress(sHash), nil
}
func (wallet *EthereumWallet) ScriptToAddress(script []byte) (btcutil.Address, error) {
	return wallet.address, nil
}
func (wallet *EthereumWallet) HasKey(addr btcutil.Address) bool {
	if !util.IsValidAddress(addr.String()) {
		return false
	}
	return wallet.account.Address().String() == addr.String()
}
func (wallet *EthereumWallet) Balance() (confirmed, unconfirmed wi.CurrencyValue) {
	var balance, ucbalance wi.CurrencyValue
	bal, err := wallet.GetBalance()
	if err == nil {
		balance = wi.CurrencyValue{
			Value:    *bal,
			Currency: EthCurrencyDefinition,
		}
	}
	ucbal, err := wallet.GetUnconfirmedBalance()
	ucb := big.NewInt(0)
	if err == nil {
		if ucbal.Cmp(bal) > 0 {
			ucb.Sub(ucbal, bal)
		}
	}
	ucbalance = wi.CurrencyValue{
		Value:    *ucb,
		Currency: EthCurrencyDefinition,
	}
	return balance, ucbalance
}
func (wallet *EthereumWallet) TransactionsFromBlock(startBlock *int) ([]wi.Txn, error) {
	txns, err := wallet.client.eClient.NormalTxByAddress(util.EnsureCorrectPrefix(wallet.account.Address().String()), startBlock, nil,
		1, 0, false)
	if err != nil {
		log.Error("err fetching transactions : ", err)
		return []wi.Txn{}, nil
	}
	ret := []wi.Txn{}
	for _, t := range txns {
		status := wi.StatusConfirmed
		prefix := ""
		if t.IsError != 0 {
			status = wi.StatusError
		}
		if strings.ToLower(t.From) == strings.ToLower(wallet.address.String()) {
			prefix = "-"
		}
		tnew := wi.Txn{
			Txid:          util.EnsureCorrectPrefix(t.Hash),
			Value:         prefix + t.Value.Int().String(),
			Height:        int32(t.BlockNumber),
			Timestamp:     t.TimeStamp.Time(),
			WatchOnly:     false,
			Confirmations: int64(t.Confirmations),
			Status:        wi.StatusCode(status),
			Bytes:         []byte(t.Input),
		}
		ret = append(ret, tnew)
	}
	return ret, nil
}
func (wallet *EthereumWallet) Transactions() ([]wi.Txn, error) {
	return wallet.TransactionsFromBlock(nil)
}
func (wallet *EthereumWallet) GetTransaction(txid chainhash.Hash) (wi.Txn, error) {
	tx, _, err := wallet.client.GetTransaction(common.HexToHash(util.EnsureCorrectPrefix(txid.String())))
	if err != nil {
		return wi.Txn{}, err
	}
	chainID, err := wallet.client.NetworkID(context.Background())
	if err != nil {
		return wi.Txn{}, err
	}
	msg, err := tx.AsMessage(types.NewEIP155Signer(chainID))  
	if err != nil {
		return wi.Txn{}, err
	}
	fromAddr := msg.From()
	toAddr := msg.To()
	valueSub := big.NewInt(5000000)
	v, err := wallet.registry.GetRecommendedVersion(nil, "escrow")
	if err == nil {
		if tx.To().String() == v.Implementation.String() {
			toAddr = wallet.address.address
		}
		if msg.Value().Cmp(valueSub) > 0 {
			valueSub = msg.Value()
		}
	}
	return wi.Txn{
		Txid:        util.EnsureCorrectPrefix(tx.Hash().Hex()),
		Value:       tx.Value().String(),
		Height:      0,
		Timestamp:   time.Now(),
		WatchOnly:   false,
		Bytes:       tx.Data(),
		ToAddress:   util.EnsureCorrectPrefix(tx.To().String()),
		FromAddress: util.EnsureCorrectPrefix(msg.From().Hex()),
		Outputs: []wi.TransactionOutput{
			{
				Address: EthAddress{toAddr},
				Value:   *valueSub,
				Index:   0,
			},
			{
				Address: EthAddress{&fromAddr},
				Value:   *valueSub,
				Index:   1,
			},
			{
				Address: EthAddress{msg.To()},
				Value:   *valueSub,
				Index:   2,
			},
		},
	}, nil
}
func (wallet *EthereumWallet) ChainTip() (uint32, chainhash.Hash) {
	num, hash, err := wallet.client.GetLatestBlock()
	if err != nil {
		return 0, *emptyChainHash
	}
	h, err := util.CreateChainHash(hash.Hex())
	if err != nil {
		log.Error(err.Error())
		h = emptyChainHash
	}
	return num, *h
}
func (wallet *EthereumWallet) GetFeePerByte(feeLevel wi.FeeLevel) big.Int {
	est, err := wallet.client.GetEthGasStationEstimate()
	ret := big.NewInt(0)
	if err != nil {
		log.Errorf("err fetching ethgas station data: %v", err)
		return *ret
	}
	switch feeLevel {
	case wi.NORMAL:
		ret, _ = big.NewFloat(est.Average * 100000000).Int(nil)
	case wi.ECONOMIC, wi.SUPER_ECONOMIC:
		ret, _ = big.NewFloat(est.SafeLow * 100000000).Int(nil)
	case wi.PRIOIRTY, wi.FEE_BUMP:
		ret, _ = big.NewFloat(est.Fast * 100000000).Int(nil)
	}
	return *ret
}
func (wallet *EthereumWallet) Spend(amount big.Int, addr btcutil.Address, feeLevel wi.FeeLevel, referenceID string, spendAll bool) (*chainhash.Hash, error) {
	var hash common.Hash
	var h *chainhash.Hash
	var err error
	actualRecipient := addr
	if referenceID == "" {
		hash, err = wallet.Transfer(util.EnsureCorrectPrefix(addr.String()), &amount, spendAll, wallet.GetFeePerByte(feeLevel))
	} else {
		scripts, err := wallet.db.WatchedScripts().GetAll()
		if err != nil {
			return nil, err
		}
		isScript := false
		addrEth := common.HexToAddress(addr.String())
		key := addrEth.Bytes()
		redeemScript := []byte{}
		for _, script := range scripts {
			if bytes.Equal(key, script[:common.AddressLength]) {
				isScript = true
				redeemScript = script[common.AddressLength:]
				break
			}
		}
		if isScript {
			ethScript, err := DeserializeEthScript(redeemScript)
			if err != nil {
				return nil, err
			}
			_, scrHash, err := GenScriptHash(ethScript)
			if err != nil {
				log.Error(err.Error())
			}
			addrScrHash := common.HexToAddress(scrHash)
			actualRecipient = EthAddress{address: &addrScrHash}
			hash, _, err = wallet.callAddTransaction(ethScript, &amount, feeLevel)
			if err != nil {
				log.Errorf("error call add txn: %v", err)
				return nil, wi.ErrInsufficientFunds
			}
		} else {
			if !wallet.balanceCheck(feeLevel, amount) {
				return nil, wi.ErrInsufficientFunds
			}
			hash, err = wallet.Transfer(util.EnsureCorrectPrefix(addr.String()), &amount, spendAll, wallet.GetFeePerByte(feeLevel))
		}
		if err != nil {
			return nil, err
		}
		nonce, err := wallet.client.GetTxnNonce(util.EnsureCorrectPrefix(hash.Hex()))
		if err == nil {
			data, err := SerializePendingTxn(PendingTxn{
				TxnID:     hash,
				Amount:    amount.String(),
				OrderID:   referenceID,
				Nonce:     nonce,
				From:      wallet.address.EncodeAddress(),
				To:        actualRecipient.EncodeAddress(),
				WithInput: false,
			})
			if err == nil {
				err0 := wallet.db.Txns().Put(data, ut.NormalizeAddress(hash.Hex()), "0", 0, time.Now(), true)
				if err0 != nil {
					log.Error(err0.Error())
				}
			}
		}
	}
	if err == nil {
		h, err = util.CreateChainHash(hash.Hex())
		if err == nil {
			wallet.invokeTxnCB(h.String(), &amount)
		}
	}
	return h, err
}
func (wallet *EthereumWallet) createTxnCallback(txID, orderID string, toAddress btcutil.Address, value big.Int, bTime time.Time, withInput bool) wi.TransactionCallback {
	output := wi.TransactionOutput{
		Address: toAddress,
		Value:   value,
		Index:   1,
		OrderID: orderID,
	}
	input := wi.TransactionInput{}
	if withInput {
		input = wi.TransactionInput{
			OutpointHash:  []byte(util.EnsureCorrectPrefix(txID)),
			OutpointIndex: 1,
			LinkedAddress: toAddress,
			Value:         value,
			OrderID:       orderID,
		}
	}
	return wi.TransactionCallback{
		Txid:      util.EnsureCorrectPrefix(txID),
		Outputs:   []wi.TransactionOutput{output},
		Inputs:    []wi.TransactionInput{input},
		Height:    1,
		Timestamp: time.Now(),
		Value:     value,
		WatchOnly: false,
		BlockTime: bTime,
	}
}
func (wallet *EthereumWallet) AssociateTransactionWithOrder(txnCB wi.TransactionCallback) {
	for _, l := range wallet.listeners {
		go l(txnCB)
	}
}
func (wallet *EthereumWallet) checkTxnRcpt(hash *common.Hash, data []byte) (*common.Hash, error) {
	var rcpt *types.Receipt
	pTxn, err := DeserializePendingTxn(data)
	if err != nil {
		return nil, err
	}
	rcpt, err = wallet.client.TransactionReceipt(context.Background(), *hash)
	if err != nil {
		log.Infof("fetching txn rcpt: %v", err)
	}
	if rcpt != nil {
		if rcpt.Status > 0 {
			chash, err := util.CreateChainHash((*hash).Hex())
			if err != nil {
				return nil, err
			}
			err = wallet.db.Txns().Delete(chash)
			if err != nil {
				log.Errorf("err deleting the pending txn : %v", err)
			}
			n := new(big.Int)
			n, _ = n.SetString(pTxn.Amount, 10)
			toAddr := common.HexToAddress(pTxn.To)
			withInput := true
			if pTxn.Amount != "0" {
				toAddr = common.HexToAddress(util.EnsureCorrectPrefix(pTxn.To))
				withInput = pTxn.WithInput
			}
			go wallet.AssociateTransactionWithOrder(
				wallet.createTxnCallback(util.EnsureCorrectPrefix(hash.Hex()), pTxn.OrderID, EthAddress{&toAddr},
					*n, time.Now(), withInput))
		}
	}
	return hash, nil
}