以太坊ETH轉(zhuǎn)賬

本文介紹以太坊(Ethereum)的轉(zhuǎn)賬,依據(jù)web3j庫(kù)實(shí)現(xiàn)。

概念介紹

DSA一種公開密鑰算法团秽,它不能用作加密,只用作數(shù)字簽名钾腺。參考
ECDSA橢圓曲線數(shù)字簽名算法(ECDSA)是使用橢圓曲線密碼(ECC)對(duì)數(shù)字簽名算法(DSA)的加密徙垫。生成的r、s簽名值參考

一放棒、解碼錢包

也就是是根據(jù)用戶密碼從錢包讀出keystore信息姻报。這基本就是錢包生成的逆向流程。
1.將用戶密碼根據(jù)scrypt算法重新生成derivedKey.如下圖紅框所示间螟,跟create()相互對(duì)照
2.根據(jù)derivedKey調(diào)用performCipherOperation()解密方法得到私鑰吴旋。如下圖藍(lán)框所示,跟create()相互對(duì)照
3.將私鑰傳給ECKeyPair::create()便可重新得到公鑰厢破。具體調(diào)用Sign::publicKeyFromPrivate():BigInteger感興趣的可以追進(jìn)去看看荣瑟。
4.根據(jù)ECKeyPair生成Credentials類,這個(gè)類主要包含ECKeyPair和錢包地址摩泪。
這個(gè)地方需要注意的是笆焰,錢包地址是重新根據(jù)公鑰生成的,而不是從文件里讀取出來见坑。
大伙想一下這樣做有什么好處嚷掠?(安全唄,這不是p話么荞驴,放文件里被篡改了咋辦不皆。)

decrypt.jpg

具體代碼

//Wallet.java內(nèi)解碼錢包
public static ECKeyPair decrypt(String password, WalletFile walletFile)throws CipherException {
    validate(walletFile);
    WalletFile.Crypto crypto = walletFile.getCrypto();
    byte[] mac = Numeric.hexStringToByteArray(crypto.getMac());
    byte[] iv = Numeric.hexStringToByteArray(crypto.getCipherparams().getIv());
    byte[] cipherText = Numeric.hexStringToByteArray(crypto.getCiphertext());
    byte[] derivedKey;
    //獲得scrypt加密的相關(guān)參數(shù),并解碼用戶密碼熊楼。
    WalletFile.KdfParams kdfParams = crypto.getKdfparams();
    if (kdfParams instanceof WalletFile.ScryptKdfParams) {
        WalletFile.ScryptKdfParams scryptKdfParams =
                (WalletFile.ScryptKdfParams) crypto.getKdfparams();
        int dklen = scryptKdfParams.getDklen();
        int n = scryptKdfParams.getN();
        int p = scryptKdfParams.getP();
        int r = scryptKdfParams.getR();
        byte[] salt = Numeric.hexStringToByteArray(scryptKdfParams.getSalt());
        derivedKey = generateDerivedScryptKey(
                password.getBytes(Charset.forName("UTF-8")), salt, n, r, p, dklen);
    } else if (kdfParams instanceof WalletFile.Aes128CtrKdfParams) {
        WalletFile.Aes128CtrKdfParams aes128CtrKdfParams =
                (WalletFile.Aes128CtrKdfParams) crypto.getKdfparams();
        int c = aes128CtrKdfParams.getC();
        String prf = aes128CtrKdfParams.getPrf();
        byte[] salt = Numeric.hexStringToByteArray(aes128CtrKdfParams.getSalt());
        derivedKey = generateAes128CtrDerivedKey(
                password.getBytes(Charset.forName("UTF-8")), salt, c, prf);
    } else {
        throw new CipherException("Unable to deserialize params: " + crypto.getKdf());
    }
    byte[] derivedMac = generateMac(derivedKey, cipherText);
    if (!Arrays.equals(derivedMac, mac)) {
        throw new CipherException("Invalid password provided");
    }
    byte[] encryptKey = Arrays.copyOfRange(derivedKey, 0, 16);
    //根據(jù)用戶密碼生成的encryptKey解碼cipherText獲得私鑰
    byte[] privateKey = performCipherOperation(Cipher.DECRYPT_MODE, iv, encryptKey, cipherText);
    return ECKeyPair.create(privateKey);
}

//Credentials.java內(nèi)霹娄,根據(jù)ECKeyPair參數(shù)重新獲取地址并保存到當(dāng)前類內(nèi)。
public static Credentials create(ECKeyPair ecKeyPair) {
    String address = Numeric.prependHexPrefix(Keys.getAddress(ecKeyPair));
    return new Credentials(ecKeyPair, address);
}

經(jīng)過上面的步驟我們便解碼了錢包,后面就可以根據(jù)這些信息執(zhí)行轉(zhuǎn)賬功能了犬耻。

二踩晶、獲取Nonce

nonce:整數(shù)類型,會(huì)隨著賬戶下的交易不斷累加枕磁。作用是“防止交易的重播攻擊”合瓢。
我們通過調(diào)用ethscan的相關(guān)接口查詢到上次交易的nonce值,此值從0開始透典。

三晴楔、代碼處理

開始之前先介紹一個(gè)r、s峭咒、v的概念税弃,其中r、s便是ECDSA簽名值凑队。v是chainid.
1.根據(jù)nonce以及gasPrice则果、gasLimit等初始化RawTransaction類
也就是交易描述文件。
RawTransaction.createTransaction()
2.根據(jù)描述文件生成byte文件漩氨。TransactionEncoder.signMessage()
此文件為在網(wǎng)絡(luò)上傳輸?shù)奈募髯场4瞬襟E會(huì)根據(jù)ECDSA進(jìn)行數(shù)字簽名以及加密。
3.調(diào)用api?action=eth_sendRawTransaction將描述文件發(fā)送到相關(guān)服務(wù)器叫惊。
4.服務(wù)器將此文件廣播到ETH公鏈款青。
接口調(diào)用代碼,具體見Github內(nèi)TransactionService.kt類

class TransactionService : IntentService("Transaction Service") {

    private var builder: NotificationCompat.Builder? = null
    internal val mNotificationId = 153

    override fun onHandleIntent(intent: Intent?) {
        sendNotification()
        try {
            val fromAddress = intent!!.getStringExtra("FROM_ADDRESS")
            val toAddress = intent.getStringExtra("TO_ADDRESS")
            val amount = intent.getStringExtra("AMOUNT")
            val gas_price = intent.getStringExtra("GAS_PRICE")
            val gas_limit = intent.getStringExtra("GAS_LIMIT")
            val data = intent.getStringExtra("DATA")
            val password = intent.getStringExtra("PASSWORD")

            val keys = WalletStorage.getInstance(applicationContext).getFullWallet(applicationContext, password, fromAddress)

            EtherscanAPI.INSTANCE.getNonceForAddress(fromAddress)
                    .subscribe(
                            object : SingleObserver<NonceForAddress> {
                                override fun onSuccess(t: NonceForAddress) {
                                    if (t.result.length < 2) return

                                    val nonce = BigInteger(t.result.substring(2), 16)

                                    val tx = RawTransaction.createTransaction(
                                            nonce,
                                            BigInteger(gas_price),
                                            BigInteger(gas_limit),
                                            toAddress,
                                            BigDecimal(amount).multiply(ExchangeCalculator.ONE_ETHER).toBigInteger(),
                                            data
                                    )

                                    Log.d("Aaron",
                                            "Nonce: " + tx.nonce + "\n" +
                                                    "gasPrice: " + tx.gasPrice + "\n" +
                                                    "gasLimit: " + tx.gasLimit + "\n" +
                                                    "To: " + tx.to + "\n" +
                                                    "Amount: " + tx.value + "\n" +
                                                    "Data: " + tx.data
                                    )

                                    val signed = TransactionEncoder.signMessage(tx, 1.toByte(), keys)

                                    forwardTX(signed)
                                }

                                override fun onSubscribe(d: Disposable) {
                                }

                                override fun onError(e: Throwable) {
                                    error("Can't connect to network, retry it later")
                                }

                            }
                    )

        } catch (e: Exception) {
            error("Invalid Wallet Password!")
            e.printStackTrace()
        }

    }

    @Throws(IOException::class)
    private fun forwardTX(signed: ByteArray) {

        EtherscanAPI.INSTANCE.forwardTransaction("0x" + Hex.toHexString(signed))
                .subscribe(
                        object : SingleObserver<ForwardTX> {
                            override fun onSuccess(t: ForwardTX) {
                                if (!TextUtils.isEmpty(t.result)) {
                                    suc(t.result)
                                } else {
                                    var errormsg = t.error.message
                                    if (errormsg.indexOf(".") > 0)
                                        errormsg = errormsg.substring(0, errormsg.indexOf("."))
                                    error(errormsg) // f.E Insufficient funds
                                }
                            }

                            override fun onSubscribe(d: Disposable) {
                            }

                            override fun onError(e: Throwable) {
                                error("Can't connect to network, retry it later")
                            }


                        }
                )
    }

    private fun suc(hash: String) {
        builder!!
                .setContentTitle(getString(R.string.notification_transfersuc))
                .setProgress(100, 100, false)
                .setOngoing(false)
                .setAutoCancel(true)
                .setContentText("")

        val main = Intent(this, MainActivity::class.java)
        main.putExtra("STARTAT", 2)
        main.putExtra("TXHASH", hash)

        val contentIntent = PendingIntent.getActivity(this, 0,
                main, PendingIntent.FLAG_UPDATE_CURRENT)
        builder!!.setContentIntent(contentIntent)

        val mNotifyMgr = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        mNotifyMgr.notify(mNotificationId, builder!!.build())
    }

    private fun error(err: String) {
        builder!!
                .setContentTitle(getString(R.string.notification_transferfail))
                .setProgress(100, 100, false)
                .setOngoing(false)
                .setAutoCancel(true)
                .setContentText(err)

        val main = Intent(this, MainActivity::class.java)
        main.putExtra("STARTAT", 2)

        val contentIntent = PendingIntent.getActivity(this, 0,
                main, PendingIntent.FLAG_UPDATE_CURRENT)
        builder!!.setContentIntent(contentIntent)

        val mNotifyMgr = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        mNotifyMgr.notify(mNotificationId, builder!!.build())
    }

    private fun sendNotification() {
        builder = NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.ic_notification)
                .setColor(0x2d435c)
                .setTicker(getString(R.string.notification_transferingticker))
                .setContentTitle(getString(R.string.notification_transfering_title))
                .setContentText(getString(R.string.notification_might_take_a_minute))
                .setOngoing(true)
                .setProgress(0, 0, true)
        val mNotifyMgr = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        mNotifyMgr.notify(mNotificationId, builder!!.build())
    }


}

Git地址:https://github.com/snailflying/ETHWallet

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末霍狰,一起剝皮案震驚了整個(gè)濱河市抡草,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蔗坯,老刑警劉巖康震,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異宾濒,居然都是意外死亡腿短,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門绘梦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來橘忱,“玉大人,你說我怎么就攤上這事谚咬○懈叮” “怎么了尚粘?”我有些...
    開封第一講書人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵择卦,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)秉继,這世上最難降的妖魔是什么祈噪? 我笑而不...
    開封第一講書人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮尚辑,結(jié)果婚禮上辑鲤,老公的妹妹穿的比我還像新娘。我一直安慰自己杠茬,他們只是感情好月褥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瓢喉,像睡著了一般宁赤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上栓票,一...
    開封第一講書人閱讀 51,258評(píng)論 1 300
  • 那天决左,我揣著相機(jī)與錄音,去河邊找鬼走贪。 笑死佛猛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的坠狡。 我是一名探鬼主播继找,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼逃沿!你這毒婦竟也來了码荔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤感挥,失蹤者是張志新(化名)和其女友劉穎缩搅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體触幼,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡硼瓣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了置谦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片堂鲤。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖媒峡,靈堂內(nèi)的尸體忽然破棺而出瘟栖,到底是詐尸還是另有隱情,我是刑警寧澤谅阿,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布半哟,位于F島的核電站酬滤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏寓涨。R本人自食惡果不足惜盯串,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望戒良。 院中可真熱鬧体捏,春花似錦、人聲如沸糯崎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沃呢。三九已至奏司,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間樟插,已是汗流浹背韵洋。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留黄锤,地道東北人搪缨。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像鸵熟,于是被迫代替她去往敵國(guó)和親副编。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 所有貨幣都需要一些方法來控制供應(yīng)流强,并強(qiáng)制執(zhí)行各種安全屬性以防止作弊痹届。在法定貨幣方面,像中央銀行這樣的組織控制貨幣供...
    Nutbox_Lab閱讀 3,102評(píng)論 1 3
  • 一打月、快速術(shù)語(yǔ)檢索 比特幣地址:(例如:1DSrfJdB2AnWaFNgSbv3MZC2m74996JafV)由一串...
    不如假如閱讀 15,945評(píng)論 4 87
  • 本文是對(duì)以太坊文檔 Ethereum Frontier Guide 和 Ethereum Homestead 的整...
    趁風(fēng)卷閱讀 9,512評(píng)論 0 16
  • 常扯痈看到聽到某些人物勵(lì)志演講,或在高校站臺(tái)奏篙,或在大會(huì)鼓吹柴淘,天花亂墜,說自己如何如何奮斗秘通,如何如何經(jīng)歷坎坷为严,如何命懸...
    大陳子閱讀 983評(píng)論 0 1
  • 早上刷臉打卡的時(shí)候,看見機(jī)器屏幕上去年錄進(jìn)去的那張臉肺稀。默默地想第股,要是哪天識(shí)別不出來了就好了。至少說明话原,要么我可以不...
    蘇小白說閱讀 340評(píng)論 5 6