From 57a18578eb13ffb4a7fd9a3b95e60a8514406556 Mon Sep 17 00:00:00 2001 From: erhant Date: Mon, 2 Oct 2023 10:34:29 +0300 Subject: [PATCH] fixed bug in POLY_EVAL when called within loop --- README.md | 30 ++++++++++++++++++++---------- TODO.md | 8 -------- src/Huffd1.huff | 20 ++++++++++---------- src/main.huff | 2 +- src/misc/Codecopy.huff | 2 +- src/util/Polynomial.huff | 13 +++++++++---- test/Huffd1.t.sol | 16 ++++++---------- 7 files changed, 47 insertions(+), 44 deletions(-) delete mode 100644 TODO.md diff --git a/README.md b/README.md index b8123ca..6bfa269 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,7 @@ $$ p = \mathtt{0xffffffffffffffffffffffffffffffffffffffd1} $$ -Notice the final hexadecimals, which is where the name of the project comes from. - -We could also use $p = 2^{160} + 7$, but I wanted all coefficients to be strictly 160-bits, which is not the case with that prime. In fact, the concept works for any order, but we would like to use an order that can fit almost all the addresses while being as large as an address. - -The degree of the polynomial is equal to total supply - 1, so for $n$ tokens we have a polynomial $P$: +Notice the final hexadecimals, which is where the name of the project comes from. The degree of the polynomial is equal to total supply - 1, so for $n$ tokens we have a polynomial $P$: $$ P \in \mathbb{F}_\mathtt{0xffffffffffffffffffffffffffffffffffffffd1}^{n-1}[X] @@ -28,12 +24,10 @@ $$ > pop // [top-N, ..., top-1] > 0x01 // [top-N, ..., top-1, 0x01] > ``` -> -> Be careful not to be confused! ## Usage -We have a Sage script that can export the basis polynomials as a codetable. +We have a Sage script that can export the basis polynomials, one for each token id, as a codetable where the coefficients of each polynomial are concatenated. ```c // basis polynomials coefficients @@ -51,13 +45,13 @@ We have a Sage script that can export the basis polynomials as a codetable. #define constant ORDER = 0xffffffffffffffffffffffffffffffffffffffd1 ``` -Using these, we can load polynomials from the code table, and work with them using [`Polynomial.huff`](./src/util/Polynomial.huff). +Using these, we can load polynomials from the code table, and work with them using [`Polynomial.huff`](./src/util/Polynomial.huff). Note that this codetable grows pretty large for 20-byte coefficient size, and may easily get past the 24KB maximum contract size. Let's describe each function of [`huffd1`](./src/Huffd1.huff): ### `ownerOf` -To find the owner of a token $t$, simply evaluate $P(t)$ and the result will be a 160-bit number corresponding to the owner address. We use Horner's method to efficiently evaluate our polynomial. +To find the owner of a token $t$, simply evaluate $P(t)$ and the result will be a 160-bit number corresponding to the owner address. We use [Horner's method](https://zcash.github.io/halo2/background/polynomials.html#aside-horners-rule) to efficiently evaluate our polynomial. Initially, all tokens are owned by the contract deployer, which can be represented by the constant polynomial that is equal to the owner address. @@ -73,6 +67,14 @@ This operation results in multiplying the coefficients of $L_t(x)$ with $(b - a) Also note that $-a$ is obtained by $p-a$ where $p$ is the order of the finite field. +#### `name` + +Returns the string `"Huffd1"`. + +### `symbol` + +Returns the string `"FFD1"`. + ## Testing Simply do: @@ -82,3 +84,11 @@ forge test ``` It shall test both the polynomial utilities and the `huffd1` contract. + +## Further Works + +- We can implement approvals with another polynomial too, but time did not permit. Also, there are many optimizations to do in many different places within the code. + +- We could also use $p = 2^{160} + 7$, but I wanted all coefficients to be strictly 160-bits, which is not the case with that prime. In fact, the concept works for any prime order, but we would like to use an order that can fit almost all the addresses while being as large as an address. + +- Maybe use foundry FFI to generate the basis polynomials during contract creation? diff --git a/TODO.md b/TODO.md deleted file mode 100644 index a018dd4..0000000 --- a/TODO.md +++ /dev/null @@ -1,8 +0,0 @@ -# TODO - -- There is a bug with memory offsets somehow? - -## Ideas - -- We could have polynomial for approvals too. -- Maybe use foundry FFI to generate the basis based on total supply via script? diff --git a/src/Huffd1.huff b/src/Huffd1.huff index f290407..37e1cd5 100644 --- a/src/Huffd1.huff +++ b/src/Huffd1.huff @@ -31,24 +31,24 @@ //// STORAGE //// /////////////////////////////////////////////////////////////////////////////// #define constant BAL_SLOT = 0xbabe69babe69babe69babe69babe69 // balances -#define constant APV_SLOT = 0xface42face42face42face42face42 // approvals +// #define constant APV_SLOT = 0xface42face42face42face42face42 // approvals /////////////////////////////////////////////////////////////////////////////// //// MACROS //// /////////////////////////////////////////////////////////////////////////////// #define macro NAME() = takes (0) returns (0) { - 0x20 0x00 mstore // offset - 0x06 0x20 mstore // length (12 hex chars, 6 bytes) - // encoded string "Huffd1" - __RIGHTPAD(0x487566666431) 0x40 mstore + 0x20 0x00 mstore // offset + 0x06 0x20 mstore // length (12 hex chars, 6 bytes) + __RIGHTPAD(0x487566666431) // "Huffd1" + 0x40 mstore 0x60 0x00 return } #define macro SYMBOL() = takes (0) returns (0) { - 0x20 0x00 mstore // offset - 0x04 0x20 mstore // length (8 hex chars, 4 bytes) - // encoded string "FFD1" - __RIGHTPAD(0x46464431) 0x40 mstore + 0x20 0x00 mstore // offset + 0x04 0x20 mstore // length (8 hex chars, 4 bytes) + __RIGHTPAD(0x46464431) // "FFD1" + 0x40 mstore 0x60 0x00 return } @@ -148,7 +148,7 @@ pop pop pop // [a, m, s, l, i] // increment index - 0x01 add // [a, m, s',l, i'] (i' := i + 1) + 0x01 add // [a, m, s, l, i'] (i' := i + 1) // [a, m, s, l, i] loop_begin jump diff --git a/src/main.huff b/src/main.huff index 71c824f..d7fe7cd 100644 --- a/src/main.huff +++ b/src/main.huff @@ -31,7 +31,7 @@ //// STORAGE //// /////////////////////////////////////////////////////////////////////////////// #define constant BAL_SLOT = 0xbabe69babe69babe69babe69babe69 // balances -#define constant APV_SLOT = 0xface42face42face42face42face42 // approvals +// #define constant APV_SLOT = 0xface42face42face42face42face42 // approvals /////////////////////////////////////////////////////////////////////////////// //// MACROS //// diff --git a/src/misc/Codecopy.huff b/src/misc/Codecopy.huff index fd044d3..a7b034a 100644 --- a/src/misc/Codecopy.huff +++ b/src/misc/Codecopy.huff @@ -17,7 +17,7 @@ // shift to remove right-padding 0x00 mload - 0xF8 shr + 0xF8 shr // 0xF8 = 0x100 - 8 * COEFF_SIZE 0x00 mstore 0x20 0x00 return diff --git a/src/util/Polynomial.huff b/src/util/Polynomial.huff index 4671770..774a7ac 100644 --- a/src/util/Polynomial.huff +++ b/src/util/Polynomial.huff @@ -152,7 +152,12 @@ dup4 // [o, l, x, s, 32, l] sub // [o, l, x, s, i] (i := l - 32) - loop_begin: + // FIXME: change the name of loop label here to something other + // than `loop_begin` and `loop_end` because it clashes with other + // outer loops when this macro is called within. + // however, when I change all labels to something more specific and long, + // it get "byte index 24238 is out of bounds of ..." errors. + evall: // [o, l, x, s, i] // loop condition @@ -160,7 +165,7 @@ // it shall underflow and this condition will fail nevertheless dup4 dup2 // [o, l, x, s, i, l, i] lt iszero // [o, l, x, s, i, i >= l] - loop_end jumpi + evallend jumpi // compute memory index dup1 dup6 // [o, l, x, s, i, i, o] @@ -188,8 +193,8 @@ sub // [o, l, x, s, i'] (i' := i - 32) // [o, l, x, s, i] - loop_begin jump - loop_end: + evall jump + evallend: // [o, l, x, s, i] pop // [o, l, x, s] swap1 // [o, l, s, x] diff --git a/test/Huffd1.t.sol b/test/Huffd1.t.sol index c160432..2f76370 100644 --- a/test/Huffd1.t.sol +++ b/test/Huffd1.t.sol @@ -12,7 +12,6 @@ contract Huffd1Test is Test { /// @dev Set-up to run before each test. function setUp() public { - // TODO: deploy with code where code has the basis? huffd1 = Huffd1(HuffDeployer.deploy_with_args("Huffd1", abi.encode(OWNER))); } @@ -67,13 +66,13 @@ contract Huffd1Test is Test { assertEq(huffd1.balanceOf(OWNER), TOTAL_SUPPLY); // transfer a token - // address NEW_OWNER = address(0x9); - // vm.prank(OWNER); - // huffd1.transfer(NEW_OWNER, 0); - // assertEq(huffd1.ownerOf(0), NEW_OWNER); + address NEW_OWNER = address(0x9); + vm.prank(OWNER); + huffd1.transfer(NEW_OWNER, 0); + assertEq(huffd1.ownerOf(0), NEW_OWNER); - // assertEq(huffd1.balanceOf(OWNER), TOTAL_SUPPLY - 1); - // assertEq(huffd1.balanceOf(NEW_OWNER), 1); + assertEq(huffd1.balanceOf(OWNER), TOTAL_SUPPLY - 1); + assertEq(huffd1.balanceOf(NEW_OWNER), 1); } } @@ -89,8 +88,5 @@ interface Huffd1 is Owned { function ownerOf(uint256 tokenId) external view returns (address owner); function balanceOf(address owner) external view returns (uint256 balance); - function transfer(address to, uint256 tokenId) external; - - // TODO: approvals? }