3

I'm coding a contract in which I would like some functions to be called only if approved by 2 people (a merchant, and a customer).

Now, I know that I could program a buffer storing pending request and waiting for each part to send a confirm transaction.

I don't want the merchant to act without the approval of the customer, but the problem is that I don't want the customer to pay for the gas.

What I was thinking was to somehow only do one transaction, sent by the merchant (who will thus pay for the gas), but that I could check in my smart contract the transaction was signed by the customer.

Do you have any advice on what's the best way to implement it ?

Thanks for your answers

2

You could use an approach similar to the Balance Proofs in Raiden's implementation of state channels. Basically you can have the customer sign a confirmation that only the customer could have possibly signed, and then verify that signature in a Solidity contract.

In your particular example I would approach it like this:

  1. Merchant stores the customer's address on a smart contract.
  2. Customer signs the transaction data off-chain.
  3. The merchant sends the transaction to the contract including the customer's signature.
  4. The smart contract derives the address from the signature and matches that up with customer's address stored in step 1.

See code for using Metamask to sign data here.

1

Here is another example of single transaction multisig by Christian Lundkvist. This scheme is closer to Bitcoin multisig than the stateful multisig contracts.

https://medium.com/@ChrisLundkvist/exploring-simpler-ethereum-multisig-contracts-b71020c19037

Code:

pragma solidity 0.4.15;
contract SimpleMultiSig {

uint public nonce;                // (only) mutable state
uint public threshold;            // immutable state
mapping (address => bool) isOwner; // immutable state
address[] public ownersArr;        // immutable state

function SimpleMultiSig(uint threshold_, address[] owners_) {
  require(owners_.length <= 10 && threshold_ <= owners_.length && threshold_ != 0);

  address lastAdd = address(0); 
  for (uint i=0; i<owners_.length; i++) {
    require(owners_[i] > lastAdd);
    isOwner[owners_[i]] = true;
    lastAdd = owners_[i];
  }
  ownersArr = owners_;
  threshold = threshold_;
}

// Note that address recovered from signatures must be strictly increasing
function execute(uint8[] sigV, bytes32[] sigR, bytes32[] sigS, address destination, uint value, bytes data) {
  require(sigR.length == threshold);
  require(sigR.length == sigS.length && sigR.length == sigV.length);

  // Follows ERC191 signature scheme: https://github.com/ethereum/EIPs/issues/191
  bytes32 txHash = keccak256(byte(0x19), byte(0), this, destination, value, data, nonce);

  address lastAdd = address(0); // cannot have address(0) as an owner
  for (uint i = 0; i < threshold; i++) {
      address recovered = ecrecover(txHash, sigV[i], sigR[i], sigS[i]);
      require(recovered > lastAdd && isOwner[recovered]);
      lastAdd = recovered;
  }

  // If we make it here all signatures are accounted for
  nonce = nonce + 1;
  require(destination.call.value(value)(data));
}

function () payable {}

}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.