[Question] Is possible to generate new account offline
Hi guys, how's going?
I very new on the blockchain, ethereum, all that stuff, and I have some sort of an "issue".
I saw a lot of libraries to work with ethereum in PHP and basically, all of them needs a connection with some ethereum network to work. But I noticed a lot of that networks as a service they don't allow us to create address/wallet/account due to security issues.
So there any way that I can generate those address offline? I saw this post and it seems possible to create offline.
I was thinking to add that feature. As an optional param to generate offline. We still will be needing a network to connect into, but that address part will be optional, with the default value as 'false'. If the network allows you to create, that one already works. If not allows, just change to 'true'.
What do you guys think?
Sorry if I said something wrong. Still learning.
Kind regards.
Amandio.
Technically Ethereum addresses are independent from any network. They just derived from a "private key" which is just some random string. Visible on chain are only the transactions from/to an account address.
You may use a wallet like Metamask to create them. I don't know of any PHP wallet currently, but it would be great to add this function in this library (And by experience, I'm sure soon after you'll find some content here too ;) )
Summing up as opposed to contract addresses (which are deployed on chain) account addresses are technically network independent, so you don't need a network to connect to.
The param stuff I actually don't understand, I guess it's not necessary. I'm to answer more questions on that.
Hey @digitaldonkey, I managed to create address "offline". The code is based on that post that I sent to you, and also based on the implementation of ethereum/web3.js.
I used two dependencies to reach my goal. kornrunner/php-keccak, and mdanter/ecc.
/**
* @param null $entropy
* @return array
* @throws \Exception
*/
public function generateAddress($entropy = '')
{
$generator = EccFactory::getNistCurves()->generator384();
$private = $generator->createPrivateKey();
$adapter = EccFactory::getAdapter();
$privateKeySerializer = new PemPrivateKeySerializer(new DerPrivateKeySerializer($adapter));
$privateKey = $privateKeySerializer->serialize($private);
$public = $private->getPublicKey();
$publicKeySerializer = new PemPublicKeySerializer(new DerPublicKeySerializer($adapter));
$publicKey = $publicKeySerializer->serialize($public);
$hashPrivateKey = Keccak::hash($privateKey . time() . $entropy, 256);
$hashPublicKey = Keccak::hash($publicKey . time() . $entropy, 256);
return [
'private' => $hashPrivateKey,
'public' => $hashPublicKey,
//'address' => '0x' . substr($hashPublicKey, 24),
'address' => $this->checksum('0x' . substr($hashPublicKey, 24)),
];
}
/**
* @param string $hashPublicKey
* @return string
* @throws \Exception
*/
private function checksum(string $hashPublicKey)
{
$addressHash = '0x' . Keccak::hash(substr($hashPublicKey, 2), 256);
$checksumAddress = '0x';
for ($i = 0; $i < 40; $i++) {
$checksumAddress .= $this->checkIntVal($hashPublicKey, $addressHash, $i);
}
return $checksumAddress;
}
/**
* @param string $hashPublicKey
* @param $addressHash
* @param $i
* @return string
*/
private function checkIntVal(string $hashPublicKey, $addressHash, $i): string
{
return intval($addressHash[$i + 2], 16) > 7 ? strtoupper($hashPublicKey[$i + 2]) : $hashPublicKey[$i + 2];
}
I agree that Ethereum addresses should and they are independent of any network, but basically, all PHP libraries when you try to create a new account, the library create a request personal_newAccount to the network you're connected. Some services will allow that request, and others don't due to security issues.
But what made me wonder was, if you see on the implementation of ethereum/web3.js to create accounts. He actually creates all hashes from scratch, as you mentioned is only a random string derived from a "private key". Check the code web3.eth.accounts.create, he uses an eth-lib to call the Account.create method eth-lib(account).
So that is why I asked you If was possible to generate those address "offline" in PHP. With that implementation that I did is possible and it works.
I would appreciate a pull request. Sorry for delay. I'm at DrupalEruope conference.
Owww, I'll try my best to add this into a PR
As Ethereum-PHP aims to be client independent (including services like infura.io which restrict the personal API, I like the "offline approach" as it would be most useful.
Let me know if I can help you I'd be really happy about such feature.
We should use PHP-7 random_bytes($length) instead of time() and mingle it with the salt($entropy) so that it is kind of secure on default
https://www.sitepoint.com/randomness-php-feel-lucky
Hey @digitaldonkey, I managed to create address "offline". The code is based on that post that I sent to you, and also based on the implementation of ethereum/web3.js.
I used two dependencies to reach my goal. kornrunner/php-keccak, and mdanter/ecc.
/** * @param null $entropy * @return array * @throws \Exception */ public function generateAddress($entropy = '') { $generator = EccFactory::getNistCurves()->generator384(); $private = $generator->createPrivateKey(); $adapter = EccFactory::getAdapter(); $privateKeySerializer = new PemPrivateKeySerializer(new DerPrivateKeySerializer($adapter)); $privateKey = $privateKeySerializer->serialize($private); $public = $private->getPublicKey(); $publicKeySerializer = new PemPublicKeySerializer(new DerPublicKeySerializer($adapter)); $publicKey = $publicKeySerializer->serialize($public); $hashPrivateKey = Keccak::hash($privateKey . time() . $entropy, 256); $hashPublicKey = Keccak::hash($publicKey . time() . $entropy, 256); return [ 'private' => $hashPrivateKey, 'public' => $hashPublicKey, //'address' => '0x' . substr($hashPublicKey, 24), 'address' => $this->checksum('0x' . substr($hashPublicKey, 24)), ]; } /** * @param string $hashPublicKey * @return string * @throws \Exception */ private function checksum(string $hashPublicKey) { $addressHash = '0x' . Keccak::hash(substr($hashPublicKey, 2), 256); $checksumAddress = '0x'; for ($i = 0; $i < 40; $i++) { $checksumAddress .= $this->checkIntVal($hashPublicKey, $addressHash, $i); } return $checksumAddress; } /** * @param string $hashPublicKey * @param $addressHash * @param $i * @return string */ private function checkIntVal(string $hashPublicKey, $addressHash, $i): string { return intval($addressHash[$i + 2], 16) > 7 ? strtoupper($hashPublicKey[$i + 2]) : $hashPublicKey[$i + 2]; }
Not Working This Code.
Private key and address incompatible.
Sample Output:
array (size=3) 'private' => string '9343ae8f689ccceb68687a18c600395ed48559f4a12d7874a438ba71fb8d8f95' (length=64) 'public' => string '940cddcbd554ec4ab0dc6262c4a44f69ee7371336a1f1845eb12f943b0b20dc6' (length=64) 'address' => string '0xc4a44f69ee7371336a1f1845eb12f943b0b20dc6' (length=42)
True: Private Key: 9343ae8f689ccceb68687a18c600395ed48559f4a12d7874a438ba71fb8d8f95 Address : 0x28428b4f9631178408E7c99EbE7FD7A67811296C