1

I was trying to build a nested map of structs in SmartContract. With a single level, after inserting data(insertData) the validate method is returning correct results with the below code containing few commented lines. But when i uncomment the childMap related things -the childMap in the ParentStruct, the parameters in insertData method and initialising the childMap then its returning wrong result. Please find the smart contract code below.

pragma solidity ^0.4.13;

contract SampleContract {
struct ChildStruct {
    bool isPresent;
    bytes32 name;
}

struct ParentStruct {
    bool isPresent;
    bytes32 name;
    //mapping (bytes32 => ChildStruct) childMap; 
}

mapping(bytes32 => ParentStruct) sampleStructs;

function insertData(bytes32 parentAddress, bytes32 parentName
                       //,bytes32 childAddress, bytes32 childName
                       )
public returns(bool success)
{
    ParentStruct storage c = sampleStructs[parentAddress];
    c.isPresent = true;
    c.name = parentName;
    //c.childMap[childAddress] = ChildStruct(true, childName);
    return true;
}

function validate(bytes32 parentAddress)
public returns(bool isPresent, string name) 
{
    return 
    (sampleStructs[parentAddress].isPresent,
    bytes32ToString(sampleStructs[parentAddress].name)
    );
}

function bytes32ToString(bytes32 x) constant returns (string) 
{
    bytes memory bytesString = new bytes(32);
    uint charCount = 0;
    for (uint j = 0; j < 32; j++) {
        byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
        if (char != 0) {
            bytesString[charCount] = char;
            charCount++;
        }
    }
    bytes memory bytesStringTrimmed = new bytes(charCount);
    for (j = 0; j < charCount; j++) {
        bytesStringTrimmed[j] = bytesString[j];
    }
    return string(bytesStringTrimmed);
}
}

Could you please help me if i'm missing anything ?

2 Answers 2

1

I played around with your code and came up with this:

pragma solidity ^0.4.11;

contract SampleContract {

  struct ChildStruct {
    bool isPresent;
    bytes32 name;
  }

  struct ParentStruct {
    bool isPresent;
    bytes32 name;
    mapping (bytes32 => ChildStruct) childStructs; 
  }

  mapping(bytes32 => ParentStruct) public parentStructs;

  function insertData(
    bytes32 parentKey, 
    bytes32 parentName, 
    bytes32 childKey, 
    bytes32 childName)
    public 
    returns(bool success)
  {

    parentStructs[parentKey].isPresent = true;
    parentStructs[parentKey].name = parentName;
    parentStructs[parentKey].childStructs[childKey].isPresent = true;
    parentStructs[parentKey].childStructs[childKey].name = childName;
    return true;
  }

  function getChild(bytes32 parentKey, bytes32 childKey) public constant returns(bool isPresent, bytes32 name) {
    return (parentStructs[parentKey].childStructs[childKey].isPresent, parentStructs[parentKey].childStructs[childKey].name);
  }

}

There's a hidden assumpting in this simple solution - the callers will have to know the keys for parent and child because the contract offers no way to enumerate them.

I removed the string conversion because clients can look after that. Also renamed "addresses" to "keys" because the former makes it sound like wallet/contract addresses. I assumed they are something else because they're not cast as address.

It looks like you're aiming for something like this. This pattern lets you do a lot with a parent-child join (one-to-many relationship). https://medium.com/@robhitchens/enforcing-referential-integrity-in-ethereum-smart-contracts-a9ab1427ff42

Hope it helps.

EDIT:

The contract seems to work, so I would concentate my efforts on the client side. I'm unsure where sampleContract come from. It seems strange to send a transaction to one contract and then ask a different contract for the result.

The functions expect hex, not strings. Also, be sure to use .then (is this Truffle?) to wait for response. BTW, the txn hash result but be encouraging but misleading. It doesn't mean it was mined or it was successful.

Something in there might help.

While I was double-checking the contract, I removed getParent(). It wasn't doing any more than the free getter we got from making the mapping public. You can use parentStructs('key') in the same way.

Here it is in Remix to show it working.

enter image description here

3
  • Rob, i have tried the code you suggested, deployed smart contract and called the methods.Tested whether the insertData is mined and called the getParent method which gave me null response. >sampleContract.insertData('parent1','parentDetails1','child‌​1','childDetails1') Response: '0xa719c11d0aefc6ed7c4c4ae2977dda5a348dafd54e0a1a5b315435b25‌​7a67a51' > sampleContract.getParent.call('parent1') Response: [ false, '0x000000000000000000000000000000000000000000000000000000000‌​0000000' ] Commented Jul 18, 2017 at 7:36
  • Rob, I have deployed a geth network and compiled the code in solidity online compiler. The output which i got from the solidity compiler is taken and deployed from a nodejs which gave me the smart contract address. smartContract = SmartContract.at(<address>) and with the help of smartContract variable i tried to use the methods. I have ensured the mined status by seeing the geth node after calling the insertData method. I am not using any framework. Should i use any frameworks like truffle to avoid this kind of responses. Commented Jul 18, 2017 at 7:45
  • Thanks Rob. Thanks for the screenshot. Its working fine with Remix. I think there is some problem with the way i deploy and call the contract from nodejs. Commented Jul 18, 2017 at 20:31
0

The problem is with the Web3 js version. I upgraded to the latest 0.20.1 and called the smart contract methods which resolved the issue.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.