Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions crypto/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,22 @@ func AddressFromPassphrase(passphrase string) (string, error) {
return privateKey.ToAddress(), nil
}

func AddressFromPublicKey(publicKeyHex string) (string, error) {
publicKey, err := PublicKeyFromHex(publicKeyHex)
if err != nil {
return "", err
}
return publicKey.ToAddress(), nil
}

func AddressFromPrivateKey(privateKeyHex string) (string, error) {
privateKey, err := PrivateKeyFromHex(privateKeyHex)
if err != nil {
return "", err
}
return privateKey.ToAddress(), nil
}

func AddressToBytes(address string) ([]byte, error) {
if !strings.HasPrefix(address, "0x") || len(address) != 2+AddressByteLength*2 {
return nil, ErrInvalidAddress
Expand Down
20 changes: 20 additions & 0 deletions crypto/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,26 @@ func TestAddressFromPassphrase(t *testing.T) {
assert.Equal(fixture.Data.Address, address)
}

func TestAddressFromPublicKey(t *testing.T) {
fixture := GetIdentityFixture()

address, err := AddressFromPublicKey(fixture.Data.PublicKey)

assert := assert.New(t)
assert.NoError(err)
assert.Equal(fixture.Data.Address, address)
}

func TestAddressFromPrivateKey(t *testing.T) {
fixture := GetIdentityFixture()

address, err := AddressFromPrivateKey(fixture.Data.PrivateKey)

assert := assert.New(t)
assert.NoError(err)
assert.Equal(fixture.Data.Address, address)
}

func TestValidateAddress(t *testing.T) {
fixture := GetIdentityFixture()

Expand Down
45 changes: 14 additions & 31 deletions crypto/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ var (
DefaultGasLimit = big.NewInt(1_000_000)
)

// Concrete transaction-type builders (BuildTransfer, BuildVote, etc.) are
// layered on top of this.
func NewTransaction() *Transaction {
return &Transaction{
Nonce: big.NewInt(1),
Expand All @@ -37,14 +35,14 @@ func BuildTransfer(to string, value *big.Int) (*Transaction, error) {
}

func BuildVote(validatorAddress string) (*Transaction, error) {
voteArg, err := AbiAddress(validatorAddress)
data, err := EncodeVoteData(validatorAddress)
if err != nil {
return nil, err
}

transaction := NewTransaction()
transaction.To = ContractConsensus
transaction.Data = AbiEncodeFunctionCall(AbiSignatureVote, voteArg)
transaction.Data = data
transaction.Vote = validatorAddress

return transaction, nil
Expand All @@ -53,7 +51,7 @@ func BuildVote(validatorAddress string) (*Transaction, error) {
func BuildUnvote() *Transaction {
transaction := NewTransaction()
transaction.To = ContractConsensus
transaction.Data = AbiEncodeFunctionCall(AbiSignatureUnvote)
transaction.Data = EncodeUnvoteData()

return transaction
}
Expand Down Expand Up @@ -92,7 +90,7 @@ func BuildValidatorUpdate(validatorPassphrase string) (*Transaction, error) {
func BuildValidatorResignation() *Transaction {
transaction := NewTransaction()
transaction.To = ContractConsensus
transaction.Data = AbiEncodeFunctionCall(AbiSignatureResignValidator)
transaction.Data = EncodeValidatorResignationData()

return transaction
}
Expand Down Expand Up @@ -122,13 +120,14 @@ func validateUsername(username string) error {
}

func BuildUsernameRegistration(username string) (*Transaction, error) {
if err := validateUsername(username); err != nil {
data, err := EncodeUsernameRegistrationData(username)
if err != nil {
return nil, err
}

transaction := NewTransaction()
transaction.To = ContractUsernames
transaction.Data = AbiEncodeFunctionCall(AbiSignatureRegisterUsername, AbiString(username))
transaction.Data = data
transaction.Username = username

return transaction, nil
Expand All @@ -137,7 +136,7 @@ func BuildUsernameRegistration(username string) (*Transaction, error) {
func BuildUsernameResignation() *Transaction {
transaction := NewTransaction()
transaction.To = ContractUsernames
transaction.Data = AbiEncodeFunctionCall(AbiSignatureResignUsername)
transaction.Data = EncodeUsernameResignationData()

return transaction
}
Expand All @@ -150,11 +149,7 @@ func BuildMultiPayment(addresses []string, amounts []*big.Int) (*Transaction, er
return nil, errors.New("crypto: multi-payment requires at least one recipient")
}

addressesArg, err := AbiAddressArray(addresses)
if err != nil {
return nil, err
}
amountsArg, err := AbiUint256Array(amounts)
data, err := EncodeMultiPaymentData(addresses, amounts)
if err != nil {
return nil, err
}
Expand All @@ -167,7 +162,7 @@ func BuildMultiPayment(addresses []string, amounts []*big.Int) (*Transaction, er
transaction := NewTransaction()
transaction.To = ContractMultipayment
transaction.Value = total
transaction.Data = AbiEncodeFunctionCall(AbiSignatureMultipayment, addressesArg, amountsArg)
transaction.Data = data
transaction.PaymentAddresses = addresses
transaction.PaymentAmounts = amounts

Expand All @@ -194,22 +189,14 @@ func BuildBatchTransfer(tokenAddress string, recipients []string, amounts []*big
return nil, errors.New("crypto: batch transfer requires at least one recipient")
}

tokenArg, err := AbiAddress(tokenAddress)
if err != nil {
return nil, err
}
recipientsArg, err := AbiAddressArray(recipients)
if err != nil {
return nil, err
}
amountsArg, err := AbiUint256Array(amounts)
data, err := EncodeBatchTransferData(tokenAddress, recipients, amounts)
if err != nil {
return nil, err
}

transaction := NewTransaction()
transaction.To = ContractBatchTransfer
transaction.Data = AbiEncodeFunctionCall(AbiSignatureERC20BatchTransferFrom, tokenArg, recipientsArg, amountsArg)
transaction.Data = data

return transaction, nil
}
Expand Down Expand Up @@ -240,18 +227,14 @@ func BuildTokenTransfer(tokenAddress string, recipient string, amount *big.Int)
return nil, err
}

recipientArg, err := AbiAddress(recipient)
if err != nil {
return nil, err
}
amountArg, err := AbiUint256(amount)
data, err := EncodeTokenTransferData(recipient, amount)
if err != nil {
return nil, err
}

transaction := NewTransaction()
transaction.To = tokenAddress
transaction.Data = AbiEncodeFunctionCall(AbiSignatureERC20Transfer, recipientArg, amountArg)
transaction.Data = data

return transaction, nil
}
13 changes: 8 additions & 5 deletions crypto/fixtures.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,15 @@ func GetTransactionFixture(name string) TestingTransactionFixture {

type TestingIdentityFixture struct {
Data struct {
PrivateKey string `json:"privateKey,omitempty"`
PublicKey string `json:"publicKey,omitempty"`
Address string `json:"address,omitempty"`
WIF string `json:"wif,omitempty"`
PrivateKey string `json:"privateKey,omitempty"`
PublicKey string `json:"publicKey,omitempty"`
Address string `json:"address,omitempty"`
WIF string `json:"wif,omitempty"`
ValidatorPublicKey string `json:"validatorPublicKey,omitempty"`
ValidatorPrivateKey string `json:"validatorPrivateKey,omitempty"`
} `json:"data,omitempty"`
Passphrase string `json:"passphrase,omitempty"`
Passphrase string `json:"passphrase,omitempty"`
SecondPassphrase string `json:"secondPassphrase,omitempty"`
}

type TestingMessageFixture struct {
Expand Down
13 changes: 8 additions & 5 deletions crypto/fixtures/identity.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
{
"data": {
"privateKey": "d8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712",
"publicKey": "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192",
"address": "0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01",
"wif": "Ue7A6vSx7ewATPp2dA6UbJ8F39DbZwaHTqhD1MrhzmJqRJmvfZ6C"
"publicKey": "0243333347c8cbf4e3cbc7a96964181d02a2b0c854faa2fef86b4b8d92afcf473d",
"privateKey": "50829dd3b7ffbe2df401d730b5e60cea7520ba3f3a18e5b1490707667fb43fae",
"address": "0x1E6747BEAa5B4076a6A98D735DF8c35a70D18Bdd",
"wif": "UZYnRZ8qpeQWTLeCNzw93guWSdKLmr2vHEWGG4sNv7TJofL7TZvy",
"validatorPublicKey": "b209f4a7454ae17c5808991dffbf204c747b851f351d2ce72a6e18903d0e2f609e0328ebbc3fb97cd4d3660b4bc156f1",
"validatorPrivateKey": "6ec4993df152b10e672567c1fdf854a4cee50708fa30986a7d9b259673099175"
},
"passphrase": "this is a top secret passphrase"
"passphrase": "found lobster oblige describe ready addict body brave live vacuum display salute lizard combine gift resemble race senior quality reunion proud tell adjust angle",
"secondPassphrase": "gold favorite math anchor detect march purpose such sausage crucial reform novel connect misery update episode invite salute barely garbage exclude winner visa cruise"
}
30 changes: 30 additions & 0 deletions crypto/private_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package crypto

import (
"crypto/sha256"
"errors"
"fmt"

"github.com/btcsuite/btcutil/base58"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
)

var ErrInvalidWif = errors.New("crypto: invalid WIF")

const ecdsaCurveByteLength = 32

// EcdsaSignature is an ECDSA secp256k1 recoverable signature: R and S are
Expand All @@ -31,6 +34,33 @@ func PrivateKeyFromHex(privateKeyHex string) (*PrivateKey, error) {
return PrivateKeyFromBytes(HexDecode(privateKeyHex)), nil
}

func PrivateKeyFromWif(wif string) (*PrivateKey, error) {
decoded, version, err := base58.CheckDecode(wif)
if err != nil {
return nil, ErrInvalidWif
}
if version != GetNetwork().Wif {
return nil, ErrInvalidWif
}

switch len(decoded) {
case ecdsaCurveByteLength:
return PrivateKeyFromBytes(decoded), nil
case ecdsaCurveByteLength + 1:
return PrivateKeyFromBytes(decoded[:ecdsaCurveByteLength]), nil
default:
return nil, ErrInvalidWif
}
}

func WIFFromPassphrase(passphrase string) (string, error) {
privateKey, err := PrivateKeyFromPassphrase(passphrase)
if err != nil {
return "", err
}
return privateKey.ToWif(), nil
}

func PrivateKeyFromBytes(bytes []byte) *PrivateKey {
privateKey := secp256k1.PrivKeyFromBytes(bytes)
return &PrivateKey{
Expand Down
27 changes: 27 additions & 0 deletions crypto/private_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,30 @@ func TestPrivateKeyToWif(t *testing.T) {
assert := assert.New(t)
assert.Equal(fixture.Data.WIF, privateKey.ToWif())
}

func TestPrivateKeyFromWif(t *testing.T) {
fixture := GetIdentityFixture()

privateKey, err := PrivateKeyFromWif(fixture.Data.WIF)

assert := assert.New(t)
assert.NoError(err)
assert.Equal(fixture.Data.PrivateKey, privateKey.ToHex())
}

func TestPrivateKeyFromWifInvalidWifErrors(t *testing.T) {
assert := assert.New(t)

_, err := PrivateKeyFromWif("not-a-wif")
assert.ErrorIs(err, ErrInvalidWif)
}

func TestWIFFromPassphrase(t *testing.T) {
fixture := GetIdentityFixture()

wif, err := WIFFromPassphrase(fixture.Passphrase)

assert := assert.New(t)
assert.NoError(err)
assert.Equal(fixture.Data.WIF, wif)
}
2 changes: 1 addition & 1 deletion crypto/slot.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ func GetTime() int32 {
}

func GetEpoch() uint32 {
return uint32(GetNetwork().Epoch.Second())
return uint32(GetNetwork().Epoch.Unix())
}
16 changes: 16 additions & 0 deletions crypto/slot_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package crypto

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestGetEpoch(t *testing.T) {
assert.Equal(t, uint32(GetNetwork().Epoch.Unix()), GetEpoch())
assert.EqualValues(t, 1490101200, GetEpoch()) // 2017-03-21T13:00:00Z
}

func TestGetTime(t *testing.T) {
assert.Greater(t, GetTime(), int32(0))
}
Loading
Loading