github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/crypto/poseidon/poseidon.go (about) 1 // from github.com/iden3/go-iden3-crypto/ff/poseidon 2 3 package poseidon 4 5 import ( 6 "errors" 7 "fmt" 8 "math/big" 9 10 "github.com/iden3/go-iden3-crypto/ff" 11 "github.com/iden3/go-iden3-crypto/utils" 12 13 "github.com/scroll-tech/go-ethereum/log" 14 ) 15 16 const NROUNDSF = 8 //nolint:golint 17 18 var NROUNDSP = []int{56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68} //nolint:golint 19 20 func zero() *ff.Element { 21 return ff.NewElement() 22 } 23 24 // exp5 performs x^5 mod p 25 // https://eprint.iacr.org/2019/458.pdf page 8 26 func exp5(a *ff.Element) { 27 a.Exp(*a, big.NewInt(5)) //nolint:gomnd 28 } 29 30 // exp5state perform exp5 for whole state 31 func exp5state(state []*ff.Element) { 32 for i := 0; i < len(state); i++ { 33 exp5(state[i]) 34 } 35 } 36 37 // ark computes Add-Round Key, from the paper https://eprint.iacr.org/2019/458.pdf 38 func ark(state []*ff.Element, c []*ff.Element, it int) { 39 for i := 0; i < len(state); i++ { 40 state[i].Add(state[i], c[it+i]) 41 } 42 } 43 44 // mix returns [[matrix]] * [vector] 45 func mix(state []*ff.Element, t int, m [][]*ff.Element) []*ff.Element { 46 mul := zero() 47 newState := make([]*ff.Element, t) 48 for i := 0; i < t; i++ { 49 newState[i] = zero() 50 } 51 for i := 0; i < len(state); i++ { 52 newState[i].SetUint64(0) 53 for j := 0; j < len(state); j++ { 54 mul.Mul(m[j][i], state[j]) 55 newState[i].Add(newState[i], mul) 56 } 57 } 58 return newState 59 } 60 61 func permute(state []*ff.Element, t int) []*ff.Element { 62 63 nRoundsF := NROUNDSF 64 nRoundsP := NROUNDSP[t-2] 65 C := c.c[t-2] 66 S := c.s[t-2] 67 M := c.m[t-2] 68 P := c.p[t-2] 69 70 ark(state, C, 0) 71 72 for i := 0; i < nRoundsF/2-1; i++ { 73 exp5state(state) 74 ark(state, C, (i+1)*t) 75 state = mix(state, t, M) 76 } 77 exp5state(state) 78 ark(state, C, (nRoundsF/2)*t) 79 state = mix(state, t, P) 80 81 for i := 0; i < nRoundsP; i++ { 82 exp5(state[0]) 83 state[0].Add(state[0], C[(nRoundsF/2+1)*t+i]) 84 85 mul := zero() 86 newState0 := zero() 87 for j := 0; j < len(state); j++ { 88 mul.Mul(S[(t*2-1)*i+j], state[j]) 89 newState0.Add(newState0, mul) 90 } 91 92 for k := 1; k < t; k++ { 93 mul = zero() 94 state[k] = state[k].Add(state[k], mul.Mul(state[0], S[(t*2-1)*i+t+k-1])) 95 } 96 state[0] = newState0 97 } 98 99 for i := 0; i < nRoundsF/2-1; i++ { 100 exp5state(state) 101 ark(state, C, (nRoundsF/2+1)*t+nRoundsP+i*t) 102 state = mix(state, t, M) 103 } 104 exp5state(state) 105 return mix(state, t, M) 106 } 107 108 // for short, use size of inpBI as cap 109 func Hash(inpBI []*big.Int, width int) (*big.Int, error) { 110 return HashWithCap(inpBI, width, int64(len(inpBI))) 111 } 112 113 // Hash using possible sponge specs specified by width (rate from 1 to 15), the size of input is applied as capacity 114 // (notice we do not include width in the capacity ) 115 func HashWithCap(inpBI []*big.Int, width int, nBytes int64) (*big.Int, error) { 116 if width < 2 { 117 return nil, fmt.Errorf("width must be ranged from 2 to 16") 118 } 119 if width-2 > len(NROUNDSP) { 120 return nil, fmt.Errorf("invalid inputs width %d, max %d", width, len(NROUNDSP)+1) //nolint:gomnd,lll 121 } 122 123 // capflag = nBytes * 2^64 124 pow64 := big.NewInt(1) 125 pow64.Lsh(pow64, 64) 126 capflag := ff.NewElement().SetBigInt(big.NewInt(nBytes)) 127 capflag.Mul(capflag, ff.NewElement().SetBigInt(pow64)) 128 129 // initialize the state 130 state := make([]*ff.Element, width) 131 state[0] = capflag 132 for i := 1; i < width; i++ { 133 state[i] = zero() 134 } 135 136 rate := width - 1 137 i := 0 138 // always perform one round of permutation even when input is empty 139 for { 140 // each round absorb at most `rate` elements from `inpBI` 141 for j := 0; j < rate && i < len(inpBI); i, j = i+1, j+1 { 142 state[j+1].Add(state[j+1], ff.NewElement().SetBigInt(inpBI[i])) 143 } 144 state = permute(state, width) 145 if i == len(inpBI) { 146 break 147 } 148 } 149 150 // squeeze 151 rE := state[0] 152 r := big.NewInt(0) 153 rE.ToBigIntRegular(r) 154 return r, nil 155 156 } 157 158 // Hash computes the Poseidon hash for the given fixed-size inputs, with specified domain field 159 func HashFixedWithDomain(inpBI []*big.Int, domain *big.Int) (*big.Int, error) { 160 t := len(inpBI) + 1 161 if len(inpBI) == 0 || len(inpBI) > len(NROUNDSP) { 162 return nil, fmt.Errorf("invalid inputs length %d, max %d", len(inpBI), len(NROUNDSP)) //nolint:gomnd,lll 163 } 164 if !utils.CheckBigIntArrayInField(inpBI[:]) { 165 return nil, errors.New("inputs values not inside Finite Field") 166 } 167 inp := utils.BigIntArrayToElementArray(inpBI[:]) 168 169 state := make([]*ff.Element, t) 170 state[0] = ff.NewElement().SetBigInt(domain) 171 copy(state[1:], inp[:]) 172 173 state = permute(state, t) 174 175 rE := state[0] 176 r := big.NewInt(0) 177 rE.ToBigIntRegular(r) 178 return r, nil 179 } 180 181 // Deprecated HashFixed entry, with domain field is 0 182 func HashFixed(inpBI []*big.Int) (*big.Int, error) { 183 log.Warn("called a deprecated method for poseidon fixed hash", "inputs", inpBI) 184 return HashFixedWithDomain(inpBI, big.NewInt(0)) 185 }