Skip to content

Opcode: log#2529

Merged
jannotti merged 46 commits into
algorand:masterfrom
shiqizng:opcode/log
Aug 11, 2021
Merged

Opcode: log#2529
jannotti merged 46 commits into
algorand:masterfrom
shiqizng:opcode/log

Conversation

@shiqizng
Copy link
Copy Markdown
Contributor

@shiqizng shiqizng commented Jul 13, 2021

This PR adds a new opcode log. It takes one "bytes" stack argument. It can be called up to 32 times in a program, and log up to a total of 1k bytes.

Copy link
Copy Markdown
Contributor

@jannotti jannotti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see some tests that contain multiple calls to log

Comment thread data/transactions/logic/doc.go
Comment thread data/transactions/logic/eval.go Outdated
Comment thread data/transactions/logic/eval.go Outdated
Comment thread data/transactions/logic/evalStateful_test.go Outdated
Comment thread ledger/appcow.go Outdated
Comment thread data/transactions/logic/eval.go Outdated
@runvnc
Copy link
Copy Markdown

runvnc commented Jul 13, 2021

Hello, this is really great. I have been doing my own "log()" in Tealang using get, put and concat. One thing I wonder though, why is it necessary to limit to 8 times, rather than using the existing cost system?

@jannotti
Copy link
Copy Markdown
Contributor

Hello, this is really great. I have been doing my own "log()" in Tealang using get, put and concat. One thing I wonder though, why is it necessary to limit to 8 times, rather than using the existing cost system?

After discussion on Discord, we've decided that allowing 32 calls makes sense, with the 1k limit intact. For posterity - the reason for two such limits is, as with ApplicationArgs, there is additional space overhead associated with each call, so it should not be possible to log 1000 1 byte messages, as that would consume much more than 1k. But 32 should not really make much difference.

@shiqizng shiqizng requested a review from AyAggarwal July 13, 2021 18:30
Comment thread data/transactions/logic/doc.go Outdated
jdtzmn
jdtzmn previously approved these changes Jul 13, 2021
Comment thread data/transactions/logic/eval_test.go
Comment thread data/transactions/logic/assembler_test.go
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Jul 13, 2021

Codecov Report

Merging #2529 (f375213) into master (9194dec) will decrease coverage by 0.07%.
The diff coverage is 36.64%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2529      +/-   ##
==========================================
- Coverage   47.16%   47.08%   -0.08%     
==========================================
  Files         350      350              
  Lines       55954    56276     +322     
==========================================
+ Hits        26389    26499     +110     
- Misses      26620    26808     +188     
- Partials     2945     2969      +24     
Impacted Files Coverage Δ
config/consensus.go 84.01% <ø> (ø)
daemon/algod/api/server/v2/handlers.go 0.00% <0.00%> (ø)
daemon/algod/api/server/v2/utils.go 14.50% <0.00%> (-2.32%) ⬇️
data/transactions/logic/doc.go 82.75% <ø> (ø)
data/transactions/logic/opcodes.go 96.00% <ø> (ø)
data/basics/msgp_gen.go 35.46% <32.80%> (-0.36%) ⬇️
ledger/applications.go 54.65% <33.33%> (-1.60%) ⬇️
daemon/algod/api/server/v2/dryrun.go 58.99% <66.66%> (+1.61%) ⬆️
data/transactions/application.go 64.44% <72.72%> (+2.67%) ⬆️
data/transactions/logic/eval.go 90.61% <80.00%> (-0.10%) ⬇️
... and 11 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9194dec...f375213. Read the comment docs.

@shiqizng shiqizng requested a review from jannotti July 14, 2021 14:46
Copy link
Copy Markdown
Contributor

@jdtzmn jdtzmn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, just had one question.

Comment thread data/basics/teal.go Outdated
Comment thread data/transactions/logic/eval_test.go Outdated
runMode: runModeSignature,
}

failCases := []failCase{failCase0, failCase1, failCase2, failCase3, failCase4, failCase5}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: A pattern we use a lot for this is an anonymous struct:

failCases := []struct {
	source      string
	runMode     runMode
	errContains string
} {
    // cases here.
}

Copy link
Copy Markdown
Contributor

@jasonpaulos jasonpaulos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good! I just left some insignificant comments

Copy link
Copy Markdown
Contributor

@jasonpaulos jasonpaulos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One small thing I noticed

Comment thread daemon/algod/api/server/v2/dryrun.go Outdated
result.LocalDeltas = &localDeltas
}

result.Logs = DeltaLogToLog(delta.Logs)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this function should also convert each basics.LogItem's ID into the actual application ID. I believe it should convert 0 into appIdx (which is guaranteed to be not 0 at this point) and it should error if the input ID is not 0.

Comment thread data/basics/teal.go
Comment thread daemon/algod/api/server/v2/dryrun_test.go
Comment thread agreement/msgp_gen_test.go
Comment thread daemon/algod/api/server/v2/dryrun_test.go
Comment thread daemon/algod/api/server/v2/dryrun_test.go Outdated
ops, err := logic.AssembleString(`
#pragma version 5
int 1
loop: byte "a"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's do some more fancy stuff there to ensure log values vary. Something like

dup // copy loop var
int 48 // 0x32 -> '0' char
+
itob // -> char
log

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't get the suggested teal code. I did something much simpler instead.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the idea is to use ascii char codes to obtain new char value on each loop iteration.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually itob always produces an 8 byte slice. It would be better to so something like:

byte "a"
int 0
dup2
getbyte // convert 0th byte of "a" to integer, 97
int 1
+ // add 1
setbyte // set 0th byte of "a" to the byte given by the integer on the stack (97 + 1, "b")

Comment thread daemon/algod/api/server/v2/handlers.go Outdated
Comment thread ledger/appcow_test.go
a.Panics(func() { c.DelKey(getRandomAddress(a), aidx, false, key, 0) })
a.Panics(func() { c.DelKey(addr, aidx+1, false, key, 0) })
}
func TestCowAppendLog(t *testing.T) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, run go fmt

Comment thread ledger/applications.go Outdated
Comment thread test/e2e-go/features/transactions/application_test.go
Comment thread test/e2e-go/restAPI/restClient_test.go Outdated
Comment thread test/e2e-go/restAPI/restClient_test.go Outdated

prog := `#pragma version 5
int 1
loop: byte "a"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's check different values

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still want to see logging 32 different values in the test:

int 0 // loop variable
loop:
dup // copy
int 97 // 0x61 'a'
+ // 97, 98, 99, ...
itob // 'a', 'b', 'c', ...
log
int 1
+
dup
int 32
<
bnz loop

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see Jason's note about getbyte above #2529 (comment)

Comment thread config/consensus.go Outdated

// MaxLogCalls is the largest number of log calls that may appear in
// an eval delta, used for decoding purposes.
var MaxLogCalls int
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs to be const, no need to initialize it checkSetAllocBounds.

Suggested change
var MaxLogCalls int
const MaxLogCalls = 32

checkSetAllocBounds might have a check that the value is not greater than 32 (or 33 as JJ suggested)

Comment on lines +1182 to +1191
for i, m := range logs {
if i == 0 {
assert.Equal(t, base64.StdEncoding.EncodeToString([]byte("a")), m.Value)
} else if i == 1 {
assert.Equal(t, base64.StdEncoding.EncodeToString([]byte("b")), m.Value)
} else if i == 2 {
assert.Equal(t, base64.StdEncoding.EncodeToString([]byte("c")), m.Value)
}

}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calculating values looks more maintainable, check below:

Suggested change
for i, m := range logs {
if i == 0 {
assert.Equal(t, base64.StdEncoding.EncodeToString([]byte("a")), m.Value)
} else if i == 1 {
assert.Equal(t, base64.StdEncoding.EncodeToString([]byte("b")), m.Value)
} else if i == 2 {
assert.Equal(t, base64.StdEncoding.EncodeToString([]byte("c")), m.Value)
}
}
for i, m := range logs {
assert.Equal(t, base64.StdEncoding.EncodeToString([]byte(string(rune('a' + i)))), m.Value)
}

If logs are not sorted, use sort.Slice. If logs is map (not sure) then flatten it:

	type flog struct {
		idx int,
		li LogItem
	}
	for k, v := range logs {
		flatlog = append(flatlog, flog{i, v})
	}
	sort.Slice(flatlog, func(i, j int) {return flatlog[i].idx < flatlog[j].idx})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order of logs matter, so they should not be sorted before comparison

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well ok, then assert.Equal(t, base64.StdEncoding.EncodeToString([]byte(string(rune('a' + i)))), m.Value) will work just fine

Comment thread data/basics/teal.go
Comment thread data/transactions/logic/eval.go Outdated
Comment thread data/transactions/logic/eval.go Outdated
Comment thread data/transactions/logic/eval_test.go Outdated
ops, err := logic.AssembleString(`
#pragma version 5
int 1
loop: byte "a"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the idea is to use ascii char codes to obtain new char value on each loop iteration.

Copy link
Copy Markdown
Contributor

@algorandskiy algorandskiy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll incorporate these MaxLogCalls fixes...

Comment thread config/consensus.go
Comment on lines +425 to +427
// MaxLogCalls is the highest allowable log messages that may appear in
// any version, used for decoding purposes. Never decrease this value.
const MaxLogCalls = 32
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// MaxLogCalls is the highest allowable log messages that may appear in
// any version, used for decoding purposes. Never decrease this value.
const MaxLogCalls = 32
// MaxLogCalls is the highest allowable log messages that may appear in
// any version, used for decoding purposes. Never decrease this value.
const MaxEncodedLogCalls = 32

const MaxLogSize = 1024

// MaxLogCalls is the limit of total log calls during a program execution
const MaxLogCalls = config.MaxLogCalls
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const MaxLogCalls = config.MaxLogCalls
const MaxLogCalls = 32

loglen: 2,
},
{
source: fmt.Sprintf(`%s int 1`, strings.Repeat(`byte "a logging message"; log;`, config.MaxLogCalls)),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
source: fmt.Sprintf(`%s int 1`, strings.Repeat(`byte "a logging message"; log;`, config.MaxLogCalls)),
source: fmt.Sprintf(`%s int 1`, strings.Repeat(`byte "a logging message"; log;`, MaxLogCalls)),

runMode: runModeApplication,
},
{
source: fmt.Sprintf(`%s; int 1`, strings.Repeat(`byte "a"; log;`, config.MaxLogCalls+1)),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
source: fmt.Sprintf(`%s; int 1`, strings.Repeat(`byte "a"; log;`, config.MaxLogCalls+1)),
source: fmt.Sprintf(`%s; int 1`, strings.Repeat(`byte "a"; log;`, MaxLogCalls+1)),

algorandskiy
algorandskiy previously approved these changes Aug 9, 2021
Comment thread data/basics/teal.go
return false
}

// Logs must be equal
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should compare the lengths of the logs before iterating over them

Comment thread data/basics/teal_test.go
d1 = EvalDelta{
Logs: []LogItem{{ID: 0, Message: "val"}},
}
a.True(d1.Equal(d2))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested test case: compare two EvalDeltas with logs [A] and [A, B] -- I believe this would currently pass because of the length issue I mentioned

Comment thread data/transactions/application.go Outdated
// not valid.
func (ac *ApplicationCallTxnFields) AppIDByIndex(i uint64) (basics.AppIndex, error) {

// Index 0 always corresponds to the sender
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: sender still mentioned here and in function below

Comment thread ledger/applications.go
idx, err := txn.IndexByAppID(txn.ApplicationID)
if err != nil {
return err
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to be paranoid, could you return an error if idx != 0? It shouldn't happen, but it would be really bad if it did happen

return false
}
for i, e := range expected {
if e.ID != actual[i].ID || e.Message != actual[i].Message {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you could use the Equals method you made here

a.Equal(32, len(*txn.Logs))
for i, l := range *txn.Logs {
a.Equal(expectedAppID, l.Id)
assert.Equal(t, base64.StdEncoding.EncodeToString([]byte(string(rune('B'+i)))), l.Value)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: is it necessary to import assert instead of using a.Equal?

jasonpaulos
jasonpaulos previously approved these changes Aug 9, 2021
Copy link
Copy Markdown
Contributor

@jasonpaulos jasonpaulos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

partitiontest.PartitionTest may need to be added to tests to satisfy reviewdog (I don't know the purpose of that), but this looks good to me

@jannotti jannotti merged commit 58bc231 into algorand:master Aug 11, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants