Blockchain-Ethereum

 view release on metacpan or  search on metacpan

examples/address_creation.pl  view on Meta::CPAN


use Blockchain::Ethereum::Keystore::Key;
use Blockchain::Ethereum::Keystore::Seed;

# generating a new address
my $key = Blockchain::Ethereum::Keystore::Key->new();
printf "%s\n", $key->address;

# importing private key
$key = Blockchain::Ethereum::Keystore::Key->new(
    private_key => pack "H*",
    '4646464646464646464646464646464646464646464646464646464646464646'
);
printf "%s\n", $key->address;

# hdwallet
my $wallet = Blockchain::Ethereum::Keystore::Seed->new();
$key = $wallet->derive_key(0);
printf "%s\n", $key->address;
$key = $wallet->derive_key(1);
printf "%s\n", $key->address;

examples/eip1559_tx_signing.pl  view on Meta::CPAN

    nonce                    => '0x0',
    max_fee_per_gas          => '0x1127A5278',
    max_priority_fee_per_gas => '0x79000000',
    gas_limit                => '0x1DE2B9',
    value                    => '0x0',
    data                     => $encoded,
    chain_id                 => '0x1'
);

my $key = Blockchain::Ethereum::Keystore::Key->new(
    private_key => pack "H*",
    '4646464646464646464646464646464646464646464646464646464646464646'
);

$key->sign_transaction($transaction);

my $raw_transaction = $transaction->serialize;

printf("0x%s", unpack "H*", $raw_transaction);

lib/Blockchain/Ethereum/Keystore.pm  view on Meta::CPAN

    $keyfile->write_to_file("...");

Signing a transaction:

    my $transaction = Blockchain::Ethereum::Transaction::EIP1559->new(
        ...
    );

    my $keyfile = Blockchain::Ethereum::Keystore::Keyfile->new;
    $keyfile->import_file("...");
    $keyfile->private_key->sign_transaction($transaction);

Export private key:

    my $keyfile = Blockchain::Ethereum::Keystore::Keyfile->new;
    $keyfile->import_file("...");

    # private key bytes
    print $keyfile->private_key->export;

=head1 OVERVIEW

This module provides a collection of Ethereum wallet management utilities.

Core functionalities:

=over 4

=item * Manage Ethereum keyfiles, facilitating import, export, and password change.

lib/Blockchain/Ethereum/Keystore/Key.pm  view on Meta::CPAN

use Crypt::Digest::Keccak256 qw(keccak256);
use Crypt::PRNG              qw(random_bytes);

use Blockchain::Ethereum::Keystore::Key::PKUtil;
use Blockchain::Ethereum::Keystore::Address;

sub new {
    my ($class, %params) = @_;
    my $self = bless {}, $class;

    if (exists $params{private_key}) {
        $self->{private_key} = $params{private_key};
    } else {
        $self->{private_key} = random_bytes(32);
    }

    my $importer = Crypt::PK::ECC->new();
    $importer->import_key_raw($self->private_key, 'secp256k1');

    # Crypt::PK::ECC does not provide support for deterministic keys
    $self->{ecc_handler} = bless Crypt::Perl::ECDSA::Parse::private($importer->export_key_der('private')),
        'Blockchain::Ethereum::Keystore::Key::PKUtil';

    return $self;
}

sub private_key {
    return shift->{private_key};
}

sub _ecc_handler {
    return shift->{ecc_handler};
}

sub sign_transaction {
    my ($self, $transaction) = @_;

    croak "transaction must be a reference of Blockchain::Ethereum::Transaction"

lib/Blockchain/Ethereum/Keystore/Key.pm  view on Meta::CPAN

    my ($x, $y) = Crypt::Perl::ECDSA::Utils::split_G_or_public($self->_ecc_handler->_decompress_public_point);

    # address is the hash of the concatenated value of x and y
    my $address     = substr(keccak256($x . $y), -20);
    my $hex_address = unpack("H*", $address);

    return Blockchain::Ethereum::Keystore::Address->new(address => "0x$hex_address");
}

sub export {
    return shift->private_key;
}

1;

__END__

=pod

=encoding UTF-8

lib/Blockchain/Ethereum/Keystore/Key.pm  view on Meta::CPAN


=head1 SYNOPSIS

Generate a new key:

    my $key = Blockchain::Ethereum::Key->new;
    $key->sign_transaction($transaction); # Blockchain::Ethereum::Transaction

Import existent key:

    my $key = Blockchain::Ethereum::Key->new(private_key => $private_key); # private key bytes
    $key->sign_transaction($transaction); # Blockchain::Ethereum::Transaction

=head1 OVERVIEW

This is a private key abstraction

If instantiated without a private key, this module uses L<Crypt::PRNG> for the random key generation

=head1 METHODS

lib/Blockchain/Ethereum/Keystore/Key.pm  view on Meta::CPAN

Export the L<Blockchain::Ethereum::Keystore::Address> from the imported/generated private key

=over 4

=back

L<Blockchain::Ethereum::Keystore::Address>

=head2 export

Use `private_key` instead this method is deprecated and will be removed.

=over 4

=back

Private key bytes

=head1 AUTHOR

REFECO <refeco@cpan.org>

lib/Blockchain/Ethereum/Keystore/Keyfile.pm  view on Meta::CPAN

use Crypt::PRNG;
use Net::SSH::Perl::Cipher;

use Blockchain::Ethereum::Keystore::Key;
use Blockchain::Ethereum::Keystore::Keyfile::KDF;

sub new {
    my ($class, %params) = @_;

    my $self = bless {}, $class;
    for (qw(cipher ciphertext mac version iv kdf id private_key)) {
        $self->{$_} = $params{$_} if exists $params{$_};
    }

    return $self;
}

sub cipher {
    shift->{cipher};
}

lib/Blockchain/Ethereum/Keystore/Keyfile.pm  view on Meta::CPAN

}

sub kdf {
    shift->{kdf};
}

sub id {
    shift->{id};
}

sub private_key {
    shift->{private_key};
}

sub _json {
    return shift->{json} //= JSON::MaybeXS->new(utf8 => 1);
}

sub import_file {
    my ($self, $file_path, $password) = @_;

    my $content = read_file($file_path);

lib/Blockchain/Ethereum/Keystore/Keyfile.pm  view on Meta::CPAN

    $self->{kdf} = Blockchain::Ethereum::Keystore::Keyfile::KDF->new(
        algorithm => $crypto->{kdf},     #
        dklen     => $header->{dklen},
        n         => $header->{n},
        p         => $header->{p},
        r         => $header->{r},
        c         => $header->{c},
        prf       => $header->{prf},
        salt      => $header->{salt});

    $self->{private_key} = $self->_private_key($password);

    return $self;
}

sub change_password {
    my ($self, $old_password, $new_password) = @_;

    return $self->import_key($self->_private_key($old_password), $new_password);
}

sub _private_key {
    my ($self, $password) = @_;

    return $self->private_key if $self->private_key;

    my $cipher = Net::SSH::Perl::Cipher->new(
        $self->cipher,    #
        $self->kdf->decode($password),
        pack("H*", $self->iv));

    my $key = $cipher->decrypt(pack("H*", $self->ciphertext));

    return Blockchain::Ethereum::Keystore::Key->new(private_key => $key);
}

sub import_key {
    my ($self, $key, $password) = @_;

    # use the internal method here otherwise would not be availble to get the kdf params
    # salt if give will be the same as the response, if not will be auto generated by the library
    my ($derived_key, $salt, $N, $r, $p);
    ($derived_key, $salt, $N, $r, $p) = Crypt::ScryptKDF::_scrypt_extra($password);
    $self->kdf->{algorithm} = "scrypt";

lib/Blockchain/Ethereum/Keystore/Keyfile.pm  view on Meta::CPAN


    my $cipher = Net::SSH::Perl::Cipher->new(
        "AES128_CTR",    #
        $derived_key,
        $iv
    );

    my $encrypted = $cipher->encrypt($key->export);
    $self->{ciphertext} = unpack "H*", $encrypted;

    $self->{private_key} = $key;

    return $self;
}

sub _write_to_object {
    my $self = shift;

    croak "KDF algorithm and parameters are not set" unless $self->kdf;

    my $file = {

lib/Blockchain/Ethereum/Keystore/Keyfile.pm  view on Meta::CPAN

=over 4

=item * C<keyfile> - L<Blockchain::Ethereum::Keystore::Key>

=back

self

=head2 write_to_file

Write the imported keyfile/private_key to a keyfile in the file system

=over 4

=item * C<file_path> - file path to save the data

=back

returns 1 upon successfully writing the file or undef if it encountered an error

=head1 AUTHOR

lib/Blockchain/Ethereum/Keystore/Seed.pm  view on Meta::CPAN

    $change    = 0  unless $change;

    my $path = Bitcoin::Crypto::BIP44->new(
        index     => $index,
        purpose   => $purpose,
        coin_type => $coin_type,
        account   => $account,
        change    => $change,
    );

    return Blockchain::Ethereum::Keystore::Key->new(private_key => $self->_hdw_handler->derive_key($path)->get_basic_key->to_serialized);
}

1;

__END__

=pod

=encoding UTF-8

lib/Blockchain/Ethereum/Transaction.pm  view on Meta::CPAN

        max_priority_fee_per_gas => '0x0',
        gas_limit                => '0x1DE2B9',
        to                       => '0x3535353535353535353535353535353535353535'
        value                    => Math::BigInt->new('1000000000000000000'),
        data                     => '0x',
        chain_id                 => '0x539'
    );

    # github.com/refeco/perl-ethereum-keystore
    my $key = Blockchain::Ethereum::Keystore::Key->new(
        private_key => pack "H*",
        '4646464646464646464646464646464646464646464646464646464646464646'
    );

    $key->sign_transaction($transaction);

    my $raw_transaction = $transaction->serialize;

    print unpack("H*", $raw_transaction);

Supported transaction types:

lib/Blockchain/Ethereum/Transaction/EIP1559.pm  view on Meta::CPAN

        max_priority_fee_per_gas => '0x0',
        gas_limit                => '0x1DE2B9',
        to                       => '0x3535353535353535353535353535353535353535'
        value                    => '0xDE0B6B3A7640000',
        data                     => '0x',
        chain_id                 => '0x539'
    );

    # github.com/refeco/perl-ethereum-keystore
    my $key = Blockchain::Ethereum::Keystore::Key->new(
        private_key => pack "H*",
        '4646464646464646464646464646464646464646464646464646464646464646'
    );

    $key->sign_transaction($transaction);

    my $raw_transaction = $transaction->serialize;

=head1 METHODS

=head2 serialize

lib/Blockchain/Ethereum/Transaction/Legacy.pm  view on Meta::CPAN

     my $transaction = Blockchain::Ethereum::Transaction::Legacy->new(
        nonce     => '0x9',
        gas_price => '0x4A817C800',
        gas_limit => '0x5208',
        to        => '0x3535353535353535353535353535353535353535',
        value     => '0xDE0B6B3A7640000',
        chain_id  => '0x1'

    # github.com/refeco/perl-ethereum-keystore
    my $key = Blockchain::Ethereum::Keystore::Key->new(
        private_key => pack "H*",
        '4646464646464646464646464646464646464646464646464646464646464646'
    );

    $key->sign_transaction($transaction);

    my $raw_transaction = $transaction->serialize;

=head1 METHODS

=head2 serialize

t/Keystore/key.t  view on Meta::CPAN

#!/usr/bin/env perl

use strict;
use warnings;

use Test::More;
use Blockchain::Ethereum::Keystore::Key;

subtest "0x008AeEda4D805471dF9b2A5B0f38A0C3bCBA786b" => sub {
    my $private_key = pack "H*", "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d";
    my $key         = Blockchain::Ethereum::Keystore::Key->new(private_key => $private_key);

    is $key->address, '0x008AeEda4D805471dF9b2A5B0f38A0C3bCBA786b';
};

subtest "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F" => sub {
    my $private_key = pack "H*", "4646464646464646464646464646464646464646464646464646464646464646";
    my $key         = Blockchain::Ethereum::Keystore::Key->new(private_key => $private_key);

    is $key->address, '0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F';
};

done_testing();

t/Keystore/keyfile.t  view on Meta::CPAN

#!/usr/bin/env perl

use strict;
use warnings;

use Test::More;
use Blockchain::Ethereum::Keystore::Keyfile;

# https://ethereum.org/pt-br/developers/docs/data-structures-and-encoding/web3-secret-storage/#PBKDF2-SHA-256
subtest "v3_pbkdf2_ctr" => sub {
    my $private_key = pack "H*", "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d";
    my $password    = "testpassword";

    my $keyfile = Blockchain::Ethereum::Keystore::Keyfile->new;

    my $key = $keyfile->import_file("./t/Keystore/resources/pbkdf2_v3.json", $password);
    is $key->private_key->export, $private_key;

    $key = $keyfile->import_key(Blockchain::Ethereum::Keystore::Key->new(private_key => $private_key), $password);
    is $key->private_key->export, $private_key;
};

# https://ethereum.org/pt-br/developers/docs/data-structures-and-encoding/web3-secret-storage/#scrypt
subtest "v3_scrypt_ctr" => sub {
    my $private_key = pack "H*", "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d";
    my $password    = "testpassword";

    my $keyfile = Blockchain::Ethereum::Keystore::Keyfile->new;

    my $key = $keyfile->import_file("./t/Keystore/resources/scrypt_v3.json", $password);
    is $key->private_key->export, $private_key;

    $key = $keyfile->import_key(Blockchain::Ethereum::Keystore::Key->new(private_key => $private_key), $password);
    is $key->private_key->export, $private_key;
};

done_testing;

t/Transaction/eip1559.t  view on Meta::CPAN

        nonce                    => '0x0',
        max_fee_per_gas          => '0x9',
        max_priority_fee_per_gas => '0x0',
        gas_limit                => '0x1DE2B9',
        value                    => '0x0',
        data                     => $compiled_contract,
        chain_id                 => '0x539'
    );

    my $key = Blockchain::Ethereum::Keystore::Key->new(
        private_key => pack "H*",
        '4646464646464646464646464646464646464646464646464646464646464646'
    );

    $key->sign_transaction($transaction);

    my $raw_transaction = $transaction->serialize;

    is(unpack("H*", $raw_transaction),
        '02f901c3820539808009831de2b98080b90170608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b604...
    );

t/Transaction/eip1559.t  view on Meta::CPAN

        nonce                    => '0x1',
        max_fee_per_gas          => '0x9',
        max_priority_fee_per_gas => '0x0',
        gas_limit                => '0x5208',
        to                       => '0x3535353535353535353535353535353535353535',
        value                    => '0xDE0B6B3A7640000',
        chain_id                 => '0x539'
    );

    my $key = Blockchain::Ethereum::Keystore::Key->new(
        private_key => pack "H*",
        '4646464646464646464646464646464646464646464646464646464646464646'
    );

    $key->sign_transaction($transaction);

    my $raw_transaction = $transaction->serialize;

    is(unpack("H*", $raw_transaction),
        '02f86c820539018009825208943535353535353535353535353535353535353535880de0b6b3a764000080c080a070816c3d026c13a53e98e5dc414398e9dcdf23e440e777114a3e04810e0dfb5da07d732e6b7f847b06d2baed033772d78407da8f4010fa9300df79f2209ba4c7a0'
    );

t/Transaction/legacy.t  view on Meta::CPAN

    my $transaction = Blockchain::Ethereum::Transaction::Legacy->new(
        nonce     => '0x9',
        gas_price => '0x4A817C800',
        gas_limit => '0x5208',
        to        => '0x3535353535353535353535353535353535353535',
        value     => '0xDE0B6B3A7640000',
        chain_id  => '0x1'
    );

    my $key = Blockchain::Ethereum::Keystore::Key->new(
        private_key => pack "H*",
        '4646464646464646464646464646464646464646464646464646464646464646'
    );

    $key->sign_transaction($transaction);

    my $raw_transaction = $transaction->serialize;

    is(unpack("H*", $raw_transaction),
        'f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83'
    );



( run in 0.919 second using v1.01-cache-2.11-cpan-beeb90c9504 )