Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for arrays #13

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions src/Domain.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ EIP712Domain.fromSignatureRequest = function fromSignatureRequest(request) {
const dfs = type => {
for (const {type: subtype} of types[type]) {
if (marked.has(subtype) || isNotStructureType(subtype)) continue
if (isArrayType(subtype)){
subtype = subtype.substring(0,subtype.indexOf("["))
}
if (cyclecheck.has(subtype)) {
throw new Error('Cannot construct domain from signature request with cyclic dependencies')
}
Expand Down
52 changes: 33 additions & 19 deletions src/Type.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,24 @@ export default function Type (primaryType, defs) {
}
}

encodeField(type, value){
if (isDynamicType(type)){
return {type:'bytes32', value:Buffer.from(keccak256(value), 'hex')}
} else if (type in domain.types){
// Structure Types are recursively encoded and hashed
return {type:"bytes32", value: Buffer.from(keccak256(value.encodeData()), 'hex')}
} else if (isArrayType(type)){
// Array types are the hash of their encoded members concatenated
var innerType = type.slice(0, type.lastIndexOf('['));
var encodedValues = value.map(item => this.encodeField(innerType, item));
return {type:'bytes32', value: Buffer.from(keccak256(abi.rawEncode(encodedValues.map(v=>v.type), encodedValues.map(v=>v.value))), 'hex')}
} else if (isAtomicType(type)){
return {type: type, value:value}
} else {
throw new Error(`Unknown type: ${type}`)
}
}

/**
* @override
* Return the EIP712 data encoding of this instance, padding each member
Expand All @@ -135,24 +153,9 @@ export default function Type (primaryType, defs) {
let values = [Buffer.from(this.constructor.typeHash(), 'hex')]

for (const {type, name} of this.constructor.properties) {
if (isDynamicType(type)) {
// Dynamic types are hashed
types.push('bytes32')
values.push(Buffer.from(keccak256(this[name]), 'hex'))
} else if (type in domain.types) {
// Structure Types are recursively encoded and hashed
types.push('bytes32')
values.push(Buffer.from(keccak256(this[name].encodeData()), 'hex'))
} else if (isArrayType(type)) {
// TODO: Figure out the spec for encoding array types
throw new Error('[DEV] Array types not yet supported')
} else if (isAtomicType(type)) {
// Atomic types have their encoding defined by the solidity ABI
types.push(type)
values.push(this[name])
} else {
throw new Error(`Unknown type: ${type}`)
}
var {type:t,value:v} = this.encodeField(type, this[name])
types.push(t)
values.push(v)
}

return abi.rawEncode(types, values)
Expand Down Expand Up @@ -227,6 +230,11 @@ function validateTypeDefinition({name, type}, domain) {
if (typeof type === 'object') {
// TODO: Allow recursive type defintions?
throw new Error('Nested type definitions not supported')
} else if (isArrayType(type)){
var primitiveType = type.substring(0, type.lastIndexOf("["));
if (!primitiveType in domain.types){
throw new Error(`Type ${primitiveType} is undefined in this domain`)
}
} else if (!isPrimitiveType(type) && !(type in domain.types)) {
// Refuse undefined, non-primitive types
throw new Error(`Type ${type} is undefined in this domain`)
Expand All @@ -246,9 +254,15 @@ function validateTypeDefinition({name, type}, domain) {
function findDependencies(props, domain, found=[]) {
for (let {type} of props) {
if (isPrimitiveType(type)) continue
var dependentTypeName = type
if (isArrayType(type)){
dependentTypeName = type.substring(0, type.lastIndexOf("["));
if (isPrimitiveType(dependentTypeName)) continue
}

// Merge the found array with new dependencies of
found = found.concat(
[type, ...findDependencies(domain.types[type].properties, domain, found)]
[dependentTypeName, ...findDependencies(domain.types[dependentTypeName].properties, domain, found)]
.filter(t => !found.includes(t))
)
}
Expand Down
16 changes: 8 additions & 8 deletions src/__tests__/data/Mail.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
],
"Mail": [
{ "name": "from", "type": "Person" },
{ "name": "to", "type": "Person" },
{ "name": "to", "type": "Person[]" },
{ "name": "contents", "type": "string" }
]
},
Expand All @@ -29,20 +29,20 @@
"name": "Cow",
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
},
"to": {
"to": [{
"name": "Bob",
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
},
}],
"contents": "Hello, Bob!"
}
},
"results": {
"Mail": {
"encodeType": "Mail(Person from,Person to,string contents)Person(string name,address wallet)",
"typeHash": "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2",
"encodeData": "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8cd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8",
"hashStruct": "0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e",
"signHash": "0xbe609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2"
"encodeType": "Mail(Person from,Person[] to,string contents)Person(string name,address wallet)",
"typeHash": "0xdd57d9596af52b430ced3d5b52d4e3d5dccfdf3e0572db1dcf526baad311fbd1",
"encodeData": "0xdd57d9596af52b430ced3d5b52d4e3d5dccfdf3e0572db1dcf526baad311fbd1fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8e2ec7c01837071382eadbfdffa6c4c47de741fa0fda906874da49c4192dbe6edb5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8",
"hashStruct": "0xb737d8ce302eba53138b4a278391f7174db4fe0efb501e813572ee339d5e7751",
"signHash": "0xe3e147d255f92f5a9de155defefb2816b1ca970e975b38baadb0cca27e733d36"
},
"domainSeparator": "0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f"
}
Expand Down