Skip to content

Commit

Permalink
refactor: break up hashing.go to reduce dependencies. According to Go…
Browse files Browse the repository at this point in the history
… practice, too much DRY leads to too many dependencies, thus some duplication is allowed.

leftPadByteSlice, buildOracleQueryID, buildContractID moved to transactions.go
Namehash moved to helpers.go
randomBytes duplicated in generateCommitmentID and moved to keystore.go:KeystoreSeal
uuidV4 moved to keystore.go
generateCommitmentID/computeCommitmentID moved to helpers.go
buildRLPMessage, buildIDTag, readIDTag moved to transactions.go
  • Loading branch information
randomshinichi committed Oct 10, 2019
1 parent caf7069 commit 5188c43
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 172 deletions.
172 changes: 0 additions & 172 deletions aeternity/hashing.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
package aeternity

import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"fmt"
"math/big"
"strings"

"github.com/aeternity/aepp-sdk-go/v5/utils"
"github.com/btcsuite/btcutil/base58"
rlp "github.com/randomshinichi/rlpae"
uuid "github.com/satori/go.uuid"
"golang.org/x/crypto/blake2b"
)

Expand Down Expand Up @@ -92,173 +87,6 @@ func Blake2bHash(in []byte) (out []byte, err error) {
return
}

func leftPadByteSlice(length int, data []byte) []byte {
dataLen := len(data)
t := make([]byte, length-dataLen)
paddedSlice := append(t, data...)
return paddedSlice
}

func buildOracleQueryID(sender string, senderNonce uint64, recipient string) (id string, err error) {
queryIDBin := []byte{}
senderBin, err := Decode(sender)
if err != nil {
return
}
queryIDBin = append(queryIDBin, senderBin...)

senderNonceBytes := utils.NewIntFromUint64(senderNonce).Bytes()
senderNonceBytesPadded := leftPadByteSlice(32, senderNonceBytes)
queryIDBin = append(queryIDBin, senderNonceBytesPadded...)

recipientBin, err := Decode(recipient)
if err != nil {
return
}
queryIDBin = append(queryIDBin, recipientBin...)

hashedQueryID, err := Blake2bHash(queryIDBin)
if err != nil {
return
}
id = Encode(PrefixOracleQueryID, hashedQueryID)
return
}

func buildContractID(sender string, senderNonce uint64) (ctID string, err error) {
senderBin, err := Decode(sender)
if err != nil {
return ctID, err
}

l := big.Int{}
l.SetUint64(senderNonce)

ctIDUnhashed := append(senderBin, l.Bytes()...)
ctIDHashed, err := Blake2bHash(ctIDUnhashed)
if err != nil {
return ctID, err
}

ctID = Encode(PrefixContractPubkey, ctIDHashed)
return ctID, err
}

// Namehash calculate the Namehash of a string. Names within aeternity are
// generally referred to only by their namehashes.
//
// The implementation is the same as ENS EIP-137
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-137.md#namehash-algorithm
// but using Blake2b.
func Namehash(name string) []byte {
buf := make([]byte, 32)
for _, s := range strings.Split(name, ".") {
sh, _ := Blake2bHash([]byte(s))
buf, _ = Blake2bHash(append(buf, sh...))
}
return buf
}

// randomBytes returns securely generated random bytes. It will return an error
// if the system's secure random number generator fails to function correctly,
// in which case the caller should not continue.
func randomBytes(n int) ([]byte, error) {
b := make([]byte, n)
_, err := rand.Read(b)
// Note that err == nil only if we read len(b) bytes.
if err != nil {
return nil, err
}
return b, nil
}

// generate an uuid v4 string
func uuidV4() (u string) {
return fmt.Sprint(uuid.NewV4())
}

// generateCommitmentID gives a commitment ID 'cm_...' given a particular AENS
// name. It is split into the deterministic part computeCommitmentID(), which
// can be tested, and the part incorporating random salt generateCommitmentID()
//
// since the salt is a uint256, which Erlang handles well, but Go has nothing
// similar to it, it is imperative that the salt be kept as a bytearray unless
// you really have to convert it into an integer. Which you usually don't,
// because it's a salt.
func generateCommitmentID(name string) (ch string, salt *big.Int, err error) {
saltBytes, err := randomBytes(32)
if err != nil {
return
}

ch, err = computeCommitmentID(name, saltBytes)

salt = new(big.Int)
salt.SetBytes(saltBytes)

return ch, salt, err
}

func computeCommitmentID(name string, salt []byte) (ch string, err error) {
nh := append(Namehash(name), salt...)
nh, _ = Blake2bHash(nh)
ch = Encode(PrefixCommitment, nh)
return
}

func buildRLPMessage(tag uint, version uint, fields ...interface{}) (rlpRawMsg []byte, err error) {
// create a message of the transaction and signature
data := []interface{}{tag, version}
data = append(data, fields...)
// fmt.Printf("TX %+v\n\n", data)
// encode the message using rlp
rlpRawMsg, err = rlp.EncodeToBytes(data)
// fmt.Printf("ENCODED %+v\n\n", data)
return
}

// buildIDTag assemble an id() object see
// https://github.com/aeternity/protocol/blob/master/serializations.md#the-id-type
func buildIDTag(IDTag uint8, encodedHash string) (v []uint8, err error) {
raw, err := Decode(encodedHash)
v = []uint8{IDTag}
for _, x := range raw {
v = append(v, uint8(x))
}
return
}

// readIDTag disassemble an id() object see
// https://github.com/aeternity/protocol/blob/master/serializations.md#the-id-type
func readIDTag(v []uint8) (IDTag uint8, encodedHash string, err error) {
IDTag = v[0]
hash := []byte{}
for _, x := range v[1:] {
hash = append(hash, byte(x))
}

var prefix HashPrefix
switch IDTag {
case IDTagAccount:
prefix = PrefixAccountPubkey
case IDTagName:
prefix = PrefixName
case IDTagCommitment:
prefix = PrefixCommitment
case IDTagOracle:
prefix = PrefixOraclePubkey
case IDTagContract:
prefix = PrefixContractPubkey
case IDTagChannel:
prefix = PrefixChannel
default:
return 0, "", fmt.Errorf("readIDTag() does not recognize this IDTag (first byte in input array): %v", IDTag)
}

encodedHash = Encode(prefix, hash)
return
}

// DecodeRLPMessage takes an RLP serialized bytearray and parses the RLP to
// return the deserialized, structured data as bytearrays ([]interfaces that
// should be later coerced into specific types). Only meant for debugging
Expand Down
49 changes: 49 additions & 0 deletions aeternity/helpers.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package aeternity

import (
"crypto/rand"
"fmt"
"io/ioutil"
"math/big"
"os"
"path/filepath"
"strings"
"time"

rlp "github.com/randomshinichi/rlpae"
Expand Down Expand Up @@ -444,3 +446,50 @@ func SignBroadcastWaitTransaction(tx rlp.Encoder, signingAccount *Account, n *No
blockHeight, blockHash, err = WaitForTransactionForXBlocks(n, hash, x)
return
}

// generateCommitmentID gives a commitment ID 'cm_...' given a particular AENS
// name. It is split into the deterministic part computeCommitmentID(), which
// can be tested, and the part incorporating random salt generateCommitmentID()
//
// since the salt is a uint256, which Erlang handles well, but Go has nothing
// similar to it, it is imperative that the salt be kept as a bytearray unless
// you really have to convert it into an integer. Which you usually don't,
// because it's a salt.
func generateCommitmentID(name string) (ch string, salt *big.Int, err error) {
// Generate 32 random bytes for a salt
saltBytes := make([]byte, 32)
_, err = rand.Read(saltBytes)
// Note that err == nil only if we read len(b) bytes.
if err != nil {
return
}

ch, err = computeCommitmentID(name, saltBytes)

salt = new(big.Int)
salt.SetBytes(saltBytes)

return ch, salt, err
}

func computeCommitmentID(name string, salt []byte) (ch string, err error) {
nh := append(Namehash(name), salt...)
nh, _ = Blake2bHash(nh)
ch = Encode(PrefixCommitment, nh)
return
}

// Namehash calculate the Namehash of a string. Names within aeternity are
// generally referred to only by their namehashes.
//
// The implementation is the same as ENS EIP-137
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-137.md#namehash-algorithm
// but using Blake2b.
func Namehash(name string) []byte {
buf := make([]byte, 32)
for _, s := range strings.Split(name, ".") {
sh, _ := Blake2bHash([]byte(s))
buf, _ = Blake2bHash(append(buf, sh...))
}
return buf
}
20 changes: 20 additions & 0 deletions aeternity/keystore.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package aeternity

import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"time"

uuid "github.com/satori/go.uuid"
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/nacl/secretbox"
)
Expand Down Expand Up @@ -80,6 +82,19 @@ func KeystoreOpen(data []byte, password string) (account *Account, err error) {
return
}

// randomBytes returns securely generated random bytes. It will return an error
// if the system's secure random number generator fails to function correctly,
// in which case the caller should not continue.
func randomBytes(n int) ([]byte, error) {
b := make([]byte, n)
_, err := rand.Read(b)
// Note that err == nil only if we read len(b) bytes.
if err != nil {
return nil, err
}
return b, nil
}

// KeystoreSeal create an encrypted json keystore with the private key of the account
func KeystoreSeal(account *Account, password string) (j []byte, e error) {
// normalize pwd
Expand Down Expand Up @@ -147,3 +162,8 @@ func toISO8601(t time.Time) string {
}
return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz)
}

// generate an uuid v4 string
func uuidV4() (u string) {
return fmt.Sprint(uuid.NewV4())
}

0 comments on commit 5188c43

Please sign in to comment.