-
Notifications
You must be signed in to change notification settings - Fork 100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
eth: Solidity atomic swap contract discussion. #1001
Comments
We must be able to audit the swap, including |
I am able to do so using truffle but not %100 sure that's not due to some helper method. I think that all the data stored in that map is public. |
It would be informative to compile the solidity contract and use |
$ ./abigen --sol DecredSwaps.sol --pkg main --out decredswaps.go $ cat decredswaps.go// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package main
import (
"math/big"
"strings"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
)
// Reference imports to suppress errors if they are not otherwise used.
var (
_ = big.NewInt
_ = strings.NewReader
_ = ethereum.NotFound
_ = bind.Bind
_ = common.Big1
_ = types.BloomLookup
_ = event.NewSubscription
)
// DecredSwapsABI is the input ABI used to generate the binding from.
const DecredSwapsABI = "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"refundTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"}],\"name\":\"initiate\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"secret\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"}],\"name\":\"redeem\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"}],\"name\":\"refund\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"swaps\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"initBlockNumber\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"refundBlockTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"secret\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"enumDecredSwaps.State\",\"name\":\"state\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]"
// DecredSwapsFuncSigs maps the 4-byte function signature to its string representation.
var DecredSwapsFuncSigs = map[string]string{
"ae052147": "initiate(uint256,bytes32,address)",
"b31597ad": "redeem(bytes32,bytes32)",
"7249fbb6": "refund(bytes32)",
"eb84e7f2": "swaps(bytes32)",
}
// DecredSwapsBin is the compiled bytecode used for deploying new contracts.
var DecredSwapsBin = "0x608060405234801561001057600080fd5b50610599806100206000396000f3fe60806040526004361061003f5760003560e01c80637249fbb614610044578063ae05214714610066578063b31597ad14610079578063eb84e7f214610099575b600080fd5b34801561005057600080fd5b5061006461005f366004610421565b6100d6565b005b610064610074366004610472565b6101bf565b34801561008557600080fd5b50610064610094366004610451565b610286565b3480156100a557600080fd5b506100b96100b4366004610421565b6103ce565b6040516100cd9897969594939291906104f7565b60405180910390f35b8033600160008381526020819052604090206007015460ff16600381111561010e57634e487b7160e01b600052602160045260246000fd5b1461011857600080fd5b6000828152602081905260409020600401546001600160a01b0382811691161461014157600080fd5b6000828152602081905260409020600101544281111561016057600080fd5b600084815260208190526040808220600601549051339282156108fc02929190818181858888f1935050505015801561019d573d6000803e3d6000fd5b505050600091825250602081905260409020600701805460ff19166003179055565b82600034116101cd57600080fd5b600081116101da57600080fd5b826000808281526020819052604090206007015460ff16600381111561021057634e487b7160e01b600052602160045260246000fd5b1461021a57600080fd5b6000848152602081905260409020438155600180820187905560028201869055600482018054336001600160a01b0319918216179091556005830180549091166001600160a01b0387161790553460068301556007909101805460ff1916828002179055505050505050565b808233600160008481526020819052604090206007015460ff1660038111156102bf57634e487b7160e01b600052602160045260246000fd5b146102c957600080fd5b6000838152602081905260409020600501546001600160a01b038281169116146102f257600080fd5b8260028360405160200161030691906104b5565b60408051601f1981840301815290829052610320916104be565b602060405180830381855afa15801561033d573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906103609190610439565b1461036a57600080fd5b600084815260208190526040808220600601549051339282156108fc02929190818181858888f193505050501580156103a7573d6000803e3d6000fd5b50505060009182525060208190526040902060078101805460ff1916600217905560030155565b6000602081905290815260409020805460018201546002830154600384015460048501546005860154600687015460079097015495969495939492936001600160a01b0392831693919092169160ff1688565b600060208284031215610432578081fd5b5035919050565b60006020828403121561044a578081fd5b5051919050565b60008060408385031215610463578081fd5b50508035926020909101359150565b600080600060608486031215610486578081fd5b833592506020840135915060408401356001600160a01b03811681146104aa578182fd5b809150509250925092565b90815260200190565b60008251815b818110156104de57602081860181015185830152016104c4565b818111156104ec5782828501525b509190910192915050565b8881526020810188905260408101879052606081018690526001600160a01b038581166080830152841660a082015260c0810183905261010081016004831061055057634e487b7160e01b600052602160045260246000fd5b8260e0830152999850505050505050505056fea2646970667358221220ad691af3e42d7584399b943644c249d9264c53c10d8c0405f5330bc5152686da64736f6c63430008010033"
// DeployDecredSwaps deploys a new Ethereum contract, binding an instance of DecredSwaps to it.
func DeployDecredSwaps(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *DecredSwaps, error) {
parsed, err := abi.JSON(strings.NewReader(DecredSwapsABI))
if err != nil {
return common.Address{}, nil, nil, err
}
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(DecredSwapsBin), backend)
if err != nil {
return common.Address{}, nil, nil, err
}
return address, tx, &DecredSwaps{DecredSwapsCaller: DecredSwapsCaller{contract: contract}, DecredSwapsTransactor: DecredSwapsTransactor{contract: contract}, DecredSwapsFilterer: DecredSwapsFilterer{contract: contract}}, nil
}
// DecredSwaps is an auto generated Go binding around an Ethereum contract.
type DecredSwaps struct {
DecredSwapsCaller // Read-only binding to the contract
DecredSwapsTransactor // Write-only binding to the contract
DecredSwapsFilterer // Log filterer for contract events
}
// DecredSwapsCaller is an auto generated read-only Go binding around an Ethereum contract.
type DecredSwapsCaller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// DecredSwapsTransactor is an auto generated write-only Go binding around an Ethereum contract.
type DecredSwapsTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// DecredSwapsFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
type DecredSwapsFilterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// DecredSwapsSession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options.
type DecredSwapsSession struct {
Contract *DecredSwaps // Generic contract binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// DecredSwapsCallerSession is an auto generated read-only Go binding around an Ethereum contract,
// with pre-set call options.
type DecredSwapsCallerSession struct {
Contract *DecredSwapsCaller // Generic contract caller binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
}
// DecredSwapsTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
// with pre-set transact options.
type DecredSwapsTransactorSession struct {
Contract *DecredSwapsTransactor // Generic contract transactor binding to set the session for
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// DecredSwapsRaw is an auto generated low-level Go binding around an Ethereum contract.
type DecredSwapsRaw struct {
Contract *DecredSwaps // Generic contract binding to access the raw methods on
}
// DecredSwapsCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
type DecredSwapsCallerRaw struct {
Contract *DecredSwapsCaller // Generic read-only contract binding to access the raw methods on
}
// DecredSwapsTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
type DecredSwapsTransactorRaw struct {
Contract *DecredSwapsTransactor // Generic write-only contract binding to access the raw methods on
}
// NewDecredSwaps creates a new instance of DecredSwaps, bound to a specific deployed contract.
func NewDecredSwaps(address common.Address, backend bind.ContractBackend) (*DecredSwaps, error) {
contract, err := bindDecredSwaps(address, backend, backend, backend)
if err != nil {
return nil, err
}
return &DecredSwaps{DecredSwapsCaller: DecredSwapsCaller{contract: contract}, DecredSwapsTransactor: DecredSwapsTransactor{contract: contract}, DecredSwapsFilterer: DecredSwapsFilterer{contract: contract}}, nil
}
// NewDecredSwapsCaller creates a new read-only instance of DecredSwaps, bound to a specific deployed contract.
func NewDecredSwapsCaller(address common.Address, caller bind.ContractCaller) (*DecredSwapsCaller, error) {
contract, err := bindDecredSwaps(address, caller, nil, nil)
if err != nil {
return nil, err
}
return &DecredSwapsCaller{contract: contract}, nil
}
// NewDecredSwapsTransactor creates a new write-only instance of DecredSwaps, bound to a specific deployed contract.
func NewDecredSwapsTransactor(address common.Address, transactor bind.ContractTransactor) (*DecredSwapsTransactor, error) {
contract, err := bindDecredSwaps(address, nil, transactor, nil)
if err != nil {
return nil, err
}
return &DecredSwapsTransactor{contract: contract}, nil
}
// NewDecredSwapsFilterer creates a new log filterer instance of DecredSwaps, bound to a specific deployed contract.
func NewDecredSwapsFilterer(address common.Address, filterer bind.ContractFilterer) (*DecredSwapsFilterer, error) {
contract, err := bindDecredSwaps(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &DecredSwapsFilterer{contract: contract}, nil
}
// bindDecredSwaps binds a generic wrapper to an already deployed contract.
func bindDecredSwaps(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader(DecredSwapsABI))
if err != nil {
return nil, err
}
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_DecredSwaps *DecredSwapsRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _DecredSwaps.Contract.DecredSwapsCaller.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_DecredSwaps *DecredSwapsRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _DecredSwaps.Contract.DecredSwapsTransactor.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_DecredSwaps *DecredSwapsRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _DecredSwaps.Contract.DecredSwapsTransactor.contract.Transact(opts, method, params...)
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_DecredSwaps *DecredSwapsCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _DecredSwaps.Contract.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_DecredSwaps *DecredSwapsTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _DecredSwaps.Contract.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_DecredSwaps *DecredSwapsTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _DecredSwaps.Contract.contract.Transact(opts, method, params...)
}
// Swaps is a free data retrieval call binding the contract method 0xeb84e7f2.
//
// Solidity: function swaps(bytes32 ) view returns(uint256 initBlockNumber, uint256 refundBlockTimestamp, bytes32 secretHash, bytes32 secret, address initiator, address participant, uint256 value, uint8 state)
func (_DecredSwaps *DecredSwapsCaller) Swaps(opts *bind.CallOpts, arg0 [32]byte) (struct {
InitBlockNumber *big.Int
RefundBlockTimestamp *big.Int
SecretHash [32]byte
Secret [32]byte
Initiator common.Address
Participant common.Address
Value *big.Int
State uint8
}, error) {
var out []interface{}
err := _DecredSwaps.contract.Call(opts, &out, "swaps", arg0)
outstruct := new(struct {
InitBlockNumber *big.Int
RefundBlockTimestamp *big.Int
SecretHash [32]byte
Secret [32]byte
Initiator common.Address
Participant common.Address
Value *big.Int
State uint8
})
if err != nil {
return *outstruct, err
}
outstruct.InitBlockNumber = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
outstruct.RefundBlockTimestamp = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int)
outstruct.SecretHash = *abi.ConvertType(out[2], new([32]byte)).(*[32]byte)
outstruct.Secret = *abi.ConvertType(out[3], new([32]byte)).(*[32]byte)
outstruct.Initiator = *abi.ConvertType(out[4], new(common.Address)).(*common.Address)
outstruct.Participant = *abi.ConvertType(out[5], new(common.Address)).(*common.Address)
outstruct.Value = *abi.ConvertType(out[6], new(*big.Int)).(**big.Int)
outstruct.State = *abi.ConvertType(out[7], new(uint8)).(*uint8)
return *outstruct, err
}
// Swaps is a free data retrieval call binding the contract method 0xeb84e7f2.
//
// Solidity: function swaps(bytes32 ) view returns(uint256 initBlockNumber, uint256 refundBlockTimestamp, bytes32 secretHash, bytes32 secret, address initiator, address participant, uint256 value, uint8 state)
func (_DecredSwaps *DecredSwapsSession) Swaps(arg0 [32]byte) (struct {
InitBlockNumber *big.Int
RefundBlockTimestamp *big.Int
SecretHash [32]byte
Secret [32]byte
Initiator common.Address
Participant common.Address
Value *big.Int
State uint8
}, error) {
return _DecredSwaps.Contract.Swaps(&_DecredSwaps.CallOpts, arg0)
}
// Swaps is a free data retrieval call binding the contract method 0xeb84e7f2.
//
// Solidity: function swaps(bytes32 ) view returns(uint256 initBlockNumber, uint256 refundBlockTimestamp, bytes32 secretHash, bytes32 secret, address initiator, address participant, uint256 value, uint8 state)
func (_DecredSwaps *DecredSwapsCallerSession) Swaps(arg0 [32]byte) (struct {
InitBlockNumber *big.Int
RefundBlockTimestamp *big.Int
SecretHash [32]byte
Secret [32]byte
Initiator common.Address
Participant common.Address
Value *big.Int
State uint8
}, error) {
return _DecredSwaps.Contract.Swaps(&_DecredSwaps.CallOpts, arg0)
}
// Initiate is a paid mutator transaction binding the contract method 0xae052147.
//
// Solidity: function initiate(uint256 refundTimestamp, bytes32 secretHash, address participant) payable returns()
func (_DecredSwaps *DecredSwapsTransactor) Initiate(opts *bind.TransactOpts, refundTimestamp *big.Int, secretHash [32]byte, participant common.Address) (*types.Transaction, error) {
return _DecredSwaps.contract.Transact(opts, "initiate", refundTimestamp, secretHash, participant)
}
// Initiate is a paid mutator transaction binding the contract method 0xae052147.
//
// Solidity: function initiate(uint256 refundTimestamp, bytes32 secretHash, address participant) payable returns()
func (_DecredSwaps *DecredSwapsSession) Initiate(refundTimestamp *big.Int, secretHash [32]byte, participant common.Address) (*types.Transaction, error) {
return _DecredSwaps.Contract.Initiate(&_DecredSwaps.TransactOpts, refundTimestamp, secretHash, participant)
}
// Initiate is a paid mutator transaction binding the contract method 0xae052147.
//
// Solidity: function initiate(uint256 refundTimestamp, bytes32 secretHash, address participant) payable returns()
func (_DecredSwaps *DecredSwapsTransactorSession) Initiate(refundTimestamp *big.Int, secretHash [32]byte, participant common.Address) (*types.Transaction, error) {
return _DecredSwaps.Contract.Initiate(&_DecredSwaps.TransactOpts, refundTimestamp, secretHash, participant)
}
// Redeem is a paid mutator transaction binding the contract method 0xb31597ad.
//
// Solidity: function redeem(bytes32 secret, bytes32 secretHash) returns()
func (_DecredSwaps *DecredSwapsTransactor) Redeem(opts *bind.TransactOpts, secret [32]byte, secretHash [32]byte) (*types.Transaction, error) {
return _DecredSwaps.contract.Transact(opts, "redeem", secret, secretHash)
}
// Redeem is a paid mutator transaction binding the contract method 0xb31597ad.
//
// Solidity: function redeem(bytes32 secret, bytes32 secretHash) returns()
func (_DecredSwaps *DecredSwapsSession) Redeem(secret [32]byte, secretHash [32]byte) (*types.Transaction, error) {
return _DecredSwaps.Contract.Redeem(&_DecredSwaps.TransactOpts, secret, secretHash)
}
// Redeem is a paid mutator transaction binding the contract method 0xb31597ad.
//
// Solidity: function redeem(bytes32 secret, bytes32 secretHash) returns()
func (_DecredSwaps *DecredSwapsTransactorSession) Redeem(secret [32]byte, secretHash [32]byte) (*types.Transaction, error) {
return _DecredSwaps.Contract.Redeem(&_DecredSwaps.TransactOpts, secret, secretHash)
}
// Refund is a paid mutator transaction binding the contract method 0x7249fbb6.
//
// Solidity: function refund(bytes32 secretHash) returns()
func (_DecredSwaps *DecredSwapsTransactor) Refund(opts *bind.TransactOpts, secretHash [32]byte) (*types.Transaction, error) {
return _DecredSwaps.contract.Transact(opts, "refund", secretHash)
}
// Refund is a paid mutator transaction binding the contract method 0x7249fbb6.
//
// Solidity: function refund(bytes32 secretHash) returns()
func (_DecredSwaps *DecredSwapsSession) Refund(secretHash [32]byte) (*types.Transaction, error) {
return _DecredSwaps.Contract.Refund(&_DecredSwaps.TransactOpts, secretHash)
}
// Refund is a paid mutator transaction binding the contract method 0x7249fbb6.
//
// Solidity: function refund(bytes32 secretHash) returns()
func (_DecredSwaps *DecredSwapsTransactorSession) Refund(secretHash [32]byte) (*types.Transaction, error) {
return _DecredSwaps.Contract.Refund(&_DecredSwaps.TransactOpts, secretHash)
} |
Here's a relevant snippet. We're looking good. I do kinda want to change the
@JoeGruffins I edited your comment to add the |
So there is a single contract that all traders use to make swaps? Calling it The One Swap Contract in #1021 Some questions:
|
Also, with an absolute refundable block number, Apart from the init tx confirmations question, the original contract used times and a duration from the init time instead, and I believe that does fit in our needs. That is, we want a certain amount of time to take actions regardless of how fast blocks are mined. I suppose I could be persuaded to accept an absolute block where refunds are allowed, if the statistical deviation from the target of say 8 or 20 hours is fairly low, like 20 minutes. |
Yes.
Not that I am aware of. It looks like it can be difficult to understand what some data means on the blockchain without seeing the .sol file: https://ethereum.stackexchange.com/questions/188/how-can-you-decompile-a-smart-contract
We will.
Yes...
I'm not sure. Everything I've read indicates that a contract cannot be used to pay gas fees. Do you have the .sol file for that contract?
Clients, even light, can read at any time for free, no tx. We can also have the redeem fail if there are not enough confirmations, how many? Also, this version does away with "participate". I am not sure what the point of those methods were? You can either redeem/refund or you can't. The initiator decides the participator. |
Sort of. The "From" account is only checked against the address specified in the initiation tx as a validation step.
I think this is sort of an over-arching question that needs to be answered early. It appears that we can do this without sending any txids at all. You mention that you want to record the transaction, and my gut agrees, but part of me wonders why we should (other than the generating client). Unless we suspect that we can't trust the methods exposed by the contract, then there doesn't appear to be a compelling case for recording that info server-side or even conveying it to the counter-party. We do probably need a
It looks like with Ethereum's block validation algorithm, there is a reasonably small restriction on block times that the network will propagate, so on the scales of hours using time will be safe to within about 15 minutes. If we want to use time, in Solidity, we would check |
Given how many times smart contracts have been pwned in the past, we might want to ask some ETH expert to review the Solidity code. The contract code above is quite short, without any loops and looks mostly declarative, but I'd like to be triple sure about its correctness. |
We agree with that @xaur. We will also have to consider the contract in the broader context of atomic swap and dex. For instance, the current contract proposed in this issue has a preimage length vulnerability that was an issue with the earliest atomicswap contracts (pre-dex). That is, the This gets to one fundamental different with the eth contract vs the btc or dcr contracts, which is that we the developers are making and auditing the swap contract, whereas with btc and dcr, the actual contract scripts are audited client-side. There's obviously still auditing of certain |
Could you expand on this? I think the current redeem will only allow a 32 byte secret as input. |
Ah, right I was reading this as |
originally posted at #1019 (comment)
|
re: contract per swap Thinking about this a bit more, I think the biggest barrier (after extra fees) is validating every contract. If there's one contract, one can at any time use the same version of solidity to compile our contract.sol and compare the resulting byte code to the byte code in the original transaction and see that the code is what we say (I think). However, if everyone has their own contract, I guess the only way to verify is to also have the correct version of solc installed on every dexc and rebuild the contract to check the byte code. I can imagine it taking a lot of code to get versions right and such. afaik contract byte code cannot be reversed engineered to view what is doing what. Related: https://ethereum.stackexchange.com/questions/188/how-can-you-decompile-a-smart-contract/238#238 |
On a related note, maybe we should hard code the version of solc used in the code. I don't think it's possible to know otherwise. Related: https://www.shawntabrizi.com/ethereum/verify-ethereum-contracts-using-web3-js-and-solc/ |
Good calls @JoeGruffins. I agree that auditing the eth contract on-the-fly is next to impossible. That's probably opening up to more vulnerabilities than using a single contract for everybody like we're planning now. We should continue to consider ways to limit the impact of an attack however. Multiple contracts, randomly choosing one? Hard-coded limits on certain amounts? Dunno. EDIT: linking to the outcome of the ETHSwapV0 PR (#1019 (review)), where we identified and confirmed a vulnerability in the originally proposed contract in which the entire contract could be drained, not just one swap worth of ETH. |
Another thing I expect we'd want to consider for the contract(s) is having it upgradable with a basic proxy like USDC does with At present for DCRDEX, this strikes me as unnecessary complexity, but potentially necessary in the long run. EDIT: Forget that. I believe that would positively undermine the trustless nature of DCRDEX, and bring devs into an undesirably active role in the operation of a dex. |
I think that delegate would break the ability to verify a contract. Whoever creates it will be able to switch out contracts without anyone noticing. My cursory impression is that we cannot use that anyway for security reasons. Or, I suppose you could include the real contract's address as an argument and have that contract verify we are being proxied to what we think we are. But at that point, I can't imagine why we would use the proxy, just use the real address. |
Yah, that's what I meant by "bring devs into an undesirably active role in the operation of a dex" in my edit, because they would be the admins that can upgrade at will. Definitely a bad idea. |
Well, someone will have to deploy the contract anyway. I guess. It's not a whole lot different than what we are planning I believe. |
Consider adding public functions that aid in debugging from an explorer: #1019 (comment) |
I believe the initial discussion can be considered finished. We have a working contract deployed on testnet. Therefore, closing this initial discussion. |
Ethereum
Ethereum does not have utxo or opcodes. It uses accounts and balances. Further more, there are "normal" accounts and contracts, which also count as accounts. Contract accounts can be sent transactions with special data that include instructions. We can use these instructions to make atomic swaps.
UTXO <-> ETH Contract
This contract is a stripped down version of this.
All of the individual functions have been checked to work as intended.
full contract
It works by keeping a map of all swaps. Apparently, the size of the map will not influence the cost of using the contract, and we can grow it forever. The map is keyed by the swap contract's secret's hash. It has a state that increases depending upon where along the swap we are. Any unitiated swap is 0/Empty. Anyone can then initiate the swap with a secret hash moving the status to 1/Filled. Initiating also requires a value, locktime, and participant (I'm not sure if the participant has been checked as a valid hex at this point). The funds are now only spendable when either the participant supplies the secret that hashes to the secret hash, or the refundBlockstamp has passed and the owner of the transaction is the initiator. The status is moved to 2/Redeemed or 3/Refunded. If redeemed, the secret has been saved by the blockchain and can be viewed publicly on the blockchain, allowing the other side of the swap to redeem their funds.
This contract will need to first be deployed on whichever network before it can be used which entails sending a transaction(s?) that creates it.
Considerations
The contract cannot be used without eth!
An eth contract cannot be interacted with without already having eth. For this contract, that means initiating, refunding, and redeeming. This could cause major problems if not checked properly. A party on either side of the trade must have some eth reserves to trade and keep them throughout the duration of the trade or risk loosing the entire trade. The official fix comming soon™.
The original has notifications.
Do we want to use these? They have been cut out of this simplified version.
The original also saves the initial block time.
It seems like we are interested in the block confirmations mostly for the initiation, so it has been switch to save the blocknumber at that time. We could, probably, track the transaction that pays the initiation, but I think we would not be using the smart contracted as intended then. It would also introduce more complexity and possible bugs.
Unknowns
I am hoping that after we have a contract that works for us, but before enabling mainnet, we can have some ethereum experts browse our code. I will probe around for some "experts" towards this end. We have all heard stories about loopholes in poorly written contracts that result in user's lost funds.
I have not researched this yet and this contract does not enable the ERC side of this.
The text was updated successfully, but these errors were encountered: