-
Notifications
You must be signed in to change notification settings - Fork 110
swap: use the SimpleSwapFactory for deployment and verification #1803
Conversation
f0fa6e5
to
b066a40
Compare
1e32aa7
to
9a894d0
Compare
9a894d0
to
9eed73d
Compare
@acud sorry, mis-clicked and requested your review by accident. |
eb50a37
to
8682760
Compare
There was a problem hiding this 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] |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
}
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
8682760
to
628451b
Compare
628451b
to
d0b3726
Compare
d0b3726
to
752cf7a
Compare
There was a problem hiding this 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.
Tests in 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 |
ad61397
to
261ee47
Compare
There was a problem hiding this 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
This PR
SimpleSwapFactory
to deploy and verify contractsSimpleSwap
(same API but different bytecode due to newer compiler)3
))swap-chequebook-factory
flag to override thisinit()
)The naming of things is a bit inconsistent. In
contract
I have aSimpleSwapFactory
because that's how the contract is called but inswap
it is referred aschequebookFactory
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 andContract
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 ifnil
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