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

[FEAT] Support rotate left and right operations #141

Merged

Conversation

roeezolantz
Copy link
Contributor

@roeezolantz roeezolantz commented Sep 11, 2024

  • Add rol (rotate left) and ror (rotate right) operations, supports both Fhe.rol(a,b) and bounded a.rol(b) for all euint<8-128> types
  • Fix solgen patternAllowedOperationsEbool to prevent or affect ror and make it available for booleans

https://github.com/FhenixProtocol/warp-drive/pull/42 - Corresponding warp-drive PR

@@ -13,7 +13,7 @@ export const EInputType = [
// FYI: Operations ["sealoutput", "seal", "decrypt", "ne"] are the minimum required for
// non failing generated code.

const patternAllowedOperationsEbool = ["ne|eq|^and$|or|xor|sealoutput|select|seal|decrypt|not"];
const patternAllowedOperationsEbool = ["ne|eq|^and$|^or$|xor|sealoutput|select|seal|decrypt|not"];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix for conflict with ror

@roeezolantz roeezolantz marked this pull request as ready for review September 11, 2024 17:01
@liorbond
Copy link
Collaborator

LGTM, generally I'm missing some unittests in Engine, Driver, etc. please make sure to follow the other operations and see what are the tests that are written for them

@@ -1354,6 +1355,104 @@ func Shr(utype byte, lhsHash []byte, rhsHash []byte, tp *TxParams) ([]byte, uint
return ctHash[:], gas, nil
}

func Rol(utype byte, lhsHash []byte, rhsHash []byte, tp *TxParams) ([]byte, uint64, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rol and Ror functions are and always will be the same, therefore I would probably create a helper function somewhere for "rotate" that does everything for which the only differences (based on function input or something?) are the "functionName" and the direction (for which you can choose calling ror or rol)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've noticed that it might corrupt the solgen generator due to the way thecontracts_parser.ts works, cuz it's relying on function calls like get2VerifiedOperands to understand that it's 2 operands, and having if lhs.UintType != rhs.UintType to make sure they are the same types and etc..
BUT, i'll add support and lmk if you think it's better

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it can be said for every function that

  1. receives two euints
  2. returns 1 euint

I mean we can create a 2OperandMathImpl()™ bc I think all these functions of the same type have basically the same implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following the conversations with Tom and Itzik, i'm reverting this refactor for now as it's out of scope here and can be handled better as a standalone separated task

contract RolTest {
using Utils for *;

function rol(string calldata test, uint256 a, uint256 b) public pure returns (uint256 output) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you write tests to trigger and test those functions?

@@ -152,7 +152,7 @@ func getRawPrecompileGas(precompileName types.PrecompileName, uintType fhe.Encry
case fhe.Uint128:
return 250000
}
case types.Shl, types.Shr:
case types.Shl, types.Shr, types.Rol, types.Ror:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TEMP

Copy link
Contributor Author

@roeezolantz roeezolantz Sep 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark for mean time consumption on 10 runs each -
(running bench of 100 each now as well)
image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After 100 runs each :
image

Copy link
Contributor

@eshelB eshelB left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@@ -85,6 +86,21 @@ async function analyzeGoFile(
continue;
}

// Support for shoretened util-based functions with "solgen: 2 verified operands"
const match = trimmedLine.match(solgenVerifiedOperands);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe put this block together with the other solgen: matches above? it's kinda separate. Also why does it require a separate push to the specificFunctionAnalysis that is not required for other solgen comments? I may have missed something

@@ -1354,6 +1355,104 @@ func Shr(utype byte, lhsHash []byte, rhsHash []byte, tp *TxParams) ([]byte, uint
return ctHash[:], gas, nil
}

func Rol(utype byte, lhsHash []byte, rhsHash []byte, tp *TxParams) ([]byte, uint64, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it can be said for every function that

  1. receives two euints
  2. returns 1 euint

I mean we can create a 2OperandMathImpl()™ bc I think all these functions of the same type have basically the same implementation.

Comment on lines 1601 to 1614
const generateTestCases = (bitSize: number) => {
const basePattern = BigInt(`0b${'11100000'.repeat(bitSize / 8)}`);
const mask = (1n << BigInt(bitSize)) - 1n;

return [1, 2, 3, 4, 5].map(rotateAmount => {
const rotated = ((basePattern >> BigInt(rotateAmount)) | (basePattern << BigInt(bitSize - rotateAmount))) & mask;
return {
a: basePattern,
b: rotateAmount,
expectedResult: rotated,
name: ` ror ${rotateAmount}`
};
});
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not mistaken, these test cases check only rotates of zeroes - can we also test rotates of a turned on bit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:busted:

@roeezolantz roeezolantz merged commit c0df312 into master Sep 19, 2024
4 checks passed
@roeezolantz roeezolantz deleted the implement-rotate-left-and-right-fhe-operations-1625101289 branch September 19, 2024 14:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants