Skip to content
This repository has been archived by the owner on Aug 2, 2021. It is now read-only.

swap: use the SimpleSwapFactory for deployment and verification #1803

Merged
merged 3 commits into from
Oct 23, 2019

Conversation

ralph-pichler
Copy link
Member

@ralph-pichler ralph-pichler commented Sep 24, 2019

This PR

  • uses the new SimpleSwapFactory to deploy and verify contracts
  • uses a newer version of SimpleSwap (same API but different bytecode due to newer compiler)
  • looks up the factory contract address for common network ids (here only ropsten (3))
  • adds a swap-chequebook-factory flag to override this
  • lets node startup fail if swap is enabled and no valid factory is found or supplied
  • deploys a factory during testing (in init())

The naming of things is a bit inconsistent. In contract I have a SimpleSwapFactory because that's how the contract is called but in swap it is referred as chequebookFactory because everything is called chequebook there. Any suggestions on how to make this better are welcome.

Starting with this PR contract abstractions now maintain their own reference to their backend. SimpleSwapFactory was already written this way and Contract was refactored. This is because a contract abstraction is always tied to a backend at creation anyway and calling anything related to it (e.g. WaitFunc) with a different backend is always wrong.

See https://hackmd.io/yZLFmgdSRDCMEpBXCiBeBA?view for how to deploy a factory for testing purposes.

Note that while this PR addresses the issue of maliciously deployed chequebook contracts it actually introduces another security issue. Since the factory stores the addresses of deployed chequebooks that too could be manipulated during deployment of the factory. This is a lesser issue though as factories are deployed by Swarm Developers and their address is hardcoded into the client for common networks. If somebody passes the swap-chequebook-factory it is their responsibility that the factory was initialised properly. There are several ways this could be addressed if needed but I would leave that to a future PR.

Since most tests now require a SimulatedBackend to work, one is automatically created if nil is passed in. The value submitted to chequebooks at test deployment has also been adjusted to avoid bouncing cheques.

closes #1809
closes #1641
closes #1583

@ralph-pichler ralph-pichler changed the base branch from incentive-chequebook_persistance1 to master September 24, 2019 16:23
@ralph-pichler ralph-pichler force-pushed the swap-factory branch 3 times, most recently from 1e32aa7 to 9a894d0 Compare September 25, 2019 12:26
@ralph-pichler ralph-pichler marked this pull request as ready for review September 25, 2019 12:38
@ralph-pichler ralph-pichler added the builds on open PR Builds on a PR that is not yet merged. It is blocked until then and the diff won't make sense yet. label Sep 25, 2019
@ralph-pichler
Copy link
Member Author

@acud sorry, mis-clicked and requested your review by accident.

@ralph-pichler ralph-pichler removed the request for review from acud September 25, 2019 16:40
@ralph-pichler ralph-pichler force-pushed the swap-factory branch 3 times, most recently from eb50a37 to 8682760 Compare September 27, 2019 06:26
@ralph-pichler ralph-pichler removed the builds on open PR Builds on a PR that is not yet merged. It is blocked until then and the diff won't make sense yet. label Sep 27, 2019
api/config.go Outdated Show resolved Hide resolved
api/config.go Outdated Show resolved Hide resolved
cmd/swarm/flags.go Outdated Show resolved Hide resolved
contracts/swap/factory.go Outdated Show resolved Hide resolved
contracts/swap/swap.go Outdated Show resolved Hide resolved
swap/swap.go Outdated Show resolved Hide resolved
swap/swap.go Outdated Show resolved Hide resolved
swap/swap.go Show resolved Hide resolved
swap/swap_test.go Outdated Show resolved Hide resolved
swap/swap_test.go Outdated Show resolved Hide resolved
Copy link
Contributor

@santicomp2014 santicomp2014 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job Ralph, the implementation looks good, I'm not really an expert in the Contract side, but I do believe this is a good way to prevent abuse of the system thru a factory pattern.

There are only two things I see with this PR is the naming, But the naming will be addressed in other PRs I assume.
The other point is what @Eknir commented.

Proposing to have the factory is a separate directory, as now the functions of factory.go are in the same namespace as swap.go, which is problematic because there are functions which overlap, such as instanceAt and factoryAt (which do essentially the same, so it would be better to name them the same as well).


// FactoryAddressForNetwork gets the default factory address for a given network id
func FactoryAddressForNetwork(networkID uint64) (common.Address, error) {
address, ok := Deployments[networkID]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't know concurrency control here right, because this is only used at startup right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this only runs once during the constructor of Swap.

// DeploySimpleSwap deploys a new SimpleSwap contract from the factory and returns the ready to use Contract abstraction
func (sf simpleSwapFactory) DeploySimpleSwap(auth *bind.TransactOpts, issuer common.Address, defaultHardDepositTimeoutDuration *big.Int) (Contract, error) {
// for some reason the automatic gas estimation is too low
auth.GasLimit = 1700000
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So how do you get to exactly this number and why is it a good value?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the real deployment cost (which is constant and can be determined by deploying through truffle) rounded up to the next 100000. I'll clarify this in a comment.

return nil, err
}

// we iterate through the logs until we find the SimpleSwapDeployed event which contains the address of the new SimpleSwap contract
Copy link
Contributor

@holisticode holisticode Oct 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this ok to wait here until WaitFunc terminates? Couldn't that potentially be long? Wouldn't this block the entire boot process?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but that's how it was previously too (except there we were waiting for deployment of the chequebook directly).

// VerifyContract verifies that the supplied address was deployed by this factory
func (sf simpleSwapFactory) VerifyContract(address common.Address) error {
isDeployed, err := sf.instance.DeployedContracts(&bind.CallOpts{}, address)
if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could just

if !isDeployed {
		return ErrNotDeployedByFactory
	}
	return err
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with this approach is that if err is not nil then isDeployed will also be false. So for actual errors it would also return ErrNotDeployedByFactory instead of err.

swap/swap.go Outdated
auditLog.Warn("chequebook deploy error", "try", try, "error", err)
continue
return nil, err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it still make sense here to actually do deployRetries here anymore? Isn't it the case that if it failed, we just return anyway and don't iterate anymore?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I don't think i did that intentionally. I changed it back to continue.

That being said I'm not sure if the deploy loop is useful. What kind of error are we trying to handle here? The only situation I can think of where it would not work and then work a little latter was if the network connection to the eth node got cut (in that case it's unlikely it will work again after 1s too).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed about the deploy loop being redundant, I would propose to remove this, or add an issue so we won't forget to do this at a later point.

Copy link
Contributor

@Eknir Eknir left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing the comments.
There are still open issues with regards to the naming, but these can be addressed in a subsequent PR. Good work! Approved.

@janos
Copy link
Member

janos commented Oct 18, 2019

Tests in swap package are consuming a large amount of memory according to mem profile
profile001.svg.zip (GitHub does not support svg attachments, hence the zip), and discovered on 32-bit system https://ci.appveyor.com/project/ethersphere/swarm/builds/28196921/job/5yls2dhjvq13pn9p#L945.

The problem is in SimulatedBacked cache which is not released on Close. I know that this was addressed earlier at go-ethereum and that it should be fixed, but according to this, it appeared again. A similar workaround like initial one can be done (minimal set of changes):

diff --git a/swap/swap_test.go b/swap/swap_test.go
index 103907f9e..276b1663c 100644
--- a/swap/swap_test.go
+++ b/swap/swap_test.go
@@ -83,20 +83,32 @@ type swapTestBackend struct {
        cashDone chan struct{}
 }
 
+func (b *swapTestBackend) Close() error {
+       // Do not close SimulatedBackend as it is a global instance.
+       return nil
+}
+
+func TestMain(m *testing.M) {
+       exitCode := m.Run()
+       // Close the global default backend
+       // when tests are done.
+       defaultBackend.Close()
+       os.Exit(exitCode)
+}
+
 func init() {
        testutil.Init()
        mrand.Seed(time.Now().UnixNano())
        swapLog = log.Root()
 }
 
+var defaultBackend = backends.NewSimulatedBackend(core.GenesisAlloc{
+       ownerAddress:       {Balance: big.NewInt(1000000000000000000)},
+       beneficiaryAddress: {Balance: big.NewInt(1000000000000000000)},
+}, 8000000)
+
 // newTestBackend creates a new test backend instance
 func newTestBackend() *swapTestBackend {
-       gasLimit := uint64(8000000)
-       defaultBackend := backends.NewSimulatedBackend(core.GenesisAlloc{
-               ownerAddress:       {Balance: big.NewInt(1000000000000000000)},
-               beneficiaryAddress: {Balance: big.NewInt(1000000000000000000)},
-       }, gasLimit)
-
        // commit the initial "pre-mined" accounts (issuer and beneficiary addresses)
        defaultBackend.Commit()

to have only one instance of the backend, which can be a temporary solution as the memory is still not released and it is questionable if it is correct to have a global backend for all tests.

With this change the memory profile looks like this
profile002.svg.zip.

Copy link
Contributor

@mortelli mortelli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for taking on this task @ralph-pichler 👏

no blockers, but some questions, comments & suggestions below.

feel free to merge after addressing any number of them

cmd/swarm/flags.go Outdated Show resolved Hide resolved
cmd/swarm/config.go Outdated Show resolved Hide resolved
cmd/swarm/config.go Outdated Show resolved Hide resolved
swap/swap.go Outdated Show resolved Hide resolved
swap/swap.go Outdated Show resolved Hide resolved
contracts/swap/swap.go Outdated Show resolved Hide resolved
swap/simulations_test.go Outdated Show resolved Hide resolved
swap/swap_test.go Show resolved Hide resolved
contracts/swap/factory.go Show resolved Hide resolved
contracts/swap/factory.go Outdated Show resolved Hide resolved
@ralph-pichler ralph-pichler merged commit 35630cc into master Oct 23, 2019
@acud acud deleted the swap-factory branch October 23, 2019 10:28
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
6 participants