Skip to content

Commit

Permalink
[Barz] Add prefixedMsgHash and diamondCut Encoder (#3680)
Browse files Browse the repository at this point in the history
* Add declaration to Barz header

* Add Bytes32 type to ETH ABI Proto

* Add DiamondCut input to Barz Proto

* Add functions for Barz

* Add Barz interface

* Add Barz test

* Update comments

* Update header comments

* Remove comments

* Update hardcode text to constants

* Update initData encoding

---------

Co-authored-by: Sztergbaum Roman <[email protected]>
  • Loading branch information
PowerStream3604 and Milerius authored Feb 8, 2024
1 parent 2c423d2 commit c08a407
Show file tree
Hide file tree
Showing 7 changed files with 316 additions and 0 deletions.
16 changes: 16 additions & 0 deletions include/TrustWalletCore/TWBarz.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,20 @@ TWData *_Nonnull TWBarzGetInitCode(TWString* _Nonnull factory, struct TWPublicKe
/// \return Bytes of the formatted signature
TW_EXPORT_STATIC_METHOD
TWData *_Nonnull TWBarzGetFormattedSignature(TWData* _Nonnull signature, TWData* _Nonnull challenge, TWData* _Nonnull authenticatorData, TWString* _Nonnull clientDataJSON);

/// Returns the final hash to be signed by Barz for signing messages & typed data
///
/// \param msgHash Original msgHash
/// \param barzAddress The address of Barz wallet signing the message
/// \param chainId The chainId of the network the verification will happen
/// \return The final hash to be signed
TW_EXPORT_STATIC_METHOD
TWData *_Nonnull TWBarzGetPrefixedMsgHash(TWData* _Nonnull msgHash, TWString* _Nonnull barzAddress, uint32_t chainId);

/// Returns the encoded diamondCut function call for Barz contract upgrades
///
/// \param input The serialized data of DiamondCutInput
/// \return The encoded bytes of diamondCut function call
TW_EXPORT_STATIC_METHOD
TWData *_Nonnull TWBarzGetDiamondCutCode(TWData *_Nonnull input);
TW_EXTERN_C_END
20 changes: 20 additions & 0 deletions src/Ethereum/ABI/ProtoParam.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,26 @@ class ProtoByteArray final: public BaseProtoParam {
Data m_data;
};

class ProtoBytes32 final: public BaseProtoParam {
public:
explicit ProtoBytes32(const Data& data): m_data(data) {
if (data.size() != 32) {
throw std::invalid_argument("Data must be exactly 32 bytes long");
}
}

~ProtoBytes32() override = default;

AbiProto::Token toToken() const override {
AbiProto::Token proto;
proto.set_byte_array_fix(m_data.data(), m_data.size());
return proto;
}

private:
Data m_data;
};

class ProtoString final: public BaseProtoParam {
public:
explicit ProtoString(std::string str): m_string(std::move(str)) {
Expand Down
133 changes: 133 additions & 0 deletions src/Ethereum/Barz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
//
// Copyright © 2017 Trust Wallet.

#include <string>
#include "ABI/ValueEncoder.h"
#include "ABI/Function.h"
#include "AddressChecksum.h"
#include "EIP1014.h"
Expand Down Expand Up @@ -85,4 +87,135 @@ Data getFormattedSignature(const Data& signature, const Data challenge, const Da
return {};
}

Data getPrefixedMsgHash(const Data msgHash, const std::string& barzAddress, const uint32_t chainId) {
// keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");
const Data& domainSeparatorTypeHashData = parse_hex("0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218");
// keccak256("BarzMessage(bytes message)")
const Data& barzMsgHashData = parse_hex("0xb1bcb804a4a3a1af3ee7920d949bdfd417ea1b736c3552c8d6563a229a619100");
const auto signedDataPrefix = "0x1901";

auto encodedDomainSeparatorData = Ethereum::ABI::Function::encodeParams(Ethereum::ABI::BaseParams {
std::make_shared<Ethereum::ABI::ProtoBytes32>(domainSeparatorTypeHashData),
std::make_shared<Ethereum::ABI::ProtoUInt256>(chainId),
std::make_shared<Ethereum::ABI::ProtoAddress>(barzAddress)
});
if (!encodedDomainSeparatorData.has_value()) {
return {};
}

Data domainSeparator = encodedDomainSeparatorData.value();
const Data domainSeparatorHash = Hash::keccak256(domainSeparator);

auto encodedRawMessageData = Ethereum::ABI::Function::encodeParams(Ethereum::ABI::BaseParams {
std::make_shared<Ethereum::ABI::ProtoBytes32>(barzMsgHashData),
std::make_shared<Ethereum::ABI::ProtoBytes32>(Hash::keccak256(msgHash)),
});

Data rawMessageData = encodedRawMessageData.value();

auto encodedMsg = Ethereum::ABI::Function::encodeParams(Ethereum::ABI::BaseParams {
std::make_shared<Ethereum::ABI::ProtoBytes32>(domainSeparatorHash),
std::make_shared<Ethereum::ABI::ProtoBytes32>(Hash::keccak256(rawMessageData))
});
auto encodedMsgData = signedDataPrefix + hex(encodedMsg.value());

Data finalEncodedMsgData = parse_hex(encodedMsgData);

const Data encodedMsgHash = Hash::keccak256(finalEncodedMsgData);

Data envelope;
append(envelope, encodedMsgHash);

return envelope;
}

// Function to encode the diamondCut function call using protobuf message as input
Data getDiamondCutCode(const Proto::DiamondCutInput& input) {
const auto diamondCutSelector = "1f931c1c";
const auto dataLocationChunk = "60";
const char defaultPadding = '0';
Data encoded;

// function diamondCut(
// FacetCut[] calldata diamondCut,
// address init,
// bytes calldata _calldata // Note that Barz does not use the _calldata for initialization.
// )
Data encodedSignature = parse_hex(diamondCutSelector); // diamondCut() function selector
encoded.insert(encoded.end(), encodedSignature.begin(), encodedSignature.end());

// First argument Data Location `diamondCut`
Data dataLocation = parse_hex(dataLocationChunk);
pad_left(dataLocation, 32);
append(encoded, dataLocation);

// Encode second Parameter `init`
Data initAddress = parse_hex(input.init_address());
pad_left(initAddress, 32);
append(encoded, initAddress);

// Third Argument Data location `_calldata`
auto callDataDataLocation = int(hex(encoded).size()) / 2;

Ethereum::ABI::ValueEncoder::encodeUInt256(input.facet_cuts_size(), encoded);

// Prepend the function selector for the diamondCut function
int instructChunk = 0;
int totalInstructChunk = 0;
int prevDataPosition = 0;
const auto encodingChunk = 32;
const auto bytesChunkLine = 5;
int chunkLocation;
Data dataPosition;
// Encode each FacetCut from the input
for (const auto& facetCut : input.facet_cuts()) {
if (instructChunk == 0) {
prevDataPosition = input.facet_cuts_size() * encodingChunk;
Ethereum::ABI::ValueEncoder::encodeUInt256(prevDataPosition, encoded);
chunkLocation = int(hex(encoded).size()) / 2;
} else {
prevDataPosition = prevDataPosition + (instructChunk * encodingChunk);
Ethereum::ABI::ValueEncoder::encodeUInt256(prevDataPosition, dataPosition);
instructChunk = 0;

encoded.insert(encoded.begin() + chunkLocation, dataPosition.begin(), dataPosition.end());
++instructChunk;
}
Ethereum::ABI::ValueEncoder::encodeAddress(parse_hex(facetCut.facet_address()), encoded); // facet address
++instructChunk;
Ethereum::ABI::ValueEncoder::encodeUInt256(facetCut.action(), encoded); // FacetAction enum
++instructChunk;
append(encoded, dataLocation); // adding 0x60 DataStorage position
++instructChunk;
Ethereum::ABI::ValueEncoder::encodeUInt256(facetCut.function_selectors_size(), encoded); // Number of FacetSelector
++instructChunk;
// Encode and append function selectors
for (const auto& selector : facetCut.function_selectors()) {
Ethereum::ABI::ValueEncoder::encodeBytes(parse_hex(hex(selector)), encoded);
++instructChunk;
}
totalInstructChunk += instructChunk;
}

Data calldataLength;
Ethereum::ABI::ValueEncoder::encodeUInt256((totalInstructChunk * encodingChunk) + (bytesChunkLine * encodingChunk), calldataLength);

encoded.insert(encoded.begin() + callDataDataLocation, calldataLength.begin(), calldataLength.end());

auto initDataSize = int(hex(parse_hex(input.init_data())).size());
if (initDataSize == 0 || initDataSize % 2 != 0)
return {};

auto initDataLength = initDataSize / 2; // 1 byte is encoded into 2 char
Ethereum::ABI::ValueEncoder::encodeUInt256(initDataLength, encoded);

append(encoded, parse_hex(input.init_data()));

const int paddingLength = (encodingChunk * 2) - (initDataSize % (encodingChunk * 2));
const std::string padding(paddingLength, defaultPadding);
append(encoded, parse_hex(padding));

return encoded;
}

} // namespace TW::Barz
2 changes: 2 additions & 0 deletions src/Ethereum/Barz.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +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, const uint32_t salt);
Data getFormattedSignature(const Data& signature, const Data challenge, const Data& authenticatorData, const std::string& clientDataJSON);
Data getPrefixedMsgHash(const Data msgHash, const std::string& barzAddress, const uint32_t chainId);
Data getDiamondCutCode(const Proto::DiamondCutInput& input); // action should be one of 0, 1, 2. 0 = Add, 1 = Remove, 2 = Replace

}
21 changes: 21 additions & 0 deletions src/interface/TWBarz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,25 @@ TWData *_Nonnull TWBarzGetFormattedSignature(TWData* _Nonnull signature, TWData*

const auto initCode = TW::Barz::getFormattedSignature(signatureData, challengeData, authenticatorDataConverted, clientDataJSONStr);
return TWDataCreateWithData(&initCode);
}

TWData *_Nonnull TWBarzGetPrefixedMsgHash(TWData* _Nonnull msgHash, TWString* _Nonnull barzAddress, uint32_t chainId) {
const auto& msgHashData = *reinterpret_cast<const TW::Data*>(msgHash);
const auto& barzAddressData = *reinterpret_cast<const std::string*>(barzAddress);

const auto prefixedMsgHash = TW::Barz::getPrefixedMsgHash(msgHashData, barzAddressData, chainId);
return TWDataCreateWithData(&prefixedMsgHash);
}

TWData *_Nonnull TWBarzGetDiamondCutCode(TWData *_Nonnull input) {
TW::Barz::Proto::DiamondCutInput inputProto;

const auto bytes = TWDataBytes(input);
const auto size = static_cast<int>(TWDataSize(input));
if (!inputProto.ParseFromArray(bytes, size)) {
return "";
}

const auto diamondCutCode = TW::Barz::getDiamondCutCode(inputProto);
return TWDataCreateWithData(&diamondCutCode);
}
22 changes: 22 additions & 0 deletions src/proto/Barz.proto
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,25 @@ message ContractAddressInput {
// Salt is used to derive multiple account from the same public key
uint32 salt = 9;
}

// FacetCutAction represents the action to be performed for a FacetCut
enum FacetCutAction {
ADD = 0;
REPLACE = 1;
REMOVE = 2;
}

// FacetCut represents a single operation to be performed on a facet
message FacetCut {
string facet_address = 1; // The address of the facet
FacetCutAction action = 2; // The action to perform
repeated bytes function_selectors = 3; // List of function selectors, each is bytes4
}

// DiamondCutInput represents the input parameters for a diamondCut operation
message DiamondCutInput {
repeated FacetCut facet_cuts = 1; // List of facet cuts to apply
string init_address = 2; // Address to call with `init` data after applying cuts
bytes init_data = 3; // Data to pass to `init` function call
}

102 changes: 102 additions & 0 deletions tests/chains/Ethereum/BarzTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,4 +313,106 @@ TEST(Barz, SignR1BatchedTransferAccountDeployed) {
TWEthereumAbiFunctionDelete(transferFunc);
}

TEST(Barz, GetPrefixedMsgHash) {
{
const Data& msgHash = parse_hex("0xa6ebe22d8c1ec7edbd7f5776e49a161f67ab97161d7b8c648d80abf365765cf2");
const std::string& barzAddress = "0x913233BfC283ffe89a5E70ADC39c0926d240bbD9";
const auto chainId = 3604;

const auto& prefixedMsgHash = Barz::getPrefixedMsgHash(msgHash, barzAddress, chainId);
ASSERT_EQ(hexEncoded(prefixedMsgHash), "0x0488fb3e4fdaa890bf55532fc9840fb9edef9c38244f431c9430a78a86d89157");
}
}

TEST(Barz, GetPrefixedMsgHashWithZeroChainId) {
{
const Data& msgHash = parse_hex("0xcf267a78c5adaf96f341a696eb576824284c572f3e61be619694d539db1925f9");
const std::string& barzAddress = "0xB91aaa96B138A1B1D94c9df4628187132c5F2bf1";
const auto chainId = 0;

const auto& prefixedMsgHash = Barz::getPrefixedMsgHash(msgHash, barzAddress, chainId);
ASSERT_EQ(hexEncoded(prefixedMsgHash), "0xc74e78634261222af51530703048f98a1b7b995a606a624f0a008e7aaba7a21b");
}
}

TEST(Barz, GetDiamondCutCode) {
{
TW::Barz::Proto::DiamondCutInput input;

input.set_init_address("0x0000000000000000000000000000000000000000");
input.set_init_data("0x00");

auto* facetCutAdd = input.add_facet_cuts();
facetCutAdd->set_facet_address("0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6");
facetCutAdd->set_action(TW::Barz::Proto::FacetCutAction::ADD);

auto functionSelectorAdd = parse_hex("0xfdd8a83c");
facetCutAdd->add_function_selectors(functionSelectorAdd.data(), functionSelectorAdd.size());

const auto& diamondCutCode = Barz::getDiamondCutCode(input);
ASSERT_EQ(hexEncoded(diamondCutCode), "0x1f931c1c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001fdd8a83c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000");
}
}

TEST(Barz, GetDiamondCutCodeWithMultipleCut) {
{
TW::Barz::Proto::DiamondCutInput input;

input.set_init_address("0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6");
input.set_init_data("0x12341234");

auto* facetCutMigrationFacet = input.add_facet_cuts();
facetCutMigrationFacet->set_facet_address("0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6");
facetCutMigrationFacet->set_action(TW::Barz::Proto::FacetCutAction::ADD);

auto migrationSignature = parse_hex("0xfdd8a83c");

facetCutMigrationFacet->add_function_selectors(migrationSignature.data(), migrationSignature.size());
facetCutMigrationFacet->add_function_selectors(migrationSignature.data(), migrationSignature.size());
facetCutMigrationFacet->add_function_selectors(migrationSignature.data(), migrationSignature.size());

auto* facetCutTestFacet = input.add_facet_cuts();
facetCutTestFacet->set_facet_address("0x6e3c94d74af6227aEeF75b54a679e969189a6aEC");
facetCutTestFacet->set_action(TW::Barz::Proto::FacetCutAction::ADD);

auto testSignature = parse_hex("0x12345678");
facetCutTestFacet->add_function_selectors(testSignature.data(), testSignature.size());


const auto& diamondCutCode = Barz::getDiamondCutCode(input);
ASSERT_EQ(hexEncoded(diamondCutCode), "0x1f931c1c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe600000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000003fdd8a83c00000000000000000000000000000000000000000000000000000000fdd8a83c00000000000000000000000000000000000000000000000000000000fdd8a83c000000000000000000000000000000000000000000000000000000000000000000000000000000006e3c94d74af6227aeef75b54a679e969189a6aec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001123456780000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041234123400000000000000000000000000000000000000000000000000000000");
}
}

TEST(Barz, GetDiamondCutCodeWithZeroSelector) {
{
TW::Barz::Proto::DiamondCutInput input;

input.set_init_address("0x0000000000000000000000000000000000000000");
input.set_init_data("0x00");
auto* facetCutAdd = input.add_facet_cuts();
facetCutAdd->set_facet_address("0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6");
facetCutAdd->set_action(TW::Barz::Proto::FacetCutAction::ADD);

const auto& diamondCutCode = Barz::getDiamondCutCode(input);
ASSERT_EQ(hexEncoded(diamondCutCode), "0x1f931c1c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000");
}
}

TEST(Barz, GetDiamondCutCodeWithLongInitData) {
{
TW::Barz::Proto::DiamondCutInput input;

input.set_init_address("0x0000000000000000000000000000000000000000");
input.set_init_data("0xb61d27f6000000000000000000000000c2ce171d25837cd43e496719f5355a847edc679b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024a526d83b00000000000000000000000090f79bf6eb2c4f870365e785982e1f101e93b90600000000000000000000000000000000000000000000000000000000");
auto* facetCutAdd = input.add_facet_cuts();
facetCutAdd->set_facet_address("0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6");
facetCutAdd->set_action(TW::Barz::Proto::FacetCutAction::ADD);

const auto& diamondCutCode = Barz::getDiamondCutCode(input);
ASSERT_EQ(hexEncoded(diamondCutCode), "0x1f931c1c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c4b61d27f6000000000000000000000000c2ce171d25837cd43e496719f5355a847edc679b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024a526d83b00000000000000000000000090f79bf6eb2c4f870365e785982e1f101e93b9060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
}
}

}

0 comments on commit c08a407

Please sign in to comment.