// accountKeys provides functions to derive account identifiers and cryptocurrency addresses from credit account keys. // These keys are used to interface with the payment proof servers to manage a user's account credits. package accountKeys //TODO: Fix this package to interface with payment proof providers rather than account credit servers // A user's account private key is required to check their account balance // Each public key corresponds to an account identifier and crypto address(es) // We only use each key for a single purpose. // The current purposes are either to use its identifier or Ethereum address. // In the future, we will add more cryptocurrencies. //TODO: Add checksum byte to account identifiers import "seekia/internal/cryptography/blake3" import "seekia/internal/cryptocurrency/ethereumAddress" import "seekia/internal/cryptocurrency/cardanoAddress" import "seekia/internal/cryptography/edwardsKeys" import "seekia/internal/helpers" import "slices" import "encoding/binary" import "errors" func GetAccountIdentifierFromAccountPublicKey(accountPublicKey [32]byte)(string, error){ // This sequence of bytes is the string "accountidentifierkeyhash" decoded from base32 to bytes hashInputSuffix := []byte{0, 132, 234, 54, 104, 25, 27, 52, 21, 4, 138, 137, 131, 130, 71} hashInput := slices.Concat(accountPublicKey[:], hashInputSuffix) accountIdentifier, err := blake3.GetBlake3HashAsHexString(14, hashInput) if (err != nil) { return "", err } return accountIdentifier, nil } func GetCryptocurrencyAddressFromAccountPublicKey(cryptocurrency string, accountPublicKey [32]byte)(string, error){ if (cryptocurrency != "Ethereum" && cryptocurrency != "Cardano"){ return "", errors.New("GetCryptocurrencyAddressFromAccountPublicKey called with invalid cryptocurrency: " + cryptocurrency) } if (cryptocurrency == "Ethereum"){ accountEthereumAddress, err := ethereumAddress.GetCreditAccountEthereumAddressFromAccountPublicKey(accountPublicKey) if (err != nil) { return "", err } return accountEthereumAddress, nil } accountCardanoAddress, err := cardanoAddress.GetCreditAccountCardanoAddressFromAccountPublicKey(accountPublicKey) if (err != nil) { return "", err } return accountCardanoAddress, nil } // KeysType refers to the type of key // Different KeyTypes need to be derived, because each key should only be used for its intended keysType purpose. // "Identifier" keys are used for receiving to an account via an account identifier // "Ethereum" keys are used for receiving to an account via an Ethereum address // "Cardano" keys are used for receiving to an account via an Cardano address // Each cryptocurrency which we add will have its own keysType // We must be able to generate many different keys for each purpose, which we accomplish with the keysIndex parameter // This allows us to create as many addresses as we need, which we do because we need to be able to avoid address/identifier reuse. // Each networkType has its own keys, because each network type has its own account credit database and servers. //Outputs: // -[32]byte: Public key // -[64]byte: Private key // -error func GetCreditAccountPublicPrivateKeys(seedPhraseHash [32]byte, networkType byte, keysType string, keysIndex int)([32]byte, [64]byte, error){ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return [32]byte{}, [64]byte{}, errors.New("GetCreditAccountPublicPrivateKeys called with invalid networkType: " + networkTypeString) } if (keysType != "Identifier" && keysType != "Ethereum" && keysType != "Cardano"){ return [32]byte{}, [64]byte{}, errors.New("GetCreditAccountPublicPrivateKeys called with invalid keysType: " + keysType) } if (keysIndex < 1 || keysIndex > 4294967295){ return [32]byte{}, [64]byte{}, errors.New("GetCreditAccountPublicPrivateKeys called with invalid keysIndex.") } // This sequence of bytes is the string "creditaccountkeyhashsalt" decoded from base32 to bytes saltBytes := []byte{20, 72, 52, 76, 2, 19, 168, 217, 168, 152, 56, 36, 121, 1, 115} getKeysTypeByte := func()byte{ if (keysType == "Identifier"){ return 1 } if (keysType == "Ethereum"){ return 2 } // keysType == "Cardano" return 3 } keysTypeByte := getKeysTypeByte() keysIndexUint32 := uint32(keysIndex) hashInput := slices.Concat(seedPhraseHash[:], saltBytes) hashInput = append(hashInput, networkType, keysTypeByte) hashInput = binary.LittleEndian.AppendUint32(hashInput, keysIndexUint32) seedBytes, err := blake3.Get32ByteBlake3Hash(hashInput) if (err != nil) { return [32]byte{}, [64]byte{}, err } publicKeyArray, privateKeyArray := edwardsKeys.GetSeededEdwardsPublicAndPrivateKeys(seedBytes) return publicKeyArray, privateKeyArray, nil }