github.com/ethereum/go-ethereum@v1.16.1/accounts/abi/bind/v2/dep_tree_test.go (about) 1 // Copyright 2024 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package bind 18 19 import ( 20 "fmt" 21 "regexp" 22 "testing" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/core/types" 26 "github.com/ethereum/go-ethereum/crypto" 27 "golang.org/x/exp/rand" 28 ) 29 30 type linkTestCase struct { 31 // map of pattern to unlinked bytecode (for the purposes of tests just contains the patterns of its dependencies) 32 libCodes map[string]string 33 contractCodes map[string]string 34 35 overrides map[string]common.Address 36 } 37 38 func copyMetaData(m *MetaData) *MetaData { 39 m.mu.Lock() 40 defer m.mu.Unlock() 41 42 var deps []*MetaData 43 if len(m.Deps) > 0 { 44 for _, dep := range m.Deps { 45 deps = append(deps, copyMetaData(dep)) 46 } 47 } 48 return &MetaData{ 49 Bin: m.Bin, 50 ABI: m.ABI, 51 Deps: deps, 52 ID: m.ID, 53 parsedABI: m.parsedABI, 54 } 55 } 56 57 func makeLinkTestCase(input map[rune][]rune, overrides map[rune]common.Address) *linkTestCase { 58 codes := make(map[string]string) 59 libCodes := make(map[string]string) 60 contractCodes := make(map[string]string) 61 62 inputMap := make(map[rune]map[rune]struct{}) 63 // set of solidity patterns for all contracts that are known to be libraries 64 libs := make(map[string]struct{}) 65 66 // map of test contract id (rune) to the solidity library pattern (hash of that rune) 67 patternMap := map[rune]string{} 68 69 for contract, deps := range input { 70 inputMap[contract] = make(map[rune]struct{}) 71 if _, ok := patternMap[contract]; !ok { 72 patternMap[contract] = crypto.Keccak256Hash([]byte(string(contract))).String()[2:36] 73 } 74 75 for _, dep := range deps { 76 if _, ok := patternMap[dep]; !ok { 77 patternMap[dep] = crypto.Keccak256Hash([]byte(string(dep))).String()[2:36] 78 } 79 codes[patternMap[contract]] = codes[patternMap[contract]] + fmt.Sprintf("__$%s$__", patternMap[dep]) 80 inputMap[contract][dep] = struct{}{} 81 libs[patternMap[dep]] = struct{}{} 82 } 83 } 84 overridesPatterns := make(map[string]common.Address) 85 for contractId, overrideAddr := range overrides { 86 pattern := crypto.Keccak256Hash([]byte(string(contractId))).String()[2:36] 87 overridesPatterns[pattern] = overrideAddr 88 } 89 90 for _, pattern := range patternMap { 91 if _, ok := libs[pattern]; ok { 92 // if the library didn't depend on others, give it some dummy code to not bork deployment logic down-the-line 93 if len(codes[pattern]) == 0 { 94 libCodes[pattern] = "ff" 95 } else { 96 libCodes[pattern] = codes[pattern] 97 } 98 } else { 99 contractCodes[pattern] = codes[pattern] 100 } 101 } 102 103 return &linkTestCase{ 104 libCodes, 105 contractCodes, 106 overridesPatterns, 107 } 108 } 109 110 var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 111 112 type linkTestCaseInput struct { 113 input map[rune][]rune 114 overrides map[rune]struct{} 115 expectDeployed map[rune]struct{} 116 } 117 118 // linkDeps will return a set of root dependencies and their sub-dependencies connected via the Deps field 119 func linkDeps(deps map[string]*MetaData) []*MetaData { 120 roots := make(map[string]struct{}) 121 for pattern := range deps { 122 roots[pattern] = struct{}{} 123 } 124 125 connectedDeps := make(map[string]*MetaData) 126 for pattern, dep := range deps { 127 connectedDeps[pattern] = internalLinkDeps(dep, deps, &roots) 128 } 129 130 var rootMetadatas []*MetaData 131 for pattern := range roots { 132 dep := connectedDeps[pattern] 133 rootMetadatas = append(rootMetadatas, dep) 134 } 135 return rootMetadatas 136 } 137 138 // internalLinkDeps is the internal recursing logic of linkDeps: 139 // It links the contract referred to by MetaData given the depMap (map of solidity 140 // link pattern to contract metadata object), deleting contract entries from the 141 // roots map if they were referenced as dependencies. It returns a new MetaData 142 // object which is the linked version of metadata parameter. 143 func internalLinkDeps(metadata *MetaData, depMap map[string]*MetaData, roots *map[string]struct{}) *MetaData { 144 linked := copyMetaData(metadata) 145 depPatterns := parseLibraryDeps(metadata.Bin) 146 for _, pattern := range depPatterns { 147 delete(*roots, pattern) 148 connectedDep := internalLinkDeps(depMap[pattern], depMap, roots) 149 linked.Deps = append(linked.Deps, connectedDep) 150 } 151 return linked 152 } 153 154 func testLinkCase(tcInput linkTestCaseInput) error { 155 var ( 156 testAddr = crypto.PubkeyToAddress(testKey.PublicKey) 157 overridesAddrs = make(map[common.Address]struct{}) 158 overrideAddrs = make(map[rune]common.Address) 159 ) 160 // generate deterministic addresses for the override set. 161 rand.Seed(42) 162 for contract := range tcInput.overrides { 163 var addr common.Address 164 rand.Read(addr[:]) 165 overrideAddrs[contract] = addr 166 overridesAddrs[addr] = struct{}{} 167 } 168 169 tc := makeLinkTestCase(tcInput.input, overrideAddrs) 170 allContracts := make(map[rune]struct{}) 171 172 for contract, deps := range tcInput.input { 173 allContracts[contract] = struct{}{} 174 for _, dep := range deps { 175 allContracts[dep] = struct{}{} 176 } 177 } 178 179 var testAddrNonce uint64 180 mockDeploy := func(input []byte, deployer []byte) (common.Address, *types.Transaction, error) { 181 contractAddr := crypto.CreateAddress(testAddr, testAddrNonce) 182 testAddrNonce++ 183 184 if len(deployer) >= 20 { 185 // assert that this contract only references libs that are known to be deployed or in the override set 186 for i := 0; i < len(deployer); i += 20 { 187 var dep common.Address 188 dep.SetBytes(deployer[i : i+20]) 189 if _, ok := overridesAddrs[dep]; !ok { 190 return common.Address{}, nil, fmt.Errorf("reference to dependent contract that has not yet been deployed: %x\n", dep) 191 } 192 } 193 } 194 overridesAddrs[contractAddr] = struct{}{} 195 // we don't care about the txs themselves for the sake of the linking tests. so we can return nil for them in the mock deployer 196 return contractAddr, nil, nil 197 } 198 199 contracts := make(map[string]*MetaData) 200 overrides := make(map[string]common.Address) 201 202 for pattern, bin := range tc.contractCodes { 203 contracts[pattern] = &MetaData{ID: pattern, Bin: "0x" + bin} 204 } 205 for pattern, bin := range tc.libCodes { 206 contracts[pattern] = &MetaData{ 207 Bin: "0x" + bin, 208 ID: pattern, 209 } 210 } 211 212 contractsList := linkDeps(contracts) 213 214 for pattern, override := range tc.overrides { 215 overrides[pattern] = override 216 } 217 218 deployParams := &DeploymentParams{ 219 Contracts: contractsList, 220 Overrides: overrides, 221 } 222 res, err := LinkAndDeploy(deployParams, mockDeploy) 223 if err != nil { 224 return err 225 } 226 227 if len(res.Txs) != len(tcInput.expectDeployed) { 228 return fmt.Errorf("got %d deployed contracts. expected %d.\n", len(res.Addresses), len(tcInput.expectDeployed)) 229 } 230 for contract := range tcInput.expectDeployed { 231 pattern := crypto.Keccak256Hash([]byte(string(contract))).String()[2:36] 232 if _, ok := res.Addresses[pattern]; !ok { 233 return fmt.Errorf("expected contract %s was not deployed\n", string(contract)) 234 } 235 } 236 return nil 237 } 238 239 func TestContractLinking(t *testing.T) { 240 for i, tc := range []linkTestCaseInput{ 241 // test simple contract without any dependencies or overrides 242 { 243 map[rune][]rune{ 244 'a': {}}, 245 map[rune]struct{}{}, 246 map[rune]struct{}{ 247 'a': {}}, 248 }, 249 // test deployment of a contract that depends on somes libraries. 250 { 251 map[rune][]rune{ 252 'a': {'b', 'c', 'd', 'e'}}, 253 map[rune]struct{}{}, 254 map[rune]struct{}{ 255 'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}}, 256 }, 257 // test deployment of a contract that depends on some libraries, 258 // one of which has its own library dependencies. 259 { 260 map[rune][]rune{ 261 'a': {'b', 'c', 'd', 'e'}, 262 'e': {'f', 'g', 'h', 'i'}}, 263 map[rune]struct{}{}, 264 map[rune]struct{}{ 265 'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {}, 'i': {}}, 266 }, 267 // test single contract only without deps 268 { 269 map[rune][]rune{ 270 'a': {}}, 271 map[rune]struct{}{}, 272 map[rune]struct{}{ 273 'a': {}, 274 }, 275 }, 276 // test that libraries at different levels of the tree can share deps, 277 // and that these shared deps will only be deployed once. 278 { 279 map[rune][]rune{ 280 'a': {'b', 'c', 'd', 'e'}, 281 'e': {'f', 'g', 'h', 'i', 'm'}, 282 'i': {'j', 'k', 'l', 'm'}}, 283 map[rune]struct{}{}, 284 map[rune]struct{}{ 285 'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {}, 'i': {}, 'j': {}, 'k': {}, 'l': {}, 'm': {}, 286 }, 287 }, 288 // test two contracts can be deployed which don't share deps 289 linkTestCaseInput{ 290 map[rune][]rune{ 291 'a': {'b', 'c', 'd', 'e'}, 292 'f': {'g', 'h', 'i', 'j'}}, 293 map[rune]struct{}{}, 294 map[rune]struct{}{ 295 'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {}, 'i': {}, 'j': {}, 296 }, 297 }, 298 // test two contracts can be deployed which share deps 299 linkTestCaseInput{ 300 map[rune][]rune{ 301 'a': {'b', 'c', 'd', 'e'}, 302 'f': {'g', 'c', 'd', 'h'}}, 303 map[rune]struct{}{}, 304 map[rune]struct{}{ 305 'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {}, 306 }, 307 }, 308 // test one contract with overrides for all lib deps 309 linkTestCaseInput{ 310 map[rune][]rune{ 311 'a': {'b', 'c', 'd', 'e'}}, 312 map[rune]struct{}{'b': {}, 'c': {}, 'd': {}, 'e': {}}, 313 map[rune]struct{}{ 314 'a': {}}, 315 }, 316 // test one contract with overrides for some lib deps 317 linkTestCaseInput{ 318 map[rune][]rune{ 319 'a': {'b', 'c'}}, 320 map[rune]struct{}{'b': {}, 'c': {}}, 321 map[rune]struct{}{ 322 'a': {}}, 323 }, 324 // test deployment of a contract with overrides 325 linkTestCaseInput{ 326 map[rune][]rune{ 327 'a': {}}, 328 map[rune]struct{}{'a': {}}, 329 map[rune]struct{}{}, 330 }, 331 // two contracts ('a' and 'f') share some dependencies. contract 'a' is marked as an override. expect that any of 332 // its depdencies that aren't shared with 'f' are not deployed. 333 linkTestCaseInput{map[rune][]rune{ 334 'a': {'b', 'c', 'd', 'e'}, 335 'f': {'g', 'c', 'd', 'h'}}, 336 map[rune]struct{}{'a': {}}, 337 map[rune]struct{}{ 338 'f': {}, 'g': {}, 'c': {}, 'd': {}, 'h': {}}, 339 }, 340 // test nested libraries that share deps at different levels of the tree... with override. 341 // same condition as above test: no sub-dependencies of 342 { 343 map[rune][]rune{ 344 'a': {'b', 'c', 'd', 'e'}, 345 'e': {'f', 'g', 'h', 'i', 'm'}, 346 'i': {'j', 'k', 'l', 'm'}, 347 'l': {'n', 'o', 'p'}}, 348 map[rune]struct{}{ 349 'i': {}, 350 }, 351 map[rune]struct{}{ 352 'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {}, 'm': {}}, 353 }, 354 } { 355 if err := testLinkCase(tc); err != nil { 356 t.Fatalf("test case %d failed: %v", i, err) 357 } 358 } 359 } 360 361 func parseLibraryDeps(unlinkedCode string) (res []string) { 362 reMatchSpecificPattern, err := regexp.Compile(`__\$([a-f0-9]+)\$__`) 363 if err != nil { 364 panic(err) 365 } 366 for _, match := range reMatchSpecificPattern.FindAllStringSubmatch(unlinkedCode, -1) { 367 res = append(res, match[1]) 368 } 369 return res 370 }