Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
112 changes: 112 additions & 0 deletions contracts/LinearCodeAddressGenerator.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@

/// LinearCodeAddressGenerator allows generating and validating Flow account addresses.
access(all)
contract LinearCodeAddressGenerator {

/// The code word for addresses on Flow Mainnet.
access(all)
let codeWordMainnet: UInt64

/// The code word for addresses on Flow Testnet.
access(all)
let codeWordTestnet: UInt64

/// The code word for addresses on transient networks, like the Flow Emulator.
access(all)
let codeWordTransient: UInt64

/// Rows of the generator matrix G of the [64,45]-code used for Flow addresses.
/// G is a (k x n) matrix with coefficients in GF(2), each row is converted into
/// a big endian integer representation of the GF(2) raw vector.
/// G is used to generate the account addresses
access(all)
let generatorMatrixRows: [UInt64; 45]

/// Columns of the parity-check matrix H of the [64,45]-code used for Flow addresses.
/// H is a (n x p) matrix with coefficients in GF(2), each column is converted into
/// a big endian integer representation of the GF(2) column vector.
/// H is used to verify a code word is a valid account address.
access(all)
let parityCheckMatrixColumns: [UInt64; 64]

init() {
self.codeWordMainnet = 0
self.codeWordTestnet = 0x6834ba37b3980209
self.codeWordTransient = 0x1cb159857af02018

self.generatorMatrixRows = [
0xe467b9dd11fa00df, 0xf233dcee88fe0abe, 0xf919ee77447b7497, 0xfc8cf73ba23a260d,
0xfe467b9dd11ee2a1, 0xff233dcee888d807, 0xff919ee774476ce6, 0x7fc8cf73ba231d10,
0x3fe467b9dd11b183, 0x1ff233dcee8f96d6, 0x8ff919ee774757ba, 0x47fc8cf73ba2b331,
0x23fe467b9dd27f6c, 0x11ff233dceee8e82, 0x88ff919ee775dd8f, 0x447fc8cf73b905e4,
0xa23fe467b9de0d83, 0xd11ff233dce8d5a7, 0xe88ff919ee73c38a, 0x7447fc8cf73f171f,
0xba23fe467b9dcb2b, 0xdd11ff233dcb0cb4, 0xee88ff919ee26c5d, 0x77447fc8cf775dd3,
0x3ba23fe467b9b5a1, 0x9dd11ff233d9117a, 0xcee88ff919efa640, 0xe77447fc8cf3e297,
0x73ba23fe467fabd2, 0xb9dd11ff233fb16c, 0xdcee88ff919adde7, 0xee77447fc8ceb196,
0xf73ba23fe4621cd0, 0x7b9dd11ff2379ac3, 0x3dcee88ff91df46c, 0x9ee77447fc88e702,
0xcf73ba23fe4131b6, 0x67b9dd11ff240f9a, 0x33dcee88ff90f9e0, 0x19ee77447fcff4e3,
0x8cf73ba23fe64091, 0x467b9dd11ff115c7, 0x233dcee88ffdb735, 0x919ee77447fe2309,
0xc8cf73ba23fdc736
]

self.parityCheckMatrixColumns = [
0x00001, 0x00002, 0x00004, 0x00008, 0x00010, 0x00020, 0x00040, 0x00080,
0x00100, 0x00200, 0x00400, 0x00800, 0x01000, 0x02000, 0x04000, 0x08000,
0x10000, 0x20000, 0x40000, 0x7328d, 0x6689a, 0x6112f, 0x6084b, 0x433fd,
0x42aab, 0x41951, 0x233ce, 0x22a81, 0x21948, 0x1ef60, 0x1deca, 0x1c639,
0x1bdd8, 0x1a535, 0x194ac, 0x18c46, 0x1632b, 0x1529b, 0x14a43, 0x13184,
0x12942, 0x118c1, 0x0f812, 0x0e027, 0x0d00e, 0x0c83c, 0x0b01d, 0x0a831,
0x0982b, 0x07034, 0x0682a, 0x05819, 0x03807, 0x007d2, 0x00727, 0x0068e,
0x0067c, 0x0059d, 0x004eb, 0x003b4, 0x0036a, 0x002d9, 0x001c7, 0x0003f
]
}

access(all)
fun encodeWord(_ word: UInt64): UInt64 {

// Multiply the index GF(2) vector by the code generator matrix

var codeWord: UInt64 = 0
var word = word

for generatorMatrixRow in self.generatorMatrixRows {
if word & 1 == 1 {
codeWord = codeWord ^ generatorMatrixRow
}
word = word >> 1
}

return codeWord
}

/// Returns the address at the given index, for the given chain code word.
access(all)
fun address(at index: UInt64, chainCodeWord: UInt64): Address {
return Address(self.encodeWord(index) ^ chainCodeWord)
}

/// Returns true if the given address is valid, for the given chain code word.
access(all)
fun isValidAddress(_ address: Address, chainCodeWord: UInt64): Bool {

let address = UInt64.fromBigEndianBytes(address.toBytes())!
var codeWord = chainCodeWord ^ address

if codeWord == 0 {
return false
}

// Multiply the code word GF(2)-vector by the parity-check matrix

var parity: UInt64 = 0

for parityCheckMatrixColumn in self.parityCheckMatrixColumns {
if codeWord & 1 == 1 {
parity = parity ^ parityCheckMatrixColumn
}
codeWord = codeWord >> 1
}

return parity == 0 && codeWord == 0
}
}
6 changes: 6 additions & 0 deletions flow.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@
"testnet": "9a0766d93b6608b7"
}
},
"LinearCodeAddressGenerator": {
"source": "./contracts/LinearCodeAddressGenerator.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"LockedTokens": {
"source": "./contracts/LockedTokens.cdc",
"aliases": {
Expand Down
112 changes: 112 additions & 0 deletions tests/LinearCodeAddressGenerator_test.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import Test
import "LinearCodeAddressGenerator"

access(all)
fun setup() {
var err: Test.Error? = Test.deployContract(
name: "LinearCodeAddressGenerator",
path: "../contracts/LinearCodeAddressGenerator.cdc",
arguments: [],
)
Test.expect(err, Test.beNil())
}

access(all)
fun generateAddresses(
count: UInt64,
chainCodeWord: UInt64
): [Address] {
let addresses: [Address] = []
for index in InclusiveRange<UInt64>(1, count) {
let address = LinearCodeAddressGenerator.address(
at: index,
chainCodeWord: chainCodeWord
)
addresses.append(address)
}

return addresses
}

access(all)
fun checkAddresses(
count: UInt64,
chainCodeWord: UInt64,
expected: [Address]
) {
let actual = generateAddresses(
count: 10,
chainCodeWord: chainCodeWord
)

Test.assertEqual(expected, actual)

for address in actual {
Test.assert(
LinearCodeAddressGenerator.isValidAddress(
address,
chainCodeWord: chainCodeWord
)
)
}
}

access(all)
fun testMainnet() {
checkAddresses(
count: 10,
chainCodeWord: LinearCodeAddressGenerator.codeWordMainnet,
expected: [
0xe467b9dd11fa00df,
0xf233dcee88fe0abe,
0x1654653399040a61,
0xf919ee77447b7497,
0x1d7e57aa55817448,
0x0b2a3299cc857e29,
0xef4d8b44dd7f7ef6,
0xfc8cf73ba23a260d,
0x18eb4ee6b3c026d2,
0x0ebf2bd52ac42cb3
]
)
}

access(all)
fun testTestnet() {
checkAddresses(
count: 10,
chainCodeWord: LinearCodeAddressGenerator.codeWordTestnet,
expected: [
0x8c5303eaa26202d6,
0x9a0766d93b6608b7,
0x7e60df042a9c0868,
0x912d5440f7e3769e,
0x754aed9de6197641,
0x631e88ae7f1d7c20,
0x877931736ee77cff,
0x94b84d0c11a22404,
0x70dff4d1005824db,
0x668b91e2995c2eba
]
)
}

access(all)
fun testTransient() {
checkAddresses(
count: 10,
chainCodeWord: LinearCodeAddressGenerator.codeWordTransient,
expected: [
0xf8d6e0586b0a20c7,
0xee82856bf20e2aa6,
0x0ae53cb6e3f42a79,
0xe5a8b7f23e8b548f,
0x01cf0e2f2f715450,
0x179b6b1cb6755e31,
0xf3fcd2c1a78f5eee,
0xe03daebed8ca0615,
0x045a1763c93006ca,
0x120e725050340cab
]
)
}
Loading