What is the purpose in asserting 'msg.data.length == 64 + 4'?











up vote
4
down vote

favorite
1












In a contract used for testing Gnosis MultiSigWallet, I found this piece of code:



/*
* This modifier is present in some real world token contracts, and due to a solidity
* bug it was not compatible with multisig wallets
*/
modifier onlyPayloadSize(uint size) {
require(msg.data.length == size + 4);
_;
}

/// @dev Transfers sender's tokens to a given address. Returns success.
/// @param _to Address of token receiver.
/// @param _value Number of tokens to transfer.
/// @return Returns success of function call.
function transfer(address _to, uint256 _value) onlyPayloadSize(2 * 32)
public
returns (bool success)
{
require(balances[msg.sender] >= _value);
balances[msg.sender] -= _value;
balances[_to] += _value;
Transfer(msg.sender, _to, _value);
return true;
}


The documentation of the onlyPayloadSize modifier is extremely unclear to me.



Can somebody please explain the following:




  1. What exactly is the purpose of this modifier?

  2. Why is it used only in the trasnsfer function?

  3. Why is it used with a specific size of 64 (2 * 32)?

  4. Why is this modifier present only in a test contract, and not in the actual MultiSigWallet contract? Does this by any chance imply that I should add this modifier in every contract in my system, designated for access only via an instance of the MultiSigWallet contract? If I don't have any such contract with a standard transfer method (i.e., an ERC20 contract), can I rest assure that I don't need this modifier anywhere else in my system?


Thank you!










share|improve this question




























    up vote
    4
    down vote

    favorite
    1












    In a contract used for testing Gnosis MultiSigWallet, I found this piece of code:



    /*
    * This modifier is present in some real world token contracts, and due to a solidity
    * bug it was not compatible with multisig wallets
    */
    modifier onlyPayloadSize(uint size) {
    require(msg.data.length == size + 4);
    _;
    }

    /// @dev Transfers sender's tokens to a given address. Returns success.
    /// @param _to Address of token receiver.
    /// @param _value Number of tokens to transfer.
    /// @return Returns success of function call.
    function transfer(address _to, uint256 _value) onlyPayloadSize(2 * 32)
    public
    returns (bool success)
    {
    require(balances[msg.sender] >= _value);
    balances[msg.sender] -= _value;
    balances[_to] += _value;
    Transfer(msg.sender, _to, _value);
    return true;
    }


    The documentation of the onlyPayloadSize modifier is extremely unclear to me.



    Can somebody please explain the following:




    1. What exactly is the purpose of this modifier?

    2. Why is it used only in the trasnsfer function?

    3. Why is it used with a specific size of 64 (2 * 32)?

    4. Why is this modifier present only in a test contract, and not in the actual MultiSigWallet contract? Does this by any chance imply that I should add this modifier in every contract in my system, designated for access only via an instance of the MultiSigWallet contract? If I don't have any such contract with a standard transfer method (i.e., an ERC20 contract), can I rest assure that I don't need this modifier anywhere else in my system?


    Thank you!










    share|improve this question


























      up vote
      4
      down vote

      favorite
      1









      up vote
      4
      down vote

      favorite
      1






      1





      In a contract used for testing Gnosis MultiSigWallet, I found this piece of code:



      /*
      * This modifier is present in some real world token contracts, and due to a solidity
      * bug it was not compatible with multisig wallets
      */
      modifier onlyPayloadSize(uint size) {
      require(msg.data.length == size + 4);
      _;
      }

      /// @dev Transfers sender's tokens to a given address. Returns success.
      /// @param _to Address of token receiver.
      /// @param _value Number of tokens to transfer.
      /// @return Returns success of function call.
      function transfer(address _to, uint256 _value) onlyPayloadSize(2 * 32)
      public
      returns (bool success)
      {
      require(balances[msg.sender] >= _value);
      balances[msg.sender] -= _value;
      balances[_to] += _value;
      Transfer(msg.sender, _to, _value);
      return true;
      }


      The documentation of the onlyPayloadSize modifier is extremely unclear to me.



      Can somebody please explain the following:




      1. What exactly is the purpose of this modifier?

      2. Why is it used only in the trasnsfer function?

      3. Why is it used with a specific size of 64 (2 * 32)?

      4. Why is this modifier present only in a test contract, and not in the actual MultiSigWallet contract? Does this by any chance imply that I should add this modifier in every contract in my system, designated for access only via an instance of the MultiSigWallet contract? If I don't have any such contract with a standard transfer method (i.e., an ERC20 contract), can I rest assure that I don't need this modifier anywhere else in my system?


      Thank you!










      share|improve this question















      In a contract used for testing Gnosis MultiSigWallet, I found this piece of code:



      /*
      * This modifier is present in some real world token contracts, and due to a solidity
      * bug it was not compatible with multisig wallets
      */
      modifier onlyPayloadSize(uint size) {
      require(msg.data.length == size + 4);
      _;
      }

      /// @dev Transfers sender's tokens to a given address. Returns success.
      /// @param _to Address of token receiver.
      /// @param _value Number of tokens to transfer.
      /// @return Returns success of function call.
      function transfer(address _to, uint256 _value) onlyPayloadSize(2 * 32)
      public
      returns (bool success)
      {
      require(balances[msg.sender] >= _value);
      balances[msg.sender] -= _value;
      balances[_to] += _value;
      Transfer(msg.sender, _to, _value);
      return true;
      }


      The documentation of the onlyPayloadSize modifier is extremely unclear to me.



      Can somebody please explain the following:




      1. What exactly is the purpose of this modifier?

      2. Why is it used only in the trasnsfer function?

      3. Why is it used with a specific size of 64 (2 * 32)?

      4. Why is this modifier present only in a test contract, and not in the actual MultiSigWallet contract? Does this by any chance imply that I should add this modifier in every contract in my system, designated for access only via an instance of the MultiSigWallet contract? If I don't have any such contract with a standard transfer method (i.e., an ERC20 contract), can I rest assure that I don't need this modifier anywhere else in my system?


      Thank you!







      solidity






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 29 at 12:16

























      asked Nov 29 at 12:04









      goodvibration

      2,412721




      2,412721






















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          4
          down vote



          accepted










          Questions 1 is answered in this question. There is also much more details at Golem's blog here.




          1. The only reason it was only used in the transfer function is because the contract is for testing. In the wild, this contract would have the safeguard in the transferFrom and approve functions as well.


          2. The 2 * 32 is because that's how large the payload should be for that function. ABI encoding for function parameters has address and uint256 using 1 256 bit slot each, hence why they're calling the modifier with 2 * 32.


          3. This vulnerability is a protection for the person calling the contract against someone that gives them the data. If the person calling the function validates the data is the correct size before submitting it to the network, then they wont have any issues. The large libraries (Web3.js, Ethers.js, etc.) do all this for you when you encode data, e.g. if you use web3.eth.abi.encodeParameters(["address"], ["0x72abde8f"]), it'll throw an error because the address is too short.



          Edit: As Smarx has pointed out in the comments, there is a lot of discussion about specifically avoiding this type of fix, as it makes some things even more difficulty as outlined here. Gnosis specifically points this out in the code quoted by OP as well. In the end, my opinion is that protections such as onlyPayloadSize are much better suited to be done off-chain.






          share|improve this answer



















          • 1




            Note that as one of the answers on that question points out, the current guidance is to not use this type of check.
            – smarx
            Nov 29 at 14:46










          • And it is done off-chain so long as I use web3, right? Can someone attack my contract (the one protected with a MultiSigWallet instance), by passing corrupted data to it without using web3? I mean, the fact that I interact with the contract via web3 makes the onlyPayloadSize redundant. But can someone use the fact that I have omitted this modifier, and attack my contract by interracting with it directly (i.e., not via web3)?
            – goodvibration
            Nov 29 at 15:01










          • It's very hard to say without specific examples. You really have to think about what would happen if someone sent a short parameter to one of the functions.
            – flygoing
            Nov 29 at 15:04










          • So you're saying that the MultiSigWallet contract is not really secure by itself, and I would need to check payload size at the beginning of every function intended to be invoked via MultiSigWallet?
            – goodvibration
            Nov 29 at 15:07












          • It's not the MultiSigWallet's responsibility to validate the data being passed to your contract. It can't be insecure at something it has no knowledge, context, or responsibility about. Its responsibility is to only act on transactions that have been given proper authorization by whatever its x-of-x scheme, and it's likely pretty safe at doing that. You need to analyze your own contracts (or pay someone else to) outside the scope of the multisig to be sure they're safe. Note in the edit that many contracts (including Gnosis safe) can't call contracts with the onlyPayloadSize check anyway
            – flygoing
            Nov 29 at 15:13











          Your Answer








          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "642"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fethereum.stackexchange.com%2fquestions%2f63278%2fwhat-is-the-purpose-in-asserting-msg-data-length-64-4%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          4
          down vote



          accepted










          Questions 1 is answered in this question. There is also much more details at Golem's blog here.




          1. The only reason it was only used in the transfer function is because the contract is for testing. In the wild, this contract would have the safeguard in the transferFrom and approve functions as well.


          2. The 2 * 32 is because that's how large the payload should be for that function. ABI encoding for function parameters has address and uint256 using 1 256 bit slot each, hence why they're calling the modifier with 2 * 32.


          3. This vulnerability is a protection for the person calling the contract against someone that gives them the data. If the person calling the function validates the data is the correct size before submitting it to the network, then they wont have any issues. The large libraries (Web3.js, Ethers.js, etc.) do all this for you when you encode data, e.g. if you use web3.eth.abi.encodeParameters(["address"], ["0x72abde8f"]), it'll throw an error because the address is too short.



          Edit: As Smarx has pointed out in the comments, there is a lot of discussion about specifically avoiding this type of fix, as it makes some things even more difficulty as outlined here. Gnosis specifically points this out in the code quoted by OP as well. In the end, my opinion is that protections such as onlyPayloadSize are much better suited to be done off-chain.






          share|improve this answer



















          • 1




            Note that as one of the answers on that question points out, the current guidance is to not use this type of check.
            – smarx
            Nov 29 at 14:46










          • And it is done off-chain so long as I use web3, right? Can someone attack my contract (the one protected with a MultiSigWallet instance), by passing corrupted data to it without using web3? I mean, the fact that I interact with the contract via web3 makes the onlyPayloadSize redundant. But can someone use the fact that I have omitted this modifier, and attack my contract by interracting with it directly (i.e., not via web3)?
            – goodvibration
            Nov 29 at 15:01










          • It's very hard to say without specific examples. You really have to think about what would happen if someone sent a short parameter to one of the functions.
            – flygoing
            Nov 29 at 15:04










          • So you're saying that the MultiSigWallet contract is not really secure by itself, and I would need to check payload size at the beginning of every function intended to be invoked via MultiSigWallet?
            – goodvibration
            Nov 29 at 15:07












          • It's not the MultiSigWallet's responsibility to validate the data being passed to your contract. It can't be insecure at something it has no knowledge, context, or responsibility about. Its responsibility is to only act on transactions that have been given proper authorization by whatever its x-of-x scheme, and it's likely pretty safe at doing that. You need to analyze your own contracts (or pay someone else to) outside the scope of the multisig to be sure they're safe. Note in the edit that many contracts (including Gnosis safe) can't call contracts with the onlyPayloadSize check anyway
            – flygoing
            Nov 29 at 15:13















          up vote
          4
          down vote



          accepted










          Questions 1 is answered in this question. There is also much more details at Golem's blog here.




          1. The only reason it was only used in the transfer function is because the contract is for testing. In the wild, this contract would have the safeguard in the transferFrom and approve functions as well.


          2. The 2 * 32 is because that's how large the payload should be for that function. ABI encoding for function parameters has address and uint256 using 1 256 bit slot each, hence why they're calling the modifier with 2 * 32.


          3. This vulnerability is a protection for the person calling the contract against someone that gives them the data. If the person calling the function validates the data is the correct size before submitting it to the network, then they wont have any issues. The large libraries (Web3.js, Ethers.js, etc.) do all this for you when you encode data, e.g. if you use web3.eth.abi.encodeParameters(["address"], ["0x72abde8f"]), it'll throw an error because the address is too short.



          Edit: As Smarx has pointed out in the comments, there is a lot of discussion about specifically avoiding this type of fix, as it makes some things even more difficulty as outlined here. Gnosis specifically points this out in the code quoted by OP as well. In the end, my opinion is that protections such as onlyPayloadSize are much better suited to be done off-chain.






          share|improve this answer



















          • 1




            Note that as one of the answers on that question points out, the current guidance is to not use this type of check.
            – smarx
            Nov 29 at 14:46










          • And it is done off-chain so long as I use web3, right? Can someone attack my contract (the one protected with a MultiSigWallet instance), by passing corrupted data to it without using web3? I mean, the fact that I interact with the contract via web3 makes the onlyPayloadSize redundant. But can someone use the fact that I have omitted this modifier, and attack my contract by interracting with it directly (i.e., not via web3)?
            – goodvibration
            Nov 29 at 15:01










          • It's very hard to say without specific examples. You really have to think about what would happen if someone sent a short parameter to one of the functions.
            – flygoing
            Nov 29 at 15:04










          • So you're saying that the MultiSigWallet contract is not really secure by itself, and I would need to check payload size at the beginning of every function intended to be invoked via MultiSigWallet?
            – goodvibration
            Nov 29 at 15:07












          • It's not the MultiSigWallet's responsibility to validate the data being passed to your contract. It can't be insecure at something it has no knowledge, context, or responsibility about. Its responsibility is to only act on transactions that have been given proper authorization by whatever its x-of-x scheme, and it's likely pretty safe at doing that. You need to analyze your own contracts (or pay someone else to) outside the scope of the multisig to be sure they're safe. Note in the edit that many contracts (including Gnosis safe) can't call contracts with the onlyPayloadSize check anyway
            – flygoing
            Nov 29 at 15:13













          up vote
          4
          down vote



          accepted







          up vote
          4
          down vote



          accepted






          Questions 1 is answered in this question. There is also much more details at Golem's blog here.




          1. The only reason it was only used in the transfer function is because the contract is for testing. In the wild, this contract would have the safeguard in the transferFrom and approve functions as well.


          2. The 2 * 32 is because that's how large the payload should be for that function. ABI encoding for function parameters has address and uint256 using 1 256 bit slot each, hence why they're calling the modifier with 2 * 32.


          3. This vulnerability is a protection for the person calling the contract against someone that gives them the data. If the person calling the function validates the data is the correct size before submitting it to the network, then they wont have any issues. The large libraries (Web3.js, Ethers.js, etc.) do all this for you when you encode data, e.g. if you use web3.eth.abi.encodeParameters(["address"], ["0x72abde8f"]), it'll throw an error because the address is too short.



          Edit: As Smarx has pointed out in the comments, there is a lot of discussion about specifically avoiding this type of fix, as it makes some things even more difficulty as outlined here. Gnosis specifically points this out in the code quoted by OP as well. In the end, my opinion is that protections such as onlyPayloadSize are much better suited to be done off-chain.






          share|improve this answer














          Questions 1 is answered in this question. There is also much more details at Golem's blog here.




          1. The only reason it was only used in the transfer function is because the contract is for testing. In the wild, this contract would have the safeguard in the transferFrom and approve functions as well.


          2. The 2 * 32 is because that's how large the payload should be for that function. ABI encoding for function parameters has address and uint256 using 1 256 bit slot each, hence why they're calling the modifier with 2 * 32.


          3. This vulnerability is a protection for the person calling the contract against someone that gives them the data. If the person calling the function validates the data is the correct size before submitting it to the network, then they wont have any issues. The large libraries (Web3.js, Ethers.js, etc.) do all this for you when you encode data, e.g. if you use web3.eth.abi.encodeParameters(["address"], ["0x72abde8f"]), it'll throw an error because the address is too short.



          Edit: As Smarx has pointed out in the comments, there is a lot of discussion about specifically avoiding this type of fix, as it makes some things even more difficulty as outlined here. Gnosis specifically points this out in the code quoted by OP as well. In the end, my opinion is that protections such as onlyPayloadSize are much better suited to be done off-chain.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 29 at 14:56

























          answered Nov 29 at 13:52









          flygoing

          6,248830




          6,248830








          • 1




            Note that as one of the answers on that question points out, the current guidance is to not use this type of check.
            – smarx
            Nov 29 at 14:46










          • And it is done off-chain so long as I use web3, right? Can someone attack my contract (the one protected with a MultiSigWallet instance), by passing corrupted data to it without using web3? I mean, the fact that I interact with the contract via web3 makes the onlyPayloadSize redundant. But can someone use the fact that I have omitted this modifier, and attack my contract by interracting with it directly (i.e., not via web3)?
            – goodvibration
            Nov 29 at 15:01










          • It's very hard to say without specific examples. You really have to think about what would happen if someone sent a short parameter to one of the functions.
            – flygoing
            Nov 29 at 15:04










          • So you're saying that the MultiSigWallet contract is not really secure by itself, and I would need to check payload size at the beginning of every function intended to be invoked via MultiSigWallet?
            – goodvibration
            Nov 29 at 15:07












          • It's not the MultiSigWallet's responsibility to validate the data being passed to your contract. It can't be insecure at something it has no knowledge, context, or responsibility about. Its responsibility is to only act on transactions that have been given proper authorization by whatever its x-of-x scheme, and it's likely pretty safe at doing that. You need to analyze your own contracts (or pay someone else to) outside the scope of the multisig to be sure they're safe. Note in the edit that many contracts (including Gnosis safe) can't call contracts with the onlyPayloadSize check anyway
            – flygoing
            Nov 29 at 15:13














          • 1




            Note that as one of the answers on that question points out, the current guidance is to not use this type of check.
            – smarx
            Nov 29 at 14:46










          • And it is done off-chain so long as I use web3, right? Can someone attack my contract (the one protected with a MultiSigWallet instance), by passing corrupted data to it without using web3? I mean, the fact that I interact with the contract via web3 makes the onlyPayloadSize redundant. But can someone use the fact that I have omitted this modifier, and attack my contract by interracting with it directly (i.e., not via web3)?
            – goodvibration
            Nov 29 at 15:01










          • It's very hard to say without specific examples. You really have to think about what would happen if someone sent a short parameter to one of the functions.
            – flygoing
            Nov 29 at 15:04










          • So you're saying that the MultiSigWallet contract is not really secure by itself, and I would need to check payload size at the beginning of every function intended to be invoked via MultiSigWallet?
            – goodvibration
            Nov 29 at 15:07












          • It's not the MultiSigWallet's responsibility to validate the data being passed to your contract. It can't be insecure at something it has no knowledge, context, or responsibility about. Its responsibility is to only act on transactions that have been given proper authorization by whatever its x-of-x scheme, and it's likely pretty safe at doing that. You need to analyze your own contracts (or pay someone else to) outside the scope of the multisig to be sure they're safe. Note in the edit that many contracts (including Gnosis safe) can't call contracts with the onlyPayloadSize check anyway
            – flygoing
            Nov 29 at 15:13








          1




          1




          Note that as one of the answers on that question points out, the current guidance is to not use this type of check.
          – smarx
          Nov 29 at 14:46




          Note that as one of the answers on that question points out, the current guidance is to not use this type of check.
          – smarx
          Nov 29 at 14:46












          And it is done off-chain so long as I use web3, right? Can someone attack my contract (the one protected with a MultiSigWallet instance), by passing corrupted data to it without using web3? I mean, the fact that I interact with the contract via web3 makes the onlyPayloadSize redundant. But can someone use the fact that I have omitted this modifier, and attack my contract by interracting with it directly (i.e., not via web3)?
          – goodvibration
          Nov 29 at 15:01




          And it is done off-chain so long as I use web3, right? Can someone attack my contract (the one protected with a MultiSigWallet instance), by passing corrupted data to it without using web3? I mean, the fact that I interact with the contract via web3 makes the onlyPayloadSize redundant. But can someone use the fact that I have omitted this modifier, and attack my contract by interracting with it directly (i.e., not via web3)?
          – goodvibration
          Nov 29 at 15:01












          It's very hard to say without specific examples. You really have to think about what would happen if someone sent a short parameter to one of the functions.
          – flygoing
          Nov 29 at 15:04




          It's very hard to say without specific examples. You really have to think about what would happen if someone sent a short parameter to one of the functions.
          – flygoing
          Nov 29 at 15:04












          So you're saying that the MultiSigWallet contract is not really secure by itself, and I would need to check payload size at the beginning of every function intended to be invoked via MultiSigWallet?
          – goodvibration
          Nov 29 at 15:07






          So you're saying that the MultiSigWallet contract is not really secure by itself, and I would need to check payload size at the beginning of every function intended to be invoked via MultiSigWallet?
          – goodvibration
          Nov 29 at 15:07














          It's not the MultiSigWallet's responsibility to validate the data being passed to your contract. It can't be insecure at something it has no knowledge, context, or responsibility about. Its responsibility is to only act on transactions that have been given proper authorization by whatever its x-of-x scheme, and it's likely pretty safe at doing that. You need to analyze your own contracts (or pay someone else to) outside the scope of the multisig to be sure they're safe. Note in the edit that many contracts (including Gnosis safe) can't call contracts with the onlyPayloadSize check anyway
          – flygoing
          Nov 29 at 15:13




          It's not the MultiSigWallet's responsibility to validate the data being passed to your contract. It can't be insecure at something it has no knowledge, context, or responsibility about. Its responsibility is to only act on transactions that have been given proper authorization by whatever its x-of-x scheme, and it's likely pretty safe at doing that. You need to analyze your own contracts (or pay someone else to) outside the scope of the multisig to be sure they're safe. Note in the edit that many contracts (including Gnosis safe) can't call contracts with the onlyPayloadSize check anyway
          – flygoing
          Nov 29 at 15:13


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Ethereum Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fethereum.stackexchange.com%2fquestions%2f63278%2fwhat-is-the-purpose-in-asserting-msg-data-length-64-4%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          How to change which sound is reproduced for terminal bell?

          Title Spacing in Bjornstrup Chapter, Removing Chapter Number From Contents

          Can I use Tabulator js library in my java Spring + Thymeleaf project?