Skip to content
Snippets Groups Projects
bct_curl.go 2.68 KiB
package curl

import "github.com/iotaledger/goshimmer/packages/ternary"

const (
	HIGH_LONG_BITS = 0xFFFFFFFFFFFFFFFF
)

type BCTCurl struct {
	hashLength     int
	numberOfRounds int
	stateLength    int
	state          ternary.BCTrits
	cTransform     func()
}

func NewBCTCurl(hashLength int, numberOfRounds int) *BCTCurl {
	this := &BCTCurl{
		hashLength:     hashLength,
		numberOfRounds: numberOfRounds,
		stateLength:    ternary.NUMBER_OF_TRITS_IN_A_TRYTE * hashLength,
		state: ternary.BCTrits{
			Lo: make([]uint, ternary.NUMBER_OF_TRITS_IN_A_TRYTE*hashLength),
			Hi: make([]uint, ternary.NUMBER_OF_TRITS_IN_A_TRYTE*hashLength),
		},
		cTransform: nil,
	}

	this.Reset()

	return this
}

func (this *BCTCurl) Reset() {
	for i := 0; i < this.stateLength; i++ {
		this.state.Lo[i] = HIGH_LONG_BITS
		this.state.Hi[i] = HIGH_LONG_BITS
	}
}

func (this *BCTCurl) Transform() {
	scratchPadLo := make([]uint, this.stateLength)
	scratchPadHi := make([]uint, this.stateLength)
	scratchPadIndex := 0

	for round := this.numberOfRounds; round > 0; round-- {
		copy(scratchPadLo, this.state.Lo)
		copy(scratchPadHi, this.state.Hi)
		for stateIndex := 0; stateIndex < this.stateLength; stateIndex++ {
			alpha := scratchPadLo[scratchPadIndex]
			beta := scratchPadHi[scratchPadIndex]

			if scratchPadIndex < 365 {
				scratchPadIndex += 364
			} else {
				scratchPadIndex -= 365
			}

			delta := beta ^ scratchPadLo[scratchPadIndex]

			this.state.Lo[stateIndex] = ^(delta & alpha)
			this.state.Hi[stateIndex] = delta | (alpha ^ scratchPadHi[scratchPadIndex])
		}
	}
}

func (this *BCTCurl) Absorb(bcTrits ternary.BCTrits) {
	length := len(bcTrits.Lo)
	offset := 0
	for {
		var lengthToCopy int
		if length < this.hashLength {
			lengthToCopy = length
		} else {
			lengthToCopy = this.hashLength
		}

		copy(this.state.Lo[0:lengthToCopy], bcTrits.Lo[offset:offset+lengthToCopy])
		copy(this.state.Hi[0:lengthToCopy], bcTrits.Hi[offset:offset+lengthToCopy])
		this.Transform()

		offset += lengthToCopy
		length -= lengthToCopy

		if length <= 0 {
			break
		}
	}
}

func (this *BCTCurl) Squeeze(tritCount int) ternary.BCTrits {
	result := ternary.BCTrits{
		Lo: make([]uint, tritCount),
		Hi: make([]uint, tritCount),
	}
	hashCount := tritCount / this.hashLength

	for i := 0; i < hashCount; i++ {
		copy(result.Lo[i*this.hashLength:(i+1)*this.hashLength], this.state.Lo[0:this.hashLength])
		copy(result.Hi[i*this.hashLength:(i+1)*this.hashLength], this.state.Hi[0:this.hashLength])

		this.Transform()
	}

	last := tritCount - hashCount*this.hashLength

	copy(result.Lo[tritCount-last:], this.state.Lo[0:last])
	copy(result.Hi[tritCount-last:], this.state.Hi[0:last])

	if tritCount%this.hashLength != 0 {
		this.Transform()
	}

	return result
}