Skip to content

Commit e5ae619

Browse files
committed
add new LinearCodeAddressGenerator contract
1 parent fc1ea49 commit e5ae619

File tree

3 files changed

+230
-0
lines changed

3 files changed

+230
-0
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
2+
/// LinearCodeAddressGenerator allows generating and validating Flow account addresses.
3+
access(all)
4+
contract LinearCodeAddressGenerator {
5+
6+
/// The code word for addresses on Flow Mainnet.
7+
access(all)
8+
let codeWordMainnet: UInt64
9+
10+
/// The code word for addresses on Flow Testnet.
11+
access(all)
12+
let codeWordTestnet: UInt64
13+
14+
/// The code word for addresses on transient networks, like the Flow Emulator.
15+
access(all)
16+
let codeWordTransient: UInt64
17+
18+
/// Rows of the generator matrix G of the [64,45]-code used for Flow addresses.
19+
/// G is a (k x n) matrix with coefficients in GF(2), each row is converted into
20+
/// a big endian integer representation of the GF(2) raw vector.
21+
/// G is used to generate the account addresses
22+
access(all)
23+
let generatorMatrixRows: [UInt64; 45]
24+
25+
/// Columns of the parity-check matrix H of the [64,45]-code used for Flow addresses.
26+
/// H is a (n x p) matrix with coefficients in GF(2), each column is converted into
27+
/// a big endian integer representation of the GF(2) column vector.
28+
/// H is used to verify a code word is a valid account address.
29+
access(all)
30+
let parityCheckMatrixColumns: [UInt64; 64]
31+
32+
init() {
33+
self.codeWordMainnet = 0
34+
self.codeWordTestnet = 0x6834ba37b3980209
35+
self.codeWordTransient = 0x1cb159857af02018
36+
37+
self.generatorMatrixRows = [
38+
0xe467b9dd11fa00df, 0xf233dcee88fe0abe, 0xf919ee77447b7497, 0xfc8cf73ba23a260d,
39+
0xfe467b9dd11ee2a1, 0xff233dcee888d807, 0xff919ee774476ce6, 0x7fc8cf73ba231d10,
40+
0x3fe467b9dd11b183, 0x1ff233dcee8f96d6, 0x8ff919ee774757ba, 0x47fc8cf73ba2b331,
41+
0x23fe467b9dd27f6c, 0x11ff233dceee8e82, 0x88ff919ee775dd8f, 0x447fc8cf73b905e4,
42+
0xa23fe467b9de0d83, 0xd11ff233dce8d5a7, 0xe88ff919ee73c38a, 0x7447fc8cf73f171f,
43+
0xba23fe467b9dcb2b, 0xdd11ff233dcb0cb4, 0xee88ff919ee26c5d, 0x77447fc8cf775dd3,
44+
0x3ba23fe467b9b5a1, 0x9dd11ff233d9117a, 0xcee88ff919efa640, 0xe77447fc8cf3e297,
45+
0x73ba23fe467fabd2, 0xb9dd11ff233fb16c, 0xdcee88ff919adde7, 0xee77447fc8ceb196,
46+
0xf73ba23fe4621cd0, 0x7b9dd11ff2379ac3, 0x3dcee88ff91df46c, 0x9ee77447fc88e702,
47+
0xcf73ba23fe4131b6, 0x67b9dd11ff240f9a, 0x33dcee88ff90f9e0, 0x19ee77447fcff4e3,
48+
0x8cf73ba23fe64091, 0x467b9dd11ff115c7, 0x233dcee88ffdb735, 0x919ee77447fe2309,
49+
0xc8cf73ba23fdc736
50+
]
51+
52+
self.parityCheckMatrixColumns = [
53+
0x00001, 0x00002, 0x00004, 0x00008, 0x00010, 0x00020, 0x00040, 0x00080,
54+
0x00100, 0x00200, 0x00400, 0x00800, 0x01000, 0x02000, 0x04000, 0x08000,
55+
0x10000, 0x20000, 0x40000, 0x7328d, 0x6689a, 0x6112f, 0x6084b, 0x433fd,
56+
0x42aab, 0x41951, 0x233ce, 0x22a81, 0x21948, 0x1ef60, 0x1deca, 0x1c639,
57+
0x1bdd8, 0x1a535, 0x194ac, 0x18c46, 0x1632b, 0x1529b, 0x14a43, 0x13184,
58+
0x12942, 0x118c1, 0x0f812, 0x0e027, 0x0d00e, 0x0c83c, 0x0b01d, 0x0a831,
59+
0x0982b, 0x07034, 0x0682a, 0x05819, 0x03807, 0x007d2, 0x00727, 0x0068e,
60+
0x0067c, 0x0059d, 0x004eb, 0x003b4, 0x0036a, 0x002d9, 0x001c7, 0x0003f
61+
]
62+
}
63+
64+
access(all)
65+
fun encodeWord(_ word: UInt64): UInt64 {
66+
67+
// Multiply the index GF(2) vector by the code generator matrix
68+
69+
var codeWord: UInt64 = 0
70+
var word = word
71+
72+
for generatorMatrixRow in self.generatorMatrixRows {
73+
if word & 1 == 1 {
74+
codeWord = codeWord ^ generatorMatrixRow
75+
}
76+
word = word >> 1
77+
}
78+
79+
return codeWord
80+
}
81+
82+
/// Returns the address at the given index, for the given chain code word.
83+
access(all)
84+
fun address(at index: UInt64, chainCodeWord: UInt64): Address {
85+
return Address(self.encodeWord(index) ^ chainCodeWord)
86+
}
87+
88+
/// Returns true if the given address is valid, for the given chain code word.
89+
access(all)
90+
fun isValidAddress(_ address: Address, chainCodeWord: UInt64): Bool {
91+
92+
let address = UInt64.fromBigEndianBytes(address.toBytes())!
93+
var codeWord = chainCodeWord ^ address
94+
95+
if codeWord == 0 {
96+
return false
97+
}
98+
99+
// Multiply the code word GF(2)-vector by the parity-check matrix
100+
101+
var parity: UInt64 = 0
102+
103+
for parityCheckMatrixColumn in self.parityCheckMatrixColumns {
104+
if codeWord & 1 == 1 {
105+
parity = parity ^ parityCheckMatrixColumn
106+
}
107+
codeWord = codeWord >> 1
108+
}
109+
110+
return parity == 0 && codeWord == 0
111+
}
112+
}

flow.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@
122122
"aliases": {
123123
"testing": "0x0000000000000007"
124124
}
125+
},
126+
"LinearCodeAddressGenerator": {
127+
"source": "./contracts/LinearCodeAddressGenerator.cdc",
128+
"aliases": {
129+
"testing": "0x0000000000000007"
130+
}
125131
}
126132
},
127133
"networks": {
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import Test
2+
import "LinearCodeAddressGenerator"
3+
4+
access(all)
5+
fun setup() {
6+
var err: Test.Error? = Test.deployContract(
7+
name: "LinearCodeAddressGenerator",
8+
path: "../contracts/LinearCodeAddressGenerator.cdc",
9+
arguments: [],
10+
)
11+
Test.expect(err, Test.beNil())
12+
}
13+
14+
access(all)
15+
fun generateAddresses(
16+
count: UInt64,
17+
chainCodeWord: UInt64
18+
): [Address] {
19+
let addresses: [Address] = []
20+
for index in InclusiveRange<UInt64>(1, count) {
21+
let address = LinearCodeAddressGenerator.address(
22+
at: index,
23+
chainCodeWord: chainCodeWord
24+
)
25+
addresses.append(address)
26+
}
27+
28+
return addresses
29+
}
30+
31+
access(all)
32+
fun checkAddresses(
33+
count: UInt64,
34+
chainCodeWord: UInt64,
35+
expected: [Address]
36+
) {
37+
let actual = generateAddresses(
38+
count: 10,
39+
chainCodeWord: chainCodeWord
40+
)
41+
42+
Test.assertEqual(expected, actual)
43+
44+
for address in actual {
45+
Test.assert(
46+
LinearCodeAddressGenerator.isValidAddress(
47+
address,
48+
chainCodeWord: chainCodeWord
49+
)
50+
)
51+
}
52+
}
53+
54+
access(all)
55+
fun testMainnet() {
56+
checkAddresses(
57+
count: 10,
58+
chainCodeWord: LinearCodeAddressGenerator.codeWordMainnet,
59+
expected: [
60+
0xe467b9dd11fa00df,
61+
0xf233dcee88fe0abe,
62+
0x1654653399040a61,
63+
0xf919ee77447b7497,
64+
0x1d7e57aa55817448,
65+
0x0b2a3299cc857e29,
66+
0xef4d8b44dd7f7ef6,
67+
0xfc8cf73ba23a260d,
68+
0x18eb4ee6b3c026d2,
69+
0x0ebf2bd52ac42cb3
70+
]
71+
)
72+
}
73+
74+
access(all)
75+
fun testTestnet() {
76+
checkAddresses(
77+
count: 10,
78+
chainCodeWord: LinearCodeAddressGenerator.codeWordTestnet,
79+
expected: [
80+
0x8c5303eaa26202d6,
81+
0x9a0766d93b6608b7,
82+
0x7e60df042a9c0868,
83+
0x912d5440f7e3769e,
84+
0x754aed9de6197641,
85+
0x631e88ae7f1d7c20,
86+
0x877931736ee77cff,
87+
0x94b84d0c11a22404,
88+
0x70dff4d1005824db,
89+
0x668b91e2995c2eba
90+
]
91+
)
92+
}
93+
94+
access(all)
95+
fun testTransient() {
96+
checkAddresses(
97+
count: 10,
98+
chainCodeWord: LinearCodeAddressGenerator.codeWordTransient,
99+
expected: [
100+
0xf8d6e0586b0a20c7,
101+
0xee82856bf20e2aa6,
102+
0x0ae53cb6e3f42a79,
103+
0xe5a8b7f23e8b548f,
104+
0x01cf0e2f2f715450,
105+
0x179b6b1cb6755e31,
106+
0xf3fcd2c1a78f5eee,
107+
0xe03daebed8ca0615,
108+
0x045a1763c93006ca,
109+
0x120e725050340cab
110+
]
111+
)
112+
}

0 commit comments

Comments
 (0)