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  }