Merge branch 'master' into CLO/1.0

This commit is contained in:
Yohan Graterol 2018-03-07 23:28:18 -05:00
commit 6d4cb5e8a4
93 changed files with 3917 additions and 710 deletions

2
.github/CODEOWNERS vendored
View file

@ -5,5 +5,7 @@ accounts/usbwallet @karalabe
consensus @karalabe consensus @karalabe
core/ @karalabe @holiman core/ @karalabe @holiman
eth/ @karalabe eth/ @karalabe
les/ @zsfelfoldi
light/ @zsfelfoldi
mobile/ @karalabe mobile/ @karalabe
p2p/ @fjl @zsfelfoldi p2p/ @fjl @zsfelfoldi

17
.github/stale.yml vendored Normal file
View file

@ -0,0 +1,17 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 366
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 42
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

3
.gitignore vendored
View file

@ -34,6 +34,9 @@ profile.cov
# IdeaIDE # IdeaIDE
.idea .idea
# VS Code
.vscode
# dashboard # dashboard
/dashboard/assets/flow-typed /dashboard/assets/flow-typed
/dashboard/assets/node_modules /dashboard/assets/node_modules

View file

@ -3,17 +3,6 @@ go_import_path: github.com/EthereumCommonwealth/go-callisto
sudo: false sudo: false
matrix: matrix:
include: include:
- os: linux
dist: trusty
sudo: required
go: 1.7.x
script:
- sudo modprobe fuse
- sudo chmod 666 /dev/fuse
- sudo chown root:$USER /etc/fuse.conf
- go run build/ci.go install
- go run build/ci.go test -coverage
- os: linux - os: linux
dist: trusty dist: trusty
sudo: required sudo: required

View file

@ -169,6 +169,21 @@ func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues []interf
return set(elem, reflectValue, arguments.NonIndexed()[0]) return set(elem, reflectValue, arguments.NonIndexed()[0])
} }
// Computes the full size of an array;
// i.e. counting nested arrays, which count towards size for unpacking.
func getArraySize(arr *Type) int {
size := arr.Size
// Arrays can be nested, with each element being the same size
arr = arr.Elem
for arr.T == ArrayTy {
// Keep multiplying by elem.Size while the elem is an array.
size *= arr.Size
arr = arr.Elem
}
// Now we have the full array size, including its children.
return size
}
// UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification, // UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
// without supplying a struct to unpack into. Instead, this method returns a list containing the // without supplying a struct to unpack into. Instead, this method returns a list containing the
// values. An atomic argument will be a list with one element. // values. An atomic argument will be a list with one element.
@ -181,9 +196,14 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
// If we have a static array, like [3]uint256, these are coded as // If we have a static array, like [3]uint256, these are coded as
// just like uint256,uint256,uint256. // just like uint256,uint256,uint256.
// This means that we need to add two 'virtual' arguments when // This means that we need to add two 'virtual' arguments when
// we count the index from now on // we count the index from now on.
//
virtualArgs += arg.Type.Size - 1 // Array values nested multiple levels deep are also encoded inline:
// [2][3]uint256: uint256,uint256,uint256,uint256,uint256,uint256
//
// Calculate the full array size to get the correct offset for the next argument.
// Decrement it by 1, as the normal index increment is still applied.
virtualArgs += getArraySize(&arg.Type) - 1
} }
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -164,118 +164,147 @@ var bindType = map[Lang]func(kind abi.Type) string{
LangJava: bindTypeJava, LangJava: bindTypeJava,
} }
// Helper function for the binding generators.
// It reads the unmatched characters after the inner type-match,
// (since the inner type is a prefix of the total type declaration),
// looks for valid arrays (possibly a dynamic one) wrapping the inner type,
// and returns the sizes of these arrays.
//
// Returned array sizes are in the same order as solidity signatures; inner array size first.
// Array sizes may also be "", indicating a dynamic array.
func wrapArray(stringKind string, innerLen int, innerMapping string) (string, []string) {
remainder := stringKind[innerLen:]
//find all the sizes
matches := regexp.MustCompile(`\[(\d*)\]`).FindAllStringSubmatch(remainder, -1)
parts := make([]string, 0, len(matches))
for _, match := range matches {
//get group 1 from the regex match
parts = append(parts, match[1])
}
return innerMapping, parts
}
// Translates the array sizes to a Go-lang declaration of a (nested) array of the inner type.
// Simply returns the inner type if arraySizes is empty.
func arrayBindingGo(inner string, arraySizes []string) string {
out := ""
//prepend all array sizes, from outer (end arraySizes) to inner (start arraySizes)
for i := len(arraySizes) - 1; i >= 0; i-- {
out += "[" + arraySizes[i] + "]"
}
out += inner
return out
}
// bindTypeGo converts a Solidity type to a Go one. Since there is no clear mapping // bindTypeGo converts a Solidity type to a Go one. Since there is no clear mapping
// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly // from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
// mapped will use an upscaled type (e.g. *big.Int). // mapped will use an upscaled type (e.g. *big.Int).
func bindTypeGo(kind abi.Type) string { func bindTypeGo(kind abi.Type) string {
stringKind := kind.String() stringKind := kind.String()
innerLen, innerMapping := bindUnnestedTypeGo(stringKind)
return arrayBindingGo(wrapArray(stringKind, innerLen, innerMapping))
}
// The inner function of bindTypeGo, this finds the inner type of stringKind.
// (Or just the type itself if it is not an array or slice)
// The length of the matched part is returned, with the the translated type.
func bindUnnestedTypeGo(stringKind string) (int, string) {
switch { switch {
case strings.HasPrefix(stringKind, "address"): case strings.HasPrefix(stringKind, "address"):
parts := regexp.MustCompile(`address(\[[0-9]*\])?`).FindStringSubmatch(stringKind) return len("address"), "common.Address"
if len(parts) != 2 {
return stringKind
}
return fmt.Sprintf("%scommon.Address", parts[1])
case strings.HasPrefix(stringKind, "bytes"): case strings.HasPrefix(stringKind, "bytes"):
parts := regexp.MustCompile(`bytes([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind) parts := regexp.MustCompile(`bytes([0-9]*)`).FindStringSubmatch(stringKind)
if len(parts) != 3 { return len(parts[0]), fmt.Sprintf("[%s]byte", parts[1])
return stringKind
}
return fmt.Sprintf("%s[%s]byte", parts[2], parts[1])
case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"): case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind) parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(stringKind)
if len(parts) != 4 {
return stringKind
}
switch parts[2] { switch parts[2] {
case "8", "16", "32", "64": case "8", "16", "32", "64":
return fmt.Sprintf("%s%sint%s", parts[3], parts[1], parts[2]) return len(parts[0]), fmt.Sprintf("%sint%s", parts[1], parts[2])
} }
return fmt.Sprintf("%s*big.Int", parts[3]) return len(parts[0]), "*big.Int"
case strings.HasPrefix(stringKind, "bool") || strings.HasPrefix(stringKind, "string"): case strings.HasPrefix(stringKind, "bool"):
parts := regexp.MustCompile(`([a-z]+)(\[[0-9]*\])?`).FindStringSubmatch(stringKind) return len("bool"), "bool"
if len(parts) != 3 {
return stringKind case strings.HasPrefix(stringKind, "string"):
} return len("string"), "string"
return fmt.Sprintf("%s%s", parts[2], parts[1])
default: default:
return stringKind return len(stringKind), stringKind
} }
} }
// Translates the array sizes to a Java declaration of a (nested) array of the inner type.
// Simply returns the inner type if arraySizes is empty.
func arrayBindingJava(inner string, arraySizes []string) string {
// Java array type declarations do not include the length.
return inner + strings.Repeat("[]", len(arraySizes))
}
// bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping // bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping
// from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly // from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly
// mapped will use an upscaled type (e.g. BigDecimal). // mapped will use an upscaled type (e.g. BigDecimal).
func bindTypeJava(kind abi.Type) string { func bindTypeJava(kind abi.Type) string {
stringKind := kind.String() stringKind := kind.String()
innerLen, innerMapping := bindUnnestedTypeJava(stringKind)
return arrayBindingJava(wrapArray(stringKind, innerLen, innerMapping))
}
// The inner function of bindTypeJava, this finds the inner type of stringKind.
// (Or just the type itself if it is not an array or slice)
// The length of the matched part is returned, with the the translated type.
func bindUnnestedTypeJava(stringKind string) (int, string) {
switch { switch {
case strings.HasPrefix(stringKind, "address"): case strings.HasPrefix(stringKind, "address"):
parts := regexp.MustCompile(`address(\[[0-9]*\])?`).FindStringSubmatch(stringKind) parts := regexp.MustCompile(`address(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 2 { if len(parts) != 2 {
return stringKind return len(stringKind), stringKind
} }
if parts[1] == "" { if parts[1] == "" {
return fmt.Sprintf("Address") return len("address"), "Address"
} }
return fmt.Sprintf("Addresses") return len(parts[0]), "Addresses"
case strings.HasPrefix(stringKind, "bytes"): case strings.HasPrefix(stringKind, "bytes"):
parts := regexp.MustCompile(`bytes([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind) parts := regexp.MustCompile(`bytes([0-9]*)`).FindStringSubmatch(stringKind)
if len(parts) != 3 { if len(parts) != 2 {
return stringKind return len(stringKind), stringKind
} }
if parts[2] != "" { return len(parts[0]), "byte[]"
return "byte[][]"
}
return "byte[]"
case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"): case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind) //Note that uint and int (without digits) are also matched,
if len(parts) != 4 { // these are size 256, and will translate to BigInt (the default).
return stringKind parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(stringKind)
if len(parts) != 3 {
return len(stringKind), stringKind
} }
switch parts[2] {
case "8", "16", "32", "64": namedSize := map[string]string{
if parts[1] == "" { "8": "byte",
if parts[3] == "" { "16": "short",
return fmt.Sprintf("int%s", parts[2]) "32": "int",
"64": "long",
}[parts[2]]
//default to BigInt
if namedSize == "" {
namedSize = "BigInt"
} }
return fmt.Sprintf("int%s[]", parts[2]) return len(parts[0]), namedSize
}
}
if parts[3] == "" {
return fmt.Sprintf("BigInt")
}
return fmt.Sprintf("BigInts")
case strings.HasPrefix(stringKind, "bool"): case strings.HasPrefix(stringKind, "bool"):
parts := regexp.MustCompile(`bool(\[[0-9]*\])?`).FindStringSubmatch(stringKind) return len("bool"), "boolean"
if len(parts) != 2 {
return stringKind
}
if parts[1] == "" {
return fmt.Sprintf("bool")
}
return fmt.Sprintf("bool[]")
case strings.HasPrefix(stringKind, "string"): case strings.HasPrefix(stringKind, "string"):
parts := regexp.MustCompile(`string(\[[0-9]*\])?`).FindStringSubmatch(stringKind) return len("string"), "String"
if len(parts) != 2 {
return stringKind
}
if parts[1] == "" {
return fmt.Sprintf("String")
}
return fmt.Sprintf("String[]")
default: default:
return stringKind return len(stringKind), stringKind
} }
} }
@ -325,11 +354,13 @@ func namedTypeJava(javaKind string, solKind abi.Type) string {
return "String" return "String"
case "string[]": case "string[]":
return "Strings" return "Strings"
case "bool": case "boolean":
return "Bool" return "Bool"
case "bool[]": case "boolean[]":
return "Bools" return "Bools"
case "BigInt": case "BigInt[]":
return "BigInts"
default:
parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String()) parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String())
if len(parts) != 4 { if len(parts) != 4 {
return javaKind return javaKind
@ -344,8 +375,6 @@ func namedTypeJava(javaKind string, solKind abi.Type) string {
default: default:
return javaKind return javaKind
} }
default:
return javaKind
} }
} }

View file

@ -737,6 +737,72 @@ var bindTests = []struct {
} }
`, `,
}, },
{
`DeeplyNestedArray`,
`
contract DeeplyNestedArray {
uint64[3][4][5] public deepUint64Array;
function storeDeepUintArray(uint64[3][4][5] arr) public {
deepUint64Array = arr;
}
function retrieveDeepArray() public view returns (uint64[3][4][5]) {
return deepUint64Array;
}
}
`,
`6060604052341561000f57600080fd5b6106438061001e6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063344248551461005c5780638ed4573a1461011457806398ed1856146101ab575b600080fd5b341561006757600080fd5b610112600480806107800190600580602002604051908101604052809291906000905b828210156101055783826101800201600480602002604051908101604052809291906000905b828210156100f25783826060020160038060200260405190810160405280929190826003602002808284378201915050505050815260200190600101906100b0565b505050508152602001906001019061008a565b5050505091905050610208565b005b341561011f57600080fd5b61012761021d565b604051808260056000925b8184101561019b578284602002015160046000925b8184101561018d5782846020020151600360200280838360005b8381101561017c578082015181840152602081019050610161565b505050509050019260010192610147565b925050509260010192610132565b9250505091505060405180910390f35b34156101b657600080fd5b6101de6004808035906020019091908035906020019091908035906020019091905050610309565b604051808267ffffffffffffffff1667ffffffffffffffff16815260200191505060405180910390f35b80600090600561021992919061035f565b5050565b6102256103b0565b6000600580602002604051908101604052809291906000905b8282101561030057838260040201600480602002604051908101604052809291906000905b828210156102ed578382016003806020026040519081016040528092919082600380156102d9576020028201916000905b82829054906101000a900467ffffffffffffffff1667ffffffffffffffff16815260200190600801906020826007010492830192600103820291508084116102945790505b505050505081526020019060010190610263565b505050508152602001906001019061023e565b50505050905090565b60008360058110151561031857fe5b600402018260048110151561032957fe5b018160038110151561033757fe5b6004918282040191900660080292509250509054906101000a900467ffffffffffffffff1681565b826005600402810192821561039f579160200282015b8281111561039e5782518290600461038e9291906103df565b5091602001919060040190610375565b5b5090506103ac919061042d565b5090565b610780604051908101604052806005905b6103c9610459565b8152602001906001900390816103c15790505090565b826004810192821561041c579160200282015b8281111561041b5782518290600361040b929190610488565b50916020019190600101906103f2565b5b5090506104299190610536565b5090565b61045691905b8082111561045257600081816104499190610562565b50600401610433565b5090565b90565b610180604051908101604052806004905b6104726105a7565b81526020019060019003908161046a5790505090565b82600380016004900481019282156105255791602002820160005b838211156104ef57835183826101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555092602001926008016020816007010492830192600103026104a3565b80156105235782816101000a81549067ffffffffffffffff02191690556008016020816007010492830192600103026104ef565b505b50905061053291906105d9565b5090565b61055f91905b8082111561055b57600081816105529190610610565b5060010161053c565b5090565b90565b50600081816105719190610610565b50600101600081816105839190610610565b50600101600081816105959190610610565b5060010160006105a59190610610565b565b6060604051908101604052806003905b600067ffffffffffffffff168152602001906001900390816105b75790505090565b61060d91905b8082111561060957600081816101000a81549067ffffffffffffffff0219169055506001016105df565b5090565b90565b50600090555600a165627a7a7230582087e5a43f6965ab6ef7a4ff056ab80ed78fd8c15cff57715a1bf34ec76a93661c0029`,
`[{"constant":false,"inputs":[{"name":"arr","type":"uint64[3][4][5]"}],"name":"storeDeepUintArray","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"retrieveDeepArray","outputs":[{"name":"","type":"uint64[3][4][5]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"name":"deepUint64Array","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"}]`,
`
// Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey()
auth := bind.NewKeyedTransactor(key)
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
//deploy the test contract
_, _, testContract, err := DeployDeeplyNestedArray(auth, sim)
if err != nil {
t.Fatalf("Failed to deploy test contract: %v", err)
}
// Finish deploy.
sim.Commit()
//Create coordinate-filled array, for testing purposes.
testArr := [5][4][3]uint64{}
for i := 0; i < 5; i++ {
testArr[i] = [4][3]uint64{}
for j := 0; j < 4; j++ {
testArr[i][j] = [3]uint64{}
for k := 0; k < 3; k++ {
//pack the coordinates, each array value will be unique, and can be validated easily.
testArr[i][j][k] = uint64(i) << 16 | uint64(j) << 8 | uint64(k)
}
}
}
if _, err := testContract.StoreDeepUintArray(&bind.TransactOpts{
From: auth.From,
Signer: auth.Signer,
}, testArr); err != nil {
t.Fatalf("Failed to store nested array in test contract: %v", err)
}
sim.Commit()
retrievedArr, err := testContract.RetrieveDeepArray(&bind.CallOpts{
From: auth.From,
Pending: false,
})
if err != nil {
t.Fatalf("Failed to retrieve nested array from test contract: %v", err)
}
//quick check to see if contents were copied
// (See accounts/abi/unpack_test.go for more extensive testing)
if retrievedArr[4][3][2] != testArr[4][3][2] {
t.Fatalf("Retrieved value does not match expected value! got: %d, expected: %d. %v", retrievedArr[4][3][2], testArr[4][3][2], err)
}`,
},
} }
// Tests that packages generated by the binder can be successfully compiled and // Tests that packages generated by the binder can be successfully compiled and

View file

@ -299,6 +299,11 @@ func TestPack(t *testing.T) {
[32]byte{1}, [32]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{
"uint32[2][3][4]",
[4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018"),
},
{ {
"address[]", "address[]",
[]common.Address{{1}, {2}}, []common.Address{{1}, {2}},

View file

@ -93,6 +93,17 @@ func readFixedBytes(t Type, word []byte) (interface{}, error) {
} }
func getFullElemSize(elem *Type) int {
//all other should be counted as 32 (slices have pointers to respective elements)
size := 32
//arrays wrap it, each element being the same size
for elem.T == ArrayTy {
size *= elem.Size
elem = elem.Elem
}
return size
}
// iteratively unpack elements // iteratively unpack elements
func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) { func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
if size < 0 { if size < 0 {
@ -104,7 +115,6 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
// this value will become our slice or our array, depending on the type // this value will become our slice or our array, depending on the type
var refSlice reflect.Value var refSlice reflect.Value
slice := output[start : start+size*32]
if t.T == SliceTy { if t.T == SliceTy {
// declare our slice // declare our slice
@ -116,15 +126,20 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage") return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage")
} }
for i, j := start, 0; j*32 < len(slice); i, j = i+32, j+1 { // Arrays have packed elements, resulting in longer unpack steps.
// this corrects the arrangement so that we get all the underlying array values // Slices have just 32 bytes per element (pointing to the contents).
if t.Elem.T == ArrayTy && j != 0 { elemSize := 32
i = start + t.Elem.Size*32*j if t.T == ArrayTy {
elemSize = getFullElemSize(t.Elem)
} }
for i, j := start, 0; j < size; i, j = i+elemSize, j+1 {
inter, err := toGoType(i, *t.Elem, output) inter, err := toGoType(i, *t.Elem, output)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// append the item to our reflect slice // append the item to our reflect slice
refSlice.Index(j).Set(reflect.ValueOf(inter)) refSlice.Index(j).Set(reflect.ValueOf(inter))
} }

View file

@ -189,6 +189,11 @@ var unpackTests = []unpackTest{
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: [2]uint32{1, 2}, want: [2]uint32{1, 2},
}, },
{
def: `[{"type": "uint32[2][3][4]"}]`,
enc: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018",
want: [4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}},
},
{ {
def: `[{"type": "uint64[]"}]`, def: `[{"type": "uint64[]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
@ -435,6 +440,46 @@ func TestMultiReturnWithArray(t *testing.T) {
} }
} }
func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
// Similar to TestMultiReturnWithArray, but with a special case in mind:
// values of nested static arrays count towards the size as well, and any element following
// after such nested array argument should be read with the correct offset,
// so that it does not read content from the previous array argument.
const definition = `[{"name" : "multi", "outputs": [{"type": "uint64[3][2][4]"}, {"type": "uint64"}]}]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
buff := new(bytes.Buffer)
// construct the test array, each 3 char element is joined with 61 '0' chars,
// to from the ((3 + 61) * 0.5) = 32 byte elements in the array.
buff.Write(common.Hex2Bytes(strings.Join([]string{
"", //empty, to apply the 61-char separator to the first element as well.
"111", "112", "113", "121", "122", "123",
"211", "212", "213", "221", "222", "223",
"311", "312", "313", "321", "322", "323",
"411", "412", "413", "421", "422", "423",
}, "0000000000000000000000000000000000000000000000000000000000000")))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000009876"))
ret1, ret1Exp := new([4][2][3]uint64), [4][2][3]uint64{
{{0x111, 0x112, 0x113}, {0x121, 0x122, 0x123}},
{{0x211, 0x212, 0x213}, {0x221, 0x222, 0x223}},
{{0x311, 0x312, 0x313}, {0x321, 0x322, 0x323}},
{{0x411, 0x412, 0x413}, {0x421, 0x422, 0x423}},
}
ret2, ret2Exp := new(uint64), uint64(0x9876)
if err := abi.Unpack(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(*ret1, ret1Exp) {
t.Error("array result", *ret1, "!= Expected", ret1Exp)
}
if *ret2 != ret2Exp {
t.Error("int result", *ret2, "!= Expected", ret2Exp)
}
}
func TestUnmarshal(t *testing.T) { func TestUnmarshal(t *testing.T) {
const definition = `[ const definition = `[
{ "name" : "int", "constant" : false, "outputs": [ { "type": "uint256" } ] }, { "name" : "int", "constant" : false, "outputs": [ { "type": "uint256" } ] },

View file

@ -182,13 +182,13 @@ func doInstall(cmdline []string) {
// Check Go version. People regularly open issues about compilation // Check Go version. People regularly open issues about compilation
// failure with outdated Go. This should save them the trouble. // failure with outdated Go. This should save them the trouble.
if !strings.Contains(runtime.Version(), "devel") { if !strings.Contains(runtime.Version(), "devel") {
// Figure out the minor version number since we can't textually compare (1.10 < 1.7) // Figure out the minor version number since we can't textually compare (1.10 < 1.8)
var minor int var minor int
fmt.Sscanf(strings.TrimPrefix(runtime.Version(), "go1."), "%d", &minor) fmt.Sscanf(strings.TrimPrefix(runtime.Version(), "go1."), "%d", &minor)
if minor < 7 { if minor < 8 {
log.Println("You have Go version", runtime.Version()) log.Println("You have Go version", runtime.Version())
log.Println("go-ethereum requires at least Go version 1.7 and cannot") log.Println("go-ethereum requires at least Go version 1.8 and cannot")
log.Println("be compiled with an earlier version. Please upgrade your Go installation.") log.Println("be compiled with an earlier version. Please upgrade your Go installation.")
os.Exit(1) os.Exit(1)
} }

View file

@ -86,10 +86,6 @@ var (
Name: "create", Name: "create",
Usage: "indicates the action should be create rather than call", Usage: "indicates the action should be create rather than call",
} }
DisableGasMeteringFlag = cli.BoolFlag{
Name: "nogasmetering",
Usage: "disable gas metering",
}
GenesisFlag = cli.StringFlag{ GenesisFlag = cli.StringFlag{
Name: "prestate", Name: "prestate",
Usage: "JSON file with prestate (genesis) config", Usage: "JSON file with prestate (genesis) config",
@ -128,7 +124,6 @@ func init() {
ValueFlag, ValueFlag,
DumpFlag, DumpFlag,
InputFlag, InputFlag,
DisableGasMeteringFlag,
MemProfileFlag, MemProfileFlag,
CPUProfileFlag, CPUProfileFlag,
StatDumpFlag, StatDumpFlag,

View file

@ -163,7 +163,6 @@ func runCmd(ctx *cli.Context) error {
EVMConfig: vm.Config{ EVMConfig: vm.Config{
Tracer: tracer, Tracer: tracer,
Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name), Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
}, },
} }

View file

@ -533,9 +533,11 @@ func (f *faucet) loop() {
} }
defer sub.Unsubscribe() defer sub.Unsubscribe()
for { // Start a goroutine to update the state from head notifications in the background
select { update := make(chan *types.Header)
case head := <-heads:
go func() {
for head := range update {
// New chain head arrived, query the current stats and stream to clients // New chain head arrived, query the current stats and stream to clients
var ( var (
balance *big.Int balance *big.Int
@ -588,6 +590,17 @@ func (f *faucet) loop() {
} }
} }
f.lock.RUnlock() f.lock.RUnlock()
}
}()
// Wait for various events and assing to the appropriate background threads
for {
select {
case head := <-heads:
// New head arrived, send if for state update if there's none running
select {
case update <- head:
default:
}
case <-f.update: case <-f.update:
// Pending requests updated, stream to clients // Pending requests updated, stream to clients

View file

@ -170,7 +170,6 @@ type parityChainSpec struct {
Params struct { Params struct {
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"` MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"` DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"`
GasLimitBoundDivisor hexutil.Uint64 `json:"gasLimitBoundDivisor"`
DurationLimit *hexutil.Big `json:"durationLimit"` DurationLimit *hexutil.Big `json:"durationLimit"`
BlockReward *hexutil.Big `json:"blockReward"` BlockReward *hexutil.Big `json:"blockReward"`
HomesteadTransition uint64 `json:"homesteadTransition"` HomesteadTransition uint64 `json:"homesteadTransition"`
@ -188,6 +187,7 @@ type parityChainSpec struct {
Params struct { Params struct {
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"` MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
MinGasLimit hexutil.Uint64 `json:"minGasLimit"` MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
GasLimitBoundDivisor hexutil.Uint64 `json:"gasLimitBoundDivisor"`
NetworkID hexutil.Uint64 `json:"networkID"` NetworkID hexutil.Uint64 `json:"networkID"`
MaxCodeSize uint64 `json:"maxCodeSize"` MaxCodeSize uint64 `json:"maxCodeSize"`
EIP155Transition uint64 `json:"eip155Transition"` EIP155Transition uint64 `json:"eip155Transition"`
@ -270,7 +270,6 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
} }
spec.Engine.Ethash.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty) spec.Engine.Ethash.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty)
spec.Engine.Ethash.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor) spec.Engine.Ethash.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor)
spec.Engine.Ethash.Params.GasLimitBoundDivisor = (hexutil.Uint64)(params.GasLimitBoundDivisor)
spec.Engine.Ethash.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit) spec.Engine.Ethash.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit)
spec.Engine.Ethash.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward) spec.Engine.Ethash.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward)
spec.Engine.Ethash.Params.HomesteadTransition = genesis.Config.HomesteadBlock.Uint64() spec.Engine.Ethash.Params.HomesteadTransition = genesis.Config.HomesteadBlock.Uint64()
@ -284,6 +283,7 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize) spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit) spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
spec.Params.GasLimitBoundDivisor = (hexutil.Uint64)(params.GasLimitBoundDivisor)
spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainId.Uint64()) spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainId.Uint64())
spec.Params.MaxCodeSize = params.MaxCodeSize spec.Params.MaxCodeSize = params.MaxCodeSize
spec.Params.EIP155Transition = genesis.Config.EIP155Block.Uint64() spec.Params.EIP155Transition = genesis.Config.EIP155Block.Uint64()

View file

@ -400,7 +400,7 @@ var (
RPCVirtualHostsFlag = cli.StringFlag{ RPCVirtualHostsFlag = cli.StringFlag{
Name: "rpcvhosts", Name: "rpcvhosts",
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.", Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
Value: "localhost", Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","),
} }
RPCApiFlag = cli.StringFlag{ RPCApiFlag = cli.StringFlag{
Name: "rpcapi", Name: "rpcapi",
@ -695,9 +695,10 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) {
if ctx.GlobalIsSet(RPCApiFlag.Name) { if ctx.GlobalIsSet(RPCApiFlag.Name) {
cfg.HTTPModules = splitAndTrim(ctx.GlobalString(RPCApiFlag.Name)) cfg.HTTPModules = splitAndTrim(ctx.GlobalString(RPCApiFlag.Name))
} }
if ctx.GlobalIsSet(RPCVirtualHostsFlag.Name) {
cfg.HTTPVirtualHosts = splitAndTrim(ctx.GlobalString(RPCVirtualHostsFlag.Name)) cfg.HTTPVirtualHosts = splitAndTrim(ctx.GlobalString(RPCVirtualHostsFlag.Name))
} }
}
// setWS creates the WebSocket RPC listener interface string from the set // setWS creates the WebSocket RPC listener interface string from the set
// command line flags, returning empty if the HTTP endpoint is disabled. // command line flags, returning empty if the HTTP endpoint is disabled.

View file

@ -22,6 +22,7 @@ package main
import ( import (
"bufio" "bufio"
"crypto/ecdsa" "crypto/ecdsa"
crand "crypto/rand"
"crypto/sha512" "crypto/sha512"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
@ -48,6 +49,7 @@ import (
) )
const quitCommand = "~Q" const quitCommand = "~Q"
const entropySize = 32
// singletons // singletons
var ( var (
@ -55,6 +57,7 @@ var (
shh *whisper.Whisper shh *whisper.Whisper
done chan struct{} done chan struct{}
mailServer mailserver.WMailServer mailServer mailserver.WMailServer
entropy [entropySize]byte
input = bufio.NewReader(os.Stdin) input = bufio.NewReader(os.Stdin)
) )
@ -76,14 +79,15 @@ var (
// cmd arguments // cmd arguments
var ( var (
bootstrapMode = flag.Bool("standalone", false, "boostrap node: don't actively connect to peers, wait for incoming connections") bootstrapMode = flag.Bool("standalone", false, "boostrap node: don't initiate connection to peers, just wait for incoming connections")
forwarderMode = flag.Bool("forwarder", false, "forwarder mode: only forward messages, neither send nor decrypt messages") forwarderMode = flag.Bool("forwarder", false, "forwarder mode: only forward messages, neither encrypt nor decrypt messages")
mailServerMode = flag.Bool("mailserver", false, "mail server mode: delivers expired messages on demand") mailServerMode = flag.Bool("mailserver", false, "mail server mode: delivers expired messages on demand")
requestMail = flag.Bool("mailclient", false, "request expired messages from the bootstrap server") requestMail = flag.Bool("mailclient", false, "request expired messages from the bootstrap server")
asymmetricMode = flag.Bool("asym", false, "use asymmetric encryption") asymmetricMode = flag.Bool("asym", false, "use asymmetric encryption")
generateKey = flag.Bool("generatekey", false, "generate and show the private key") generateKey = flag.Bool("generatekey", false, "generate and show the private key")
fileExMode = flag.Bool("fileexchange", false, "file exchange mode") fileExMode = flag.Bool("fileexchange", false, "file exchange mode")
testMode = flag.Bool("test", false, "use of predefined parameters for diagnostics") fileReader = flag.Bool("filereader", false, "load and decrypt messages saved as files, display as plain text")
testMode = flag.Bool("test", false, "use of predefined parameters for diagnostics (password, etc.)")
echoMode = flag.Bool("echo", false, "echo mode: prints some arguments for diagnostics") echoMode = flag.Bool("echo", false, "echo mode: prints some arguments for diagnostics")
argVerbosity = flag.Int("verbosity", int(log.LvlError), "log verbosity level") argVerbosity = flag.Int("verbosity", int(log.LvlError), "log verbosity level")
@ -99,13 +103,14 @@ var (
argIDFile = flag.String("idfile", "", "file name with node id (private key)") argIDFile = flag.String("idfile", "", "file name with node id (private key)")
argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)") argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)")
argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)") argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)")
argSaveDir = flag.String("savedir", "", "directory where incoming messages will be saved as files") argSaveDir = flag.String("savedir", "", "directory where all incoming messages will be saved as files")
) )
func main() { func main() {
processArgs() processArgs()
initialize() initialize()
run() run()
shutdown()
} }
func processArgs() { func processArgs() {
@ -205,21 +210,6 @@ func initialize() {
MinimumAcceptedPOW: *argPoW, MinimumAcceptedPOW: *argPoW,
} }
if *mailServerMode {
if len(msPassword) == 0 {
msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ")
if err != nil {
utils.Fatalf("Failed to read Mail Server password: %s", err)
}
}
shh = whisper.New(cfg)
shh.RegisterServer(&mailServer)
mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW)
} else {
shh = whisper.New(cfg)
}
if *argPoW != whisper.DefaultMinimumPoW { if *argPoW != whisper.DefaultMinimumPoW {
err := shh.SetMinimumPoW(*argPoW) err := shh.SetMinimumPoW(*argPoW)
if err != nil { if err != nil {
@ -261,6 +251,26 @@ func initialize() {
maxPeers = 800 maxPeers = 800
} }
_, err = crand.Read(entropy[:])
if err != nil {
utils.Fatalf("crypto/rand failed: %s", err)
}
if *mailServerMode {
if len(msPassword) == 0 {
msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ")
if err != nil {
utils.Fatalf("Failed to read Mail Server password: %s", err)
}
}
shh = whisper.New(cfg)
shh.RegisterServer(&mailServer)
mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW)
} else {
shh = whisper.New(cfg)
}
server = &p2p.Server{ server = &p2p.Server{
Config: p2p.Config{ Config: p2p.Config{
PrivateKey: nodeid, PrivateKey: nodeid,
@ -276,10 +286,11 @@ func initialize() {
} }
} }
func startServer() { func startServer() error {
err := server.Start() err := server.Start()
if err != nil { if err != nil {
utils.Fatalf("Failed to start Whisper peer: %s.", err) fmt.Printf("Failed to start Whisper peer: %s.", err)
return err
} }
fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey))) fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey)))
@ -298,6 +309,7 @@ func startServer() {
if !*forwarderMode { if !*forwarderMode {
fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand) fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand)
} }
return nil
} }
func isKeyValid(k *ecdsa.PublicKey) bool { func isKeyValid(k *ecdsa.PublicKey) bool {
@ -411,8 +423,10 @@ func waitForConnection(timeout bool) {
} }
func run() { func run() {
defer mailServer.Close() err := startServer()
startServer() if err != nil {
return
}
defer server.Stop() defer server.Stop()
shh.Start(nil) shh.Start(nil)
defer shh.Stop() defer shh.Stop()
@ -425,21 +439,26 @@ func run() {
requestExpiredMessagesLoop() requestExpiredMessagesLoop()
} else if *fileExMode { } else if *fileExMode {
sendFilesLoop() sendFilesLoop()
} else if *fileReader {
fileReaderLoop()
} else { } else {
sendLoop() sendLoop()
} }
} }
func shutdown() {
close(done)
mailServer.Close()
}
func sendLoop() { func sendLoop() {
for { for {
s := scanLine("") s := scanLine("")
if s == quitCommand { if s == quitCommand {
fmt.Println("Quit command received") fmt.Println("Quit command received")
close(done) return
break
} }
sendMsg([]byte(s)) sendMsg([]byte(s))
if *asymmetricMode { if *asymmetricMode {
// print your own message for convenience, // print your own message for convenience,
// because in asymmetric mode it is impossible to decrypt it // because in asymmetric mode it is impossible to decrypt it
@ -455,13 +474,11 @@ func sendFilesLoop() {
s := scanLine("") s := scanLine("")
if s == quitCommand { if s == quitCommand {
fmt.Println("Quit command received") fmt.Println("Quit command received")
close(done) return
break
} }
b, err := ioutil.ReadFile(s) b, err := ioutil.ReadFile(s)
if err != nil { if err != nil {
fmt.Printf(">>> Error: %s \n", err) fmt.Printf(">>> Error: %s \n", err)
continue
} else { } else {
h := sendMsg(b) h := sendMsg(b)
if (h == common.Hash{}) { if (h == common.Hash{}) {
@ -475,6 +492,38 @@ func sendFilesLoop() {
} }
} }
func fileReaderLoop() {
watcher1 := shh.GetFilter(symFilterID)
watcher2 := shh.GetFilter(asymFilterID)
if watcher1 == nil && watcher2 == nil {
fmt.Println("Error: neither symmetric nor asymmetric filter is installed")
return
}
for {
s := scanLine("")
if s == quitCommand {
fmt.Println("Quit command received")
return
}
raw, err := ioutil.ReadFile(s)
if err != nil {
fmt.Printf(">>> Error: %s \n", err)
} else {
env := whisper.Envelope{Data: raw} // the topic is zero
msg := env.Open(watcher1) // force-open envelope regardless of the topic
if msg == nil {
msg = env.Open(watcher2)
}
if msg == nil {
fmt.Printf(">>> Error: failed to decrypt the message \n")
} else {
printMessageInfo(msg)
}
}
}
}
func scanLine(prompt string) string { func scanLine(prompt string) string {
if len(prompt) > 0 { if len(prompt) > 0 {
fmt.Print(prompt) fmt.Print(prompt)
@ -548,20 +597,18 @@ func messageLoop() {
for { for {
select { select {
case <-ticker.C: case <-ticker.C:
messages := sf.Retrieve() m1 := sf.Retrieve()
m2 := af.Retrieve()
messages := append(m1, m2...)
for _, msg := range messages { for _, msg := range messages {
if *fileExMode || len(msg.Payload) > 2048 { // All messages are saved upon specifying argSaveDir.
// fileExMode only specifies how messages are displayed on the console after they are saved.
// if fileExMode == true, only the hashes are displayed, since messages might be too big.
if len(*argSaveDir) > 0 {
writeMessageToFile(*argSaveDir, msg) writeMessageToFile(*argSaveDir, msg)
} else {
printMessageInfo(msg)
}
} }
messages = af.Retrieve() if !*fileExMode && len(msg.Payload) <= 2048 {
for _, msg := range messages {
if *fileExMode || len(msg.Payload) > 2048 {
writeMessageToFile(*argSaveDir, msg)
} else {
printMessageInfo(msg) printMessageInfo(msg)
} }
} }
@ -596,27 +643,30 @@ func writeMessageToFile(dir string, msg *whisper.ReceivedMessage) {
address = crypto.PubkeyToAddress(*msg.Src) address = crypto.PubkeyToAddress(*msg.Src)
} }
if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) { // this is a sample code; uncomment if you don't want to save your own messages.
// message from myself: don't save, only report //if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) {
fmt.Printf("\n%s <%x>: message received: '%s'\n", timestamp, address, name) // fmt.Printf("\n%s <%x>: message from myself received, not saved: '%s'\n", timestamp, address, name)
} else if len(dir) > 0 { // return
//}
if len(dir) > 0 {
fullpath := filepath.Join(dir, name) fullpath := filepath.Join(dir, name)
err := ioutil.WriteFile(fullpath, msg.Payload, 0644) err := ioutil.WriteFile(fullpath, msg.Raw, 0644)
if err != nil { if err != nil {
fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err) fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err)
} else { } else {
fmt.Printf("\n%s {%x}: message received and saved as '%s' (%d bytes)\n", timestamp, address, name, len(msg.Payload)) fmt.Printf("\n%s {%x}: message received and saved as '%s' (%d bytes)\n", timestamp, address, name, len(msg.Raw))
} }
} else { } else {
fmt.Printf("\n%s {%x}: big message received (%d bytes), but not saved: %s\n", timestamp, address, len(msg.Payload), name) fmt.Printf("\n%s {%x}: message received (%d bytes), but not saved: %s\n", timestamp, address, len(msg.Raw), name)
} }
} }
func requestExpiredMessagesLoop() { func requestExpiredMessagesLoop() {
var key, peerID []byte var key, peerID, bloom []byte
var timeLow, timeUpp uint32 var timeLow, timeUpp uint32
var t string var t string
var xt, empty whisper.TopicType var xt whisper.TopicType
keyID, err := shh.AddSymKeyFromPassword(msPassword) keyID, err := shh.AddSymKeyFromPassword(msPassword)
if err != nil { if err != nil {
@ -639,18 +689,19 @@ func requestExpiredMessagesLoop() {
utils.Fatalf("Failed to parse the topic: %s", err) utils.Fatalf("Failed to parse the topic: %s", err)
} }
xt = whisper.BytesToTopic(x) xt = whisper.BytesToTopic(x)
bloom = whisper.TopicToBloom(xt)
obfuscateBloom(bloom)
} else {
bloom = whisper.MakeFullNodeBloom()
} }
if timeUpp == 0 { if timeUpp == 0 {
timeUpp = 0xFFFFFFFF timeUpp = 0xFFFFFFFF
} }
data := make([]byte, 8+whisper.TopicLength) data := make([]byte, 8, 8+whisper.BloomFilterSize)
binary.BigEndian.PutUint32(data, timeLow) binary.BigEndian.PutUint32(data, timeLow)
binary.BigEndian.PutUint32(data[4:], timeUpp) binary.BigEndian.PutUint32(data[4:], timeUpp)
copy(data[8:], xt[:]) data = append(data, bloom...)
if xt == empty {
data = data[:8]
}
var params whisper.MessageParams var params whisper.MessageParams
params.PoW = *argServerPoW params.PoW = *argServerPoW
@ -684,3 +735,20 @@ func extractIDFromEnode(s string) []byte {
} }
return n.ID[:] return n.ID[:]
} }
// obfuscateBloom adds 16 random bits to the the bloom
// filter, in order to obfuscate the containing topics.
// it does so deterministically within every session.
// despite additional bits, it will match on average
// 32000 times less messages than full node's bloom filter.
func obfuscateBloom(bloom []byte) {
const half = entropySize / 2
for i := 0; i < half; i++ {
x := int(entropy[i])
if entropy[half+i] < 128 {
x += 256
}
bloom[x/8] = 1 << uint(x%8) // set the bit number X
}
}

View file

@ -19,6 +19,7 @@ package ethash
import ( import (
"encoding/binary" "encoding/binary"
"hash" "hash"
"math/big"
"reflect" "reflect"
"runtime" "runtime"
"sync" "sync"
@ -47,6 +48,48 @@ const (
loopAccesses = 64 // Number of accesses in hashimoto loop loopAccesses = 64 // Number of accesses in hashimoto loop
) )
// cacheSize returns the size of the ethash verification cache that belongs to a certain
// block number.
func cacheSize(block uint64) uint64 {
epoch := int(block / epochLength)
if epoch < maxEpoch {
return cacheSizes[epoch]
}
return calcCacheSize(epoch)
}
// calcCacheSize calculates the cache size for epoch. The cache size grows linearly,
// however, we always take the highest prime below the linearly growing threshold in order
// to reduce the risk of accidental regularities leading to cyclic behavior.
func calcCacheSize(epoch int) uint64 {
size := cacheInitBytes + cacheGrowthBytes*uint64(epoch) - hashBytes
for !new(big.Int).SetUint64(size / hashBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
size -= 2 * hashBytes
}
return size
}
// datasetSize returns the size of the ethash mining dataset that belongs to a certain
// block number.
func datasetSize(block uint64) uint64 {
epoch := int(block / epochLength)
if epoch < maxEpoch {
return datasetSizes[epoch]
}
return calcDatasetSize(epoch)
}
// calcDatasetSize calculates the dataset size for epoch. The dataset size grows linearly,
// however, we always take the highest prime below the linearly growing threshold in order
// to reduce the risk of accidental regularities leading to cyclic behavior.
func calcDatasetSize(epoch int) uint64 {
size := datasetInitBytes + datasetGrowthBytes*uint64(epoch) - mixBytes
for !new(big.Int).SetUint64(size / mixBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
size -= 2 * mixBytes
}
return size
}
// hasher is a repetitive hasher allowing the same hash data structures to be // hasher is a repetitive hasher allowing the same hash data structures to be
// reused between hash runs instead of requiring new ones to be created. // reused between hash runs instead of requiring new ones to be created.
type hasher func(dest []byte, data []byte) type hasher func(dest []byte, data []byte)

View file

@ -1,47 +0,0 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build !go1.8
package ethash
// cacheSize calculates and returns the size of the ethash verification cache that
// belongs to a certain block number. The cache size grows linearly, however, we
// always take the highest prime below the linearly growing threshold in order to
// reduce the risk of accidental regularities leading to cyclic behavior.
func cacheSize(block uint64) uint64 {
// If we have a pre-generated value, use that
epoch := int(block / epochLength)
if epoch < maxEpoch {
return cacheSizes[epoch]
}
// We don't have a way to verify primes fast before Go 1.8
panic("fast prime testing unsupported in Go < 1.8")
}
// datasetSize calculates and returns the size of the ethash mining dataset that
// belongs to a certain block number. The dataset size grows linearly, however, we
// always take the highest prime below the linearly growing threshold in order to
// reduce the risk of accidental regularities leading to cyclic behavior.
func datasetSize(block uint64) uint64 {
// If we have a pre-generated value, use that
epoch := int(block / epochLength)
if epoch < maxEpoch {
return datasetSizes[epoch]
}
// We don't have a way to verify primes fast before Go 1.8
panic("fast prime testing unsupported in Go < 1.8")
}

View file

@ -1,63 +0,0 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build go1.8
package ethash
import "math/big"
// cacheSize returns the size of the ethash verification cache that belongs to a certain
// block number.
func cacheSize(block uint64) uint64 {
epoch := int(block / epochLength)
if epoch < maxEpoch {
return cacheSizes[epoch]
}
return calcCacheSize(epoch)
}
// calcCacheSize calculates the cache size for epoch. The cache size grows linearly,
// however, we always take the highest prime below the linearly growing threshold in order
// to reduce the risk of accidental regularities leading to cyclic behavior.
func calcCacheSize(epoch int) uint64 {
size := cacheInitBytes + cacheGrowthBytes*uint64(epoch) - hashBytes
for !new(big.Int).SetUint64(size / hashBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
size -= 2 * hashBytes
}
return size
}
// datasetSize returns the size of the ethash mining dataset that belongs to a certain
// block number.
func datasetSize(block uint64) uint64 {
epoch := int(block / epochLength)
if epoch < maxEpoch {
return datasetSizes[epoch]
}
return calcDatasetSize(epoch)
}
// calcDatasetSize calculates the dataset size for epoch. The dataset size grows linearly,
// however, we always take the highest prime below the linearly growing threshold in order
// to reduce the risk of accidental regularities leading to cyclic behavior.
func calcDatasetSize(epoch int) uint64 {
size := datasetInitBytes + datasetGrowthBytes*uint64(epoch) - mixBytes
for !new(big.Int).SetUint64(size / mixBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
size -= 2 * mixBytes
}
return size
}

View file

@ -1,37 +0,0 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build go1.8
package ethash
import "testing"
// Tests whether the dataset size calculator works correctly by cross checking the
// hard coded lookup table with the value generated by it.
func TestSizeCalculations(t *testing.T) {
// Verify all the cache and dataset sizes from the lookup table.
for epoch, want := range cacheSizes {
if size := calcCacheSize(epoch); size != want {
t.Errorf("cache %d: cache size mismatch: have %d, want %d", epoch, size, want)
}
}
for epoch, want := range datasetSizes {
if size := calcDatasetSize(epoch); size != want {
t.Errorf("dataset %d: dataset size mismatch: have %d, want %d", epoch, size, want)
}
}
}

View file

@ -30,6 +30,22 @@ import (
"github.com/EthereumCommonwealth/go-callisto/core/types" "github.com/EthereumCommonwealth/go-callisto/core/types"
) )
// Tests whether the dataset size calculator works correctly by cross checking the
// hard coded lookup table with the value generated by it.
func TestSizeCalculations(t *testing.T) {
// Verify all the cache and dataset sizes from the lookup table.
for epoch, want := range cacheSizes {
if size := calcCacheSize(epoch); size != want {
t.Errorf("cache %d: cache size mismatch: have %d, want %d", epoch, size, want)
}
}
for epoch, want := range datasetSizes {
if size := calcDatasetSize(epoch); size != want {
t.Errorf("dataset %d: dataset size mismatch: have %d, want %d", epoch, size, want)
}
}
}
// Tests that verification caches can be correctly generated. // Tests that verification caches can be correctly generated.
func TestCacheGeneration(t *testing.T) { func TestCacheGeneration(t *testing.T) {
tests := []struct { tests := []struct {

View file

@ -55,7 +55,6 @@ var (
errDuplicateUncle = errors.New("duplicate uncle") errDuplicateUncle = errors.New("duplicate uncle")
errUncleIsAncestor = errors.New("uncle is ancestor") errUncleIsAncestor = errors.New("uncle is ancestor")
errDanglingUncle = errors.New("uncle's parent is not ancestor") errDanglingUncle = errors.New("uncle's parent is not ancestor")
errNonceOutOfRange = errors.New("nonce out of range")
errInvalidDifficulty = errors.New("non-positive difficulty") errInvalidDifficulty = errors.New("non-positive difficulty")
errInvalidMixDigest = errors.New("invalid mix digest") errInvalidMixDigest = errors.New("invalid mix digest")
errInvalidPoW = errors.New("invalid proof-of-work") errInvalidPoW = errors.New("invalid proof-of-work")
@ -409,7 +408,7 @@ func calcDifficultyByzantium(time uint64, parent *types.Header) *big.Int {
if x.Cmp(params.MinimumDifficulty) < 0 { if x.Cmp(params.MinimumDifficulty) < 0 {
x.Set(params.MinimumDifficulty) x.Set(params.MinimumDifficulty)
} }
// calculate a fake block numer for the ice-age delay: // calculate a fake block number for the ice-age delay:
// https://github.com/ethereum/EIPs/pull/669 // https://github.com/ethereum/EIPs/pull/669
// fake_block_number = min(0, block.number - 3_000_000 // fake_block_number = min(0, block.number - 3_000_000
fakeBlockNumber := new(big.Int) fakeBlockNumber := new(big.Int)
@ -527,18 +526,13 @@ func (ethash *Ethash) VerifySeal(chain consensus.ChainReader, header *types.Head
if ethash.shared != nil { if ethash.shared != nil {
return ethash.shared.VerifySeal(chain, header) return ethash.shared.VerifySeal(chain, header)
} }
// Sanity check that the block number is below the lookup table size (60M blocks)
number := header.Number.Uint64()
if number/epochLength >= maxEpoch {
// Go < 1.7 cannot calculate new cache/dataset sizes (no fast prime check)
return errNonceOutOfRange
}
// Ensure that we have a valid difficulty for the block // Ensure that we have a valid difficulty for the block
if header.Difficulty.Sign() <= 0 { if header.Difficulty.Sign() <= 0 {
return errInvalidDifficulty return errInvalidDifficulty
} }
// Recompute the digest and PoW value and verify against the header // Recompute the digest and PoW value and verify against the header
number := header.Number.Uint64()
cache := ethash.cache(number) cache := ethash.cache(number)
size := datasetSize(number) size := datasetSize(number)
if ethash.config.PowMode == ModeTest { if ethash.config.PowMode == ModeTest {

View file

@ -281,8 +281,8 @@ func TestDeposit(t *testing.T) {
t.Fatalf("expected balance %v, got %v", exp, chbook.Balance()) t.Fatalf("expected balance %v, got %v", exp, chbook.Balance())
} }
// autodeposit every 30ms if new cheque issued // autodeposit every 200ms if new cheque issued
interval := 30 * time.Millisecond interval := 200 * time.Millisecond
chbook.AutoDeposit(interval, common.Big1, balance) chbook.AutoDeposit(interval, common.Big1, balance)
_, err = chbook.Issue(addr1, amount) _, err = chbook.Issue(addr1, amount)
if err != nil { if err != nil {

View file

@ -206,7 +206,7 @@ func lexLine(l *lexer) stateFn {
return lexComment return lexComment
case isSpace(r): case isSpace(r):
l.ignore() l.ignore()
case isAlphaNumeric(r) || r == '_': case isLetter(r) || r == '_':
return lexElement return lexElement
case isNumber(r): case isNumber(r):
return lexNumber return lexNumber
@ -278,7 +278,7 @@ func lexElement(l *lexer) stateFn {
return lexLine return lexLine
} }
func isAlphaNumeric(t rune) bool { func isLetter(t rune) bool {
return unicode.IsLetter(t) return unicode.IsLetter(t)
} }

View file

@ -107,8 +107,8 @@ type BlockChain struct {
procmu sync.RWMutex // block processor lock procmu sync.RWMutex // block processor lock
checkpoint int // checkpoint counts towards the new checkpoint checkpoint int // checkpoint counts towards the new checkpoint
currentBlock *types.Block // Current head of the block chain currentBlock atomic.Value // Current head of the block chain
currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!) currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!)
stateCache state.Database // State database to reuse between imports (contains state cache) stateCache state.Database // State database to reuse between imports (contains state cache)
bodyCache *lru.Cache // Cache for the most recent block bodies bodyCache *lru.Cache // Cache for the most recent block bodies
@ -224,10 +224,10 @@ func (bc *BlockChain) loadLastState() error {
} }
} }
// Everything seems to be fine, set as the head block // Everything seems to be fine, set as the head block
bc.currentBlock = currentBlock bc.currentBlock.Store(currentBlock)
// Restore the last known head header // Restore the last known head header
currentHeader := bc.currentBlock.Header() currentHeader := currentBlock.Header()
if head := GetHeadHeaderHash(bc.db); head != (common.Hash{}) { if head := GetHeadHeaderHash(bc.db); head != (common.Hash{}) {
if header := bc.GetHeaderByHash(head); header != nil { if header := bc.GetHeaderByHash(head); header != nil {
currentHeader = header currentHeader = header
@ -236,21 +236,23 @@ func (bc *BlockChain) loadLastState() error {
bc.hc.SetCurrentHeader(currentHeader) bc.hc.SetCurrentHeader(currentHeader)
// Restore the last known head fast block // Restore the last known head fast block
bc.currentFastBlock = bc.currentBlock bc.currentFastBlock.Store(currentBlock)
if head := GetHeadFastBlockHash(bc.db); head != (common.Hash{}) { if head := GetHeadFastBlockHash(bc.db); head != (common.Hash{}) {
if block := bc.GetBlockByHash(head); block != nil { if block := bc.GetBlockByHash(head); block != nil {
bc.currentFastBlock = block bc.currentFastBlock.Store(block)
} }
} }
// Issue a status log for the user // Issue a status log for the user
currentFastBlock := bc.CurrentFastBlock()
headerTd := bc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64()) headerTd := bc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64())
blockTd := bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64()) blockTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
fastTd := bc.GetTd(bc.currentFastBlock.Hash(), bc.currentFastBlock.NumberU64()) fastTd := bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64())
log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd) log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd)
log.Info("Loaded most recent local full block", "number", bc.currentBlock.Number(), "hash", bc.currentBlock.Hash(), "td", blockTd) log.Info("Loaded most recent local full block", "number", currentBlock.Number(), "hash", currentBlock.Hash(), "td", blockTd)
log.Info("Loaded most recent local fast block", "number", bc.currentFastBlock.Number(), "hash", bc.currentFastBlock.Hash(), "td", fastTd) log.Info("Loaded most recent local fast block", "number", currentFastBlock.Number(), "hash", currentFastBlock.Hash(), "td", fastTd)
return nil return nil
} }
@ -279,30 +281,32 @@ func (bc *BlockChain) SetHead(head uint64) error {
bc.futureBlocks.Purge() bc.futureBlocks.Purge()
// Rewind the block chain, ensuring we don't end up with a stateless head block // Rewind the block chain, ensuring we don't end up with a stateless head block
if bc.currentBlock != nil && currentHeader.Number.Uint64() < bc.currentBlock.NumberU64() { if currentBlock := bc.CurrentBlock(); currentBlock != nil && currentHeader.Number.Uint64() < currentBlock.NumberU64() {
bc.currentBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()) bc.currentBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()))
} }
if bc.currentBlock != nil { if currentBlock := bc.CurrentBlock(); currentBlock != nil {
if _, err := state.New(bc.currentBlock.Root(), bc.stateCache); err != nil { if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil {
// Rewound state missing, rolled back to before pivot, reset to genesis // Rewound state missing, rolled back to before pivot, reset to genesis
bc.currentBlock = nil bc.currentBlock.Store(bc.genesisBlock)
} }
} }
// Rewind the fast block in a simpleton way to the target head // Rewind the fast block in a simpleton way to the target head
if bc.currentFastBlock != nil && currentHeader.Number.Uint64() < bc.currentFastBlock.NumberU64() { if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock != nil && currentHeader.Number.Uint64() < currentFastBlock.NumberU64() {
bc.currentFastBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()) bc.currentFastBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()))
} }
// If either blocks reached nil, reset to the genesis state // If either blocks reached nil, reset to the genesis state
if bc.currentBlock == nil { if currentBlock := bc.CurrentBlock(); currentBlock == nil {
bc.currentBlock = bc.genesisBlock bc.currentBlock.Store(bc.genesisBlock)
} }
if bc.currentFastBlock == nil { if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock == nil {
bc.currentFastBlock = bc.genesisBlock bc.currentFastBlock.Store(bc.genesisBlock)
} }
if err := WriteHeadBlockHash(bc.db, bc.currentBlock.Hash()); err != nil { currentBlock := bc.CurrentBlock()
currentFastBlock := bc.CurrentFastBlock()
if err := WriteHeadBlockHash(bc.db, currentBlock.Hash()); err != nil {
log.Crit("Failed to reset head full block", "err", err) log.Crit("Failed to reset head full block", "err", err)
} }
if err := WriteHeadFastBlockHash(bc.db, bc.currentFastBlock.Hash()); err != nil { if err := WriteHeadFastBlockHash(bc.db, currentFastBlock.Hash()); err != nil {
log.Crit("Failed to reset head fast block", "err", err) log.Crit("Failed to reset head fast block", "err", err)
} }
return bc.loadLastState() return bc.loadLastState()
@ -321,7 +325,7 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
} }
// If all checks out, manually set the head block // If all checks out, manually set the head block
bc.mu.Lock() bc.mu.Lock()
bc.currentBlock = block bc.currentBlock.Store(block)
bc.mu.Unlock() bc.mu.Unlock()
log.Info("Committed new head block", "number", block.Number(), "hash", hash) log.Info("Committed new head block", "number", block.Number(), "hash", hash)
@ -330,28 +334,19 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
// GasLimit returns the gas limit of the current HEAD block. // GasLimit returns the gas limit of the current HEAD block.
func (bc *BlockChain) GasLimit() uint64 { func (bc *BlockChain) GasLimit() uint64 {
bc.mu.RLock() return bc.CurrentBlock().GasLimit()
defer bc.mu.RUnlock()
return bc.currentBlock.GasLimit()
} }
// CurrentBlock retrieves the current head block of the canonical chain. The // CurrentBlock retrieves the current head block of the canonical chain. The
// block is retrieved from the blockchain's internal cache. // block is retrieved from the blockchain's internal cache.
func (bc *BlockChain) CurrentBlock() *types.Block { func (bc *BlockChain) CurrentBlock() *types.Block {
bc.mu.RLock() return bc.currentBlock.Load().(*types.Block)
defer bc.mu.RUnlock()
return bc.currentBlock
} }
// CurrentFastBlock retrieves the current fast-sync head block of the canonical // CurrentFastBlock retrieves the current fast-sync head block of the canonical
// chain. The block is retrieved from the blockchain's internal cache. // chain. The block is retrieved from the blockchain's internal cache.
func (bc *BlockChain) CurrentFastBlock() *types.Block { func (bc *BlockChain) CurrentFastBlock() *types.Block {
bc.mu.RLock() return bc.currentFastBlock.Load().(*types.Block)
defer bc.mu.RUnlock()
return bc.currentFastBlock
} }
// SetProcessor sets the processor required for making state modifications. // SetProcessor sets the processor required for making state modifications.
@ -416,10 +411,10 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
} }
bc.genesisBlock = genesis bc.genesisBlock = genesis
bc.insert(bc.genesisBlock) bc.insert(bc.genesisBlock)
bc.currentBlock = bc.genesisBlock bc.currentBlock.Store(bc.genesisBlock)
bc.hc.SetGenesis(bc.genesisBlock.Header()) bc.hc.SetGenesis(bc.genesisBlock.Header())
bc.hc.SetCurrentHeader(bc.genesisBlock.Header()) bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
bc.currentFastBlock = bc.genesisBlock bc.currentFastBlock.Store(bc.genesisBlock)
return nil return nil
} }
@ -444,7 +439,7 @@ func (bc *BlockChain) repair(head **types.Block) error {
// Export writes the active chain to the given writer. // Export writes the active chain to the given writer.
func (bc *BlockChain) Export(w io.Writer) error { func (bc *BlockChain) Export(w io.Writer) error {
return bc.ExportN(w, uint64(0), bc.currentBlock.NumberU64()) return bc.ExportN(w, uint64(0), bc.CurrentBlock().NumberU64())
} }
// ExportN writes a subset of the active chain to the given writer. // ExportN writes a subset of the active chain to the given writer.
@ -488,7 +483,7 @@ func (bc *BlockChain) insert(block *types.Block) {
if err := WriteHeadBlockHash(bc.db, block.Hash()); err != nil { if err := WriteHeadBlockHash(bc.db, block.Hash()); err != nil {
log.Crit("Failed to insert head block hash", "err", err) log.Crit("Failed to insert head block hash", "err", err)
} }
bc.currentBlock = block bc.currentBlock.Store(block)
// If the block is better than our head or is on a different chain, force update heads // If the block is better than our head or is on a different chain, force update heads
if updateHeads { if updateHeads {
@ -497,7 +492,7 @@ func (bc *BlockChain) insert(block *types.Block) {
if err := WriteHeadFastBlockHash(bc.db, block.Hash()); err != nil { if err := WriteHeadFastBlockHash(bc.db, block.Hash()); err != nil {
log.Crit("Failed to insert head fast block hash", "err", err) log.Crit("Failed to insert head fast block hash", "err", err)
} }
bc.currentFastBlock = block bc.currentFastBlock.Store(block)
} }
} }
@ -714,13 +709,15 @@ func (bc *BlockChain) Rollback(chain []common.Hash) {
if currentHeader.Hash() == hash { if currentHeader.Hash() == hash {
bc.hc.SetCurrentHeader(bc.GetHeader(currentHeader.ParentHash, currentHeader.Number.Uint64()-1)) bc.hc.SetCurrentHeader(bc.GetHeader(currentHeader.ParentHash, currentHeader.Number.Uint64()-1))
} }
if bc.currentFastBlock.Hash() == hash { if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock.Hash() == hash {
bc.currentFastBlock = bc.GetBlock(bc.currentFastBlock.ParentHash(), bc.currentFastBlock.NumberU64()-1) newFastBlock := bc.GetBlock(currentFastBlock.ParentHash(), currentFastBlock.NumberU64()-1)
WriteHeadFastBlockHash(bc.db, bc.currentFastBlock.Hash()) bc.currentFastBlock.Store(newFastBlock)
WriteHeadFastBlockHash(bc.db, newFastBlock.Hash())
} }
if bc.currentBlock.Hash() == hash { if currentBlock := bc.CurrentBlock(); currentBlock.Hash() == hash {
bc.currentBlock = bc.GetBlock(bc.currentBlock.ParentHash(), bc.currentBlock.NumberU64()-1) newBlock := bc.GetBlock(currentBlock.ParentHash(), currentBlock.NumberU64()-1)
WriteHeadBlockHash(bc.db, bc.currentBlock.Hash()) bc.currentBlock.Store(newBlock)
WriteHeadBlockHash(bc.db, newBlock.Hash())
} }
} }
} }
@ -829,11 +826,12 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
bc.mu.Lock() bc.mu.Lock()
head := blockChain[len(blockChain)-1] head := blockChain[len(blockChain)-1]
if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case
if bc.GetTd(bc.currentFastBlock.Hash(), bc.currentFastBlock.NumberU64()).Cmp(td) < 0 { currentFastBlock := bc.CurrentFastBlock()
if bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64()).Cmp(td) < 0 {
if err := WriteHeadFastBlockHash(bc.db, head.Hash()); err != nil { if err := WriteHeadFastBlockHash(bc.db, head.Hash()); err != nil {
log.Crit("Failed to update head fast block hash", "err", err) log.Crit("Failed to update head fast block hash", "err", err)
} }
bc.currentFastBlock = head bc.currentFastBlock.Store(head)
} }
} }
bc.mu.Unlock() bc.mu.Unlock()
@ -880,7 +878,8 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
bc.mu.Lock() bc.mu.Lock()
defer bc.mu.Unlock() defer bc.mu.Unlock()
localTd := bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64()) currentBlock := bc.CurrentBlock()
localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
externTd := new(big.Int).Add(block.Difficulty(), ptd) externTd := new(big.Int).Add(block.Difficulty(), ptd)
// Irrelevant of the canonical status, write the block itself to the database // Irrelevant of the canonical status, write the block itself to the database
@ -955,14 +954,15 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
// Second clause in the if statement reduces the vulnerability to selfish mining. // Second clause in the if statement reduces the vulnerability to selfish mining.
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
reorg := externTd.Cmp(localTd) > 0 reorg := externTd.Cmp(localTd) > 0
currentBlock = bc.CurrentBlock()
if !reorg && externTd.Cmp(localTd) == 0 { if !reorg && externTd.Cmp(localTd) == 0 {
// Split same-difficulty blocks by number, then at random // Split same-difficulty blocks by number, then at random
reorg = block.NumberU64() < bc.currentBlock.NumberU64() || (block.NumberU64() == bc.currentBlock.NumberU64() && mrand.Float64() < 0.5) reorg = block.NumberU64() < currentBlock.NumberU64() || (block.NumberU64() == currentBlock.NumberU64() && mrand.Float64() < 0.5)
} }
if reorg { if reorg {
// Reorganise the chain if the parent is not the head block // Reorganise the chain if the parent is not the head block
if block.ParentHash() != bc.currentBlock.Hash() { if block.ParentHash() != currentBlock.Hash() {
if err := bc.reorg(bc.currentBlock, block); err != nil { if err := bc.reorg(currentBlock, block); err != nil {
return NonStatTy, err return NonStatTy, err
} }
} }
@ -1091,7 +1091,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
case err == consensus.ErrPrunedAncestor: case err == consensus.ErrPrunedAncestor:
// Block competing with the canonical chain, store in the db, but don't process // Block competing with the canonical chain, store in the db, but don't process
// until the competitor TD goes above the canonical TD // until the competitor TD goes above the canonical TD
localTd := bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64()) currentBlock := bc.CurrentBlock()
localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
externTd := new(big.Int).Add(bc.GetTd(block.ParentHash(), block.NumberU64()-1), block.Difficulty()) externTd := new(big.Int).Add(bc.GetTd(block.ParentHash(), block.NumberU64()-1), block.Difficulty())
if localTd.Cmp(externTd) > 0 { if localTd.Cmp(externTd) > 0 {
if err = bc.WriteBlockWithoutState(block, externTd); err != nil { if err = bc.WriteBlockWithoutState(block, externTd); err != nil {
@ -1480,9 +1481,6 @@ func (bc *BlockChain) writeHeader(header *types.Header) error {
// CurrentHeader retrieves the current head header of the canonical chain. The // CurrentHeader retrieves the current head header of the canonical chain. The
// header is retrieved from the HeaderChain's internal cache. // header is retrieved from the HeaderChain's internal cache.
func (bc *BlockChain) CurrentHeader() *types.Header { func (bc *BlockChain) CurrentHeader() *types.Header {
bc.mu.RLock()
defer bc.mu.RUnlock()
return bc.hc.CurrentHeader() return bc.hc.CurrentHeader()
} }

View file

@ -47,6 +47,7 @@ var (
headHeaderKey = []byte("LastHeader") headHeaderKey = []byte("LastHeader")
headBlockKey = []byte("LastBlock") headBlockKey = []byte("LastBlock")
headFastKey = []byte("LastFast") headFastKey = []byte("LastFast")
trieSyncKey = []byte("TrieSync")
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`). // Data item prefixes (use single byte to avoid mixing data types, avoid `i`).
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
@ -146,6 +147,16 @@ func GetHeadFastBlockHash(db DatabaseReader) common.Hash {
return common.BytesToHash(data) return common.BytesToHash(data)
} }
// GetTrieSyncProgress retrieves the number of tries nodes fast synced to allow
// reportinc correct numbers across restarts.
func GetTrieSyncProgress(db DatabaseReader) uint64 {
data, _ := db.Get(trieSyncKey)
if len(data) == 0 {
return 0
}
return new(big.Int).SetBytes(data).Uint64()
}
// GetHeaderRLP retrieves a block header in its raw RLP database encoding, or nil // GetHeaderRLP retrieves a block header in its raw RLP database encoding, or nil
// if the header's not found. // if the header's not found.
func GetHeaderRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue { func GetHeaderRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
@ -374,6 +385,15 @@ func WriteHeadFastBlockHash(db ethdb.Putter, hash common.Hash) error {
return nil return nil
} }
// WriteTrieSyncProgress stores the fast sync trie process counter to support
// retrieving it across restarts.
func WriteTrieSyncProgress(db ethdb.Putter, count uint64) error {
if err := db.Put(trieSyncKey, new(big.Int).SetUint64(count).Bytes()); err != nil {
log.Crit("Failed to store fast sync trie progress", "err", err)
}
return nil
}
// WriteHeader serializes a block header into the database. // WriteHeader serializes a block header into the database.
func WriteHeader(db ethdb.Putter, header *types.Header) error { func WriteHeader(db ethdb.Putter, header *types.Header) error {
data, err := rlp.EncodeToBytes(header) data, err := rlp.EncodeToBytes(header)

View file

@ -32,6 +32,7 @@ import (
"github.com/EthereumCommonwealth/go-callisto/log" "github.com/EthereumCommonwealth/go-callisto/log"
"github.com/EthereumCommonwealth/go-callisto/params" "github.com/EthereumCommonwealth/go-callisto/params"
"github.com/hashicorp/golang-lru" "github.com/hashicorp/golang-lru"
"sync/atomic"
) )
const ( const (
@ -51,7 +52,7 @@ type HeaderChain struct {
chainDb ethdb.Database chainDb ethdb.Database
genesisHeader *types.Header genesisHeader *types.Header
currentHeader *types.Header // Current head of the header chain (may be above the block chain!) currentHeader atomic.Value // Current head of the header chain (may be above the block chain!)
currentHeaderHash common.Hash // Hash of the current head of the header chain (prevent recomputing all the time) currentHeaderHash common.Hash // Hash of the current head of the header chain (prevent recomputing all the time)
headerCache *lru.Cache // Cache for the most recent block headers headerCache *lru.Cache // Cache for the most recent block headers
@ -95,13 +96,13 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c
return nil, ErrNoGenesis return nil, ErrNoGenesis
} }
hc.currentHeader = hc.genesisHeader hc.currentHeader.Store(hc.genesisHeader)
if head := GetHeadBlockHash(chainDb); head != (common.Hash{}) { if head := GetHeadBlockHash(chainDb); head != (common.Hash{}) {
if chead := hc.GetHeaderByHash(head); chead != nil { if chead := hc.GetHeaderByHash(head); chead != nil {
hc.currentHeader = chead hc.currentHeader.Store(chead)
} }
} }
hc.currentHeaderHash = hc.currentHeader.Hash() hc.currentHeaderHash = hc.CurrentHeader().Hash()
return hc, nil return hc, nil
} }
@ -139,7 +140,7 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
if ptd == nil { if ptd == nil {
return NonStatTy, consensus.ErrUnknownAncestor return NonStatTy, consensus.ErrUnknownAncestor
} }
localTd := hc.GetTd(hc.currentHeaderHash, hc.currentHeader.Number.Uint64()) localTd := hc.GetTd(hc.currentHeaderHash, hc.CurrentHeader().Number.Uint64())
externTd := new(big.Int).Add(header.Difficulty, ptd) externTd := new(big.Int).Add(header.Difficulty, ptd)
// Irrelevant of the canonical status, write the td and header to the database // Irrelevant of the canonical status, write the td and header to the database
@ -181,7 +182,8 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil { if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil {
log.Crit("Failed to insert head header hash", "err", err) log.Crit("Failed to insert head header hash", "err", err)
} }
hc.currentHeaderHash, hc.currentHeader = hash, types.CopyHeader(header) hc.currentHeaderHash = hash
hc.currentHeader.Store(types.CopyHeader(header))
status = CanonStatTy status = CanonStatTy
} else { } else {
@ -383,7 +385,7 @@ func (hc *HeaderChain) GetHeaderByNumber(number uint64) *types.Header {
// CurrentHeader retrieves the current head header of the canonical chain. The // CurrentHeader retrieves the current head header of the canonical chain. The
// header is retrieved from the HeaderChain's internal cache. // header is retrieved from the HeaderChain's internal cache.
func (hc *HeaderChain) CurrentHeader() *types.Header { func (hc *HeaderChain) CurrentHeader() *types.Header {
return hc.currentHeader return hc.currentHeader.Load().(*types.Header)
} }
// SetCurrentHeader sets the current head header of the canonical chain. // SetCurrentHeader sets the current head header of the canonical chain.
@ -391,7 +393,7 @@ func (hc *HeaderChain) SetCurrentHeader(head *types.Header) {
if err := WriteHeadHeaderHash(hc.chainDb, head.Hash()); err != nil { if err := WriteHeadHeaderHash(hc.chainDb, head.Hash()); err != nil {
log.Crit("Failed to insert head header hash", "err", err) log.Crit("Failed to insert head header hash", "err", err)
} }
hc.currentHeader = head hc.currentHeader.Store(head)
hc.currentHeaderHash = head.Hash() hc.currentHeaderHash = head.Hash()
} }
@ -403,19 +405,20 @@ type DeleteCallback func(common.Hash, uint64)
// will be deleted and the new one set. // will be deleted and the new one set.
func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) { func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) {
height := uint64(0) height := uint64(0)
if hc.currentHeader != nil {
height = hc.currentHeader.Number.Uint64() if hdr := hc.CurrentHeader(); hdr != nil {
height = hdr.Number.Uint64()
} }
for hc.currentHeader != nil && hc.currentHeader.Number.Uint64() > head { for hdr := hc.CurrentHeader(); hdr != nil && hdr.Number.Uint64() > head; hdr = hc.CurrentHeader() {
hash := hc.currentHeader.Hash() hash := hdr.Hash()
num := hc.currentHeader.Number.Uint64() num := hdr.Number.Uint64()
if delFn != nil { if delFn != nil {
delFn(hash, num) delFn(hash, num)
} }
DeleteHeader(hc.chainDb, hash, num) DeleteHeader(hc.chainDb, hash, num)
DeleteTd(hc.chainDb, hash, num) DeleteTd(hc.chainDb, hash, num)
hc.currentHeader = hc.GetHeader(hc.currentHeader.ParentHash, hc.currentHeader.Number.Uint64()-1) hc.currentHeader.Store(hc.GetHeader(hdr.ParentHash, hdr.Number.Uint64()-1))
} }
// Roll back the canonical chain numbering // Roll back the canonical chain numbering
for i := height; i > head; i-- { for i := height; i > head; i-- {
@ -426,10 +429,10 @@ func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) {
hc.tdCache.Purge() hc.tdCache.Purge()
hc.numberCache.Purge() hc.numberCache.Purge()
if hc.currentHeader == nil { if hc.CurrentHeader() == nil {
hc.currentHeader = hc.genesisHeader hc.currentHeader.Store(hc.genesisHeader)
} }
hc.currentHeaderHash = hc.currentHeader.Hash() hc.currentHeaderHash = hc.CurrentHeader().Hash()
if err := WriteHeadHeaderHash(hc.chainDb, hc.currentHeaderHash); err != nil { if err := WriteHeadHeaderHash(hc.chainDb, hc.currentHeaderHash); err != nil {
log.Crit("Failed to reset head header hash", "err", err) log.Crit("Failed to reset head header hash", "err", err)

View file

@ -251,26 +251,12 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) {
return common.LeftPadBytes(base.Exp(base, exp, mod).Bytes(), int(modLen)), nil return common.LeftPadBytes(base.Exp(base, exp, mod).Bytes(), int(modLen)), nil
} }
var (
// errNotOnCurve is returned if a point being unmarshalled as a bn256 elliptic
// curve point is not on the curve.
errNotOnCurve = errors.New("point not on elliptic curve")
// errInvalidCurvePoint is returned if a point being unmarshalled as a bn256
// elliptic curve point is invalid.
errInvalidCurvePoint = errors.New("invalid elliptic curve point")
)
// newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point, // newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point,
// returning it, or an error if the point is invalid. // returning it, or an error if the point is invalid.
func newCurvePoint(blob []byte) (*bn256.G1, error) { func newCurvePoint(blob []byte) (*bn256.G1, error) {
p, onCurve := new(bn256.G1).Unmarshal(blob) p := new(bn256.G1)
if !onCurve { if _, err := p.Unmarshal(blob); err != nil {
return nil, errNotOnCurve return nil, err
}
gx, gy, _, _ := p.CurvePoints()
if gx.Cmp(bn256.P) >= 0 || gy.Cmp(bn256.P) >= 0 {
return nil, errInvalidCurvePoint
} }
return p, nil return p, nil
} }
@ -278,14 +264,9 @@ func newCurvePoint(blob []byte) (*bn256.G1, error) {
// newTwistPoint unmarshals a binary blob into a bn256 elliptic curve point, // newTwistPoint unmarshals a binary blob into a bn256 elliptic curve point,
// returning it, or an error if the point is invalid. // returning it, or an error if the point is invalid.
func newTwistPoint(blob []byte) (*bn256.G2, error) { func newTwistPoint(blob []byte) (*bn256.G2, error) {
p, onCurve := new(bn256.G2).Unmarshal(blob) p := new(bn256.G2)
if !onCurve { if _, err := p.Unmarshal(blob); err != nil {
return nil, errNotOnCurve return nil, err
}
x2, y2, _, _ := p.CurvePoints()
if x2.Real().Cmp(bn256.P) >= 0 || x2.Imag().Cmp(bn256.P) >= 0 ||
y2.Real().Cmp(bn256.P) >= 0 || y2.Imag().Cmp(bn256.P) >= 0 {
return nil, errInvalidCurvePoint
} }
return p, nil return p, nil
} }

View file

@ -37,8 +37,6 @@ type Config struct {
// NoRecursion disabled Interpreter call, callcode, // NoRecursion disabled Interpreter call, callcode,
// delegate call and create. // delegate call and create.
NoRecursion bool NoRecursion bool
// Disable gas metering
DisableGasMetering bool
// Enable recording of SHA3/keccak preimages // Enable recording of SHA3/keccak preimages
EnablePreimageRecording bool EnablePreimageRecording bool
// JumpTable contains the EVM instruction table. This // JumpTable contains the EVM instruction table. This
@ -189,15 +187,12 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er
return nil, errGasUintOverflow return nil, errGasUintOverflow
} }
} }
if !in.cfg.DisableGasMetering {
// consume the gas and return an error if not enough gas is available. // consume the gas and return an error if not enough gas is available.
// cost is explicitly set so that the capture state defer method cas get the proper cost // cost is explicitly set so that the capture state defer method cas get the proper cost
cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize) cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize)
if err != nil || !contract.UseGas(cost) { if err != nil || !contract.UseGas(cost) {
return nil, ErrOutOfGas return nil, ErrOutOfGas
} }
}
if memorySize > 0 { if memorySize > 0 {
mem.Resize(memorySize) mem.Resize(memorySize)
} }

View file

@ -0,0 +1,63 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build amd64,!appengine,!gccgo
// Package bn256 implements the Optimal Ate pairing over a 256-bit Barreto-Naehrig curve.
package bn256
import (
"math/big"
"github.com/ethereum/go-ethereum/crypto/bn256/cloudflare"
)
// G1 is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.
type G1 struct {
bn256.G1
}
// Add sets e to a+b and then returns e.
func (e *G1) Add(a, b *G1) *G1 {
e.G1.Add(&a.G1, &b.G1)
return e
}
// ScalarMult sets e to a*k and then returns e.
func (e *G1) ScalarMult(a *G1, k *big.Int) *G1 {
e.G1.ScalarMult(&a.G1, k)
return e
}
// G2 is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.
type G2 struct {
bn256.G2
}
// PairingCheck calculates the Optimal Ate pairing for a set of points.
func PairingCheck(a []*G1, b []*G2) bool {
as := make([]*bn256.G1, len(a))
for i, p := range a {
as[i] = &p.G1
}
bs := make([]*bn256.G2, len(b))
for i, p := range b {
bs[i] = &p.G2
}
return bn256.PairingCheck(as, bs)
}

View file

@ -0,0 +1,63 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build !amd64 appengine gccgo
// Package bn256 implements the Optimal Ate pairing over a 256-bit Barreto-Naehrig curve.
package bn256
import (
"math/big"
"github.com/ethereum/go-ethereum/crypto/bn256/google"
)
// G1 is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.
type G1 struct {
bn256.G1
}
// Add sets e to a+b and then returns e.
func (e *G1) Add(a, b *G1) *G1 {
e.G1.Add(&a.G1, &b.G1)
return e
}
// ScalarMult sets e to a*k and then returns e.
func (e *G1) ScalarMult(a *G1, k *big.Int) *G1 {
e.G1.ScalarMult(&a.G1, k)
return e
}
// G2 is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.
type G2 struct {
bn256.G2
}
// PairingCheck calculates the Optimal Ate pairing for a set of points.
func PairingCheck(a []*G1, b []*G2) bool {
as := make([]*bn256.G1, len(a))
for i, p := range a {
as[i] = &p.G1
}
bs := make([]*bn256.G2, len(b))
for i, p := range b {
bs[i] = &p.G2
}
return bn256.PairingCheck(as, bs)
}

View file

@ -0,0 +1,481 @@
// Package bn256 implements a particular bilinear group at the 128-bit security
// level.
//
// Bilinear groups are the basis of many of the new cryptographic protocols that
// have been proposed over the past decade. They consist of a triplet of groups
// (G₁, G₂ and GT) such that there exists a function e(g₁ˣ,g₂ʸ)=gTˣʸ (where gₓ
// is a generator of the respective group). That function is called a pairing
// function.
//
// This package specifically implements the Optimal Ate pairing over a 256-bit
// Barreto-Naehrig curve as described in
// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is compatible
// with the implementation described in that paper.
package bn256
import (
"crypto/rand"
"errors"
"io"
"math/big"
)
func randomK(r io.Reader) (k *big.Int, err error) {
for {
k, err = rand.Int(r, Order)
if k.Sign() > 0 || err != nil {
return
}
}
}
// G1 is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.
type G1 struct {
p *curvePoint
}
// RandomG1 returns x and g₁ˣ where x is a random, non-zero number read from r.
func RandomG1(r io.Reader) (*big.Int, *G1, error) {
k, err := randomK(r)
if err != nil {
return nil, nil, err
}
return k, new(G1).ScalarBaseMult(k), nil
}
func (g *G1) String() string {
return "bn256.G1" + g.p.String()
}
// ScalarBaseMult sets e to g*k where g is the generator of the group and then
// returns e.
func (e *G1) ScalarBaseMult(k *big.Int) *G1 {
if e.p == nil {
e.p = &curvePoint{}
}
e.p.Mul(curveGen, k)
return e
}
// ScalarMult sets e to a*k and then returns e.
func (e *G1) ScalarMult(a *G1, k *big.Int) *G1 {
if e.p == nil {
e.p = &curvePoint{}
}
e.p.Mul(a.p, k)
return e
}
// Add sets e to a+b and then returns e.
func (e *G1) Add(a, b *G1) *G1 {
if e.p == nil {
e.p = &curvePoint{}
}
e.p.Add(a.p, b.p)
return e
}
// Neg sets e to -a and then returns e.
func (e *G1) Neg(a *G1) *G1 {
if e.p == nil {
e.p = &curvePoint{}
}
e.p.Neg(a.p)
return e
}
// Set sets e to a and then returns e.
func (e *G1) Set(a *G1) *G1 {
if e.p == nil {
e.p = &curvePoint{}
}
e.p.Set(a.p)
return e
}
// Marshal converts e to a byte slice.
func (e *G1) Marshal() []byte {
// Each value is a 256-bit number.
const numBytes = 256 / 8
e.p.MakeAffine()
ret := make([]byte, numBytes*2)
if e.p.IsInfinity() {
return ret
}
temp := &gfP{}
montDecode(temp, &e.p.x)
temp.Marshal(ret)
montDecode(temp, &e.p.y)
temp.Marshal(ret[numBytes:])
return ret
}
// Unmarshal sets e to the result of converting the output of Marshal back into
// a group element and then returns e.
func (e *G1) Unmarshal(m []byte) ([]byte, error) {
// Each value is a 256-bit number.
const numBytes = 256 / 8
if len(m) < 2*numBytes {
return nil, errors.New("bn256: not enough data")
}
// Unmarshal the points and check their caps
if e.p == nil {
e.p = &curvePoint{}
} else {
e.p.x, e.p.y = gfP{0}, gfP{0}
}
var err error
if err = e.p.x.Unmarshal(m); err != nil {
return nil, err
}
if err = e.p.y.Unmarshal(m[numBytes:]); err != nil {
return nil, err
}
// Encode into Montgomery form and ensure it's on the curve
montEncode(&e.p.x, &e.p.x)
montEncode(&e.p.y, &e.p.y)
zero := gfP{0}
if e.p.x == zero && e.p.y == zero {
// This is the point at infinity.
e.p.y = *newGFp(1)
e.p.z = gfP{0}
e.p.t = gfP{0}
} else {
e.p.z = *newGFp(1)
e.p.t = *newGFp(1)
if !e.p.IsOnCurve() {
return nil, errors.New("bn256: malformed point")
}
}
return m[2*numBytes:], nil
}
// G2 is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.
type G2 struct {
p *twistPoint
}
// RandomG2 returns x and g₂ˣ where x is a random, non-zero number read from r.
func RandomG2(r io.Reader) (*big.Int, *G2, error) {
k, err := randomK(r)
if err != nil {
return nil, nil, err
}
return k, new(G2).ScalarBaseMult(k), nil
}
func (e *G2) String() string {
return "bn256.G2" + e.p.String()
}
// ScalarBaseMult sets e to g*k where g is the generator of the group and then
// returns out.
func (e *G2) ScalarBaseMult(k *big.Int) *G2 {
if e.p == nil {
e.p = &twistPoint{}
}
e.p.Mul(twistGen, k)
return e
}
// ScalarMult sets e to a*k and then returns e.
func (e *G2) ScalarMult(a *G2, k *big.Int) *G2 {
if e.p == nil {
e.p = &twistPoint{}
}
e.p.Mul(a.p, k)
return e
}
// Add sets e to a+b and then returns e.
func (e *G2) Add(a, b *G2) *G2 {
if e.p == nil {
e.p = &twistPoint{}
}
e.p.Add(a.p, b.p)
return e
}
// Neg sets e to -a and then returns e.
func (e *G2) Neg(a *G2) *G2 {
if e.p == nil {
e.p = &twistPoint{}
}
e.p.Neg(a.p)
return e
}
// Set sets e to a and then returns e.
func (e *G2) Set(a *G2) *G2 {
if e.p == nil {
e.p = &twistPoint{}
}
e.p.Set(a.p)
return e
}
// Marshal converts e into a byte slice.
func (e *G2) Marshal() []byte {
// Each value is a 256-bit number.
const numBytes = 256 / 8
if e.p == nil {
e.p = &twistPoint{}
}
e.p.MakeAffine()
ret := make([]byte, numBytes*4)
if e.p.IsInfinity() {
return ret
}
temp := &gfP{}
montDecode(temp, &e.p.x.x)
temp.Marshal(ret)
montDecode(temp, &e.p.x.y)
temp.Marshal(ret[numBytes:])
montDecode(temp, &e.p.y.x)
temp.Marshal(ret[2*numBytes:])
montDecode(temp, &e.p.y.y)
temp.Marshal(ret[3*numBytes:])
return ret
}
// Unmarshal sets e to the result of converting the output of Marshal back into
// a group element and then returns e.
func (e *G2) Unmarshal(m []byte) ([]byte, error) {
// Each value is a 256-bit number.
const numBytes = 256 / 8
if len(m) < 4*numBytes {
return nil, errors.New("bn256: not enough data")
}
// Unmarshal the points and check their caps
if e.p == nil {
e.p = &twistPoint{}
}
var err error
if err = e.p.x.x.Unmarshal(m); err != nil {
return nil, err
}
if err = e.p.x.y.Unmarshal(m[numBytes:]); err != nil {
return nil, err
}
if err = e.p.y.x.Unmarshal(m[2*numBytes:]); err != nil {
return nil, err
}
if err = e.p.y.y.Unmarshal(m[3*numBytes:]); err != nil {
return nil, err
}
// Encode into Montgomery form and ensure it's on the curve
montEncode(&e.p.x.x, &e.p.x.x)
montEncode(&e.p.x.y, &e.p.x.y)
montEncode(&e.p.y.x, &e.p.y.x)
montEncode(&e.p.y.y, &e.p.y.y)
if e.p.x.IsZero() && e.p.y.IsZero() {
// This is the point at infinity.
e.p.y.SetOne()
e.p.z.SetZero()
e.p.t.SetZero()
} else {
e.p.z.SetOne()
e.p.t.SetOne()
if !e.p.IsOnCurve() {
return nil, errors.New("bn256: malformed point")
}
}
return m[4*numBytes:], nil
}
// GT is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.
type GT struct {
p *gfP12
}
// Pair calculates an Optimal Ate pairing.
func Pair(g1 *G1, g2 *G2) *GT {
return &GT{optimalAte(g2.p, g1.p)}
}
// PairingCheck calculates the Optimal Ate pairing for a set of points.
func PairingCheck(a []*G1, b []*G2) bool {
acc := new(gfP12)
acc.SetOne()
for i := 0; i < len(a); i++ {
if a[i].p.IsInfinity() || b[i].p.IsInfinity() {
continue
}
acc.Mul(acc, miller(b[i].p, a[i].p))
}
return finalExponentiation(acc).IsOne()
}
// Miller applies Miller's algorithm, which is a bilinear function from the
// source groups to F_p^12. Miller(g1, g2).Finalize() is equivalent to Pair(g1,
// g2).
func Miller(g1 *G1, g2 *G2) *GT {
return &GT{miller(g2.p, g1.p)}
}
func (g *GT) String() string {
return "bn256.GT" + g.p.String()
}
// ScalarMult sets e to a*k and then returns e.
func (e *GT) ScalarMult(a *GT, k *big.Int) *GT {
if e.p == nil {
e.p = &gfP12{}
}
e.p.Exp(a.p, k)
return e
}
// Add sets e to a+b and then returns e.
func (e *GT) Add(a, b *GT) *GT {
if e.p == nil {
e.p = &gfP12{}
}
e.p.Mul(a.p, b.p)
return e
}
// Neg sets e to -a and then returns e.
func (e *GT) Neg(a *GT) *GT {
if e.p == nil {
e.p = &gfP12{}
}
e.p.Conjugate(a.p)
return e
}
// Set sets e to a and then returns e.
func (e *GT) Set(a *GT) *GT {
if e.p == nil {
e.p = &gfP12{}
}
e.p.Set(a.p)
return e
}
// Finalize is a linear function from F_p^12 to GT.
func (e *GT) Finalize() *GT {
ret := finalExponentiation(e.p)
e.p.Set(ret)
return e
}
// Marshal converts e into a byte slice.
func (e *GT) Marshal() []byte {
// Each value is a 256-bit number.
const numBytes = 256 / 8
ret := make([]byte, numBytes*12)
temp := &gfP{}
montDecode(temp, &e.p.x.x.x)
temp.Marshal(ret)
montDecode(temp, &e.p.x.x.y)
temp.Marshal(ret[numBytes:])
montDecode(temp, &e.p.x.y.x)
temp.Marshal(ret[2*numBytes:])
montDecode(temp, &e.p.x.y.y)
temp.Marshal(ret[3*numBytes:])
montDecode(temp, &e.p.x.z.x)
temp.Marshal(ret[4*numBytes:])
montDecode(temp, &e.p.x.z.y)
temp.Marshal(ret[5*numBytes:])
montDecode(temp, &e.p.y.x.x)
temp.Marshal(ret[6*numBytes:])
montDecode(temp, &e.p.y.x.y)
temp.Marshal(ret[7*numBytes:])
montDecode(temp, &e.p.y.y.x)
temp.Marshal(ret[8*numBytes:])
montDecode(temp, &e.p.y.y.y)
temp.Marshal(ret[9*numBytes:])
montDecode(temp, &e.p.y.z.x)
temp.Marshal(ret[10*numBytes:])
montDecode(temp, &e.p.y.z.y)
temp.Marshal(ret[11*numBytes:])
return ret
}
// Unmarshal sets e to the result of converting the output of Marshal back into
// a group element and then returns e.
func (e *GT) Unmarshal(m []byte) ([]byte, error) {
// Each value is a 256-bit number.
const numBytes = 256 / 8
if len(m) < 12*numBytes {
return nil, errors.New("bn256: not enough data")
}
if e.p == nil {
e.p = &gfP12{}
}
var err error
if err = e.p.x.x.x.Unmarshal(m); err != nil {
return nil, err
}
if err = e.p.x.x.y.Unmarshal(m[numBytes:]); err != nil {
return nil, err
}
if err = e.p.x.y.x.Unmarshal(m[2*numBytes:]); err != nil {
return nil, err
}
if err = e.p.x.y.y.Unmarshal(m[3*numBytes:]); err != nil {
return nil, err
}
if err = e.p.x.z.x.Unmarshal(m[4*numBytes:]); err != nil {
return nil, err
}
if err = e.p.x.z.y.Unmarshal(m[5*numBytes:]); err != nil {
return nil, err
}
if err = e.p.y.x.x.Unmarshal(m[6*numBytes:]); err != nil {
return nil, err
}
if err = e.p.y.x.y.Unmarshal(m[7*numBytes:]); err != nil {
return nil, err
}
if err = e.p.y.y.x.Unmarshal(m[8*numBytes:]); err != nil {
return nil, err
}
if err = e.p.y.y.y.Unmarshal(m[9*numBytes:]); err != nil {
return nil, err
}
if err = e.p.y.z.x.Unmarshal(m[10*numBytes:]); err != nil {
return nil, err
}
if err = e.p.y.z.y.Unmarshal(m[11*numBytes:]); err != nil {
return nil, err
}
montEncode(&e.p.x.x.x, &e.p.x.x.x)
montEncode(&e.p.x.x.y, &e.p.x.x.y)
montEncode(&e.p.x.y.x, &e.p.x.y.x)
montEncode(&e.p.x.y.y, &e.p.x.y.y)
montEncode(&e.p.x.z.x, &e.p.x.z.x)
montEncode(&e.p.x.z.y, &e.p.x.z.y)
montEncode(&e.p.y.x.x, &e.p.y.x.x)
montEncode(&e.p.y.x.y, &e.p.y.x.y)
montEncode(&e.p.y.y.x, &e.p.y.y.x)
montEncode(&e.p.y.y.y, &e.p.y.y.y)
montEncode(&e.p.y.z.x, &e.p.y.z.x)
montEncode(&e.p.y.z.y, &e.p.y.z.y)
return m[12*numBytes:], nil
}

View file

@ -0,0 +1,118 @@
// +build amd64,!appengine,!gccgo
package bn256
import (
"bytes"
"crypto/rand"
"testing"
)
func TestG1Marshal(t *testing.T) {
_, Ga, err := RandomG1(rand.Reader)
if err != nil {
t.Fatal(err)
}
ma := Ga.Marshal()
Gb := new(G1)
_, err = Gb.Unmarshal(ma)
if err != nil {
t.Fatal(err)
}
mb := Gb.Marshal()
if !bytes.Equal(ma, mb) {
t.Fatal("bytes are different")
}
}
func TestG2Marshal(t *testing.T) {
_, Ga, err := RandomG2(rand.Reader)
if err != nil {
t.Fatal(err)
}
ma := Ga.Marshal()
Gb := new(G2)
_, err = Gb.Unmarshal(ma)
if err != nil {
t.Fatal(err)
}
mb := Gb.Marshal()
if !bytes.Equal(ma, mb) {
t.Fatal("bytes are different")
}
}
func TestBilinearity(t *testing.T) {
for i := 0; i < 2; i++ {
a, p1, _ := RandomG1(rand.Reader)
b, p2, _ := RandomG2(rand.Reader)
e1 := Pair(p1, p2)
e2 := Pair(&G1{curveGen}, &G2{twistGen})
e2.ScalarMult(e2, a)
e2.ScalarMult(e2, b)
if *e1.p != *e2.p {
t.Fatalf("bad pairing result: %s", e1)
}
}
}
func TestTripartiteDiffieHellman(t *testing.T) {
a, _ := rand.Int(rand.Reader, Order)
b, _ := rand.Int(rand.Reader, Order)
c, _ := rand.Int(rand.Reader, Order)
pa, pb, pc := new(G1), new(G1), new(G1)
qa, qb, qc := new(G2), new(G2), new(G2)
pa.Unmarshal(new(G1).ScalarBaseMult(a).Marshal())
qa.Unmarshal(new(G2).ScalarBaseMult(a).Marshal())
pb.Unmarshal(new(G1).ScalarBaseMult(b).Marshal())
qb.Unmarshal(new(G2).ScalarBaseMult(b).Marshal())
pc.Unmarshal(new(G1).ScalarBaseMult(c).Marshal())
qc.Unmarshal(new(G2).ScalarBaseMult(c).Marshal())
k1 := Pair(pb, qc)
k1.ScalarMult(k1, a)
k1Bytes := k1.Marshal()
k2 := Pair(pc, qa)
k2.ScalarMult(k2, b)
k2Bytes := k2.Marshal()
k3 := Pair(pa, qb)
k3.ScalarMult(k3, c)
k3Bytes := k3.Marshal()
if !bytes.Equal(k1Bytes, k2Bytes) || !bytes.Equal(k2Bytes, k3Bytes) {
t.Errorf("keys didn't agree")
}
}
func BenchmarkG1(b *testing.B) {
x, _ := rand.Int(rand.Reader, Order)
b.ResetTimer()
for i := 0; i < b.N; i++ {
new(G1).ScalarBaseMult(x)
}
}
func BenchmarkG2(b *testing.B) {
x, _ := rand.Int(rand.Reader, Order)
b.ResetTimer()
for i := 0; i < b.N; i++ {
new(G2).ScalarBaseMult(x)
}
}
func BenchmarkPairing(b *testing.B) {
for i := 0; i < b.N; i++ {
Pair(&G1{curveGen}, &G2{twistGen})
}
}

View file

@ -0,0 +1,59 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bn256
import (
"math/big"
)
func bigFromBase10(s string) *big.Int {
n, _ := new(big.Int).SetString(s, 10)
return n
}
// u is the BN parameter that determines the prime: 1868033³.
var u = bigFromBase10("4965661367192848881")
// Order is the number of elements in both G₁ and G₂: 36u⁴+36u³+18u²+6u+1.
var Order = bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495617")
// P is a prime over which we form a basic field: 36u⁴+36u³+24u²+6u+1.
var P = bigFromBase10("21888242871839275222246405745257275088696311157297823662689037894645226208583")
// p2 is p, represented as little-endian 64-bit words.
var p2 = [4]uint64{0x3c208c16d87cfd47, 0x97816a916871ca8d, 0xb85045b68181585d, 0x30644e72e131a029}
// np is the negative inverse of p, mod 2^256.
var np = [4]uint64{0x87d20782e4866389, 0x9ede7d651eca6ac9, 0xd8afcbd01833da80, 0xf57a22b791888c6b}
// rN1 is R^-1 where R = 2^256 mod p.
var rN1 = &gfP{0xed84884a014afa37, 0xeb2022850278edf8, 0xcf63e9cfb74492d9, 0x2e67157159e5c639}
// r2 is R^2 where R = 2^256 mod p.
var r2 = &gfP{0xf32cfc5b538afa89, 0xb5e71911d44501fb, 0x47ab1eff0a417ff6, 0x06d89f71cab8351f}
// r3 is R^3 where R = 2^256 mod p.
var r3 = &gfP{0xb1cd6dafda1530df, 0x62f210e6a7283db6, 0xef7f0b0c0ada0afb, 0x20fd6e902d592544}
// xiToPMinus1Over6 is ξ^((p-1)/6) where ξ = i+9.
var xiToPMinus1Over6 = &gfP2{gfP{0xa222ae234c492d72, 0xd00f02a4565de15b, 0xdc2ff3a253dfc926, 0x10a75716b3899551}, gfP{0xaf9ba69633144907, 0xca6b1d7387afb78a, 0x11bded5ef08a2087, 0x02f34d751a1f3a7c}}
// xiToPMinus1Over3 is ξ^((p-1)/3) where ξ = i+9.
var xiToPMinus1Over3 = &gfP2{gfP{0x6e849f1ea0aa4757, 0xaa1c7b6d89f89141, 0xb6e713cdfae0ca3a, 0x26694fbb4e82ebc3}, gfP{0xb5773b104563ab30, 0x347f91c8a9aa6454, 0x7a007127242e0991, 0x1956bcd8118214ec}}
// xiToPMinus1Over2 is ξ^((p-1)/2) where ξ = i+9.
var xiToPMinus1Over2 = &gfP2{gfP{0xa1d77ce45ffe77c7, 0x07affd117826d1db, 0x6d16bd27bb7edc6b, 0x2c87200285defecc}, gfP{0xe4bbdd0c2936b629, 0xbb30f162e133bacb, 0x31a9d1b6f9645366, 0x253570bea500f8dd}}
// xiToPSquaredMinus1Over3 is ξ^((p²-1)/3) where ξ = i+9.
var xiToPSquaredMinus1Over3 = &gfP{0x3350c88e13e80b9c, 0x7dce557cdb5e56b9, 0x6001b4b8b615564a, 0x2682e617020217e0}
// xiTo2PSquaredMinus2Over3 is ξ^((2p²-2)/3) where ξ = i+9 (a cubic root of unity, mod p).
var xiTo2PSquaredMinus2Over3 = &gfP{0x71930c11d782e155, 0xa6bb947cffbe3323, 0xaa303344d4741444, 0x2c3b3f0d26594943}
// xiToPSquaredMinus1Over6 is ξ^((1p²-1)/6) where ξ = i+9 (a cubic root of -1, mod p).
var xiToPSquaredMinus1Over6 = &gfP{0xca8d800500fa1bf2, 0xf0c5d61468b39769, 0x0e201271ad0d4418, 0x04290f65bad856e6}
// xiTo2PMinus2Over3 is ξ^((2p-2)/3) where ξ = i+9.
var xiTo2PMinus2Over3 = &gfP2{gfP{0x5dddfd154bd8c949, 0x62cb29a5a4445b60, 0x37bc870a0c7dd2b9, 0x24830a9d3171f0fd}, gfP{0x7361d77f843abe92, 0xa5bb2bd3273411fb, 0x9c941f314b3e2399, 0x15df9cddbb9fd3ec}}

View file

@ -0,0 +1,229 @@
package bn256
import (
"math/big"
)
// curvePoint implements the elliptic curve y²=x³+3. Points are kept in Jacobian
// form and t=z² when valid. G₁ is the set of points of this curve on GF(p).
type curvePoint struct {
x, y, z, t gfP
}
var curveB = newGFp(3)
// curveGen is the generator of G₁.
var curveGen = &curvePoint{
x: *newGFp(1),
y: *newGFp(2),
z: *newGFp(1),
t: *newGFp(1),
}
func (c *curvePoint) String() string {
c.MakeAffine()
x, y := &gfP{}, &gfP{}
montDecode(x, &c.x)
montDecode(y, &c.y)
return "(" + x.String() + ", " + y.String() + ")"
}
func (c *curvePoint) Set(a *curvePoint) {
c.x.Set(&a.x)
c.y.Set(&a.y)
c.z.Set(&a.z)
c.t.Set(&a.t)
}
// IsOnCurve returns true iff c is on the curve.
func (c *curvePoint) IsOnCurve() bool {
c.MakeAffine()
if c.IsInfinity() {
return true
}
y2, x3 := &gfP{}, &gfP{}
gfpMul(y2, &c.y, &c.y)
gfpMul(x3, &c.x, &c.x)
gfpMul(x3, x3, &c.x)
gfpAdd(x3, x3, curveB)
return *y2 == *x3
}
func (c *curvePoint) SetInfinity() {
c.x = gfP{0}
c.y = *newGFp(1)
c.z = gfP{0}
c.t = gfP{0}
}
func (c *curvePoint) IsInfinity() bool {
return c.z == gfP{0}
}
func (c *curvePoint) Add(a, b *curvePoint) {
if a.IsInfinity() {
c.Set(b)
return
}
if b.IsInfinity() {
c.Set(a)
return
}
// See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/addition/add-2007-bl.op3
// Normalize the points by replacing a = [x1:y1:z1] and b = [x2:y2:z2]
// by [u1:s1:z1·z2] and [u2:s2:z1·z2]
// where u1 = x1·z2², s1 = y1·z2³ and u1 = x2·z1², s2 = y2·z1³
z12, z22 := &gfP{}, &gfP{}
gfpMul(z12, &a.z, &a.z)
gfpMul(z22, &b.z, &b.z)
u1, u2 := &gfP{}, &gfP{}
gfpMul(u1, &a.x, z22)
gfpMul(u2, &b.x, z12)
t, s1 := &gfP{}, &gfP{}
gfpMul(t, &b.z, z22)
gfpMul(s1, &a.y, t)
s2 := &gfP{}
gfpMul(t, &a.z, z12)
gfpMul(s2, &b.y, t)
// Compute x = (2h)²(s²-u1-u2)
// where s = (s2-s1)/(u2-u1) is the slope of the line through
// (u1,s1) and (u2,s2). The extra factor 2h = 2(u2-u1) comes from the value of z below.
// This is also:
// 4(s2-s1)² - 4h²(u1+u2) = 4(s2-s1)² - 4h³ - 4h²(2u1)
// = r² - j - 2v
// with the notations below.
h := &gfP{}
gfpSub(h, u2, u1)
xEqual := *h == gfP{0}
gfpAdd(t, h, h)
// i = 4h²
i := &gfP{}
gfpMul(i, t, t)
// j = 4h³
j := &gfP{}
gfpMul(j, h, i)
gfpSub(t, s2, s1)
yEqual := *t == gfP{0}
if xEqual && yEqual {
c.Double(a)
return
}
r := &gfP{}
gfpAdd(r, t, t)
v := &gfP{}
gfpMul(v, u1, i)
// t4 = 4(s2-s1)²
t4, t6 := &gfP{}, &gfP{}
gfpMul(t4, r, r)
gfpAdd(t, v, v)
gfpSub(t6, t4, j)
gfpSub(&c.x, t6, t)
// Set y = -(2h)³(s1 + s*(x/4h²-u1))
// This is also
// y = - 2·s1·j - (s2-s1)(2x - 2i·u1) = r(v-x) - 2·s1·j
gfpSub(t, v, &c.x) // t7
gfpMul(t4, s1, j) // t8
gfpAdd(t6, t4, t4) // t9
gfpMul(t4, r, t) // t10
gfpSub(&c.y, t4, t6)
// Set z = 2(u2-u1)·z1·z2 = 2h·z1·z2
gfpAdd(t, &a.z, &b.z) // t11
gfpMul(t4, t, t) // t12
gfpSub(t, t4, z12) // t13
gfpSub(t4, t, z22) // t14
gfpMul(&c.z, t4, h)
}
func (c *curvePoint) Double(a *curvePoint) {
// See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/doubling/dbl-2009-l.op3
A, B, C := &gfP{}, &gfP{}, &gfP{}
gfpMul(A, &a.x, &a.x)
gfpMul(B, &a.y, &a.y)
gfpMul(C, B, B)
t, t2 := &gfP{}, &gfP{}
gfpAdd(t, &a.x, B)
gfpMul(t2, t, t)
gfpSub(t, t2, A)
gfpSub(t2, t, C)
d, e, f := &gfP{}, &gfP{}, &gfP{}
gfpAdd(d, t2, t2)
gfpAdd(t, A, A)
gfpAdd(e, t, A)
gfpMul(f, e, e)
gfpAdd(t, d, d)
gfpSub(&c.x, f, t)
gfpAdd(t, C, C)
gfpAdd(t2, t, t)
gfpAdd(t, t2, t2)
gfpSub(&c.y, d, &c.x)
gfpMul(t2, e, &c.y)
gfpSub(&c.y, t2, t)
gfpMul(t, &a.y, &a.z)
gfpAdd(&c.z, t, t)
}
func (c *curvePoint) Mul(a *curvePoint, scalar *big.Int) {
sum, t := &curvePoint{}, &curvePoint{}
sum.SetInfinity()
for i := scalar.BitLen(); i >= 0; i-- {
t.Double(sum)
if scalar.Bit(i) != 0 {
sum.Add(t, a)
} else {
sum.Set(t)
}
}
c.Set(sum)
}
func (c *curvePoint) MakeAffine() {
if c.z == *newGFp(1) {
return
} else if c.z == *newGFp(0) {
c.x = gfP{0}
c.y = *newGFp(1)
c.t = gfP{0}
return
}
zInv := &gfP{}
zInv.Invert(&c.z)
t, zInv2 := &gfP{}, &gfP{}
gfpMul(t, &c.y, zInv)
gfpMul(zInv2, zInv, zInv)
gfpMul(&c.x, &c.x, zInv2)
gfpMul(&c.y, t, zInv2)
c.z = *newGFp(1)
c.t = *newGFp(1)
}
func (c *curvePoint) Neg(a *curvePoint) {
c.x.Set(&a.x)
gfpNeg(&c.y, &a.y)
c.z.Set(&a.z)
c.t = gfP{0}
}

View file

@ -0,0 +1,45 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build amd64,!appengine,!gccgo
package bn256
import (
"crypto/rand"
)
func ExamplePair() {
// This implements the tripartite Diffie-Hellman algorithm from "A One
// Round Protocol for Tripartite Diffie-Hellman", A. Joux.
// http://www.springerlink.com/content/cddc57yyva0hburb/fulltext.pdf
// Each of three parties, a, b and c, generate a private value.
a, _ := rand.Int(rand.Reader, Order)
b, _ := rand.Int(rand.Reader, Order)
c, _ := rand.Int(rand.Reader, Order)
// Then each party calculates g₁ and g₂ times their private value.
pa := new(G1).ScalarBaseMult(a)
qa := new(G2).ScalarBaseMult(a)
pb := new(G1).ScalarBaseMult(b)
qb := new(G2).ScalarBaseMult(b)
pc := new(G1).ScalarBaseMult(c)
qc := new(G2).ScalarBaseMult(c)
// Now each party exchanges its public values with the other two and
// all parties can calculate the shared key.
k1 := Pair(pb, qc)
k1.ScalarMult(k1, a)
k2 := Pair(pc, qa)
k2.ScalarMult(k2, b)
k3 := Pair(pa, qb)
k3.ScalarMult(k3, c)
// k1, k2 and k3 will all be equal.
}

View file

@ -0,0 +1,81 @@
package bn256
import (
"errors"
"fmt"
)
type gfP [4]uint64
func newGFp(x int64) (out *gfP) {
if x >= 0 {
out = &gfP{uint64(x)}
} else {
out = &gfP{uint64(-x)}
gfpNeg(out, out)
}
montEncode(out, out)
return out
}
func (e *gfP) String() string {
return fmt.Sprintf("%16.16x%16.16x%16.16x%16.16x", e[3], e[2], e[1], e[0])
}
func (e *gfP) Set(f *gfP) {
e[0] = f[0]
e[1] = f[1]
e[2] = f[2]
e[3] = f[3]
}
func (e *gfP) Invert(f *gfP) {
bits := [4]uint64{0x3c208c16d87cfd45, 0x97816a916871ca8d, 0xb85045b68181585d, 0x30644e72e131a029}
sum, power := &gfP{}, &gfP{}
sum.Set(rN1)
power.Set(f)
for word := 0; word < 4; word++ {
for bit := uint(0); bit < 64; bit++ {
if (bits[word]>>bit)&1 == 1 {
gfpMul(sum, sum, power)
}
gfpMul(power, power, power)
}
}
gfpMul(sum, sum, r3)
e.Set(sum)
}
func (e *gfP) Marshal(out []byte) {
for w := uint(0); w < 4; w++ {
for b := uint(0); b < 8; b++ {
out[8*w+b] = byte(e[3-w] >> (56 - 8*b))
}
}
}
func (e *gfP) Unmarshal(in []byte) error {
// Unmarshal the bytes into little endian form
for w := uint(0); w < 4; w++ {
for b := uint(0); b < 8; b++ {
e[3-w] += uint64(in[8*w+b]) << (56 - 8*b)
}
}
// Ensure the point respects the curve modulus
for i := 3; i >= 0; i-- {
if e[i] < p2[i] {
return nil
}
if e[i] > p2[i] {
return errors.New("bn256: coordinate exceeds modulus")
}
}
return errors.New("bn256: coordinate equals modulus")
}
func montEncode(c, a *gfP) { gfpMul(c, a, r2) }
func montDecode(c, a *gfP) { gfpMul(c, a, &gfP{1}) }

View file

@ -0,0 +1,32 @@
#define storeBlock(a0,a1,a2,a3, r) \
MOVQ a0, 0+r \
MOVQ a1, 8+r \
MOVQ a2, 16+r \
MOVQ a3, 24+r
#define loadBlock(r, a0,a1,a2,a3) \
MOVQ 0+r, a0 \
MOVQ 8+r, a1 \
MOVQ 16+r, a2 \
MOVQ 24+r, a3
#define gfpCarry(a0,a1,a2,a3,a4, b0,b1,b2,b3,b4) \
\ // b = a-p
MOVQ a0, b0 \
MOVQ a1, b1 \
MOVQ a2, b2 \
MOVQ a3, b3 \
MOVQ a4, b4 \
\
SUBQ ·p2+0(SB), b0 \
SBBQ ·p2+8(SB), b1 \
SBBQ ·p2+16(SB), b2 \
SBBQ ·p2+24(SB), b3 \
SBBQ $0, b4 \
\
\ // if b is negative then return a
\ // else return b
CMOVQCC b0, a0 \
CMOVQCC b1, a1 \
CMOVQCC b2, a2 \
CMOVQCC b3, a3

View file

@ -0,0 +1,160 @@
package bn256
// For details of the algorithms used, see "Multiplication and Squaring on
// Pairing-Friendly Fields, Devegili et al.
// http://eprint.iacr.org/2006/471.pdf.
import (
"math/big"
)
// gfP12 implements the field of size p¹² as a quadratic extension of gfP6
// where ω²=τ.
type gfP12 struct {
x, y gfP6 // value is xω + y
}
func (e *gfP12) String() string {
return "(" + e.x.String() + "," + e.y.String() + ")"
}
func (e *gfP12) Set(a *gfP12) *gfP12 {
e.x.Set(&a.x)
e.y.Set(&a.y)
return e
}
func (e *gfP12) SetZero() *gfP12 {
e.x.SetZero()
e.y.SetZero()
return e
}
func (e *gfP12) SetOne() *gfP12 {
e.x.SetZero()
e.y.SetOne()
return e
}
func (e *gfP12) IsZero() bool {
return e.x.IsZero() && e.y.IsZero()
}
func (e *gfP12) IsOne() bool {
return e.x.IsZero() && e.y.IsOne()
}
func (e *gfP12) Conjugate(a *gfP12) *gfP12 {
e.x.Neg(&a.x)
e.y.Set(&a.y)
return e
}
func (e *gfP12) Neg(a *gfP12) *gfP12 {
e.x.Neg(&a.x)
e.y.Neg(&a.y)
return e
}
// Frobenius computes (xω+y)^p = x^p ω·ξ^((p-1)/6) + y^p
func (e *gfP12) Frobenius(a *gfP12) *gfP12 {
e.x.Frobenius(&a.x)
e.y.Frobenius(&a.y)
e.x.MulScalar(&e.x, xiToPMinus1Over6)
return e
}
// FrobeniusP2 computes (xω+y)^p² = x^p² ω·ξ^((p²-1)/6) + y^p²
func (e *gfP12) FrobeniusP2(a *gfP12) *gfP12 {
e.x.FrobeniusP2(&a.x)
e.x.MulGFP(&e.x, xiToPSquaredMinus1Over6)
e.y.FrobeniusP2(&a.y)
return e
}
func (e *gfP12) FrobeniusP4(a *gfP12) *gfP12 {
e.x.FrobeniusP4(&a.x)
e.x.MulGFP(&e.x, xiToPSquaredMinus1Over3)
e.y.FrobeniusP4(&a.y)
return e
}
func (e *gfP12) Add(a, b *gfP12) *gfP12 {
e.x.Add(&a.x, &b.x)
e.y.Add(&a.y, &b.y)
return e
}
func (e *gfP12) Sub(a, b *gfP12) *gfP12 {
e.x.Sub(&a.x, &b.x)
e.y.Sub(&a.y, &b.y)
return e
}
func (e *gfP12) Mul(a, b *gfP12) *gfP12 {
tx := (&gfP6{}).Mul(&a.x, &b.y)
t := (&gfP6{}).Mul(&b.x, &a.y)
tx.Add(tx, t)
ty := (&gfP6{}).Mul(&a.y, &b.y)
t.Mul(&a.x, &b.x).MulTau(t)
e.x.Set(tx)
e.y.Add(ty, t)
return e
}
func (e *gfP12) MulScalar(a *gfP12, b *gfP6) *gfP12 {
e.x.Mul(&e.x, b)
e.y.Mul(&e.y, b)
return e
}
func (c *gfP12) Exp(a *gfP12, power *big.Int) *gfP12 {
sum := (&gfP12{}).SetOne()
t := &gfP12{}
for i := power.BitLen() - 1; i >= 0; i-- {
t.Square(sum)
if power.Bit(i) != 0 {
sum.Mul(t, a)
} else {
sum.Set(t)
}
}
c.Set(sum)
return c
}
func (e *gfP12) Square(a *gfP12) *gfP12 {
// Complex squaring algorithm
v0 := (&gfP6{}).Mul(&a.x, &a.y)
t := (&gfP6{}).MulTau(&a.x)
t.Add(&a.y, t)
ty := (&gfP6{}).Add(&a.x, &a.y)
ty.Mul(ty, t).Sub(ty, v0)
t.MulTau(v0)
ty.Sub(ty, t)
e.x.Add(v0, v0)
e.y.Set(ty)
return e
}
func (e *gfP12) Invert(a *gfP12) *gfP12 {
// See "Implementing cryptographic pairings", M. Scott, section 3.2.
// ftp://136.206.11.249/pub/crypto/pairings.pdf
t1, t2 := &gfP6{}, &gfP6{}
t1.Square(&a.x)
t2.Square(&a.y)
t1.MulTau(t1).Sub(t2, t1)
t2.Invert(t1)
e.x.Neg(&a.x)
e.y.Set(&a.y)
e.MulScalar(e, t2)
return e
}

View file

@ -0,0 +1,156 @@
package bn256
// For details of the algorithms used, see "Multiplication and Squaring on
// Pairing-Friendly Fields, Devegili et al.
// http://eprint.iacr.org/2006/471.pdf.
// gfP2 implements a field of size p² as a quadratic extension of the base field
// where i²=-1.
type gfP2 struct {
x, y gfP // value is xi+y.
}
func gfP2Decode(in *gfP2) *gfP2 {
out := &gfP2{}
montDecode(&out.x, &in.x)
montDecode(&out.y, &in.y)
return out
}
func (e *gfP2) String() string {
return "(" + e.x.String() + ", " + e.y.String() + ")"
}
func (e *gfP2) Set(a *gfP2) *gfP2 {
e.x.Set(&a.x)
e.y.Set(&a.y)
return e
}
func (e *gfP2) SetZero() *gfP2 {
e.x = gfP{0}
e.y = gfP{0}
return e
}
func (e *gfP2) SetOne() *gfP2 {
e.x = gfP{0}
e.y = *newGFp(1)
return e
}
func (e *gfP2) IsZero() bool {
zero := gfP{0}
return e.x == zero && e.y == zero
}
func (e *gfP2) IsOne() bool {
zero, one := gfP{0}, *newGFp(1)
return e.x == zero && e.y == one
}
func (e *gfP2) Conjugate(a *gfP2) *gfP2 {
e.y.Set(&a.y)
gfpNeg(&e.x, &a.x)
return e
}
func (e *gfP2) Neg(a *gfP2) *gfP2 {
gfpNeg(&e.x, &a.x)
gfpNeg(&e.y, &a.y)
return e
}
func (e *gfP2) Add(a, b *gfP2) *gfP2 {
gfpAdd(&e.x, &a.x, &b.x)
gfpAdd(&e.y, &a.y, &b.y)
return e
}
func (e *gfP2) Sub(a, b *gfP2) *gfP2 {
gfpSub(&e.x, &a.x, &b.x)
gfpSub(&e.y, &a.y, &b.y)
return e
}
// See "Multiplication and Squaring in Pairing-Friendly Fields",
// http://eprint.iacr.org/2006/471.pdf
func (e *gfP2) Mul(a, b *gfP2) *gfP2 {
tx, t := &gfP{}, &gfP{}
gfpMul(tx, &a.x, &b.y)
gfpMul(t, &b.x, &a.y)
gfpAdd(tx, tx, t)
ty := &gfP{}
gfpMul(ty, &a.y, &b.y)
gfpMul(t, &a.x, &b.x)
gfpSub(ty, ty, t)
e.x.Set(tx)
e.y.Set(ty)
return e
}
func (e *gfP2) MulScalar(a *gfP2, b *gfP) *gfP2 {
gfpMul(&e.x, &a.x, b)
gfpMul(&e.y, &a.y, b)
return e
}
// MulXi sets e=ξa where ξ=i+9 and then returns e.
func (e *gfP2) MulXi(a *gfP2) *gfP2 {
// (xi+y)(i+9) = (9x+y)i+(9y-x)
tx := &gfP{}
gfpAdd(tx, &a.x, &a.x)
gfpAdd(tx, tx, tx)
gfpAdd(tx, tx, tx)
gfpAdd(tx, tx, &a.x)
gfpAdd(tx, tx, &a.y)
ty := &gfP{}
gfpAdd(ty, &a.y, &a.y)
gfpAdd(ty, ty, ty)
gfpAdd(ty, ty, ty)
gfpAdd(ty, ty, &a.y)
gfpSub(ty, ty, &a.x)
e.x.Set(tx)
e.y.Set(ty)
return e
}
func (e *gfP2) Square(a *gfP2) *gfP2 {
// Complex squaring algorithm:
// (xi+y)² = (x+y)(y-x) + 2*i*x*y
tx, ty := &gfP{}, &gfP{}
gfpSub(tx, &a.y, &a.x)
gfpAdd(ty, &a.x, &a.y)
gfpMul(ty, tx, ty)
gfpMul(tx, &a.x, &a.y)
gfpAdd(tx, tx, tx)
e.x.Set(tx)
e.y.Set(ty)
return e
}
func (e *gfP2) Invert(a *gfP2) *gfP2 {
// See "Implementing cryptographic pairings", M. Scott, section 3.2.
// ftp://136.206.11.249/pub/crypto/pairings.pdf
t1, t2 := &gfP{}, &gfP{}
gfpMul(t1, &a.x, &a.x)
gfpMul(t2, &a.y, &a.y)
gfpAdd(t1, t1, t2)
inv := &gfP{}
inv.Invert(t1)
gfpNeg(t1, &a.x)
gfpMul(&e.x, t1, inv)
gfpMul(&e.y, &a.y, inv)
return e
}

View file

@ -0,0 +1,213 @@
package bn256
// For details of the algorithms used, see "Multiplication and Squaring on
// Pairing-Friendly Fields, Devegili et al.
// http://eprint.iacr.org/2006/471.pdf.
// gfP6 implements the field of size p⁶ as a cubic extension of gfP2 where τ³=ξ
// and ξ=i+3.
type gfP6 struct {
x, y, z gfP2 // value is xτ² + yτ + z
}
func (e *gfP6) String() string {
return "(" + e.x.String() + ", " + e.y.String() + ", " + e.z.String() + ")"
}
func (e *gfP6) Set(a *gfP6) *gfP6 {
e.x.Set(&a.x)
e.y.Set(&a.y)
e.z.Set(&a.z)
return e
}
func (e *gfP6) SetZero() *gfP6 {
e.x.SetZero()
e.y.SetZero()
e.z.SetZero()
return e
}
func (e *gfP6) SetOne() *gfP6 {
e.x.SetZero()
e.y.SetZero()
e.z.SetOne()
return e
}
func (e *gfP6) IsZero() bool {
return e.x.IsZero() && e.y.IsZero() && e.z.IsZero()
}
func (e *gfP6) IsOne() bool {
return e.x.IsZero() && e.y.IsZero() && e.z.IsOne()
}
func (e *gfP6) Neg(a *gfP6) *gfP6 {
e.x.Neg(&a.x)
e.y.Neg(&a.y)
e.z.Neg(&a.z)
return e
}
func (e *gfP6) Frobenius(a *gfP6) *gfP6 {
e.x.Conjugate(&a.x)
e.y.Conjugate(&a.y)
e.z.Conjugate(&a.z)
e.x.Mul(&e.x, xiTo2PMinus2Over3)
e.y.Mul(&e.y, xiToPMinus1Over3)
return e
}
// FrobeniusP2 computes (xτ²+yτ+z)^(p²) = xτ^(2p²) + yτ^(p²) + z
func (e *gfP6) FrobeniusP2(a *gfP6) *gfP6 {
// τ^(2p²) = τ²τ^(2p²-2) = τ²ξ^((2p²-2)/3)
e.x.MulScalar(&a.x, xiTo2PSquaredMinus2Over3)
// τ^(p²) = ττ^(p²-1) = τξ^((p²-1)/3)
e.y.MulScalar(&a.y, xiToPSquaredMinus1Over3)
e.z.Set(&a.z)
return e
}
func (e *gfP6) FrobeniusP4(a *gfP6) *gfP6 {
e.x.MulScalar(&a.x, xiToPSquaredMinus1Over3)
e.y.MulScalar(&a.y, xiTo2PSquaredMinus2Over3)
e.z.Set(&a.z)
return e
}
func (e *gfP6) Add(a, b *gfP6) *gfP6 {
e.x.Add(&a.x, &b.x)
e.y.Add(&a.y, &b.y)
e.z.Add(&a.z, &b.z)
return e
}
func (e *gfP6) Sub(a, b *gfP6) *gfP6 {
e.x.Sub(&a.x, &b.x)
e.y.Sub(&a.y, &b.y)
e.z.Sub(&a.z, &b.z)
return e
}
func (e *gfP6) Mul(a, b *gfP6) *gfP6 {
// "Multiplication and Squaring on Pairing-Friendly Fields"
// Section 4, Karatsuba method.
// http://eprint.iacr.org/2006/471.pdf
v0 := (&gfP2{}).Mul(&a.z, &b.z)
v1 := (&gfP2{}).Mul(&a.y, &b.y)
v2 := (&gfP2{}).Mul(&a.x, &b.x)
t0 := (&gfP2{}).Add(&a.x, &a.y)
t1 := (&gfP2{}).Add(&b.x, &b.y)
tz := (&gfP2{}).Mul(t0, t1)
tz.Sub(tz, v1).Sub(tz, v2).MulXi(tz).Add(tz, v0)
t0.Add(&a.y, &a.z)
t1.Add(&b.y, &b.z)
ty := (&gfP2{}).Mul(t0, t1)
t0.MulXi(v2)
ty.Sub(ty, v0).Sub(ty, v1).Add(ty, t0)
t0.Add(&a.x, &a.z)
t1.Add(&b.x, &b.z)
tx := (&gfP2{}).Mul(t0, t1)
tx.Sub(tx, v0).Add(tx, v1).Sub(tx, v2)
e.x.Set(tx)
e.y.Set(ty)
e.z.Set(tz)
return e
}
func (e *gfP6) MulScalar(a *gfP6, b *gfP2) *gfP6 {
e.x.Mul(&a.x, b)
e.y.Mul(&a.y, b)
e.z.Mul(&a.z, b)
return e
}
func (e *gfP6) MulGFP(a *gfP6, b *gfP) *gfP6 {
e.x.MulScalar(&a.x, b)
e.y.MulScalar(&a.y, b)
e.z.MulScalar(&a.z, b)
return e
}
// MulTau computes τ·(aτ²+bτ+c) = bτ²+cτ+aξ
func (e *gfP6) MulTau(a *gfP6) *gfP6 {
tz := (&gfP2{}).MulXi(&a.x)
ty := (&gfP2{}).Set(&a.y)
e.y.Set(&a.z)
e.x.Set(ty)
e.z.Set(tz)
return e
}
func (e *gfP6) Square(a *gfP6) *gfP6 {
v0 := (&gfP2{}).Square(&a.z)
v1 := (&gfP2{}).Square(&a.y)
v2 := (&gfP2{}).Square(&a.x)
c0 := (&gfP2{}).Add(&a.x, &a.y)
c0.Square(c0).Sub(c0, v1).Sub(c0, v2).MulXi(c0).Add(c0, v0)
c1 := (&gfP2{}).Add(&a.y, &a.z)
c1.Square(c1).Sub(c1, v0).Sub(c1, v1)
xiV2 := (&gfP2{}).MulXi(v2)
c1.Add(c1, xiV2)
c2 := (&gfP2{}).Add(&a.x, &a.z)
c2.Square(c2).Sub(c2, v0).Add(c2, v1).Sub(c2, v2)
e.x.Set(c2)
e.y.Set(c1)
e.z.Set(c0)
return e
}
func (e *gfP6) Invert(a *gfP6) *gfP6 {
// See "Implementing cryptographic pairings", M. Scott, section 3.2.
// ftp://136.206.11.249/pub/crypto/pairings.pdf
// Here we can give a short explanation of how it works: let j be a cubic root of
// unity in GF(p²) so that 1+j+j²=0.
// Then (xτ² + yτ + z)(xj²τ² + yjτ + z)(xjτ² + yj²τ + z)
// = (xτ² + yτ + z)(Cτ²+Bτ+A)
// = (x³ξ²+y³ξ+z³-3ξxyz) = F is an element of the base field (the norm).
//
// On the other hand (xj²τ² + yjτ + z)(xjτ² + yj²τ + z)
// = τ²(y²-ξxz) + τ(ξx²-yz) + (z²-ξxy)
//
// So that's why A = (z²-ξxy), B = (ξx²-yz), C = (y²-ξxz)
t1 := (&gfP2{}).Mul(&a.x, &a.y)
t1.MulXi(t1)
A := (&gfP2{}).Square(&a.z)
A.Sub(A, t1)
B := (&gfP2{}).Square(&a.x)
B.MulXi(B)
t1.Mul(&a.y, &a.z)
B.Sub(B, t1)
C := (&gfP2{}).Square(&a.y)
t1.Mul(&a.x, &a.z)
C.Sub(C, t1)
F := (&gfP2{}).Mul(C, &a.y)
F.MulXi(F)
t1.Mul(A, &a.z)
F.Add(F, t1)
t1.Mul(B, &a.x).MulXi(t1)
F.Add(F, t1)
F.Invert(F)
e.x.Mul(C, F)
e.y.Mul(B, F)
e.z.Mul(A, F)
return e
}

View file

@ -0,0 +1,15 @@
// +build amd64,!appengine,!gccgo
package bn256
// go:noescape
func gfpNeg(c, a *gfP)
//go:noescape
func gfpAdd(c, a, b *gfP)
//go:noescape
func gfpSub(c, a, b *gfP)
//go:noescape
func gfpMul(c, a, b *gfP)

View file

@ -0,0 +1,97 @@
// +build amd64,!appengine,!gccgo
#include "gfp.h"
#include "mul.h"
#include "mul_bmi2.h"
TEXT ·gfpNeg(SB),0,$0-16
MOVQ ·p2+0(SB), R8
MOVQ ·p2+8(SB), R9
MOVQ ·p2+16(SB), R10
MOVQ ·p2+24(SB), R11
MOVQ a+8(FP), DI
SUBQ 0(DI), R8
SBBQ 8(DI), R9
SBBQ 16(DI), R10
SBBQ 24(DI), R11
MOVQ $0, AX
gfpCarry(R8,R9,R10,R11,AX, R12,R13,R14,R15,BX)
MOVQ c+0(FP), DI
storeBlock(R8,R9,R10,R11, 0(DI))
RET
TEXT ·gfpAdd(SB),0,$0-24
MOVQ a+8(FP), DI
MOVQ b+16(FP), SI
loadBlock(0(DI), R8,R9,R10,R11)
MOVQ $0, R12
ADDQ 0(SI), R8
ADCQ 8(SI), R9
ADCQ 16(SI), R10
ADCQ 24(SI), R11
ADCQ $0, R12
gfpCarry(R8,R9,R10,R11,R12, R13,R14,R15,AX,BX)
MOVQ c+0(FP), DI
storeBlock(R8,R9,R10,R11, 0(DI))
RET
TEXT ·gfpSub(SB),0,$0-24
MOVQ a+8(FP), DI
MOVQ b+16(FP), SI
loadBlock(0(DI), R8,R9,R10,R11)
MOVQ ·p2+0(SB), R12
MOVQ ·p2+8(SB), R13
MOVQ ·p2+16(SB), R14
MOVQ ·p2+24(SB), R15
MOVQ $0, AX
SUBQ 0(SI), R8
SBBQ 8(SI), R9
SBBQ 16(SI), R10
SBBQ 24(SI), R11
CMOVQCC AX, R12
CMOVQCC AX, R13
CMOVQCC AX, R14
CMOVQCC AX, R15
ADDQ R12, R8
ADCQ R13, R9
ADCQ R14, R10
ADCQ R15, R11
MOVQ c+0(FP), DI
storeBlock(R8,R9,R10,R11, 0(DI))
RET
TEXT ·gfpMul(SB),0,$160-24
MOVQ a+8(FP), DI
MOVQ b+16(FP), SI
// Jump to a slightly different implementation if MULX isn't supported.
CMPB runtime·support_bmi2(SB), $0
JE nobmi2Mul
mulBMI2(0(DI),8(DI),16(DI),24(DI), 0(SI))
storeBlock( R8, R9,R10,R11, 0(SP))
storeBlock(R12,R13,R14,R15, 32(SP))
gfpReduceBMI2()
JMP end
nobmi2Mul:
mul(0(DI),8(DI),16(DI),24(DI), 0(SI), 0(SP))
gfpReduce(0(SP))
end:
MOVQ c+0(FP), DI
storeBlock(R12,R13,R14,R15, 0(DI))
RET

View file

@ -0,0 +1,19 @@
// +build !amd64 appengine gccgo
package bn256
func gfpNeg(c, a *gfP) {
panic("unsupported architecture")
}
func gfpAdd(c, a, b *gfP) {
panic("unsupported architecture")
}
func gfpSub(c, a, b *gfP) {
panic("unsupported architecture")
}
func gfpMul(c, a, b *gfP) {
panic("unsupported architecture")
}

View file

@ -0,0 +1,62 @@
// +build amd64,!appengine,!gccgo
package bn256
import (
"testing"
)
// Tests that negation works the same way on both assembly-optimized and pure Go
// implementation.
func TestGFpNeg(t *testing.T) {
n := &gfP{0x0123456789abcdef, 0xfedcba9876543210, 0xdeadbeefdeadbeef, 0xfeebdaedfeebdaed}
w := &gfP{0xfedcba9876543211, 0x0123456789abcdef, 0x2152411021524110, 0x0114251201142512}
h := &gfP{}
gfpNeg(h, n)
if *h != *w {
t.Errorf("negation mismatch: have %#x, want %#x", *h, *w)
}
}
// Tests that addition works the same way on both assembly-optimized and pure Go
// implementation.
func TestGFpAdd(t *testing.T) {
a := &gfP{0x0123456789abcdef, 0xfedcba9876543210, 0xdeadbeefdeadbeef, 0xfeebdaedfeebdaed}
b := &gfP{0xfedcba9876543210, 0x0123456789abcdef, 0xfeebdaedfeebdaed, 0xdeadbeefdeadbeef}
w := &gfP{0xc3df73e9278302b8, 0x687e956e978e3572, 0x254954275c18417f, 0xad354b6afc67f9b4}
h := &gfP{}
gfpAdd(h, a, b)
if *h != *w {
t.Errorf("addition mismatch: have %#x, want %#x", *h, *w)
}
}
// Tests that subtraction works the same way on both assembly-optimized and pure Go
// implementation.
func TestGFpSub(t *testing.T) {
a := &gfP{0x0123456789abcdef, 0xfedcba9876543210, 0xdeadbeefdeadbeef, 0xfeebdaedfeebdaed}
b := &gfP{0xfedcba9876543210, 0x0123456789abcdef, 0xfeebdaedfeebdaed, 0xdeadbeefdeadbeef}
w := &gfP{0x02468acf13579bdf, 0xfdb97530eca86420, 0xdfc1e401dfc1e402, 0x203e1bfe203e1bfd}
h := &gfP{}
gfpSub(h, a, b)
if *h != *w {
t.Errorf("subtraction mismatch: have %#x, want %#x", *h, *w)
}
}
// Tests that multiplication works the same way on both assembly-optimized and pure Go
// implementation.
func TestGFpMul(t *testing.T) {
a := &gfP{0x0123456789abcdef, 0xfedcba9876543210, 0xdeadbeefdeadbeef, 0xfeebdaedfeebdaed}
b := &gfP{0xfedcba9876543210, 0x0123456789abcdef, 0xfeebdaedfeebdaed, 0xdeadbeefdeadbeef}
w := &gfP{0xcbcbd377f7ad22d3, 0x3b89ba5d849379bf, 0x87b61627bd38b6d2, 0xc44052a2a0e654b2}
h := &gfP{}
gfpMul(h, a, b)
if *h != *w {
t.Errorf("multiplication mismatch: have %#x, want %#x", *h, *w)
}
}

View file

@ -0,0 +1,73 @@
// +build amd64,!appengine,!gccgo
package bn256
import (
"testing"
"crypto/rand"
)
func TestRandomG2Marshal(t *testing.T) {
for i := 0; i < 10; i++ {
n, g2, err := RandomG2(rand.Reader)
if err != nil {
t.Error(err)
continue
}
t.Logf("%d: %x\n", n, g2.Marshal())
}
}
func TestPairings(t *testing.T) {
a1 := new(G1).ScalarBaseMult(bigFromBase10("1"))
a2 := new(G1).ScalarBaseMult(bigFromBase10("2"))
a37 := new(G1).ScalarBaseMult(bigFromBase10("37"))
an1 := new(G1).ScalarBaseMult(bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495616"))
b0 := new(G2).ScalarBaseMult(bigFromBase10("0"))
b1 := new(G2).ScalarBaseMult(bigFromBase10("1"))
b2 := new(G2).ScalarBaseMult(bigFromBase10("2"))
b27 := new(G2).ScalarBaseMult(bigFromBase10("27"))
b999 := new(G2).ScalarBaseMult(bigFromBase10("999"))
bn1 := new(G2).ScalarBaseMult(bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495616"))
p1 := Pair(a1, b1)
pn1 := Pair(a1, bn1)
np1 := Pair(an1, b1)
if pn1.String() != np1.String() {
t.Error("Pairing mismatch: e(a, -b) != e(-a, b)")
}
if !PairingCheck([]*G1{a1, an1}, []*G2{b1, b1}) {
t.Error("MultiAte check gave false negative!")
}
p0 := new(GT).Add(p1, pn1)
p0_2 := Pair(a1, b0)
if p0.String() != p0_2.String() {
t.Error("Pairing mismatch: e(a, b) * e(a, -b) != 1")
}
p0_3 := new(GT).ScalarMult(p1, bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495617"))
if p0.String() != p0_3.String() {
t.Error("Pairing mismatch: e(a, b) has wrong order")
}
p2 := Pair(a2, b1)
p2_2 := Pair(a1, b2)
p2_3 := new(GT).ScalarMult(p1, bigFromBase10("2"))
if p2.String() != p2_2.String() {
t.Error("Pairing mismatch: e(a, b * 2) != e(a * 2, b)")
}
if p2.String() != p2_3.String() {
t.Error("Pairing mismatch: e(a, b * 2) != e(a, b) ** 2")
}
if p2.String() == p1.String() {
t.Error("Pairing is degenerate!")
}
if PairingCheck([]*G1{a1, a1}, []*G2{b1, b1}) {
t.Error("MultiAte check gave false positive!")
}
p999 := Pair(a37, b27)
p999_2 := Pair(a1, b999)
if p999.String() != p999_2.String() {
t.Error("Pairing mismatch: e(a * 37, b * 27) != e(a, b * 999)")
}
}

View file

@ -0,0 +1,181 @@
#define mul(a0,a1,a2,a3, rb, stack) \
MOVQ a0, AX \
MULQ 0+rb \
MOVQ AX, R8 \
MOVQ DX, R9 \
MOVQ a0, AX \
MULQ 8+rb \
ADDQ AX, R9 \
ADCQ $0, DX \
MOVQ DX, R10 \
MOVQ a0, AX \
MULQ 16+rb \
ADDQ AX, R10 \
ADCQ $0, DX \
MOVQ DX, R11 \
MOVQ a0, AX \
MULQ 24+rb \
ADDQ AX, R11 \
ADCQ $0, DX \
MOVQ DX, R12 \
\
storeBlock(R8,R9,R10,R11, 0+stack) \
MOVQ R12, 32+stack \
\
MOVQ a1, AX \
MULQ 0+rb \
MOVQ AX, R8 \
MOVQ DX, R9 \
MOVQ a1, AX \
MULQ 8+rb \
ADDQ AX, R9 \
ADCQ $0, DX \
MOVQ DX, R10 \
MOVQ a1, AX \
MULQ 16+rb \
ADDQ AX, R10 \
ADCQ $0, DX \
MOVQ DX, R11 \
MOVQ a1, AX \
MULQ 24+rb \
ADDQ AX, R11 \
ADCQ $0, DX \
MOVQ DX, R12 \
\
ADDQ 8+stack, R8 \
ADCQ 16+stack, R9 \
ADCQ 24+stack, R10 \
ADCQ 32+stack, R11 \
ADCQ $0, R12 \
storeBlock(R8,R9,R10,R11, 8+stack) \
MOVQ R12, 40+stack \
\
MOVQ a2, AX \
MULQ 0+rb \
MOVQ AX, R8 \
MOVQ DX, R9 \
MOVQ a2, AX \
MULQ 8+rb \
ADDQ AX, R9 \
ADCQ $0, DX \
MOVQ DX, R10 \
MOVQ a2, AX \
MULQ 16+rb \
ADDQ AX, R10 \
ADCQ $0, DX \
MOVQ DX, R11 \
MOVQ a2, AX \
MULQ 24+rb \
ADDQ AX, R11 \
ADCQ $0, DX \
MOVQ DX, R12 \
\
ADDQ 16+stack, R8 \
ADCQ 24+stack, R9 \
ADCQ 32+stack, R10 \
ADCQ 40+stack, R11 \
ADCQ $0, R12 \
storeBlock(R8,R9,R10,R11, 16+stack) \
MOVQ R12, 48+stack \
\
MOVQ a3, AX \
MULQ 0+rb \
MOVQ AX, R8 \
MOVQ DX, R9 \
MOVQ a3, AX \
MULQ 8+rb \
ADDQ AX, R9 \
ADCQ $0, DX \
MOVQ DX, R10 \
MOVQ a3, AX \
MULQ 16+rb \
ADDQ AX, R10 \
ADCQ $0, DX \
MOVQ DX, R11 \
MOVQ a3, AX \
MULQ 24+rb \
ADDQ AX, R11 \
ADCQ $0, DX \
MOVQ DX, R12 \
\
ADDQ 24+stack, R8 \
ADCQ 32+stack, R9 \
ADCQ 40+stack, R10 \
ADCQ 48+stack, R11 \
ADCQ $0, R12 \
storeBlock(R8,R9,R10,R11, 24+stack) \
MOVQ R12, 56+stack
#define gfpReduce(stack) \
\ // m = (T * N') mod R, store m in R8:R9:R10:R11
MOVQ ·np+0(SB), AX \
MULQ 0+stack \
MOVQ AX, R8 \
MOVQ DX, R9 \
MOVQ ·np+0(SB), AX \
MULQ 8+stack \
ADDQ AX, R9 \
ADCQ $0, DX \
MOVQ DX, R10 \
MOVQ ·np+0(SB), AX \
MULQ 16+stack \
ADDQ AX, R10 \
ADCQ $0, DX \
MOVQ DX, R11 \
MOVQ ·np+0(SB), AX \
MULQ 24+stack \
ADDQ AX, R11 \
\
MOVQ ·np+8(SB), AX \
MULQ 0+stack \
MOVQ AX, R12 \
MOVQ DX, R13 \
MOVQ ·np+8(SB), AX \
MULQ 8+stack \
ADDQ AX, R13 \
ADCQ $0, DX \
MOVQ DX, R14 \
MOVQ ·np+8(SB), AX \
MULQ 16+stack \
ADDQ AX, R14 \
\
ADDQ R12, R9 \
ADCQ R13, R10 \
ADCQ R14, R11 \
\
MOVQ ·np+16(SB), AX \
MULQ 0+stack \
MOVQ AX, R12 \
MOVQ DX, R13 \
MOVQ ·np+16(SB), AX \
MULQ 8+stack \
ADDQ AX, R13 \
\
ADDQ R12, R10 \
ADCQ R13, R11 \
\
MOVQ ·np+24(SB), AX \
MULQ 0+stack \
ADDQ AX, R11 \
\
storeBlock(R8,R9,R10,R11, 64+stack) \
\
\ // m * N
mul(·p2+0(SB),·p2+8(SB),·p2+16(SB),·p2+24(SB), 64+stack, 96+stack) \
\
\ // Add the 512-bit intermediate to m*N
loadBlock(96+stack, R8,R9,R10,R11) \
loadBlock(128+stack, R12,R13,R14,R15) \
\
MOVQ $0, AX \
ADDQ 0+stack, R8 \
ADCQ 8+stack, R9 \
ADCQ 16+stack, R10 \
ADCQ 24+stack, R11 \
ADCQ 32+stack, R12 \
ADCQ 40+stack, R13 \
ADCQ 48+stack, R14 \
ADCQ 56+stack, R15 \
ADCQ $0, AX \
\
gfpCarry(R12,R13,R14,R15,AX, R8,R9,R10,R11,BX)

View file

@ -0,0 +1,112 @@
#define mulBMI2(a0,a1,a2,a3, rb) \
MOVQ a0, DX \
MOVQ $0, R13 \
MULXQ 0+rb, R8, R9 \
MULXQ 8+rb, AX, R10 \
ADDQ AX, R9 \
MULXQ 16+rb, AX, R11 \
ADCQ AX, R10 \
MULXQ 24+rb, AX, R12 \
ADCQ AX, R11 \
ADCQ $0, R12 \
ADCQ $0, R13 \
\
MOVQ a1, DX \
MOVQ $0, R14 \
MULXQ 0+rb, AX, BX \
ADDQ AX, R9 \
ADCQ BX, R10 \
MULXQ 16+rb, AX, BX \
ADCQ AX, R11 \
ADCQ BX, R12 \
ADCQ $0, R13 \
MULXQ 8+rb, AX, BX \
ADDQ AX, R10 \
ADCQ BX, R11 \
MULXQ 24+rb, AX, BX \
ADCQ AX, R12 \
ADCQ BX, R13 \
ADCQ $0, R14 \
\
MOVQ a2, DX \
MOVQ $0, R15 \
MULXQ 0+rb, AX, BX \
ADDQ AX, R10 \
ADCQ BX, R11 \
MULXQ 16+rb, AX, BX \
ADCQ AX, R12 \
ADCQ BX, R13 \
ADCQ $0, R14 \
MULXQ 8+rb, AX, BX \
ADDQ AX, R11 \
ADCQ BX, R12 \
MULXQ 24+rb, AX, BX \
ADCQ AX, R13 \
ADCQ BX, R14 \
ADCQ $0, R15 \
\
MOVQ a3, DX \
MULXQ 0+rb, AX, BX \
ADDQ AX, R11 \
ADCQ BX, R12 \
MULXQ 16+rb, AX, BX \
ADCQ AX, R13 \
ADCQ BX, R14 \
ADCQ $0, R15 \
MULXQ 8+rb, AX, BX \
ADDQ AX, R12 \
ADCQ BX, R13 \
MULXQ 24+rb, AX, BX \
ADCQ AX, R14 \
ADCQ BX, R15
#define gfpReduceBMI2() \
\ // m = (T * N') mod R, store m in R8:R9:R10:R11
MOVQ ·np+0(SB), DX \
MULXQ 0(SP), R8, R9 \
MULXQ 8(SP), AX, R10 \
ADDQ AX, R9 \
MULXQ 16(SP), AX, R11 \
ADCQ AX, R10 \
MULXQ 24(SP), AX, BX \
ADCQ AX, R11 \
\
MOVQ ·np+8(SB), DX \
MULXQ 0(SP), AX, BX \
ADDQ AX, R9 \
ADCQ BX, R10 \
MULXQ 16(SP), AX, BX \
ADCQ AX, R11 \
MULXQ 8(SP), AX, BX \
ADDQ AX, R10 \
ADCQ BX, R11 \
\
MOVQ ·np+16(SB), DX \
MULXQ 0(SP), AX, BX \
ADDQ AX, R10 \
ADCQ BX, R11 \
MULXQ 8(SP), AX, BX \
ADDQ AX, R11 \
\
MOVQ ·np+24(SB), DX \
MULXQ 0(SP), AX, BX \
ADDQ AX, R11 \
\
storeBlock(R8,R9,R10,R11, 64(SP)) \
\
\ // m * N
mulBMI2(·p2+0(SB),·p2+8(SB),·p2+16(SB),·p2+24(SB), 64(SP)) \
\
\ // Add the 512-bit intermediate to m*N
MOVQ $0, AX \
ADDQ 0(SP), R8 \
ADCQ 8(SP), R9 \
ADCQ 16(SP), R10 \
ADCQ 24(SP), R11 \
ADCQ 32(SP), R12 \
ADCQ 40(SP), R13 \
ADCQ 48(SP), R14 \
ADCQ 56(SP), R15 \
ADCQ $0, AX \
\
gfpCarry(R12,R13,R14,R15,AX, R8,R9,R10,R11,BX)

View file

@ -0,0 +1,271 @@
package bn256
func lineFunctionAdd(r, p *twistPoint, q *curvePoint, r2 *gfP2) (a, b, c *gfP2, rOut *twistPoint) {
// See the mixed addition algorithm from "Faster Computation of the
// Tate Pairing", http://arxiv.org/pdf/0904.0854v3.pdf
B := (&gfP2{}).Mul(&p.x, &r.t)
D := (&gfP2{}).Add(&p.y, &r.z)
D.Square(D).Sub(D, r2).Sub(D, &r.t).Mul(D, &r.t)
H := (&gfP2{}).Sub(B, &r.x)
I := (&gfP2{}).Square(H)
E := (&gfP2{}).Add(I, I)
E.Add(E, E)
J := (&gfP2{}).Mul(H, E)
L1 := (&gfP2{}).Sub(D, &r.y)
L1.Sub(L1, &r.y)
V := (&gfP2{}).Mul(&r.x, E)
rOut = &twistPoint{}
rOut.x.Square(L1).Sub(&rOut.x, J).Sub(&rOut.x, V).Sub(&rOut.x, V)
rOut.z.Add(&r.z, H).Square(&rOut.z).Sub(&rOut.z, &r.t).Sub(&rOut.z, I)
t := (&gfP2{}).Sub(V, &rOut.x)
t.Mul(t, L1)
t2 := (&gfP2{}).Mul(&r.y, J)
t2.Add(t2, t2)
rOut.y.Sub(t, t2)
rOut.t.Square(&rOut.z)
t.Add(&p.y, &rOut.z).Square(t).Sub(t, r2).Sub(t, &rOut.t)
t2.Mul(L1, &p.x)
t2.Add(t2, t2)
a = (&gfP2{}).Sub(t2, t)
c = (&gfP2{}).MulScalar(&rOut.z, &q.y)
c.Add(c, c)
b = (&gfP2{}).Neg(L1)
b.MulScalar(b, &q.x).Add(b, b)
return
}
func lineFunctionDouble(r *twistPoint, q *curvePoint) (a, b, c *gfP2, rOut *twistPoint) {
// See the doubling algorithm for a=0 from "Faster Computation of the
// Tate Pairing", http://arxiv.org/pdf/0904.0854v3.pdf
A := (&gfP2{}).Square(&r.x)
B := (&gfP2{}).Square(&r.y)
C := (&gfP2{}).Square(B)
D := (&gfP2{}).Add(&r.x, B)
D.Square(D).Sub(D, A).Sub(D, C).Add(D, D)
E := (&gfP2{}).Add(A, A)
E.Add(E, A)
G := (&gfP2{}).Square(E)
rOut = &twistPoint{}
rOut.x.Sub(G, D).Sub(&rOut.x, D)
rOut.z.Add(&r.y, &r.z).Square(&rOut.z).Sub(&rOut.z, B).Sub(&rOut.z, &r.t)
rOut.y.Sub(D, &rOut.x).Mul(&rOut.y, E)
t := (&gfP2{}).Add(C, C)
t.Add(t, t).Add(t, t)
rOut.y.Sub(&rOut.y, t)
rOut.t.Square(&rOut.z)
t.Mul(E, &r.t).Add(t, t)
b = (&gfP2{}).Neg(t)
b.MulScalar(b, &q.x)
a = (&gfP2{}).Add(&r.x, E)
a.Square(a).Sub(a, A).Sub(a, G)
t.Add(B, B).Add(t, t)
a.Sub(a, t)
c = (&gfP2{}).Mul(&rOut.z, &r.t)
c.Add(c, c).MulScalar(c, &q.y)
return
}
func mulLine(ret *gfP12, a, b, c *gfP2) {
a2 := &gfP6{}
a2.y.Set(a)
a2.z.Set(b)
a2.Mul(a2, &ret.x)
t3 := (&gfP6{}).MulScalar(&ret.y, c)
t := (&gfP2{}).Add(b, c)
t2 := &gfP6{}
t2.y.Set(a)
t2.z.Set(t)
ret.x.Add(&ret.x, &ret.y)
ret.y.Set(t3)
ret.x.Mul(&ret.x, t2).Sub(&ret.x, a2).Sub(&ret.x, &ret.y)
a2.MulTau(a2)
ret.y.Add(&ret.y, a2)
}
// sixuPlus2NAF is 6u+2 in non-adjacent form.
var sixuPlus2NAF = []int8{0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0,
0, 1, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 0, 0, 1, 1,
1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1,
1, 0, 0, -1, 0, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, 1, 1}
// miller implements the Miller loop for calculating the Optimal Ate pairing.
// See algorithm 1 from http://cryptojedi.org/papers/dclxvi-20100714.pdf
func miller(q *twistPoint, p *curvePoint) *gfP12 {
ret := (&gfP12{}).SetOne()
aAffine := &twistPoint{}
aAffine.Set(q)
aAffine.MakeAffine()
bAffine := &curvePoint{}
bAffine.Set(p)
bAffine.MakeAffine()
minusA := &twistPoint{}
minusA.Neg(aAffine)
r := &twistPoint{}
r.Set(aAffine)
r2 := (&gfP2{}).Square(&aAffine.y)
for i := len(sixuPlus2NAF) - 1; i > 0; i-- {
a, b, c, newR := lineFunctionDouble(r, bAffine)
if i != len(sixuPlus2NAF)-1 {
ret.Square(ret)
}
mulLine(ret, a, b, c)
r = newR
switch sixuPlus2NAF[i-1] {
case 1:
a, b, c, newR = lineFunctionAdd(r, aAffine, bAffine, r2)
case -1:
a, b, c, newR = lineFunctionAdd(r, minusA, bAffine, r2)
default:
continue
}
mulLine(ret, a, b, c)
r = newR
}
// In order to calculate Q1 we have to convert q from the sextic twist
// to the full GF(p^12) group, apply the Frobenius there, and convert
// back.
//
// The twist isomorphism is (x', y') -> (xω², yω³). If we consider just
// x for a moment, then after applying the Frobenius, we have x̄ω^(2p)
// where x̄ is the conjugate of x. If we are going to apply the inverse
// isomorphism we need a value with a single coefficient of ω² so we
// rewrite this as x̄ω^(2p-2)ω². ξ⁶ = ω and, due to the construction of
// p, 2p-2 is a multiple of six. Therefore we can rewrite as
// x̄ξ^((p-1)/3)ω² and applying the inverse isomorphism eliminates the
// ω².
//
// A similar argument can be made for the y value.
q1 := &twistPoint{}
q1.x.Conjugate(&aAffine.x).Mul(&q1.x, xiToPMinus1Over3)
q1.y.Conjugate(&aAffine.y).Mul(&q1.y, xiToPMinus1Over2)
q1.z.SetOne()
q1.t.SetOne()
// For Q2 we are applying the p² Frobenius. The two conjugations cancel
// out and we are left only with the factors from the isomorphism. In
// the case of x, we end up with a pure number which is why
// xiToPSquaredMinus1Over3 is ∈ GF(p). With y we get a factor of -1. We
// ignore this to end up with -Q2.
minusQ2 := &twistPoint{}
minusQ2.x.MulScalar(&aAffine.x, xiToPSquaredMinus1Over3)
minusQ2.y.Set(&aAffine.y)
minusQ2.z.SetOne()
minusQ2.t.SetOne()
r2.Square(&q1.y)
a, b, c, newR := lineFunctionAdd(r, q1, bAffine, r2)
mulLine(ret, a, b, c)
r = newR
r2.Square(&minusQ2.y)
a, b, c, newR = lineFunctionAdd(r, minusQ2, bAffine, r2)
mulLine(ret, a, b, c)
r = newR
return ret
}
// finalExponentiation computes the (p¹²-1)/Order-th power of an element of
// GF(p¹²) to obtain an element of GT (steps 13-15 of algorithm 1 from
// http://cryptojedi.org/papers/dclxvi-20100714.pdf)
func finalExponentiation(in *gfP12) *gfP12 {
t1 := &gfP12{}
// This is the p^6-Frobenius
t1.x.Neg(&in.x)
t1.y.Set(&in.y)
inv := &gfP12{}
inv.Invert(in)
t1.Mul(t1, inv)
t2 := (&gfP12{}).FrobeniusP2(t1)
t1.Mul(t1, t2)
fp := (&gfP12{}).Frobenius(t1)
fp2 := (&gfP12{}).FrobeniusP2(t1)
fp3 := (&gfP12{}).Frobenius(fp2)
fu := (&gfP12{}).Exp(t1, u)
fu2 := (&gfP12{}).Exp(fu, u)
fu3 := (&gfP12{}).Exp(fu2, u)
y3 := (&gfP12{}).Frobenius(fu)
fu2p := (&gfP12{}).Frobenius(fu2)
fu3p := (&gfP12{}).Frobenius(fu3)
y2 := (&gfP12{}).FrobeniusP2(fu2)
y0 := &gfP12{}
y0.Mul(fp, fp2).Mul(y0, fp3)
y1 := (&gfP12{}).Conjugate(t1)
y5 := (&gfP12{}).Conjugate(fu2)
y3.Conjugate(y3)
y4 := (&gfP12{}).Mul(fu, fu2p)
y4.Conjugate(y4)
y6 := (&gfP12{}).Mul(fu3, fu3p)
y6.Conjugate(y6)
t0 := (&gfP12{}).Square(y6)
t0.Mul(t0, y4).Mul(t0, y5)
t1.Mul(y3, y5).Mul(t1, t0)
t0.Mul(t0, y2)
t1.Square(t1).Mul(t1, t0).Square(t1)
t0.Mul(t1, y1)
t1.Mul(t1, y0)
t0.Square(t0).Mul(t0, t1)
return t0
}
func optimalAte(a *twistPoint, b *curvePoint) *gfP12 {
e := miller(a, b)
ret := finalExponentiation(e)
if a.IsInfinity() || b.IsInfinity() {
ret.SetOne()
}
return ret
}

View file

@ -0,0 +1,204 @@
package bn256
import (
"math/big"
)
// twistPoint implements the elliptic curve y²=x³+3/ξ over GF(p²). Points are
// kept in Jacobian form and t=z² when valid. The group G₂ is the set of
// n-torsion points of this curve over GF(p²) (where n = Order)
type twistPoint struct {
x, y, z, t gfP2
}
var twistB = &gfP2{
gfP{0x38e7ecccd1dcff67, 0x65f0b37d93ce0d3e, 0xd749d0dd22ac00aa, 0x0141b9ce4a688d4d},
gfP{0x3bf938e377b802a8, 0x020b1b273633535d, 0x26b7edf049755260, 0x2514c6324384a86d},
}
// twistGen is the generator of group G₂.
var twistGen = &twistPoint{
gfP2{
gfP{0xafb4737da84c6140, 0x6043dd5a5802d8c4, 0x09e950fc52a02f86, 0x14fef0833aea7b6b},
gfP{0x8e83b5d102bc2026, 0xdceb1935497b0172, 0xfbb8264797811adf, 0x19573841af96503b},
},
gfP2{
gfP{0x64095b56c71856ee, 0xdc57f922327d3cbb, 0x55f935be33351076, 0x0da4a0e693fd6482},
gfP{0x619dfa9d886be9f6, 0xfe7fd297f59e9b78, 0xff9e1a62231b7dfe, 0x28fd7eebae9e4206},
},
gfP2{*newGFp(0), *newGFp(1)},
gfP2{*newGFp(0), *newGFp(1)},
}
func (c *twistPoint) String() string {
c.MakeAffine()
x, y := gfP2Decode(&c.x), gfP2Decode(&c.y)
return "(" + x.String() + ", " + y.String() + ")"
}
func (c *twistPoint) Set(a *twistPoint) {
c.x.Set(&a.x)
c.y.Set(&a.y)
c.z.Set(&a.z)
c.t.Set(&a.t)
}
// IsOnCurve returns true iff c is on the curve.
func (c *twistPoint) IsOnCurve() bool {
c.MakeAffine()
if c.IsInfinity() {
return true
}
y2, x3 := &gfP2{}, &gfP2{}
y2.Square(&c.y)
x3.Square(&c.x).Mul(x3, &c.x).Add(x3, twistB)
if *y2 != *x3 {
return false
}
cneg := &twistPoint{}
cneg.Mul(c, Order)
return cneg.z.IsZero()
}
func (c *twistPoint) SetInfinity() {
c.x.SetZero()
c.y.SetOne()
c.z.SetZero()
c.t.SetZero()
}
func (c *twistPoint) IsInfinity() bool {
return c.z.IsZero()
}
func (c *twistPoint) Add(a, b *twistPoint) {
// For additional comments, see the same function in curve.go.
if a.IsInfinity() {
c.Set(b)
return
}
if b.IsInfinity() {
c.Set(a)
return
}
// See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/addition/add-2007-bl.op3
z12 := (&gfP2{}).Square(&a.z)
z22 := (&gfP2{}).Square(&b.z)
u1 := (&gfP2{}).Mul(&a.x, z22)
u2 := (&gfP2{}).Mul(&b.x, z12)
t := (&gfP2{}).Mul(&b.z, z22)
s1 := (&gfP2{}).Mul(&a.y, t)
t.Mul(&a.z, z12)
s2 := (&gfP2{}).Mul(&b.y, t)
h := (&gfP2{}).Sub(u2, u1)
xEqual := h.IsZero()
t.Add(h, h)
i := (&gfP2{}).Square(t)
j := (&gfP2{}).Mul(h, i)
t.Sub(s2, s1)
yEqual := t.IsZero()
if xEqual && yEqual {
c.Double(a)
return
}
r := (&gfP2{}).Add(t, t)
v := (&gfP2{}).Mul(u1, i)
t4 := (&gfP2{}).Square(r)
t.Add(v, v)
t6 := (&gfP2{}).Sub(t4, j)
c.x.Sub(t6, t)
t.Sub(v, &c.x) // t7
t4.Mul(s1, j) // t8
t6.Add(t4, t4) // t9
t4.Mul(r, t) // t10
c.y.Sub(t4, t6)
t.Add(&a.z, &b.z) // t11
t4.Square(t) // t12
t.Sub(t4, z12) // t13
t4.Sub(t, z22) // t14
c.z.Mul(t4, h)
}
func (c *twistPoint) Double(a *twistPoint) {
// See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/doubling/dbl-2009-l.op3
A := (&gfP2{}).Square(&a.x)
B := (&gfP2{}).Square(&a.y)
C := (&gfP2{}).Square(B)
t := (&gfP2{}).Add(&a.x, B)
t2 := (&gfP2{}).Square(t)
t.Sub(t2, A)
t2.Sub(t, C)
d := (&gfP2{}).Add(t2, t2)
t.Add(A, A)
e := (&gfP2{}).Add(t, A)
f := (&gfP2{}).Square(e)
t.Add(d, d)
c.x.Sub(f, t)
t.Add(C, C)
t2.Add(t, t)
t.Add(t2, t2)
c.y.Sub(d, &c.x)
t2.Mul(e, &c.y)
c.y.Sub(t2, t)
t.Mul(&a.y, &a.z)
c.z.Add(t, t)
}
func (c *twistPoint) Mul(a *twistPoint, scalar *big.Int) {
sum, t := &twistPoint{}, &twistPoint{}
for i := scalar.BitLen(); i >= 0; i-- {
t.Double(sum)
if scalar.Bit(i) != 0 {
sum.Add(t, a)
} else {
sum.Set(t)
}
}
c.Set(sum)
}
func (c *twistPoint) MakeAffine() {
if c.z.IsOne() {
return
} else if c.z.IsZero() {
c.x.SetZero()
c.y.SetOne()
c.t.SetZero()
return
}
zInv := (&gfP2{}).Invert(&c.z)
t := (&gfP2{}).Mul(&c.y, zInv)
zInv2 := (&gfP2{}).Square(zInv)
c.y.Mul(t, zInv2)
t.Mul(&c.x, zInv2)
c.x.Set(t)
c.z.SetOne()
c.t.SetOne()
}
func (c *twistPoint) Neg(a *twistPoint) {
c.x.Set(&a.x)
c.y.Neg(&a.y)
c.z.Set(&a.z)
c.t.SetZero()
}

View file

@ -18,6 +18,7 @@ package bn256
import ( import (
"crypto/rand" "crypto/rand"
"errors"
"io" "io"
"math/big" "math/big"
) )
@ -115,21 +116,25 @@ func (n *G1) Marshal() []byte {
// Unmarshal sets e to the result of converting the output of Marshal back into // Unmarshal sets e to the result of converting the output of Marshal back into
// a group element and then returns e. // a group element and then returns e.
func (e *G1) Unmarshal(m []byte) (*G1, bool) { func (e *G1) Unmarshal(m []byte) ([]byte, error) {
// Each value is a 256-bit number. // Each value is a 256-bit number.
const numBytes = 256 / 8 const numBytes = 256 / 8
if len(m) != 2*numBytes { if len(m) != 2*numBytes {
return nil, false return nil, errors.New("bn256: not enough data")
} }
// Unmarshal the points and check their caps
if e.p == nil { if e.p == nil {
e.p = newCurvePoint(nil) e.p = newCurvePoint(nil)
} }
e.p.x.SetBytes(m[0*numBytes : 1*numBytes]) e.p.x.SetBytes(m[0*numBytes : 1*numBytes])
if e.p.x.Cmp(P) >= 0 {
return nil, errors.New("bn256: coordinate exceeds modulus")
}
e.p.y.SetBytes(m[1*numBytes : 2*numBytes]) e.p.y.SetBytes(m[1*numBytes : 2*numBytes])
if e.p.y.Cmp(P) >= 0 {
return nil, errors.New("bn256: coordinate exceeds modulus")
}
// Ensure the point is on the curve
if e.p.x.Sign() == 0 && e.p.y.Sign() == 0 { if e.p.x.Sign() == 0 && e.p.y.Sign() == 0 {
// This is the point at infinity. // This is the point at infinity.
e.p.y.SetInt64(1) e.p.y.SetInt64(1)
@ -140,11 +145,10 @@ func (e *G1) Unmarshal(m []byte) (*G1, bool) {
e.p.t.SetInt64(1) e.p.t.SetInt64(1)
if !e.p.IsOnCurve() { if !e.p.IsOnCurve() {
return nil, false return nil, errors.New("bn256: malformed point")
} }
} }
return m[2*numBytes:], nil
return e, true
} }
// G2 is an abstract cyclic group. The zero value is suitable for use as the // G2 is an abstract cyclic group. The zero value is suitable for use as the
@ -233,23 +237,33 @@ func (n *G2) Marshal() []byte {
// Unmarshal sets e to the result of converting the output of Marshal back into // Unmarshal sets e to the result of converting the output of Marshal back into
// a group element and then returns e. // a group element and then returns e.
func (e *G2) Unmarshal(m []byte) (*G2, bool) { func (e *G2) Unmarshal(m []byte) ([]byte, error) {
// Each value is a 256-bit number. // Each value is a 256-bit number.
const numBytes = 256 / 8 const numBytes = 256 / 8
if len(m) != 4*numBytes { if len(m) != 4*numBytes {
return nil, false return nil, errors.New("bn256: not enough data")
} }
// Unmarshal the points and check their caps
if e.p == nil { if e.p == nil {
e.p = newTwistPoint(nil) e.p = newTwistPoint(nil)
} }
e.p.x.x.SetBytes(m[0*numBytes : 1*numBytes]) e.p.x.x.SetBytes(m[0*numBytes : 1*numBytes])
if e.p.x.x.Cmp(P) >= 0 {
return nil, errors.New("bn256: coordinate exceeds modulus")
}
e.p.x.y.SetBytes(m[1*numBytes : 2*numBytes]) e.p.x.y.SetBytes(m[1*numBytes : 2*numBytes])
if e.p.x.y.Cmp(P) >= 0 {
return nil, errors.New("bn256: coordinate exceeds modulus")
}
e.p.y.x.SetBytes(m[2*numBytes : 3*numBytes]) e.p.y.x.SetBytes(m[2*numBytes : 3*numBytes])
if e.p.y.x.Cmp(P) >= 0 {
return nil, errors.New("bn256: coordinate exceeds modulus")
}
e.p.y.y.SetBytes(m[3*numBytes : 4*numBytes]) e.p.y.y.SetBytes(m[3*numBytes : 4*numBytes])
if e.p.y.y.Cmp(P) >= 0 {
return nil, errors.New("bn256: coordinate exceeds modulus")
}
// Ensure the point is on the curve
if e.p.x.x.Sign() == 0 && if e.p.x.x.Sign() == 0 &&
e.p.x.y.Sign() == 0 && e.p.x.y.Sign() == 0 &&
e.p.y.x.Sign() == 0 && e.p.y.x.Sign() == 0 &&
@ -263,11 +277,10 @@ func (e *G2) Unmarshal(m []byte) (*G2, bool) {
e.p.t.SetOne() e.p.t.SetOne()
if !e.p.IsOnCurve() { if !e.p.IsOnCurve() {
return nil, false return nil, errors.New("bn256: malformed point")
} }
} }
return m[4*numBytes:], nil
return e, true
} }
// GT is an abstract cyclic group. The zero value is suitable for use as the // GT is an abstract cyclic group. The zero value is suitable for use as the

View file

@ -219,15 +219,16 @@ func TestBilinearity(t *testing.T) {
func TestG1Marshal(t *testing.T) { func TestG1Marshal(t *testing.T) {
g := new(G1).ScalarBaseMult(new(big.Int).SetInt64(1)) g := new(G1).ScalarBaseMult(new(big.Int).SetInt64(1))
form := g.Marshal() form := g.Marshal()
_, ok := new(G1).Unmarshal(form) _, err := new(G1).Unmarshal(form)
if !ok { if err != nil {
t.Fatalf("failed to unmarshal") t.Fatalf("failed to unmarshal")
} }
g.ScalarBaseMult(Order) g.ScalarBaseMult(Order)
form = g.Marshal() form = g.Marshal()
g2, ok := new(G1).Unmarshal(form)
if !ok { g2 := new(G1)
if _, err = g2.Unmarshal(form); err != nil {
t.Fatalf("failed to unmarshal ∞") t.Fatalf("failed to unmarshal ∞")
} }
if !g2.p.IsInfinity() { if !g2.p.IsInfinity() {
@ -238,15 +239,15 @@ func TestG1Marshal(t *testing.T) {
func TestG2Marshal(t *testing.T) { func TestG2Marshal(t *testing.T) {
g := new(G2).ScalarBaseMult(new(big.Int).SetInt64(1)) g := new(G2).ScalarBaseMult(new(big.Int).SetInt64(1))
form := g.Marshal() form := g.Marshal()
_, ok := new(G2).Unmarshal(form) _, err := new(G2).Unmarshal(form)
if !ok { if err != nil {
t.Fatalf("failed to unmarshal") t.Fatalf("failed to unmarshal")
} }
g.ScalarBaseMult(Order) g.ScalarBaseMult(Order)
form = g.Marshal() form = g.Marshal()
g2, ok := new(G2).Unmarshal(form) g2 := new(G2)
if !ok { if _, err = g2.Unmarshal(form); err != nil {
t.Fatalf("failed to unmarshal ∞") t.Fatalf("failed to unmarshal ∞")
} }
if !g2.p.IsInfinity() { if !g2.p.IsInfinity() {
@ -273,12 +274,18 @@ func TestTripartiteDiffieHellman(t *testing.T) {
b, _ := rand.Int(rand.Reader, Order) b, _ := rand.Int(rand.Reader, Order)
c, _ := rand.Int(rand.Reader, Order) c, _ := rand.Int(rand.Reader, Order)
pa, _ := new(G1).Unmarshal(new(G1).ScalarBaseMult(a).Marshal()) pa := new(G1)
qa, _ := new(G2).Unmarshal(new(G2).ScalarBaseMult(a).Marshal()) pa.Unmarshal(new(G1).ScalarBaseMult(a).Marshal())
pb, _ := new(G1).Unmarshal(new(G1).ScalarBaseMult(b).Marshal()) qa := new(G2)
qb, _ := new(G2).Unmarshal(new(G2).ScalarBaseMult(b).Marshal()) qa.Unmarshal(new(G2).ScalarBaseMult(a).Marshal())
pc, _ := new(G1).Unmarshal(new(G1).ScalarBaseMult(c).Marshal()) pb := new(G1)
qc, _ := new(G2).Unmarshal(new(G2).ScalarBaseMult(c).Marshal()) pb.Unmarshal(new(G1).ScalarBaseMult(b).Marshal())
qb := new(G2)
qb.Unmarshal(new(G2).ScalarBaseMult(b).Marshal())
pc := new(G1)
pc.Unmarshal(new(G1).ScalarBaseMult(c).Marshal())
qc := new(G2)
qc.Unmarshal(new(G2).ScalarBaseMult(c).Marshal())
k1 := Pair(pb, qc) k1 := Pair(pb, qc)
k1.ScalarMult(k1, a) k1.ScalarMult(k1, a)

View file

@ -76,7 +76,13 @@ func (c *twistPoint) IsOnCurve() bool {
yy.Sub(yy, xxx) yy.Sub(yy, xxx)
yy.Sub(yy, twistB) yy.Sub(yy, twistB)
yy.Minimal() yy.Minimal()
return yy.x.Sign() == 0 && yy.y.Sign() == 0
if yy.x.Sign() != 0 || yy.y.Sign() != 0 {
return false
}
cneg := newTwistPoint(pool)
cneg.Mul(c, Order, pool)
return cneg.z.IsZero()
} }
func (c *twistPoint) SetInfinity() { func (c *twistPoint) SetInfinity() {

View file

@ -27,6 +27,7 @@ import (
ethereum "github.com/EthereumCommonwealth/go-callisto" ethereum "github.com/EthereumCommonwealth/go-callisto"
"github.com/EthereumCommonwealth/go-callisto/common" "github.com/EthereumCommonwealth/go-callisto/common"
"github.com/EthereumCommonwealth/go-callisto/core"
"github.com/EthereumCommonwealth/go-callisto/core/types" "github.com/EthereumCommonwealth/go-callisto/core/types"
"github.com/EthereumCommonwealth/go-callisto/ethdb" "github.com/EthereumCommonwealth/go-callisto/ethdb"
"github.com/EthereumCommonwealth/go-callisto/event" "github.com/EthereumCommonwealth/go-callisto/event"
@ -221,6 +222,9 @@ func New(mode SyncMode, stateDb ethdb.Database, mux *event.TypeMux, chain BlockC
quitCh: make(chan struct{}), quitCh: make(chan struct{}),
stateCh: make(chan dataPack), stateCh: make(chan dataPack),
stateSyncStart: make(chan *stateSync), stateSyncStart: make(chan *stateSync),
syncStatsState: stateSyncStats{
processed: core.GetTrieSyncProgress(stateDb),
},
trackStateReq: make(chan *stateReq), trackStateReq: make(chan *stateReq),
} }
go dl.qosTuner() go dl.qosTuner()

View file

@ -23,6 +23,7 @@ import (
"time" "time"
"github.com/EthereumCommonwealth/go-callisto/common" "github.com/EthereumCommonwealth/go-callisto/common"
"github.com/EthereumCommonwealth/go-callisto/core"
"github.com/EthereumCommonwealth/go-callisto/core/state" "github.com/EthereumCommonwealth/go-callisto/core/state"
"github.com/EthereumCommonwealth/go-callisto/crypto/sha3" "github.com/EthereumCommonwealth/go-callisto/crypto/sha3"
"github.com/EthereumCommonwealth/go-callisto/ethdb" "github.com/EthereumCommonwealth/go-callisto/ethdb"
@ -466,4 +467,7 @@ func (s *stateSync) updateStats(written, duplicate, unexpected int, duration tim
if written > 0 || duplicate > 0 || unexpected > 0 { if written > 0 || duplicate > 0 || unexpected > 0 {
log.Info("Imported new state entries", "count", written, "elapsed", common.PrettyDuration(duration), "processed", s.d.syncStatsState.processed, "pending", s.d.syncStatsState.pending, "retry", len(s.tasks), "duplicate", s.d.syncStatsState.duplicate, "unexpected", s.d.syncStatsState.unexpected) log.Info("Imported new state entries", "count", written, "elapsed", common.PrettyDuration(duration), "processed", s.d.syncStatsState.processed, "pending", s.d.syncStatsState.pending, "retry", len(s.tasks), "duplicate", s.d.syncStatsState.duplicate, "unexpected", s.d.syncStatsState.unexpected)
} }
if written > 0 {
core.WriteTrieSyncProgress(s.d.stateDB, s.d.syncStatsState.processed)
}
} }

View file

@ -249,7 +249,8 @@ func (pm *ProtocolManager) newPeer(pv int, p *p2p.Peer, rw p2p.MsgReadWriter) *p
// handle is the callback invoked to manage the life cycle of an eth peer. When // handle is the callback invoked to manage the life cycle of an eth peer. When
// this function terminates, the peer is disconnected. // this function terminates, the peer is disconnected.
func (pm *ProtocolManager) handle(p *peer) error { func (pm *ProtocolManager) handle(p *peer) error {
if pm.peers.Len() >= pm.maxPeers { // Ignore maxPeers if this is a trusted peer
if pm.peers.Len() >= pm.maxPeers && !p.Peer.Info().Network.Trusted {
return p2p.DiscTooManyPeers return p2p.DiscTooManyPeers
} }
p.Log().Debug("Ethereum peer connected", "name", p.Name()) p.Log().Debug("Ethereum peer connected", "name", p.Name())

View file

@ -140,10 +140,9 @@ func (h *HandlerT) GoTrace(file string, nsec uint) error {
return nil return nil
} }
// BlockProfile turns on CPU profiling for nsec seconds and writes // BlockProfile turns on goroutine profiling for nsec seconds and writes profile data to
// profile data to file. It uses a profile rate of 1 for most accurate // file. It uses a profile rate of 1 for most accurate information. If a different rate is
// information. If a different rate is desired, set the rate // desired, set the rate and write the profile manually.
// and write the profile manually.
func (*HandlerT) BlockProfile(file string, nsec uint) error { func (*HandlerT) BlockProfile(file string, nsec uint) error {
runtime.SetBlockProfileRate(1) runtime.SetBlockProfileRate(1)
time.Sleep(time.Duration(nsec) * time.Second) time.Sleep(time.Duration(nsec) * time.Second)
@ -162,6 +161,26 @@ func (*HandlerT) WriteBlockProfile(file string) error {
return writeProfile("block", file) return writeProfile("block", file)
} }
// MutexProfile turns on mutex profiling for nsec seconds and writes profile data to file.
// It uses a profile rate of 1 for most accurate information. If a different rate is
// desired, set the rate and write the profile manually.
func (*HandlerT) MutexProfile(file string, nsec uint) error {
runtime.SetMutexProfileFraction(1)
time.Sleep(time.Duration(nsec) * time.Second)
defer runtime.SetMutexProfileFraction(0)
return writeProfile("mutex", file)
}
// SetMutexProfileFraction sets the rate of mutex profiling.
func (*HandlerT) SetMutexProfileFraction(rate int) {
runtime.SetMutexProfileFraction(rate)
}
// WriteMutexProfile writes a goroutine blocking profile to the given file.
func (*HandlerT) WriteMutexProfile(file string) error {
return writeProfile("mutex", file)
}
// WriteMemProfile writes an allocation profile to the given file. // WriteMemProfile writes an allocation profile to the given file.
// Note that the profiling rate cannot be set through the API, // Note that the profiling rate cannot be set through the API,
// it must be set on the command line. // it must be set on the command line.

View file

@ -611,7 +611,7 @@ type CallArgs struct {
Data hexutil.Bytes `json:"data"` Data hexutil.Bytes `json:"data"`
} }
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config) ([]byte, uint64, bool, error) { func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration) ([]byte, uint64, bool, error) {
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr) state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
@ -630,7 +630,7 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
// Set default gas & gas price if none were set // Set default gas & gas price if none were set
gas, gasPrice := uint64(args.Gas), args.GasPrice.ToInt() gas, gasPrice := uint64(args.Gas), args.GasPrice.ToInt()
if gas == 0 { if gas == 0 {
gas = 50000000 gas = math.MaxUint64 / 2
} }
if gasPrice.Sign() == 0 { if gasPrice.Sign() == 0 {
gasPrice = new(big.Int).SetUint64(defaultGasPrice) gasPrice = new(big.Int).SetUint64(defaultGasPrice)
@ -642,14 +642,14 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
// Setup context so it may be cancelled the call has completed // Setup context so it may be cancelled the call has completed
// or, in case of unmetered gas, setup a context with a timeout. // or, in case of unmetered gas, setup a context with a timeout.
var cancel context.CancelFunc var cancel context.CancelFunc
if vmCfg.DisableGasMetering { if timeout > 0 {
ctx, cancel = context.WithTimeout(ctx, time.Second*5) ctx, cancel = context.WithTimeout(ctx, timeout)
} else { } else {
ctx, cancel = context.WithCancel(ctx) ctx, cancel = context.WithCancel(ctx)
} }
// Make sure the context is cancelled when the call has completed // Make sure the context is cancelled when the call has completed
// this makes sure resources are cleaned up. // this makes sure resources are cleaned up.
defer func() { cancel() }() defer cancel()
// Get a new instance of the EVM. // Get a new instance of the EVM.
evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg) evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg)
@ -676,7 +676,7 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
// Call executes the given transaction on the state for the given block number. // Call executes the given transaction on the state for the given block number.
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values. // It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) { func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
result, _, _, err := s.doCall(ctx, args, blockNr, vm.Config{DisableGasMetering: true}) result, _, _, err := s.doCall(ctx, args, blockNr, vm.Config{}, 5*time.Second)
return (hexutil.Bytes)(result), err return (hexutil.Bytes)(result), err
} }
@ -705,7 +705,7 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h
executable := func(gas uint64) bool { executable := func(gas uint64) bool {
args.Gas = hexutil.Uint64(gas) args.Gas = hexutil.Uint64(gas)
_, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{}) _, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{}, 0)
if err != nil || failed { if err != nil || failed {
return false return false
} }
@ -1043,7 +1043,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
return nil, err return nil, err
} }
if len(receipts) <= int(index) { if len(receipts) <= int(index) {
return nil, errors.New("unknown receipt") return nil, nil
} }
receipt := receipts[index] receipt := receipts[index]

View file

@ -307,6 +307,21 @@ web3._extend({
call: 'debug_writeBlockProfile', call: 'debug_writeBlockProfile',
params: 1 params: 1
}), }),
new web3._extend.Method({
name: 'mutexProfile',
call: 'debug_mutexProfile',
params: 2
}),
new web3._extend.Method({
name: 'setMutexProfileRate',
call: 'debug_setMutexProfileRate',
params: 1
}),
new web3._extend.Method({
name: 'writeMutexProfile',
call: 'debug_writeMutexProfile',
params: 1
}),
new web3._extend.Method({ new web3._extend.Method({
name: 'writeMemProfile', name: 'writeMemProfile',
call: 'debug_writeMemProfile', call: 'debug_writeMemProfile',

View file

@ -260,7 +260,8 @@ func (pm *ProtocolManager) newPeer(pv int, nv uint64, p *p2p.Peer, rw p2p.MsgRea
// handle is the callback invoked to manage the life cycle of a les peer. When // handle is the callback invoked to manage the life cycle of a les peer. When
// this function terminates, the peer is disconnected. // this function terminates, the peer is disconnected.
func (pm *ProtocolManager) handle(p *peer) error { func (pm *ProtocolManager) handle(p *peer) error {
if pm.peers.Len() >= pm.maxPeers { // Ignore maxPeers if this is a trusted peer
if pm.peers.Len() >= pm.maxPeers && !p.Peer.Info().Network.Trusted {
return p2p.DiscTooManyPeers return p2p.DiscTooManyPeers
} }

View file

@ -171,9 +171,6 @@ func (bc *LightChain) SetHead(head uint64) {
// GasLimit returns the gas limit of the current HEAD block. // GasLimit returns the gas limit of the current HEAD block.
func (self *LightChain) GasLimit() uint64 { func (self *LightChain) GasLimit() uint64 {
self.mu.RLock()
defer self.mu.RUnlock()
return self.hc.CurrentHeader().GasLimit return self.hc.CurrentHeader().GasLimit
} }
@ -387,9 +384,6 @@ func (self *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int)
// CurrentHeader retrieves the current head header of the canonical chain. The // CurrentHeader retrieves the current head header of the canonical chain. The
// header is retrieved from the HeaderChain's internal cache. // header is retrieved from the HeaderChain's internal cache.
func (self *LightChain) CurrentHeader() *types.Header { func (self *LightChain) CurrentHeader() *types.Header {
self.mu.RLock()
defer self.mu.RUnlock()
return self.hc.CurrentHeader() return self.hc.CurrentHeader()
} }

View file

@ -58,18 +58,18 @@ type trustedCheckpoint struct {
var ( var (
mainnetCheckpoint = trustedCheckpoint{ mainnetCheckpoint = trustedCheckpoint{
name: "mainnet", name: "mainnet",
sectionIdx: 153, sectionIdx: 157,
sectionHead: common.HexToHash("04c2114a8cbe49ba5c37a03cc4b4b8d3adfc0bd2c78e0e726405dd84afca1d63"), sectionHead: common.HexToHash("1963c080887ca7f406c2bb114293eea83e54f783f94df24b447f7e3b6317c747"),
chtRoot: common.HexToHash("d7ec603e5d30b567a6e894ee7704e4603232f206d3e5a589794cec0c57bf318e"), chtRoot: common.HexToHash("42abc436567dfb678a38fa6a9f881aa4c8a4cc8eaa2def08359292c3d0bd48ec"),
bloomTrieRoot: common.HexToHash("0b139b8fb692e21f663ff200da287192201c28ef5813c1ac6ba02a0a4799eef9"), bloomTrieRoot: common.HexToHash("281c9f8fb3cb8b37ae45e9907ef8f3b19cd22c54e297c2d6c09c1db1593dce42"),
} }
ropstenCheckpoint = trustedCheckpoint{ ropstenCheckpoint = trustedCheckpoint{
name: "ropsten", name: "ropsten",
sectionIdx: 79, sectionIdx: 83,
sectionHead: common.HexToHash("1b1ba890510e06411fdee9bb64ca7705c56a1a4ce3559ddb34b3680c526cb419"), sectionHead: common.HexToHash("3ca623586bc0da35f1fc8d9b6b55950f3b1f69be9c6501846a2df672adb61236"),
chtRoot: common.HexToHash("71d60207af74e5a22a3e1cfbfc89f9944f91b49aa980c86fba94d568369eaf44"), chtRoot: common.HexToHash("8f08ec7783969768c6ef06e5fe3398223cbf4ae2907b676da7b6fe6c7f55b059"),
bloomTrieRoot: common.HexToHash("70aca4b3b6d08dde8704c95cedb1420394453c1aec390947751e69ff8c436360"), bloomTrieRoot: common.HexToHash("02d86d3c6a87f8f8a92c2a59bbba2132ff6f9f61b0915a5dc28a9d8279219fd0"),
} }
) )

View file

@ -6,6 +6,7 @@ import (
"log" "log"
"sync" "sync"
"testing" "testing"
"time"
) )
const FANOUT = 128 const FANOUT = 128
@ -114,7 +115,7 @@ func Example() {
// Threadsafe registration // Threadsafe registration
t := GetOrRegisterTimer("db.get.latency", nil) t := GetOrRegisterTimer("db.get.latency", nil)
t.Time(func() {}) t.Time(func() { time.Sleep(10 * time.Millisecond) })
t.Update(1) t.Update(1)
fmt.Println(c.Count()) fmt.Println(c.Count())

View file

@ -47,8 +47,8 @@ func TestTimerStop(t *testing.T) {
func TestTimerFunc(t *testing.T) { func TestTimerFunc(t *testing.T) {
tm := NewTimer() tm := NewTimer()
tm.Time(func() { time.Sleep(50e6) }) tm.Time(func() { time.Sleep(50e6) })
if max := tm.Max(); 45e6 > max || max > 55e6 { if max := tm.Max(); 35e6 > max || max > 95e6 {
t.Errorf("tm.Max(): 45e6 > %v || %v > 55e6\n", max, max) t.Errorf("tm.Max(): 35e6 > %v || %v > 95e6\n", max, max)
} }
} }

View file

@ -308,6 +308,11 @@ func (api *PublicDebugAPI) Metrics(raw bool) (map[string]interface{}, error) {
// Fill the counter with the metric details, formatting if requested // Fill the counter with the metric details, formatting if requested
if raw { if raw {
switch metric := metric.(type) { switch metric := metric.(type) {
case metrics.Counter:
root[name] = map[string]interface{}{
"Overall": float64(metric.Count()),
}
case metrics.Meter: case metrics.Meter:
root[name] = map[string]interface{}{ root[name] = map[string]interface{}{
"AvgRate01Min": metric.Rate1(), "AvgRate01Min": metric.Rate1(),
@ -338,6 +343,11 @@ func (api *PublicDebugAPI) Metrics(raw bool) (map[string]interface{}, error) {
} }
} else { } else {
switch metric := metric.(type) { switch metric := metric.(type) {
case metrics.Counter:
root[name] = map[string]interface{}{
"Overall": float64(metric.Count()),
}
case metrics.Meter: case metrics.Meter:
root[name] = map[string]interface{}{ root[name] = map[string]interface{}{
"Avg01Min": format(metric.Rate1()*60, metric.Rate1()), "Avg01Min": format(metric.Rate1()*60, metric.Rate1()),

View file

@ -38,6 +38,7 @@ var DefaultConfig = Config{
DataDir: DefaultDataDir(), DataDir: DefaultDataDir(),
HTTPPort: DefaultHTTPPort, HTTPPort: DefaultHTTPPort,
HTTPModules: []string{"net", "web3"}, HTTPModules: []string{"net", "web3"},
HTTPVirtualHosts: []string{"localhost"},
WSPort: DefaultWSPort, WSPort: DefaultWSPort,
WSModules: []string{"net", "web3"}, WSModules: []string{"net", "web3"},
P2P: p2p.Config{ P2P: p2p.Config{

View file

@ -121,10 +121,6 @@ func (t *rlpx) close(err error) {
t.fd.Close() t.fd.Close()
} }
// doEncHandshake runs the protocol handshake using authenticated
// messages. the protocol handshake is the first authenticated message
// and also verifies whether the encryption handshake 'worked' and the
// remote side actually provided the right public key.
func (t *rlpx) doProtoHandshake(our *protoHandshake) (their *protoHandshake, err error) { func (t *rlpx) doProtoHandshake(our *protoHandshake) (their *protoHandshake, err error) {
// Writing our handshake happens concurrently, we prefer // Writing our handshake happens concurrently, we prefer
// returning the handshake read error. If the remote side // returning the handshake read error. If the remote side
@ -175,6 +171,10 @@ func readProtocolHandshake(rw MsgReader, our *protoHandshake) (*protoHandshake,
return &hs, nil return &hs, nil
} }
// doEncHandshake runs the protocol handshake using authenticated
// messages. the protocol handshake is the first authenticated message
// and also verifies whether the encryption handshake 'worked' and the
// remote side actually provided the right public key.
func (t *rlpx) doEncHandshake(prv *ecdsa.PrivateKey, dial *discover.Node) (discover.NodeID, error) { func (t *rlpx) doEncHandshake(prv *ecdsa.PrivateKey, dial *discover.Node) (discover.NodeID, error) {
var ( var (
sec secrets sec secrets

View file

@ -24,7 +24,7 @@ const (
VersionMajor = 1 // Major version component of the current release VersionMajor = 1 // Major version component of the current release
VersionMinor = 8 // Minor version component of the current release VersionMinor = 8 // Minor version component of the current release
VersionPatch = 2 // Patch version component of the current release VersionPatch = 2 // Patch version component of the current release
VersionMeta = "unstable" // Version metadata to append to the version string VersionMeta = "stable" // Version metadata to append to the version string
) )
// Version holds the textual version string. // Version holds the textual version string.

View file

@ -35,6 +35,7 @@ import (
//templateMap holds a mapping of an HTTP error code to a template //templateMap holds a mapping of an HTTP error code to a template
var templateMap map[int]*template.Template var templateMap map[int]*template.Template
var caseErrors []CaseError
//metrics variables //metrics variables
var ( var (
@ -51,6 +52,13 @@ type ErrorParams struct {
Details template.HTML Details template.HTML
} }
//a custom error case struct that would be used to store validators and
//additional error info to display with client responses.
type CaseError struct {
Validator func(*Request) bool
Msg func(*Request) string
}
//we init the error handling right on boot time, so lookup and http response is fast //we init the error handling right on boot time, so lookup and http response is fast
func init() { func init() {
initErrHandling() initErrHandling()
@ -74,6 +82,29 @@ func initErrHandling() {
//assign formatted HTML to the code //assign formatted HTML to the code
templateMap[code] = template.Must(template.New(fmt.Sprintf("%d", code)).Parse(tname)) templateMap[code] = template.Must(template.New(fmt.Sprintf("%d", code)).Parse(tname))
} }
caseErrors = []CaseError{
{
Validator: func(r *Request) bool { return r.uri != nil && r.uri.Addr != "" && strings.HasPrefix(r.uri.Addr, "0x") },
Msg: func(r *Request) string {
uriCopy := r.uri
uriCopy.Addr = strings.TrimPrefix(uriCopy.Addr, "0x")
return fmt.Sprintf(`The requested hash seems to be prefixed with '0x'. You will be redirected to the correct URL within 5 seconds.<br/>
Please click <a href='%[1]s'>here</a> if your browser does not redirect you.<script>setTimeout("location.href='%[1]s';",5000);</script>`, "/"+uriCopy.String())
},
}}
}
//ValidateCaseErrors is a method that process the request object through certain validators
//that assert if certain conditions are met for further information to log as an error
func ValidateCaseErrors(r *Request) string {
for _, err := range caseErrors {
if err.Validator(r) {
return err.Msg(r)
}
}
return ""
} }
//ShowMultipeChoices is used when a user requests a resource in a manifest which results //ShowMultipeChoices is used when a user requests a resource in a manifest which results
@ -82,10 +113,10 @@ func initErrHandling() {
//For example, if the user requests bzz:/<hash>/read and that manifest contains entries //For example, if the user requests bzz:/<hash>/read and that manifest contains entries
//"readme.md" and "readinglist.txt", a HTML page is returned with this two links. //"readme.md" and "readinglist.txt", a HTML page is returned with this two links.
//This only applies if the manifest has no default entry //This only applies if the manifest has no default entry
func ShowMultipleChoices(w http.ResponseWriter, r *http.Request, list api.ManifestList) { func ShowMultipleChoices(w http.ResponseWriter, r *Request, list api.ManifestList) {
msg := "" msg := ""
if list.Entries == nil { if list.Entries == nil {
ShowError(w, r, "Internal Server Error", http.StatusInternalServerError) ShowError(w, r, "Could not resolve", http.StatusInternalServerError)
return return
} }
//make links relative //make links relative
@ -102,7 +133,7 @@ func ShowMultipleChoices(w http.ResponseWriter, r *http.Request, list api.Manife
//create clickable link for each entry //create clickable link for each entry
msg += "<a href='" + base + e.Path + "'>" + e.Path + "</a><br/>" msg += "<a href='" + base + e.Path + "'>" + e.Path + "</a><br/>"
} }
respond(w, r, &ErrorParams{ respond(w, &r.Request, &ErrorParams{
Code: http.StatusMultipleChoices, Code: http.StatusMultipleChoices,
Details: template.HTML(msg), Details: template.HTML(msg),
Timestamp: time.Now().Format(time.RFC1123), Timestamp: time.Now().Format(time.RFC1123),
@ -115,13 +146,15 @@ func ShowMultipleChoices(w http.ResponseWriter, r *http.Request, list api.Manife
//The function just takes a string message which will be displayed in the error page. //The function just takes a string message which will be displayed in the error page.
//The code is used to evaluate which template will be displayed //The code is used to evaluate which template will be displayed
//(and return the correct HTTP status code) //(and return the correct HTTP status code)
func ShowError(w http.ResponseWriter, r *http.Request, msg string, code int) { func ShowError(w http.ResponseWriter, r *Request, msg string, code int) {
additionalMessage := ValidateCaseErrors(r)
if code == http.StatusInternalServerError { if code == http.StatusInternalServerError {
log.Error(msg) log.Error(msg)
} }
respond(w, r, &ErrorParams{ respond(w, &r.Request, &ErrorParams{
Code: code, Code: code,
Msg: msg, Msg: msg,
Details: template.HTML(additionalMessage),
Timestamp: time.Now().Format(time.RFC1123), Timestamp: time.Now().Format(time.RFC1123),
template: getTemplate(code), template: getTemplate(code),
}) })

View file

@ -168,6 +168,11 @@ func GetGenericErrorPage() string {
{{.Msg}} {{.Msg}}
</td> </td>
</tr> </tr>
<tr>
<td class="value">
{{.Details}}
</td>
</tr>
<tr> <tr>
<td class="key"> <td class="key">
@ -342,6 +347,12 @@ func GetNotFoundErrorPage() string {
{{.Msg}} {{.Msg}}
</td> </td>
</tr> </tr>
<tr>
<td class="value">
{{.Details}}
</td>
</tr>
<tr> <tr>
<td class="key"> <td class="key">

View file

@ -18,12 +18,13 @@ package http_test
import ( import (
"encoding/json" "encoding/json"
"golang.org/x/net/html"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings" "strings"
"testing" "testing"
"golang.org/x/net/html"
"github.com/EthereumCommonwealth/go-callisto/swarm/testutil" "github.com/EthereumCommonwealth/go-callisto/swarm/testutil"
) )
@ -96,8 +97,37 @@ func Test500Page(t *testing.T) {
defer resp.Body.Close() defer resp.Body.Close()
respbody, err = ioutil.ReadAll(resp.Body) respbody, err = ioutil.ReadAll(resp.Body)
if resp.StatusCode != 500 || !strings.Contains(string(respbody), "500") { if resp.StatusCode != 404 {
t.Fatalf("Invalid Status Code received, expected 500, got %d", resp.StatusCode) t.Fatalf("Invalid Status Code received, expected 404, got %d", resp.StatusCode)
}
_, err = html.Parse(strings.NewReader(string(respbody)))
if err != nil {
t.Fatalf("HTML validation failed for error page returned!")
}
}
func Test500PageWith0xHashPrefix(t *testing.T) {
srv := testutil.NewTestSwarmServer(t)
defer srv.Close()
var resp *http.Response
var respbody []byte
url := srv.URL + "/bzz:/0xthisShouldFailWith500CodeAndAHelpfulMessage"
resp, err := http.Get(url)
if err != nil {
t.Fatalf("Request failed: %v", err)
}
defer resp.Body.Close()
respbody, err = ioutil.ReadAll(resp.Body)
if resp.StatusCode != 404 {
t.Fatalf("Invalid Status Code received, expected 404, got %d", resp.StatusCode)
}
if !strings.Contains(string(respbody), "The requested hash seems to be prefixed with") {
t.Fatalf("Did not receive the expected error message")
} }
_, err = html.Parse(strings.NewReader(string(respbody))) _, err = html.Parse(strings.NewReader(string(respbody)))
@ -127,8 +157,8 @@ func TestJsonResponse(t *testing.T) {
defer resp.Body.Close() defer resp.Body.Close()
respbody, err = ioutil.ReadAll(resp.Body) respbody, err = ioutil.ReadAll(resp.Body)
if resp.StatusCode != 500 { if resp.StatusCode != 404 {
t.Fatalf("Invalid Status Code received, expected 500, got %d", resp.StatusCode) t.Fatalf("Invalid Status Code received, expected 404, got %d", resp.StatusCode)
} }
if !isJSON(string(respbody)) { if !isJSON(string(respbody)) {

View file

@ -336,7 +336,7 @@ func (s *Server) HandleGet(w http.ResponseWriter, r *Request) {
key, err := s.api.Resolve(r.uri) key, err := s.api.Resolve(r.uri)
if err != nil { if err != nil {
getFail.Inc(1) getFail.Inc(1)
s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err)) s.NotFound(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
return return
} }
@ -421,7 +421,7 @@ func (s *Server) HandleGetFiles(w http.ResponseWriter, r *Request) {
key, err := s.api.Resolve(r.uri) key, err := s.api.Resolve(r.uri)
if err != nil { if err != nil {
getFilesFail.Inc(1) getFilesFail.Inc(1)
s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err)) s.NotFound(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
return return
} }
@ -494,7 +494,7 @@ func (s *Server) HandleGetList(w http.ResponseWriter, r *Request) {
key, err := s.api.Resolve(r.uri) key, err := s.api.Resolve(r.uri)
if err != nil { if err != nil {
getListFail.Inc(1) getListFail.Inc(1)
s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err)) s.NotFound(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
return return
} }
@ -598,7 +598,7 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) {
key, err := s.api.Resolve(r.uri) key, err := s.api.Resolve(r.uri)
if err != nil { if err != nil {
getFileFail.Inc(1) getFileFail.Inc(1)
s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err)) s.NotFound(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
return return
} }
@ -628,7 +628,7 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) {
s.logDebug(fmt.Sprintf("Multiple choices! --> %v", list)) s.logDebug(fmt.Sprintf("Multiple choices! --> %v", list))
//show a nice page links to available entries //show a nice page links to available entries
ShowMultipleChoices(w, &r.Request, list) ShowMultipleChoices(w, r, list)
return return
} }
@ -660,6 +660,15 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
s.logDebug("HTTP %s request URL: '%s', Host: '%s', Path: '%s', Referer: '%s', Accept: '%s'", r.Method, r.RequestURI, r.URL.Host, r.URL.Path, r.Referer(), r.Header.Get("Accept")) s.logDebug("HTTP %s request URL: '%s', Host: '%s', Path: '%s', Referer: '%s', Accept: '%s'", r.Method, r.RequestURI, r.URL.Host, r.URL.Path, r.Referer(), r.Header.Get("Accept"))
if r.RequestURI == "/" && strings.Contains(r.Header.Get("Accept"), "text/html") {
err := landingPageTemplate.Execute(w, nil)
if err != nil {
s.logError("error rendering landing page: %s", err)
}
return
}
uri, err := api.Parse(strings.TrimLeft(r.URL.Path, "/")) uri, err := api.Parse(strings.TrimLeft(r.URL.Path, "/"))
req := &Request{Request: *r, uri: uri} req := &Request{Request: *r, uri: uri}
if err != nil { if err != nil {
@ -684,7 +693,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// strictly a traditional PUT request which replaces content // strictly a traditional PUT request which replaces content
// at a URI, and POST is more ubiquitous) // at a URI, and POST is more ubiquitous)
if uri.Raw() || uri.DeprecatedRaw() { if uri.Raw() || uri.DeprecatedRaw() {
ShowError(w, r, fmt.Sprintf("No PUT to %s allowed.", uri), http.StatusBadRequest) ShowError(w, req, fmt.Sprintf("No PUT to %s allowed.", uri), http.StatusBadRequest)
return return
} else { } else {
s.HandlePostFiles(w, req) s.HandlePostFiles(w, req)
@ -692,7 +701,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
case "DELETE": case "DELETE":
if uri.Raw() || uri.DeprecatedRaw() { if uri.Raw() || uri.DeprecatedRaw() {
ShowError(w, r, fmt.Sprintf("No DELETE to %s allowed.", uri), http.StatusBadRequest) ShowError(w, req, fmt.Sprintf("No DELETE to %s allowed.", uri), http.StatusBadRequest)
return return
} }
s.HandleDelete(w, req) s.HandleDelete(w, req)
@ -716,7 +725,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.HandleGetFile(w, req) s.HandleGetFile(w, req)
default: default:
ShowError(w, r, fmt.Sprintf("Method "+r.Method+" is not supported.", uri), http.StatusMethodNotAllowed) ShowError(w, req, fmt.Sprintf("Method "+r.Method+" is not supported.", uri), http.StatusMethodNotAllowed)
} }
} }
@ -748,13 +757,13 @@ func (s *Server) logError(format string, v ...interface{}) {
} }
func (s *Server) BadRequest(w http.ResponseWriter, r *Request, reason string) { func (s *Server) BadRequest(w http.ResponseWriter, r *Request, reason string) {
ShowError(w, &r.Request, fmt.Sprintf("Bad request %s %s: %s", r.Method, r.uri, reason), http.StatusBadRequest) ShowError(w, r, fmt.Sprintf("Bad request %s %s: %s", r.Request.Method, r.uri, reason), http.StatusBadRequest)
} }
func (s *Server) Error(w http.ResponseWriter, r *Request, err error) { func (s *Server) Error(w http.ResponseWriter, r *Request, err error) {
ShowError(w, &r.Request, fmt.Sprintf("Error serving %s %s: %s", r.Method, r.uri, err), http.StatusInternalServerError) ShowError(w, r, fmt.Sprintf("Error serving %s %s: %s", r.Request.Method, r.uri, err), http.StatusInternalServerError)
} }
func (s *Server) NotFound(w http.ResponseWriter, r *Request, err error) { func (s *Server) NotFound(w http.ResponseWriter, r *Request, err error) {
ShowError(w, &r.Request, fmt.Sprintf("NOT FOUND error serving %s %s: %s", r.Method, r.uri, err), http.StatusNotFound) ShowError(w, r, fmt.Sprintf("NOT FOUND error serving %s %s: %s", r.Request.Method, r.uri, err), http.StatusNotFound)
} }

File diff suppressed because one or more lines are too long

View file

@ -17,7 +17,6 @@
package mailserver package mailserver
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
@ -108,17 +107,16 @@ func (s *WMailServer) DeliverMail(peer *whisper.Peer, request *whisper.Envelope)
return return
} }
ok, lower, upper, topic := s.validateRequest(peer.ID(), request) ok, lower, upper, bloom := s.validateRequest(peer.ID(), request)
if ok { if ok {
s.processRequest(peer, lower, upper, topic) s.processRequest(peer, lower, upper, bloom)
} }
} }
func (s *WMailServer) processRequest(peer *whisper.Peer, lower, upper uint32, topic whisper.TopicType) []*whisper.Envelope { func (s *WMailServer) processRequest(peer *whisper.Peer, lower, upper uint32, bloom []byte) []*whisper.Envelope {
ret := make([]*whisper.Envelope, 0) ret := make([]*whisper.Envelope, 0)
var err error var err error
var zero common.Hash var zero common.Hash
var empty whisper.TopicType
kl := NewDbKey(lower, zero) kl := NewDbKey(lower, zero)
ku := NewDbKey(upper, zero) ku := NewDbKey(upper, zero)
i := s.db.NewIterator(&util.Range{Start: kl.raw, Limit: ku.raw}, nil) i := s.db.NewIterator(&util.Range{Start: kl.raw, Limit: ku.raw}, nil)
@ -131,7 +129,7 @@ func (s *WMailServer) processRequest(peer *whisper.Peer, lower, upper uint32, to
log.Error(fmt.Sprintf("RLP decoding failed: %s", err)) log.Error(fmt.Sprintf("RLP decoding failed: %s", err))
} }
if topic == empty || envelope.Topic == topic { if whisper.BloomFilterMatch(bloom, envelope.Bloom()) {
if peer == nil { if peer == nil {
// used for test purposes // used for test purposes
ret = append(ret, &envelope) ret = append(ret, &envelope)
@ -153,39 +151,45 @@ func (s *WMailServer) processRequest(peer *whisper.Peer, lower, upper uint32, to
return ret return ret
} }
func (s *WMailServer) validateRequest(peerID []byte, request *whisper.Envelope) (bool, uint32, uint32, whisper.TopicType) { func (s *WMailServer) validateRequest(peerID []byte, request *whisper.Envelope) (bool, uint32, uint32, []byte) {
var topic whisper.TopicType
if s.pow > 0.0 && request.PoW() < s.pow { if s.pow > 0.0 && request.PoW() < s.pow {
return false, 0, 0, topic return false, 0, 0, nil
} }
f := whisper.Filter{KeySym: s.key} f := whisper.Filter{KeySym: s.key}
decrypted := request.Open(&f) decrypted := request.Open(&f)
if decrypted == nil { if decrypted == nil {
log.Warn(fmt.Sprintf("Failed to decrypt p2p request")) log.Warn(fmt.Sprintf("Failed to decrypt p2p request"))
return false, 0, 0, topic return false, 0, 0, nil
}
if len(decrypted.Payload) < 8 {
log.Warn(fmt.Sprintf("Undersized p2p request"))
return false, 0, 0, topic
} }
src := crypto.FromECDSAPub(decrypted.Src) src := crypto.FromECDSAPub(decrypted.Src)
if len(src)-len(peerID) == 1 { if len(src)-len(peerID) == 1 {
src = src[1:] src = src[1:]
} }
if !bytes.Equal(peerID, src) {
// if you want to check the signature, you can do it here. e.g.:
// if !bytes.Equal(peerID, src) {
if src == nil {
log.Warn(fmt.Sprintf("Wrong signature of p2p request")) log.Warn(fmt.Sprintf("Wrong signature of p2p request"))
return false, 0, 0, topic return false, 0, 0, nil
}
var bloom []byte
payloadSize := len(decrypted.Payload)
if payloadSize < 8 {
log.Warn(fmt.Sprintf("Undersized p2p request"))
return false, 0, 0, nil
} else if payloadSize == 8 {
bloom = whisper.MakeFullNodeBloom()
} else if payloadSize < 8+whisper.BloomFilterSize {
log.Warn(fmt.Sprintf("Undersized bloom filter in p2p request"))
return false, 0, 0, nil
} else {
bloom = decrypted.Payload[8 : 8+whisper.BloomFilterSize]
} }
lower := binary.BigEndian.Uint32(decrypted.Payload[:4]) lower := binary.BigEndian.Uint32(decrypted.Payload[:4])
upper := binary.BigEndian.Uint32(decrypted.Payload[4:8]) upper := binary.BigEndian.Uint32(decrypted.Payload[4:8])
return true, lower, upper, bloom
if len(decrypted.Payload) >= 8+whisper.TopicLength {
topic = whisper.BytesToTopic(decrypted.Payload[8:])
}
return true, lower, upper, topic
} }

View file

@ -17,6 +17,7 @@
package mailserver package mailserver
import ( import (
"bytes"
"crypto/ecdsa" "crypto/ecdsa"
"encoding/binary" "encoding/binary"
"io/ioutil" "io/ioutil"
@ -61,7 +62,7 @@ func generateEnvelope(t *testing.T) *whisper.Envelope {
h := crypto.Keccak256Hash([]byte("test sample data")) h := crypto.Keccak256Hash([]byte("test sample data"))
params := &whisper.MessageParams{ params := &whisper.MessageParams{
KeySym: h[:], KeySym: h[:],
Topic: whisper.TopicType{}, Topic: whisper.TopicType{0x1F, 0x7E, 0xA1, 0x7F},
Payload: []byte("test payload"), Payload: []byte("test payload"),
PoW: powRequirement, PoW: powRequirement,
WorkTime: 2, WorkTime: 2,
@ -121,6 +122,7 @@ func deliverTest(t *testing.T, server *WMailServer, env *whisper.Envelope) {
upp: birth + 1, upp: birth + 1,
key: testPeerID, key: testPeerID,
} }
singleRequest(t, server, env, p, true) singleRequest(t, server, env, p, true)
p.low, p.upp = birth+1, 0xffffffff p.low, p.upp = birth+1, 0xffffffff
@ -131,14 +133,14 @@ func deliverTest(t *testing.T, server *WMailServer, env *whisper.Envelope) {
p.low = birth - 1 p.low = birth - 1
p.upp = birth + 1 p.upp = birth + 1
p.topic[0]++ p.topic[0] = 0xFF
singleRequest(t, server, env, p, false) singleRequest(t, server, env, p, false)
} }
func singleRequest(t *testing.T, server *WMailServer, env *whisper.Envelope, p *ServerTestParams, expect bool) { func singleRequest(t *testing.T, server *WMailServer, env *whisper.Envelope, p *ServerTestParams, expect bool) {
request := createRequest(t, p) request := createRequest(t, p)
src := crypto.FromECDSAPub(&p.key.PublicKey) src := crypto.FromECDSAPub(&p.key.PublicKey)
ok, lower, upper, topic := server.validateRequest(src, request) ok, lower, upper, bloom := server.validateRequest(src, request)
if !ok { if !ok {
t.Fatalf("request validation failed, seed: %d.", seed) t.Fatalf("request validation failed, seed: %d.", seed)
} }
@ -148,12 +150,13 @@ func singleRequest(t *testing.T, server *WMailServer, env *whisper.Envelope, p *
if upper != p.upp { if upper != p.upp {
t.Fatalf("request validation failed (upper bound), seed: %d.", seed) t.Fatalf("request validation failed (upper bound), seed: %d.", seed)
} }
if topic != p.topic { expectedBloom := whisper.TopicToBloom(p.topic)
if !bytes.Equal(bloom, expectedBloom) {
t.Fatalf("request validation failed (topic), seed: %d.", seed) t.Fatalf("request validation failed (topic), seed: %d.", seed)
} }
var exist bool var exist bool
mail := server.processRequest(nil, p.low, p.upp, p.topic) mail := server.processRequest(nil, p.low, p.upp, bloom)
for _, msg := range mail { for _, msg := range mail {
if msg.Hash() == env.Hash() { if msg.Hash() == env.Hash() {
exist = true exist = true
@ -166,17 +169,19 @@ func singleRequest(t *testing.T, server *WMailServer, env *whisper.Envelope, p *
} }
src[0]++ src[0]++
ok, lower, upper, topic = server.validateRequest(src, request) ok, lower, upper, bloom = server.validateRequest(src, request)
if ok { if !ok {
t.Fatalf("request validation false positive, seed: %d (lower: %d, upper: %d).", seed, lower, upper) // request should be valid regardless of signature
t.Fatalf("request validation false negative, seed: %d (lower: %d, upper: %d).", seed, lower, upper)
} }
} }
func createRequest(t *testing.T, p *ServerTestParams) *whisper.Envelope { func createRequest(t *testing.T, p *ServerTestParams) *whisper.Envelope {
data := make([]byte, 8+whisper.TopicLength) bloom := whisper.TopicToBloom(p.topic)
data := make([]byte, 8)
binary.BigEndian.PutUint32(data, p.low) binary.BigEndian.PutUint32(data, p.low)
binary.BigEndian.PutUint32(data[4:], p.upp) binary.BigEndian.PutUint32(data[4:], p.upp)
copy(data[8:], p.topic[:]) data = append(data, bloom...)
key, err := shh.GetSymKey(keyID) key, err := shh.GetSymKey(keyID)
if err != nil { if err != nil {

View file

@ -60,7 +60,7 @@ const (
aesKeyLength = 32 // in bytes aesKeyLength = 32 // in bytes
aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize() aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize()
keyIDSize = 32 // in bytes keyIDSize = 32 // in bytes
bloomFilterSize = 64 // in bytes BloomFilterSize = 64 // in bytes
flagsLength = 1 flagsLength = 1
EnvelopeHeaderLength = 20 EnvelopeHeaderLength = 20

View file

@ -208,6 +208,10 @@ func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) {
// Open tries to decrypt an envelope, and populates the message fields in case of success. // Open tries to decrypt an envelope, and populates the message fields in case of success.
func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) { func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) {
if watcher == nil {
return nil
}
// The API interface forbids filters doing both symmetric and asymmetric encryption. // The API interface forbids filters doing both symmetric and asymmetric encryption.
if watcher.expectsAsymmetricEncryption() && watcher.expectsSymmetricEncryption() { if watcher.expectsAsymmetricEncryption() && watcher.expectsSymmetricEncryption() {
return nil return nil
@ -249,7 +253,7 @@ func (e *Envelope) Bloom() []byte {
// TopicToBloom converts the topic (4 bytes) to the bloom filter (64 bytes) // TopicToBloom converts the topic (4 bytes) to the bloom filter (64 bytes)
func TopicToBloom(topic TopicType) []byte { func TopicToBloom(topic TopicType) []byte {
b := make([]byte, bloomFilterSize) b := make([]byte, BloomFilterSize)
var index [3]int var index [3]int
for j := 0; j < 3; j++ { for j := 0; j < 3; j++ {
index[j] = int(topic[j]) index[j] = int(topic[j])

View file

@ -35,6 +35,7 @@ type Filter struct {
PoW float64 // Proof of work as described in the Whisper spec PoW float64 // Proof of work as described in the Whisper spec
AllowP2P bool // Indicates whether this filter is interested in direct peer-to-peer messages AllowP2P bool // Indicates whether this filter is interested in direct peer-to-peer messages
SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization
id string // unique identifier
Messages map[common.Hash]*ReceivedMessage Messages map[common.Hash]*ReceivedMessage
mutex sync.RWMutex mutex sync.RWMutex
@ -43,6 +44,10 @@ type Filter struct {
// Filters represents a collection of filters // Filters represents a collection of filters
type Filters struct { type Filters struct {
watchers map[string]*Filter watchers map[string]*Filter
topicMatcher map[TopicType]map[*Filter]struct{} // map a topic to the filters that are interested in being notified when a message matches that topic
allTopicsMatcher map[*Filter]struct{} // list all the filters that will be notified of a new message, no matter what its topic is
whisper *Whisper whisper *Whisper
mutex sync.RWMutex mutex sync.RWMutex
} }
@ -51,6 +56,8 @@ type Filters struct {
func NewFilters(w *Whisper) *Filters { func NewFilters(w *Whisper) *Filters {
return &Filters{ return &Filters{
watchers: make(map[string]*Filter), watchers: make(map[string]*Filter),
topicMatcher: make(map[TopicType]map[*Filter]struct{}),
allTopicsMatcher: make(map[*Filter]struct{}),
whisper: w, whisper: w,
} }
} }
@ -81,7 +88,9 @@ func (fs *Filters) Install(watcher *Filter) (string, error) {
watcher.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym) watcher.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
} }
watcher.id = id
fs.watchers[id] = watcher fs.watchers[id] = watcher
fs.addTopicMatcher(watcher)
return id, err return id, err
} }
@ -91,12 +100,51 @@ func (fs *Filters) Uninstall(id string) bool {
fs.mutex.Lock() fs.mutex.Lock()
defer fs.mutex.Unlock() defer fs.mutex.Unlock()
if fs.watchers[id] != nil { if fs.watchers[id] != nil {
fs.removeFromTopicMatchers(fs.watchers[id])
delete(fs.watchers, id) delete(fs.watchers, id)
return true return true
} }
return false return false
} }
// addTopicMatcher adds a filter to the topic matchers.
// If the filter's Topics array is empty, it will be tried on every topic.
// Otherwise, it will be tried on the topics specified.
func (fs *Filters) addTopicMatcher(watcher *Filter) {
if len(watcher.Topics) == 0 {
fs.allTopicsMatcher[watcher] = struct{}{}
} else {
for _, t := range watcher.Topics {
topic := BytesToTopic(t)
if fs.topicMatcher[topic] == nil {
fs.topicMatcher[topic] = make(map[*Filter]struct{})
}
fs.topicMatcher[topic][watcher] = struct{}{}
}
}
}
// removeFromTopicMatchers removes a filter from the topic matchers
func (fs *Filters) removeFromTopicMatchers(watcher *Filter) {
delete(fs.allTopicsMatcher, watcher)
for _, topic := range watcher.Topics {
delete(fs.topicMatcher[BytesToTopic(topic)], watcher)
}
}
// getWatchersByTopic returns a slice containing the filters that
// match a specific topic
func (fs *Filters) getWatchersByTopic(topic TopicType) []*Filter {
res := make([]*Filter, 0, len(fs.allTopicsMatcher))
for watcher := range fs.allTopicsMatcher {
res = append(res, watcher)
}
for watcher := range fs.topicMatcher[topic] {
res = append(res, watcher)
}
return res
}
// Get returns a filter from the collection with a specific ID // Get returns a filter from the collection with a specific ID
func (fs *Filters) Get(id string) *Filter { func (fs *Filters) Get(id string) *Filter {
fs.mutex.RLock() fs.mutex.RLock()
@ -112,11 +160,10 @@ func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
fs.mutex.RLock() fs.mutex.RLock()
defer fs.mutex.RUnlock() defer fs.mutex.RUnlock()
i := -1 // only used for logging info candidates := fs.getWatchersByTopic(env.Topic)
for _, watcher := range fs.watchers { for _, watcher := range candidates {
i++
if p2pMessage && !watcher.AllowP2P { if p2pMessage && !watcher.AllowP2P {
log.Trace(fmt.Sprintf("msg [%x], filter [%d]: p2p messages are not allowed", env.Hash(), i)) log.Trace(fmt.Sprintf("msg [%x], filter [%s]: p2p messages are not allowed", env.Hash(), watcher.id))
continue continue
} }
@ -128,10 +175,10 @@ func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
if match { if match {
msg = env.Open(watcher) msg = env.Open(watcher)
if msg == nil { if msg == nil {
log.Trace("processing message: failed to open", "message", env.Hash().Hex(), "filter", i) log.Trace("processing message: failed to open", "message", env.Hash().Hex(), "filter", watcher.id)
} }
} else { } else {
log.Trace("processing message: does not match", "message", env.Hash().Hex(), "filter", i) log.Trace("processing message: does not match", "message", env.Hash().Hex(), "filter", watcher.id)
} }
} }
@ -144,20 +191,6 @@ func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
} }
} }
func (f *Filter) processEnvelope(env *Envelope) *ReceivedMessage {
if f.MatchEnvelope(env) {
msg := env.Open(f)
if msg != nil {
return msg
}
log.Trace("processing envelope: failed to open", "hash", env.Hash().Hex())
} else {
log.Trace("processing envelope: does not match", "hash", env.Hash().Hex())
}
return nil
}
func (f *Filter) expectsAsymmetricEncryption() bool { func (f *Filter) expectsAsymmetricEncryption() bool {
return f.KeyAsym != nil return f.KeyAsym != nil
} }
@ -194,16 +227,17 @@ func (f *Filter) Retrieve() (all []*ReceivedMessage) {
// MatchMessage checks if the filter matches an already decrypted // MatchMessage checks if the filter matches an already decrypted
// message (i.e. a Message that has already been handled by // message (i.e. a Message that has already been handled by
// MatchEnvelope when checked by a previous filter) // MatchEnvelope when checked by a previous filter).
// Topics are not checked here, since this is done by topic matchers.
func (f *Filter) MatchMessage(msg *ReceivedMessage) bool { func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
if f.PoW > 0 && msg.PoW < f.PoW { if f.PoW > 0 && msg.PoW < f.PoW {
return false return false
} }
if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() { if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() {
return IsPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) && f.MatchTopic(msg.Topic) return IsPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst)
} else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() { } else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() {
return f.SymKeyHash == msg.SymKeyHash && f.MatchTopic(msg.Topic) return f.SymKeyHash == msg.SymKeyHash
} }
return false return false
} }
@ -211,27 +245,9 @@ func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
// MatchEnvelope checks if it's worth decrypting the message. If // MatchEnvelope checks if it's worth decrypting the message. If
// it returns `true`, client code is expected to attempt decrypting // it returns `true`, client code is expected to attempt decrypting
// the message and subsequently call MatchMessage. // the message and subsequently call MatchMessage.
// Topics are not checked here, since this is done by topic matchers.
func (f *Filter) MatchEnvelope(envelope *Envelope) bool { func (f *Filter) MatchEnvelope(envelope *Envelope) bool {
if f.PoW > 0 && envelope.pow < f.PoW { return f.PoW <= 0 || envelope.pow >= f.PoW
return false
}
return f.MatchTopic(envelope.Topic)
}
// MatchTopic checks that the filter captures a given topic.
func (f *Filter) MatchTopic(topic TopicType) bool {
if len(f.Topics) == 0 {
// any topic matches
return true
}
for _, bt := range f.Topics {
if matchSingleTopic(topic, bt) {
return true
}
}
return false
} }
func matchSingleTopic(topic TopicType, bt []byte) bool { func matchSingleTopic(topic TopicType, bt []byte) bool {

View file

@ -303,9 +303,8 @@ func TestMatchEnvelope(t *testing.T) {
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
} }
params.Topic[0] = 0xFF // ensure mismatch params.Topic[0] = 0xFF // topic mismatch
// mismatch with pseudo-random data
msg, err := NewSentMessage(params) msg, err := NewSentMessage(params)
if err != nil { if err != nil {
t.Fatalf("failed to create new message with seed %d: %s.", seed, err) t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
@ -314,14 +313,6 @@ func TestMatchEnvelope(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed Wrap with seed %d: %s.", seed, err) t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
} }
match := fsym.MatchEnvelope(env)
if match {
t.Fatalf("failed MatchEnvelope symmetric with seed %d.", seed)
}
match = fasym.MatchEnvelope(env)
if match {
t.Fatalf("failed MatchEnvelope asymmetric with seed %d.", seed)
}
// encrypt symmetrically // encrypt symmetrically
i := mrand.Int() % 4 i := mrand.Int() % 4
@ -337,7 +328,7 @@ func TestMatchEnvelope(t *testing.T) {
} }
// symmetric + matching topic: match // symmetric + matching topic: match
match = fsym.MatchEnvelope(env) match := fsym.MatchEnvelope(env)
if !match { if !match {
t.Fatalf("failed MatchEnvelope() symmetric with seed %d.", seed) t.Fatalf("failed MatchEnvelope() symmetric with seed %d.", seed)
} }
@ -396,7 +387,7 @@ func TestMatchEnvelope(t *testing.T) {
// asymmetric + matching topic: match // asymmetric + matching topic: match
fasym.Topics[i] = fasym.Topics[i+1] fasym.Topics[i] = fasym.Topics[i+1]
match = fasym.MatchEnvelope(env) match = fasym.MatchEnvelope(env)
if match { if !match {
t.Fatalf("failed MatchEnvelope(asymmetric + matching topic) with seed %d.", seed) t.Fatalf("failed MatchEnvelope(asymmetric + matching topic) with seed %d.", seed)
} }
@ -431,7 +422,8 @@ func TestMatchEnvelope(t *testing.T) {
// filter with topic + envelope without topic: mismatch // filter with topic + envelope without topic: mismatch
fasym.Topics = fsym.Topics fasym.Topics = fsym.Topics
match = fasym.MatchEnvelope(env) match = fasym.MatchEnvelope(env)
if match { if !match {
// topic mismatch should have no affect, as topics are handled by topic matchers
t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed) t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed)
} }
} }
@ -487,7 +479,8 @@ func TestMatchMessageSym(t *testing.T) {
// topic mismatch // topic mismatch
f.Topics[index][0]++ f.Topics[index][0]++
if f.MatchMessage(msg) { if !f.MatchMessage(msg) {
// topic mismatch should have no affect, as topics are handled by topic matchers
t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed) t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed)
} }
f.Topics[index][0]-- f.Topics[index][0]--
@ -580,7 +573,8 @@ func TestMatchMessageAsym(t *testing.T) {
// topic mismatch // topic mismatch
f.Topics[index][0]++ f.Topics[index][0]++
if f.MatchMessage(msg) { if !f.MatchMessage(msg) {
// topic mismatch should have no affect, as topics are handled by topic matchers
t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed) t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed)
} }
f.Topics[index][0]-- f.Topics[index][0]--
@ -829,8 +823,9 @@ func TestVariableTopics(t *testing.T) {
f.Topics[i][lastTopicByte]++ f.Topics[i][lastTopicByte]++
match = f.MatchEnvelope(env) match = f.MatchEnvelope(env)
if match { if !match {
t.Fatalf("MatchEnvelope symmetric with seed %d, step %d: false positive.", seed, i) // topic mismatch should have no affect, as topics are handled by topic matchers
t.Fatalf("MatchEnvelope symmetric with seed %d, step %d.", seed, i)
} }
} }
} }

View file

@ -56,7 +56,7 @@ func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer {
powRequirement: 0.0, powRequirement: 0.0,
known: set.New(), known: set.New(),
quit: make(chan struct{}), quit: make(chan struct{}),
bloomFilter: makeFullNodeBloom(), bloomFilter: MakeFullNodeBloom(),
fullNode: true, fullNode: true,
} }
} }
@ -120,7 +120,7 @@ func (peer *Peer) handshake() error {
err = s.Decode(&bloom) err = s.Decode(&bloom)
if err == nil { if err == nil {
sz := len(bloom) sz := len(bloom)
if sz != bloomFilterSize && sz != 0 { if sz != BloomFilterSize && sz != 0 {
return fmt.Errorf("peer [%x] sent bad status message: wrong bloom filter size %d", peer.ID(), sz) return fmt.Errorf("peer [%x] sent bad status message: wrong bloom filter size %d", peer.ID(), sz)
} }
peer.setBloomFilter(bloom) peer.setBloomFilter(bloom)
@ -229,7 +229,7 @@ func (peer *Peer) notifyAboutBloomFilterChange(bloom []byte) error {
func (peer *Peer) bloomMatch(env *Envelope) bool { func (peer *Peer) bloomMatch(env *Envelope) bool {
peer.bloomMu.Lock() peer.bloomMu.Lock()
defer peer.bloomMu.Unlock() defer peer.bloomMu.Unlock()
return peer.fullNode || bloomFilterMatch(peer.bloomFilter, env.Bloom()) return peer.fullNode || BloomFilterMatch(peer.bloomFilter, env.Bloom())
} }
func (peer *Peer) setBloomFilter(bloom []byte) { func (peer *Peer) setBloomFilter(bloom []byte) {
@ -238,13 +238,13 @@ func (peer *Peer) setBloomFilter(bloom []byte) {
peer.bloomFilter = bloom peer.bloomFilter = bloom
peer.fullNode = isFullNode(bloom) peer.fullNode = isFullNode(bloom)
if peer.fullNode && peer.bloomFilter == nil { if peer.fullNode && peer.bloomFilter == nil {
peer.bloomFilter = makeFullNodeBloom() peer.bloomFilter = MakeFullNodeBloom()
} }
} }
func makeFullNodeBloom() []byte { func MakeFullNodeBloom() []byte {
bloom := make([]byte, bloomFilterSize) bloom := make([]byte, BloomFilterSize)
for i := 0; i < bloomFilterSize; i++ { for i := 0; i < BloomFilterSize; i++ {
bloom[i] = 0xFF bloom[i] = 0xFF
} }
return bloom return bloom

View file

@ -23,6 +23,7 @@ import (
mrand "math/rand" mrand "math/rand"
"net" "net"
"sync" "sync"
"sync/atomic"
"testing" "testing"
"time" "time"
@ -71,7 +72,7 @@ var keys = []string{
} }
type TestData struct { type TestData struct {
started int started int64
counter [NumNodes]int counter [NumNodes]int
mutex sync.RWMutex mutex sync.RWMutex
} }
@ -151,7 +152,7 @@ func resetParams(t *testing.T) {
} }
func initBloom(t *testing.T) { func initBloom(t *testing.T) {
masterBloomFilter = make([]byte, bloomFilterSize) masterBloomFilter = make([]byte, BloomFilterSize)
_, err := mrand.Read(masterBloomFilter) _, err := mrand.Read(masterBloomFilter)
if err != nil { if err != nil {
t.Fatalf("rand failed: %s.", err) t.Fatalf("rand failed: %s.", err)
@ -163,7 +164,7 @@ func initBloom(t *testing.T) {
masterBloomFilter[i] = 0xFF masterBloomFilter[i] = 0xFF
} }
if !bloomFilterMatch(masterBloomFilter, msgBloom) { if !BloomFilterMatch(masterBloomFilter, msgBloom) {
t.Fatalf("bloom mismatch on initBloom.") t.Fatalf("bloom mismatch on initBloom.")
} }
} }
@ -177,7 +178,7 @@ func initialize(t *testing.T) {
for i := 0; i < NumNodes; i++ { for i := 0; i < NumNodes; i++ {
var node TestNode var node TestNode
b := make([]byte, bloomFilterSize) b := make([]byte, BloomFilterSize)
copy(b, masterBloomFilter) copy(b, masterBloomFilter)
node.shh = New(&DefaultConfig) node.shh = New(&DefaultConfig)
node.shh.SetMinimumPoW(masterPow) node.shh.SetMinimumPoW(masterPow)
@ -240,9 +241,7 @@ func startServer(t *testing.T, s *p2p.Server) {
t.Fatalf("failed to start the fisrt server.") t.Fatalf("failed to start the fisrt server.")
} }
result.mutex.Lock() atomic.AddInt64(&result.started, 1)
defer result.mutex.Unlock()
result.started++
} }
func stopServers() { func stopServers() {
@ -472,7 +471,10 @@ func checkPowExchange(t *testing.T) {
func checkBloomFilterExchangeOnce(t *testing.T, mustPass bool) bool { func checkBloomFilterExchangeOnce(t *testing.T, mustPass bool) bool {
for i, node := range nodes { for i, node := range nodes {
for peer := range node.shh.peers { for peer := range node.shh.peers {
if !bytes.Equal(peer.bloomFilter, masterBloomFilter) { peer.bloomMu.Lock()
equals := bytes.Equal(peer.bloomFilter, masterBloomFilter)
peer.bloomMu.Unlock()
if !equals {
if mustPass { if mustPass {
t.Fatalf("node %d: failed to exchange bloom filter requirement in round %d. \n%x expected \n%x got", t.Fatalf("node %d: failed to exchange bloom filter requirement in round %d. \n%x expected \n%x got",
i, round, masterBloomFilter, peer.bloomFilter) i, round, masterBloomFilter, peer.bloomFilter)
@ -500,11 +502,13 @@ func checkBloomFilterExchange(t *testing.T) {
func waitForServersToStart(t *testing.T) { func waitForServersToStart(t *testing.T) {
const iterations = 200 const iterations = 200
var started int64
for j := 0; j < iterations; j++ { for j := 0; j < iterations; j++ {
time.Sleep(50 * time.Millisecond) time.Sleep(50 * time.Millisecond)
if result.started == NumNodes { started = atomic.LoadInt64(&result.started)
if started == NumNodes {
return return
} }
} }
t.Fatalf("Failed to start all the servers, running: %d", result.started) t.Fatalf("Failed to start all the servers, running: %d", started)
} }

View file

@ -232,11 +232,11 @@ func (whisper *Whisper) SetMaxMessageSize(size uint32) error {
// SetBloomFilter sets the new bloom filter // SetBloomFilter sets the new bloom filter
func (whisper *Whisper) SetBloomFilter(bloom []byte) error { func (whisper *Whisper) SetBloomFilter(bloom []byte) error {
if len(bloom) != bloomFilterSize { if len(bloom) != BloomFilterSize {
return fmt.Errorf("invalid bloom filter size: %d", len(bloom)) return fmt.Errorf("invalid bloom filter size: %d", len(bloom))
} }
b := make([]byte, bloomFilterSize) b := make([]byte, BloomFilterSize)
copy(b, bloom) copy(b, bloom)
whisper.settings.Store(bloomFilterIdx, b) whisper.settings.Store(bloomFilterIdx, b)
@ -558,14 +558,14 @@ func (whisper *Whisper) Subscribe(f *Filter) (string, error) {
// updateBloomFilter recalculates the new value of bloom filter, // updateBloomFilter recalculates the new value of bloom filter,
// and informs the peers if necessary. // and informs the peers if necessary.
func (whisper *Whisper) updateBloomFilter(f *Filter) { func (whisper *Whisper) updateBloomFilter(f *Filter) {
aggregate := make([]byte, bloomFilterSize) aggregate := make([]byte, BloomFilterSize)
for _, t := range f.Topics { for _, t := range f.Topics {
top := BytesToTopic(t) top := BytesToTopic(t)
b := TopicToBloom(top) b := TopicToBloom(top)
aggregate = addBloom(aggregate, b) aggregate = addBloom(aggregate, b)
} }
if !bloomFilterMatch(whisper.BloomFilter(), aggregate) { if !BloomFilterMatch(whisper.BloomFilter(), aggregate) {
// existing bloom filter must be updated // existing bloom filter must be updated
aggregate = addBloom(whisper.BloomFilter(), aggregate) aggregate = addBloom(whisper.BloomFilter(), aggregate)
whisper.SetBloomFilter(aggregate) whisper.SetBloomFilter(aggregate)
@ -701,7 +701,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
case bloomFilterExCode: case bloomFilterExCode:
var bloom []byte var bloom []byte
err := packet.Decode(&bloom) err := packet.Decode(&bloom)
if err == nil && len(bloom) != bloomFilterSize { if err == nil && len(bloom) != BloomFilterSize {
err = fmt.Errorf("wrong bloom filter size %d", len(bloom)) err = fmt.Errorf("wrong bloom filter size %d", len(bloom))
} }
@ -779,11 +779,11 @@ func (whisper *Whisper) add(envelope *Envelope, isP2P bool) (bool, error) {
} }
} }
if !bloomFilterMatch(whisper.BloomFilter(), envelope.Bloom()) { if !BloomFilterMatch(whisper.BloomFilter(), envelope.Bloom()) {
// maybe the value was recently changed, and the peers did not adjust yet. // maybe the value was recently changed, and the peers did not adjust yet.
// in this case the previous value is retrieved by BloomFilterTolerance() // in this case the previous value is retrieved by BloomFilterTolerance()
// for a short period of peer synchronization. // for a short period of peer synchronization.
if !bloomFilterMatch(whisper.BloomFilterTolerance(), envelope.Bloom()) { if !BloomFilterMatch(whisper.BloomFilterTolerance(), envelope.Bloom()) {
return false, fmt.Errorf("envelope does not match bloom filter, hash=[%v], bloom: \n%x \n%x \n%x", return false, fmt.Errorf("envelope does not match bloom filter, hash=[%v], bloom: \n%x \n%x \n%x",
envelope.Hash().Hex(), whisper.BloomFilter(), envelope.Bloom(), envelope.Topic) envelope.Hash().Hex(), whisper.BloomFilter(), envelope.Bloom(), envelope.Topic)
} }
@ -928,24 +928,6 @@ func (whisper *Whisper) Envelopes() []*Envelope {
return all return all
} }
// Messages iterates through all currently floating envelopes
// and retrieves all the messages, that this filter could decrypt.
func (whisper *Whisper) Messages(id string) []*ReceivedMessage {
result := make([]*ReceivedMessage, 0)
whisper.poolMu.RLock()
defer whisper.poolMu.RUnlock()
if filter := whisper.filters.Get(id); filter != nil {
for _, env := range whisper.envelopes {
msg := filter.processEnvelope(env)
if msg != nil {
result = append(result, msg)
}
}
}
return result
}
// isEnvelopeCached checks if envelope with specific hash has already been received and cached. // isEnvelopeCached checks if envelope with specific hash has already been received and cached.
func (whisper *Whisper) isEnvelopeCached(hash common.Hash) bool { func (whisper *Whisper) isEnvelopeCached(hash common.Hash) bool {
whisper.poolMu.Lock() whisper.poolMu.Lock()
@ -1043,12 +1025,12 @@ func isFullNode(bloom []byte) bool {
return true return true
} }
func bloomFilterMatch(filter, sample []byte) bool { func BloomFilterMatch(filter, sample []byte) bool {
if filter == nil { if filter == nil {
return true return true
} }
for i := 0; i < bloomFilterSize; i++ { for i := 0; i < BloomFilterSize; i++ {
f := filter[i] f := filter[i]
s := sample[i] s := sample[i]
if (f | s) != f { if (f | s) != f {
@ -1060,8 +1042,8 @@ func bloomFilterMatch(filter, sample []byte) bool {
} }
func addBloom(a, b []byte) []byte { func addBloom(a, b []byte) []byte {
c := make([]byte, bloomFilterSize) c := make([]byte, BloomFilterSize)
for i := 0; i < bloomFilterSize; i++ { for i := 0; i < BloomFilterSize; i++ {
c[i] = a[i] | b[i] c[i] = a[i] | b[i]
} }
return c return c

View file

@ -75,10 +75,6 @@ func TestWhisperBasic(t *testing.T) {
if len(mail) != 0 { if len(mail) != 0 {
t.Fatalf("failed w.Envelopes().") t.Fatalf("failed w.Envelopes().")
} }
m := w.Messages("non-existent")
if len(m) != 0 {
t.Fatalf("failed w.Messages.")
}
derived := pbkdf2.Key([]byte(peerID), nil, 65356, aesKeyLength, sha256.New) derived := pbkdf2.Key([]byte(peerID), nil, 65356, aesKeyLength, sha256.New)
if !validateDataIntegrity(derived, aesKeyLength) { if !validateDataIntegrity(derived, aesKeyLength) {
@ -593,7 +589,7 @@ func TestCustomization(t *testing.T) {
} }
// check w.messages() // check w.messages()
id, err := w.Subscribe(f) _, err = w.Subscribe(f)
if err != nil { if err != nil {
t.Fatalf("failed subscribe with seed %d: %s.", seed, err) t.Fatalf("failed subscribe with seed %d: %s.", seed, err)
} }
@ -602,11 +598,6 @@ func TestCustomization(t *testing.T) {
if len(mail) > 0 { if len(mail) > 0 {
t.Fatalf("received premature mail") t.Fatalf("received premature mail")
} }
mail = w.Messages(id)
if len(mail) != 2 {
t.Fatalf("failed to get whisper messages")
}
} }
func TestSymmetricSendCycle(t *testing.T) { func TestSymmetricSendCycle(t *testing.T) {
@ -835,11 +826,11 @@ func TestSymmetricSendKeyMismatch(t *testing.T) {
func TestBloom(t *testing.T) { func TestBloom(t *testing.T) {
topic := TopicType{0, 0, 255, 6} topic := TopicType{0, 0, 255, 6}
b := TopicToBloom(topic) b := TopicToBloom(topic)
x := make([]byte, bloomFilterSize) x := make([]byte, BloomFilterSize)
x[0] = byte(1) x[0] = byte(1)
x[32] = byte(1) x[32] = byte(1)
x[bloomFilterSize-1] = byte(128) x[BloomFilterSize-1] = byte(128)
if !bloomFilterMatch(x, b) || !bloomFilterMatch(b, x) { if !BloomFilterMatch(x, b) || !BloomFilterMatch(b, x) {
t.Fatalf("bloom filter does not match the mask") t.Fatalf("bloom filter does not match the mask")
} }
@ -851,11 +842,11 @@ func TestBloom(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("math rand error") t.Fatalf("math rand error")
} }
if !bloomFilterMatch(b, b) { if !BloomFilterMatch(b, b) {
t.Fatalf("bloom filter does not match self") t.Fatalf("bloom filter does not match self")
} }
x = addBloom(x, b) x = addBloom(x, b)
if !bloomFilterMatch(x, b) { if !BloomFilterMatch(x, b) {
t.Fatalf("bloom filter does not match combined bloom") t.Fatalf("bloom filter does not match combined bloom")
} }
if !isFullNode(nil) { if !isFullNode(nil) {
@ -865,16 +856,16 @@ func TestBloom(t *testing.T) {
if isFullNode(x) { if isFullNode(x) {
t.Fatalf("isFullNode false positive") t.Fatalf("isFullNode false positive")
} }
for i := 0; i < bloomFilterSize; i++ { for i := 0; i < BloomFilterSize; i++ {
b[i] = byte(255) b[i] = byte(255)
} }
if !isFullNode(b) { if !isFullNode(b) {
t.Fatalf("isFullNode false negative") t.Fatalf("isFullNode false negative")
} }
if bloomFilterMatch(x, b) { if BloomFilterMatch(x, b) {
t.Fatalf("bloomFilterMatch false positive") t.Fatalf("bloomFilterMatch false positive")
} }
if !bloomFilterMatch(b, x) { if !BloomFilterMatch(b, x) {
t.Fatalf("bloomFilterMatch false negative") t.Fatalf("bloomFilterMatch false negative")
} }
@ -888,7 +879,7 @@ func TestBloom(t *testing.T) {
t.Fatalf("failed to set bloom filter: %s", err) t.Fatalf("failed to set bloom filter: %s", err)
} }
f = w.BloomFilter() f = w.BloomFilter()
if !bloomFilterMatch(f, x) || !bloomFilterMatch(x, f) { if !BloomFilterMatch(f, x) || !BloomFilterMatch(x, f) {
t.Fatalf("retireved wrong bloom filter") t.Fatalf("retireved wrong bloom filter")
} }
} }