-
Notifications
You must be signed in to change notification settings - Fork 50
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
turbolent
wants to merge
13
commits into
master
Choose a base branch
from
bastian/linear-code-address-generator
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
177f46a
add new LinearCodeAddressGenerator contract
turbolent a818f5a
refactor code words into dictionary to improve extensibility
turbolent ac68008
update assets
turbolent e5496c6
test address is not valid with other chain codewords
turbolent 26ba620
hide (invalid) code words behind chain
turbolent 99e5d61
update assets
turbolent cff9ad6
Apply suggestions from code review
turbolent 4f3c420
clean up
turbolent aecc974
rename code word function
turbolent 7fb76ed
address at index is only valid in range [1 .. 2^45 - 1]
turbolent b79ca1d
update assets
turbolent 9ae9a32
remove TODO, use bitshift
turbolent d7f11be
use maxIndex variable
turbolent File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
turbolent marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// 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() { | ||
turbolent marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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. | ||
turbolent marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 | ||
} | ||
turbolent marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// 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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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( | ||
turbolent marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 | ||
] | ||
) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a bit more docs comments to this contract to make it more clear what the purpose of each part is? Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The public functions have docstrings, and the private functions are implementation details. I only ported the Go code over, I'm not an expert in the details of how the underlying code works.