Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new LinearCodeAddressGenerator contract #467

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
127 changes: 127 additions & 0 deletions contracts/LinearCodeAddressGenerator.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@

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

access(all)
enum Chain: UInt8 {
access(all)
case Mainnet

access(all)
case Testnet

access(all)
case Transient
}

/// 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(self)
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(self)
let parityCheckMatrixColumns: [UInt64; 64]

init() {
turbolent marked this conversation as resolved.
Show resolved Hide resolved
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(self)
fun invalidCodeWord(forChain chain: Chain): UInt64 {
switch chain {
case Chain.Mainnet:
return 0
case Chain.Testnet:
return 0x6834ba37b3980209
case Chain.Transient:
return 0x1cb159857af02018
default:
panic("unsupported chain")
}
}

access(self)
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.
access(all)
fun address(at index: UInt64, chain: Chain): Address? {
// The index must be in the range [1 .. 2^45 - 1]
// TODO: Use Cadence exponentiation operator, once available
turbolent marked this conversation as resolved.
Show resolved Hide resolved
if index < 1 || index > 35184372088831 {
return nil
}
return Address(self.encodeWord(index) ^ self.invalidCodeWord(forChain: chain))
}

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

let address = UInt64.fromBigEndianBytes(address.toBytes())!
var codeWord = self.invalidCodeWord(forChain: chain) ^ 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
23 changes: 23 additions & 0 deletions lib/go/contracts/internal/assets/assets.go

Large diffs are not rendered by default.

166 changes: 166 additions & 0 deletions tests/LinearCodeAddressGenerator_test.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
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,
chain: LinearCodeAddressGenerator.Chain
): [Address] {
let addresses: [Address] = []
for index in InclusiveRange<UInt64>(1, count) {
let address = LinearCodeAddressGenerator.address(
at: index,
chain: chain
)
addresses.append(address!)
}

return addresses
}

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

Test.assertEqual(expected, actual)

for address in actual {
Test.assert(
LinearCodeAddressGenerator.isValidAddress(
turbolent marked this conversation as resolved.
Show resolved Hide resolved
address,
chain: chain
)
)

for otherChain in [
LinearCodeAddressGenerator.Chain.Mainnet,
LinearCodeAddressGenerator.Chain.Testnet,
LinearCodeAddressGenerator.Chain.Transient
] {
if otherChain == chain {
continue
}

Test.assert(
!LinearCodeAddressGenerator.isValidAddress(
address,
chain: otherChain
)
)
}
}
}

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

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

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

access(all)
fun testAddressAtIndex() {

let minIndex: UInt64 = 1
let maxIndex: UInt64 = 35184372088831

for chain in [
LinearCodeAddressGenerator.Chain.Mainnet,
LinearCodeAddressGenerator.Chain.Testnet,
LinearCodeAddressGenerator.Chain.Transient
] {
let lessThanMinAddress = LinearCodeAddressGenerator.address(
at: minIndex - 1,
chain: chain
)
Test.assert(lessThanMinAddress == nil)

let minAddress = LinearCodeAddressGenerator.address(
at: minIndex,
chain: chain
)
Test.assert(minAddress != nil)

let maxAddress = LinearCodeAddressGenerator.address(
at: maxIndex,
chain: chain
)
Test.assert(maxAddress != nil)

let greaterThanMaxAddress = LinearCodeAddressGenerator.address(
at: 35184372088832,
chain: chain
)
Test.assert(greaterThanMaxAddress == nil)
}
}
Loading