Created
November 18, 2019 21:33
-
-
Save wadealexc/7820c0cd82fd5fdc11a0ad58a84165ae to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity 0.5.11; | |
import "../utils/RLPReader.sol"; | |
/** | |
* @notice Data structure and its decode function for Payment transaction | |
*/ | |
library PaymentTransactionModel { | |
using RLPReader for bytes; | |
using RLPReader for RLPReader.RLPItem; | |
uint8 constant public MAX_INPUT_NUM = 4; | |
uint8 constant public MAX_OUTPUT_NUM = 4; | |
uint8 constant private ENCODED_LENGTH = 4; | |
struct Output { | |
uint256 outputType; | |
bytes20 outputGuard; | |
address token; | |
uint256 amount; | |
} | |
struct Transaction { | |
uint256 txType; | |
bytes32[] inputs; | |
PaymentOutputModel.Output[] outputs; | |
bytes32 metaData; | |
} | |
function decodeSpendTx(bytes memory _tx) internal pure returns (PaymentTransactionModel.Transaction memory) { | |
// NOTE - With a fixed-length encoding, each function could include a length check like this | |
require(_tx.length == 646); | |
// NOTE - Specifying .toLongList rather than .toList allows RLPReader to reject some invalid encodings | |
// almost immediately | |
RLPReader.RLPItem[] memory rlpTx = _tx.toRlpItem().toLongList(); | |
require(rlpTx.length == ENCODED_LENGTH); | |
// NOTE - While some inputs and outputs may be zeroed-out, a long list with 4 elements | |
// is still expected | |
// - One possible option here is to use a method to check payload size for each element | |
// as the list is created. toLongListWithPayload(uint _size) would check that each | |
// element in the list has a certain size. | |
RLPReader.RLPItem[] memory rlpInputs = rlpTx[1].toLongList(); | |
require(rlpInputs.length == ENCODED_LENGTH); | |
RLPReader.RLPItem[] memory rlpOutputs = rlpTx[2].toLongList(); | |
require(rlpOutputs.length == ENCODED_LENGTH); | |
// NOTE - If using an RLP byte as the txType, use an explicit method | |
uint txType = rlpTx[0].byteToUint(); | |
// NOTE - References to these arrays may reference zeroed-out values. | |
// This could have been true before as well (but make sure to check on access!) | |
bytes32[] memory inputs = new bytes32[](ENCODED_LENGTH); | |
Output[] memory outputs = new Output[](ENCODED_LENGTH); | |
for (uint i = 0; i < ENCODED_LENGTH; i++) { | |
bytes32 input = bytes32(rlpInputs[i].shortStringToUint()); | |
inputs[i] = input; | |
Output memory output = decodeOutput(rlpOutputs[i]); | |
outputs[i] = output; | |
} | |
bytes32 metaData = bytes32(rlpTx[3].shortStringToUint()); | |
return Transaction({txType: txType, inputs: inputs, outputs: outputs, metaData: metaData}); | |
} | |
function decodeOutput(RLPReader.RLPItem memory encoded) internal pure returns (Output memory) { | |
// NOTE - Again, a check for the size of each element would be helpful (as well as a | |
// check on the overall payload size) | |
RLPReader.RLPItem[] memory rlpEncoded = encoded.toLongList(); | |
require(rlpEncoded.length == 4, "Invalid output encoding"); | |
// NOTE - Each of these conversion methods should expect a fixed-length payload | |
// (32 bytes for uint, 20 for address) | |
Output memory output = Output({ | |
outputType: rlpEncoded[0].shortStringToUint(), | |
outputGuard: bytes20(rlpEncoded[1].shortStringToAddress()), | |
token: rlpEncoded[2].shortStringToAddress(), | |
amount: rlpEncoded[3].shortStringToUint() | |
}); | |
return output; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment