Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 127 additions & 2 deletions contracts/SyscoinSuperblocks.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ contract SyscoinSuperblocks is Initializable, SyscoinSuperblocksI, SyscoinErrorC
// SyscoinClaimManager
address public trustedClaimManager;

uint32 constant SYSCOIN_TX_VERSION_ASSET_ACTIVATE = 0x7402;
uint32 constant SYSCOIN_TX_VERSION_ASSET_UPDATE = 0x7403;
modifier onlyClaimManager() {
require(msg.sender == trustedClaimManager);
_;
Expand Down Expand Up @@ -194,6 +196,34 @@ contract SyscoinSuperblocks is Initializable, SyscoinSuperblocksI, SyscoinErrorC
);
}


/** @dev Parse syscoin asset transaction to recover asset guid and contract, for purposes of updating asset registry in erc20manager
* @param txBytes syscoin raw transaction
* @return errorCode, assetGuid, erc20Address
*/
function parseAssetTx(bytes memory txBytes)
public
view
returns (uint errorCode, uint32 assetGuid, address erc20Address)
{
uint32 version;
uint pos = 0;
version = bytesToUint32Flipped(txBytes, pos);
if(version != SYSCOIN_TX_VERSION_ASSET_ACTIVATE && version != SYSCOIN_TX_VERSION_ASSET_UPDATE){
return (ERR_PARSE_TX_SYS, 0, address(0));
}
pos = getOpReturnPos(txBytes, 4);
byte pushDataOp = txBytes[pos+1];
pos += 2; // we will have to skip pushdata op as well as atleast 1 byte
if(pushDataOp == 0x4d){
pos++; // skip pushdata2 + 2 bytes for opreturn varint
}

(assetGuid, erc20Address) = scanAssetTx(txBytes, pos);
require(erc20Address != address(0),
"parseAssetTx(): erc20Address cannot be empty");
}

function bytesToUint16(bytes memory input, uint pos) public pure returns (uint16 result) {
result = uint16(uint8(input[pos+1])) + uint16(uint8(input[pos]))*(2**8);
}
Expand Down Expand Up @@ -241,6 +271,61 @@ contract SyscoinSuperblocks is Initializable, SyscoinSuperblocksI, SyscoinErrorC
return ethTxReceipt;
}

/**
* Parse txBytes and returns assetguid + contract address
* @param txBytes syscoin raw transaction
* @param pos position at where to start parsing
* @return asset guid (uint32) and erc20 address linked to the asset guid to update registry in erc20manager
*/
function scanAssetTx(bytes memory txBytes, uint pos)
public
view
returns (uint32, address)
{
uint32 assetGUID;
address erc20Address;
uint bytesToRead;
// skip vchPubData
(bytesToRead, pos) = parseVarInt(txBytes, pos);
pos += bytesToRead;
// skip txHash
pos += 32;
// get nAsset
assetGUID = bytesToUint32Flipped(txBytes, pos);
pos += 4;
// skip strSymbol
(bytesToRead, pos) = parseVarInt(txBytes, pos);
pos += bytesToRead;
// skip witnessAddress.nVersion
pos += 1;
// skip witnessAddress.vchWitnessProgram
(bytesToRead, pos) = parseVarInt(txBytes, pos);
pos += bytesToRead;
// skip witnessAddressTransfer.nVersion
pos += 1;
// skip witnessAddressTransfer.vchWitnessProgram
(bytesToRead, pos) = parseVarInt(txBytes, pos);
pos += bytesToRead;
// skip nBalance
pos += 8;
// skip nTotalSupply
pos += 8;
// skip nMaxSupply
pos += 8;
// skip nHeight
pos += 4;
// skip nUpdateFlags
pos += 1;
// skip nPrecision
pos += 1;
// get vchContract
(bytesToRead, pos) = parseVarInt(txBytes, pos);
require(bytesToRead == 0x14,
"scanAssetTx(): Invalid number of bytes read for contract field");
erc20Address = readEthereumAddress(txBytes, pos);
return (assetGUID, erc20Address);
}

// @dev converts bytes of any length to bytes32.
// If `_rawBytes` is longer than 32 bytes, it truncates to the 32 leftmost bytes.
// If it is shorter, it pads with 0s on the left.
Expand Down Expand Up @@ -578,6 +663,46 @@ contract SyscoinSuperblocks is Initializable, SyscoinSuperblocksI, SyscoinErrorC
return(ERR_RELAY_VERIFY);
}

// @dev - relays asset transaction(new or update) `_txBytes` to ERC20Manager's processAsset() method.
// Also logs the value of processAsset.
// Note: callers cannot be 100% certain when an ERR_RELAY_VERIFY occurs because
// it may also have been returned by processAsset(). Callers should be
// aware of the contract that they are relaying transactions to and
// understand what that contract's processTransaction method returns.
//
// @param _txBytes - transaction bytes
// @param _txIndex - transaction's index within the block
// @param _txSiblings - transaction's Merkle siblings
// @param _syscoinBlockHeader - block header containing transaction
// @param _syscoinBlockIndex - block's index within superblock
// @param _syscoinBlockSiblings - block's merkle siblings
// @param _superblockHash - superblock containing block header
function relayAssetTx(
bytes memory _txBytes,
uint _txIndex,
uint[] memory _txSiblings,
bytes memory _syscoinBlockHeader,
uint _syscoinBlockIndex,
uint[] memory _syscoinBlockSiblings,
bytes32 _superblockHash
) public returns (uint) {
uint txHash = verifySPVProofs(_syscoinBlockHeader, _syscoinBlockIndex, _syscoinBlockSiblings, _superblockHash, _txBytes, _txIndex, _txSiblings);
if (txHash != 0) {
uint ret;
uint32 assetGUID;
address erc20ContractAddress;
(ret, assetGUID, erc20ContractAddress) = parseAssetTx(_txBytes);
if(ret != 0){
emit RelayTransaction(bytes32(txHash), ret);
return ret;
}
syscoinERC20Manager.processAsset(txHash, assetGUID, erc20ContractAddress);
return 0;
}
emit RelayTransaction(bytes32(0), ERR_RELAY_VERIFY);
return(ERR_RELAY_VERIFY);
}

// Challenges a bridge cancellation request with SPV proofs linking tx to superblock and showing that a valid
// cancellation request exists. If challenge fails, the cancellation request continues until timeout at which point erc20 is refunded
//
Expand Down Expand Up @@ -653,11 +778,11 @@ contract SyscoinSuperblocks is Initializable, SyscoinSuperblocksI, SyscoinErrorC
// if dummy 0x00 is present this is a witness transaction
if(n_inputs == 0x00){
(n_inputs, pos) = parseVarInt(txBytes, pos); // flag
require(n_inputs != 0x00);
require(n_inputs != 0x00, "#SyscoinSuperblocks skipInputs(): Unexpected dummy/flag");
// after dummy/flag the real var int comes for txins
(n_inputs, pos) = parseVarInt(txBytes, pos);
}
require(n_inputs < 100);
require(n_inputs < 100, "#SyscoinSuperblocks skipInputs(): Incorrect size of n_inputs");

for (uint i = 0; i < n_inputs; i++) {
pos += 36; // skip outpoint
Expand Down
1 change: 1 addition & 0 deletions contracts/SyscoinTransactionProcessor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ interface SyscoinTransactionProcessor {
function cancelTransferRequest(uint32 bridgeTransferId) external;
function cancelTransferSuccess(uint32 bridgeTransferId, address challengerAddress) external;
function processCancelTransferFail(uint32 bridgeTransferId, address payable challengerAddress) external;
function processAsset(uint txHash, uint32 assetGUID, address erc20ContractAddress) external;
}
21 changes: 18 additions & 3 deletions contracts/token/SyscoinERC20Manager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ contract SyscoinERC20Manager is Initializable {
event CancelTransferRequest(address canceller, uint32 bridgetransferid);
event CancelTransferSucceeded(address canceller, uint32 bridgetransferid);
event CancelTransferFailed(address canceller, uint32 bridgetransferid);

mapping(uint32 => address) public assetRegistry;
event TokenRegistry(uint32 assetGuid, address erc20ContractAddress);
function contains(uint value) private view returns (bool) {
return syscoinTxHashesAlreadyProcessed[value];
}
Expand Down Expand Up @@ -132,9 +135,19 @@ contract SyscoinERC20Manager is Initializable {
erc20.transfer(destinationAddress, userValue);
emit TokenUnfreeze(destinationAddress, userValue);
}

function processAsset(
uint txHash,
uint32 assetGUID,
address erc20ContractAddress
) public onlyTrustedRelayer {
// Add tx to the syscoinTxHashesAlreadyProcessed and Check tx was not already processed
require(insert(txHash), "TX already processed");
assetRegistry[assetGUID] = erc20ContractAddress;
emit TokenRegistry(assetGUID, erc20ContractAddress);
}

function cancelTransferRequest(uint32 bridgeTransferId) public payable {
revert("disabled");
// lookup state by bridgeTransferId
BridgeTransfer storage bridgeTransfer = bridgeTransfers[bridgeTransferId];
// ensure state is Ok
Expand Down Expand Up @@ -210,10 +223,13 @@ contract SyscoinERC20Manager is Initializable {
{
require(syscoinAddress.length > 0, "syscoinAddress cannot be zero");
require(assetGUID > 0, "Asset GUID must not be 0");

if (net != Network.REGTEST) {
require(assetRegistry[assetGUID] == erc20ContractAddress, "Asset registry contract does not match what was provided to this call");
}

SyscoinERC20I erc20 = SyscoinERC20I(erc20ContractAddress);
require(precision == erc20.decimals(), "Decimals were not provided with the correct value");
erc20.transferFrom(msg.sender, address(this), value);
assetBalances[assetGUID] = assetBalances[assetGUID].add(value);

// store some state needed for potential bridge transfer cancellation
Expand All @@ -227,7 +243,6 @@ contract SyscoinERC20Manager is Initializable {
timestamp: block.timestamp,
tokenFreezerAddress: msg.sender
});
erc20.transferFrom(msg.sender, address(this), value);
emit TokenFreeze(msg.sender, value, bridgeTransferIdCount);
return true;
}
Expand Down
2 changes: 1 addition & 1 deletion migrations/2_deploy_contracts.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ module.exports = function(deployer, networkName, accounts) {
const { network, txParams } = await ConfigManager.initNetworkConfiguration({ network: networkName, from: accounts[0] })

if (networkName === 'development') {
SyscoinERC20Manager = await deploy(networkName, { network, txParams }, accounts, SYSCOIN_MAINNET, SUPERBLOCK_OPTIONS_LOCAL);
SyscoinERC20Manager = await deploy(networkName, { network, txParams }, accounts, SYSCOIN_REGTEST, SUPERBLOCK_OPTIONS_LOCAL);
} else {
if (networkName === 'ropsten') {
SyscoinERC20Manager = await deploy(networkName, { network, txParams }, accounts, SYSCOIN_MAINNET, SUPERBLOCK_OPTIONS_PRODUCTION);
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions test/superblocks.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading