eth-offline

EVM token minter frontend for offline issuance using ERC712 structured signatures.
Log | Files | Refs

commit 122da8e693ba5c660d070683689630e46ad01dd7
parent 4e92f53c42f59a1a032c1dbaa7e98650390c0ca3
Author: lash <dev@holbrook.no>
Date:   Mon, 13 Mar 2023 17:37:48 +0000

Complete test for digest for beneficiary, contract and signer

Diffstat:
Apython/eth_offline/data/Offline.bin | 2++
Apython/requirements.txt | 1+
Apython/test_requirements.txt | 3+++
Apython/tests/test_basic.py | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asolidity/Offline.sol | 41+++++++++++++++++++++++++++++++++++++++++
Asolidity/OfflineBase.sol | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsolidity/OfflineMinter.sol | 95-------------------------------------------------------------------------------
Dsolidity/OfflineMinterImplement.sol | 12------------
8 files changed, 301 insertions(+), 107 deletions(-)

diff --git a/python/eth_offline/data/Offline.bin b/python/eth_offline/data/Offline.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611628806100606000396000f3fe608060405234801561001057600080fd5b50600436106100a5576000357c010000000000000000000000000000000000000000000000000000000090048063a7bb580311610078578063a7bb580314610145578063aa1d23a014610177578063aeb02492146101a7578063e9395679146101d7576100a5565b8063557ba855146100aa578063772295f6146100c65780638da5cb5b146100f757806397aba7f914610115575b600080fd5b6100c460048036038101906100bf9190610d64565b610207565b005b6100e060048036038101906100db9190610d64565b61020a565b6040516100ee929190610e6d565b60405180910390f35b6100ff6104ba565b60405161010c9190610e9d565b60405180910390f35b61012f600480360381019061012a9190610eee565b6104e3565b60405161013c9190610e9d565b60405180910390f35b61015f600480360381019061015a9190610d64565b610552565b60405161016e93929190610f75565b60405180910390f35b610191600480360381019061018c9190610fac565b6105ba565b60405161019e9190610e9d565b60405180910390f35b6101c160048036038101906101bc9190610fac565b6109a2565b6040516101ce919061103f565b60405180910390f35b6101f160048036038101906101ec9190611090565b6109f0565b6040516101fe9190611112565b60405180910390f35b50565b600060608060606014855111610255576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161024c90611180565b60405180910390fd5b601467ffffffffffffffff8111156102705761026f610c39565b5b6040519080825280601f01601f1916602001820160405280156102a25781602001600182028036833780820191505090505b509150601485516102b391906111cf565b67ffffffffffffffff8111156102cc576102cb610c39565b5b6040519080825280601f01601f1916602001820160405280156102fe5781602001600182028036833780820191505090505b50905060005b82518110156103bf578581815181106103205761031f611203565b5b60200101517f010000000000000000000000000000000000000000000000000000000000000090047f01000000000000000000000000000000000000000000000000000000000000000283828151811061037d5761037c611203565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535080806103b790611232565b915050610304565b5060005b815181101561048a57856014826103da919061127a565b815181106103eb576103ea611203565b5b60200101517f010000000000000000000000000000000000000000000000000000000000000090047f01000000000000000000000000000000000000000000000000000000000000000282828151811061044857610447611203565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350808061048290611232565b9150506103c3565b5061049481610207565b8161049e9061130f565b6c01000000000000000000000000900481935093505050915091565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000806000806104f285610552565b9250925092506001868285856040516000815260200160405260405161051b9493929190611376565b6020604051602081039080840390855afa15801561053d573d6000803e3d6000fd5b50505060206040510351935050505092915050565b6000806000604184511461059b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161059290611407565b60405180910390fd5b6020840151925060408401519150606084015160001a90509193909250565b600060606000806060600080600060148a516105d6919061127a565b94506105e1856109f0565b93508351925082601a866105f5919061127a565b6105ff919061127a565b67ffffffffffffffff81111561061857610617610c39565b5b6040519080825280601f01601f19166020018201604052801561064a5781602001600182028036833780820191505090505b50965060005b601a81101561070a57604051602001610668906114a4565b604051602081830303815290604052610680906114fa565b81601a811061069257610691611203565b5b1a7f0100000000000000000000000000000000000000000000000000000000000000028882815181106106c8576106c7611203565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350808061070290611232565b915050610650565b50601a82610718919061127a565b915060005b838110156107e25784818151811061073857610737611203565b5b60200101517f010000000000000000000000000000000000000000000000000000000000000090047f01000000000000000000000000000000000000000000000000000000000000000288848361078f919061127a565b815181106107a05761079f611203565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535080806107da90611232565b91505061071d565b5082826107ef919061127a565b9150306c0100000000000000000000000002905060005b60148110156108a55781816014811061082257610821611203565b5b1a7f010000000000000000000000000000000000000000000000000000000000000002888483610852919061127a565b8151811061086357610862611203565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350808061089d90611232565b915050610806565b506014826108b3919061127a565b915060005b8a5181101561097e578a81815181106108d4576108d3611203565b5b60200101517f010000000000000000000000000000000000000000000000000000000000000090047f01000000000000000000000000000000000000000000000000000000000000000288848361092b919061127a565b8151811061093c5761093b611203565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350808061097690611232565b9150506108b8565b5086805190602001209550610993868a6104e3565b97505050505050505092915050565b6000806109ad6104ba565b90506109b984846105ba565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161491505092915050565b606060006064905060008167ffffffffffffffff811115610a1457610a13610c39565b5b6040519080825280601f01601f191660200182016040528015610a465781602001600182028036833780820191505090505b50905060005b60008514610ad8576000600a86610a639190611590565b9050600a86610a7291906115c1565b9550806030610a81919061127a565b600102838380610a9090611232565b945081518110610aa357610aa2611203565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050610a4c565b60008167ffffffffffffffff811115610af457610af3610c39565b5b6040519080825280601f01601f191660200182016040528015610b265781602001600182028036833780820191505090505b50905060005b82811015610bfd578381600185610b4391906111cf565b610b4d91906111cf565b81518110610b5e57610b5d611203565b5b60200101517f010000000000000000000000000000000000000000000000000000000000000090047f010000000000000000000000000000000000000000000000000000000000000002828281518110610bbb57610bba611203565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508080610bf590611232565b915050610b2c565b5080945050505050919050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610c7182610c28565b810181811067ffffffffffffffff82111715610c9057610c8f610c39565b5b80604052505050565b6000610ca3610c0a565b9050610caf8282610c68565b919050565b600067ffffffffffffffff821115610ccf57610cce610c39565b5b610cd882610c28565b9050602081019050919050565b82818337600083830152505050565b6000610d07610d0284610cb4565b610c99565b905082815260208101848484011115610d2357610d22610c23565b5b610d2e848285610ce5565b509392505050565b600082601f830112610d4b57610d4a610c1e565b5b8135610d5b848260208601610cf4565b91505092915050565b600060208284031215610d7a57610d79610c14565b5b600082013567ffffffffffffffff811115610d9857610d97610c19565b5b610da484828501610d36565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610dd882610dad565b9050919050565b610de881610dcd565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610e28578082015181840152602081019050610e0d565b60008484015250505050565b6000610e3f82610dee565b610e498185610df9565b9350610e59818560208601610e0a565b610e6281610c28565b840191505092915050565b6000604082019050610e826000830185610ddf565b8181036020830152610e948184610e34565b90509392505050565b6000602082019050610eb26000830184610ddf565b92915050565b6000819050919050565b610ecb81610eb8565b8114610ed657600080fd5b50565b600081359050610ee881610ec2565b92915050565b60008060408385031215610f0557610f04610c14565b5b6000610f1385828601610ed9565b925050602083013567ffffffffffffffff811115610f3457610f33610c19565b5b610f4085828601610d36565b9150509250929050565b610f5381610eb8565b82525050565b600060ff82169050919050565b610f6f81610f59565b82525050565b6000606082019050610f8a6000830186610f4a565b610f976020830185610f4a565b610fa46040830184610f66565b949350505050565b60008060408385031215610fc357610fc2610c14565b5b600083013567ffffffffffffffff811115610fe157610fe0610c19565b5b610fed85828601610d36565b925050602083013567ffffffffffffffff81111561100e5761100d610c19565b5b61101a85828601610d36565b9150509250929050565b60008115159050919050565b61103981611024565b82525050565b60006020820190506110546000830184611030565b92915050565b6000819050919050565b61106d8161105a565b811461107857600080fd5b50565b60008135905061108a81611064565b92915050565b6000602082840312156110a6576110a5610c14565b5b60006110b48482850161107b565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60006110e4826110bd565b6110ee81856110c8565b93506110fe818560208601610e0a565b61110781610c28565b840191505092915050565b6000602082019050818103600083015261112c81846110d9565b905092915050565b7f4552525f434f4e54454e54530000000000000000000000000000000000000000600082015250565b600061116a600c836110c8565b915061117582611134565b602082019050919050565b600060208201905081810360008301526111998161115d565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006111da8261105a565b91506111e58361105a565b92508282039050818111156111fd576111fc6111a0565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600061123d8261105a565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361126f5761126e6111a0565b5b600182019050919050565b60006112858261105a565b91506112908361105a565b92508282019050808211156112a8576112a76111a0565b5b92915050565b6000819050602082019050919050565b60007fffffffffffffffffffffffffffffffffffffffff00000000000000000000000082169050919050565b60006112f682516112be565b80915050919050565b60008160020a8302905092915050565b600061131a82610dee565b82611324846112ae565b905061132f816112ea565b9250601482101561136f5761136a7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000836014036008026112ff565b831692505b5050919050565b600060808201905061138b6000830187610f4a565b6113986020830186610f66565b6113a56040830185610f4a565b6113b26060830184610f4a565b95945050505050565b7f696e76616c6964207369676e6174757265206c656e6774680000000000000000600082015250565b60006113f16018836110c8565b91506113fc826113bb565b602082019050919050565b60006020820190508181036000830152611420816113e4565b9050919050565b600081905092915050565b7f307831393435373436383635373236353735366432303533363936373665363560008201527f3634323034643635373337333631363736353361306100000000000000000000602082015250565b600061148e603683611427565b915061149982611432565b603682019050919050565b60006114af82611481565b9150819050919050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000082169050919050565b60006114f182516114b9565b80915050919050565b600061150582610dee565b8261150f846112ae565b905061151a816114e5565b9250601a82101561155a576115557fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000083601a036008026112ff565b831692505b5050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061159b8261105a565b91506115a68361105a565b9250826115b6576115b5611561565b5b828206905092915050565b60006115cc8261105a565b91506115d78361105a565b9250826115e7576115e6611561565b5b82820490509291505056fea2646970667358221220b100f5ede2d5731f6ea861c88ad1802aac0d821239fbef797283fdd94cbdfbab64736f6c63430008120033 +\ No newline at end of file diff --git a/python/requirements.txt b/python/requirements.txt @@ -0,0 +1 @@ +chainlib-eth~=0.4.17 diff --git a/python/test_requirements.txt b/python/test_requirements.txt @@ -0,0 +1,3 @@ +eth_tester==0.5.0b3 +py-evm==0.3.0a20 +funga-eth~=0.6.5 diff --git a/python/tests/test_basic.py b/python/tests/test_basic.py @@ -0,0 +1,145 @@ +# standard imports +import logging +import os +import unittest + +# external imports +from chainlib.eth.tx import TxFactory +from chainlib.eth.unittest.ethtester import EthTesterCase +from chainlib.connection import RPCConnection +from chainlib.eth.nonce import RPCNonceOracle +from chainlib.eth.address import to_checksum_address +from chainlib.eth.tx import receipt +from chainlib.eth.gas import Gas +from chainlib.eth.gas import OverrideGasOracle +from chainlib.eth.contract import ABIContractEncoder +from chainlib.eth.contract import ABIContractType +from chainlib.jsonrpc import JSONRPCRequest +from hexathon import add_0x +from hexathon import strip_0x +from hexathon import same as same_hex +from funga.eth.message import to_digest + +script_dir = os.path.realpath(os.path.dirname(__file__)) +data_dir = os.path.join(script_dir, '..', 'eth_offline', 'data') + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger(__name__) + + +class TestOfflineEth(EthTesterCase): + + def setUp(self): + super(TestOfflineEth, self).setUp() + nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) + gas_oracle = OverrideGasOracle(limit=3000000) + + fp = os.path.join(data_dir, 'Offline.bin') + f = open(fp, 'r') + bytecode = f.read() + f.close() + + c = Gas(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle) + self.conn = RPCConnection.connect(self.chain_spec, 'default') + + (tx_hash, o) = c.create(self.accounts[0], None, 0, data=bytecode) + r = self.conn.do(o) + + o = receipt(r) + r = self.conn.do(o) + self.address = to_checksum_address(r['contract_address']) + logg.debug('smart contract published with hash {} address {}'.format(r, self.address)) + + + def test_split(self): + c = TxFactory(self.chain_spec) + j = JSONRPCRequest() + o = j.template() + o['method'] = 'eth_call' + enc = ABIContractEncoder() + enc.method('splitData') + enc.typ(ABIContractType.BYTES) + enc.bytes(strip_0x(self.accounts[1]) + '666f6f') + data = add_0x(enc.get()) + tx = c.template(self.accounts[0], self.address) + tx = c.set_code(tx, data) + o['params'].append(c.normalize(tx)) + o['params'].append('latest') + o = j.finalize(o) + + r = self.rpc.do(o) + r = strip_0x(r) + self.assertTrue(same_hex(r[24:64], self.accounts[1])) + self.assertTrue(same_hex(r[192:198], '666f6f')) + + + def test_sign_match(self): + contract_bin = bytes.fromhex(strip_0x(self.address)) + beneficiary_bin = bytes.fromhex(strip_0x(self.accounts[2])) + msg_bin = b'foo' + msg_data = beneficiary_bin + msg_bin + msg_tosign = contract_bin + msg_data + msg_digest = to_digest(msg_tosign) + + sig = self.signer.sign_ethereum_message(self.accounts[1], msg_tosign) + sig = sig[:64] + (sig[64] + 27).to_bytes(1, byteorder='big') + logg.debug('message is signed by {}'.format(self.accounts[1])) + + c = TxFactory(self.chain_spec) + j = JSONRPCRequest() + o = j.template() + o['method'] = 'eth_call' + enc = ABIContractEncoder() + enc.method('recoverSigner') + #enc.typ(ABIContractType.BYTES) + enc.typ(ABIContractType.BYTES32) + enc.typ(ABIContractType.BYTES) + enc.bytes32(msg_digest.hex()) + enc.bytes(sig.hex()) + data = add_0x(enc.get()) + tx = c.template(self.accounts[0], self.address) + tx = c.set_code(tx, data) + o['params'].append(c.normalize(tx)) + o['params'].append('latest') + o = j.finalize(o) + + r = self.rpc.do(o) + r = strip_0x(r) + + self.assertTrue(same_hex(r[24:64], self.accounts[1])) + + + def test_verify(self): + contract_bin = bytes.fromhex(strip_0x(self.address)) + beneficiary_bin = bytes.fromhex(strip_0x(self.accounts[2])) + msg_bin = b'bar' + msg_data = beneficiary_bin + msg_bin + msg_tosign = contract_bin + msg_data + + sig = self.signer.sign_ethereum_message(self.accounts[1], msg_tosign) + sig = sig[:64] + (sig[64] + 27).to_bytes(1, byteorder='big') + logg.debug('message is signed by {}'.format(self.accounts[1])) + + c = TxFactory(self.chain_spec) + j = JSONRPCRequest() + o = j.template() + o['method'] = 'eth_call' + enc = ABIContractEncoder() + enc.method('fromRequest') + enc.typ(ABIContractType.BYTES) + enc.typ(ABIContractType.BYTES) + enc.bytes(msg_data.hex()) + enc.bytes(sig.hex()) + data = add_0x(enc.get()) + tx = c.template(self.accounts[0], self.address) + tx = c.set_code(tx, data) + o['params'].append(c.normalize(tx)) + o['params'].append('latest') + o = j.finalize(o) + + r = self.rpc.do(o) + r = strip_0x(r) + + +if __name__ == '__main__': + unittest.main() diff --git a/solidity/Offline.sol b/solidity/Offline.sol @@ -0,0 +1,41 @@ +pragma solidity ^0.8.0; + +// SPDX-License-Identifier: GPL-3.0-or-later + +import './OfflineBase.sol'; + +contract OfflineRubber is Offline { + + address contractOwner; + + constructor() { + contractOwner = msg.sender; + } + + function verifyExtraData(bytes memory _data) public override pure { + _data; + } + + function owner() public override view returns(address) { + return contractOwner; + } + + function splitData(bytes memory _data) public pure returns(address _beneficiary, bytes memory _extra) { + bytes memory beneficiary; + bytes memory extra; + + require(_data.length > 20, 'ERR_CONTENTS'); + + beneficiary = new bytes(20); + extra = new bytes(_data.length - 20); + + for (uint256 i; i < beneficiary.length; i++) { + beneficiary[i] = _data[i]; + } + for (uint256 i; i < extra.length; i++) { + extra[i] = _data[i+20]; + } + verifyExtraData(extra); + return (address(bytes20(beneficiary)), extra); + } +} diff --git a/solidity/OfflineBase.sol b/solidity/OfflineBase.sol @@ -0,0 +1,109 @@ +pragma solidity ^0.8.0; + +// SPDX-License-Identifier: GPL-3.0-or-later + +abstract contract Offline { + + bytes26 constant prefix = bytes26(abi.encodePacked("0x19457468657265756d205369676e6564204d6573736167653a0a")); + + function fromRequest(bytes memory _data, bytes memory _signature) public view returns(address) { + //address beneficiary; + //bytes memory extra; + bytes memory message; + bytes32 messageDigest; + uint256 messageLength; + bytes memory messageLengthStr; + uint256 messageLengthLength; + uint256 c; + bytes20 contractBytes; + + //(beneficiary, extra) = splitData(_data); + messageLength = _data.length + 20; + messageLengthStr = bytes(uintToString(uint(messageLength))); + messageLengthLength = bytes(messageLengthStr).length; + + message = new bytes(messageLength + 26 + messageLengthLength); + + for (uint256 i; i < 26; i++) { + message[i] = prefix[i]; + } + c += 26; + + for (uint256 i; i < messageLengthLength; i++) { + message[i+c] = messageLengthStr[i]; + } + c += messageLengthLength; + + contractBytes = bytes20(address(this)); + for (uint256 i; i < 20; i++) { + message[i+c] = contractBytes[i]; + } + c += 20; + + for (uint256 i; i < _data.length; i++) { + message[i+c] = _data[i]; + } + + messageDigest = keccak256(message); + + return recoverSigner(messageDigest, _signature); + } + + function verifyRequest(bytes memory _data, bytes memory _signature) public view returns(bool) { + address _owner = owner(); + + return _owner == fromRequest(_data, _signature); + } + + function verifyExtraData(bytes memory _data) public virtual pure; + + function owner() public virtual view returns(address); + + // from https://solidity-by-example.org/signature/ + function recoverSigner(bytes32 _ethSignedMessageHash, bytes memory _signature) public pure returns (address) { + (bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature); + return ecrecover(_ethSignedMessageHash, v, r, s); + } + + // from https://solidity-by-example.org/signature/ + function splitSignature(bytes memory sig) public pure returns (bytes32 r, bytes32 s, uint8 v) { + require(sig.length == 65, "invalid signature length"); + + assembly { + /* + First 32 bytes stores the length of the signature + + add(sig, 32) = pointer of sig + 32 + effectively, skips first 32 bytes of signature + + mload(p) loads next 32 bytes starting at the memory address p into memory + */ + + // first 32 bytes, after the length prefix + r := mload(add(sig, 32)) + // second 32 bytes + s := mload(add(sig, 64)) + // final byte (first byte of the next 32 bytes) + v := byte(0, mload(add(sig, 96))) + } + + // implicitly return (r, s, v) + } + + // https://ethereum.stackexchange.com/questions/10811/solidity-concatenate-uint-into-a-string + function uintToString(uint v) public pure returns (string memory str) { + uint maxlength = 100; + bytes memory reversed = new bytes(maxlength); + uint i = 0; + while (v != 0) { + uint remainder = v % 10; + v = v / 10; + reversed[i++] = bytes1(bytes32(48 + remainder)); + } + bytes memory s = new bytes(i); + for (uint j = 0; j < i; j++) { + s[j] = reversed[i - 1 - j]; + } + str = string(s); + } +} diff --git a/solidity/OfflineMinter.sol b/solidity/OfflineMinter.sol @@ -1,95 +0,0 @@ -pragma solidity ^0.8.0; - -// SPDX-License-Identifier: GPL-3.0-or-later - -abstract contract OfflineMinter { - - bytes26 constant prefix = bytes26(abi.encodePacked("0x19457468657265756d205369676e6564204d6573736167653a0a")); - - function verifyRequest(address _owner, bytes memory _data, bytes memory _signature) public view returns(bool) { - address beneficiary; - bytes memory extra; - bytes memory message; - bytes32 messageDigest; - string memory messageLength; - uint256 messageLengthLength; - - (beneficiary, extra) = splitData(_data); - messageLength = uintToString(uint(_data.length + 20)); - messageLengthLength = bytes(messageLength).length; - - message = new bytes(_data.length + 46 + messageLengthLength); - messageDigest = keccak256(abi.encodePacked(prefix, messageLength, address(this), _data)); - - return _owner == recoverSigner(messageDigest, _signature); - } - - function splitData(bytes memory _data) public pure returns(address _beneficiary, bytes memory _extra) { - bytes memory beneficiary; - bytes memory extra; - - require(_data.length > 20, 'ERR_CONTENTS'); - - beneficiary = new bytes(20); - extra = new bytes(_data.length - 20); - - for (uint256 i; i < beneficiary.length; i++) { - beneficiary[i] = _data[i]; - } - for (uint256 i; i < extra.length; i++) { - extra[i] = _data[i+20]; - } - verifyExtraData(extra); - return (address(bytes20(beneficiary)), extra); - } - - function verifyExtraData(bytes memory _data) public virtual pure; - - // from https://solidity-by-example.org/signature/ - function recoverSigner(bytes32 _ethSignedMessageHash, bytes memory _signature) public pure returns (address) { - (bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature); - return ecrecover(_ethSignedMessageHash, v, r, s); - } - - // from https://solidity-by-example.org/signature/ - function splitSignature(bytes memory sig) public pure returns (bytes32 r, bytes32 s, uint8 v) { - require(sig.length == 65, "invalid signature length"); - - assembly { - /* - First 32 bytes stores the length of the signature - - add(sig, 32) = pointer of sig + 32 - effectively, skips first 32 bytes of signature - - mload(p) loads next 32 bytes starting at the memory address p into memory - */ - - // first 32 bytes, after the length prefix - r := mload(add(sig, 32)) - // second 32 bytes - s := mload(add(sig, 64)) - // final byte (first byte of the next 32 bytes) - v := byte(0, mload(add(sig, 96))) - } - - // implicitly return (r, s, v) - } - - // https://ethereum.stackexchange.com/questions/10811/solidity-concatenate-uint-into-a-string - function uintToString(uint v) public pure returns (string memory str) { - uint maxlength = 100; - bytes memory reversed = new bytes(maxlength); - uint i = 0; - while (v != 0) { - uint remainder = v % 10; - v = v / 10; - reversed[i++] = bytes1(bytes32(48 + remainder)); - } - bytes memory s = new bytes(i); - for (uint j = 0; j < i; j++) { - s[j] = reversed[i - 1 - j]; - } - str = string(s); - } -} diff --git a/solidity/OfflineMinterImplement.sol b/solidity/OfflineMinterImplement.sol @@ -1,12 +0,0 @@ -pragma solidity ^0.8.0; - -// SPDX-License-Identifier: GPL-3.0-or-later - -import './OfflineMinter.sol'; - -contract OfflineMinterImpl is OfflineMinter { - - function verifyExtraData(bytes memory _data) public override pure { - _data; - } -}