Skip to content

Commit

Permalink
HSM integration
Browse files Browse the repository at this point in the history
  • Loading branch information
dolgopolovwork committed Jul 1, 2019
1 parent 4d9ab69 commit 520facc
Show file tree
Hide file tree
Showing 48 changed files with 530 additions and 239 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ package com.d3.btc.generation.config

import com.d3.btc.generation.BTC_ADDRESS_GENERATION_SERVICE_NAME
import com.d3.btc.provider.BtcChangeAddressProvider
import com.d3.btc.provider.network.BtcNetworkConfigProvider
import com.d3.btc.wallet.createWalletIfAbsent
import com.d3.commons.config.loadLocalConfigs
import com.d3.commons.expansion.ServiceExpansion
import com.d3.commons.model.IrohaCredential
Expand All @@ -22,10 +20,8 @@ import com.d3.commons.util.createPrettySingleThreadPool
import io.grpc.ManagedChannelBuilder
import jp.co.soramitsu.iroha.java.IrohaAPI
import jp.co.soramitsu.iroha.java.Utils
import org.bitcoinj.wallet.Wallet
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.io.File

val btcAddressGenerationConfig =
loadLocalConfigs(
Expand Down Expand Up @@ -101,13 +97,6 @@ class BtcAddressGenerationAppConfiguration {
@Bean
fun btcAddressGenerationConfig() = btcAddressGenerationConfig

@Bean
fun keysWallet(networkProvider: BtcNetworkConfigProvider): Wallet {
val walletPath = btcAddressGenerationConfig.btcKeysWalletPath
createWalletIfAbsent(walletPath, networkProvider)
return Wallet.loadFromFile(File(walletPath))!!
}

@Bean
fun notaryPeerListProvider(): NotaryPeerListProvider {
return NotaryPeerListProviderImpl(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ interface BtcAddressGenerationConfig {
//Iroha config
val iroha: IrohaConfig

//Path to BTC wallet file
val btcKeysWalletPath: String

//TODO the only purpose of this account is creating PeerListProvider. This account must be removed from config.
//Account that is used to register BTC addresses
val registrationAccount: IrohaCredentialRawConfig
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import com.d3.btc.provider.generation.ADDRESS_GENERATION_NODE_ID_KEY
import com.d3.btc.provider.generation.ADDRESS_GENERATION_TIME_KEY
import com.d3.btc.provider.generation.BtcPublicKeyProvider
import com.d3.btc.provider.network.BtcNetworkConfigProvider
import com.d3.btc.wallet.checkWalletNetwork
import com.d3.btc.wallet.safeSave
import com.d3.commons.sidechain.iroha.CLIENT_DOMAIN
import com.d3.commons.sidechain.iroha.IrohaChainListener
import com.d3.commons.sidechain.iroha.util.IrohaQueryHelper
Expand All @@ -34,7 +32,6 @@ import io.reactivex.schedulers.Schedulers
import iroha.protocol.BlockOuterClass
import iroha.protocol.Commands
import mu.KLogging
import org.bitcoinj.wallet.Wallet
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.stereotype.Component

Expand All @@ -43,7 +40,6 @@ import org.springframework.stereotype.Component
*/
@Component
class BtcAddressGenerationInitialization(
private val keysWallet: Wallet,
@Qualifier("registrationQueryHelper")
private val registrationQueryHelper: IrohaQueryHelper,
private val btcAddressGenerationConfig: BtcAddressGenerationConfig,
Expand All @@ -59,10 +55,7 @@ class BtcAddressGenerationInitialization(
* @param onIrohaFail - function that will be called on Iroha failure
*/
fun init(onIrohaFail: () -> Unit): Result<Unit, Exception> {
//Check wallet network
return keysWallet.checkWalletNetwork(btcNetworkConfigProvider.getConfig()).flatMap {
irohaChainListener.getBlockObservable()
}.map { irohaObservable ->
return irohaChainListener.getBlockObservable().map { irohaObservable ->
initIrohaObservable(irohaObservable, onIrohaFail)
}.flatMap {
// Start free address generation at initial phase
Expand Down Expand Up @@ -174,7 +167,7 @@ class BtcAddressGenerationInitialization(

// Generates new key
private fun onGenerateKey(sessionAccountName: String): Result<String, Exception> {
return btcPublicKeyProvider.createKey(sessionAccountName) { saveWallet() }
return btcPublicKeyProvider.createKey(sessionAccountName)
}

/**
Expand Down Expand Up @@ -203,18 +196,13 @@ class BtcAddressGenerationInitialization(
addressType,
time,
nodeId
) { saveWallet() }
)
} else {
Result.of { Unit }
}
}
}

// Safes wallet full of keys
private fun saveWallet() {
keysWallet.safeSave(btcAddressGenerationConfig.btcKeysWalletPath)
}

/**
* Logger
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package com.d3.btc.generation

import com.d3.btc.generation.init.BtcAddressGenerationInitialization
import com.d3.btc.keypair.getKeyProviderProfile
import com.d3.commons.config.getProfile
import com.github.kittinunf.result.Result
import com.github.kittinunf.result.failure
Expand All @@ -24,7 +25,9 @@ const val BTC_ADDRESS_GENERATION_SERVICE_NAME = "btc-add-gen"
"com.d3.btc.healthcheck",
"com.d3.btc.provider.generation",
"com.d3.btc.provider.network",
"com.d3.btc.generation.trigger"]
"com.d3.btc.generation.trigger",
"com.d3.btc.keypair"
]
)
class BtcAddressGenerationApplication

Expand All @@ -33,7 +36,7 @@ private val logger = KLogging().logger
fun main(args: Array<String>) {
Result.of {
val context = AnnotationConfigApplicationContext()
context.environment.setActiveProfiles(getProfile())
context.environment.setActiveProfiles(getProfile(), getKeyProviderProfile())
context.register(BtcAddressGenerationApplication::class.java)
context.refresh()
context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,3 @@ btc-address-generation.mstRegistrationAccount.privkey=e457ce0afc8059e7a2e4d14339
btc-address-generation.iroha.hostname=d3-iroha
# Iroha peer port
btc-address-generation.iroha.port=50051
# --------- Bitcoin ---------
btc-address-generation.btcKeysWalletPath=deploy/bitcoin/regtest/keys.d3.wallet
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,3 @@ btc-address-generation.mstRegistrationAccount.privkey=e457ce0afc8059e7a2e4d14339
btc-address-generation.iroha.hostname=d3-iroha
# Iroha peer port
btc-address-generation.iroha.port=50051
# --------- Bitcoin ---------
btc-address-generation.btcKeysWalletPath=deploy/bitcoin/mainnet/keys.d3.wallet
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,3 @@ btc-address-generation.mstRegistrationAccount.privkey=e457ce0afc8059e7a2e4d14339
btc-address-generation.iroha.hostname=d3-iroha
# Iroha peer port
btc-address-generation.iroha.port=50051
# --------- Bitcoin ---------
btc-address-generation.btcKeysWalletPath=deploy/bitcoin/testnet/keys.d3.wallet
6 changes: 4 additions & 2 deletions btc-dw-bridge/src/main/kotlin/com/d3/btc/dwbridge/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package com.d3.btc.dwbridge

import com.d3.btc.deposit.init.BtcNotaryInitialization
import com.d3.btc.dwbridge.config.dwBridgeConfig
import com.d3.btc.keypair.getKeyProviderProfile
import com.d3.btc.withdrawal.init.BtcWithdrawalInitialization
import com.d3.commons.config.getProfile
import com.d3.commons.util.createFolderIfDoesntExist
Expand Down Expand Up @@ -43,7 +44,8 @@ const val BTC_DW_BRIDGE_SERVICE_NAME = "btc-dw-bridge"
"com.d3.btc.deposit.expansion",
"com.d3.btc.peer",
"com.d3.btc.dwbridge",
"com.d3.btc.healthcheck"]
"com.d3.btc.healthcheck",
"com.d3.btc.keypair"]
)
class BtcDWBridgeApplication

Expand All @@ -58,7 +60,7 @@ fun main(args: Array<String>) {
createFolderIfDoesntExist(dwBridgeConfig.bitcoin.blockStoragePath)
}.map {
val context = AnnotationConfigApplicationContext()
context.environment.setActiveProfiles(getProfile())
context.environment.setActiveProfiles(getProfile(), getKeyProviderProfile())
context.register(BtcDWBridgeApplication::class.java)
context.refresh()
context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ interface BtcWithdrawalConfig {
val irohaBlockQueue: String
// Path to wallet that stores transfers(UTXO)
val btcTransfersWalletPath: String
// Path to wallet that stores keys
val btcKeysWalletPath: String
// Account that is used to store created Bitcoin transactions
val txStorageAccount: String
// Account that is used to store used UTXO
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.d3.btc.withdrawal.handler

import com.d3.btc.withdrawal.config.BtcWithdrawalConfig
import com.d3.btc.withdrawal.service.BtcRollbackService
import com.d3.btc.withdrawal.transaction.SignCollector
import com.d3.btc.withdrawal.transaction.TransactionsStorage
Expand All @@ -14,7 +13,6 @@ import org.springframework.stereotype.Component
class NewTransactionCreatedHandler(
private val signCollector: SignCollector,
private val transactionsStorage: TransactionsStorage,
private val btcWithdrawalConfig: BtcWithdrawalConfig,
private val btcRollbackService: BtcRollbackService
) {

Expand All @@ -28,7 +26,7 @@ class NewTransactionCreatedHandler(
val txHash = createNewTxCommand.key
transactionsStorage.get(createNewTxCommand.key).map { (withdrawalDetails, transaction) ->
logger.info { "Tx to sign\n$transaction" }
signCollector.signAndSave(transaction, btcWithdrawalConfig.btcKeysWalletPath).fold({
signCollector.signAndSave(transaction).fold({
logger.info { "Signatures for ${transaction.hashAsString} were successfully processed" }
}, { ex ->
logger.error("Cannot sign transaction $transaction", ex)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,9 @@ class SignCollector(
* 2) Create special account named after tx hash for signature storing
* 3) Save signatures in recently created account details
* @param tx - transaction to sign
* @param walletPath - path to current wallet. Used to get private keys
*/
fun signAndSave(tx: Transaction, walletPath: String): Result<Unit, Exception> {
return transactionSigner.sign(tx, walletPath).flatMap { signedInputs ->
fun signAndSave(tx: Transaction): Result<Unit, Exception> {
return transactionSigner.sign(tx).flatMap { signedInputs ->
if (signedInputs.isEmpty()) {
logger.warn(
"Cannot sign transaction ${tx.hashAsString}. " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@ import com.d3.btc.helper.address.createMsRedeemScript
import com.d3.btc.helper.address.outPutToBase58Address
import com.d3.btc.helper.address.toEcPubKey
import com.d3.btc.helper.input.getConnectedOutput
import com.d3.btc.keypair.KeyPairService
import com.d3.btc.provider.BtcChangeAddressProvider
import com.d3.btc.provider.BtcRegisteredAddressesProvider
import com.d3.btc.wallet.safeLoad
import com.d3.commons.util.hex
import com.github.kittinunf.result.Result
import com.github.kittinunf.result.fanout
import com.github.kittinunf.result.map
import mu.KLogging
import org.bitcoinj.core.ECKey
import org.bitcoinj.core.Transaction
import org.bitcoinj.wallet.Wallet
import org.springframework.stereotype.Component
Expand All @@ -29,17 +28,17 @@ import org.springframework.stereotype.Component
class TransactionSigner(
private val btcRegisteredAddressesProvider: BtcRegisteredAddressesProvider,
private val btcChangeAddressesProvider: BtcChangeAddressProvider,
private val transfersWallet: Wallet
private val transfersWallet: Wallet,
private val keyPairService: KeyPairService
) {
/**
* Signs transaction using available private keys from wallet
* Signs transaction using available private keys
*
* @param tx - transaction to sign
* @param keysWalletPath - path to wallet file. Used to take private keys
* @return - result with list full of signatures in form "input index"->"signatureHex hex"
*/
fun sign(tx: Transaction, keysWalletPath: String): Result<List<InputSignature>, Exception> {
return Result.of { signUnsafe(tx, safeLoad(keysWalletPath)) }
fun sign(tx: Transaction): Result<List<InputSignature>, Exception> {
return Result.of { signUnsafe(tx) }
}

/**
Expand All @@ -59,26 +58,29 @@ class TransactionSigner(
}
}

// Main signing function
private fun signUnsafe(tx: Transaction, wallet: Wallet): List<InputSignature> {
/**
* Signs given transaction inputs if it possible
* @return list of input signatures
*/
private fun signUnsafe(tx: Transaction): List<InputSignature> {
var inputIndex = 0
val signatures = ArrayList<InputSignature>()
tx.inputs.forEach { input ->
val connectedOutput = input.getConnectedOutput(transfersWallet)
getUsedPubKeys(outPutToBase58Address(connectedOutput)).fold({ pubKeys ->
val keyPair = getPrivPubKeyPair(pubKeys, wallet)
if (keyPair != null) {
val pubKeyHex = getAvailableKey(pubKeys)
if (pubKeyHex != null) {
val redeem = createMsRedeemScript(pubKeys)
logger.info("Redeem script for tx ${tx.hashAsString} input $inputIndex is $redeem")
val hashOut =
tx.hashForSignature(inputIndex, redeem, Transaction.SigHash.ALL, false)
val signature = keyPair.sign(hashOut)

signatures.add(
InputSignature(
inputIndex,
SignaturePubKey(
String.hex(signature.encodeToDER()),
keyPair.publicKeyAsHex
String.hex(keyPairService.sign(hashOut.bytes, pubKeyHex)!!),
pubKeyHex
)
)
)
Expand All @@ -94,16 +96,16 @@ class TransactionSigner(
return signatures
}

//Returns key pair related to one of given public keys. Returns null if no key pair was found
private fun getPrivPubKeyPair(pubKeys: List<String>, wallet: Wallet): ECKey? {
pubKeys.forEach { pubKey ->
/**
* Returns public key hex which corresponding private key is controlled by the current node
* @param pubKeys - public keys to check
* @return public key hex or null if no keys are controlled by us
*/
private fun getAvailableKey(pubKeys: List<String>): String? {
return pubKeys.find { pubKey ->
val ecKey = toEcPubKey(pubKey)
val keyPair = wallet.findKeyFromPubHash(ecKey.pubKeyHash)
if (keyPair != null) {
return keyPair
}
keyPairService.exists(ecKey.publicKeyAsHex)
}
return null
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ btc-withdrawal.notaryListSetterAccount=notary@notary
btc-withdrawal.notaryListStorageAccount=notaries@notary
btc-withdrawal.irohaBlockQueue=btc_withdrawal_blocks
btc-withdrawal.btcTransfersWalletPath=deploy/bitcoin/regtest/transfers.d3.wallet
btc-withdrawal.btcKeysWalletPath=deploy/bitcoin/regtest/keys.d3.wallet
# --------- Credentials -------
btc-withdrawal.withdrawalCredential.accountId=btc_withdrawal_service@notary
btc-withdrawal.withdrawalCredential.pubkey=2ea1d2c3eb5b35a85b393622609ae116ddb2ea35e060fc74adb3b2caa324eacb
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ btc-withdrawal.notaryListSetterAccount=notary@notary
btc-withdrawal.notaryListStorageAccount=notaries@notary
btc-withdrawal.irohaBlockQueue=btc_withdrawal_blocks
btc-withdrawal.btcTransfersWalletPath=deploy/bitcoin/mainnet/transfers.d3.wallet
btc-withdrawal.btcKeysWalletPath=deploy/bitcoin/mainnet/keys.d3.wallet
# --------- Credentials -------
btc-withdrawal.withdrawalCredential.accountId=btc_withdrawal_service@notary
btc-withdrawal.withdrawalCredential.pubkey=2ea1d2c3eb5b35a85b393622609ae116ddb2ea35e060fc74adb3b2caa324eacb
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ btc-withdrawal.notaryListSetterAccount=notary@notary
btc-withdrawal.notaryListStorageAccount=notaries@notary
btc-withdrawal.irohaBlockQueue=btc_withdrawal_blocks
btc-withdrawal.btcTransfersWalletPath=deploy/bitcoin/testnet/transfers.d3.wallet
btc-withdrawal.btcKeysWalletPath=deploy/bitcoin/testnet/keys.d3.wallet
# --------- Credentials -------
btc-withdrawal.withdrawalCredential.accountId=btc_withdrawal_service@notary
btc-withdrawal.withdrawalCredential.pubkey=2ea1d2c3eb5b35a85b393622609ae116ddb2ea35e060fc74adb3b2caa324eacb
Expand Down
13 changes: 2 additions & 11 deletions btc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ buildscript {
apply plugin: "kotlin-spring" // See https://kotlinlang.org/docs/reference/compiler-plugins.html#kotlin-spring-compiler-plugin

dependencies {
compile files('libs/primusX.jar')
//TODO change version after D3 release
compile "com.github.d3ledger.notary:notary-commons:$notary_version"
//bitcoin
Expand Down Expand Up @@ -68,14 +69,4 @@ task btcGenerateBlocks(type: JavaExec) {
args getBtcGenerateBlocksArgs()
classpath = sourceSets.main.runtimeClasspath
setWorkingDir("$rootDir/")
}

/** Recreates wallets in RegTest mode
*
* Usage ./gradlew btcRefreshWallets
*/
task btcRefreshWallets(type: JavaExec) {
main = 'com.d3.btc.cli.BtcRefreshWalletsMain'
classpath = sourceSets.main.runtimeClasspath
setWorkingDir("$rootDir/")
}
}
Binary file added btc/libs/primusX.jar
Binary file not shown.
Loading

0 comments on commit 520facc

Please sign in to comment.