Skip to content

Commit

Permalink
[Barz] Allow deriving multiple addresses from a single public key (#3340
Browse files Browse the repository at this point in the history
)

* [Barz] Allow deriving multiple addresses from a single public key

* fix tests

* Update BarzTests.swift

* update tests

* update tests

* update tests
  • Loading branch information
rsrbk authored Aug 4, 2023
1 parent ab65cfd commit f28d866
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,21 @@ class TestBarz {
val publicKeyData = Numeric.hexStringToByteArray("04e6f4e0351e2f556fd7284a9a033832bae046ac31fd529ad02ab6220870624b79eb760e718fdaed7a037dd1d77a561759cee9f2706eb55a729dc953e0d5719b02")
val publicKey = PublicKey(publicKeyData, PublicKeyType.NIST256P1EXTENDED)
val verificationFacet = "0x6BF22ff186CC97D88ECfbA47d1473a234CEBEFDf"
val result = WCBarz.getInitCode(factoryAddress, publicKey, verificationFacet)
val result = WCBarz.getInitCode(factoryAddress, publicKey, verificationFacet, 0)
assertEquals(Numeric.toHexString(result), "0x3fc708630d85a3b5ec217e53100ec2b735d4f800296601cd0000000000000000000000006bf22ff186cc97d88ecfba47d1473a234cebefdf00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004104e6f4e0351e2f556fd7284a9a033832bae046ac31fd529ad02ab6220870624b79eb760e718fdaed7a037dd1d77a561759cee9f2706eb55a729dc953e0d5719b0200000000000000000000000000000000000000000000000000000000000000")
}

@Test
fun testInitCodeNonZeroSalt() {
val factoryAddress = "0x3fC708630d85A3B5ec217E53100eC2b735d4f800"
val publicKeyData = Numeric.hexStringToByteArray("04e6f4e0351e2f556fd7284a9a033832bae046ac31fd529ad02ab6220870624b79eb760e718fdaed7a037dd1d77a561759cee9f2706eb55a729dc953e0d5719b02")
val publicKey = PublicKey(publicKeyData, PublicKeyType.NIST256P1EXTENDED)
val verificationFacet = "0x6BF22ff186CC97D88ECfbA47d1473a234CEBEFDf"
val salt = 1
val result = WCBarz.getInitCode(factoryAddress, publicKey, verificationFacet, salt)
assertEquals(Numeric.toHexString(result), "0x3fc708630d85a3b5ec217e53100ec2b735d4f800296601cd0000000000000000000000006bf22ff186cc97d88ecfba47d1473a234cebefdf00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004104e6f4e0351e2f556fd7284a9a033832bae046ac31fd529ad02ab6220870624b79eb760e718fdaed7a037dd1d77a561759cee9f2706eb55a729dc953e0d5719b0200000000000000000000000000000000000000000000000000000000000000")
}

@Test
fun testCounterfactualAddress() {
val input = Barz.ContractAddressInput.newBuilder()
Expand All @@ -55,6 +66,24 @@ class TestBarz {
assertEquals(result, "0x77F62bb3E43190253D4E198199356CD2b25063cA")
}

@Test
fun testCounterfactualAddressNonZeroSalt() {
val input = Barz.ContractAddressInput.newBuilder()
input.apply {
factory = "0x96C489979E39F877BDb8637b75A25C1a5B2DE14C"
accountFacet = "0xF6F5e5fC74905e65e3FF53c6BacEba8535dd14d1"
verificationFacet = "0xaB84813cbf26Fd951CB3d7E33Dccb8995027e490"
entryPoint = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
facetRegistry = "0x9a95d201BB8F559771784D12c01F8084278c65E5"
defaultFallback = "0x522cDc7558b5f798dF5D61AB09B6D95Ebd342EF9"
bytecode = "0x60806040526040516104c83803806104c883398101604081905261002291610163565b6000858585858560405160240161003d959493929190610264565b60408051601f198184030181529181526020820180516001600160e01b0316634a93641760e01b1790525190915060009081906001600160a01b038a16906100869085906102c3565b600060405180830381855af49150503d80600081146100c1576040519150601f19603f3d011682016040523d82523d6000602084013e6100c6565b606091505b50915091508115806100e157506100dc816102df565b600114155b156100ff57604051636ff35f8960e01b815260040160405180910390fd5b505050505050505050610306565b80516001600160a01b038116811461012457600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561015a578181015183820152602001610142565b50506000910152565b60008060008060008060c0878903121561017c57600080fd5b6101858761010d565b95506101936020880161010d565b94506101a16040880161010d565b93506101af6060880161010d565b92506101bd6080880161010d565b60a08801519092506001600160401b03808211156101da57600080fd5b818901915089601f8301126101ee57600080fd5b81518181111561020057610200610129565b604051601f8201601f19908116603f0116810190838211818310171561022857610228610129565b816040528281528c602084870101111561024157600080fd5b61025283602083016020880161013f565b80955050505050509295509295509295565b600060018060a01b0380881683528087166020840152808616604084015280851660608401525060a0608083015282518060a08401526102ab8160c085016020870161013f565b601f01601f19169190910160c0019695505050505050565b600082516102d581846020870161013f565b9190910192915050565b80516020808301519190811015610300576000198160200360031b1b821691505b50919050565b6101b3806103156000396000f3fe60806040523661000b57005b600080356001600160e01b03191681527f183cde5d4f6bb7b445b8fc2f7f15d0fd1d162275aded24183babbffee7cd491f6020819052604090912054819060601c806100cf576004838101546040516366ffd66360e11b81526000356001600160e01b031916928101929092526001600160a01b03169063cdffacc690602401602060405180830381865afa1580156100a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100cc919061014d565b90505b6001600160a01b0381166101295760405162461bcd60e51b815260206004820152601d60248201527f4261727a3a2046756e6374696f6e20646f6573206e6f74206578697374000000604482015260640160405180910390fd5b3660008037600080366000845af43d6000803e808015610148573d6000f35b3d6000fd5b60006020828403121561015f57600080fd5b81516001600160a01b038116811461017657600080fd5b939250505056fea2646970667358221220d35db061bb6ecdb7688c3674af669ce44d527cae4ded59214d06722d73da62be64736f6c63430008120033"
publicKey = "0xB5547FBdC56DCE45e1B8ef75569916D438e09c46"
salt = 123456
}
val result = WCBarz.getCounterfactualAddress(input.build().toByteArray())
assertEquals(result, "0xB91aaa96B138A1B1D94c9df4628187132c5F2bf1")
}

@Test
fun testGetFormattedSignature() {
val signature = Numeric.hexStringToByteArray("0x3044022012d89e3b41e253dc9e90bd34dc1750d059b76d0b1d16af2059aa26e90b8960bf0220256d8a05572c654906ce422464693e280e243e6d9dbc5f96a681dba846bca276")
Expand Down Expand Up @@ -126,7 +155,7 @@ class TestBarz {
sender = "0x1392Ae041BfBdBAA0cFF9234a0C8F64df97B7218"
preVerificationGas = ByteString.copyFrom("0xb708".toHexByteArray())
verificationGasLimit = ByteString.copyFrom("0x2DC6C0".toHexByteArray())
initCode = ByteString.copyFrom(WCBarz.getInitCode(factoryAddress, publicKey, verificationFacet))
initCode = ByteString.copyFrom(WCBarz.getInitCode(factoryAddress, publicKey, verificationFacet, 0))
}.build()

transaction = Ethereum.Transaction.newBuilder().apply {
Expand Down
2 changes: 1 addition & 1 deletion include/TrustWalletCore/TWBarz.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ TWString *_Nonnull TWBarzGetCounterfactualAddress(TWData *_Nonnull input);
/// \param verificationFacet Verification facet address
/// \return The address.
TW_EXPORT_STATIC_METHOD
TWData *_Nonnull TWBarzGetInitCode(TWString* _Nonnull factory, struct TWPublicKey* _Nonnull publicKey, TWString* _Nonnull verificationFacet);
TWData *_Nonnull TWBarzGetInitCode(TWString* _Nonnull factory, struct TWPublicKey* _Nonnull publicKey, TWString* _Nonnull verificationFacet, uint32_t salt);

/// Converts the original ASN-encoded signature from webauthn to the format accepted by Barz
///
Expand Down
7 changes: 3 additions & 4 deletions src/Ethereum/Barz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include "EIP1014.h"
#include "Hash.h"
#include "HexCoding.h"
#include <WebAuthn.h>
#include "../proto/Barz.pb.h"
#include "AsnParser.h"
#include "Base64.h"
Expand All @@ -35,15 +34,15 @@ std::string getCounterfactualAddress(const Proto::ContractAddressInput input) {
append(initCode, encoded);

const Data initCodeHash = Hash::keccak256(initCode);
const Data salt(32, 0);
Data salt = store(input.salt(), 32);
return Ethereum::checksumed(Ethereum::Address(hexEncoded(Ethereum::create2Address(input.factory(), salt, initCodeHash))));
}

Data getInitCode(const std::string& factoryAddress, const PublicKey& publicKey, const std::string& verificationFacet) {
Data getInitCode(const std::string& factoryAddress, const PublicKey& publicKey, const std::string& verificationFacet, const uint32_t salt) {
auto createAccountFunc = Ethereum::ABI::Function("createAccount", ParamCollection{
std::make_shared<Ethereum::ABI::ParamAddress>(parse_hex(verificationFacet)),
std::make_shared<Ethereum::ABI::ParamByteArray>(publicKey.bytes),
std::make_shared<Ethereum::ABI::ParamUInt256>(0)});
std::make_shared<Ethereum::ABI::ParamUInt256>(salt)});
Data createAccountFuncEncoded;
createAccountFunc.encode(createAccountFuncEncoded);

Expand Down
2 changes: 1 addition & 1 deletion src/Ethereum/Barz.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
namespace TW::Barz {

std::string getCounterfactualAddress(const Proto::ContractAddressInput input);
Data getInitCode(const std::string& factoryAddress, const PublicKey& publicKey, const std::string& verificationFacet);
Data getInitCode(const std::string& factoryAddress, const PublicKey& publicKey, const std::string& verificationFacet, const uint32_t salt);
Data getFormattedSignature(const Data& signature, const Data challenge, const Data& authenticatorData, const std::string& clientDataJSON);

}
4 changes: 2 additions & 2 deletions src/interface/TWBarz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ TWString *_Nonnull TWBarzGetCounterfactualAddress(TWData *_Nonnull input) {
return TWStringCreateWithUTF8Bytes(TW::Barz::getCounterfactualAddress(inputProto).c_str());
}

TWData *_Nonnull TWBarzGetInitCode(TWString* _Nonnull factory, struct TWPublicKey* _Nonnull publicKey, TWString* _Nonnull verificationFacet) {
TWData *_Nonnull TWBarzGetInitCode(TWString* _Nonnull factory, struct TWPublicKey* _Nonnull publicKey, TWString* _Nonnull verificationFacet, uint32_t salt) {
const auto& factoryStr = *reinterpret_cast<const std::string*>(factory);
const auto& publicKeyConverted = *reinterpret_cast<const TW::PublicKey*>(publicKey);
const auto& verificationFacetStr = *reinterpret_cast<const std::string*>(verificationFacet);

const auto initCode = TW::Barz::getInitCode(factoryStr, publicKeyConverted, verificationFacetStr);
const auto initCode = TW::Barz::getInitCode(factoryStr, publicKeyConverted, verificationFacetStr, salt);
return TWDataCreateWithData(&initCode);
}

Expand Down
3 changes: 3 additions & 0 deletions src/proto/Barz.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,7 @@ message ContractAddressInput {
string bytecode = 7;
// PublicKey of the wallet
string public_key = 8;

// Salt is used to derive multiple account from the same public key
uint32 salt = 9;
}
31 changes: 29 additions & 2 deletions swift/Tests/BarzTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,19 @@ class BarzTests: XCTestCase {
let publicKeyData = Data(hexString: "0x04e6f4e0351e2f556fd7284a9a033832bae046ac31fd529ad02ab6220870624b79eb760e718fdaed7a037dd1d77a561759cee9f2706eb55a729dc953e0d5719b02")!
let publicKey = PublicKey(data: publicKeyData, type: .nist256p1Extended)!
let verificationFacet = "0x6BF22ff186CC97D88ECfbA47d1473a234CEBEFDf"
let result = Barz.getInitCode(factory: factoryAddress, publicKey: publicKey, verificationFacet: verificationFacet)
let result = Barz.getInitCode(factory: factoryAddress, publicKey: publicKey, verificationFacet: verificationFacet, salt: 0)
XCTAssertEqual(result.hexString, "3fc708630d85a3b5ec217e53100ec2b735d4f800296601cd0000000000000000000000006bf22ff186cc97d88ecfba47d1473a234cebefdf00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004104e6f4e0351e2f556fd7284a9a033832bae046ac31fd529ad02ab6220870624b79eb760e718fdaed7a037dd1d77a561759cee9f2706eb55a729dc953e0d5719b0200000000000000000000000000000000000000000000000000000000000000")
}

func testInitCodeNoneZeroSalt() {
let factoryAddress = "0x3fC708630d85A3B5ec217E53100eC2b735d4f800"
let publicKeyData = Data(hexString: "0x04e6f4e0351e2f556fd7284a9a033832bae046ac31fd529ad02ab6220870624b79eb760e718fdaed7a037dd1d77a561759cee9f2706eb55a729dc953e0d5719b02")!
let publicKey = PublicKey(data: publicKeyData, type: .nist256p1Extended)!
let verificationFacet = "0x6BF22ff186CC97D88ECfbA47d1473a234CEBEFDf"
let result = Barz.getInitCode(factory: factoryAddress, publicKey: publicKey, verificationFacet: verificationFacet, salt: 1)
XCTAssertEqual(result.hexString, "3fc708630d85a3b5ec217e53100ec2b735d4f800296601cd0000000000000000000000006bf22ff186cc97d88ecfba47d1473a234cebefdf00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004104e6f4e0351e2f556fd7284a9a033832bae046ac31fd529ad02ab6220870624b79eb760e718fdaed7a037dd1d77a561759cee9f2706eb55a729dc953e0d5719b0200000000000000000000000000000000000000000000000000000000000000")
}

func testCounterfactualAddress() {
let input = BarzContractAddressInput.with {
$0.factory = "0x2c97f4a366Dd5D91178ec9E36c5C1fcA393A538C"
Expand All @@ -29,11 +38,28 @@ class BarzTests: XCTestCase {
$0.defaultFallback = "0x22eB0720d9Fc4bC90BB812B309e939880B71c20d"
$0.bytecode = bytecode
$0.publicKey = "0xB5547FBdC56DCE45e1B8ef75569916D438e09c46"
$0.salt = 0
}

XCTAssertEqual(Barz.getCounterfactualAddress(input: try! input.serializedData()), "0x77F62bb3E43190253D4E198199356CD2b25063cA");
}

func testCounterfactualAddressNonZeroSalt() {
let input = BarzContractAddressInput.with {
$0.factory = "0x96C489979E39F877BDb8637b75A25C1a5B2DE14C"
$0.accountFacet = "0xF6F5e5fC74905e65e3FF53c6BacEba8535dd14d1"
$0.verificationFacet = "0xaB84813cbf26Fd951CB3d7E33Dccb8995027e490"
$0.entryPoint = entryPoint
$0.facetRegistry = "0x9a95d201BB8F559771784D12c01F8084278c65E5"
$0.defaultFallback = "0x522cDc7558b5f798dF5D61AB09B6D95Ebd342EF9"
$0.bytecode = "0x60806040526040516104c83803806104c883398101604081905261002291610163565b6000858585858560405160240161003d959493929190610264565b60408051601f198184030181529181526020820180516001600160e01b0316634a93641760e01b1790525190915060009081906001600160a01b038a16906100869085906102c3565b600060405180830381855af49150503d80600081146100c1576040519150601f19603f3d011682016040523d82523d6000602084013e6100c6565b606091505b50915091508115806100e157506100dc816102df565b600114155b156100ff57604051636ff35f8960e01b815260040160405180910390fd5b505050505050505050610306565b80516001600160a01b038116811461012457600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561015a578181015183820152602001610142565b50506000910152565b60008060008060008060c0878903121561017c57600080fd5b6101858761010d565b95506101936020880161010d565b94506101a16040880161010d565b93506101af6060880161010d565b92506101bd6080880161010d565b60a08801519092506001600160401b03808211156101da57600080fd5b818901915089601f8301126101ee57600080fd5b81518181111561020057610200610129565b604051601f8201601f19908116603f0116810190838211818310171561022857610228610129565b816040528281528c602084870101111561024157600080fd5b61025283602083016020880161013f565b80955050505050509295509295509295565b600060018060a01b0380881683528087166020840152808616604084015280851660608401525060a0608083015282518060a08401526102ab8160c085016020870161013f565b601f01601f19169190910160c0019695505050505050565b600082516102d581846020870161013f565b9190910192915050565b80516020808301519190811015610300576000198160200360031b1b821691505b50919050565b6101b3806103156000396000f3fe60806040523661000b57005b600080356001600160e01b03191681527f183cde5d4f6bb7b445b8fc2f7f15d0fd1d162275aded24183babbffee7cd491f6020819052604090912054819060601c806100cf576004838101546040516366ffd66360e11b81526000356001600160e01b031916928101929092526001600160a01b03169063cdffacc690602401602060405180830381865afa1580156100a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100cc919061014d565b90505b6001600160a01b0381166101295760405162461bcd60e51b815260206004820152601d60248201527f4261727a3a2046756e6374696f6e20646f6573206e6f74206578697374000000604482015260640160405180910390fd5b3660008037600080366000845af43d6000803e808015610148573d6000f35b3d6000fd5b60006020828403121561015f57600080fd5b81516001600160a01b038116811461017657600080fd5b939250505056fea2646970667358221220d35db061bb6ecdb7688c3674af669ce44d527cae4ded59214d06722d73da62be64736f6c63430008120033"
$0.publicKey = "0xB5547FBdC56DCE45e1B8ef75569916D438e09c46"
$0.salt = 123456
}

XCTAssertEqual(Barz.getCounterfactualAddress(input: try! input.serializedData()), "0xB91aaa96B138A1B1D94c9df4628187132c5F2bf1");
}

func testFormatSignature() {
let signature = Data(hexString: "0x3044022012d89e3b41e253dc9e90bd34dc1750d059b76d0b1d16af2059aa26e90b8960bf0220256d8a05572c654906ce422464693e280e243e6d9dbc5f96a681dba846bca276")!
let challenge = Data(hexString: "0xcf267a78c5adaf96f341a696eb576824284c572f3e61be619694d539db1925f9")!
Expand Down Expand Up @@ -96,7 +122,8 @@ class BarzTests: XCTestCase {
$0.initCode = Barz.getInitCode(
factory: factory,
publicKey: publicKey,
verificationFacet: "0x5034534Efe9902779eD6eA6983F435c00f3bc510"
verificationFacet: "0x5034534Efe9902779eD6eA6983F435c00f3bc510",
salt: 0
)
}

Expand Down
Loading

0 comments on commit f28d866

Please sign in to comment.