@@ -3,6 +3,7 @@ package workflows
33import (
44 "crypto/sha256"
55 "encoding/hex"
6+ "strings"
67 "testing"
78 "unicode/utf8"
89
@@ -108,3 +109,175 @@ func TestNormalizeWorkflowName(t *testing.T) {
108109 })
109110 }
110111}
112+
113+ func Test_GenerateWorkflowOwnerAddress_DifferentInputsGenerateDifferentAddresses (t * testing.T ) {
114+ tests := []struct {
115+ name string
116+ prefix1 string
117+ ownerKey1 string
118+ prefix2 string
119+ ownerKey2 string
120+ description string
121+ }{
122+ {
123+ name : "different_prefix" ,
124+ prefix1 : "registry_v1" ,
125+ ownerKey1 : "owner123" ,
126+ prefix2 : "registry_v2" ,
127+ ownerKey2 : "owner123" ,
128+ description : "Same ownerKey, different prefix" ,
129+ },
130+ {
131+ name : "different_ownerKey" ,
132+ prefix1 : "registry_v1" ,
133+ ownerKey1 : "owner123" ,
134+ prefix2 : "registry_v1" ,
135+ ownerKey2 : "owner124" ,
136+ description : "Same prefix, different ownerKey" ,
137+ },
138+ {
139+ name : "case_sensitive_prefix" ,
140+ prefix1 : "Registry" ,
141+ ownerKey1 : "owner123" ,
142+ prefix2 : "registry" ,
143+ ownerKey2 : "owner123" ,
144+ description : "Case sensitive prefix difference" ,
145+ },
146+ {
147+ name : "case_sensitive_ownerKey" ,
148+ prefix1 : "registry" ,
149+ ownerKey1 : "Owner123" ,
150+ prefix2 : "registry" ,
151+ ownerKey2 : "owner123" ,
152+ description : "Case sensitive ownerKey difference" ,
153+ },
154+ {
155+ name : "single_char_difference_prefix" ,
156+ prefix1 : "registrya" ,
157+ ownerKey1 : "owner123" ,
158+ prefix2 : "registryb" ,
159+ ownerKey2 : "owner123" ,
160+ description : "Single character difference in prefix" ,
161+ },
162+ {
163+ name : "single_char_difference_ownerKey" ,
164+ prefix1 : "registry" ,
165+ ownerKey1 : "owner123a" ,
166+ prefix2 : "registry" ,
167+ ownerKey2 : "owner123b" ,
168+ description : "Single character difference in ownerKey" ,
169+ },
170+ }
171+
172+ for _ , tt := range tests {
173+ t .Run (tt .name , func (t * testing.T ) {
174+ // Generate first address
175+ addr1 , err := GenerateWorkflowOwnerAddress (tt .prefix1 , tt .ownerKey1 )
176+ require .NoError (t , err , "Failed to generate first address" )
177+
178+ // Generate second address
179+ addr2 , err := GenerateWorkflowOwnerAddress (tt .prefix2 , tt .ownerKey2 )
180+ require .NoError (t , err , "Failed to generate second address" )
181+
182+ // Verify addresses are different
183+ require .NotEqual (t , hex .EncodeToString (addr1 ), hex .EncodeToString (addr2 ), "Addresses should not match" )
184+
185+ // Verify addresses are 20 bytes (Ethereum address length)
186+ require .Len (t , addr1 , 20 , "First address should be 20 bytes" )
187+ require .Len (t , addr2 , 20 , "Second address should be 20 bytes" )
188+ })
189+ }
190+ }
191+
192+ func Test_GenerateWorkflowOwnerAddress_SameInputsGenerateSameAddress (t * testing.T ) {
193+ prefix := "test_registry"
194+ ownerKey := "test_owner_123"
195+
196+ // Generate address multiple times
197+ addr1 , err := GenerateWorkflowOwnerAddress (prefix , ownerKey )
198+ require .NoError (t , err , "Failed to generate first address" )
199+
200+ addr2 , err := GenerateWorkflowOwnerAddress (prefix , ownerKey )
201+ require .NoError (t , err , "Failed to generate second address" )
202+
203+ // Verify all addresses are identical
204+ addr1Hex := hex .EncodeToString (addr1 )
205+ addr2Hex := hex .EncodeToString (addr2 )
206+
207+ require .Equal (t , addr1Hex , addr2Hex , "Same inputs should generate same address" )
208+ }
209+
210+ func Test_GenerateWorkflowOwnerAddress_SolidityCompatibility (t * testing.T ) {
211+ /*
212+ // SPDX-License-Identifier: MIT
213+ pragma solidity ^0.8.0;
214+
215+ contract WorkflowOwnerAddressGenerator {
216+
217+ function generateWorkflowOwnerAddress(
218+ string memory prefix,
219+ string memory ownerKey
220+ ) public pure returns (address) {
221+ // Step 1: Create nested hash of prefix + ownerKey
222+ bytes32 nestedHash = keccak256(abi.encodePacked(prefix, ownerKey));
223+
224+ // Step 2: Create the full preimage for outer hash
225+ // 0xff + 84 zero bytes + nested hash
226+ bytes memory preimage = new bytes(117); // 1 + 84 + 32 = 117 bytes
227+
228+ // Set first byte to 0xff
229+ preimage[0] = 0xff;
230+
231+ // Bytes 1-84 are already zero (default in Solidity)
232+
233+ // Copy nested hash to bytes 85-116
234+ for (uint256 i = 0; i < 32; i++) {
235+ preimage[85 + i] = nestedHash[i];
236+ }
237+
238+ // Step 3: Hash the full preimage and return last 20 bytes as address
239+ bytes32 outerHash = keccak256(preimage);
240+ return address(uint160(uint256(outerHash)));
241+ }
242+ }
243+ */
244+ // These expected addresses were generated using the Solidity contract above
245+ // You can verify these by deploying the contract and calling the function
246+ testCases := []struct {
247+ prefix string
248+ ownerKey string
249+ expectedHex string // This should be generated by running the Solidity contract
250+ }{
251+ {
252+ prefix : "registry1" ,
253+ ownerKey : "owner123" ,
254+ expectedHex : "0x58c0e4aaf5fb13fcaea5790f8a19014ad9646da3" , // convert to lowercase, not checksum
255+ },
256+ {
257+ prefix : "registry2" ,
258+ ownerKey : "owner123" ,
259+ expectedHex : "0xf094995741cffc6c173fa9edb2e8d766d1524039" , // convert to lowercase, not checksum
260+ },
261+ {
262+ prefix : "registry2" ,
263+ ownerKey : "ownerSomethingElse" ,
264+ expectedHex : "0x4be6a8e38aa493cac0aa4c6dd13bad41f8219f0c" , // convert to lowercase, not checksum
265+ },
266+ }
267+
268+ for _ , tc := range testCases {
269+ t .Run (tc .prefix + "_" + tc .ownerKey , func (t * testing.T ) {
270+ goAddr , err := GenerateWorkflowOwnerAddress (tc .prefix , tc .ownerKey )
271+ require .NoError (t , err )
272+
273+ goAddrHex := hex .EncodeToString (goAddr )
274+
275+ // Remove 0x prefix if present in expected
276+ expected := strings .TrimPrefix (tc .expectedHex , "0x" )
277+
278+ require .Equal (t , expected , goAddrHex ,
279+ "Go implementation should match Solidity for prefix='%s', ownerKey='%s'" ,
280+ tc .prefix , tc .ownerKey )
281+ })
282+ }
283+ }
0 commit comments