Commit 5a012383 authored by Ralph Pichler's avatar Ralph Pichler Committed by GitHub

feat: update default factory and add support for legacy factories (#1539)

parent 54b400eb
......@@ -56,6 +56,7 @@ const (
optionNameClefSignerEthereumAddress = "clef-signer-ethereum-address"
optionNameSwapEndpoint = "swap-endpoint"
optionNameSwapFactoryAddress = "swap-factory-address"
optionNameSwapLegacyFactoryAddresses = "swap-legacy-factory-addresses"
optionNameSwapInitialDeposit = "swap-initial-deposit"
optionNameSwapEnable = "swap-enable"
optionNameTransactionHash = "transaction"
......@@ -226,7 +227,8 @@ func (c *command) setAllFlags(cmd *cobra.Command) {
cmd.Flags().String(optionNameClefSignerEndpoint, "", "clef signer endpoint")
cmd.Flags().String(optionNameClefSignerEthereumAddress, "", "ethereum address to use from clef signer")
cmd.Flags().String(optionNameSwapEndpoint, "http://localhost:8545", "swap ethereum blockchain endpoint")
cmd.Flags().String(optionNameSwapFactoryAddress, "", "swap factory address")
cmd.Flags().String(optionNameSwapFactoryAddress, "", "swap factory addresses")
cmd.Flags().StringSlice(optionNameSwapLegacyFactoryAddresses, nil, "legacy swap factory addresses")
cmd.Flags().String(optionNameSwapInitialDeposit, "100000000000000000", "initial deposit if deploying a new chequebook")
cmd.Flags().Bool(optionNameSwapEnable, true, "enable swap")
cmd.Flags().Bool(optionNameFullNode, false, "cause the node to start in full mode")
......
......@@ -74,6 +74,7 @@ func (c *command) initDeployCmd() error {
chainID,
transactionService,
factoryAddress,
nil,
)
if err != nil {
return err
......
......@@ -145,6 +145,7 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz
BootnodeMode: bootNode,
SwapEndpoint: c.config.GetString(optionNameSwapEndpoint),
SwapFactoryAddress: c.config.GetString(optionNameSwapFactoryAddress),
SwapLegacyFactoryAddresses: c.config.GetStringSlice(optionNameSwapLegacyFactoryAddresses),
SwapInitialDeposit: c.config.GetString(optionNameSwapInitialDeposit),
SwapEnable: c.config.GetBool(optionNameSwapEnable),
FullNodeMode: fullNode,
......
......@@ -9,7 +9,7 @@ require (
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
github.com/ethereum/go-ethereum v1.9.23
github.com/ethersphere/go-storage-incentives-abi v0.1.0
github.com/ethersphere/go-sw3-abi v0.3.2
github.com/ethersphere/go-sw3-abi v0.4.0
github.com/ethersphere/langos v1.0.0
github.com/gogo/protobuf v1.3.1
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
......
......@@ -143,6 +143,7 @@ github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUn
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
......@@ -161,8 +162,8 @@ github.com/ethereum/go-ethereum v1.9.23 h1:SIKhg/z4Q7AbvqcxuPYvMxf36che/Rq/Pp0Id
github.com/ethereum/go-ethereum v1.9.23/go.mod h1:JIfVb6esrqALTExdz9hRYvrP0xBDf6wCncIu1hNwHpM=
github.com/ethersphere/go-storage-incentives-abi v0.1.0 h1:yxNME3q5dha/pUtIYB07DALhhQjd3+uYhGLFqKMXVyg=
github.com/ethersphere/go-storage-incentives-abi v0.1.0/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc=
github.com/ethersphere/go-sw3-abi v0.3.2 h1:BVTuSZ9Ph/JJBglU9pCRSch3gDq4g5QEto6KzMYP/08=
github.com/ethersphere/go-sw3-abi v0.3.2/go.mod h1:BmpsvJ8idQZdYEtWnvxA8POYQ8Rl/NhyCdF0zLMOOJU=
github.com/ethersphere/go-sw3-abi v0.4.0 h1:T3ANY+ktWrPAwe2U0tZi+DILpkHzto5ym/XwV/Bbz8g=
github.com/ethersphere/go-sw3-abi v0.4.0/go.mod h1:BmpsvJ8idQZdYEtWnvxA8POYQ8Rl/NhyCdF0zLMOOJU=
github.com/ethersphere/langos v1.0.0 h1:NBtNKzXTTRSue95uOlzPN4py7Aofs0xWPzyj4AI1Vcc=
github.com/ethersphere/langos v1.0.0/go.mod h1:dlcN2j4O8sQ+BlCaxeBu43bgr4RQ+inJ+pHwLeZg5Tw=
github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
......
......@@ -86,26 +86,43 @@ func InitChequebookFactory(
chainID int64,
transactionService transaction.Service,
factoryAddress string,
legacyFactoryAddresses []string,
) (chequebook.Factory, error) {
var addr common.Address
var currentFactory common.Address
var legacyFactories []common.Address
foundFactory, foundLegacyFactories, found := chequebook.DiscoverFactoryAddress(chainID)
if factoryAddress == "" {
var found bool
addr, found = chequebook.DiscoverFactoryAddress(chainID)
if !found {
return nil, errors.New("no known factory address for this network")
}
logger.Infof("using default factory address for chain id %d: %x", chainID, addr)
currentFactory = foundFactory
logger.Infof("using default factory address for chain id %d: %x", chainID, currentFactory)
} else if !common.IsHexAddress(factoryAddress) {
return nil, errors.New("malformed factory address")
} else {
addr = common.HexToAddress(factoryAddress)
logger.Infof("using custom factory address: %x", addr)
currentFactory = common.HexToAddress(factoryAddress)
logger.Infof("using custom factory address: %x", currentFactory)
}
if legacyFactoryAddresses == nil {
if found {
legacyFactories = foundLegacyFactories
}
} else {
for _, legacyAddress := range legacyFactoryAddresses {
if !common.IsHexAddress(legacyAddress) {
return nil, errors.New("malformed factory address")
}
legacyFactories = append(legacyFactories, common.HexToAddress(legacyAddress))
}
}
return chequebook.NewFactory(
backend,
transactionService,
addr,
currentFactory,
legacyFactories,
), nil
}
......
......@@ -126,6 +126,7 @@ type Options struct {
BootnodeMode bool
SwapEndpoint string
SwapFactoryAddress string
SwapLegacyFactoryAddresses []string
SwapInitialDeposit string
SwapEnable bool
FullNodeMode bool
......@@ -241,6 +242,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
chainID,
transactionService,
o.SwapFactoryAddress,
o.SwapLegacyFactoryAddresses,
)
if err != nil {
return nil, err
......
......@@ -256,7 +256,7 @@ func TestCashoutStatusReverted(t *testing.T) {
),
transactionmock.New(
transactionmock.WithABISend(&chequebookABI, txHash, chequebookAddress, big.NewInt(0), "cashChequeBeneficiary", recipientAddress, cheque.CumulativePayout, cheque.Signature),
transactionmock.WithABICall(&chequebookABI, onChainPaidOut.FillBytes(make([]byte, 32)), "paidOut", beneficiary),
transactionmock.WithABICall(&chequebookABI, chequebookAddress, onChainPaidOut.FillBytes(make([]byte, 32)), "paidOut", beneficiary),
),
chequestoremock.NewChequeStore(
chequestoremock.WithLastChequeFunc(func(c common.Address) (*chequebook.SignedCheque, error) {
......
......@@ -46,7 +46,7 @@ func TestChequebookBalance(t *testing.T) {
balance := big.NewInt(10)
chequebookService, err := chequebook.New(
transactionmock.New(
transactionmock.WithABICall(&chequebookABI, balance.FillBytes(make([]byte, 32)), "balance"),
transactionmock.WithABICall(&chequebookABI, address, balance.FillBytes(make([]byte, 32)), "balance"),
),
address,
ownerAdress,
......@@ -191,12 +191,12 @@ func TestChequebookIssue(t *testing.T) {
chequebookService, err := chequebook.New(
transactionmock.New(
transactionmock.WithABICallSequence(
transactionmock.ABICall(&chequebookABI, big.NewInt(100).FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
transactionmock.ABICall(&chequebookABI, big.NewInt(100).FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
transactionmock.ABICall(&chequebookABI, big.NewInt(100).FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
transactionmock.ABICall(&chequebookABI, address, big.NewInt(100).FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
transactionmock.ABICall(&chequebookABI, address, big.NewInt(100).FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
transactionmock.ABICall(&chequebookABI, address, big.NewInt(100).FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
),
),
address,
......@@ -377,8 +377,8 @@ func TestChequebookIssueOutOfFunds(t *testing.T) {
chequebookService, err := chequebook.New(
transactionmock.New(
transactionmock.WithABICallSequence(
transactionmock.ABICall(&chequebookABI, big.NewInt(0).FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
transactionmock.ABICall(&chequebookABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
),
),
address,
......@@ -416,8 +416,8 @@ func TestChequebookWithdraw(t *testing.T) {
chequebookService, err := chequebook.New(
transactionmock.New(
transactionmock.WithABICallSequence(
transactionmock.ABICall(&chequebookABI, balance.FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
transactionmock.ABICall(&chequebookABI, address, balance.FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
),
transactionmock.WithABISend(&chequebookABI, txHash, address, big.NewInt(0), "withdraw", withdrawAmount),
),
......@@ -451,8 +451,8 @@ func TestChequebookWithdrawInsufficientFunds(t *testing.T) {
transactionmock.New(
transactionmock.WithABISend(&chequebookABI, txHash, address, big.NewInt(0), "withdraw", withdrawAmount),
transactionmock.WithABICallSequence(
transactionmock.ABICall(&chequebookABI, big.NewInt(0).FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
transactionmock.ABICall(&chequebookABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
),
),
address,
......
......@@ -53,12 +53,12 @@ func TestReceiveCheque(t *testing.T) {
beneficiary,
transactionmock.New(
transactionmock.WithABICallSequence(
transactionmock.ABICall(&chequebookABI, issuer.Hash().Bytes(), "issuer"),
transactionmock.ABICall(&chequebookABI, cumulativePayout2.FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
transactionmock.ABICall(&chequebookABI, issuer.Hash().Bytes(), "issuer"),
transactionmock.ABICall(&chequebookABI, cumulativePayout2.FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
transactionmock.ABICall(&chequebookABI, chequebookAddress, issuer.Hash().Bytes(), "issuer"),
transactionmock.ABICall(&chequebookABI, chequebookAddress, cumulativePayout2.FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, chequebookAddress, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
transactionmock.ABICall(&chequebookABI, chequebookAddress, issuer.Hash().Bytes(), "issuer"),
transactionmock.ABICall(&chequebookABI, chequebookAddress, cumulativePayout2.FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, chequebookAddress, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
),
),
func(c *chequebook.SignedCheque, cid int64) (common.Address, error) {
......@@ -175,9 +175,9 @@ func TestReceiveChequeInvalidAmount(t *testing.T) {
beneficiary,
transactionmock.New(
transactionmock.WithABICallSequence(
transactionmock.ABICall(&chequebookABI, issuer.Hash().Bytes(), "issuer"),
transactionmock.ABICall(&chequebookABI, cumulativePayout.FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
transactionmock.ABICall(&chequebookABI, chequebookAddress, issuer.Hash().Bytes(), "issuer"),
transactionmock.ABICall(&chequebookABI, chequebookAddress, cumulativePayout.FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, chequebookAddress, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
),
),
func(c *chequebook.SignedCheque, cid int64) (common.Address, error) {
......@@ -232,8 +232,8 @@ func TestReceiveChequeInvalidChequebook(t *testing.T) {
beneficiary,
transactionmock.New(
transactionmock.WithABICallSequence(
transactionmock.ABICall(&chequebookABI, issuer.Bytes(), "issuer"),
transactionmock.ABICall(&chequebookABI, cumulativePayout.FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, chequebookAddress, issuer.Bytes(), "issuer"),
transactionmock.ABICall(&chequebookABI, chequebookAddress, cumulativePayout.FillBytes(make([]byte, 32)), "balance"),
),
),
func(c *chequebook.SignedCheque, cid int64) (common.Address, error) {
......@@ -273,7 +273,7 @@ func TestReceiveChequeInvalidSignature(t *testing.T) {
beneficiary,
transactionmock.New(
transactionmock.WithABICallSequence(
transactionmock.ABICall(&chequebookABI, issuer.Hash().Bytes(), "issuer"),
transactionmock.ABICall(&chequebookABI, chequebookAddress, issuer.Hash().Bytes(), "issuer"),
),
),
func(c *chequebook.SignedCheque, cid int64) (common.Address, error) {
......@@ -313,9 +313,9 @@ func TestReceiveChequeInsufficientBalance(t *testing.T) {
beneficiary,
transactionmock.New(
transactionmock.WithABICallSequence(
transactionmock.ABICall(&chequebookABI, issuer.Hash().Bytes(), "issuer"),
transactionmock.ABICall(&chequebookABI, new(big.Int).Sub(cumulativePayout, big.NewInt(1)).FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
transactionmock.ABICall(&chequebookABI, chequebookAddress, issuer.Hash().Bytes(), "issuer"),
transactionmock.ABICall(&chequebookABI, chequebookAddress, new(big.Int).Sub(cumulativePayout, big.NewInt(1)).FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, chequebookAddress, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
),
),
func(c *chequebook.SignedCheque, cid int64) (common.Address, error) {
......@@ -355,9 +355,9 @@ func TestReceiveChequeSufficientBalancePaidOut(t *testing.T) {
beneficiary,
transactionmock.New(
transactionmock.WithABICallSequence(
transactionmock.ABICall(&chequebookABI, issuer.Hash().Bytes(), "issuer"),
transactionmock.ABICall(&chequebookABI, new(big.Int).Sub(cumulativePayout, big.NewInt(100)).FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
transactionmock.ABICall(&chequebookABI, chequebookAddress, issuer.Hash().Bytes(), "issuer"),
transactionmock.ABICall(&chequebookABI, chequebookAddress, new(big.Int).Sub(cumulativePayout, big.NewInt(100)).FillBytes(make([]byte, 32)), "balance"),
transactionmock.ABICall(&chequebookABI, chequebookAddress, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
),
),
func(c *chequebook.SignedCheque, cid int64) (common.Address, error) {
......
......@@ -22,7 +22,7 @@ func (m *chequeSignerMock) Sign(cheque *chequebook.Cheque) ([]byte, error) {
type factoryMock struct {
erc20Address func(ctx context.Context) (common.Address, error)
deploy func(ctx context.Context, issuer common.Address, defaultHardDepositTimeoutDuration *big.Int) (common.Hash, error)
deploy func(ctx context.Context, issuer common.Address, defaultHardDepositTimeoutDuration *big.Int, nonce common.Hash) (common.Hash, error)
waitDeployed func(ctx context.Context, txHash common.Hash) (common.Address, error)
verifyBytecode func(ctx context.Context) error
verifyChequebook func(ctx context.Context, chequebook common.Address) error
......@@ -33,8 +33,8 @@ func (m *factoryMock) ERC20Address(ctx context.Context) (common.Address, error)
return m.erc20Address(ctx)
}
func (m *factoryMock) Deploy(ctx context.Context, issuer common.Address, defaultHardDepositTimeoutDuration *big.Int) (common.Hash, error) {
return m.deploy(ctx, issuer, defaultHardDepositTimeoutDuration)
func (m *factoryMock) Deploy(ctx context.Context, issuer common.Address, defaultHardDepositTimeoutDuration *big.Int, nonce common.Hash) (common.Hash, error) {
return m.deploy(ctx, issuer, defaultHardDepositTimeoutDuration, nonce)
}
func (m *factoryMock) WaitDeployed(ctx context.Context, txHash common.Hash) (common.Address, error) {
......
......@@ -22,7 +22,7 @@ var (
ErrNotDeployedByFactory = errors.New("chequebook not deployed by factory")
errDecodeABI = errors.New("could not decode abi data")
factoryABI = transaction.ParseABIUnchecked(sw3abi.SimpleSwapFactoryABIv0_3_1)
factoryABI = transaction.ParseABIUnchecked(sw3abi.SimpleSwapFactoryABIv0_4_0)
simpleSwapDeployedEventType = factoryABI.Events["SimpleSwapDeployed"]
)
......@@ -31,7 +31,7 @@ type Factory interface {
// ERC20Address returns the token for which this factory deploys chequebooks.
ERC20Address(ctx context.Context) (common.Address, error)
// Deploy deploys a new chequebook and returns once the transaction has been submitted.
Deploy(ctx context.Context, issuer common.Address, defaultHardDepositTimeoutDuration *big.Int) (common.Hash, error)
Deploy(ctx context.Context, issuer common.Address, defaultHardDepositTimeoutDuration *big.Int, nonce common.Hash) (common.Hash, error)
// WaitDeployed waits for the deployment transaction to confirm and returns the chequebook address
WaitDeployed(ctx context.Context, txHash common.Hash) (common.Address, error)
// VerifyBytecode checks that the factory is valid.
......@@ -43,25 +43,36 @@ type Factory interface {
type factory struct {
backend transaction.Backend
transactionService transaction.Service
address common.Address
address common.Address // address of the factory to use for deployments
legacyAddresses []common.Address // addresses of old factories which were allowed for deployment
}
type simpleSwapDeployedEvent struct {
ContractAddress common.Address
}
// the bytecode of factories which can be used for deployment
var currentDeployVersion []byte = common.FromHex(sw3abi.SimpleSwapFactoryDeployedBinv0_4_0)
// the bytecode of factories from which we accept chequebooks
var supportedVersions = [][]byte{
currentDeployVersion,
common.FromHex(sw3abi.SimpleSwapFactoryDeployedBinv0_3_1),
}
// NewFactory creates a new factory service for the provided factory contract.
func NewFactory(backend transaction.Backend, transactionService transaction.Service, address common.Address) Factory {
func NewFactory(backend transaction.Backend, transactionService transaction.Service, address common.Address, legacyAddresses []common.Address) Factory {
return &factory{
backend: backend,
transactionService: transactionService,
address: address,
legacyAddresses: legacyAddresses,
}
}
// Deploy deploys a new chequebook and returns once the transaction has been submitted.
func (c *factory) Deploy(ctx context.Context, issuer common.Address, defaultHardDepositTimeoutDuration *big.Int) (common.Hash, error) {
callData, err := factoryABI.Pack("deploySimpleSwap", issuer, big.NewInt(0).Set(defaultHardDepositTimeoutDuration))
func (c *factory) Deploy(ctx context.Context, issuer common.Address, defaultHardDepositTimeoutDuration *big.Int, nonce common.Hash) (common.Hash, error) {
callData, err := factoryABI.Pack("deploySimpleSwap", issuer, big.NewInt(0).Set(defaultHardDepositTimeoutDuration), nonce)
if err != nil {
return common.Hash{}, err
}
......@@ -105,45 +116,83 @@ func (c *factory) VerifyBytecode(ctx context.Context) (err error) {
return err
}
referenceCode := common.FromHex(sw3abi.SimpleSwapFactoryDeployedBinv0_3_1)
if !bytes.Equal(code, referenceCode) {
if !bytes.Equal(code, currentDeployVersion) {
return ErrInvalidFactory
}
LOOP:
for _, factoryAddress := range c.legacyAddresses {
code, err := c.backend.CodeAt(ctx, factoryAddress, nil)
if err != nil {
return err
}
for _, referenceCode := range supportedVersions {
if bytes.Equal(code, referenceCode) {
continue LOOP
}
}
return fmt.Errorf("failed to find matching bytecode for factory %x: %w", factoryAddress, ErrInvalidFactory)
}
return nil
}
// VerifyChequebook checks that the supplied chequebook has been deployed by this factory.
func (c *factory) VerifyChequebook(ctx context.Context, chequebook common.Address) error {
func (c *factory) verifyChequebookAgainstFactory(ctx context.Context, factory common.Address, chequebook common.Address) (bool, error) {
callData, err := factoryABI.Pack("deployedContracts", chequebook)
if err != nil {
return err
return false, err
}
output, err := c.transactionService.Call(ctx, &transaction.TxRequest{
To: &c.address,
To: &factory,
Data: callData,
})
if err != nil {
return err
return false, err
}
results, err := factoryABI.Unpack("deployedContracts", output)
if err != nil {
return err
return false, err
}
if len(results) != 1 {
return errDecodeABI
return false, errDecodeABI
}
deployed, ok := abi.ConvertType(results[0], new(bool)).(*bool)
if !ok || deployed == nil {
return errDecodeABI
return false, errDecodeABI
}
if !*deployed {
return ErrNotDeployedByFactory
return false, nil
}
return true, nil
}
// VerifyChequebook checks that the supplied chequebook has been deployed by a supported factory.
func (c *factory) VerifyChequebook(ctx context.Context, chequebook common.Address) error {
deployed, err := c.verifyChequebookAgainstFactory(ctx, c.address, chequebook)
if err != nil {
return err
}
if deployed {
return nil
}
for _, factoryAddress := range c.legacyAddresses {
deployed, err := c.verifyChequebookAgainstFactory(ctx, factoryAddress, chequebook)
if err != nil {
return err
}
if deployed {
return nil
}
}
return ErrNotDeployedByFactory
}
// ERC20Address returns the token for which this factory deploys chequebooks.
......@@ -178,10 +227,12 @@ func (c *factory) ERC20Address(ctx context.Context) (common.Address, error) {
}
// DiscoverFactoryAddress returns the canonical factory for this chainID
func DiscoverFactoryAddress(chainID int64) (common.Address, bool) {
func DiscoverFactoryAddress(chainID int64) (currentFactory common.Address, legacyFactories []common.Address, found bool) {
if chainID == 5 {
// goerli
return common.HexToAddress("0xf0277caffea72734853b834afc9892461ea18474"), true
return common.HexToAddress("0x73c412512E1cA0be3b89b77aB3466dA6A1B9d273"), []common.Address{
common.HexToAddress("0xf0277caffea72734853b834afc9892461ea18474"),
}, true
}
return common.Address{}, false
return common.Address{}, nil, false
}
......@@ -7,6 +7,7 @@ package chequebook_test
import (
"context"
"errors"
"fmt"
"math/big"
"testing"
......@@ -20,7 +21,7 @@ import (
)
var (
factoryABI = transaction.ParseABIUnchecked(sw3abi.SimpleSwapFactoryABIv0_3_1)
factoryABI = transaction.ParseABIUnchecked(sw3abi.SimpleSwapFactoryABIv0_4_0)
simpleSwapDeployedEvent = factoryABI.Events["SimpleSwapDeployed"]
)
......@@ -32,11 +33,13 @@ func TestFactoryERC20Address(t *testing.T) {
transactionmock.New(
transactionmock.WithABICall(
&factoryABI,
factoryAddress,
erc20Address.Hash().Bytes(),
"ERC20Address",
),
),
factoryAddress,
nil,
)
addr, err := factory.ERC20Address(context.Background())
......@@ -49,46 +52,73 @@ func TestFactoryERC20Address(t *testing.T) {
}
}
func TestFactoryVerifySelf(t *testing.T) {
factoryAddress := common.HexToAddress("0xabcd")
factory := chequebook.NewFactory(
backendmock.New(
func backendWithCodeAt(codeMap map[common.Address]string) transaction.Backend {
return backendmock.New(
backendmock.WithCodeAtFunc(func(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
if contract != factoryAddress {
t.Fatalf("called with wrong address. wanted %x, got %x", factoryAddress, contract)
code, ok := codeMap[contract]
if !ok {
return nil, fmt.Errorf("called with wrong address. wanted one of %v, got %x", codeMap, contract)
}
if blockNumber != nil {
t.Fatal("not called for latest block")
return nil, errors.New("not called for latest block")
}
return common.FromHex(sw3abi.SimpleSwapFactoryDeployedBinv0_3_1), nil
return common.FromHex(code), nil
}),
)
}
func TestFactoryVerifySelf(t *testing.T) {
factoryAddress := common.HexToAddress("0xabcd")
legacyFactory1 := common.HexToAddress("0xbbbb")
legacyFactory2 := common.HexToAddress("0xcccc")
t.Run("valid", func(t *testing.T) {
factory := chequebook.NewFactory(
backendWithCodeAt(map[common.Address]string{
factoryAddress: sw3abi.SimpleSwapFactoryDeployedBinv0_4_0,
legacyFactory1: sw3abi.SimpleSwapFactoryDeployedBinv0_3_1,
legacyFactory2: sw3abi.SimpleSwapFactoryDeployedBinv0_3_1,
}),
),
transactionmock.New(),
factoryAddress,
[]common.Address{legacyFactory1, legacyFactory2},
)
err := factory.VerifyBytecode(context.Background())
if err != nil {
t.Fatal(err)
}
}
})
func TestFactoryVerifySelfInvalidCode(t *testing.T) {
factoryAddress := common.HexToAddress("0xabcd")
t.Run("invalid deploy factory", func(t *testing.T) {
factory := chequebook.NewFactory(
backendmock.New(
backendmock.WithCodeAtFunc(func(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
if contract != factoryAddress {
t.Fatalf("called with wrong address. wanted %x, got %x", factoryAddress, contract)
backendWithCodeAt(map[common.Address]string{
factoryAddress: "abcd",
}),
transactionmock.New(),
factoryAddress,
nil,
)
err := factory.VerifyBytecode(context.Background())
if err == nil {
t.Fatal("verified invalid factory")
}
if blockNumber != nil {
t.Fatal("not called for latest block")
if !errors.Is(err, chequebook.ErrInvalidFactory) {
t.Fatalf("wrong error. wanted %v, got %v", chequebook.ErrInvalidFactory, err)
}
return common.FromHex("abcd"), nil
})
t.Run("invalid legacy factories", func(t *testing.T) {
factory := chequebook.NewFactory(
backendWithCodeAt(map[common.Address]string{
factoryAddress: sw3abi.SimpleSwapFactoryDeployedBinv0_4_0,
legacyFactory1: sw3abi.SimpleSwapFactoryDeployedBinv0_3_1,
legacyFactory2: "abcd",
}),
),
transactionmock.New(),
factoryAddress,
[]common.Address{legacyFactory1, legacyFactory2},
)
err := factory.VerifyBytecode(context.Background())
......@@ -98,43 +128,102 @@ func TestFactoryVerifySelfInvalidCode(t *testing.T) {
if !errors.Is(err, chequebook.ErrInvalidFactory) {
t.Fatalf("wrong error. wanted %v, got %v", chequebook.ErrInvalidFactory, err)
}
})
}
func TestFactoryVerifyChequebook(t *testing.T) {
factoryAddress := common.HexToAddress("0xabcd")
chequebookAddress := common.HexToAddress("0xefff")
legacyFactory1 := common.HexToAddress("0xbbbb")
legacyFactory2 := common.HexToAddress("0xcccc")
t.Run("valid", func(t *testing.T) {
factory := chequebook.NewFactory(
backendmock.New(),
transactionmock.New(
transactionmock.WithABICall(
&factoryABI,
factoryAddress,
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
"deployedContracts",
chequebookAddress,
),
),
factoryAddress,
[]common.Address{legacyFactory1, legacyFactory2},
)
err := factory.VerifyChequebook(context.Background(), chequebookAddress)
if err != nil {
t.Fatal(err)
}
}
})
func TestFactoryVerifyChequebookInvalid(t *testing.T) {
factoryAddress := common.HexToAddress("0xabcd")
chequebookAddress := common.HexToAddress("0xefff")
t.Run("valid legacy", func(t *testing.T) {
factory := chequebook.NewFactory(
backendmock.New(),
transactionmock.New(
transactionmock.WithABICall(
transactionmock.WithABICallSequence(
transactionmock.ABICall(
&factoryABI,
factoryAddress,
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"),
"deployedContracts",
chequebookAddress,
),
transactionmock.ABICall(
&factoryABI,
legacyFactory1,
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"),
"deployedContracts",
chequebookAddress,
),
transactionmock.ABICall(
&factoryABI,
legacyFactory2,
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
"deployedContracts",
chequebookAddress,
),
)),
factoryAddress,
[]common.Address{legacyFactory1, legacyFactory2},
)
err := factory.VerifyChequebook(context.Background(), chequebookAddress)
if err != nil {
t.Fatal(err)
}
})
t.Run("invalid", func(t *testing.T) {
factory := chequebook.NewFactory(
backendmock.New(),
transactionmock.New(
transactionmock.WithABICallSequence(
transactionmock.ABICall(
&factoryABI,
factoryAddress,
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"),
"deployedContracts",
chequebookAddress,
),
transactionmock.ABICall(
&factoryABI,
legacyFactory1,
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"),
"deployedContracts",
chequebookAddress,
),
transactionmock.ABICall(
&factoryABI,
legacyFactory2,
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"),
"deployedContracts",
chequebookAddress,
),
)),
factoryAddress,
[]common.Address{legacyFactory1, legacyFactory2},
)
err := factory.VerifyChequebook(context.Background(), chequebookAddress)
......@@ -144,6 +233,7 @@ func TestFactoryVerifyChequebookInvalid(t *testing.T) {
if !errors.Is(err, chequebook.ErrNotDeployedByFactory) {
t.Fatalf("wrong error. wanted %v, got %v", chequebook.ErrNotDeployedByFactory, err)
}
})
}
func TestFactoryDeploy(t *testing.T) {
......@@ -152,10 +242,12 @@ func TestFactoryDeploy(t *testing.T) {
defaultTimeout := big.NewInt(1)
deployTransactionHash := common.HexToHash("0xffff")
deployAddress := common.HexToAddress("0xdddd")
nonce := common.HexToHash("eeff")
factory := chequebook.NewFactory(
backendmock.New(),
transactionmock.New(
transactionmock.WithABISend(&factoryABI, deployTransactionHash, factoryAddress, big.NewInt(0), "deploySimpleSwap", issuerAddress, defaultTimeout),
transactionmock.WithABISend(&factoryABI, deployTransactionHash, factoryAddress, big.NewInt(0), "deploySimpleSwap", issuerAddress, defaultTimeout, nonce),
transactionmock.WithWaitForReceiptFunc(func(ctx context.Context, txHash common.Hash) (receipt *types.Receipt, err error) {
if txHash != deployTransactionHash {
t.Fatalf("waiting for wrong transaction. wanted %x, got %x", deployTransactionHash, txHash)
......@@ -180,9 +272,10 @@ func TestFactoryDeploy(t *testing.T) {
},
)),
factoryAddress,
nil,
)
txHash, err := factory.Deploy(context.Background(), issuerAddress, defaultTimeout)
txHash, err := factory.Deploy(context.Background(), issuerAddress, defaultTimeout, nonce)
if err != nil {
t.Fatal(err)
}
......@@ -217,6 +310,7 @@ func TestFactoryDeployReverted(t *testing.T) {
}),
),
factoryAddress,
nil,
)
_, err := factory.WaitDeployed(context.Background(), deployTransactionHash)
......
......@@ -6,6 +6,7 @@ package chequebook
import (
"context"
"crypto/rand"
"fmt"
"math/big"
"time"
......@@ -137,8 +138,14 @@ func Init(
}
}
nonce := make([]byte, 32)
_, err = rand.Read(nonce)
if err != nil {
return nil, err
}
// if we don't yet have a chequebook, deploy a new one
txHash, err = chequebookFactory.Deploy(ctx, overlayEthAddress, big.NewInt(0))
txHash, err = chequebookFactory.Deploy(ctx, overlayEthAddress, big.NewInt(0), common.BytesToHash(nonce))
if err != nil {
return nil, err
}
......
......@@ -31,6 +31,7 @@ func TestBalanceOf(t *testing.T) {
transactionmock.New(
transactionmock.WithABICall(
&erc20ABI,
erc20Address,
expectedBalance.FillBytes(make([]byte, 32)),
"balanceOf",
account,
......
......@@ -89,13 +89,15 @@ func New(opts ...Option) transaction.Service {
type Call struct {
abi *abi.ABI
to common.Address
result []byte
method string
params []interface{}
}
func ABICall(abi *abi.ABI, result []byte, method string, params ...interface{}) Call {
func ABICall(abi *abi.ABI, to common.Address, result []byte, method string, params ...interface{}) Call {
return Call{
to: to,
abi: abi,
result: result,
method: method,
......@@ -121,6 +123,13 @@ func WithABICallSequence(calls ...Call) Option {
return nil, fmt.Errorf("wrong data. wanted %x, got %x", data, request.Data)
}
if request.To == nil {
return nil, errors.New("call with no recipient")
}
if *request.To != call.to {
return nil, fmt.Errorf("wrong recipient. wanted %x, got %x", call.to, *request.To)
}
calls = calls[1:]
return call.result, nil
......@@ -128,8 +137,8 @@ func WithABICallSequence(calls ...Call) Option {
})
}
func WithABICall(abi *abi.ABI, result []byte, method string, params ...interface{}) Option {
return WithABICallSequence(ABICall(abi, result, method, params...))
func WithABICall(abi *abi.ABI, to common.Address, result []byte, method string, params ...interface{}) Option {
return WithABICallSequence(ABICall(abi, to, result, method, params...))
}
func WithABISend(abi *abi.ABI, txHash common.Hash, expectedAddress common.Address, expectedValue *big.Int, method string, params ...interface{}) Option {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment